diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b412b6dd..dcaa21b2 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -33,19 +33,16 @@ Steps to reproduce the behaviour: A clear and concise description of what you expected to happen. -**Desktop (please complete the following information):** +**System Information (please complete the following information):** -- The operating system you're using [e.g. Windows 11, MacOS Ventura, Ubuntu Kinetic] -- The version of Python you're using (e.g. Python 3.11.1) +- The operating system you're using [e.g. Windows 11, MacOS 27.0, Ubuntu 26.04] +- The version of Python and Tkinter you're using (e.g. Python 3.14.7, Tkinter 9.0.4) - The type of serial connection [e.g. USB, UART1] - -**GNSS/GPS Device (please complete the following information as best you can):** - - Device Model/Generation: [e.g. u-blox ZED-F9P] - Firmware Version: [e.g. HPG 1.32] - Protocol: [e.g. 32.00] -This information is typically output by the device at startup via a series of NMEA TXT messages. It can also be found by polling the device with a UBX MON-VER message. If you're using the PyGPSClient GUI, a screenshot of the UBXConfig window should suffice. +**FYI** Click on ![clipboard icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-clipboard-13-24.png?raw=true) in the About dialog (Menu..Help..About) to copy the current system information and package versions to the clipboard e.g. for inclusion in bug reports or discussion queries. **Additional context** diff --git a/.github/workflows/checkpr.yml b/.github/workflows/checkpr.yml index 0117a420..950e760e 100644 --- a/.github/workflows/checkpr.yml +++ b/.github/workflows/checkpr.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15.0-rc.2"] steps: - uses: actions/checkout@v7 @@ -20,6 +20,8 @@ jobs: run: pip install --group deploy - name: Install code dependencies run: pip install . + - name: Install dill 0.4.2.dev for Python 3.15 + run: pip install --force-reinstall git+https://github.com/uqfoundation/dill.git - name: Lint with pylint run: pylint -E src - name: Scan security vulnerabilities with bandit diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9393881e..1b460285 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15.0-rc.2"] steps: - uses: actions/checkout@v7 @@ -22,6 +22,8 @@ jobs: run: pip install --group deploy - name: Install code dependencies run: pip install . + - name: Install dill 0.4.2.dev for Python 3.15 + run: pip install --force-reinstall git+https://github.com/uqfoundation/dill.git - name: Lint with pylint run: pylint -E src - name: Security vulnerability analysis with bandit diff --git a/.vscode/settings.json b/.vscode/settings.json index 58b12cc0..b4073cae 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,7 +22,7 @@ "python.analysis.addHoverSummaries": false, "python-envs.defaultEnvManager": "ms-python.python:venv", "python.venvPath": "${userHome}/pygpsclient/bin/python3", - "python.systemPath": "/usr/local/bin/python3.14", + "python.systemPath": "/usr/local/bin/python3.15t", // free-threading version "python.defaultInterpreterPath": "${userHome}/pygpsclient/bin/python3", //"python.defaultInterpreterPath": "C:/Users/steve/pygpsclient/Scripts/python.exe", "python.testing.unittestEnabled": false, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ae4ac50a..90ed30d1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -75,6 +75,19 @@ ], "problemMatcher": [], }, + { + "label": "Install dill 0.4.2.dev for Python 3.15", + "type": "shell", + "command": "${config:python.defaultInterpreterPath}", + "args": [ + "-m", + "pip", + "install", + "--force-reinstall", + "git+https://github.com/uqfoundation/dill.git", + ], + "problemMatcher": [], + }, { "label": "Clean", "type": "shell", @@ -167,6 +180,7 @@ //"Create Venv", "Install Deploy Dependencies", "Install Optional Dependencies", + "Install dill 0.4.2.dev for Python 3.15", "Clean", "Sort Imports", "Format", diff --git a/INSTALLATION.md b/INSTALLATION.md index cc56c497..24169f90 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -85,7 +85,7 @@ Normally installs without any additional steps. ⁵ 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. -⁶ 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`. +⁶ 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 have been reports of compatibility issues with tkinter 9.0 with certain platform configurations which *may* render the PyGPSClient UI somewhat sluggish or unresponsive. If you experience any issues, consider using an 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. diff --git a/README.md b/README.md index 1000b232..bbad6ef5 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ [NTRIP Caster/Socket Server](#socketserver) | [GPX Track Viewer](#gpxviewer) | [RINEX Conversion](#rinex) | +[About](#about) | [Mapquest API Key](#mapquestapi) | [User-defined Presets](#userdefined) | [CLI Utilities](#cli) | @@ -20,7 +21,7 @@ [Author Information](#author) PyGPSClient is a free, open-source, multi-platform graphical GNSS/GPS testing, diagnostic and configuration application written entirely by volunteers in Python and tkinter. -* Runs on any platform which supports a Python 3 interpreter (>=3.10) and tkinter (>=8.6) GUI framework, including Windows, MacOS and Linux. +* Runs on any Windows, MacOS or Linux platform which supports a Python 3 interpreter (>=3.10) and tkinter (>=8.6) GUI framework, including popular single-board computers like Raspberry Pi, BeagleBoard and Jetson. * Supports NMEA, UBX (u-blox binary), SBF (Septentrio binary), UNI (Unicore binary), QGC (Quectel binary), RTCM3, SPARTN, NTRIP and TTY (ASCII text) protocols¹. * Capable of reading from a variety of GNSS data streams: Serial (USB / UART), Socket (TCP / UDP), binary data stream (terminal or file capture) and binary recording (e.g. u-center \*.ubx). * Provides [NTRIP client](#ntripconfig) facilities for both RTCM3 and SPARTN NTRIP services. @@ -141,9 +142,9 @@ For more comprehensive installation instructions, please refer to [INSTALLATION. pygpsclient -C myconfig.json ``` -#### Checking for the latest version +#### About Dialog and Checking for the latest version -16. The About dialog (Menu..Help..About) includes a facility to check the latest available versions of PyGPSClient and its subsidiary modules, and initiate an automatic update. Tick the 'Check on startup' box to perform this check on startup (*note that this requires internet access, which may result in slower startup times on platforms with low bandwidth / high latency internet connections*). The facility may be unavailable in certain Homebrew-installed Python environments due to security constraints. The application must be closed and restarted for any update to take effect. +16. The [About dialog (Menu..Help..About)](#about) displays current homepage, sponsor, license and system information. It includes a facility to check the latest available versions of PyGPSClient and its subsidiary packages and, if any updates are available, initiate an automatic update. The facility may be unavailable in certain Homebrew-installed Python environments due to security constraints. #### Datalogging, GPX Track Recording and Database @@ -182,7 +183,9 @@ For more comprehensive installation instructions, please refer to [INSTALLATION. #### GUI refresh rate setting -31. PyGPSClient processes all incoming GNSS data in 'real time' but, by default, the GUI is only refreshed every 0.5 seconds. The refresh rate can be manually configured via the `guiupdateinterval_f` setting in the json configuration file or Menu..Options..App Configuration dialog (*save configuration and restart after any changes*). **NB:** PyGPSClient may become unresponsive on slower platforms (e.g. Raspberry Pi and similar SBCs) at high message rates if the GUI update interval is less than 0.2 seconds - set a value commensurate with the performance of your platform. +31. PyGPSClient processes all incoming GNSS data in 'real time' but, by default, the GUI is only refreshed every 500 ms. The refresh rate can be manually configured via the `guiupdateinterval_f` setting in the json configuration file or Menu..Options..App Configuration dialog (*save configuration and restart after any changes*). + + **NB:** The GUI refresh rate should be set to a value commensurate with the performance of your platform. For example, on slower platforms (*e.g. Raspberry Pi 4 and similar Single Board Computers*) a value of less than 250 ms may result in the GUI becoming sluggish or unresponsive. On more capable platforms (*e.g. MacBook Pro 10-core M1 Max with [Python free threading](https://docs.python.org/3/howto/free-threading-python.html) enabled*), the refresh rate can be happily set to 100 ms. #### Toplevel ('pop-up') dialog setting @@ -197,7 +200,7 @@ For more comprehensive installation instructions, please refer to [INSTALLATION. | Widget | To show or hide the various widgets, go to Menu..View and click on the relevant hide/show option. | |---------------------------|---------------------------------------------------------------------------------------------------| |![banner widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/banner_widget.png?raw=true)| Expandable banner showing key navigation status information based on messages received from receiver. To expand or collapse the banner or serial port configuration widgets, click the ![expand icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-arrow-80-16.png?raw=true)/![expand icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-triangle-1-16.png?raw=true) buttons. Double-click the "hae" label or value to toggle between hae (height above ellipsoid) and sep (separation) values. **NB**: some fields (e.g. hdop/vdop, hacc/vacc) are only available from proprietary NMEA or UBX messages and may not be output by default. The minimum messages required to populate all available fields are: NMEA: GGA, GSA, GSV, RMC, UBX00 (proprietary); UBX: NAV-DOP, NAV-PVT, NAV-SAT; UNI: BESTNAV, SATSINFO, STADOP. | -|![console widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/console_widget.png?raw=true)| Configurable serial console widget showing incoming GNSS data streams in either parsed, binary or tabular hexadecimal formats. Double-right-click to copy contents of console to the clipboard. The scroll behaviour and number of lines retained in the console can be configured via the settings panel. Supports user-configurable color tagging of selected strings for easy identification. Color tags are loaded from the `"colortag_b":` value (`0` = disable, `1` = enable) and `"colortags_l":` list (`[string, color]` pairs) in your json configuration file (see example provided). If color is set to "HALT", streaming will halt on any match and a warning displayed. NB: color tagging does impose a small performance overhead - turning it off will improve console response times at very high transaction rates.| +|![console widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/console_widget.png?raw=true)| Configurable serial console widget showing incoming GNSS data streams in either parsed, binary or tabular hexadecimal formats. Click ![clipboard icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-clipboard-13-24.png?raw=true) to copy contents to the clipboard. The scroll behaviour and number of lines retained in the console can be configured via the settings panel. Supports user-configurable color tagging of selected strings for easy identification. Color tags are loaded from the `"colortag_b":` value (`0` = disable, `1` = enable) and `"colortags_l":` list (`[string, color]` pairs) in your json configuration file (see example provided). If color is set to "HALT", streaming will halt on any match and a warning displayed. NB: color tagging does impose a small performance overhead - turning it off will improve console response times at very high transaction rates.| |![skyview widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/skyview_widget.png?raw=true)| Skyview widget showing current satellite visibility and position (elevation / azimuth). Satellite icon borders are colour-coded to distinguish between different GNSS constellations. For consistency between NMEA and UBX data sources, will display GLONASS NMEA SVID (65-96) rather than slot (1-24). A manually-edited boolean configuration parameter `unusedsat_b` determines whether or not to include satellites where CNo = 0; default = 0. | |![levelsview widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/graphview_widget.png?raw=true)| Levels view widget showing current satellite carrier-to-noise (C/No) levels for each GNSS constellation. Double-click to toggle legend.| |![signalsview widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/signalsview_widget.png?raw=true)| Signals view widget showing current svid/signal carrier-to-noise (C/No) level and (where applicable) correction source for each GNSS svid/signal received (*GNSS receiver must be capable of outputting UBX NAV-SIG messages*). Signal identifiers are in RINEX format e.g. `L1_C/A`, `E5_aQ`, etc. Double-click to toggle legend. Double-right-click to toggle signals where C/No = 0 dbHz. | @@ -209,7 +212,7 @@ For more comprehensive installation instructions, please refer to [INSTALLATION. |![sysmon widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/sysmon_widget.png?raw=true)| System Monitor widget showing device cpu, memory and I/O utilisation (*GNSS receiver must be capable of outputting UBX MON-SYS/MON-COMMS or SBF ReceiverStatus messages*). Tick checkbox to toggle between actual (cumulative) I/O stats and pending I/O. Primarily intended for u-blox modules, but can display limited system information for other devices. | |![scatterplot widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/scatterplot_widget.png?raw=true)| Scatterplot widget showing variability in position reporting over time. (Optional) Enter fixed reference position. Select Average to center plot on dynamic average position (*displayed at top left*), or Fixed to center on fixed reference position (*if entered*). Check Autorange to set plot range automatically. Set the update interval (e.g. 4 = every 4th navigation solution). Use the range slider or mouse wheel to adjust plot range. Right-click to set fixed reference point to the current mouse cursor position. Double-click to clear the existing data. | |![rover widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/rover_widget.png?raw=true) | Relative Position (formerly 'Rover') widget plots relative 2D bearing, distance, track and status information for the following configurations: (i) NAV-RELPOSNED - position of roving receiver relative to a fixed or moving RTK base (including an NTRIP mountpoint); (ii) RTCM 1005/6 - position of roving receiver relative to a fixed RTK NTRIP antenna reference point (ARP); (iii) NAV-DAHEADING - static heading ('yaw') from a dual-antenna receiver e.g. u-blox ZED-X20D. Double-click to clear existing plot. | -|![chart view](https://github.com/semuconsulting/PyGPSClient/blob/main/images/chart_widget.png?raw=true) | Chart widget acts as a multi-channel "plotter", allowing the user to plot a series of named numeric data attributes from any parsed GNSS data source, with configurable y (value) and x (time) axes. By default, the number of channels is set to 4, but this can be manually edited by the user via the json configuration file setting `chartsettings_d["numchn_n"]` or Menu..Options..App Configuration dialog (*save configuration and restart after any changes*). For each channel, user can select: (*optional*) identity of message source e.g. `NAV-PVT`; attribute name e.g. `hAcc`; scaling factor (divisor) e.g. 1000; y axis range e.g. 0 - 5. Wildcards are available for attribute groups - "\*" (average of group values), "+" (maximum of group values), "-" (minimum of group values) e.g. `cno*` will plot the average `cno` value for a group of satellites. Double-click to clear the existing data. Double-right-click to save the current chart data to the clipboard in CSV format, which can be directly pasted into a spreadsheet application. | +|![chart view](https://github.com/semuconsulting/PyGPSClient/blob/main/images/chart_widget.png?raw=true) | Chart widget acts as a multi-channel "plotter", allowing the user to plot a series of named numeric data attributes from any parsed GNSS data source, with configurable y (value) and x (time) axes. By default, the number of channels is set to 4, but this can be manually edited by the user via the json configuration file setting `chartsettings_d["numchn_n"]` or Menu..Options..App Configuration dialog (*save configuration and restart after any changes*). For each channel, user can select: (*optional*) identity of message source e.g. `NAV-PVT`; attribute name e.g. `hAcc`; scaling factor (divisor) e.g. 1000; y axis range e.g. 0 - 5. Wildcards are available for attribute groups - "\*" (average of group values), "+" (maximum of group values), "-" (minimum of group values) e.g. `cno*` will plot the average `cno` value for a group of satellites. Double-click to clear the existing data. Click ![clipboard icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-clipboard-13-24.png?raw=true) to copy the current chart data to the clipboard in CSV format, which can be directly pasted into a spreadsheet application. | |![attitude widget](https://github.com/semuconsulting/PyGPSClient/blob/main/images/attitude_widget.png?raw=true) | Attitude Monitor widget (*formerly "IMU Monitor"*) showing current orientation/attitude (roll, pitch, yaw *aka 'static heading'*) and status from a variety of IMU, Dead Reckoning, Dual Antenna or other 2D/3D attitude message sources. Select range in degrees (from ±1 to ±180 degrees). | --- @@ -416,7 +419,7 @@ The GPX Track Viewer can display any valid GPX file containing track point (`trk Click ![refresh icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-refresh-lined-24.png?raw=true) to refresh the display after any changes (e.g. resizing, zooming or change of units). The location marker indicates the nominal center point of the track. --- -## RINEX Conversion +## RINEX Conversion (Beta) ![rinex screenshot](https://github.com/semuconsulting/PyGPSClient/blob/main/images/rinex_dialog.png?raw=true) @@ -439,6 +442,17 @@ The RINEX Conversion Dialog supports the conversion of raw observation, navigati 9. Click ![start icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-media-control-48-24.png?raw=true) to process the file. A progress bar will be displayed and, when complete, the output file names (*.rnx) and record counts will be displayed at the foot of the dialog. 10. Processing can be cancelled by clicking ![cancel icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-x-mark-9-24.png?raw=true). +--- +## About (System Information) + +![about screenshot](https://github.com/semuconsulting/PyGPSClient/blob/main/images/about_dialog.png?raw=true) + +The About dialog displays homepage, sponsor and license links along with system information which can be copied to the clipboard e.g. for inclusion in bug reports or discussion queries. + +- Click ![info icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-info-lined-24.png?raw=true) to refresh the system information. +- Click ![update icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-refresh-1-24.png?raw=true) to initiate a Python application package update (if available). The application must be closed and restarted for any update to take effect. +- Click on ![clipboard icon](https://github.com/semuconsulting/PyGPSClient/blob/main/src/pygpsclient/resources/iconmonstr-clipboard-13-24.png?raw=true) to copy the current system information and package versions to the clipboard e.g. for inclusion in bug reports or discussion queries. + --- ## MapQuest API Key @@ -516,7 +530,7 @@ For further details, refer to the `pygnssutils` homepage at [https://github.com/ --- ## Troubleshooting and Known Issues -1. Recent versions of Python for MacOS (>=3.14.5) come with a new version of tkinter (9.0). Early iterations (3.14.5, 3.14.6) exhibited various performance issues on MacOS. The issues appear to have been resolved in version >=3.14.7 (tkinter 9.0.4), but if you experience any compatibility or performance problems, consider reverting to Python <=3.14.4 (tkinter 8.6). The issues do *not* appear to affect other operating systems or Python apps not using tkinter. +1. Recent versions of Python for MacOS (>=3.14.5) come with a new version of tkinter (9.0). Early iterations (3.14.5, 3.14.6) exhibited various performance issues. The issues appear to have been resolved in version >=3.14.7 (tkinter 9.0.4), but if you experience any compatibility or performance problems, consider reverting to Python <=3.14.4 (tkinter 8.6). 2. If you encounter persistent `WARNING>>Error parsing data stream Serial stream terminated unexpectedly` messages in the console, this may be indicative of insufficient serial port bandwidth (baudrate or timeout) for the current output message cohort (*particularly if this includes raw Ephemerides or Observation data*). Try increasing the baudrate in the first instance. @@ -528,6 +542,8 @@ For further details, refer to the `pygnssutils` homepage at [https://github.com/ 6. Some Homebrew-installed Python environments on MacOS can give rise to critical segmentation errors (*illegal memory access*) when shell subprocesses are invoked, due to MacOS security constraints. For this reason, application updates via the About..Update button are disabled on MacOS Homebrew environments; use the CLI `python3 -m pip install --upgrade pygpsclient` command instead. +7. **FYI** Free-threading mode (*i.e. with GIL or global interpreter lock disabled*), where supported by the platform, may afford a worthwhile performance improvement (since a number of PyGPSClient background processes run in separate threads) [but note caveats](https://docs.python.org/3/howto/free-threading-python.html). The Python 3.15 free-threading interpreter (`python3.15t`) is now installed by default on MacOS platforms and is available as an option in Python>=3.13 on other platforms. + --- ## License diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 109b31ce..c21462c4 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,12 @@ # PyGPSClient Release Notes +### RELEASE 1.7.7 + +1. Refactor About dialog. Add scrollable system info text box and ability to copy system info to clipboard e.g. for inclusion in Bug Reports or Discussion queries. +1. Rationalise customer tkinter classes into single custom_classes.py module. +1. Python 3.15.0-rc.2 (GIL and free-threading) added to build and test workflows. +1. Dependency versions updated (includes minor performance enhancements. + ### RELEASE 1.7.6 1. Widget performance improvements. @@ -7,6 +14,13 @@ 1. Update github actions to latest versions. 1. Add Chart plotter channels to App Configuration dialog. +The PyGPSClient repo has been rebased following this release. The following contributions to older versions are acknowledged with thanks: + +- @davidtlascelles - Jun 25, 2025 - Set N/A for invalid precision and accuracy values. +- @Williangalvani - Feb 13, 2024 - Send an empty datagram to establish connection to udp servers. +- @nmichaels-qualinx - Mar 24, 2023 - Add a scatterplot view. +- @NicholasZolton - Jul 21, 2022 - Updated README to reflect About section. + ### RELEASE 1.7.5 FIXES: diff --git a/docs/conf.py b/docs/conf.py index 6fd529dc..95322b40 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,11 +12,15 @@ # import os import sys +try: + GIL = "" if sys._is_gil_enabled() else "t" +except ImportError: + GIL = "" # get path to site-packages (source) folder within venv pypath = ( f"{os.path.expanduser("~")}/pygpsclient/lib/python" - f"{sys.version_info.major}.{sys.version_info.minor}/site-packages" + f"{sys.version_info.major}.{sys.version_info.minor}{GIL}/site-packages" ) print(f"\n\033[1mUsing absolute path:\033[0m \033[95m{pypath}\033[0m\n") sys.path.insert(0, os.path.abspath(pypath)) @@ -26,7 +30,7 @@ # -- Project information ----------------------------------------------------- project = "PyGPSClient" -copyright = "2021 semuadmin (Steve Smith)" +copyright = "2020-2026 semuadmin (Steve Smith)" author = "semuadmin (Steve Smith)" # The full version, including alpha/beta/rc tags diff --git a/docs/pygpsclient.rst b/docs/pygpsclient.rst index 6e6fb40c..0c9bb308 100644 --- a/docs/pygpsclient.rst +++ b/docs/pygpsclient.rst @@ -52,14 +52,6 @@ pygpsclient.canvas\_map module :undoc-members: :show-inheritance: -pygpsclient.canvas\_subclasses module -------------------------------------- - -.. automodule:: pygpsclient.canvas_subclasses - :members: - :undoc-members: - :show-inheritance: - pygpsclient.chart\_frame module ------------------------------- @@ -92,6 +84,14 @@ pygpsclient.console\_frame module :undoc-members: :show-inheritance: +pygpsclient.custom\_classes module +---------------------------------- + +.. automodule:: pygpsclient.custom_classes + :members: + :undoc-members: + :show-inheritance: + pygpsclient.dialog\_state module -------------------------------- diff --git a/examples/python_compile.sh b/examples/python_compile.sh index 5d4e2cdc..bf50b380 100644 --- a/examples/python_compile.sh +++ b/examples/python_compile.sh @@ -24,9 +24,11 @@ set -e # set required Python major and minor version e.g. 3.13.10 -PYVER="3.14.3" +PYVER="3.14.7" # NB: uncomment this line to install this version alongside existing versions # ALTINSTALL=1 +# NB: uncomment this line to disable GIL for versions that support free-threading (>=13.3) +# DISABLEGIL=1 # download and unzip source code sudo apt install vim wget screen -y @@ -34,18 +36,30 @@ wget https://www.python.org/ftp/python/${PYVER}/Python-${PYVER}.tgz tar zvxf Python-${PYVER}.tgz # enable the Debian source repos -SRCDEB="/etc/apt/sources.list" +SRCDEBOLD="/etc/apt/sources.list" +SRCDEB="/etc/apt/sources.list.d/debian.sources" SRCUBU="/etc/apt/sources.list.d/ubuntu.sources" -# Debian, including Raspberry Pi OS: +SRCRPI="/etc/apt/sources.list.d/raspi.sources" +# Legacy Debian: +if test -f $SRCDEBOLD +then +sudo sed -i -e 's/#deb-src/deb-src/g' $SRCDEBOLD +fi +# Debian, including Trixie: if test -f $SRCDEB then -sudo sed -i -e 's/#deb-src/deb-src/g' $SRCDEB +sudo sed -i 's/^Types: deb$/Types: deb deb-src/' $SRCDEB fi # Ubuntu, including 24.04 LTS: if test -f $SRCUBU then sudo sed -i 's/^Types: deb$/Types: deb deb-src/' $SRCUBU fi +# Raspi: +if test -f $SRCRPI +then +sudo sed -i 's/^Types: deb$/Types: deb deb-src/' $SRCRPI +fi # install build dependencies sudo apt update @@ -66,7 +80,12 @@ sudo apt install libsqlite3-mod-spatialite || true # compile source and install - this will take several minutes cd Python-${PYVER} +if [z ${DISABLEGIL+x} ] +then +./configure --enable-optimizations --enable-loadable-sqlite-extensions --disable-gil +else ./configure --enable-optimizations --enable-loadable-sqlite-extensions +fi make # make test if [ -z ${ALTINSTALL+x} ] diff --git a/images/about_dialog.png b/images/about_dialog.png new file mode 100644 index 00000000..ba378857 Binary files /dev/null and b/images/about_dialog.png differ diff --git a/images/rinex_dialog.png b/images/rinex_dialog.png index 4b668433..59420836 100644 Binary files a/images/rinex_dialog.png and b/images/rinex_dialog.png differ diff --git a/images/sponsor.png b/images/sponsor.png new file mode 100644 index 00000000..fc34afb0 Binary files /dev/null and b/images/sponsor.png differ diff --git a/pygpsclient.json b/pygpsclient.json index db4d9cc8..fedf2e6e 100644 --- a/pygpsclient.json +++ b/pygpsclient.json @@ -1,5 +1,5 @@ { - "version_s": "1.5.21", + "version_s": "1.7.6", "Banner": true, "Settings": true, "Status": true, @@ -16,7 +16,7 @@ "checkforupdate_b": 0, "transient_dialog_b": 1, "resize_dialog_b": 0, - "guiupdateinterval_f": 0.2, + "guiupdateinterval_f": 0.5, "mapupdateinterval_n": 60, "defaultport_s": "USB", "mqapikey_s": "<======= YOUR MQ API KEY ======>", diff --git a/pyproject.toml b/pyproject.toml index 22fb2dda..f9483633 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,7 @@ classifiers = [ "Topic :: Scientific/Engineering :: GIS", ] -dependencies = ["requests>=2.34.0", "Pillow>=12.0.0", "pygnssutils>=1.2.7"] +dependencies = ["requests>=2.34.0", "Pillow>=12.0.0", "pygnssutils>=1.2.8"] [project.scripts] pygpsclient = "pygpsclient.__main__:main" @@ -72,7 +72,7 @@ repository = "https://github.com/semuconsulting/PyGPSClient" changelog = "https://github.com/semuconsulting/PyGPSClient/blob/main/RELEASE_NOTES.md" [dependency-groups] -optional = ["rasterio", "cryptography"] +optional = ["rasterio"] build = [ "awscli", "build", diff --git a/src/pygpsclient/_version.py b/src/pygpsclient/_version.py index 53f0cbc2..4b563e4c 100644 --- a/src/pygpsclient/_version.py +++ b/src/pygpsclient/_version.py @@ -8,4 +8,4 @@ :license: BSD 3-Clause """ -__version__ = "1.7.6" +__version__ = "1.7.7" diff --git a/src/pygpsclient/about_dialog.py b/src/pygpsclient/about_dialog.py index 13430c3b..bc782eb7 100644 --- a/src/pygpsclient/about_dialog.py +++ b/src/pygpsclient/about_dialog.py @@ -3,6 +3,9 @@ About Dialog Box class for PyGPSClient application. +Includes functionality to display system information and +check for Python application package updates. + Created on 20 Sep 2020 :author: semuadmin (Steve Smith) @@ -11,35 +14,51 @@ """ import logging -from platform import machine, python_version +from datetime import datetime +from platform import python_version, uname from tkinter import ( CENTER, + DISABLED, EW, + NONE, + NORMAL, NSEW, Button, Checkbutton, Frame, IntVar, Label, + N, + S, Tcl, + W, ttk, ) from webbrowser import open_new_tab from PIL import Image, ImageTk +from pygpsclient.custom_classes import ScrollableText from pygpsclient.globals import ( CLICK_CURSOR, ERRCOL, + FONT_FIXED, ICON_APP128, + ICON_CLIPBOARD, + ICON_INFO, ICON_SPONSOR, + ICON_UPDATE, INFOCOL, LICENSE_URL, OKCOL, SPONSOR_URL, TRACEMODE_WRITE, ) -from pygpsclient.helpers import LIBVERSIONS, brew_installed, check_for_updates +from pygpsclient.helpers import ( + brew_installed, + check_for_updates, + secs2unit, +) from pygpsclient.sqlite_handler import SQLSTATUS from pygpsclient.strings import ( ABOUTTXT, @@ -55,6 +74,15 @@ ) from pygpsclient.toplevel_dialog import ToplevelDialog +try: + from sys import _is_gil_enabled + + IGE = True +except ImportError: + IGE = False + +NSW = (N, S, W) + class AboutDialog(ToplevelDialog): """ @@ -71,9 +99,14 @@ def __init__(self, app, *args, **kwargs): # pylint: disable=unused-argument self.__app = app # Reference to main application class self.logger = logging.getLogger(__name__) self._img_icon = ImageTk.PhotoImage(Image.open(ICON_APP128).resize((64, 64))) + self._img_clipboard = ImageTk.PhotoImage(Image.open(ICON_CLIPBOARD)) + self._img_info = ImageTk.PhotoImage(Image.open(ICON_INFO)) self._img_sponsor = ImageTk.PhotoImage(Image.open(ICON_SPONSOR)) + self._img_update = ImageTk.PhotoImage(Image.open(ICON_UPDATE)) self._checkonstartup = IntVar() self._checkonstartup.set(self.__app.configuration.get("checkforupdate_b")) + self._sysinfo = "" + self._updates_available = False super().__init__(app, DLGTABOUT) @@ -81,6 +114,7 @@ def __init__(self, app, *args, **kwargs): # pylint: disable=unused-argument self._do_layout() self._attach_events() self._finalise() + self.after(50, self._reset) def _body(self): """ @@ -89,7 +123,10 @@ def _body(self): self._frm_body = Frame(self.container) self._lbl_icon = Label( - self._frm_body, image=self._img_icon, borderwidth=0, anchor=CENTER + self._frm_body, + image=self._img_icon, + borderwidth=0, + anchor=CENTER, ) self._lbl_desc = Label( self._frm_body, @@ -105,32 +142,29 @@ def _body(self): cursor=CLICK_CURSOR, anchor=CENTER, ) - tkv = Tcl().call("info", "patchlevel") - self._lbl_python_version = Label( + self.frm_sysinfo = ScrollableText( + self.__app, + self, self._frm_body, - text=( - f"Arch: {machine()} " - f"Python: {python_version()} Tk: {tkv} " - f"Spatial: {SQLSTATUS[self.__app.db_enabled]}" - ), - anchor=CENTER, + font=FONT_FIXED, + state=DISABLED, + wrap=NONE, + height=10, + width=53, ) - self._lbl_lib_versions = [] - for nam, ver in LIBVERSIONS.items(): - self._lbl_lib_versions.append( - Label( - self._frm_body, - text=f"{nam}: {ver}", - anchor=CENTER, - border=0, - highlightthickness=0, - ) - ) - self._btn_checkupdate = Button( + self._btn_info = Button( self._frm_body, - text="", + image=self._img_info, width=16, cursor=CLICK_CURSOR, + command=self._reset, + ) + self._btn_update = Button( + self._frm_body, + image=self._img_update, + width=16, + command=self._on_update, + state=DISABLED, ) self._chk_checkupdate = Checkbutton( self._frm_body, @@ -157,32 +191,24 @@ def _do_layout(self): """ self._frm_body.grid(column=0, row=0, ipadx=2, ipady=2, sticky=NSEW) - self._lbl_icon.grid(column=0, row=0, columnspan=2, padx=3, pady=0, sticky=EW) - self._lbl_desc.grid(column=0, row=1, columnspan=2, padx=3, pady=0, sticky=EW) - self._lbl_github.grid(column=0, row=2, columnspan=2, padx=3, pady=0, sticky=EW) + self._lbl_icon.grid(column=0, row=0, columnspan=4, padx=3, pady=0, sticky=EW) + self._lbl_desc.grid(column=0, row=1, columnspan=4, padx=3, pady=0, sticky=EW) + self._lbl_github.grid(column=0, row=2, columnspan=4, padx=3, pady=0, sticky=EW) ttk.Separator(self._frm_body).grid( - column=0, row=3, columnspan=2, padx=3, pady=3, sticky=EW - ) - self._lbl_python_version.grid( - column=0, row=4, columnspan=2, padx=3, pady=1, sticky=EW - ) - for i, lbl in enumerate(self._lbl_lib_versions): - lbl.grid(column=0, row=5 + i, columnspan=2, padx=2, pady=0, sticky=EW) - lv = len(self._lbl_lib_versions) - self._btn_checkupdate.grid( - column=0, row=6 + lv, ipadx=3, ipady=3, padx=3, pady=3 - ) - self._chk_checkupdate.grid( - column=1, row=6 + lv, ipadx=3, ipady=3, padx=3, pady=3 + column=0, row=3, columnspan=4, padx=3, pady=3, sticky=EW ) + self.frm_sysinfo.grid(column=0, row=4, columnspan=3, sticky=NSEW) + self._btn_info.grid(column=0, row=6, ipadx=3, ipady=3, padx=3, pady=3) + self._btn_update.grid(column=1, row=6, ipadx=3, ipady=3, padx=3, pady=3) + self._chk_checkupdate.grid(column=2, row=6, ipadx=3, ipady=3, padx=3, pady=3) ttk.Separator(self._frm_body).grid( - column=0, row=7 + lv, columnspan=2, padx=3, pady=3, sticky=EW + column=0, row=7, columnspan=4, padx=3, pady=3, sticky=EW ) self._lbl_sponsoricon.grid( - column=0, row=8 + lv, columnspan=2, padx=3, pady=3, sticky=EW + column=0, row=8, columnspan=4, padx=3, pady=3, sticky=EW ) self._lbl_copyright.grid( - column=0, row=9 + lv, columnspan=2, padx=3, pady=3, sticky=EW + column=0, row=9, columnspan=4, padx=3, pady=3, sticky=EW ) def _attach_events(self): @@ -190,13 +216,58 @@ def _attach_events(self): Bind events to dialog. """ - self._set_update_btn_mode(False) self._lbl_github.bind("