Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ In the following, `python3` & `pip` refer to the Python 3 executables. You may n

- Graphical User Interface (window system / manager)
- Python >= 3.10⁴
- Tk (tkinter) >= 8.6⁵ (*tkinter is a commonly used library for developing Graphical User Interfaces (GUI) in Python*)
- Tk (tkinter) >= 8.6⁵ (*tkinter is a commonly used library for developing Graphical User Interfaces (GUI) in Python*)
- Screen resolution >= 640 x 480 (VGA); Ideally 1920 x 1080, though at lower screen resolutions (<= 1024 width), top level dialogs will be resizable and scrollable.

**NB** It is highly recommended to use the latest official [Python.org](https://www.python.org/downloads/) installation package for your platform, rather than any pre-installed version.
Expand All @@ -81,18 +81,18 @@ Normally installs without any additional steps.

### MacOS 13 or later

⁵ The version of Python supplied with some older Apple MacOS platforms includes a [deprecated version of tkinter](https://www.python.org/download/mac/tcltk/) (8.5). Use an official [Python.org](https://www.python.org/downloads/macos) installation package instead.
**NB:** PyGPSClient does ***NOT*** normally require Homebrew or MacPorts to be installed on MacOS (either Intel or Apple Silicon). The Python organisation provides serviceable [64-bit universal MacOS installation packages](https://www.python.org/downloads/macos/) for all current and legacy versions of Python, including release candidates. Note, however, the following caveats:

**NB:** Python does ***NOT*** normally require Homebrew or MacPorts to be installed on MacOS. The Python organisation provides serviceable [64-bit universal installation packages](https://www.python.org/downloads/macos/) for all current and legacy versions of Python, including release candidates.
⁵ The version of Python supplied with some older Apple MacOS platforms includes a [deprecated version of tkinter](https://www.python.org/download/mac/tcltk/) (8.5). Use an official [Python for MacOS](https://www.python.org/downloads/macos) installation package instead.

However, if you wish to install Python using [Homebrew](https://brew.sh/) to take advantage of certain non-default configurations (*e.g. support for sqlite3 extensions*), use the `python-tk` formula rather than `python`, e.g.
⁶ The version of tkinter supplied with the very latest official versions of [Python for MacOS](https://www.python.org/downloads/macos) (`>= 3.14.5`), and with Homebrew formula `python-tk >= 3.12`, include a new version of tkinter (9.0). There appear to be some performance and compatibility issues with this version on MacOS Sonoma and Tahoe which may render the PyGPSClient UI somewhat sluggish or unresponsive. Until this is resolved, MacOS users are recommended to use official Python <= 3.14.4 or Homebrew formula `python-tk = 3.11`.

If you wish to install Python using [Homebrew](https://brew.sh/) to take advantage of certain non-default configurations (*e.g. support for sqlite3 extensions*), use the `python-tk` formula rather than `python`, e.g.

```shell
brew install python-tk@3.13 libspatialite
brew install python-tk@3.11 libspatialite
```

Note also that the Homebrew formulae for python-tk>=3.12 include the latest tkinter 9.0 (rather than 8.6). There are known compatibility issues between tkinter 9.0 and other Python packages (*e.g. ImageTk*) on some platform configurations, which may result in PyGPSClient being unable to load. If you encounter these issues, consider using `brew install python-tk@3.11` or an official [Python.org](https://www.python.org/downloads/macos) installation package instead.

Note that on MacOS, serial ports may appear as `/dev/tty*` *or* `/dev/cu*`. To understand the differences between the two, see [here](https://www.codegenes.net/blog/what-s-the-difference-between-dev-tty-and-dev-cu-on-macos/).

### Linux (including Raspberry Pi OS)
Expand Down Expand Up @@ -128,7 +128,7 @@ Add your user to this group using usermod (*you will need to log out and in agai
sudo usermod -aG dialout myuser
```

For Debian-based platforms, the group is normally `dialout` or `tty`; on Arch-based platforms it is normally `uucp`.
For Debian-based platforms, the group is normally `dialout`; on Arch-based platforms it is normally `uucp`.

Other than this, no special privileges are required.

Expand Down
57 changes: 34 additions & 23 deletions README.md

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# PyGPSClient Release Notes

### RELEASE 1.7.1

FIXES:

1. Fix 'memory leak' issue (socket server message queue filling up when server not active).
1. Internal refactoring and performance enhancements to improve long-term UI responsiveness and stability.
1. Fix u-blox preset configuration validation which was preventing some valid preset commands.

ENHANCEMENTS:

1. Split UBX Configuration panel into two separate panels - one for modern (Gen9+) receivers and one for legacy (Gen8-) receivers. The intention is to make the panels easier to use on lower-resolution or scaled displays.
1. Display UBX NAV-DAHEADING data from dual-antenna receivers such as the u-blox ZED-X20D in the "Rover Plot" widget.
1. Update portid designation in System Monitor I/O widget (X20 assigns different MON-COMMS.portids to F9P).
1. Add satellite filter entry field to RINEX conversion dialog (requires pygnssutils>=1.2.6).
1. "Meta Only" parsing mode added. When enabled, incoming raw data will *only* be parsed for basic metadata (protocol, identity, length) rather than individual data attributes. This mode is significantly quicker than full parsing, but individual data attributes *will no longer be available* and PyGPSClient's widgets other than the console will *not* be updated. Utilises the new [GNSSReader](https://github.com/semuconsulting/pygnssutils#gnssreader) `parsing=2` argument. Requires pygnssutils>=1.2.6.

### RELEASE 1.7.0

1. Can now double-click to toggle between 'hae' (height above ellipsoid) and 'sep' (separation) values in banner panel.
Expand Down
8 changes: 8 additions & 0 deletions docs/pygpsclient.rst
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,14 @@ pygpsclient.ubx\_handler module
:undoc-members:
:show-inheritance:

pygpsclient.ubx\_legacy\_config\_dialog module
----------------------------------------------

.. automodule:: pygpsclient.ubx_legacy_config_dialog
:members:
:undoc-members:
:show-inheritance:

pygpsclient.ubx\_msgrate\_frame module
--------------------------------------

Expand Down
Binary file modified images/rover_widget.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions pygpsclient.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
"gpxmapzoom_n": 10,
"gpxtype_s": "track",
"showtrack_b": 0,
"legend_b": 1,
"unusedsat_b": 0,
"legend_b": true,
"unusedsat_b": false,
"datalog_b": 0,
"logformat_s": "Binary",
"logpath_s": "",
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ classifiers = [
dependencies = [
"requests>=2.34.0",
"Pillow>=12.0.0",
"pygnssutils>=1.2.5",
"pygnssutils>=1.2.5", # TODO requires >=1.2.6 when available
"pyunigps>=1.0.0",
"pynmeagps>=1.1.5",
"pyubx2>=1.3.3",
"pyubx2>=1.3.5",
]

[project.scripts]
Expand Down
15 changes: 7 additions & 8 deletions src/pygpsclient/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

from argparse import SUPPRESS, ArgumentDefaultsHelpFormatter, ArgumentParser
from logging import getLogger
from tkinter import Tk

from pygnssutils import (
VERBOSITY_CRITICAL,
Expand Down Expand Up @@ -118,8 +117,8 @@ def main():
f"{VERBOSITY_CRITICAL} = critical, "
f"{VERBOSITY_LOW} = low (error), "
f"{VERBOSITY_MEDIUM} = medium (warning), "
f"{VERBOSITY_HIGH} = high (info), {VERBOSITY_DEBUG} = debug, "
f"default = {VERBOSITY_CRITICAL}"
f"{VERBOSITY_HIGH} = high (info), "
f"{VERBOSITY_DEBUG} = debug"
),
type=int,
choices=[
Expand All @@ -129,7 +128,7 @@ def main():
VERBOSITY_DEBUG,
VERBOSITY_CRITICAL,
],
default=VERBOSITY_LOW,
default=VERBOSITY_CRITICAL,
)
ap.add_argument(
"--logtofile",
Expand All @@ -139,18 +138,18 @@ def main():
kwargs = vars(ap.parse_args())

# set up global logging configuration
verbosity = int(kwargs.pop("verbosity", VERBOSITY_LOW))
verbosity = int(kwargs.pop("verbosity", VERBOSITY_CRITICAL))
logtofile = kwargs.pop("logtofile", "")
logger = getLogger(APPNAME) # "pygpsclient"
logger_utils = getLogger("pygnssutils")
logger_pyubx2 = getLogger("pyubx2")
for logr in (logger, logger_utils, logger_pyubx2):
set_logging(logr, verbosity, logtofile)

root = Tk()
App(root, **kwargs)
root.mainloop()
app = App(**kwargs)
app.mainloop()


if __name__ == "__main__":

main()
2 changes: 1 addition & 1 deletion src/pygpsclient/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
:license: BSD 3-Clause
"""

__version__ = "1.7.0"
__version__ = "1.7.1"
35 changes: 18 additions & 17 deletions src/pygpsclient/about_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,10 @@ def __init__(self, app, *args, **kwargs): # pylint: disable=unused-argument
"""
Initialise Toplevel dialog

:param Frame app: reference to main tkinter application
:param Tk 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._img_icon = ImageTk.PhotoImage(Image.open(ICON_APP128).resize((64, 64)))
self._img_github = ImageTk.PhotoImage(Image.open(ICON_GITHUB).resize((32, 32)))
Expand Down Expand Up @@ -190,7 +189,7 @@ def _on_github(self, *args, **kwargs): # pylint: disable=unused-argument
"""

if brew_installed():
self.status_label = (BREWWARN, INFOCOL)
self.set_status_label(BREWWARN, INFOCOL)
return

open_new_tab(GITHUB_URL)
Expand All @@ -202,7 +201,7 @@ def _on_sponsor(self, *args, **kwargs): # pylint: disable=unused-argument
"""

if brew_installed():
self.status_label = (BREWWARN, INFOCOL)
self.set_status_label(BREWWARN, INFOCOL)
return

open_new_tab(SPONSOR_URL)
Expand All @@ -214,7 +213,7 @@ def _on_license(self, *args, **kwargs): # pylint: disable=unused-argument
"""

if brew_installed():
self.status_label = (BREWWARN, INFOCOL)
self.set_status_label(BREWWARN, INFOCOL)
return

open_new_tab(LICENSE_URL)
Expand All @@ -225,7 +224,7 @@ def _check_for_update(self, *args, **kwargs): # pylint: disable=unused-argument
Check for updates.
"""

self.status_label = ("Checking for updates...", INFOCOL)
self.set_status_label("Checking for updates...", INFOCOL)
versions = check_for_updates()
for i, (nam, current, latest) in enumerate(versions):
txt = f"{nam}: {current}"
Expand All @@ -238,30 +237,30 @@ def _check_for_update(self, *args, **kwargs): # pylint: disable=unused-argument
else:
txt += f" - Latest version is {latest}"
col = ERRCOL
self._lbl_lib_versions[i].config(text=txt, fg=col)

self._lbl_lib_versions[i]["text"] = txt
self._lbl_lib_versions[i]["fg"] = col
updates = [nam for (nam, current, latest) in versions if latest != current]
if len(updates) > 0:
self.status_label = ("Updates available", OKCOL)
self.set_status_label("Updates available", OKCOL)
self._set_update_btn_mode(True)
else:
self.status_label = ("No updates available", INFOCOL)
self.set_status_label("No updates available", INFOCOL)

def _do_update(self, *args, **kwargs): # pylint: disable=unused-argument
"""
Run python update.
"""

if brew_installed():
self.status_label = (BREWUPDATE, INFOCOL)
self.set_status_label(BREWUPDATE, INFOCOL)
return

self.status_label = (UPDATEINPROG, INFOCOL)
self.set_status_label(UPDATEINPROG, INFOCOL)
rc = self.__app.do_app_update()
if rc:
self.status_label = (UPDATERESTART, OKCOL)
self.set_status_label(UPDATERESTART, OKCOL)
else:
self.status_label = (UPDATEERR.format(err=rc), ERRCOL)
self.set_status_label(UPDATEERR.format(err=rc), ERRCOL)
self._set_update_btn_mode(False)

def _set_update_btn_mode(self, update: bool):
Expand All @@ -272,9 +271,11 @@ def _set_update_btn_mode(self, update: bool):
"""

if update:
self._btn_checkupdate.config(text="UPDATE", fg=OKCOL)
self._btn_checkupdate["text"] = "UPDATE"
self._btn_checkupdate["fg"] = OKCOL
self._btn_checkupdate.bind("<Button>", self._do_update)
else:
self._btn_checkupdate.config(text="CHECK FOR UPDATES", fg=INFOCOL)
self._btn_checkupdate["text"] = "CHECK FOR UPDATES"
self._btn_checkupdate["fg"] = INFOCOL
self._btn_checkupdate.bind("<Button>", self._check_for_update)
self.update_idletasks()
self.__app.update_idletasks()
Loading