diff --git a/README.md b/README.md index 0e139999..af47e196 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Easy to use image comparison based auto splitter for speedrunning on console or PC. -This program compares split images to a capture region of any window (OBS, Streamlabs, etc.) and automatically hits your split hotkey when there is a match. It can be used in tandem with any speedrun timer that accepts hotkeys (LiveSplit, wsplit, etc.), but can be integrated with LiveSplit. The purpose of this program is to remove the need to manually press your split hotkey and also increase the accuracy of your splits. +This program compares split images to a capture region of any window (OBS, Streamlabs, etc.) and automatically hits your split hotkey when there is a match. It can also be used to start and reset your timer, allowing for it to take full control of starting, splitting, and resetting your timer without ever having to touch a hotkey. It can be used in tandem with any speedrun timer that accepts hotkeys (LiveSplit, wsplit, etc.), but can be integrated with LiveSplit. ![Example](res/example1.6.0.gif) @@ -73,14 +73,22 @@ This program compares split images to a capture region of any window (OBS, Strea - Shows the highest similarity between the capture region and current split image. -### Similarity Threshold +### Current Similarity Threshold - When the live similarity goes above this value, the program hits your split hotkey and moves to the next split image. +### Default Similarity Threshold + +- This value will be set as the threshold for an image if there is no custom threshold set for that image. + ### Pause Time - Time in seconds that the program stops comparison after a split. Useful for if you have two of the same split images in a row and want to avoid double-splitting. Also useful for reducing CPU usage. +### Default Pause Time + +- This value will be set as the Pause Time for an image if there is no custom Pause Time set for that image. + ### Delay Time - Time in milliseconds that the program waits before hitting the split hotkey for that specific split. @@ -129,9 +137,9 @@ The start image is similar to the reset image. You can only have one start image ### Group dummy splits when undoing / skipping -If this option is disabled, AutoSplit will not account for dummy splits when undoing/skipping. Meaning it will cycle through ths splits normally even if they are dummy splits (this was the normal behavior in versions 1.2.0 and older). +If this option is disabled, AutoSplit will not account for dummy splits when undoing/skipping. Meaning it will cycle through the images normally even if they have the dummy flag `{d}` applied to them. -If it is enabled, AutoSplit will group dummy splits together with a real split when undoing/skipping. This basically allows you to tie one or more dummy splits to a real split to keep it in sync with LiveSplit/wsplit. +If it is enabled, AutoSplit will group dummy splits together with a real split when undoing/skipping. This basically allows you to tie one or more dummy splits to a real split to keep it as in sync as possible with the real splits in LiveSplit/wsplit. Examples: Given these splits: 1 dummy, 2 normal, 3 dummy, 4 dummy, 5 normal, 6 normal. @@ -157,7 +165,6 @@ If this option is disabled, when the reset hotkey is hit, the reset button is pr ### Settings - Settings files use the extension `.pkl`. Settings files can be saved and opened by using File -> Save Settings As... and File -> Load Settings. A settings file can be loaded upon opening AutoSplit if placed in the same directory as AutoSplit.exe. -- For v1.4 and below, settings work differently. Each time AutoSplit is closed, it saves a the setting file `settings.pkl` to the directory AutoSplit.exe is located in. This settings file must be in the same directory as AutoSplit.exe and is loaded upon opening the program. Settings can be reloaded using the Reload Settings button. - The settings in the settings file include split image directory, capture region, capture region dimensions, fps limit, threshold and pause time settings, all hotkeys, "Group dummy splits when undoing/skipping" check box, "Loop Split Images" check box, and "Auto Start On Reset" check box. - If you are upgrading to Windows 11, it's possible that save files may not transfer perfectly. You may need to readjust or reselect your Capture Region, for example. diff --git a/res/design.ui b/res/design.ui index 2bfff701..1e74b9d8 100644 --- a/res/design.ui +++ b/res/design.ui @@ -207,9 +207,9 @@ - 480 + 494 250 - 71 + 64 24 @@ -961,14 +961,14 @@ - 380 + 379 252 - 108 + 113 20 - Image Loop #: + Image Loop: diff --git a/src/AutoSplit.py b/src/AutoSplit.py index c1373ee9..5c168976 100644 --- a/src/AutoSplit.py +++ b/src/AutoSplit.py @@ -155,7 +155,7 @@ def run(self): self.setpausehotkeyButton.clicked.connect(self.setPauseHotkey) self.alignregionButton.clicked.connect(self.alignRegion) self.selectwindowButton.clicked.connect(self.selectWindow) - self.startImageReloadButton.clicked.connect(lambda: self.loadStartImage(True, False)) + self.startImageReloadButton.clicked.connect(lambda: self.loadStartImage(True, True)) # update x, y, width, and height when changing the value of these spinbox's are changed self.xSpinBox.valueChanged.connect(self.updateX) @@ -217,7 +217,7 @@ def run(self): self.check_start_image_timestamp = 0.0 # Try to load start image - self.loadStartImage(wait_for_delay=False) + self.loadStartImage() # FUNCTIONS @@ -323,16 +323,15 @@ def loadStartImage(self, started_by_button=False, wait_for_delay=True): self.start_image = cv2.resize(self.start_image, COMPARISON_RESIZE) start_image_pause = split_parser.pause_from_filename(self.start_image_name) - if wait_for_delay and start_image_pause is not None and start_image_pause > 0: + if not wait_for_delay and start_image_pause is not None and start_image_pause > 0: self.check_start_image_timestamp = time.time() + start_image_pause self.startImageLabel.setText("Start image: paused") - self.currentSplitImage.setText('none (paused)') self.highestsimilarityLabel.setText(' ') self.currentsimilaritythresholdnumberLabel.setText(' ') else: self.check_start_image_timestamp = 0.0 self.startImageLabel.setText("Start image: ready") - self.updateSplitImage(self.start_image_name, from_load_start_image=True) + self.updateSplitImage(self.start_image_name, from_start_image=True) self.highest_similarity = 0.0 self.start_image_split_below_threshold = False @@ -343,12 +342,15 @@ def loadStartImage(self, started_by_button=False, wait_for_delay=True): def startImageFunction(self): if time.time() < self.check_start_image_timestamp \ or (not self.splitLineEdit.text() and not self.is_auto_controlled): + pause_time_left = "{:.1f}".format(self.check_start_image_timestamp - time.time()) + self.currentSplitImage.setText(f'None\n (Paused before loading Start Image).\n {pause_time_left} sec remaining') + self.currentSplitImage.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) return if self.check_start_image_timestamp > 0: self.check_start_image_timestamp = 0.0 self.startImageLabel.setText("Start image: ready") - self.updateSplitImage(self.start_image_name) + self.updateSplitImage(self.start_image_name, from_start_image=True) capture = self.getCaptureForComparison() start_image_similarity = self.compareImage(self.start_image, self.start_image_mask, capture) @@ -390,10 +392,20 @@ def split(): self.startAutoSplitter() self.timerStartImage.stop() - self.startImageLabel.setText("Start image: started") - self.start_image_split_below_threshold = False - threading.Timer(start_image_delay / 1000, split).start() + + # delay start image if needed + if start_image_delay > 0: + self.currentSplitImage.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.startImageLabel.setText("Start image: delaying start...") + delay_start_time = time.time() + while time.time() - delay_start_time < (start_image_delay / 1000): + delay_time_left = round((start_image_delay / 1000) - (time.time() - delay_start_time), 1) + self.currentSplitImage.setText(f'Delayed Before Starting:\n {delay_time_left} sec remaining') + QtTest.QTest.qWait(1) + + self.startImageLabel.setText("Start image: started") + split() # update x, y, width, height when spinbox values are changed def updateX(self): @@ -498,6 +510,9 @@ def is_current_split_out_of_range(self): def undoSplit(self): # Can't undo until timer is started # or Undoing past the first image + if self.startautosplitterButton.text() == 'Start Auto Splitter' or ("Delayed Split") in self.currentSplitImage.text(): + return + if (not self.undosplitButton.isEnabled() and not self.is_auto_controlled) \ or self.is_current_split_out_of_range(): return @@ -519,6 +534,9 @@ def undoSplit(self): def skipSplit(self): # Can't skip or split until timer is started # or Splitting/skipping when there are no images left + if self.startautosplitterButton.text() == 'Start Auto Splitter' or ("Delayed Split") in self.currentSplitImage.text(): + return + if (not self.skipsplitButton.isEnabled() and not self.is_auto_controlled) or self.is_current_split_out_of_range(): return @@ -855,7 +873,7 @@ def autoSplitter(self): if self.number_of_split_images != self.split_image_number: # set current split image to none self.currentsplitimagefileLabel.setText(' ') - self.imageloopLabel.setText('Image Loop # -') + self.imageloopLabel.setText('Image Loop: -') if not self.is_auto_controlled: # if its the last split image and last loop number, disable the skip split button @@ -928,7 +946,7 @@ def guiChangesOnStart(self): def guiChangesOnReset(self): self.startautosplitterButton.setText('Start Auto Splitter') - self.imageloopLabel.setText("Image Loop # -") + self.imageloopLabel.setText('Image Loop: -') self.currentSplitImage.setText(' ') self.currentsplitimagefileLabel.setText(' ') self.livesimilarityLabel.setText(' ') @@ -1046,7 +1064,7 @@ def removeStartAutoSplitterImage(self): self.split_image_filenames.remove(start_auto_splitter_image_file) - def updateSplitImage(self, custom_image_file: str = '', from_load_start_image: bool = False): + def updateSplitImage(self, custom_image_file: str = '', from_start_image: bool = False): # Splitting/skipping when there are no images left or Undoing past the first image # Start image is expected to be out of range (index 0 of 0-length array) if "START_AUTO_SPLITTER" not in custom_image_file.upper() and self.is_current_split_out_of_range(): @@ -1115,11 +1133,11 @@ def updateSplitImage(self, custom_image_file: str = '', from_load_start_image: b self.split_delay = split_parser.delay_from_filename(split_image_file) # Set Image Loop # - if not from_load_start_image: + if not from_start_image: loop_tuple = self.split_image_filenames_and_loop_number[self.split_image_number] - self.imageloopLabel.setText(f"Image Loop # {loop_tuple[1]}/{loop_tuple[2]}") + self.imageloopLabel.setText(f"Image Loop: {loop_tuple[1]}/{loop_tuple[2]}") else: - self.imageloopLabel.setText("Image Loop # 1/1") + self.imageloopLabel.setText("Image Loop: 1/1") # need to set split below threshold to false each time an image updates. self.split_below_threshold = False diff --git a/src/design.py b/src/design.py index fd9d690f..28f0990c 100644 --- a/src/design.py +++ b/src/design.py @@ -72,7 +72,7 @@ def setupUi(self, MainWindow): self.resetButton.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.resetButton.setObjectName("resetButton") self.undosplitButton = QtWidgets.QPushButton(self.centralwidget) - self.undosplitButton.setGeometry(QtCore.QRect(480, 250, 71, 24)) + self.undosplitButton.setGeometry(QtCore.QRect(494, 250, 64, 24)) self.undosplitButton.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.undosplitButton.setObjectName("undosplitButton") self.skipsplitButton = QtWidgets.QPushButton(self.centralwidget) @@ -279,7 +279,7 @@ def setupUi(self, MainWindow): self.selectwindowButton.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.selectwindowButton.setObjectName("selectwindowButton") self.imageloopLabel = QtWidgets.QLabel(self.centralwidget) - self.imageloopLabel.setGeometry(QtCore.QRect(380, 252, 108, 20)) + self.imageloopLabel.setGeometry(QtCore.QRect(379, 252, 113, 20)) self.imageloopLabel.setObjectName("imageloopLabel") self.pausehotkeyLabel = QtWidgets.QLabel(self.centralwidget) self.pausehotkeyLabel.setGeometry(QtCore.QRect(230, 418, 31, 16)) @@ -482,7 +482,7 @@ def retranslateUi(self, MainWindow): self.alignregionButton.setText(_translate("MainWindow", "Align Region")) self.groupDummySplitsCheckBox.setText(_translate("MainWindow", "Group dummy splits when undoing/skipping")) self.selectwindowButton.setText(_translate("MainWindow", "Select Window")) - self.imageloopLabel.setText(_translate("MainWindow", "Image Loop #:")) + self.imageloopLabel.setText(_translate("MainWindow", "Image Loop:")) self.pausehotkeyLabel.setText(_translate("MainWindow", "Pause")) self.setpausehotkeyButton.setText(_translate("MainWindow", "Set Hotkey")) self.loopCheckBox.setText(_translate("MainWindow", "Loop Split Images"))