Skip to content

Commit a8cfdff

Browse files
committed
Merge branch 'dev' of https://github.com/Toufool/Auto-Split into Save-'Check-For-Updates-On-Open'-immediatly
2 parents 5c17beb + 3cb2aa6 commit a8cfdff

File tree

4 files changed

+96
-46
lines changed

4 files changed

+96
-46
lines changed

src/AutoSplit.py

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/usr/bin/python3.9
22
# -*- coding: utf-8 -*-
3-
from typing import Callable, List, Optional
3+
import traceback
4+
from types import FunctionType, TracebackType
5+
from typing import Callable, List, Optional, Type
46

57
from copy import copy
68
from PyQt6 import QtCore, QtGui, QtTest, QtWidgets
@@ -31,7 +33,9 @@
3133
DISPLAY_RESIZE_WIDTH = 240
3234
DISPLAY_RESIZE_HEIGHT = 180
3335
DISPLAY_RESIZE = (DISPLAY_RESIZE_WIDTH, DISPLAY_RESIZE_HEIGHT)
34-
36+
CREATE_NEW_ISSUE_MESSAGE = \
37+
"Please create a New Issue at <a href='https://github.com/Toufool/Auto-Split/issues'>"
38+
"github.com/Toufool/Auto-Split/issues</a>, describe what happened, and copy & paste the error message below"
3539

3640
class AutoSplit(QtWidgets.QMainWindow, design.Ui_MainWindow):
3741
from hotkeys import send_command
@@ -53,6 +57,8 @@ class AutoSplit(QtWidgets.QMainWindow, design.Ui_MainWindow):
5357
pauseSignal = QtCore.pyqtSignal()
5458
afterSettingHotkeySignal = QtCore.pyqtSignal()
5559
updateCheckerWidgetSignal = QtCore.pyqtSignal(str, bool)
60+
# Use this signal when trying to show an error from outside the main thread
61+
showErrorSignal = QtCore.pyqtSignal(FunctionType)
5662

5763
def __init__(self, parent=None):
5864
super(AutoSplit, self).__init__(parent)
@@ -103,7 +109,7 @@ def run(self):
103109
try:
104110
line = input()
105111
except RuntimeError:
106-
# stdin not supported or lost, stop looking for inputs
112+
self.autosplit.showErrorSignal.emit(error_messages.stdinLostError)
107113
break
108114
# TODO: "AutoSplit Integration" needs to call this and wait instead of outright killing the app.
109115
# TODO: See if we can also get LiveSplit to wait on Exit in "AutoSplit Integration"
@@ -173,6 +179,7 @@ def run(self):
173179
self.resetSignal.connect(self.reset)
174180
self.skipSplitSignal.connect(self.skipSplit)
175181
self.undoSplitSignal.connect(self.undoSplit)
182+
self.showErrorSignal.connect(lambda errorMessageBox: errorMessageBox())
176183

177184
# live image checkbox
178185
self.liveimageCheckBox.clicked.connect(self.checkLiveImage)
@@ -204,13 +211,6 @@ def run(self):
204211
self.hwnd_title = ''
205212
self.rect = ctypes.wintypes.RECT()
206213

207-
# Last loaded settings and last successful loaded settings file path to None until we try to load them
208-
self.last_loaded_settings = None
209-
self.last_successfully_loaded_settings_file_path = None
210-
211-
if not self.is_auto_controlled:
212-
self.loadSettings(load_settings_on_open=True)
213-
214214
# Automatic timer start
215215
self.timerStartImage = QtCore.QTimer()
216216
self.timerStartImage.timeout.connect(self.startImageFunction)
@@ -219,8 +219,12 @@ def run(self):
219219
self.highest_similarity = 0.0
220220
self.check_start_image_timestamp = 0.0
221221

222-
# Try to load start image
223-
self.loadStartImage()
222+
# Last loaded settings and last successful loaded settings file path to None until we try to load them
223+
self.last_loaded_settings = None
224+
self.last_successfully_loaded_settings_file_path = None
225+
226+
if not self.is_auto_controlled:
227+
self.loadSettings(load_settings_on_open=True)
224228

225229
# FUNCTIONS
226230

@@ -237,6 +241,7 @@ def browse(self):
237241
# set the split image folder line to the directory text
238242
self.split_image_directory = new_split_image_directory
239243
self.splitimagefolderLineEdit.setText(f"{new_split_image_directory}/")
244+
self.loadStartImage()
240245

241246
def checkLiveImage(self):
242247
if self.liveimageCheckBox.isChecked():
@@ -296,6 +301,10 @@ def loadStartImage(self, started_by_button=False, wait_for_delay=True):
296301
error_messages.noKeywordImageError('start_auto_splitter')
297302
return
298303

304+
if self.start_image_name is not None and (not self.splitLineEdit.text() or not self.resetLineEdit.text() or not self.pausehotkeyLineEdit.text()) and not self.is_auto_controlled:
305+
error_messages.loadStartImageError()
306+
return
307+
299308
self.split_image_filenames = os.listdir(self.split_image_directory)
300309
self.split_image_number = 0
301310
self.start_image_mask = None
@@ -340,8 +349,7 @@ def loadStartImage(self, started_by_button=False, wait_for_delay=True):
340349
QtWidgets.QApplication.processEvents()
341350

342351
def startImageFunction(self):
343-
if time.time() < self.check_start_image_timestamp \
344-
or (not self.splitLineEdit.text() and not self.is_auto_controlled):
352+
if time.time() < self.check_start_image_timestamp:
345353
pause_time_left = "{:.1f}".format(self.check_start_image_timestamp - time.time())
346354
self.currentSplitImage.setText(f'None\n (Paused before loading Start Image).\n {pause_time_left} sec remaining')
347355
return
@@ -751,6 +759,7 @@ def autoSplitter(self):
751759
reset_similarity = self.compareImage(self.reset_image, self.reset_mask, capture)
752760
if reset_similarity >= self.reset_image_threshold:
753761
self.send_command("reset")
762+
self.reset()
754763

755764
if self.checkForReset():
756765
return
@@ -1189,22 +1198,46 @@ def exit():
11891198

11901199
def main():
11911200
app = QtWidgets.QApplication(sys.argv)
1192-
app.setWindowIcon(QtGui.QIcon(':/resources/icon.ico'))
1193-
1194-
main_window = AutoSplit()
1195-
main_window.show()
1196-
if main_window.actionCheck_for_Updates_on_Open.isChecked():
1197-
checkForUpdates(main_window, check_on_open=True)
1201+
try:
1202+
app.setWindowIcon(QtGui.QIcon(':/resources/icon.ico'))
1203+
main_window = AutoSplit()
1204+
main_window.show()
1205+
# Needs to be after main_window.show() to be shown over
1206+
if main_window.actionCheck_for_Updates_on_Open.isChecked():
1207+
checkForUpdates(main_window, check_for_updates_on_open=True)
1208+
1209+
# Kickoff the event loop every so often so we can handle KeyboardInterrupt (^C)
1210+
timer = QtCore.QTimer()
1211+
timer.timeout.connect(lambda: None)
1212+
timer.start(500)
1213+
1214+
exit_code = app.exec()
1215+
except Exception as exception:
1216+
# Print error to console if not running in executable
1217+
if getattr(sys, 'frozen', False):
1218+
error_messages.exceptionTraceback(
1219+
f"AutoSplit encountered an unrecoverable exception and will now close. {CREATE_NEW_ISSUE_MESSAGE}",
1220+
exception)
1221+
else:
1222+
traceback.print_exception(type(exception), exception, exception.__traceback__)
1223+
sys.exit(1)
11981224

1199-
# Kickoff the event loop every so often so we can handle KeyboardInterrupt (^C)
1200-
timer = QtCore.QTimer()
1201-
timer.timeout.connect(lambda: None)
1202-
timer.start(500)
12031225
# Catch Keyboard Interrupts for a clean close
1204-
signal.signal(signal.SIGINT, lambda _, __: sys.exit(app))
1226+
signal.signal(signal.SIGINT, lambda code, _: sys.exit(code))
1227+
1228+
sys.exit(exit_code)
12051229

1206-
sys.exit(app.exec())
1230+
1231+
def excepthook(exceptionType: Type[BaseException], exception: BaseException, traceback: Optional[TracebackType]):
1232+
# Catch Keyboard Interrupts for a clean close
1233+
if exceptionType is KeyboardInterrupt:
1234+
sys.exit(0)
1235+
error_messages.exceptionTraceback(
1236+
"AutoSplit encountered an unhandled exception and will try to recover, "
1237+
f"however, there is no guarantee everything will work properly. {CREATE_NEW_ISSUE_MESSAGE}",
1238+
exception)
12071239

12081240

12091241
if __name__ == '__main__':
1242+
sys.excepthook = excepthook
12101243
main()

src/error_messages.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
# Error messages
2-
from PyQt6 import QtWidgets
2+
import traceback
3+
from PyQt6 import QtCore, QtWidgets
34

45

5-
def setTextMessage(message: str):
6-
msgBox = QtWidgets.QMessageBox()
7-
msgBox.setWindowTitle('Error')
8-
msgBox.setText(message)
9-
msgBox.exec()
6+
def setTextMessage(message: str, details: str = ''):
7+
messageBox = QtWidgets.QMessageBox()
8+
messageBox.setWindowTitle('Error')
9+
messageBox.setTextFormat(QtCore.Qt.TextFormat.RichText)
10+
messageBox.setText(message)
11+
if details:
12+
messageBox.setDetailedText(details)
13+
for button in messageBox.buttons():
14+
if messageBox.buttonRole(button) == QtWidgets.QMessageBox.ButtonRole.ActionRole:
15+
button.click()
16+
break
17+
messageBox.exec()
1018

1119

1220
def splitImageDirectoryError():
@@ -76,5 +84,18 @@ def noSettingsFileOnOpenError():
7684
def tooManySettingsFilesOnOpenError():
7785
setTextMessage("Too many settings files found. Only one can be loaded on open if placed in the same folder as AutoSplit.exe")
7886

87+
7988
def checkForUpdatesError():
8089
setTextMessage("An error occurred while attempting to check for updates. Please check your connection.")
90+
91+
def loadStartImageError():
92+
setTextMessage("Start Image found, but cannot be loaded unless Start, Reset, and Pause hotkeys are set. Please set these hotkeys, and then click the Reload Start Image button.")
93+
94+
def stdinLostError():
95+
setTextMessage("stdin not supported or lost, external control like LiveSplit integration will not work.")
96+
97+
98+
def exceptionTraceback(message: str, exception: Exception):
99+
setTextMessage(
100+
message,
101+
"\n".join(traceback.format_exception(None, exception, exception.__traceback__)))

src/menu_bar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def run(self):
8989
self.autosplit.updateCheckerWidgetSignal.emit(latest_version, self.check_on_open)
9090
except (RequestException, KeyError, JSONDecodeError):
9191
if not self.check_on_open:
92-
error_messages.checkForUpdatesError()
92+
self.autosplit.showErrorSignal(error_messages.checkForUpdatesError)
9393

9494

9595
def checkForUpdates(autosplit: AutoSplit, check_on_open: bool = False):

src/settings_file.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,11 @@ def saveSettingsAs(self: AutoSplit):
158158
def loadSettings(self: AutoSplit, load_settings_on_open: bool = False, load_settings_from_livesplit: bool = False):
159159
if load_settings_on_open:
160160

161-
settings_files = []
162-
for file in os.listdir(auto_split_directory):
163-
if file.endswith(".pkl"):
164-
settings_files.append(file)
161+
settings_files = [
162+
file for file
163+
in os.listdir(auto_split_directory)
164+
if file.endswith(".pkl")
165+
]
165166

166167
# find all .pkls in AutoSplit folder, error if there is none or more than 1
167168
if len(settings_files) < 1:
@@ -191,17 +192,15 @@ def loadSettings(self: AutoSplit, load_settings_on_open: bool = False, load_sett
191192
settings: List[Union[str, int]] = pickle.load(f)
192193
settings_count = len(settings)
193194
if settings_count < 18:
194-
if not load_settings_from_livesplit:
195-
error_messages.oldVersionSettingsFileError()
195+
self.showErrorSignal.emit(error_messages.oldVersionSettingsFileError)
196196
return
197197
# v1.3-1.4 settings. Add default pause_key and auto_start_on_reset_setting
198198
if settings_count == 18:
199199
settings.insert(9, '')
200200
settings.insert(20, 0)
201201
# v1.5 settings
202202
elif settings_count != 20:
203-
if not load_settings_from_livesplit:
204-
error_messages.invalidSettingsError()
203+
self.showErrorSignal.emit(error_messages.invalidSettingsError)
205204
return
206205
self.last_loaded_settings = [
207206
self.split_image_directory,
@@ -225,11 +224,7 @@ def loadSettings(self: AutoSplit, load_settings_on_open: bool = False, load_sett
225224
self.loop_setting,
226225
self.auto_start_on_reset_setting] = settings
227226
except (FileNotFoundError, MemoryError, pickle.UnpicklingError):
228-
# HACK / Workaround: Executing the error QMessageBox from the auto-controlled Worker Thread makes it hangs.
229-
# I don't like this solution as we should probably ensure the Worker works nicely with PyQt instead,
230-
# but in the mean time, this will do.
231-
if not load_settings_from_livesplit:
232-
error_messages.invalidSettingsError()
227+
self.showErrorSignal.emit(error_messages.invalidSettingsError)
233228
return
234229

235230
self.splitimagefolderLineEdit.setText(self.split_image_directory)
@@ -309,6 +304,7 @@ def loadSettings(self: AutoSplit, load_settings_on_open: bool = False, load_sett
309304

310305
self.last_successfully_loaded_settings_file_path = self.load_settings_file_path
311306
self.checkLiveImage()
307+
self.loadStartImage()
312308

313309

314310
def load_check_for_updates_on_open(designWindow: design.Ui_MainWindow):

0 commit comments

Comments
 (0)