Skip to content
Merged
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,13 @@ For more comprehensive installation instructions, please refer to [INSTALLATION.

#### <a name="config">Saving and loading configuration settings</a>

- Configuration settings for PyGPSClient can be saved and recalled via the Menu..File..Save Configuration and Menu..File..Load Configuration options. By default, PyGPSClient will look for a file named `pygpsclient.json` in the user's home directory. Certain configuration settings require manual editing e.g. custom preset UBX, NMEA and TTY commands and tag colour schemes - see details below.
- Configuration settings for PyGPSClient can be saved and recalled via the Menu..File..Save/Load Configuration options. By default, PyGPSClient will look for a file named `pygpsclient.json` in the user's home directory. Certain configuration settings require manual editing e.g. custom preset UBX, NMEA and TTY commands and tag colour schemes - see details below.
- It is recommended to re-save the configuration settings after each PyGPSClient version update, or if you see the warning "Consider re-saving" on startup.
- PyGPSClient will prompt you to stop all running input and output streams before loading a new configuration.

#### <a name="transient">Toplevel ('pop-up') dialog setting</a>

- The behaviour of Toplevel ('pop-up') dialogs will depend on the screen resolution. If the width or height of a Toplevel dialog exceeds the screen resolution, the dialog will be displayed in a scrollable, resizeable window. Otherwise, the dialog is displayed as a fixed, non-resizeable panel.
- The behaviour of Toplevel ('pop-up') dialogs will depend on the screen resolution and 'transient' setting. If the width or height of a Toplevel dialog exceeds the screen resolution, the dialog will be displayed in a scrollable, resizeable window. Otherwise, the dialog is displayed as a fixed, non-resizeable panel.
- A boolean configuration setting `transient_dialog_b` governs whether Toplevel dialogs are 'transient' (i.e. always on top of main application dialog) or not. Changing this setting to `0` allows Toplevel dialogs to be minimised independently of the main application window, but be mindful that some dialogs may end up hidden behind others e.g. "Open file/folder" dialogs. **If a file open button appears unresponsive, check that the "Open file/folder" panel isn't already open but obscured**.
- If you're accessing the desktop via a VNC session (e.g. to a headless Raspberry Pi) it is recommended to keep the setting at the default `1`, as VNC may not recognise keystrokes on overlaid non-transient windows.

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

### RELEASE 1.6.1

1. Updates to main application window geometry (size and position) handling. Current window geometry is now saved to json configuration file as `screengeom_s` (e.g. `"1373x798+71+44"`), and will be restored on restart. Default startup geometry is centered at 75% of screen resolution.

### RELEASE 1.6.0

FIXES:
Expand Down
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.6.0"
__version__ = "1.6.1"
97 changes: 48 additions & 49 deletions src/pygpsclient/about_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,12 @@
"""

import logging
from platform import python_version
from platform import machine, python_version
from tkinter import Button, Checkbutton, Frame, IntVar, Label, Tcl
from webbrowser import open_new_tab

from PIL import Image, ImageTk
from pygnssutils import version as PGVERSION
from pynmeagps import version as NMEAVERSION
from pyqgc import version as QGCVERSION
from pyrtcm import version as RTCMVERSION
from pysbf2 import version as SBFVERSION
from pyspartn import version as SPARTNVERSION
from pyubx2 import version as UBXVERSION

from pygpsclient._version import __version__ as VERSION

from pygpsclient.globals import (
ERRCOL,
ICON_APP128,
Expand All @@ -36,22 +28,22 @@
SPONSOR_URL,
TRACEMODE_WRITE,
)
from pygpsclient.helpers import brew_installed, check_latest
from pygpsclient.helpers import LIBVERSIONS, brew_installed, check_for_updates
from pygpsclient.sqlite_handler import SQLSTATUS
from pygpsclient.strings import ABOUTTXT, BREWWARN, COPYRIGHT, DLGTABOUT, GITHUB_URL
from pygpsclient.strings import (
ABOUTTXT,
BREWUPDATE,
BREWWARN,
COPYRIGHT,
DLGTABOUT,
GITHUB_URL,
NA,
UPDATEERR,
UPDATEINPROG,
UPDATERESTART,
)
from pygpsclient.toplevel_dialog import ToplevelDialog

LIBVERSIONS = {
"PyGPSClient": VERSION,
"pygnssutils": PGVERSION,
"pyubx2": UBXVERSION,
"pysbf2": SBFVERSION,
"pyqgc": QGCVERSION,
"pynmeagps": NMEAVERSION,
"pyrtcm": RTCMVERSION,
"pyspartn": SPARTNVERSION,
}


class AboutDialog(ToplevelDialog):
"""
Expand All @@ -73,7 +65,6 @@ def __init__(self, app, *args, **kwargs): # pylint: disable=unused-argument
self._img_sponsor = ImageTk.PhotoImage(Image.open(ICON_SPONSOR))
self._checkonstartup = IntVar()
self._checkonstartup.set(self.__app.configuration.get("checkforupdate_b"))
self._updates = []

super().__init__(app, DLGTABOUT)

Expand Down Expand Up @@ -102,6 +93,7 @@ def _body(self):
self._lbl_python_version = Label(
self._frm_body,
text=(
f"Arch: {machine()} "
f"Python: {python_version()} Tk: {tkv} "
f"Spatial: {SQLSTATUS[self.__app.db_enabled]}"
),
Expand All @@ -118,7 +110,7 @@ def _body(self):
)
self._btn_checkupdate = Button(
self._frm_body,
text="Check for updates",
text="",
width=14,
cursor="hand2",
)
Expand Down Expand Up @@ -175,7 +167,7 @@ def _attach_events(self):
Bind events to dialog.
"""

self._btn_checkupdate.bind("<Button>", self._check_for_update)
self._set_update_btn_mode(False)
self._lbl_github.bind("<Button>", self._on_github)
self._lbl_sponsoricon.bind("<Button>", self._on_sponsor)
self._lbl_copyright.bind("<Button>", self._on_license)
Expand All @@ -197,7 +189,7 @@ def _on_github(self, *args, **kwargs): # pylint: disable=unused-argument
"""

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

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

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

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

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

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

self.status_label = ""
self._updates = []
for i, (nam, current) in enumerate(LIBVERSIONS.items()):
latest = check_latest(nam)
versions = check_for_updates()
self.status_label = ("Checking for updates...", INFOCOL)
for i, (nam, current, latest) in enumerate(versions):
txt = f"{nam}: {current}"
if latest == current:
txt += " ✓"
col = OKCOL
elif latest == "N/A":
elif latest == NA:
txt += " - Info not available!"
col = ERRCOL
else:
self._updates.append(nam)
txt += f" - Latest version is {latest}"
col = ERRCOL
self._lbl_lib_versions[i].config(text=txt, fg=col)

if len(self._updates) > 0:
self._btn_checkupdate.config(text="UPDATE", fg=INFOCOL)
self._btn_checkupdate.bind("<Button>", self._do_update)
updates = [nam for (nam, current, latest) in versions if latest != current]
if len(updates) > 0:
self.status_label = ("Updates available", OKCOL)
self._set_update_btn_mode(True)
else:
self.status_label = ("No updates available", INFOCOL)

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

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

self._btn_checkupdate.config(text="UPDATING...", fg=INFOCOL)
self.update_idletasks()
rc = self.__app.do_app_update(self._updates)
self.status_label = (UPDATEINPROG, INFOCOL)
rc = self.__app.do_app_update()
if rc:
self._btn_checkupdate.config(text="RESTART APP", fg=OKCOL)
self._btn_checkupdate.bind("<Button>", self.__app.on_exit)
self.status_label = (UPDATERESTART, OKCOL)
else:
self._btn_checkupdate.config(text="UPDATE FAILED", fg=ERRCOL)
self._btn_checkupdate.bind("<Button>", self._check_for_update)
self.status_label = (UPDATEERR.format(err=rc), ERRCOL)
self._set_update_btn_mode(False)

def _brew_warning(self):
def _set_update_btn_mode(self, update: bool):
"""
Display warning that some functionality unavailable with Homebrew.
Set Check for update button label and binding.

:param bool update: False = check, True = update
"""

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