mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2026-10-27 01:16:06 +03:00
Merge pull request #2525 from HullSeals/enhancement/2116/additional-tk-to-ttk
Updater Refactor and Additional TK->TTK
This commit is contained in:
12 files changed
+159
-166
No files matched your search
@@ -2052,7 +2052,7 @@ def show_killswitch_poppup(root=None):
|
||||
idx += 1
|
||||
idx += 1
|
||||
|
||||
ok_button = tk.Button(frame, text="Ok", command=tl.destroy)
|
||||
ok_button = ttk.Button(frame, text="Ok", command=tl.destroy)
|
||||
ok_button.grid(columnspan=2, sticky=tk.EW)
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -38,8 +38,8 @@ You will need several pieces of software installed, or the files from their
|
||||
auto-select some others). NB: If you have need to uninstall this it's
|
||||
"Windows Software Development Kit - Windows 10.0.19041.1" in
|
||||
"Apps & Features", *not* "Windows SDK AddOn".
|
||||
1. [Python](https://python.org): 32-bit version of Python 3.10 for Windows.
|
||||
[v3.10.3](https://www.python.org/downloads/release/python-3103/) is the most
|
||||
1. [Python](https://python.org): 32-bit version of Python 3.13 for Windows.
|
||||
[v3.13.9](https://www.python.org/downloads/release/python-3139/) is the most
|
||||
recently tested version. You need the `Windows x86 executable installer`
|
||||
file, for the 32-bit version. Double-check the version against the
|
||||
`.python.version` file, as it should always contain the intended version.
|
||||
|
||||
+8
-13
@@ -41,17 +41,14 @@ class Frame(ttk.Frame):
|
||||
"""Custom ttk.Frame class to fix some display issues."""
|
||||
|
||||
def __init__(self, master: ttk.Notebook | None = None, **kw):
|
||||
if sys.platform == 'win32':
|
||||
ttk.Frame.__init__(self, master, style='nb.TFrame', **kw)
|
||||
ttk.Frame(self).grid(pady=5) # top spacer
|
||||
else:
|
||||
ttk.Frame.__init__(self, master, **kw)
|
||||
ttk.Frame(self).grid(pady=5) # top spacer
|
||||
self.configure(takefocus=1) # let the frame take focus so that no particular child is focused
|
||||
style = 'nb.TFrame' if sys.platform == 'win32' else None
|
||||
super().__init__(master, style=style, **kw) # type: ignore
|
||||
ttk.Frame(self).grid(pady=5) # Top spacer
|
||||
self.configure(takefocus=1) # let the frame take focus so that no particular child is focused
|
||||
|
||||
|
||||
class Label(tk.Label):
|
||||
"""Custom tk.Label class to fix some display issues."""
|
||||
class Label(ttk.Label):
|
||||
"""Custom ttk.Label class to fix some display issues."""
|
||||
|
||||
def __init__(self, master: ttk.Frame | None = None, **kw):
|
||||
kw['foreground'] = kw.pop('foreground', PAGEFG if sys.platform == 'win32'
|
||||
@@ -134,10 +131,8 @@ class Button(ttk.Button):
|
||||
"""Custom ttk.Button class to fix some display issues."""
|
||||
|
||||
def __init__(self, master: ttk.Frame | None = None, **kw):
|
||||
if sys.platform == 'win32':
|
||||
ttk.Button.__init__(self, master, style='nb.TButton', **kw)
|
||||
else:
|
||||
ttk.Button.__init__(self, master, **kw)
|
||||
style = 'nb.TButton' if sys.platform == 'win32' else None
|
||||
super().__init__(master, style=style, **kw) # type: ignore
|
||||
|
||||
|
||||
class ColoredButton(tk.Button):
|
||||
|
||||
+2
-2
@@ -302,8 +302,8 @@ def export(data, filename) -> None:
|
||||
try:
|
||||
m = lookup(v, ship_name_map)
|
||||
if m:
|
||||
h.write(f'{rowheader}, {m["category"]}, {m["name"]}, {m.get("mount","")},'
|
||||
f'{m.get("guidance","")}, {m.get("ship","")}, {m["class"]}, {m["rating"]},'
|
||||
h.write(f'{rowheader}, {m["category"]}, {m["name"]}, {m.get("mount", "")},'
|
||||
f'{m.get("guidance", "")}, {m.get("ship", "")}, {m["class"]}, {m["rating"]},'
|
||||
f'{m["id"]}, {data["timestamp"]}\n')
|
||||
|
||||
except ValueError as e:
|
||||
|
||||
@@ -13,6 +13,7 @@ import logging
|
||||
import operator
|
||||
import os
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
import tkinter as tk
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
@@ -33,14 +34,12 @@ PLUGINS_broken = []
|
||||
|
||||
|
||||
# For asynchronous error display
|
||||
@dataclass
|
||||
class LastError:
|
||||
"""Holds the last plugin error."""
|
||||
|
||||
msg: str | None
|
||||
root: tk.Tk
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.msg = None
|
||||
msg: str | None = None
|
||||
root: tk.Tk | None = field(default=None, repr=False) # not initialized by default, hide in repr
|
||||
|
||||
|
||||
last_error = LastError()
|
||||
|
||||
+28
-27
@@ -29,6 +29,7 @@ import pathlib
|
||||
import re
|
||||
import sqlite3
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
import warnings
|
||||
from platform import system
|
||||
from textwrap import dedent
|
||||
@@ -115,15 +116,15 @@ class This:
|
||||
|
||||
# Tracking UI
|
||||
self.ui: tk.Frame
|
||||
self.ui_system_name: tk.Label
|
||||
self.ui_system_address: tk.Label
|
||||
self.ui_j_body_name: tk.Label
|
||||
self.ui_j_body_id: tk.Label
|
||||
self.ui_j_body_type: tk.Label
|
||||
self.ui_s_body_name: tk.Label
|
||||
self.ui_station_name: tk.Label
|
||||
self.ui_station_type: tk.Label
|
||||
self.ui_station_marketid: tk.Label
|
||||
self.ui_system_name: ttk.Label
|
||||
self.ui_system_address: ttk.Label
|
||||
self.ui_j_body_name: ttk.Label
|
||||
self.ui_j_body_id: ttk.Label
|
||||
self.ui_j_body_type: ttk.Label
|
||||
self.ui_s_body_name: ttk.Label
|
||||
self.ui_station_name: ttk.Label
|
||||
self.ui_station_type: ttk.Label
|
||||
self.ui_station_marketid: ttk.Label
|
||||
|
||||
|
||||
this = This()
|
||||
@@ -2027,15 +2028,15 @@ def plugin_app(parent: tk.Tk) -> tk.Frame | None:
|
||||
# System
|
||||
#######################################################################
|
||||
# SystemName
|
||||
system_name_label = tk.Label(this.ui, text="J:SystemName:")
|
||||
system_name_label = ttk.Label(this.ui, text="J:SystemName:")
|
||||
system_name_label.grid(row=row, column=0, sticky=tk.W)
|
||||
this.ui_system_name = tk.Label(this.ui, name='eddn_track_system_name', anchor=tk.W)
|
||||
this.ui_system_name = ttk.Label(this.ui, name='eddn_track_system_name', anchor=tk.W)
|
||||
this.ui_system_name.grid(row=row, column=1, sticky=tk.E)
|
||||
row += 1
|
||||
# SystemAddress
|
||||
system_address_label = tk.Label(this.ui, text="J:SystemAddress:")
|
||||
system_address_label = ttk.Label(this.ui, text="J:SystemAddress:")
|
||||
system_address_label.grid(row=row, column=0, sticky=tk.W)
|
||||
this.ui_system_address = tk.Label(this.ui, name='eddn_track_system_address', anchor=tk.W)
|
||||
this.ui_system_address = ttk.Label(this.ui, name='eddn_track_system_address', anchor=tk.W)
|
||||
this.ui_system_address.grid(row=row, column=1, sticky=tk.E)
|
||||
row += 1
|
||||
#######################################################################
|
||||
@@ -2044,27 +2045,27 @@ def plugin_app(parent: tk.Tk) -> tk.Frame | None:
|
||||
# Body
|
||||
#######################################################################
|
||||
# Body Name from Journal
|
||||
journal_body_name_label = tk.Label(this.ui, text="J:BodyName:")
|
||||
journal_body_name_label = ttk.Label(this.ui, text="J:BodyName:")
|
||||
journal_body_name_label.grid(row=row, column=0, sticky=tk.W)
|
||||
this.ui_j_body_name = tk.Label(this.ui, name='eddn_track_j_body_name', anchor=tk.W)
|
||||
this.ui_j_body_name = ttk.Label(this.ui, name='eddn_track_j_body_name', anchor=tk.W)
|
||||
this.ui_j_body_name.grid(row=row, column=1, sticky=tk.E)
|
||||
row += 1
|
||||
# Body ID from Journal
|
||||
journal_body_id_label = tk.Label(this.ui, text="J:BodyID:")
|
||||
journal_body_id_label = ttk.Label(this.ui, text="J:BodyID:")
|
||||
journal_body_id_label.grid(row=row, column=0, sticky=tk.W)
|
||||
this.ui_j_body_id = tk.Label(this.ui, name='eddn_track_j_body_id', anchor=tk.W)
|
||||
this.ui_j_body_id = ttk.Label(this.ui, name='eddn_track_j_body_id', anchor=tk.W)
|
||||
this.ui_j_body_id.grid(row=row, column=1, sticky=tk.E)
|
||||
row += 1
|
||||
# Body Type from Journal
|
||||
journal_body_type_label = tk.Label(this.ui, text="J:BodyType:")
|
||||
journal_body_type_label = ttk.Label(this.ui, text="J:BodyType:")
|
||||
journal_body_type_label.grid(row=row, column=0, sticky=tk.W)
|
||||
this.ui_j_body_type = tk.Label(this.ui, name='eddn_track_j_body_type', anchor=tk.W)
|
||||
this.ui_j_body_type = ttk.Label(this.ui, name='eddn_track_j_body_type', anchor=tk.W)
|
||||
this.ui_j_body_type.grid(row=row, column=1, sticky=tk.E)
|
||||
row += 1
|
||||
# Body Name from Status.json
|
||||
status_body_name_label = tk.Label(this.ui, text="S:BodyName:")
|
||||
status_body_name_label = ttk.Label(this.ui, text="S:BodyName:")
|
||||
status_body_name_label.grid(row=row, column=0, sticky=tk.W)
|
||||
this.ui_s_body_name = tk.Label(this.ui, name='eddn_track_s_body_name', anchor=tk.W)
|
||||
this.ui_s_body_name = ttk.Label(this.ui, name='eddn_track_s_body_name', anchor=tk.W)
|
||||
this.ui_s_body_name.grid(row=row, column=1, sticky=tk.E)
|
||||
row += 1
|
||||
#######################################################################
|
||||
@@ -2073,21 +2074,21 @@ def plugin_app(parent: tk.Tk) -> tk.Frame | None:
|
||||
# Station
|
||||
#######################################################################
|
||||
# Name
|
||||
status_station_name_label = tk.Label(this.ui, text="J:StationName:")
|
||||
status_station_name_label = ttk.Label(this.ui, text="J:StationName:")
|
||||
status_station_name_label.grid(row=row, column=0, sticky=tk.W)
|
||||
this.ui_station_name = tk.Label(this.ui, name='eddn_track_station_name', anchor=tk.W)
|
||||
this.ui_station_name = ttk.Label(this.ui, name='eddn_track_station_name', anchor=tk.W)
|
||||
this.ui_station_name.grid(row=row, column=1, sticky=tk.E)
|
||||
row += 1
|
||||
# Type
|
||||
status_station_type_label = tk.Label(this.ui, text="J:StationType:")
|
||||
status_station_type_label = ttk.Label(this.ui, text="J:StationType:")
|
||||
status_station_type_label.grid(row=row, column=0, sticky=tk.W)
|
||||
this.ui_station_type = tk.Label(this.ui, name='eddn_track_station_type', anchor=tk.W)
|
||||
this.ui_station_type = ttk.Label(this.ui, name='eddn_track_station_type', anchor=tk.W)
|
||||
this.ui_station_type.grid(row=row, column=1, sticky=tk.E)
|
||||
row += 1
|
||||
# MarketID
|
||||
status_station_marketid_label = tk.Label(this.ui, text="J:StationID:")
|
||||
status_station_marketid_label = ttk.Label(this.ui, text="J:StationID:")
|
||||
status_station_marketid_label.grid(row=row, column=0, sticky=tk.W)
|
||||
this.ui_station_marketid = tk.Label(this.ui, name='eddn_track_station_id', anchor=tk.W)
|
||||
this.ui_station_marketid = ttk.Label(this.ui, name='eddn_track_station_id', anchor=tk.W)
|
||||
this.ui_station_marketid.grid(row=row, column=1, sticky=tk.E)
|
||||
row += 1
|
||||
#######################################################################
|
||||
|
||||
+5
-5
@@ -90,11 +90,11 @@ class This:
|
||||
self.newgame: bool = False # starting up - batch initial burst of events
|
||||
self.newgame_docked: bool = False # starting up while docked
|
||||
self.navbeaconscan: int = 0 # batch up burst of Scan events after NavBeaconScan
|
||||
self.system_link: tk.Widget | None = None
|
||||
self.system_link: ttk.Widget | None = None
|
||||
self.system_name: tk.Tk | None = None
|
||||
self.system_address: int | None = None # Frontier SystemAddress
|
||||
self.system_population: int | None = None
|
||||
self.station_link: tk.Widget | None = None
|
||||
self.station_link: ttk.Widget | None = None
|
||||
self.station_name: str | None = None
|
||||
self.station_marketid: int | None = None # Frontier MarketID
|
||||
self.on_foot = False
|
||||
@@ -109,7 +109,7 @@ class This:
|
||||
self.log: tk.IntVar | None = None
|
||||
self.log_button: ttk.Checkbutton | None = None
|
||||
|
||||
self.label: tk.Widget | None = None
|
||||
self.label: ttk.Widget | None = None
|
||||
|
||||
self.cmdr_label: nb.Label | None = None
|
||||
self.cmdr_text: nb.Label | None = None
|
||||
@@ -729,8 +729,8 @@ def send_to_edsm( # noqa: CCR001
|
||||
# Respect rate limits if they exist
|
||||
if remaining == 0:
|
||||
# Calculate sleep time until the rate limit reset time
|
||||
reset_time = datetime.utcfromtimestamp(reset)
|
||||
current_time = datetime.utcnow()
|
||||
reset_time = datetime.fromtimestamp(reset, tz=timezone.utc)
|
||||
current_time = datetime.now(timezone.utc)
|
||||
|
||||
sleep_time = (reset_time - current_time).total_seconds()
|
||||
|
||||
|
||||
+5
-5
@@ -113,20 +113,20 @@ class This:
|
||||
self.timer_run = True
|
||||
|
||||
# Main window clicks
|
||||
self.system_link: tk.Widget = None # type: ignore
|
||||
self.system_link: ttk.Widget = None # type: ignore
|
||||
self.system_name: str | None = None # type: ignore
|
||||
self.system_address: str | None = None # type: ignore
|
||||
self.system_population: int | None = None
|
||||
self.station_link: tk.Widget = None # type: ignore
|
||||
self.station_link: ttk.Widget = None # type: ignore
|
||||
self.station_name = None
|
||||
self.station_marketid = None
|
||||
|
||||
# Prefs UI
|
||||
self.log: 'tk.IntVar'
|
||||
self.log: tk.IntVar
|
||||
self.log_button: nb.Checkbutton
|
||||
self.label: HyperlinkLabel
|
||||
self.apikey: nb.EntryMenu
|
||||
self.apikey_label: tk.Label
|
||||
self.apikey_label: ttk.Label
|
||||
|
||||
self.events: dict[Credentials, Deque[Event]] = defaultdict(deque)
|
||||
self.event_lock: Lock = threading.Lock() # protects events, for use when rewriting events
|
||||
@@ -1610,7 +1610,7 @@ def handle_api_error(data: Mapping[str, Any], status: int, reply: dict[str, Any]
|
||||
"""
|
||||
error_message = reply['header'].get('eventStatusText', "")
|
||||
logger.warning(f'Inara\t{status} {error_message}')
|
||||
logger.debug(f'JSON data:\n{json.dumps(data, indent=2, separators = (",", ": "))}')
|
||||
logger.debug(f'JSON data:\n{json.dumps(data, indent=2, separators=(",", ": "))}')
|
||||
# LANG: INARA API returned some kind of error (error message will be contained in {MSG})
|
||||
plug.show_error(tr.tl('Error: Inara {MSG}').format(MSG=error_message))
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ referenced in this file (or only in any other core plugin), and if so...
|
||||
from __future__ import annotations
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from typing import Any
|
||||
import requests
|
||||
from companion import CAPIData
|
||||
@@ -39,11 +40,11 @@ class This:
|
||||
def __init__(self):
|
||||
self.parent: tk.Tk
|
||||
self.shutting_down = False # Plugin is shutting down.
|
||||
self.system_link: tk.Widget = None # type: ignore
|
||||
self.system_link: ttk.Widget = None # type: ignore
|
||||
self.system_name: str | None = None
|
||||
self.system_address: str | None = None
|
||||
self.system_population: int | None = None
|
||||
self.station_link: tk.Widget = None # type: ignore
|
||||
self.station_link: ttk.Widget = None # type: ignore
|
||||
self.station_name = None
|
||||
self.station_marketid = None
|
||||
self.on_foot = False
|
||||
|
||||
@@ -457,7 +457,7 @@ class StatsResults(tk.Toplevel):
|
||||
# label = HyperlinkLabel(parent, text=col_content, popup_copy=True)
|
||||
label = nb.Label(parent, text=col_content)
|
||||
if with_copy:
|
||||
label.bind('<Button-1>', self.copy_callback(label, col_content))
|
||||
label.bind('<Button-1>', self.copy_callback(label, col_content)) # type: ignore
|
||||
|
||||
if i == 0:
|
||||
label.grid(padx=10, sticky=tk.W)
|
||||
|
||||
@@ -117,7 +117,7 @@ class TestNewConfig:
|
||||
@mark.parametrize("lst", _build_test_list(list_tests, _get_fuzz(list)))
|
||||
def test_list(self, lst: list[str]) -> None:
|
||||
"""Save a list and then ask for it back."""
|
||||
name = f'list_test_{ hash("".join(lst)) }'
|
||||
name = f'list_test_{hash("".join(lst))}'
|
||||
config.set(name, lst)
|
||||
|
||||
config.save()
|
||||
@@ -222,7 +222,7 @@ class TestOldNewConfig:
|
||||
# So insert those spaces here, to ensure that it works otherwise.
|
||||
lst = [e if len(e) > 0 else ' ' for e in lst]
|
||||
|
||||
name = self.KEY_PREFIX + f'list_test_{ hash("".join(lst)) }'
|
||||
name = self.KEY_PREFIX + f'list_test_{hash("".join(lst))}'
|
||||
old_config.set(name, lst)
|
||||
old_config.save()
|
||||
|
||||
|
||||
@@ -12,9 +12,10 @@ import shutil
|
||||
import sys
|
||||
import threading
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from tkinter import messagebox
|
||||
from traceback import print_exc
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, cast, Any
|
||||
from xml.etree import ElementTree
|
||||
import requests
|
||||
import semantic_version
|
||||
@@ -25,58 +26,68 @@ from l10n import translations as tr
|
||||
if TYPE_CHECKING:
|
||||
import tkinter as tk
|
||||
|
||||
|
||||
logger = get_main_logger()
|
||||
|
||||
|
||||
def check_for_fdev_updates(silent: bool = False, local: bool = False) -> None: # noqa: CCR001
|
||||
def read_normalized_file(path: pathlib.Path) -> str:
|
||||
"""Read a UTF-8 (with BOM-safe) file and normalize line endings and whitespace."""
|
||||
try:
|
||||
return path.read_bytes().decode('utf-8-sig').replace('\r\n', '\n').strip()
|
||||
except FileNotFoundError:
|
||||
return ''
|
||||
|
||||
|
||||
def copy_bundle_file(filename: str, dest_dir: pathlib.Path) -> str:
|
||||
"""Attempt to copy a missing file from the bundled FDevIDs folder."""
|
||||
try:
|
||||
shutil.copy(f"FDevIDs/{filename}", dest_dir)
|
||||
return read_normalized_file(dest_dir / filename)
|
||||
except (FileNotFoundError, shutil.SameFileError):
|
||||
logger.info("Bundle copy failed or identical; continuing with empty content.")
|
||||
return ''
|
||||
|
||||
|
||||
def fetch_remote_file(url: str) -> str | None:
|
||||
"""Fetch a remote file and normalize its content."""
|
||||
try:
|
||||
response = requests.get(url, timeout=20)
|
||||
response.raise_for_status()
|
||||
return response.text.replace('\r\n', '\n').strip()
|
||||
except requests.RequestException:
|
||||
return None
|
||||
|
||||
|
||||
def check_for_fdev_updates(silent: bool = False, local: bool = False) -> None:
|
||||
"""Check for and download FDEV ID file updates."""
|
||||
pathway = config.respath_path if local else config.app_dir_path
|
||||
base_path = config.respath_path if local else config.app_dir_path
|
||||
fdevid_dir = pathlib.Path(base_path, 'FDevIDs')
|
||||
fdevid_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
files_urls = [
|
||||
('commodity.csv', 'https://raw.githubusercontent.com/EDCD/FDevIDs/master/commodity.csv'),
|
||||
('rare_commodity.csv', 'https://raw.githubusercontent.com/EDCD/FDevIDs/master/rare_commodity.csv')
|
||||
]
|
||||
files_urls = {
|
||||
'commodity.csv': 'https://raw.githubusercontent.com/EDCD/FDevIDs/master/commodity.csv',
|
||||
'rare_commodity.csv': 'https://raw.githubusercontent.com/EDCD/FDevIDs/master/rare_commodity.csv'
|
||||
}
|
||||
|
||||
for file, url in files_urls:
|
||||
fdevid_file = pathlib.Path(pathway / 'FDevIDs' / file)
|
||||
fdevid_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
with open(fdevid_file, newline='', encoding='utf-8') as f:
|
||||
local_content = f.read()
|
||||
except FileNotFoundError:
|
||||
logger.info(f'File {file} not found. Writing from bundle...')
|
||||
try:
|
||||
for localfile in files_urls:
|
||||
filepath = pathlib.Path(f"FDevIDs/{localfile[0]}")
|
||||
try:
|
||||
shutil.copy(filepath, pathway / 'FDevIDs')
|
||||
except shutil.SameFileError:
|
||||
logger.info("Not replacing same file...")
|
||||
fdevid_file = pathlib.Path(pathway / 'FDevIDs' / file)
|
||||
with open(fdevid_file, newline='', encoding='utf-8') as f:
|
||||
local_content = f.read()
|
||||
except FileNotFoundError:
|
||||
local_content = None
|
||||
|
||||
try:
|
||||
response = requests.get(url, timeout=20)
|
||||
response.raise_for_status()
|
||||
except requests.RequestException:
|
||||
for filename, url in files_urls.items():
|
||||
file_path = fdevid_dir / filename
|
||||
local_content = read_normalized_file(file_path) or copy_bundle_file(filename, fdevid_dir)
|
||||
remote_content = fetch_remote_file(url)
|
||||
if not remote_content:
|
||||
if not silent:
|
||||
logger.error(f'Failed to download {file}! Unable to continue.')
|
||||
logger.error(f'Failed to download {filename}! Unable to continue.')
|
||||
continue
|
||||
|
||||
if local_content == response.text:
|
||||
if local_content == remote_content:
|
||||
if not silent:
|
||||
logger.info(f'FDEV ID file {file} already up to date.')
|
||||
else:
|
||||
if not silent:
|
||||
logger.info(f'FDEV ID file {file} not up to date. Downloading...')
|
||||
with open(fdevid_file, 'w', newline='', encoding='utf-8') as csvfile:
|
||||
csvfile.write(response.text)
|
||||
logger.info(f'FDEV ID file {filename} already up to date.')
|
||||
continue
|
||||
|
||||
if not silent:
|
||||
logger.info(f'Updating FDEV ID file {filename}...')
|
||||
file_path.write_text(remote_content, encoding='utf-8', newline='\n')
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class EDMCVersion:
|
||||
"""
|
||||
Hold all the information about an EDMC version.
|
||||
@@ -91,10 +102,9 @@ class EDMCVersion:
|
||||
semantic_version object for this version
|
||||
"""
|
||||
|
||||
def __init__(self, version: str, title: str, sv: semantic_version.base.Version):
|
||||
self.version: str = version
|
||||
self.title: str = title
|
||||
self.sv: semantic_version.base.Version = sv
|
||||
version: str
|
||||
title: str
|
||||
sv: semantic_version.Version
|
||||
|
||||
|
||||
class Updater:
|
||||
@@ -105,6 +115,21 @@ class Updater:
|
||||
WinSparkle on win32.
|
||||
"""
|
||||
|
||||
def __init__(self, tkroot: tk.Tk | None = None, provider: str = 'internal'):
|
||||
"""
|
||||
Initialise an Updater instance.
|
||||
|
||||
:param tkroot: reference to the root window of the GUI
|
||||
:param provider: 'internal' or other string if not
|
||||
"""
|
||||
self.root: tk.Tk | None = tkroot
|
||||
self.provider: str = provider
|
||||
self.thread: threading.Thread | None = None
|
||||
self.updater: Any | None = None # ensure attribute exists
|
||||
|
||||
if not self.use_internal() and sys.platform == 'win32':
|
||||
self._init_winsparkle()
|
||||
|
||||
def shutdown_request(self) -> None:
|
||||
"""Receive (Win)Sparkle shutdown request and send it to parent."""
|
||||
if not config.shutting_down and self.root:
|
||||
@@ -116,61 +141,38 @@ class Updater:
|
||||
|
||||
:return: bool
|
||||
"""
|
||||
if self.provider == 'internal':
|
||||
return True
|
||||
return self.provider == 'internal'
|
||||
|
||||
return False
|
||||
def _init_winsparkle(self) -> None:
|
||||
"""Initialize WinSparkle updater for Windows."""
|
||||
import ctypes
|
||||
try:
|
||||
self.updater = cast(ctypes.CDLL, ctypes.cdll.WinSparkle)
|
||||
self.updater.win_sparkle_set_appcast_url(get_update_feed().encode()) # Set the appcast URL
|
||||
|
||||
def __init__(self, tkroot: tk.Tk | None = None, provider: str = 'internal'):
|
||||
"""
|
||||
Initialise an Updater instance.
|
||||
# Set the appversion *without* build metadata, as WinSparkle
|
||||
# doesn't do proper Semantic Version checks.
|
||||
# NB: It 'accidentally' supports pre-release due to how it
|
||||
# splits and compares strings:
|
||||
# <https://github.com/vslavik/winsparkle/issues/214>
|
||||
self.updater.win_sparkle_set_app_build_version(str(appversion_nobuild()))
|
||||
|
||||
:param tkroot: reference to the root window of the GUI
|
||||
:param provider: 'internal' or other string if not
|
||||
"""
|
||||
self.root: tk.Tk | None = tkroot
|
||||
self.provider: str = provider
|
||||
self.thread: threading.Thread | None = None
|
||||
# set up shutdown callback
|
||||
self.callback_t = ctypes.CFUNCTYPE(None) # keep reference
|
||||
self.callback_fn = self.callback_t(self.shutdown_request)
|
||||
self.updater.win_sparkle_set_shutdown_request_callback(self.callback_fn)
|
||||
|
||||
if self.use_internal():
|
||||
return
|
||||
# Get WinSparkle running
|
||||
self.updater.win_sparkle_init()
|
||||
|
||||
if sys.platform == 'win32':
|
||||
import ctypes
|
||||
|
||||
try:
|
||||
self.updater: ctypes.CDLL | None = ctypes.cdll.WinSparkle
|
||||
|
||||
# Set the appcast URL
|
||||
self.updater.win_sparkle_set_appcast_url(get_update_feed().encode())
|
||||
|
||||
# Set the appversion *without* build metadata, as WinSparkle
|
||||
# doesn't do proper Semantic Version checks.
|
||||
# NB: It 'accidentally' supports pre-release due to how it
|
||||
# splits and compares strings:
|
||||
# <https://github.com/vslavik/winsparkle/issues/214>
|
||||
self.updater.win_sparkle_set_app_build_version(str(appversion_nobuild()))
|
||||
|
||||
# set up shutdown callback
|
||||
self.callback_t = ctypes.CFUNCTYPE(None) # keep reference
|
||||
self.callback_fn = self.callback_t(self.shutdown_request)
|
||||
self.updater.win_sparkle_set_shutdown_request_callback(self.callback_fn)
|
||||
|
||||
# Get WinSparkle running
|
||||
self.updater.win_sparkle_init()
|
||||
|
||||
except Exception:
|
||||
print_exc()
|
||||
self.updater = None
|
||||
if not os.getenv("EDMC_NO_UI"):
|
||||
messagebox.showerror(
|
||||
title=appname,
|
||||
message="Updater Failed to Initialize. Please file a bug report!"
|
||||
)
|
||||
else:
|
||||
logger.error("Updater Failed to Initialize. Please file a bug report!")
|
||||
|
||||
return
|
||||
except Exception:
|
||||
print_exc()
|
||||
self.updater = None
|
||||
msg = "Updater Failed to Initialize. Please file a bug report!"
|
||||
if not os.getenv("EDMC_NO_UI"):
|
||||
messagebox.showerror(title=appname, message=msg)
|
||||
else:
|
||||
logger.error(msg)
|
||||
|
||||
def set_automatic_updates_check(self, onoroff: bool) -> None:
|
||||
"""
|
||||
@@ -187,10 +189,8 @@ class Updater:
|
||||
def check_for_updates(self) -> None:
|
||||
"""Trigger the requisite method to check for an update."""
|
||||
if self.use_internal():
|
||||
self.thread = threading.Thread(target=self.worker, name='update worker')
|
||||
self.thread.daemon = True
|
||||
self.thread = threading.Thread(target=self.worker, name='update worker', daemon=True)
|
||||
self.thread.start()
|
||||
|
||||
elif sys.platform == 'win32' and self.updater:
|
||||
self.updater.win_sparkle_check_update_with_ui()
|
||||
|
||||
@@ -218,7 +218,6 @@ class Updater:
|
||||
|
||||
except requests.RequestException as ex:
|
||||
logger.exception(f'Error retrieving update_feed file: {ex}')
|
||||
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -226,7 +225,6 @@ class Updater:
|
||||
|
||||
except SyntaxError as ex:
|
||||
logger.exception(f'Syntax error in update_feed file: {ex}')
|
||||
|
||||
return None
|
||||
|
||||
# For *these* purposes all systems are the same as 'windows', as
|
||||
@@ -270,7 +268,6 @@ class Updater:
|
||||
# LANG: Update Available Text
|
||||
status['text'] = tr.tl("{NEWVER} is available").format(NEWVER=newversion.title)
|
||||
self.root.update_idletasks()
|
||||
|
||||
else:
|
||||
logger.info("No new version available at this time")
|
||||
|
||||
|
||||
Reference in new issue
Block a user