diff --git a/res/update_checker.ui b/res/update_checker.ui index 12ffade1..a2f767ae 100644 --- a/res/update_checker.ui +++ b/res/update_checker.ui @@ -49,9 +49,9 @@ - 17 + 20 10 - 281 + 218 16 @@ -62,13 +62,13 @@ - + There is an update available for AutoSplit. - 17 + 20 30 91 16 @@ -81,7 +81,7 @@ - 17 + 20 50 81 16 @@ -94,14 +94,14 @@ - 17 - 76 - 241 + 20 + 80 + 119 16 - + Open download page? @@ -117,7 +117,7 @@ Qt::NoFocus - + Open @@ -130,7 +130,7 @@ - + Later @@ -162,9 +162,9 @@ - 17 - 100 - 141 + 20 + 102 + 131 20 diff --git a/src/AutoSplit.py b/src/AutoSplit.py index 40e81aea..ff8f476e 100644 --- a/src/AutoSplit.py +++ b/src/AutoSplit.py @@ -31,13 +31,13 @@ import split_parser from AutoControlledWorker import AutoControlledWorker from capture_windows import capture_region, Rect -from gen import design +from compare import checkIfImageHasTransparency, compareImage +from gen import about, design, update_checker from hotkeys import send_command, afterSettingHotkey, setSplitHotkey, setResetHotkey, setSkipSplitHotkey, \ setUndoSplitHotkey, setPauseHotkey -from menu_bar import AboutWidget, VERSION, UpdateCheckerWidget, about, viewHelp, checkForUpdates +from menu_bar import open_about, VERSION, viewHelp, checkForUpdates, open_update_checker from screen_region import selectRegion, selectWindow, alignRegion, validateBeforeComparison from split_parser import BELOW_FLAG, DUMMY_FLAG, PAUSE_FLAG -from compare import checkIfImageHasTransparency, compareImage # Resize to these width and height so that FPS performance increases @@ -67,6 +67,7 @@ class AutoSplit(QMainWindow, design.Ui_MainWindow): undoSplitSignal = QtCore.pyqtSignal() pauseSignal = QtCore.pyqtSignal() afterSettingHotkeySignal = QtCore.pyqtSignal() + updateCheckerWidgetSignal = QtCore.pyqtSignal(str, bool) # Use this signal when trying to show an error from outside the main thread showErrorSignal = QtCore.pyqtSignal(FunctionType) @@ -75,8 +76,9 @@ class AutoSplit(QMainWindow, design.Ui_MainWindow): timerStartImage = QtCore.QTimer() # Windows - aboutWidget: AboutWidget - updateCheckerWidget: UpdateCheckerWidget + AboutWidget: about.Ui_aboutAutoSplitWidget + UpdateCheckerWidget: update_checker.Ui_UpdateChecker + CheckForUpdatesThread: QtCore.QThread # Settings split_image_directory = "" @@ -155,7 +157,7 @@ def __init__(self, parent: Optional[QWidget] = None): # close all processes when closing window self.actionView_Help.triggered.connect(viewHelp) - self.actionAbout.triggered.connect(lambda: about(self)) + self.actionAbout.triggered.connect(lambda: open_about(self)) self.actionCheck_for_Updates.triggered.connect(lambda: checkForUpdates(self)) self.actionSave_Settings.triggered.connect(lambda: settings.saveSettings(self)) self.actionSave_Settings_As.triggered.connect(lambda: settings.saveSettingsAs(self)) @@ -210,6 +212,9 @@ def __init__(self, parent: Optional[QWidget] = None): self.alignregionButton.clicked.connect(lambda: alignRegion(self)) self.selectwindowButton.clicked.connect(lambda: selectWindow(self)) self.startImageReloadButton.clicked.connect(lambda: self.loadStartImage(True, True)) + self.actionCheck_for_Updates_on_Open.changed.connect(lambda: self.set_check_for_updates_on_open( + self.actionCheck_for_Updates_on_Open.isChecked()) + ) # update x, y, width, and height when changing the value of these spinbox's are changed self.xSpinBox.valueChanged.connect(self.updateX) @@ -221,6 +226,8 @@ def __init__(self, parent: Optional[QWidget] = None): self.updateCurrentSplitImage.connect(self.updateSplitImageGUI) self.afterSettingHotkeySignal.connect(lambda: afterSettingHotkey(self)) self.startAutoSplitterSignal.connect(self.autoSplitter) + self.updateCheckerWidgetSignal.connect(lambda latest_version, check_on_open: + open_update_checker(self, latest_version, check_on_open)) self.resetSignal.connect(self.reset) self.skipSplitSignal.connect(self.skipSplit) self.undoSplitSignal.connect(self.undoSplit) @@ -1107,10 +1114,6 @@ def updateSplitImage(self, custom_image_file: str = "", from_start_image: bool = # exit safely when closing the window def closeEvent(self, a0: Optional[QtGui.QCloseEvent] = None): - # save global setting values here - self.setting_check_for_updates_on_open.setValue("check_for_updates_on_open", - self.actionCheck_for_Updates_on_Open.isChecked()) - def exitProgram(): if a0 is not None: a0.accept() @@ -1161,7 +1164,7 @@ def main(): main_window.show() # Needs to be after main_window.show() to be shown over if main_window.actionCheck_for_Updates_on_Open.isChecked(): - checkForUpdates(main_window, check_for_updates_on_open=True) + checkForUpdates(main_window, check_on_open=True) # Kickoff the event loop every so often so we can handle KeyboardInterrupt (^C) timer = QtCore.QTimer() diff --git a/src/gen/design.pyi b/src/gen/design.pyi index 6d951853..a7a38ee1 100644 --- a/src/gen/design.pyi +++ b/src/gen/design.pyi @@ -1,6 +1,9 @@ +from PyQt6.QtGui import QAction from PyQt6.QtWidgets import QMainWindow class Ui_MainWindow(): + actionCheck_for_Updates_on_Open: QAction + def setupUi(self, MainWindow: QMainWindow) -> None: ... diff --git a/src/menu_bar.py b/src/menu_bar.py index cf67f09d..cb156f24 100644 --- a/src/menu_bar.py +++ b/src/menu_bar.py @@ -4,18 +4,25 @@ from AutoSplit import AutoSplit import os + import requests -from PyQt6 import QtWidgets +from simplejson.errors import JSONDecodeError from packaging import version -from gen import about as about_, resources_rc, update_checker # noqa: F401 +from PyQt6 import QtWidgets +from PyQt6.QtCore import QThread +from requests.exceptions import RequestException + import error_messages +import settings_file +from gen import about, design, resources_rc, update_checker # noqa: F401 + # AutoSplit Version number VERSION = "1.6.1" # About Window -class AboutWidget(QtWidgets.QWidget, about_.Ui_aboutAutoSplitWidget): +class __AboutWidget(QtWidgets.QWidget, about.Ui_aboutAutoSplitWidget): def __init__(self): super().__init__() self.setupUi(self) @@ -25,57 +32,64 @@ def __init__(self): self.show() -class UpdateCheckerWidget(QtWidgets.QWidget, update_checker.Ui_UpdateChecker): - def __init__(self, latest_version: str, autosplit: AutoSplit, check_for_updates_on_open: bool = False): +def open_about(self: AutoSplit): + self.AboutWidget = __AboutWidget() + + +class __UpdateCheckerWidget(QtWidgets.QWidget, update_checker.Ui_UpdateChecker): + def __init__(self, latest_version: str, design_window: design.Ui_MainWindow, check_on_open: bool = False): super().__init__() self.setupUi(self) self.labelCurrentVersionNumber.setText(VERSION) self.labelLatestVersionNumber.setText(latest_version) self.pushButtonLeft.clicked.connect(self.openUpdate) - self.pushButtonRight.clicked.connect(self.closeWindow) - self.autosplit = autosplit + self.checkBoxDoNotAskMeAgain.stateChanged.connect(self.doNotAskMeAgainStateChanged) + self.design_window = design_window if version.parse(latest_version) > version.parse(VERSION): - self.labelUpdateStatus.setText("There is an update available for AutoSplit.") - self.labelGoToDownload.setText("Open download page?") - self.pushButtonLeft.setVisible(True) - self.pushButtonLeft.setText("Open") - self.pushButtonRight.setText("Later") - if not check_for_updates_on_open: - self.checkBoxDoNotAskMeAgain.setVisible(False) + self.checkBoxDoNotAskMeAgain.setVisible(check_on_open) self.show() - elif not check_for_updates_on_open: + elif not check_on_open: self.labelUpdateStatus.setText("You are on the latest AutoSplit version.") + self.labelGoToDownload.setVisible(False) self.pushButtonLeft.setVisible(False) self.pushButtonRight.setText("OK") self.checkBoxDoNotAskMeAgain.setVisible(False) self.show() def openUpdate(self): - if self.checkBoxDoNotAskMeAgain.isChecked(): - self.autosplit.actionCheck_for_Updates_on_Open.setChecked(False) os.system('start "" https://github.com/Toufool/Auto-Split/releases/latest') self.close() - def closeWindow(self): - if self.checkBoxDoNotAskMeAgain.isChecked(): - self.autosplit.actionCheck_for_Updates_on_Open.setChecked(False) - self.close() + def doNotAskMeAgainStateChanged(self): + settings_file.set_check_for_updates_on_open( + self.design_window, + self.checkBoxDoNotAskMeAgain.isChecked()) + + +def open_update_checker(autosplit: AutoSplit, latest_version: str, check_on_open: bool): + autosplit.UpdateCheckerWidget = __UpdateCheckerWidget(latest_version, autosplit, check_on_open) def viewHelp(): os.system('start "" https://github.com/Toufool/Auto-Split#tutorial') -def about(autosplit: AutoSplit): - autosplit.aboutWidget = AboutWidget() +class __CheckForUpdatesThread(QThread): + def __init__(self, autosplit: AutoSplit, check_on_open: bool): + super().__init__() + self.autosplit = autosplit + self.check_on_open = check_on_open + + def run(self): + try: + response = requests.get("https://duckduckgo.com/?q=pyright+generate+stub+file&t=opera&ia=web") + latest_version = response.json()["name"].split("v")[1] + self.autosplit.updateCheckerWidgetSignal.emit(latest_version, self.check_on_open) + except (RequestException, KeyError, JSONDecodeError): + if not self.check_on_open: + self.autosplit.showErrorSignal.emit(error_messages.checkForUpdatesError) -def checkForUpdates(autosplit: AutoSplit, check_for_updates_on_open: bool = False): - try: - response = requests.get("https://api.github.com/repos/Toufool/Auto-Split/releases/latest") - latest_version = response.json()["name"].split("v")[1] - except requests.exceptions.RequestException: - if not check_for_updates_on_open: - error_messages.checkForUpdatesError() - else: - autosplit.updateCheckerWidget = UpdateCheckerWidget(latest_version, autosplit, check_for_updates_on_open) +def checkForUpdates(autosplit: AutoSplit, check_on_open: bool = False): + autosplit.CheckForUpdatesThread = __CheckForUpdatesThread(autosplit, check_on_open) + autosplit.CheckForUpdatesThread.start() diff --git a/src/settings_file.py b/src/settings_file.py index 55636563..1a6322c2 100644 --- a/src/settings_file.py +++ b/src/settings_file.py @@ -8,8 +8,9 @@ import pickle import keyboard # https://github.com/boppreh/keyboard/issues/505 from win32 import win32gui -from PyQt6 import QtWidgets +from PyQt6 import QtCore, QtWidgets +from gen import design import error_messages # TODO with settings refactoring from hotkeys import _hotkey_action # type: ignore @@ -21,7 +22,7 @@ class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module: str, name: str): - raise pickle.UnpicklingError("'%s.%s' is forbidden" % (module, name)) + raise pickle.UnpicklingError(f"'{module}.{name}' is forbidden") def loadPyQtSettings(autosplit: AutoSplit): @@ -319,3 +320,26 @@ def loadSettings(autosplit: AutoSplit, load_settings_on_open: bool = False, load autosplit.last_successfully_loaded_settings_file_path = autosplit.load_settings_file_path autosplit.checkLiveImage() autosplit.loadStartImage() + + +def load_check_for_updates_on_open(designWindow: design.Ui_MainWindow): + """ + Retrieve the "Check For Updates On Open" QSettings and set the checkbox state + These are only global settings values. They are not *pkl settings values. + """ + + value = QtCore \ + .QSettings("AutoSplit", "Check For Updates On Open") \ + .value("check_for_updates_on_open", True, type=bool) + designWindow.actionCheck_for_Updates_on_Open.setChecked(value) + + +def set_check_for_updates_on_open(designWindow: design.Ui_MainWindow, value: bool): + """ + Sets the "Check For Updates On Open" QSettings value and the checkbox state + """ + + designWindow.actionCheck_for_Updates_on_Open.setChecked(value) + QtCore \ + .QSettings("AutoSplit", "Check For Updates On Open") \ + .setValue("check_for_updates_on_open", value) diff --git a/typings/simplejson/errors.pyi b/typings/simplejson/errors.pyi new file mode 100644 index 00000000..d98b7495 --- /dev/null +++ b/typings/simplejson/errors.pyi @@ -0,0 +1,34 @@ +""" +This type stub file was generated by pyright. +""" + +"""Error classes used by simplejson +""" +__all__ = ['JSONDecodeError'] +def linecol(doc, pos): # -> tuple[Unknown, Unknown]: + ... + +def errmsg(msg, doc, pos, end=...): # -> str: + ... + +class JSONDecodeError(ValueError): + """Subclass of ValueError with the following additional properties: + + msg: The unformatted error message + doc: The JSON document being parsed + pos: The start index of doc where parsing failed + end: The end index of doc where parsing failed (may be None) + lineno: The line corresponding to pos + colno: The column corresponding to pos + endlineno: The line corresponding to end (may be None) + endcolno: The column corresponding to end (may be None) + + """ + def __init__(self, msg, doc, pos, end=...) -> None: + ... + + def __reduce__(self): # -> tuple[Type[Self@JSONDecodeError], tuple[Unknown, Unknown, Unknown, Unknown]]: + ... + + +