### Start Socket Server Thread Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/app.html Starts the socket server thread. It retrieves configuration settings for the socket mode, host, and port to establish the server connection. ```python def sockserver_start(self, ntriprtcmstr: str = RTCMSTR): """ Start socket server thread. :param str ntriprtcmstr: source table string indicating RTCM3 types and intervals """ cfg = self.configuration ntripmode = cfg.get("sockmode_b") host = cfg.get("sockhost_s") if ntripmode: # NTRIP CASTER port = cfg.get("sockportntrip_n") ``` -------------------------------- ### Setup RoverFrame Widgets Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/rover_frame.html Configures the grid layout and creates the CanvasCompass widget for the RoverFrame. ```python def _body(self): """Set up frame and widgets.""" self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self._canvas = CanvasCompass( self.__app, self, MODE_POL, width=self.width, height=self.height, bg=BGCOL ) self._canvas.grid(column=0, row=0, sticky=NSEW) ``` -------------------------------- ### Setup Frame and Widgets Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/levelsview_frame.html Configures the grid layout for the frame and creates the CanvasGraph widget, which serves as the drawing surface for the C/No data. ```python def _body(self): """ Set up frame and widgets. """ self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self._canvas = CanvasGraph( self.__app, self, width=self.width, height=self.height, bg=BGCOL ) self._canvas.grid(column=0, row=0, sticky=NSEW) ``` -------------------------------- ### IMUFrame Body Setup Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/imu_frame.html Configures the grid layout for the IMUFrame and initializes its child widgets, including the canvas, labels, and spinboxes for range and option selection. ```python def _body(self): """Set up frame and widgets.""" for i in range(4): self.grid_columnconfigure(i, weight=1, uniform="ent") self.grid_rowconfigure(0, weight=1) self.grid_rowconfigure(1, weight=0) self.grid_rowconfigure(2, weight=0) self._canvas = Canvas(self, width=self.width, height=self.height, bg=BGCOL) self._lbl_range = Label(self, text="Range", fg=FGCOL, bg=BGCOL, anchor=W) self._spn_range = Spinbox( self, values=RANGES, width=8, wrap=True, fg=PNTCOL, bg=BGCOL, readonlybackground=BGCOL, buttonbackground=BGCOL, textvariable=self._range, state=READONLY, ) self._lbl_option = Label(self, text="Option", fg=FGCOL, bg=BGCOL, anchor=W) self._spn_option = Spinbox( self, values=OPTIONS, width=8, wrap=True, fg=PNTCOL, bg=BGCOL, readonlybackground=BGCOL, buttonbackground=BGCOL, textvariable=self._option, state=READONLY, ) self._canvas.grid(column=0, row=0, columnspan=4, sticky=NSEW) self._lbl_range.grid(column=0, row=1, sticky=EW) self._spn_range.grid(column=1, row=1, sticky=EW) self._lbl_option.grid(column=2, row=1, sticky=EW) self._spn_option.grid(column=3, row=1, sticky=EW) ``` -------------------------------- ### get Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/configuration.html Retrieves the value of an individual configuration setting. Raises a KeyError if the setting name is not found. ```APIDOC ## get ### Description Get individual value. ### Method ```python def get(self, name: str) -> Any: ``` ### Parameters * `name` (str): The name of the setting to retrieve. ### Returns * `Any`: The value of the specified setting. ### Raises * `KeyError`: If the provided `name` does not exist in the configuration settings. ``` -------------------------------- ### Widget Setup and Management Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/app.html Instantiates or destroys a widget based on its state. If the widget should be visible and does not exist, it's created; if it should be hidden and exists, it's destroyed. ```python frm = state[FRAME] visible = state[VISIBLE] w = getattr(self, frm, None) if visible and w is None: setattr( self, frm, state[CLASS](self, self.frm_widgets, borderwidth=1, relief="groove"), ) elif not visible and w is not None: w.grid_forget() w.destroy() w = None setattr(self, frm, None) return getattr(self, frm, None) ``` -------------------------------- ### StatusFrame Widget Setup Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/status_frame.html Sets up the individual Label widgets that will be used to display connection, device, and status information within the status bar frame. ```python def _body(self): """ Set up frame and widgets. """ self.lbl_connection = Label(self, anchor=W, bg=BGCOL) self.lbl_device = Label(self, anchor=W, bg=BGCOL) self.lbl_status = Label(self, anchor=W, bg=BGCOL) ``` -------------------------------- ### MapviewFrame Widget Setup Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/map_frame.html Configures the widgets within the MapviewFrame, including the map canvas and option controls. ```python def _body(self): """ Set up frame and widgets. """ self._canvas = CanvasMap( self.__app, self, height=self.height, width=self.width, bg=BGCOL, ) self._frm_options = Frame(self, bg=BGCOL) self._spn_maptype = Spinbox( self._frm_options, values=MAPTYPES, width=7, wrap=True, textvariable=self._maptype, state=READONLY, background=BGCOL, readonlybackground=BGCOL, foreground=PNTCOL, buttonbackground=BGCOL, ) self._lbl_zoom = Label( self._frm_options, width=5, text="Zoom", bg=BGCOL, fg=PNTCOL ) self._spn_zoom = Spinbox( self._frm_options, from_=MIN_ZOOM, to=MAX_ZOOM, width=4, wrap=False, textvariable=self._mapzoom, state=READONLY, background=BGCOL, readonlybackground=BGCOL, disabledbackground=BGCOL, disabledforeground="grey", foreground=PNTCOL, buttonbackground=BGCOL, ) self._chk_showtrack = Checkbutton( self._frm_options, text=LBLSHOWTRACK, variable=self._showtrack, background=BGCOL, foreground=PNTCOL, ) ``` -------------------------------- ### SocketConfigFrame Widget Body Setup Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/socketconfig_frame.html Sets up the basic Tkinter widgets for server, port, protocol, TLS, and self-sign options within the frame. ```python def _body(self): """ Set up widgets. """ self._frm_basic = Frame(self) self._lbl_server = Label(self._frm_basic, text="Server") self.ent_server = Entry( self._frm_basic, textvariable=self.server, relief="sunken", width=32, ) self._lbl_port = Label(self._frm_basic, text="Port") self.ent_port = Entry( self._frm_basic, textvariable=self.port, relief="sunken", width=6, ) self._lbl_protocol = Label(self._frm_basic, text="Protocol") self._spn_protocol = Spinbox( self._frm_basic, textvariable=self.protocol, values=self._protocol_range, width=12, state=READONLY, wrap=True, ) self._chk_https = Checkbutton(self._frm_basic, variable=self.https, text="TLS") self._chk_selfsign = Checkbutton( self._frm_basic, variable=self.selfsign, text="Self Sign" ) ``` -------------------------------- ### Start Socket Server Thread Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/app.html Initiates a new thread to run the socket server. Configuration parameters are passed to the thread target function. ```python self._socket_thread = Thread( target=self._sockserver_thread, args=( ntripmode, host, port, https, tlspempath, ntriprtcmstr, ntripuser, ntrippassword, SOCKSERVER_MAX_CLIENTS, self.socket_outqueue, ), daemon=True, ) self._socket_thread.start() self.server_status = 0 # 0 = active, no clients ``` -------------------------------- ### Get MQTT Subscription Topics Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/spartn_json_config.html Returns a dictionary containing the MQTT subscription topics for Key, AssistNow, and Data, as configured in the SPARTN JSON. ```python @property def topics(self) -> list: """ Getter for topics. :return: list of topics :rtype: list """ return self._topics ``` -------------------------------- ### Get Connection Status Integer Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/app.html Provides a getter for the integer representation of the connection status. For example, 1 typically indicates a connected state. ```python @property def conn_status(self) -> int: """ Getter for connection status. :return: connection status e.g. 1 = CONNECTED :rtype: int """ return self._conn_status ``` -------------------------------- ### Initialize Connection Buttons Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/settings_child_frame.html Sets up buttons for connecting via socket, file, and disconnecting, along with their associated labels and commands. ```Python self._btn_connect_socket = Button( self._frm_buttons, width=45, height=35, image=self._img_socket, command=lambda: self._on_connect(CONNECTED_SOCKET), state=NORMAL, ) self._lbl_connect_socket = Label(self._frm_buttons, text="TCP/UDP") self._btn_connect_file = Button( self._frm_buttons, width=45, height=35, image=self._img_dataread, command=lambda: self._on_connect(CONNECTED_FILE), state=NORMAL, ) self._lbl_connect_file = Label(self._frm_buttons, text="FILE") self._btn_disconnect = Button( self._frm_buttons, width=45, height=35, image=self._img_disconn, command=lambda: self._on_connect(DISCONNECTED), state=DISABLED, ) self._lbl_disconnect = Label(self._frm_buttons, text="STOP") self._btn_exit = Button( self._frm_buttons, width=45, height=35, image=self._img_exit, command=lambda: self.__app.on_exit(), state=NORMAL, ) ``` -------------------------------- ### Get Next SPARTN Key Details Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/spartn_json_config.html Returns a tuple containing the next SPARTN key, its start datetime, and its end datetime, based on the dynamic key configuration in the JSON. ```python @property def next_key(self) -> tuple: """ Getter for next key. :return: tuple of (key, start date, end date) :rtype: tuple """ return (self._next_key, self._next_start, self._next_end) ``` -------------------------------- ### Initialize Hardware Info Frame Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/hardware_info_frame.html Initializes the Hardware_Info_Frame, setting up references to the application and parent frame, loading necessary icons, and preparing the widget layout. ```python class Hardware_Info_Frame(Frame): """ Hardware & firmware information panel. """ def __init__(self, app: Frame, parent: Frame, *args, **kwargs): """ Constructor. :param Frame app: reference to main tkinter application :param Frame parent: reference to parent frame (config-dialog) :param args: optional args to pass to Frame parent class :param kwargs: optional kwargs to pass to Frame parent class """ self.__app = app # Reference to main application class self.__container = parent self._protocol = kwargs.pop("protocol", "UBX") super().__init__(parent.container, *args, **kwargs) self._img_send = ImageTk.PhotoImage(Image.open(ICON_SEND)) self._img_pending = ImageTk.PhotoImage(Image.open(ICON_PENDING)) self._img_confirmed = ImageTk.PhotoImage(Image.open(ICON_CONFIRMED)) self._img_warn = ImageTk.PhotoImage(Image.open(ICON_WARNING)) self._body() self._do_layout() self.reset() self._attach_events() ``` -------------------------------- ### Set up Settings Widgets Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/settings_frame.html Initializes the main settings child frame and references its sub-frames for serial and socket client settings. ```python def _body(self): """ Set up frame and widgets. """ self.frm_settings = SettingsChildFrame(self.__app, self._frm_container) self.frm_serial = self.frm_settings.frm_serial self.frm_socketclient = self.frm_settings.frm_socketclient ``` -------------------------------- ### Get Current SPARTN Key Details Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/spartn_json_config.html Returns a tuple containing the current SPARTN key, its start datetime, and its end datetime, derived from the dynamic key configuration in the JSON. ```python @property def current_key(self) -> tuple: """ Getter for current key. :return: tuple of (key, start date, end date) :rtype: tuple """ return (self._curr_key, self._curr_start, self._curr_end) ``` -------------------------------- ### Check for Homebrew Python Installation Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/helpers.html Verifies if Python 3 is installed via Homebrew by checking for the existence of the default Homebrew Python 3 executable. This is useful for identifying installations that might cause issues with subprocesses. ```python def brew_installed() -> bool: """ Check if Python installed under Homebrew. Some Python/tkinter installations under Homebrew cause a critical segmentation error when shell subprocesses are invoked. :return: yes/no :rtype: bool """ return path.isfile("/opt/homebrew/bin/python3") ``` -------------------------------- ### Setup Widgets for Hardware Info Frame Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/hardware_info_frame.html Defines and initializes the labels used to display hardware, software, firmware, and protocol version information within the frame. ```python def _body(self): """ Set up frame and widgets. """ self._lbl_hwverl = Label(self, text="Hardware") self._lbl_hwver = Label(self, anchor=W) self._lbl_swverl = Label(self, text="Software") self._lbl_swver = Label(self, anchor=W) self._lbl_fwverl = Label(self, text="Firmware") self._lbl_fwver = Label(self, anchor=W) self._lbl_romverl = Label(self, text="Protocol") self._lbl_romver = Label(self, anchor=W) self._lbl_gnssl = Label(self, text="GNSS/AS") self._lbl_gnss = Label(self, anchor=W) ``` -------------------------------- ### Set up UBX_RATE_Frame Widgets Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/ubx_solrate_frame.html Sets up the labels, spinboxes, and buttons within the UBX_RATE_Frame for user interaction and configuration. ```python def _body(self): """ Set up frame and widgets. """ self._lbl_cfg_rate = Label(self, text=LBLCFGRATE, anchor=W) self._lbl_ubx_measint = Label(self, text="Solution Interval (ms)") self._spn_ubx_measint = Spinbox( self, values=(25, 50, 100, 200, 250, 500, 1000, 2000, 5000, 10000), width=6, state=READONLY, wrap=True, textvariable=self._measint, ) self._lbl_ubx_navrate = Label(self, text="Measurement Ratio") self._spn_ubx_navrate = Spinbox( self, from_=1, to=127, width=4, state=READONLY, wrap=True, textvariable=self._navrate, ) self._lbl_ubx_timeref = Label(self, text="Time Reference") self._spn_ubx_timeref = Spinbox( self, values=list(TIMEREFS.values()), width=5, state=READONLY, wrap=True, textvariable=self._timeref, ) self._lbl_send_command = Label(self, image=self.__container.img_none) self._btn_send_command = Button( self, image=self.__container.img_send, width=50, command=self._on_send_rate, font=self.__app.font_md, ) ``` -------------------------------- ### Get Range of Value Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/helpers.html This function is intended to get the range of a value, but its implementation is incomplete in the provided source. ```python def get_range(val: float, rng: tuple): """ ``` -------------------------------- ### Initialize QGCHandler Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/qgc_handler.html Initializes the QGCHandler with a reference to the main application. It sets up internal references and logging. ```python def __init__(self, app): """ Constructor. :param Frame app: reference to main tkinter application """ self.__app = app # Reference to main application class self.__master = self.__app.appmaster # Reference to root class (Tk) self.logger = logging.getLogger(__name__) self._raw_data = None self._parsed_data = None ``` -------------------------------- ### NTRIPConfigDialog Widget Setup Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/ntrip_client_dialog.html Sets up the main frame and widgets for the NTRIP configuration dialog. This includes creating frames for socket configuration, labels and entry fields for mount point, and listboxes with scrollbars for the source table. ```python def _body(self): """ Set up frame and widgets. """ # pylint: disable=unnecessary-lambda self._frm_body = Frame(self) self._frm_socket = SocketConfigNtripFrame( self.__app, self._frm_body, protocols=[TCPIPV4, TCPIPV6], server_callback=self._on_server, ) self._lbl_mountpoint = Label(self._frm_body, text=LBLNTRIPMOUNT) self._ent_mountpoint = Entry( self._frm_body, textvariable=self._ntrip_mountpoint, state=NORMAL, relief="sunken", width=12, ) self._lbl_mpdist = Label( self._frm_body, width=30, anchor=W, ) self._lbl_sourcetable = Label(self._frm_body, text=LBLNTRIPSTR) self._lbx_sourcetable = Listbox( self._frm_body, height=4, relief="sunken", width=40, ) self._scr_sourcetablev = Scrollbar(self._frm_body, orient=VERTICAL) self._scr_sourcetableh = Scrollbar(self._frm_body, orient=HORIZONTAL) self._lbx_sourcetable.config(yscrollcommand=self._scr_sourcetablev.set) self._lbx_sourcetable.config(xscrollcommand=self._scr_sourcetableh.set) ``` -------------------------------- ### Get Message Mode Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/serialconfig_frame.html Returns the current message parsing mode. The mode is determined by comparing the internal state with predefined constants for SET, POLL, SETPOLL, and GET. ```python if self._msgmode_name.get() == MSGMODED[SET]: return SET if self._msgmode_name.get() == MSGMODED[POLL]: return POLL if self._msgmode_name.get() == MSGMODED[SETPOLL]: return SETPOLL return GET ``` -------------------------------- ### Load Settings from CLI/Environment Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/configuration.html Loads configuration settings from command-line arguments or environment variables. It checks for various parameters like user port, API keys, and MQTT settings. ```python def loadcli(self, **kwargs): """ Load settings from CLI keyword arguments or environment variables. :param dict kwargs: CLI keyword arguments """ arg = kwargs.pop("userport", getenv("PYGPSCLIENT_USERPORT", None)) if arg is not None: self.set("userport_s", arg) arg = kwargs.pop("spartnport", getenv("PYGPSCLIENT_SPARTNPORT", None)) if arg is not None: self.set("spartnport_s", arg) arg = kwargs.pop("mqapikey", getenv("MQAPIKEY", None)) if arg is not None: self.set("mqapikey_s", arg) arg = kwargs.pop("mqttclientid", getenv("MQTTCLIENTID", None)) if arg is not None: self.set("mqttclientid_s", arg) arg = kwargs.pop("spartnkey", getenv("MQTTKEY", None)) if arg is not None: self.set("spartnkey_s", arg) arg = kwargs.pop("spartnbasedate", getenv("SPARTNBASEDATE", None)) if arg is not None: self.set("spartnbasedate_n", int(arg)) arg = kwargs.pop("mqttclientregion", getenv("MQTTCLIENTREGION", None)) if arg is not None: self.set("mqttclientregion_s", arg) arg = kwargs.pop("mqttclientmode", getenv("MQTTCLIENTMODE", None)) if arg is not None: self.set("mqttclientmode_n", int(arg)) arg = kwargs.pop("ntripcasteruser", getenv("NTRIPCASTER_USER", None)) if arg is not None: self.set("ntripcasteruser_s", arg) arg = kwargs.pop("ntripcasterpassword", getenv("NTRIPCASTER_PASSWORD", None)) if arg is not None: self.set("ntripcasterpassword_s", arg) arg = kwargs.pop("tlspempath", getenv(PYGNSSUTILS_PEMPATH, PYGNSSUTILS_PEM)) if arg is not None: self.set("tlspempath_s", arg) arg = kwargs.pop("tlscrtpath", getenv(PYGNSSUTILS_CRTPATH, PYGNSSUTILS_CRT)) if arg is not None: self.set("tlscrtpath_s", arg) ``` -------------------------------- ### Start Stream Read Thread Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/stream_handler.html Starts the background thread responsible for reading data from the configured stream. Clears the stop event and creates a new thread targeting the _read_thread method. ```python def start(self, caller: Frame, settings: dict): """ Start the stream read thread. :param Frame caller: calling Frame :param dict settings: settings dictionary """ self._stopevent.clear() self._stream_thread = Thread( target=self._read_thread, args=( self.__master, self._stopevent, settings, caller.status_label, # for status update messages ), daemon=True, ) self._stream_thread.start() ``` -------------------------------- ### Handle Socket Server Action Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/serverconfig_dialog.html Handles the action when the socket server is started or stopped. It first validates the settings and then determines the appropriate Ntrip RTCM message string based on the receiver type before starting the socket server. ```python def _on_socketserve(self, var, index, mode): """ Action when socket server is started or stopped. """ # pylint: disable=line-too-long if self.valid_settings(): self.status_label = ("", INFOCOL) else: self.status_label = ("ERROR - invalid entry", ERRCOL) return # different receiver models support different RTCM3 output message cohorts if self.receiver_type.get() in (UBLOX_ZEDF9, UBLOX_ZEDX20): ntriprtcmstr = ( "1002(5),1006(5),1010(5),1077(1),1087(1),1097(1),1127(1),1230(1)" ) elif self.receiver_type.get() == UNICORE_UM980: ntriprtcmstr = "1002(10),1006(10),1012(10),1019(10),1020(10),1033(10),1074(1),1084(1),1094(1),1124(1)" elif self.receiver_type.get() == QUECTEL_LCSERIES: ntriprtcmstr = "1005(10),1012(10),1019(10),1020(10),1033(10),1042(10),1046(10),1077(1),1087(1),1097(1),1127(1)" elif self.receiver_type.get() == QUECTEL_LGSERIES: ntriprtcmstr = "1005(10),1012(10),1019(10),1020(10),1033(10),1077(1),1087(1),1097(1),1127(1),1137(1)" elif self.receiver_type.get() == SEPTENTRIO_MOSAIC: ntriprtcmstr = "1006(5),1013(5),1019(5),1020(5),1033(5),1077(1),1087(1),1097(1),1127(1),1230(1)" else: ntriprtcmstr = RTCMSTR if self._socket_serve.get(): # start server self.__app.sockserver_start(ntriprtcmstr) ``` -------------------------------- ### Initialize Device Communication Protocols Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/app.html Serializes and prepares messages for different communication protocols (UBX, SBF, NMEA) before sending them to the device. Includes a delay for connection stabilization and intervals between commands. ```python self.device_label = NA msgs = [] if protocol & UBX_PROTOCOL: msgs.append(UBXMessage("MON", "MON-VER", POLL).serialize()) if protocol & SBF_PROTOCOL: msgs.append(b"SSSSSSSSSS\r\n") msgs.append(b"exeSBFOnce, COM1, ReceiverSetup\r\n") if protocol & UNI_PROTOCOL: msgs.append(b"VERSIONB\r\n") if protocol & NMEA_PROTOCOL: msgs.append(NMEAMessage("P", "QTMVERNO", POLL).serialize()) # pause for n seconds before sending first command to allow connection to stabilise # allow a small interval between individual commands to allow receiver time to process self.send_to_device( msgs, pause=CMDINITDELAY, interval=self.configuration.get("ttydelay_b") * CMDPAUSE, ) ``` -------------------------------- ### Application Update Logic Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/app.html Updates outdated application packages to the latest versions. It checks for updates and uses 'pipx upgrade' or 'pip install --upgrade' based on the installation method. Note that some platforms may block subprocess calls. ```python def do_app_update(self, *args, **kwargs) -> int: # pylint: disable=unused-argument """ Update outdated application packages to latest versions. NB: Some platforms (e.g. Homebrew-installed Python environments) may block Python subprocess calls ('run') on security grounds. :return: return code 0 = error, 1 = OK :rtype: int """ if brew_installed(): self.status_label = (BREWUPDATE, INFOCOL) return 0 self.status_label = (UPDATEINPROG, INFOCOL) updates = [ nam for (nam, current, latest) in check_for_updates() if latest != current ] if len(updates) < 1: return 1 pth = path.dirname(path.abspath(getfile(currentframe()))) if "pipx" in pth: # installed into venv using pipx cmd = [ "pipx", "upgrade", "pygpsclient", ] else: # installed using pip cmd = [ executable, # i.e. python3 or python "-m", "pip", "install", "--upgrade", ] for name in updates: cmd.append(name) result = None try: self.logger.debug(f"{executable=} {pth=} {cmd=}") result = run(cmd, check=True, capture_output=True) self.status_label = (UPDATERESTART, OKCOL) self.logger.debug(result.stdout) return 1 except CalledProcessError as err: ``` -------------------------------- ### ChartviewFrame Widget Body Setup Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/chart_frame.html Sets up the main frame and its widgets, including the canvas for graph display and labels for channel identity, name, scale, min/max Y values, time range, and max points. It configures grid layout for widgets. ```python def _body(self): """ Set up frame and widgets. """ srange, yrange = gen_yrange() # set column and row expand behaviour for i in range(6): self.grid_columnconfigure(i, weight=1, uniform="ent") self.grid_rowconfigure(0, weight=1) for i in range(1, 2 + self._num_chans): self.grid_rowconfigure(i, weight=0) self._canvas = CanvasGraph( self.__app, self, width=self.width, height=self.height, bg=BGCOL ) self._lbl_id = Label( self, text="Identity", fg=LBLCOL, bg=BGCOL, ) self._lbl_name = Label( self, text="Name", fg=LBLCOL, bg=BGCOL, ) self._lbl_scale = Label( self, text="Scale", fg=LBLCOL, bg=BGCOL, ) self._lbl_miny = Label( self, text=MINY.format(""), fg=LBLCOL, bg=BGCOL, ) self._lbl_maxy = Label( self, text=MAXY.format(""), fg=LBLCOL, bg=BGCOL, ) self._lbl_timrange = Label( self, text="Time Range s", fg=LBLCOL, bg=BGCOL, ) self._lbl_maxpoints = Label( self, text="Max Points", fg=LBLCOL, bg=BGCOL, ) self._ent_id = [None] * self._num_chans self._ent_name = [None] * self._num_chans ``` -------------------------------- ### marker property Source: https://www.semuconsulting.com/pygpsclient/pygpsclient.html Gets or sets the marker point. ```APIDOC _property _marker _: Point_ Getter for marker. Returns: marked location Return type: Point ``` -------------------------------- ### Initialize SysmonChart Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/sysmon_frame.html Initializes the system monitor chart by clearing existing data. ```python def init_chart(self): """ Initialise sysmon chart. """ self._canvas.delete(TAG_DATA) ``` -------------------------------- ### pygpsclient.helpers.brew_installed Source: https://www.semuconsulting.com/pygpsclient/pygpsclient.html Determines if the current Python installation is managed by Homebrew. ```APIDOC ## pygpsclient.helpers.brew_installed ### Description Check if Python installed under Homebrew. Some Python/tkinter installations under Homebrew cause a critical segmentation error when shell subprocesses are invoked. ### Returns * **bool** – True if installed under Homebrew, False otherwise. ``` -------------------------------- ### brew_installed Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/helpers.html Determines if Python is installed via Homebrew on macOS. ```APIDOC ## brew_installed ### Description Check if Python installed under Homebrew. Some Python/tkinter installations under Homebrew cause a critical segmentation error when shell subprocesses are invoked. ### Returns - bool: True if Python 3 is found in the Homebrew path, False otherwise. ``` -------------------------------- ### track property Source: https://www.semuconsulting.com/pygpsclient/pygpsclient.html Gets or sets the current track data. ```APIDOC _property _track _: list_ Getter for track. Returns: recorded track Return type: list or None ``` -------------------------------- ### Load Configuration from JSON File Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/configuration.html This method loads configuration settings from a specified JSON file. It handles potential typos like 'mgtt' for 'mqtt' and checks for version compatibility, triggering a resave if the configuration version differs from the current application version. ```python def loadfile(self, filename: str | NoneType = None) -> tuple: """ Load configuration from json file. :param str | NoneType filename: config file name :return: tuple of filename and err message (or "" if OK) :rtype: tuple """ fname, config, err = self.__app.file_handler.load_config(filename) key = "" val = 0 resave = False if err == "": # load succeeded for key, val in config.items(): if key == "version_s" and val != version: resave = True key = key.replace("mgtt", "mqtt") # tolerate "mgtt" typo ``` -------------------------------- ### zoom property Source: https://www.semuconsulting.com/pygpsclient/pygpsclient.html Gets or sets the current zoom level. ```APIDOC _property _zoom _: int_ Getter for zoom. Returns: applied zoom Return type: int or None ``` -------------------------------- ### bounds property Source: https://www.semuconsulting.com/pygpsclient/pygpsclient.html Gets or sets the custom map bounds. ```APIDOC _property _bounds _: Area_ Getter for custom map bounds. Returns: bounds of displayed map Return type: Area or None ``` -------------------------------- ### Initialize SkyviewFrame Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/skyview_frame.html Initializes the SkyviewFrame, setting up the canvas and attaching event bindings for resizing. ```python def __init__(self, app: Frame, parent: Frame, *args, **kwargs): """ Constructor. :param Frame app: reference to main tkinter application :param Frame parent: reference to parent frame :param args: optional args to pass to Frame parent class :param kwargs: optional kwargs to pass to Frame parent class """ self.__app = app # Reference to main application class self.__master = self.__app.appmaster # Reference to root class (Tk) super().__init__(parent, *args, **kwargs) def_w, def_h = WIDGETU2 self.width = kwargs.get("width", def_w) self.height = kwargs.get("height", def_h) self.bg_col = BGCOL self.fg_col = FGCOL self._redraw = True self._waiting = True self._body() self._attach_events() ``` -------------------------------- ### trackbounds property Source: https://www.semuconsulting.com/pygpsclient/pygpsclient.html Gets the bounds and center point of the current track. ```APIDOC _property _trackbounds _: tuple_ Getter for track bounds and center. Returns: tuple of (bounds, center point) Return type: (Area, Point) ``` -------------------------------- ### Set up SkyviewFrame Widgets Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/skyview_frame.html Configures the grid layout for the frame and creates the main canvas widget for displaying the sky view. ```python def _body(self): """ Set up frame and widgets. """ self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self._canvas = CanvasCompass( self.__app, self, MODE_CEL, width=self.width, height=self.height, bg=self.bg_col, ) self._canvas.grid(column=0, row=0, sticky=NSEW) ``` -------------------------------- ### Get Total Count Source: https://www.semuconsulting.com/pygpsclient/_modules/collections.html Calculate the sum of all counts for all elements in the Counter. ```python sum(c.values()) ``` -------------------------------- ### BannerFrame Initialization Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/banner_frame.html Initializes the BannerFrame, setting up references to the main application and its master, and loading necessary image assets for UI elements. ```python class BannerFrame(Frame): """ Banner frame class. """ def __init__(self, app: Frame, parent: Frame, *args, **kwargs): """ Constructor. :param Frame app: reference to main tkinter application :param Frame parent: reference to parent frame :param args: optional args to pass to Frame parent class :param kwargs: optional kwargs to pass to Frame parent class """ self.__app = app # Reference to main application class self.__master = self.__app.appmaster # Reference to root class (Tk) self._status = False self._show_advanced = False self._img_conn = ImageTk.PhotoImage(Image.open(ICON_CONN)) self._img_serial = ImageTk.PhotoImage(Image.open(ICON_SERIAL)) self._img_socket = ImageTk.PhotoImage(Image.open(ICON_SOCKET)) self._img_file = ImageTk.PhotoImage(Image.open(ICON_LOGREAD)) self._img_disconn = ImageTk.PhotoImage(Image.open(ICON_DISCONN)) self._img_expand = ImageTk.PhotoImage(Image.open(ICON_EXPAND)) self._img_contract = ImageTk.PhotoImage(Image.open(ICON_CONTRACT)) self._img_transmit = ImageTk.PhotoImage(Image.open(ICON_TRANSMIT)) self._img_noclient = ImageTk.PhotoImage(Image.open(ICON_NOCLIENT)) self._img_ntrip = ImageTk.PhotoImage(Image.open(ICON_NTRIPCONFIG)) self._img_spartn = ImageTk.PhotoImage(Image.open(ICON_SPARTNCONFIG)) self._img_blank = ImageTk.PhotoImage(Image.open(ICON_BLANK)) super().__init__(parent, *args, **kwargs) self.width, self.height = self.get_size() self._body() self._do_layout() self._attach_events() ``` -------------------------------- ### clientid getter/setter Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/spartn_dialog.html Gets or sets the client ID for the IP client. ```APIDOC ## clientid ### Description Getter and setter for the Client ID from the IP configuration dialog. ### Method `clientid(self) -> str` (getter) `clientid(self, clientid: str)` (setter) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python # Get client ID current_client_id = spartn_dialog_instance.clientid # Set client ID spartn_dialog_instance.clientid = "new_client_id_123" ``` ### Response #### Success Response (200) - **clientid** (str) - The client ID. #### Response Example ```json { "clientid": "current_client_id_abc" } ``` ``` -------------------------------- ### Load Configuration File Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/app.html Loads the application's configuration from a file. It includes checks for active connections and updates widgets and layout upon successful loading. If loading is cancelled, it does nothing. ```python def load_config(self): """ Load configuration file menu option. """ # Warn if Streaming, NTRIP or SPARTN clients are running if self.conn_status == DISCONNECTED and self.rtk_conn_status == DISCONNECTED: self.status_label = ("", OKCOL) else: self.status_label = (DLGSTOPRTK, ERRCOL) return _, err = self.configuration.loadfile() if err == "": # load succeeded self.update_widgets() for frm in ( self.frm_settings.frm_settings, self.frm_settings.frm_serial, self.frm_settings.frm_socketclient, ): frm.reset() self._do_layout() elif err == "cancelled": pass ``` -------------------------------- ### server getter/setter Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/spartn_dialog.html Gets or sets the server address for the IP client. ```APIDOC ## server ### Description Getter and setter for the server address. ### Method `server(self) -> str` (getter) `server(self, server: str)` (setter) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python # Get server current_server = spartn_dialog_instance.server # Set server spartn_dialog_instance.server = "new.server.com" ``` ### Response #### Success Response (200) - **server** (str) - The server address. #### Response Example ```json { "server": "current.server.com" } ``` ``` -------------------------------- ### pygpsclient.helpers.get_point_at_vector Source: https://www.semuconsulting.com/pygpsclient/pygpsclient.html Calculates a new geographical point based on a starting point, distance, and bearing. ```APIDOC ## pygpsclient.helpers.get_point_at_vector ### Description Get new point at vector from start position. ### Parameters * **start** (Point) – starting position * **dist** (float) – vector distance * **bearing** (float) – vector bearing (true) * **radius** (float) – optional radius of sphere, defaults to mean radius of earth ### Returns new position as lat/lon ### Return type Point ``` -------------------------------- ### sockserver_start Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/app.html Starts the socket server thread, which can be used for NTRIP casting or other socket-based communication. ```APIDOC ## sockserver_start ### Description Start socket server thread. ### Method - `ntriprtcmstr` (str): source table string indicating RTCM3 types and intervals ``` -------------------------------- ### Initialize SBFHandler Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/sbf_handler.html Initializes the SBFHandler with a reference to the main application. Sets up internal attributes and logging. ```python def __init__(self, app): """ Constructor. :param Frame app: reference to main tkinter application """ self.__app = app # Reference to main application class self.__master = self.__app.appmaster # Reference to root class (Tk) self.logger = logging.getLogger(__name__) self._cdb = 0 self._raw_data = None self._parsed_data = None # Holds array of current satellites self.gsv_data = {} ``` -------------------------------- ### Open Configuration File Dialog Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/recorder_dialog.html Opens a file dialog for the user to select a configuration file. Supports multiple file types including binary, TTY, u-center UBX, and all files. ```python def _open_configfile(self): """ Open configuration file. """ return self.__app.file_handler.open_file( self, "bin", ( ("config files", "*.bin"), ("TTY config files", "*.tty"), ("u-center UBX config files", "*.txt"), ("all files", "*.*"), ), ) ``` -------------------------------- ### Build TTYPresetDialog Widgets Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/tty_preset_dialog.html Sets up the frame and widgets for the TTY Preset and User-defined configuration command dialog. This includes input fields, checkboxes, listboxes, and buttons. ```python def _body(self): """ Set up frame and widgets. """ self.frm_device_info = Hardware_Info_Frame( self.__app, self, protocol="TTY", borderwidth=2, relief="groove" ) self._frm_body = Frame(self.container, borderwidth=2, relief="groove") self._lbl_command = Label( self._frm_body, text="Command", ) self._ent_command = Entry( self._frm_body, textvariable=self._command, relief="sunken", width=50, ) self._chk_crlf = Checkbutton( self._frm_body, text="CRLF", variable=self._crlf, ) self._chk_echo = Checkbutton( self._frm_body, text="Echo", variable=self._echo, ) self._chk_delay = Checkbutton( self._frm_body, text="Delay", variable=self._delay, ) self._lbl_presets = Label(self._frm_body, text="Preset TTY Commands", anchor=W) self._lbx_preset = Listbox( self._frm_body, border=2, relief="sunken", height=20, width=55, justify=LEFT, exportselection=False, ) self._scr_presetv = Scrollbar(self._frm_body, orient=VERTICAL) self._scr_preseth = Scrollbar(self._frm_body, orient=HORIZONTAL) self._lbx_preset.config(yscrollcommand=self._scr_presetv.set) self._lbx_preset.config(xscrollcommand=self._scr_preseth.set) self._scr_presetv.config(command=self._lbx_preset.yview) self._scr_preseth.config(command=self._lbx_preset.xview) self._lbl_send_command = Label(self._frm_body, image=self.img_none) self._btn_send_command = Button( self._frm_body, image=self.img_send, width=50, command=self._on_send_command, ) ``` -------------------------------- ### Get Canvas Mode Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/canvas_subclasses.html Retrieves the current display mode of the canvas. This is a simple getter property. ```python @property def mode(self) -> str: """ Getter for mode. :return: mode as string :rtype: str """ return self._mode ``` -------------------------------- ### Load Configuration File Source: https://www.semuconsulting.com/pygpsclient/_modules/pygpsclient/file_handler.html Loads a JSON configuration file. Defaults to $HOME/pygpsclient.json or prompts the user if no filename is provided. Validates the configuration upon loading. ```python def load_config(self, filename: Path = CONFIGFILE) -> tuple: """ Load configuration file. If filename is not provided, defaults to $HOME/pygpsclient.json, otherwise user is prompted for path. :param Path filename: fully qualified filename, or None for prompt :return: filename, saved settings as dictionary and any error message :rtype: tuple """ try: if filename is None: filename = self.open_file( None, "config", ( ("config files", "*.json"), ("all files", "*.*"), ), ) if filename is None: return (None, None, "cancelled") # User cancelled with open(filename, "r", encoding="utf-8") as jsonfile: config = json.load(jsonfile) err = self.validate_config(config) if err != "": raise ValueError(err) except (ValueError, OSError, json.JSONDecodeError) as err: return (filename, None, str(err)) return (filename, config, "") ```