From 8e1e82acb189f372ff9cc7d4f74363a63a6896ee Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Thu, 9 Apr 2020 12:31:07 +0200
Subject: [PATCH 01/19] Add pause hotkey setting and pause flag {p}
---
README.md | 7 +--
res/design.ui | 84 ++++++++++++++++++++++++++--------
src/AutoSplit.py | 108 +++++++++++++++++++++++++++++++++++++-------
src/split_parser.py | 7 +--
4 files changed, 161 insertions(+), 45 deletions(-)
diff --git a/README.md b/README.md
index 14a38039..2f41a1a0 100644
--- a/README.md
+++ b/README.md
@@ -70,9 +70,10 @@ This program compares split images to a capture region of any window (OBS, xspli
- Custom delay times are placed between hash signs `##` in the filename. Note that these are in milliseconds. For example, a 10 second split delay would be `#10000#`. You cannot skip or undo splits during split delays.
- Image loop amounts are placed between at symbols `@@` in the filename. For example, a specific image that you want to split 5 times in a row would be `@5@`. The current loop # is conveniently located beneath the current split image.
- Flags are placed between curly brackets `{}` in the filename. Multiple flags are placed in the same set of curly brackets. Current available flags:
- - {d} dummy split image. When matched, it moves to the next image without hitting your split hokey.
- - {b} split when similarity goes below the threshold rather than above. When a split image filename has this flag, the split image similarity will go above the threshold, do nothing, and then split the next time the similarity goes below the threshold.
- - {m} masked split image. This allows you to customize what you want compared in your split image by using transparency. Transparent pixels in the split image are ignored when comparing. This is useful if only a certain part of the capture region is consistent (for example, consistent text on the screen, but the background is always different). These images MUST be .png and contain transparency. For more on this, see [How to Create a Masked Image](https://github.com/Toufool/Auto-Split/blob/master/README.md#how-to-create-a-masked-image). Histogram or L2 norm comparison is recommended if you use any masked images. It is highly recommended that you do NOT use pHash comparison if you use any masked images, as it is very inaccurate
+ - `{d}` dummy split image. When matched, it moves to the next image without hitting your split hotkey.
+ - `{b}` split when similarity goes below the threshold rather than above. When a split image filename has this flag, the split image similarity will go above the threshold, do nothing, and then split the next time the similarity goes below the threshold.
+ - `{m}` masked split image. This allows you to customize what you want compared in your split image by using transparency. Transparent pixels in the split image are ignored when comparing. This is useful if only a certain part of the capture region is consistent (for example, consistent text on the screen, but the background is always different). These images MUST be .png and contain transparency. For more on this, see [How to Create a Masked Image](https://github.com/Toufool/Auto-Split/blob/master/README.md#how-to-create-a-masked-image). Histogram or L2 norm comparison is recommended if you use any masked images. It is highly recommended that you do NOT use pHash comparison if you use any masked images, as it is very inaccurate
+ - `{p}` pause/unpause timer. This allows you to let AutoSplit remove the loading times. Use `{d}` as well if you only want to pause/unpause but don't want to split. There is no "Pause / Unpause" button in AutoSplit because it doesn't keep track of whether your timer is currently paused.
- Filename examples:
- `001_SplitName_(0.9)_[10].png` is a split image with a threshold of 0.9 and a pause time of 10 seconds.
- `002_SplitName_(0.9)_[10]_{d}.png` is the second split image with a threshold of 0.9, pause time of 10, and is a dummy split.
diff --git a/res/design.ui b/res/design.ui
index ac4545ac..abf75c61 100644
--- a/res/design.ui
+++ b/res/design.ui
@@ -19,13 +19,13 @@
612
- 490
+ 520
612
- 490
+ 520
@@ -139,7 +139,7 @@
10
- 378
+ 408
91
16
@@ -152,7 +152,7 @@
160
- 383
+ 413
64
22
@@ -171,7 +171,7 @@
501
- 425
+ 455
101
31
@@ -187,7 +187,7 @@
501
- 385
+ 415
101
31
@@ -235,7 +235,7 @@
10
- 420
+ 450
111
16
@@ -280,7 +280,7 @@
10
- 330
+ 360
111
17
@@ -302,7 +302,7 @@
10
- 351
+ 381
131
17
@@ -321,7 +321,7 @@
160
- 332
+ 362
46
13
@@ -334,7 +334,7 @@
160
- 353
+ 383
46
13
@@ -395,6 +395,19 @@
Undo Split
+
+
+
+ 249
+ 440
+ 61
+ 16
+
+
+
+ Pause / Unpause
+
+
@@ -450,10 +463,23 @@
true
+
+
+
+ 316
+ 440
+ 81
+ 20
+
+
+
+ true
+
+
- 409
+ 410
320
71
21
@@ -514,6 +540,22 @@
Set Hotkey
+
+
+
+ 410
+ 440
+ 71
+ 21
+
+
+
+ Qt::NoFocus
+
+
+ Set Hotkey
+
+
@@ -704,7 +746,7 @@
489
296
2
- 163
+ 193
@@ -989,7 +1031,7 @@
143
- 299
+ 329
81
22
@@ -1014,7 +1056,7 @@
160
- 425
+ 455
64
22
@@ -1036,7 +1078,7 @@
10
- 435
+ 465
121
17
@@ -1061,7 +1103,7 @@
10
- 394
+ 424
111
17
@@ -1083,7 +1125,7 @@
10
- 300
+ 330
101
16
@@ -1112,7 +1154,7 @@
252
- 440
+ 470
230
17
@@ -1163,14 +1205,17 @@
resetLabel
skiptsplitLabel
undosplitLabel
+ pausehotkeyLabel
splitLineEdit
undosplitLineEdit
skipsplitLineEdit
resetLineEdit
+ pauseLineEdit
setsplithotkeyButton
setresethotkeyButton
setskipsplithotkeyButton
setundosplithotkeyButton
+ setpausehotkeyButton
line_live_bottom
line_live_top
line_live_right
@@ -1255,6 +1300,7 @@
resetLineEdit
skipsplitLineEdit
undosplitLineEdit
+ pauseLineEdit
groupDummySplitsCheckBox
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index 92eac961..4826561b 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -63,6 +63,7 @@ def __init__(self, parent=None):
self.setresethotkeyButton.clicked.connect(self.setResetHotkey)
self.setskipsplithotkeyButton.clicked.connect(self.setSkipSplitHotkey)
self.setundosplithotkeyButton.clicked.connect(self.setUndoSplitHotkey)
+ self.setpausehotkeyButton.clicked.connect(self.setPauseHotkey)
self.alignregionButton.clicked.connect(self.alignRegion)
self.selectwindowButton.clicked.connect(self.selectWindow)
self.reloadsettingsButton.clicked.connect(self.loadSettings)
@@ -416,7 +417,7 @@ def callback():
# If the key the user presses is equal to itself or another hotkey already set,
# this causes issues. so here, it catches that, and will make no changes to the hotkey.
try:
- if self.split_key == self.splitLineEdit.text() or self.split_key == self.resetLineEdit.text() or self.split_key == self.skipsplitLineEdit.text() or self.split_key == self.undosplitLineEdit.text():
+ if self.split_key == self.splitLineEdit.text() or self.split_key == self.resetLineEdit.text() or self.split_key == self.skipsplitLineEdit.text() or self.split_key == self.undosplitLineEdit.text() or self.split_key == self.pauseLineEdit.text():
self.split_hotkey = keyboard.add_hotkey(self.old_split_key, self.startAutoSplitter)
self.afterSettingHotkeySignal.emit()
return
@@ -461,7 +462,7 @@ def callback():
pass
self.reset_key = keyboard.read_hotkey(False)
try:
- if self.reset_key == self.splitLineEdit.text() or self.reset_key == self.resetLineEdit.text() or self.reset_key == self.skipsplitLineEdit.text() or self.reset_key == self.undosplitLineEdit.text():
+ if self.reset_key == self.splitLineEdit.text() or self.reset_key == self.resetLineEdit.text() or self.reset_key == self.skipsplitLineEdit.text() or self.reset_key == self.undosplitLineEdit.text() or self.reset_key == self.pauseLineEdit.text():
self.reset_hotkey = keyboard.add_hotkey(self.old_reset_key, self.startReset)
self.afterSettingHotkeySignal.emit()
return
@@ -499,7 +500,7 @@ def callback():
self.skip_split_key = keyboard.read_hotkey(False)
try:
- if self.skip_split_key == self.splitLineEdit.text() or self.skip_split_key == self.resetLineEdit.text() or self.skip_split_key == self.skipsplitLineEdit.text() or self.skip_split_key == self.undosplitLineEdit.text():
+ if self.skip_split_key == self.splitLineEdit.text() or self.skip_split_key == self.resetLineEdit.text() or self.skip_split_key == self.skipsplitLineEdit.text() or self.skip_split_key == self.undosplitLineEdit.text() or self.skip_split_key == self.pauseLineEdit.text():
self.skip_split_hotkey = keyboard.add_hotkey(self.old_skip_split_key, self.startSkipSplit)
self.afterSettingHotkeySignal.emit()
return
@@ -539,7 +540,7 @@ def callback():
self.undo_split_key = keyboard.read_hotkey(False)
try:
- if self.undo_split_key == self.splitLineEdit.text() or self.undo_split_key == self.resetLineEdit.text() or self.undo_split_key == self.skipsplitLineEdit.text() or self.undo_split_key == self.undosplitLineEdit.text():
+ if self.undo_split_key == self.splitLineEdit.text() or self.undo_split_key == self.resetLineEdit.text() or self.undo_split_key == self.skipsplitLineEdit.text() or self.undo_split_key == self.undosplitLineEdit.text() or self.undo_split_key == self.pauseLineEdit.text():
self.undo_split_hotkey = keyboard.add_hotkey(self.old_undo_split_key, self.startUndoSplit)
self.afterSettingHotkeySignal.emit()
return
@@ -566,6 +567,37 @@ def callback():
t.start()
return
+ # this one is shorter because AutoSplit will ignore pause hotkey presses
+ # since it doesn't keep track of whether the timer is paused
+ def setPauseHotkey(self):
+ self.setpausehotkeyButton.setText('Press a key..')
+ self.beforeSettingHotkey()
+
+ def callback():
+ self.pause_key = keyboard.read_hotkey(False)
+ try:
+ if self.pause_key == self.splitLineEdit.text() or self.pause_key == self.resetLineEdit.text() or self.pause_key == self.skipsplitLineEdit.text() or self.pause_key == self.undosplitLineEdit.text() or self.pause_key == self.pauseLineEdit.text():
+ self.afterSettingHotkeySignal.emit()
+ return
+ except AttributeError:
+ self.afterSettingHotkeySignal.emit()
+ return
+ try:
+ if '+' in self.pause_key:
+ self.afterSettingHotkeySignal.emit()
+ return
+ except AttributeError:
+ self.afterSettingHotkeySignal.emit()
+ return
+ self.pauseLineEdit.setText(self.pause_key)
+ self.old_pause_key = self.pause_key
+ self.afterSettingHotkeySignal.emit()
+ return
+
+ t = threading.Thread(target=callback)
+ t.start()
+ return
+
# do all of these after you click "set hotkey" but before you type the hotkey.
def beforeSettingHotkey(self):
self.startautosplitterButton.setEnabled(False)
@@ -573,6 +605,7 @@ def beforeSettingHotkey(self):
self.setresethotkeyButton.setEnabled(False)
self.setskipsplithotkeyButton.setEnabled(False)
self.setundosplithotkeyButton.setEnabled(False)
+ self.setpausehotkeyButton.setEnabled(False)
self.reloadsettingsButton.setEnabled(False)
# do all of these things after you set a hotkey. a signal connects to this because
@@ -582,11 +615,13 @@ def afterSettingHotkey(self):
self.setresethotkeyButton.setText('Set Hotkey')
self.setskipsplithotkeyButton.setText('Set Hotkey')
self.setundosplithotkeyButton.setText('Set Hotkey')
+ self.setpausehotkeyButton.setText('Set Hotkey')
self.startautosplitterButton.setEnabled(True)
self.setsplithotkeyButton.setEnabled(True)
self.setresethotkeyButton.setEnabled(True)
self.setskipsplithotkeyButton.setEnabled(True)
self.setundosplithotkeyButton.setEnabled(True)
+ self.setpausehotkeyButton.setEnabled(True)
self.reloadsettingsButton.setEnabled(True)
return
@@ -762,6 +797,11 @@ def autoSplitter(self):
self.customThresholdError(image)
return
+ if self.pauseLineEdit.text() == '' and split_parser.flags_from_filename(image) & 0x08 == 0x08:
+ # Error, no pause hotkey set even though pause flag is set
+ self.pauseHotkeyError()
+ return
+
if self.splitLineEdit.text() == '':
self.splitHotkeyError()
return
@@ -829,6 +869,7 @@ def autoSplitter(self):
self.setresethotkeyButton.setEnabled(False)
self.setskipsplithotkeyButton.setEnabled(False)
self.setundosplithotkeyButton.setEnabled(False)
+ self.setpausehotkeyButton.setEnabled(False)
self.custompausetimesCheckBox.setEnabled(False)
self.customthresholdsCheckBox.setEnabled(False)
self.groupDummySplitsCheckBox.setEnabled(False)
@@ -891,6 +932,7 @@ def autoSplitter(self):
self.setresethotkeyButton.setEnabled(True)
self.setskipsplithotkeyButton.setEnabled(True)
self.setundosplithotkeyButton.setEnabled(True)
+ self.setpausehotkeyButton.setEnabled(True)
self.custompausetimesCheckBox.setEnabled(True)
self.customthresholdsCheckBox.setEnabled(True)
self.groupDummySplitsCheckBox.setEnabled(True)
@@ -955,9 +997,9 @@ def autoSplitter(self):
# comes here when threshold gets met
- # We need to make sure that this isn't a dummy split before sending
- # the key press.
- if (self.flags & 0x01 == 0x01):
+ # We need to make sure that this isn't a dummy split without pause flag
+ # before sending a key press.
+ if (self.flags & 0x09 == 0x01):
pass
else:
# If it's a delayed split, check if the delay has passed
@@ -993,6 +1035,7 @@ def autoSplitter(self):
self.setresethotkeyButton.setEnabled(True)
self.setskipsplithotkeyButton.setEnabled(True)
self.setundosplithotkeyButton.setEnabled(True)
+ self.setpausehotkeyButton.setEnabled(True)
self.custompausetimesCheckBox.setEnabled(True)
self.customthresholdsCheckBox.setEnabled(True)
self.groupDummySplitsCheckBox.setEnabled(True)
@@ -1011,9 +1054,15 @@ def autoSplitter(self):
QtTest.QTest.qWait(1)
- # Split key press
self.waiting_for_split_delay = False
- keyboard.send(str(self.splitLineEdit.text()))
+
+ # Split key press unless dummy flag is set
+ if (self.flags & 0x01 == 0x00):
+ keyboard.send(str(self.splitLineEdit.text()))
+
+ # Pause key press if pause flag is set
+ if (self.flags & 0x08 == 0x08):
+ keyboard.send(str(self.pauseLineEdit.text()))
#increase loop number if needed, set to 1 if it was the last loop.
if self.loop_number < self.split_image_loop_amount[self.split_image_number]:
@@ -1079,6 +1128,7 @@ def autoSplitter(self):
self.setresethotkeyButton.setEnabled(True)
self.setskipsplithotkeyButton.setEnabled(True)
self.setundosplithotkeyButton.setEnabled(True)
+ self.setpausehotkeyButton.setEnabled(True)
self.custompausetimesCheckBox.setEnabled(True)
self.customthresholdsCheckBox.setEnabled(True)
self.groupDummySplitsCheckBox.setEnabled(True)
@@ -1117,6 +1167,7 @@ def autoSplitter(self):
self.setresethotkeyButton.setEnabled(True)
self.setskipsplithotkeyButton.setEnabled(True)
self.setundosplithotkeyButton.setEnabled(True)
+ self.setpausehotkeyButton.setEnabled(True)
self.custompausetimesCheckBox.setEnabled(True)
self.customthresholdsCheckBox.setEnabled(True)
self.groupDummySplitsCheckBox.setEnabled(True)
@@ -1353,6 +1404,12 @@ def resetHotkeyError(self):
msgBox.setText("Your split image folder contains a reset image, but no reset hotkey is set.")
msgBox.exec_()
+ def pauseHotkeyError(self):
+ msgBox = QtGui.QMessageBox()
+ msgBox.setWindowTitle('Error')
+ msgBox.setText("Your split image folder contains an image with the {p} flag, but no pause hotkey is set.")
+ msgBox.exec_()
+
def dummySplitsError(self):
msgBox = QtGui.QMessageBox()
msgBox.setWindowTitle('Error')
@@ -1386,6 +1443,7 @@ def saveSettings(self):
self.reset_key = str(self.resetLineEdit.text())
self.skip_split_key = str(self.skipsplitLineEdit.text())
self.undo_split_key = str(self.undosplitLineEdit.text())
+ self.pause_key = str(self.pauseLineEdit.text())
if self.custompausetimesCheckBox.isChecked():
self.custom_pause_times_setting = 1
@@ -1415,17 +1473,27 @@ def saveSettings(self):
self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
self.hwnd_title,
self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting], f)
+ self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.pause_key], f)
def loadSettings(self):
try:
- with open('settings.pkl', 'rb') as f:
- [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting] = pickle.load(f)
+ with pickle.load(open('settings.pkl', 'rb')) as f:
+ if len(f) == 18:
+
+ # the settings file might not include the pause hotkey yet
+ [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
+ self.fps_limit, self.split_key,
+ self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
+ self.hwnd_title,
+ self.custom_pause_times_setting, self.custom_thresholds_setting,
+ self.group_dummy_splits_undo_skip_setting, self.loop_setting] = f
+ else:
+ [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
+ self.fps_limit, self.split_key,
+ self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
+ self.hwnd_title,
+ self.custom_pause_times_setting, self.custom_thresholds_setting,
+ self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.pause_key] = f
self.split_image_directory = str(self.split_image_directory)
self.splitimagefolderLineEdit.setText(self.split_image_directory)
@@ -1506,6 +1574,12 @@ def loadSettings(self):
except ValueError:
pass
+ try:
+ self.pauseLineEdit.setText(str(self.pause_key))
+ self.old_pause_key = self.pause_key
+ except ValueError:
+ pass
+
self.checkLiveImage()
except IOError:
diff --git a/src/split_parser.py b/src/split_parser.py
index 226efdd4..ce8d1e59 100644
--- a/src/split_parser.py
+++ b/src/split_parser.py
@@ -98,7 +98,7 @@ def flags_from_filename(filename):
List of flags:
'd' = dummy, do nothing when this split is found
'm' = mask, use a mask when comparing this split
- 'b' = below threshold, after threshold is met, split when it goes below the threhsold.
+ 'b' = below threshold, after threshold is met, split when it goes below the threshold.
'p' = pause, hit pause key when this split is found
"""
@@ -130,11 +130,6 @@ def flags_from_filename(filename):
# return 0. We don't want to interpret any misleading filenames
return 0
- # Check for any conflicting flags that were set
- # For instance, we can't have a dummy split also pause
- if (flags & DUMMY_FLAG == DUMMY_FLAG) and (flags & PAUSE_FLAG == PAUSE_FLAG):
- return 0
-
return flags
def is_reset_image(filename):
From c587bcaed50d7a1cddf1440bc933d5bda0b95d01 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Thu, 9 Apr 2020 13:35:59 +0200
Subject: [PATCH 02/19] Fix settings.pkl not loading
---
src/AutoSplit.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index 4826561b..69ead29d 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -1466,7 +1466,7 @@ def saveSettings(self):
self.loop_setting = 0
# save settings to settings.pkl
- with open('settings.pkl', 'wb') as f:
+ with open(os.path.join(sys.path[0], 'settings.pkl'), 'wb') as f:
pickle.dump(
[self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
self.fps_limit, self.split_key,
@@ -1477,7 +1477,7 @@ def saveSettings(self):
def loadSettings(self):
try:
- with pickle.load(open('settings.pkl', 'rb')) as f:
+ with pickle.load(open(os.path.join(sys.path[0], 'settings.pkl'), 'rb')) as f:
if len(f) == 18:
# the settings file might not include the pause hotkey yet
From 850694c2ceda6a9a6e8dbabd63ad92d94ec0aba1 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Wed, 15 Apr 2020 01:15:11 +0200
Subject: [PATCH 03/19] Apply design.ui's changes to design.py
---
src/design.py | 59 +++++++++++++++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 21 deletions(-)
diff --git a/src/design.py b/src/design.py
index f3c86435..bac1e67d 100644
--- a/src/design.py
+++ b/src/design.py
@@ -25,14 +25,14 @@ def _translate(context, text, disambig):
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
- MainWindow.resize(612, 490)
+ MainWindow.resize(612, 520)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
MainWindow.setSizePolicy(sizePolicy)
- MainWindow.setMinimumSize(QtCore.QSize(612, 490))
- MainWindow.setMaximumSize(QtCore.QSize(612, 490))
+ MainWindow.setMinimumSize(QtCore.QSize(612, 520))
+ MainWindow.setMaximumSize(QtCore.QSize(612, 520))
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8("../../VideoAutoSplitter/icon.ico")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
@@ -71,20 +71,20 @@ def setupUi(self, MainWindow):
self.selectregionButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.selectregionButton.setObjectName(_fromUtf8("selectregionButton"))
self.similaritythresholdLabel = QtGui.QLabel(self.centralwidget)
- self.similaritythresholdLabel.setGeometry(QtCore.QRect(10, 378, 91, 16))
+ self.similaritythresholdLabel.setGeometry(QtCore.QRect(10, 408, 91, 16))
self.similaritythresholdLabel.setObjectName(_fromUtf8("similaritythresholdLabel"))
self.similaritythresholdDoubleSpinBox = QtGui.QDoubleSpinBox(self.centralwidget)
- self.similaritythresholdDoubleSpinBox.setGeometry(QtCore.QRect(160, 383, 64, 22))
+ self.similaritythresholdDoubleSpinBox.setGeometry(QtCore.QRect(160, 413, 64, 22))
self.similaritythresholdDoubleSpinBox.setMaximum(1.0)
self.similaritythresholdDoubleSpinBox.setSingleStep(0.01)
self.similaritythresholdDoubleSpinBox.setProperty("value", 0.9)
self.similaritythresholdDoubleSpinBox.setObjectName(_fromUtf8("similaritythresholdDoubleSpinBox"))
self.startautosplitterButton = QtGui.QPushButton(self.centralwidget)
- self.startautosplitterButton.setGeometry(QtCore.QRect(501, 425, 101, 31))
+ self.startautosplitterButton.setGeometry(QtCore.QRect(501, 455, 101, 31))
self.startautosplitterButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.startautosplitterButton.setObjectName(_fromUtf8("startautosplitterButton"))
self.resetButton = QtGui.QPushButton(self.centralwidget)
- self.resetButton.setGeometry(QtCore.QRect(501, 385, 101, 31))
+ self.resetButton.setGeometry(QtCore.QRect(501, 415, 101, 31))
self.resetButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.resetButton.setObjectName(_fromUtf8("resetButton"))
self.reloadsettingsButton = QtGui.QPushButton(self.centralwidget)
@@ -100,7 +100,7 @@ def setupUi(self, MainWindow):
self.skipsplitButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.skipsplitButton.setObjectName(_fromUtf8("skipsplitButton"))
self.pauseLabel = QtGui.QLabel(self.centralwidget)
- self.pauseLabel.setGeometry(QtCore.QRect(10, 420, 111, 16))
+ self.pauseLabel.setGeometry(QtCore.QRect(10, 450, 111, 16))
self.pauseLabel.setObjectName(_fromUtf8("pauseLabel"))
self.checkfpsButton = QtGui.QPushButton(self.centralwidget)
self.checkfpsButton.setGeometry(QtCore.QRect(5, 225, 51, 21))
@@ -111,22 +111,22 @@ def setupUi(self, MainWindow):
self.fpsLabel.setObjectName(_fromUtf8("fpsLabel"))
self.showlivesimilarityCheckBox = QtGui.QCheckBox(self.centralwidget)
self.showlivesimilarityCheckBox.setEnabled(True)
- self.showlivesimilarityCheckBox.setGeometry(QtCore.QRect(10, 330, 111, 17))
+ self.showlivesimilarityCheckBox.setGeometry(QtCore.QRect(10, 360, 111, 17))
self.showlivesimilarityCheckBox.setChecked(True)
self.showlivesimilarityCheckBox.setTristate(False)
self.showlivesimilarityCheckBox.setObjectName(_fromUtf8("showlivesimilarityCheckBox"))
self.showhighestsimilarityCheckBox = QtGui.QCheckBox(self.centralwidget)
self.showhighestsimilarityCheckBox.setEnabled(True)
- self.showhighestsimilarityCheckBox.setGeometry(QtCore.QRect(10, 351, 131, 17))
+ self.showhighestsimilarityCheckBox.setGeometry(QtCore.QRect(10, 381, 131, 17))
self.showhighestsimilarityCheckBox.setChecked(True)
self.showhighestsimilarityCheckBox.setTristate(False)
self.showhighestsimilarityCheckBox.setObjectName(_fromUtf8("showhighestsimilarityCheckBox"))
self.livesimilarityLabel = QtGui.QLabel(self.centralwidget)
- self.livesimilarityLabel.setGeometry(QtCore.QRect(160, 332, 46, 13))
+ self.livesimilarityLabel.setGeometry(QtCore.QRect(160, 362, 46, 13))
self.livesimilarityLabel.setText(_fromUtf8(""))
self.livesimilarityLabel.setObjectName(_fromUtf8("livesimilarityLabel"))
self.highestsimilarityLabel = QtGui.QLabel(self.centralwidget)
- self.highestsimilarityLabel.setGeometry(QtCore.QRect(160, 353, 46, 13))
+ self.highestsimilarityLabel.setGeometry(QtCore.QRect(160, 383, 46, 13))
self.highestsimilarityLabel.setText(_fromUtf8(""))
self.highestsimilarityLabel.setObjectName(_fromUtf8("highestsimilarityLabel"))
self.splitLabel = QtGui.QLabel(self.centralwidget)
@@ -141,6 +141,9 @@ def setupUi(self, MainWindow):
self.undosplitLabel = QtGui.QLabel(self.centralwidget)
self.undosplitLabel.setGeometry(QtCore.QRect(249, 410, 61, 16))
self.undosplitLabel.setObjectName(_fromUtf8("undosplitLabel"))
+ self.pausehotkeyLabel = QtGui.QLabel(self.centralwidget)
+ self.pausehotkeyLabel.setGeometry(QtCore.QRect(249, 440, 61, 16))
+ self.pausehotkeyLabel.setObjectName(_fromUtf8("pausehotkeyLabel"))
self.splitLineEdit = QtGui.QLineEdit(self.centralwidget)
self.splitLineEdit.setGeometry(QtCore.QRect(316, 320, 81, 20))
self.splitLineEdit.setReadOnly(True)
@@ -158,8 +161,12 @@ def setupUi(self, MainWindow):
self.resetLineEdit.setGeometry(QtCore.QRect(316, 350, 81, 20))
self.resetLineEdit.setReadOnly(True)
self.resetLineEdit.setObjectName(_fromUtf8("resetLineEdit"))
+ self.pauseLineEdit = QtGui.QLineEdit(self.centralwidget)
+ self.pauseLineEdit.setGeometry(QtCore.QRect(316, 440, 81, 20))
+ self.pauseLineEdit.setReadOnly(True)
+ self.pauseLineEdit.setObjectName(_fromUtf8("pauseLineEdit"))
self.setsplithotkeyButton = QtGui.QPushButton(self.centralwidget)
- self.setsplithotkeyButton.setGeometry(QtCore.QRect(409, 320, 71, 21))
+ self.setsplithotkeyButton.setGeometry(QtCore.QRect(410, 320, 71, 21))
self.setsplithotkeyButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.setsplithotkeyButton.setObjectName(_fromUtf8("setsplithotkeyButton"))
self.setresethotkeyButton = QtGui.QPushButton(self.centralwidget)
@@ -174,6 +181,10 @@ def setupUi(self, MainWindow):
self.setundosplithotkeyButton.setGeometry(QtCore.QRect(410, 410, 71, 21))
self.setundosplithotkeyButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.setundosplithotkeyButton.setObjectName(_fromUtf8("setundosplithotkeyButton"))
+ self.setpausehotkeyButton = QtGui.QPushButton(self.centralwidget)
+ self.setpausehotkeyButton.setGeometry(QtCore.QRect(410, 440, 71, 21))
+ self.setpausehotkeyButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.setpausehotkeyButton.setObjectName(_fromUtf8("setpausehotkeyButton"))
self.line_live_bottom = QtGui.QFrame(self.centralwidget)
self.line_live_bottom.setGeometry(QtCore.QRect(111, 247, 240, 2))
self.line_live_bottom.setFrameShadow(QtGui.QFrame.Plain)
@@ -193,7 +204,7 @@ def setupUi(self, MainWindow):
self.line_live_right.setFrameShape(QtGui.QFrame.VLine)
self.line_live_right.setObjectName(_fromUtf8("line_live_right"))
self.line_left = QtGui.QFrame(self.centralwidget)
- self.line_left.setGeometry(QtCore.QRect(234, 296, 2, 163))
+ self.line_left.setGeometry(QtCore.QRect(234, 296, 2, 193))
self.line_left.setFrameShadow(QtGui.QFrame.Plain)
self.line_left.setLineWidth(1)
self.line_left.setFrameShape(QtGui.QFrame.VLine)
@@ -319,40 +330,40 @@ def setupUi(self, MainWindow):
self.yLabel.setGeometry(QtCore.QRect(81, 139, 7, 16))
self.yLabel.setObjectName(_fromUtf8("yLabel"))
self.comparisonmethodComboBox = QtGui.QComboBox(self.centralwidget)
- self.comparisonmethodComboBox.setGeometry(QtCore.QRect(143, 299, 81, 22))
+ self.comparisonmethodComboBox.setGeometry(QtCore.QRect(143, 329, 81, 22))
self.comparisonmethodComboBox.setObjectName(_fromUtf8("comparisonmethodComboBox"))
self.comparisonmethodComboBox.addItem(_fromUtf8(""))
self.comparisonmethodComboBox.addItem(_fromUtf8(""))
self.comparisonmethodComboBox.addItem(_fromUtf8(""))
self.pauseDoubleSpinBox = QtGui.QDoubleSpinBox(self.centralwidget)
- self.pauseDoubleSpinBox.setGeometry(QtCore.QRect(160, 425, 64, 22))
+ self.pauseDoubleSpinBox.setGeometry(QtCore.QRect(160, 455, 64, 22))
self.pauseDoubleSpinBox.setMaximum(999999999.0)
self.pauseDoubleSpinBox.setSingleStep(1.0)
self.pauseDoubleSpinBox.setProperty("value", 10.0)
self.pauseDoubleSpinBox.setObjectName(_fromUtf8("pauseDoubleSpinBox"))
self.custompausetimesCheckBox = QtGui.QCheckBox(self.centralwidget)
self.custompausetimesCheckBox.setEnabled(True)
- self.custompausetimesCheckBox.setGeometry(QtCore.QRect(10, 435, 121, 17))
+ self.custompausetimesCheckBox.setGeometry(QtCore.QRect(10, 465, 121, 17))
self.custompausetimesCheckBox.setWhatsThis(_fromUtf8(""))
self.custompausetimesCheckBox.setChecked(False)
self.custompausetimesCheckBox.setTristate(False)
self.custompausetimesCheckBox.setObjectName(_fromUtf8("custompausetimesCheckBox"))
self.customthresholdsCheckBox = QtGui.QCheckBox(self.centralwidget)
self.customthresholdsCheckBox.setEnabled(True)
- self.customthresholdsCheckBox.setGeometry(QtCore.QRect(10, 394, 111, 17))
+ self.customthresholdsCheckBox.setGeometry(QtCore.QRect(10, 424, 111, 17))
self.customthresholdsCheckBox.setWhatsThis(_fromUtf8(""))
self.customthresholdsCheckBox.setChecked(False)
self.customthresholdsCheckBox.setTristate(False)
self.customthresholdsCheckBox.setObjectName(_fromUtf8("customthresholdsCheckBox"))
self.comparisonmethodLabel = QtGui.QLabel(self.centralwidget)
- self.comparisonmethodLabel.setGeometry(QtCore.QRect(10, 300, 101, 16))
+ self.comparisonmethodLabel.setGeometry(QtCore.QRect(10, 330, 101, 16))
self.comparisonmethodLabel.setObjectName(_fromUtf8("comparisonmethodLabel"))
self.alignregionButton = QtGui.QPushButton(self.centralwidget)
self.alignregionButton.setGeometry(QtCore.QRect(5, 92, 101, 23))
self.alignregionButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.alignregionButton.setObjectName(_fromUtf8("alignregionButton"))
self.groupDummySplitsCheckBox = QtGui.QCheckBox(self.centralwidget)
- self.groupDummySplitsCheckBox.setGeometry(QtCore.QRect(252, 440, 230, 17))
+ self.groupDummySplitsCheckBox.setGeometry(QtCore.QRect(252, 470, 230, 17))
self.groupDummySplitsCheckBox.setChecked(False)
self.groupDummySplitsCheckBox.setObjectName(_fromUtf8("groupDummySplitsCheckBox"))
self.selectwindowButton = QtGui.QPushButton(self.centralwidget)
@@ -384,14 +395,17 @@ def setupUi(self, MainWindow):
self.resetLabel.raise_()
self.skiptsplitLabel.raise_()
self.undosplitLabel.raise_()
+ self.pausehotkeyLabel.raise_()
self.splitLineEdit.raise_()
self.undosplitLineEdit.raise_()
self.skipsplitLineEdit.raise_()
self.resetLineEdit.raise_()
+ self.pauseLineEdit.raise_()
self.setsplithotkeyButton.raise_()
self.setresethotkeyButton.raise_()
self.setskipsplithotkeyButton.raise_()
self.setundosplithotkeyButton.raise_()
+ self.setpausehotkeyButton.raise_()
self.line_live_bottom.raise_()
self.line_live_top.raise_()
self.line_live_right.raise_()
@@ -462,7 +476,8 @@ def setupUi(self, MainWindow):
MainWindow.setTabOrder(self.splitLineEdit, self.resetLineEdit)
MainWindow.setTabOrder(self.resetLineEdit, self.skipsplitLineEdit)
MainWindow.setTabOrder(self.skipsplitLineEdit, self.undosplitLineEdit)
- MainWindow.setTabOrder(self.undosplitLineEdit, self.groupDummySplitsCheckBox)
+ MainWindow.setTabOrder(self.undosplitLineEdit, self.pauseLineEdit)
+ MainWindow.setTabOrder(self.pauseLineEdit, self.groupDummySplitsCheckBox)
MainWindow.setTabOrder(self.groupDummySplitsCheckBox, self.loopCheckBox)
def retranslateUi(self, MainWindow):
@@ -488,10 +503,12 @@ def retranslateUi(self, MainWindow):
self.resetLabel.setText(_translate("MainWindow", "Reset", None))
self.skiptsplitLabel.setText(_translate("MainWindow", "Skip Split", None))
self.undosplitLabel.setText(_translate("MainWindow", "Undo Split", None))
+ self.pausehotkeyLabel.setText(_translate("MainWindow", "Pause / Unpause", None))
self.setsplithotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
self.setresethotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
self.setskipsplithotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
self.setundosplithotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
+ self.setpausehotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
self.timerglobalhotkeysLabel.setText(_translate("MainWindow", "Timer Global Hotkeys", None))
self.currentsplitimageLabel.setText(_translate("MainWindow", "Current Split Image", None))
self.imageloopLabel.setText(_translate("MainWindow", "Image Loop #:", None))
From f89b80b16b3884b8c6cb46ca3af024dd77a2c978 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Sun, 19 Apr 2020 01:00:16 +0200
Subject: [PATCH 04/19] Huge commit - no testing yet
-Add SplitImage class which makes a lot of code a lot less complicated
-Make settings.pkl code shorter
-Add include next flag {n}
-Add undo flag {u}
-Group dummy splits is now compatible with loops
-Add disclaimer about complexity of the two new flags
-A ton of other stuff
---
README.md | 15 +-
src/AutoSplit.py | 604 +++++++++++++++++---------------------------
src/split_parser.py | 275 ++++++++++----------
3 files changed, 377 insertions(+), 517 deletions(-)
diff --git a/README.md b/README.md
index 2f41a1a0..1464a359 100644
--- a/README.md
+++ b/README.md
@@ -15,10 +15,10 @@ This program compares split images to a capture region of any window (OBS, xspli
### Opening the program
- Download the [latest version](https://github.com/austinryan/Auto-Split/releases)
-- Extract the file and open AutoSplit.exe.
+- Extract the file and open `AutoSplit.exe`.
## Split Image Folder
-- Supported image file types: .png, .jpg, .jpeg, .bmp, and [more](https://docs.opencv.org/3.0-beta/modules/imgcodecs/doc/reading_and_writing_images.html#imread).
+- Supported image file types: `.png`, `.jpg`, `.jpeg`, `.bmp`, and [more](https://docs.opencv.org/3.0-beta/modules/imgcodecs/doc/reading_and_writing_images.html#imread).
- Images can be any size.
- Images are matched in alphanumerical order.
- Recommended filenaming convention: `001_SplitName.png, 002_SplitName.png, 003_SplitName.png`...
@@ -70,10 +70,13 @@ This program compares split images to a capture region of any window (OBS, xspli
- Custom delay times are placed between hash signs `##` in the filename. Note that these are in milliseconds. For example, a 10 second split delay would be `#10000#`. You cannot skip or undo splits during split delays.
- Image loop amounts are placed between at symbols `@@` in the filename. For example, a specific image that you want to split 5 times in a row would be `@5@`. The current loop # is conveniently located beneath the current split image.
- Flags are placed between curly brackets `{}` in the filename. Multiple flags are placed in the same set of curly brackets. Current available flags:
+ - **NOTE:** If you think these flags are too hard to understand, don't worry, just ignore the last two ones. They make everything more complicated.
- `{d}` dummy split image. When matched, it moves to the next image without hitting your split hotkey.
- `{b}` split when similarity goes below the threshold rather than above. When a split image filename has this flag, the split image similarity will go above the threshold, do nothing, and then split the next time the similarity goes below the threshold.
- - `{m}` masked split image. This allows you to customize what you want compared in your split image by using transparency. Transparent pixels in the split image are ignored when comparing. This is useful if only a certain part of the capture region is consistent (for example, consistent text on the screen, but the background is always different). These images MUST be .png and contain transparency. For more on this, see [How to Create a Masked Image](https://github.com/Toufool/Auto-Split/blob/master/README.md#how-to-create-a-masked-image). Histogram or L2 norm comparison is recommended if you use any masked images. It is highly recommended that you do NOT use pHash comparison if you use any masked images, as it is very inaccurate
- - `{p}` pause/unpause timer. This allows you to let AutoSplit remove the loading times. Use `{d}` as well if you only want to pause/unpause but don't want to split. There is no "Pause / Unpause" button in AutoSplit because it doesn't keep track of whether your timer is currently paused.
+ - `{m}` masked split image. This allows you to customize what you want compared in your split image by using transparency. Transparent pixels in the split image are ignored when comparing. This is useful if only a certain part of the capture region is consistent (for example, consistent text on the screen, but the background is always different). These images *must* be `.png` and contain transparency. For more on this, see [How to Create a Masked Image](https://github.com/Toufool/Auto-Split/blob/master/README.md#how-to-create-a-masked-image). Histogram or L2 norm comparison is recommended if you use any masked images. It is highly recommended that you do **_not_** use pHash comparison if you use any masked images, as it is very inaccurate
+ - `{p}` pause/unpause timer. This allows you to let AutoSplit remove the loading times. Use dummy flag as well if you only want to pause/unpause but don't want to split. There is no "Pause / Unpause" button in AutoSplit because it doesn't keep track of whether your timer is currently paused.
+ - `{n}` include next split as well. This allows you to have more than one split image that the live image is compared to simultaneously. You can chain split images marked with this flag together. Only the first image in such a chain can have a loop value greater than 1. This flag is compatible with all other values/flags.
+ - `{u}` undo split image. This flag is only useful with the "Group dummy splits" checkbox enabled. The split image with this flag is the split in this split group that is chosen when you hit undo while in the split group after this one. This is useful if there are split images at the beginning and the end of a split image group. If multiple split images in the same group have this flag, only the last one will work. Default is the first split of the group.
- Filename examples:
- `001_SplitName_(0.9)_[10].png` is a split image with a threshold of 0.9 and a pause time of 10 seconds.
- `002_SplitName_(0.9)_[10]_{d}.png` is the second split image with a threshold of 0.9, pause time of 10, and is a dummy split.
@@ -81,7 +84,7 @@ This program compares split images to a capture region of any window (OBS, xspli
- `004_SplitName(0.9)_[10]_#3500#_@3@_{b}.png` is the fourth split image with a threshold of 0.9, pause time of 10 seconds, delay split time of 3.5 seconds, will loop 3 times, and will split when similarity is below the threshold rather than above.
### How to Create a Masked Image
-The best way to create a masked image is to set your capture region as the entire game screen, take a screenshot, and use a program like [paint.net](https://www.getpaint.net/) to "erase" (make transparent) everything you don't want the program to compare. More on how to creating images with transparency using paint.net can be found in [this tutorial](https://www.youtube.com/watch?v=v53kkUYFVn8). The last thing you need to do is add {m} to the filename. For visualization, here is what the capture region compared to a masked split image looks like if you would want to split on "Shine Get!" text in Super Mario Sunshine:
+The best way to create a masked image is to set your capture region as the entire game screen, take a screenshot, and use a program like [paint.net](https://www.getpaint.net/) to "erase" (make transparent) everything you don't want the program to compare. More on how to creating images with transparency using paint.net can be found in [this tutorial](https://www.youtube.com/watch?v=v53kkUYFVn8). The last thing you need to do is add `{m}` to the filename. For visualization, here is what the capture region compared to a masked split image looks like if you would want to split on "Shine Get!" text in Super Mario Sunshine:
@@ -110,7 +113,7 @@ In this situation you would have only 3 splits in LiveSplit/wsplit (even though
- If you are in the 3rd, 4th or 5th image and press the skip key, it will end up on the 6th image
- If you are in the 6th image and press the undo key, it will end up on the 3rd image
-Please note this option cannot currently be used if you have any loop parameter `@@` greater than 1 in any image.
+Please note that splits marked with include next flag will always be grouped, no matter if this checkbox is enabled.
### Loop Split Images
If this option is disabled, when the last split meets the threshold and splits, it will automatically reset.
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index 69ead29d..fea74392 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -385,7 +385,7 @@ def takeScreenshot(self):
while os.path.exists(self.split_image_directory + take_screenshot_filename + '.png') == True:
FileNameNumber = (f"{i:03}")
take_screenshot_filename = FileNameNumber + '_SplitImage'
- i = i + 1
+ i += 1
# grab screenshot of capture region
capture = capture_windows.capture_region(self.hwnd, self.rect)
@@ -590,7 +590,6 @@ def callback():
self.afterSettingHotkeySignal.emit()
return
self.pauseLineEdit.setText(self.pause_key)
- self.old_pause_key = self.pause_key
self.afterSettingHotkeySignal.emit()
return
@@ -650,9 +649,7 @@ def checkFPS(self):
return
# grab first image in the split image folder
- split_image_file = split_image_filenames[0]
- split_image_path = split_image_directory + split_image_file
- split_image = cv2.imread(split_image_path, cv2.IMREAD_COLOR)
+ split_image = cv2.imread(split_image_directory + split_image_filenames[0], cv2.IMREAD_COLOR)
split_image = cv2.cvtColor(split_image, cv2.COLOR_BGR2RGB)
split_image = cv2.resize(split_image, (self.RESIZE_WIDTH, self.RESIZE_HEIGHT))
@@ -672,7 +669,7 @@ def checkFPS(self):
elif self.comparisonmethodComboBox.currentIndex() == 2:
similarity = compare.compare_phash(split_image, capture)
- count = count + 1
+ count += 1
# calculate FPS
t1 = time.time()
@@ -685,18 +682,10 @@ def undoSplit(self):
if self.undosplitButton.isEnabled() == False:
return
- if self.loop_number != 1 and self.groupDummySplitsCheckBox.isChecked() == False:
- self.loop_number = self.loop_number - 1
-
- elif self.groupDummySplitsCheckBox.isChecked() == True:
- for i, group in enumerate(self.split_groups):
- if i > 0 and self.split_image_number in group:
- self.split_image_number = self.split_groups[i - 1][0]
- break
-
+ if self.loop_number != 1:
+ self.loop_number -= 1
else:
- self.split_image_number = self.split_image_number - 1
- self.loop_number = self.split_image_loop_amount[self.split_image_number]
+ self.split_image_index = self.split_images[self.split_image_index].undo_image_index
self.updateSplitImage()
@@ -708,16 +697,10 @@ def skipSplit(self):
if self.skipsplitButton.isEnabled() == False:
return
- if self.loop_number < self.split_image_loop_amount[self.split_image_number] and self.groupDummySplitsCheckBox.isChecked() == False:
- self.loop_number = self.loop_number + 1
- elif self.groupDummySplitsCheckBox.isChecked() == True:
- for group in self.split_groups:
- if self.split_image_number in group:
- self.split_image_number = group[-1] + 1
- break
+ if self.loop_number < self.split_images[self.split_image_index].loop:
+ self.loop_number += 1
else:
- self.split_image_number = self.split_image_number + 1
- self.loop_number = 1
+ self.split_image_index = self.split_images[self.split_image_index].skip_image_index
self.updateSplitImage()
@@ -755,108 +738,125 @@ def autoSplitter(self):
return
# get split image filenames
- self.split_image_filenames = os.listdir(self.split_image_directory)
+ self.split_images = []
- # Make sure that each of the images follows the guidelines for correct format
- # according to all of the settings selected by the user.
- for image in self.split_image_filenames:
+ last_n_flag = False
+
+ for image_filename in os.listdir(self.split_image_directory):
+ self.split_images.append(split_parser.SplitImage(self.split_image_directory, image_filename))
+
+ # Make sure that each of the images follows the guidelines for correct format
+ # according to all of the settings selected by the user.
# Check to make sure the file is actually an image format that can be opened
# according to the mask flag
- if split_parser.flags_from_filename(image) & 0x02 == 0x02:
- source = cv2.imread(self.split_image_directory + image, cv2.IMREAD_UNCHANGED)
+ if self.split_images[-1].flags & 0x02 == 0x02:
+ source = cv2.imread(self.split_images[-1].path, cv2.IMREAD_UNCHANGED)
if source is None:
# Opencv couldn't open this file as an image, this isn't a correct
# file format that is supported
- self.imageTypeError(image)
+ self.imageTypeError(self.split_images[-1].filename)
return
if source.shape[2] != 4:
# Error, this file doesn't have an alpha channel even
# though the flag for masking was added
- self.alphaChannelError(image)
+ self.alphaChannelError(self.split_images[-1].filename)
return
else:
- if cv2.imread(self.split_image_directory + image, cv2.IMREAD_COLOR) is None:
+ if cv2.imread(self.split_images[-1].path, cv2.IMREAD_COLOR) is None:
# Opencv couldn't open this file as an image, this isn't a correct
# file format that is supported
- self.imageTypeError(image)
+ self.imageTypeError(self.split_images[-1].filename)
return
- if self.custompausetimesCheckBox.isChecked() and split_parser.pause_from_filename(image) is None:
+ if self.custompausetimesCheckBox.isChecked() and self.split_images[-1].pause is None:
# Error, this file doesn't have a pause, but the checkbox was
# selected for unique pause times
- self.customPauseError(image)
+ self.customPauseError(self.split_images[-1].filename)
return
- if self.customthresholdsCheckBox.isChecked() and split_parser.threshold_from_filename(image) is None:
+ if self.customthresholdsCheckBox.isChecked() and self.split_images[-1].threshold is None:
# Error, this file doesn't have a threshold, but the checkbox
# was selected for unique thresholds
- self.customThresholdError(image)
+ self.customThresholdError(self.split_images[-1].filename)
return
- if self.pauseLineEdit.text() == '' and split_parser.flags_from_filename(image) & 0x08 == 0x08:
+ if self.pauseLineEdit.text() == '' and self.split_images[-1].flags & 0x08 == 0x08:
# Error, no pause hotkey set even though pause flag is set
self.pauseHotkeyError()
return
- if self.splitLineEdit.text() == '':
- self.splitHotkeyError()
- return
-
- # find reset image then remove it from the list
- self.findResetImage()
-
- # Check that there's only one reset image
- for image in self.split_image_filenames:
+ if self.splitLineEdit.text() == '' and self.split_images[-1].flags & 0x01 == 0:
+ # Error, no split hotkey set even though dummy flag is not set
+ self.splitHotkeyError()
+ return
- if split_parser.is_reset_image(image):
- self.multipleResetImagesError()
+ if last_n_flag and self.split_images[-1].loop > 1:
+ # Error, an image with the {n} flag is followed by an image with a loop > 1
+ self.includeNextFlagWithLoopError()
return
- # if there is no custom threshold for the reset image, throw an error.
- if self.reset_image is not None and self.reset_image_threshold is None:
- self.noResetImageThresholdError()
- return
+ last_n_flag = self.split_images[-1].flags & 0x10 = 0x10
- # If there is no reset hotkey set but a reset image is present, throw an error.
- if self.resetLineEdit.text() == '' and self.reset_image is not None:
- self.resetHotkeyError()
- return
+ # If the last split has the {n} flag, throw an error
+ if self.split_images[-1].flags & 0x10 == 0x10:
+ self.lastImageHasIncludeNextFlagError()
- # construct groups of splits if needed
- self.split_groups = []
- if self.groupDummySplitsCheckBox.isChecked():
- current_group = []
- self.split_groups.append(current_group)
-
- for i, image in enumerate(self.split_image_filenames):
- current_group.append(i)
-
- flags = split_parser.flags_from_filename(image)
- if flags & 0x01 != 0x01 and i < len(self.split_image_filenames) - 1:
- current_group = []
- self.split_groups.append(current_group)
-
- #construct dummy splits array
- self.dummy_splits_array = []
- for i, image in enumerate(self.split_image_filenames):
- if split_parser.flags_from_filename(image) & 0x01 == 0x01:
- self.dummy_splits_array.append(True)
- else:
- self.dummy_splits_array.append(False)
+ # Find reset image then remove it from the list
+ self.reset_image = None
+ for i, image in enumerate(self.split_images):
+ if image.is_reset_image:
+ # Check that there's only one reset image
+ if reset_image is None:
+ self.reset_image = image
+ self.split_images.pop(i)
+ else:
+ self.multipleResetImagesError()
+ return
+ if self.reset_image is not None:
+ # If there is no custom threshold for the reset image, throw an error
+ if self.reset_image.threshold is None:
+ self.noResetImageThresholdError()
+ return
- #construct loop amounts for each split image
- self.split_image_loop_amount = []
- for i, image in enumerate(self.split_image_filenames):
- self.split_image_loop_amount.append(split_parser.loop_from_filename(image))
+ # If the reset image has the {n} flag, throw an error
+ if self.reset_image.flags & 0x10 == 0x10:
+ self.resetImageHasIncludeNextFlagError()
+ return
- if any(x > 1 for x in self.split_image_loop_amount) and self.groupDummySplitsCheckBox.isChecked() == True:
- self.dummySplitsError()
- return
+ # If there is no reset hotkey set but a reset image is present, throw an error
+ if self.resetLineEdit.text() == '':
+ self.resetHotkeyError()
+ return
+
+ # Construct groups of splits
+ previous_group_start = None
+ previous_group_undo = None
+ current_group_start = 0
+ current_group_undo = 0
+ flags = 0x11
+ if self.groupDummySplitsCheckBox.isChecked() == False:
+ flags -= 0x01
+
+ for i, image in enumerate(self.split_images):
+ if image.flags & 0x20 == 0x20:
+ current_group_undo = i
+ if image.flags & flags == 0:
+ for image in self.split_images[current_group_start, i + 1]:
+ image.undo_image_index = previous_group_undo
+
+ if previous_group_start is not None:
+ for image in self.split_images[previous_group_start, current_group_start]:
+ image.skip_image_index = i + 1
+
+ previous_group_start = current_group_start
+ previous_group_undo = current_group_undo
+ current_group_start = i + 1
+ current_group_undo = current_group_start
# change auto splitter button text and disable/enable some buttons
self.startautosplitterButton.setText('Running..')
@@ -874,26 +874,24 @@ def autoSplitter(self):
self.customthresholdsCheckBox.setEnabled(False)
self.groupDummySplitsCheckBox.setEnabled(False)
- #initialize some settings
- self.split_image_number = 0
+ # Initialize some settings
+ self.split_image_index = 0
self.loop_number = 1
- self.number_of_split_images = len(self.split_image_filenames)
- self.waiting_for_split_delay = False
- self.split_below_threshold = False
+ self.number_of_split_images = len(self.split_images)
self.run_start_time = time.time()
# First while loop: stays in this loop until all of the split images have been split
- while self.split_image_number < self.number_of_split_images:
+ while self.split_image_index < self.number_of_split_images:
- # Check if we are not waiting for the split delay to send the key press
- if self.waiting_for_split_delay == True:
- time_millis = int(round(time.time() * 1000))
- if time_millis < self.split_time:
- QtGui.QApplication.processEvents()
- continue
+ # Construct list of images that should be compared
+ self.current_split_images = []
+ for image in self.split_images[self.split_image_index:]:
+ self.current_split_images.append(image)
+ if image.flags & 0x10 == 0:
+ break
- self.updateSplitImage()
+ self.updateSplitImage(self.current_split_images[0])
# second while loop: stays in this loop until similarity threshold is met
# skip loop if we just finished waiting for the split delay and need to press the split key!
@@ -908,55 +906,43 @@ def autoSplitter(self):
capture = None
if self.shouldCheckResetImage():
- reset_masked = (self.reset_mask is not None)
+ reset_masked = (self.reset_image.mask is not None)
capture = self.getCaptureForComparison(reset_masked)
- reset_similarity = self.compareImage(self.reset_image, self.reset_mask, capture)
- if reset_similarity >= self.reset_image_threshold:
+ self.reset_image.similarity = self.compareImage(self.reset_image.image, self.reset_image.mask, capture)
+ if self.reset_image.similarity >= self.reset_image.threshold:
keyboard.send(str(self.resetLineEdit.text()))
self.reset()
# loop goes into here if start auto splitter text is "Start Auto Splitter"
if self.startautosplitterButton.text() == 'Start Auto Splitter':
- self.imageloopLabel.setText("Image Loop #:")
- self.currentSplitImage.setText(' ')
- self.currentsplitimagefileLabel.setText(' ')
- self.livesimilarityLabel.setText(' ')
- self.highestsimilarityLabel.setText(' ')
- self.browseButton.setEnabled(True)
- self.startautosplitterButton.setEnabled(True)
- self.resetButton.setEnabled(False)
- self.undosplitButton.setEnabled(False)
- self.skipsplitButton.setEnabled(False)
- self.setsplithotkeyButton.setEnabled(True)
- self.setresethotkeyButton.setEnabled(True)
- self.setskipsplithotkeyButton.setEnabled(True)
- self.setundosplithotkeyButton.setEnabled(True)
- self.setpausehotkeyButton.setEnabled(True)
- self.custompausetimesCheckBox.setEnabled(True)
- self.customthresholdsCheckBox.setEnabled(True)
- self.groupDummySplitsCheckBox.setEnabled(True)
+ reset_UI()
QtGui.QApplication.processEvents()
return
- # get capture again if needed
- masked = (self.flags & 0x02 == 0x02)
- if capture is None or masked != reset_masked:
- capture = self.getCaptureForComparison(masked)
+ # Reuse capture variable as non-masked capture variable
+ self.masked_capture = None
+ if reset_masked is not None:
+ masked_capture = capture
+ capture = None
+
+ # Calculate similarities for split images
+ for image in self.current_split_images:
+ if image.mask is None:
+ self.image.similarity = self.compareImage(image, image.mask, capture)
+ else:
+ self.image.similarity = self.compareImage(image, image.mask, masked_capture)
- # calculate similarity for split image
- self.similarity = self.compareImage(self.split_image, self.mask, capture)
+ # If the similarity becomes higher than highest similarity, set it as such
+ if image.similarity > self.highest_similarity:
+ self.highest_similarity = image.similarity
- # show live similarity if the checkbox is checked
+ # Show live similarity of first comparison image if the checkbox is checked
if self.showlivesimilarityCheckBox.isChecked():
- self.livesimilarityLabel.setText(str(self.similarity)[:4])
+ self.livesimilarityLabel.setText(str(self.current_split_images[0].similarity)[:4])
else:
self.livesimilarityLabel.setText(' ')
- # if the similarity becomes higher than highest similarity, set it as such.
- if self.similarity > self.highest_similarity:
- self.highest_similarity = self.similarity
-
# show live highest similarity if the checkbox is checked
if self.showhighestsimilarityCheckBox.isChecked():
self.highestsimilarityLabel.setText(str(self.highest_similarity)[:4])
@@ -964,31 +950,28 @@ def autoSplitter(self):
self.highestsimilarityLabel.setText(' ')
# if its the last split image and last loop number, disable the skip split button
- if (self.split_image_number == self.number_of_split_images - 1 and self.loop_number == self.split_image_loop_amount[self.split_image_number]) or (self.groupDummySplitsCheckBox.isChecked() == True and self.dummy_splits_array[self.split_image_number:].count(False) <= 1):
- self.skipsplitButton.setEnabled(False)
- else:
- self.skipsplitButton.setEnabled(True)
+ self.skipsplitButton.setEnabled(self.current_split_images[0].skip_image_index is not None or self.current_split_images[0].loop != self.loop_number)
# if its the first split image and first loop, disable the undo split button
- if self.split_image_number == 0 and self.loop_number == 1:
- self.undosplitButton.setEnabled(False)
- else:
- self.undosplitButton.setEnabled(True)
-
- #if the b flag is set, let similarity go above threshold first, then split on similarity below threshold.
- #if no b flag, just split when similarity goes above threshold.
- if self.flags & 0x04 == 0x04 and self.split_below_threshold == False:
- if self.waiting_for_split_delay == False and self.similarity >= self.similaritythresholdDoubleSpinBox.value():
- self.split_below_threshold = True
- continue
- elif self.flags & 0x04 == 0x04 and self.split_below_threshold == True:
- if self.waiting_for_split_delay == False and self.similarity < self.similaritythresholdDoubleSpinBox.value():
- self.split_below_threshold = False
- break
- else:
- if self.waiting_for_split_delay == False and self.similarity >= self.similaritythresholdDoubleSpinBox.value():
- break
-
+ self.undosplitButton.setEnabled(self.current_split_images[0].undo_image_index is not None or self.current_split_images[0].loop != 1)
+
+ try:
+ for image in self.current_split_images:
+ # If the {b} flag is set, let similarity go above threshold first, then split on similarity below threshold
+ # Otherwise just split when similarity goes above threshold
+ if image.flags & 0x04 == 0x04 and image.split_below_threshold == False and image.similarity >= self.similaritythresholdDoubleSpinBox.value():
+ image.split_below_threshold = True
+ raise ContinueLoop
+ if (image.flags & 0x04 == 0x04 and image.split_below_threshold == True and image.similarity < self.similaritythresholdDoubleSpinBox.value()) or (image.similarity >= self.similaritythresholdDoubleSpinBox.value() and image.flags & 0x04 == 0):
+ self.successful_split_image = image
+ raise BreakLoop
+
+ # These exceptions are necessary because else the for loop would be broken/continued instead of the while loop
+ except ContinueLoop:
+ continue
+ except BreakLoop:
+ break
+
# limit the number of time the comparison runs to reduce cpu usage
fps_limit = self.fpslimitSpinBox.value()
time.sleep((1 / fps_limit) - (time.time() - start) % (1 / fps_limit))
@@ -999,14 +982,13 @@ def autoSplitter(self):
# We need to make sure that this isn't a dummy split without pause flag
# before sending a key press.
- if (self.flags & 0x09 == 0x01):
+ if (self.successful_split_image.flags & 0x09 == 0x01):
pass
else:
# If it's a delayed split, check if the delay has passed
# Otherwise calculate the split time for the key press
- if self.split_delay > 0 and self.waiting_for_split_delay == False:
- self.split_time = int(round(time.time() * 1000)) + self.split_delay
- self.waiting_for_split_delay = True
+ if self.successful_split_image.delay > 0:
+ self.split_time = int(round(time.time() * 1000)) + self.successful_split_image.delay
self.currentSplitImage.setText('Delayed split...')
self.undosplitButton.setEnabled(False)
@@ -1016,46 +998,27 @@ def autoSplitter(self):
# check for reset while delayed
delay_start_time = time.time()
- while time.time() - delay_start_time < (self.split_delay / 1000):
+ while time.time() - delay_start_time < (self.successful_split_image.delay / 1000):
# check for reset
if win32gui.GetWindowText(self.hwnd) == '':
self.reset()
if self.startautosplitterButton.text() == 'Start Auto Splitter':
- self.imageloopLabel.setText("Image Loop #:")
- self.currentSplitImage.setText(' ')
- self.currentsplitimagefileLabel.setText(' ')
- self.livesimilarityLabel.setText(' ')
- self.highestsimilarityLabel.setText(' ')
- self.browseButton.setEnabled(True)
- self.startautosplitterButton.setEnabled(True)
- self.resetButton.setEnabled(False)
- self.undosplitButton.setEnabled(False)
- self.skipsplitButton.setEnabled(False)
- self.setsplithotkeyButton.setEnabled(True)
- self.setresethotkeyButton.setEnabled(True)
- self.setskipsplithotkeyButton.setEnabled(True)
- self.setundosplithotkeyButton.setEnabled(True)
- self.setpausehotkeyButton.setEnabled(True)
- self.custompausetimesCheckBox.setEnabled(True)
- self.customthresholdsCheckBox.setEnabled(True)
- self.groupDummySplitsCheckBox.setEnabled(True)
+ self.reset_UI()
return
# calculate similarity for reset image
- if self.shouldCheckResetImage() == True:
- reset_masked = (self.reset_mask is not None)
+ if self.shouldCheckResetImage():
+ reset_masked = (self.reset_image.mask is not None)
capture = self.getCaptureForComparison(reset_masked)
- reset_similarity = self.compareImage(self.reset_image, self.reset_mask, capture)
- if reset_similarity >= self.reset_image_threshold:
+ reset_image.similarity = self.compareImage(self.reset_image, self.reset_mask, capture)
+ if reset_image.similarity >= self.reset_image.threshold:
keyboard.send(str(self.resetLineEdit.text()))
self.reset()
continue
QtTest.QTest.qWait(1)
- self.waiting_for_split_delay = False
-
# Split key press unless dummy flag is set
if (self.flags & 0x01 == 0x00):
keyboard.send(str(self.splitLineEdit.text()))
@@ -1064,45 +1027,38 @@ def autoSplitter(self):
if (self.flags & 0x08 == 0x08):
keyboard.send(str(self.pauseLineEdit.text()))
- #increase loop number if needed, set to 1 if it was the last loop.
- if self.loop_number < self.split_image_loop_amount[self.split_image_number]:
- self.loop_number = self.loop_number + 1
+ # Increase loop number if needed, set to 1 if it was the last loop
+ if self.loop_number < self.successful_split_image.loop:
+ self.loop_number += 1
else:
self.loop_number = 1
- # if loop check box is checked and its the last split, go to first split.
- # else if current loop amount is back to 1, add 1 to split image number
- # else pass, dont change split image number.
- if self.loopCheckBox.isChecked() and self.split_image_number == self.number_of_split_images - 1 and self.loop_number == 1:
- self.split_image_number = 0
+ number_of_comparison_images = len(self.current_split_images)
+
+ # If loop check box is checked and its the last split, go to first split
+ # Else if current loop amount is back to 1, add 1 to split image number
+ if self.loopCheckBox.isChecked() and self.split_image_index == self.number_of_split_images - 1 and self.loop_number == 1:
+ self.split_image_index = 0
elif self.loop_number == 1:
- self.split_image_number = self.split_image_number + 1
- else:
- pass
+ self.split_image_index += number_of_comparison_images
- # set a "pause" split image number. This is done so that it can detect if user hit split/undo split while paused.
- pause_split_image_number = self.split_image_number
+ # Set a "pause" split image number. This is done so that it can detect if user hit split/undo split while paused
+ pause_split_image_index = self.split_image_index
pause_loop_number = self.loop_number
- # if its not the last split image, pause for the amount set by the user
- if self.number_of_split_images != self.split_image_number:
- # set current split image to none
+ # If it's not the last split image, pause for the amount set by the user
+ if self.number_of_split_images != self.split_image_index:
+ # Set current split image to none
self.currentSplitImage.setText('none (paused)')
self.currentsplitimagefileLabel.setText(' ')
self.currentSplitImage.setAlignment(QtCore.Qt.AlignCenter)
self.imageloopLabel.setText('Image Loop #: -')
- # if its the last split image and last loop number, disable the skip split button
- if (self.split_image_number == self.number_of_split_images - 1 and self.loop_number == self.split_image_loop_amount[self.split_image_number]) or (self.groupDummySplitsCheckBox.isChecked() == True and self.dummy_splits_array[self.split_image_number:].count(False) <= 1):
- self.skipsplitButton.setEnabled(False)
- else:
- self.skipsplitButton.setEnabled(True)
+ # If it's the last split image and last loop number, disable the skip split button
+ self.skipsplitButton.setEnabled(self.split_images[self.split_image_index].skip_image_index is not None or self.split_images[self.split_image_index].loop != self.loop_number)
- # if its the first split image and first loop, disable the undo split button
- if self.split_image_number == 0 and self.loop_number == 1:
- self.undosplitButton.setEnabled(False)
- else:
- self.undosplitButton.setEnabled(True)
+ # If it's the first split image and first loop, disable the undo split button
+ self.undosplitButton.setEnabled(self.split_images[self.split_image_index].undo_image_index is not None or self.split_images[self.split_image_index].loop != 1)
QtGui.QApplication.processEvents()
@@ -1114,28 +1070,11 @@ def autoSplitter(self):
if win32gui.GetWindowText(self.hwnd) == '':
self.reset()
if self.startautosplitterButton.text() == 'Start Auto Splitter':
- self.imageloopLabel.setText("Image Loop #:")
- self.currentSplitImage.setText(' ')
- self.currentsplitimagefileLabel.setText(' ')
- self.livesimilarityLabel.setText(' ')
- self.highestsimilarityLabel.setText(' ')
- self.browseButton.setEnabled(True)
- self.startautosplitterButton.setEnabled(True)
- self.resetButton.setEnabled(False)
- self.undosplitButton.setEnabled(False)
- self.skipsplitButton.setEnabled(False)
- self.setsplithotkeyButton.setEnabled(True)
- self.setresethotkeyButton.setEnabled(True)
- self.setskipsplithotkeyButton.setEnabled(True)
- self.setundosplithotkeyButton.setEnabled(True)
- self.setpausehotkeyButton.setEnabled(True)
- self.custompausetimesCheckBox.setEnabled(True)
- self.customthresholdsCheckBox.setEnabled(True)
- self.groupDummySplitsCheckBox.setEnabled(True)
+ reset_UI()
return
# check for skip/undo split:
- if self.split_image_number != pause_split_image_number or self.loop_number != pause_loop_number:
+ if self.split_image_index != pause_split_image_index or self.loop_number != pause_loop_number:
break
# calculate similarity for reset image
@@ -1143,8 +1082,8 @@ def autoSplitter(self):
reset_masked = (self.reset_mask is not None)
capture = self.getCaptureForComparison(reset_masked)
- reset_similarity = self.compareImage(self.reset_image, self.reset_mask, capture)
- if reset_similarity >= self.reset_image_threshold:
+ reset_image.similarity = self.compareImage(self.reset_image.image, self.reset_image.mask, capture)
+ if reset_image.similarity >= self.reset_image.threshold:
keyboard.send(str(self.resetLineEdit.text()))
self.reset()
continue
@@ -1153,6 +1092,10 @@ def autoSplitter(self):
# loop breaks to here when the last image splits
self.startautosplitterButton.setText('Start Auto Splitter')
+ reset_UI()
+ QtGui.QApplication.processEvents()
+
+ def reset_UI(self):
self.imageloopLabel.setText("Image Loop #:")
self.currentSplitImage.setText(' ')
self.currentsplitimagefileLabel.setText(' ')
@@ -1171,7 +1114,6 @@ def autoSplitter(self):
self.custompausetimesCheckBox.setEnabled(True)
self.customthresholdsCheckBox.setEnabled(True)
self.groupDummySplitsCheckBox.setEnabled(True)
- QtGui.QApplication.processEvents()
def compareImage(self, image, mask, capture):
if mask is None:
@@ -1190,91 +1132,43 @@ def compareImage(self, image, mask, capture):
return compare.compare_phash_masked(image, capture, mask)
def getCaptureForComparison(self, masked):
- # grab screenshot of capture region
+ # Grab screenshot of capture region
capture = capture_windows.capture_region(self.hwnd, self.rect)
- # if flagged as a mask, capture with nearest neighbor interpolation. else don't so that
+ # If flagged as a mask, capture with nearest neighbor interpolation. else don't so that
# threshold settings on versions below 1.2.0 aren't messed up
if (masked):
capture = cv2.resize(capture, (self.RESIZE_WIDTH, self.RESIZE_HEIGHT), interpolation=cv2.INTER_NEAREST)
else:
capture = cv2.resize(capture, (self.RESIZE_WIDTH, self.RESIZE_HEIGHT))
- # convert to BGR
+ # Convert to BGR
capture = cv2.cvtColor(capture, cv2.COLOR_BGRA2BGR)
return capture
def shouldCheckResetImage(self):
- if self.reset_image is not None and time.time() - self.run_start_time > self.reset_image_pause_time:
- return True
-
- return False
-
- def findResetImage(self):
- self.reset_image = None
- self.reset_mask = None
- self.reset_image_threshold = None
+ return (self.reset_image is not None and time.time() - self.run_start_time > self.reset_image_pause_time)
- reset_image_file = None
- for i, image in enumerate(self.split_image_filenames):
- if split_parser.is_reset_image(image):
- reset_image_file = image
- break
+ def updateSplitImage(self, split_image):
- if reset_image_file is None:
+ if len(self.current_split_images) > 1:
+ self.currentSplitImage.setText(str(len(self.current_split_images)) + ' images')
+ self.currentsplitimagefileLabel.setText(' ')
+ self.currentSplitImage.setAlignment(QtCore.Qt.AlignCenter)
return
- self.split_image_filenames.remove(reset_image_file)
-
- # create reset image and keep in memory
- path = self.split_image_directory + reset_image_file
- flags = split_parser.flags_from_filename(reset_image_file)
-
- self.reset_image_threshold = split_parser.threshold_from_filename(reset_image_file)
-
- self.reset_image_pause_time = split_parser.pause_from_filename(reset_image_file)
- if self.reset_image_pause_time is None:
- self.reset_image_pause_time = 0
-
- # if theres a mask flag, create a mask
- if (flags & 0x02 == 0x02):
- # create mask based on resized, nearest neighbor interpolated split image
- self.reset_image = cv2.imread(path, cv2.IMREAD_UNCHANGED)
- self.reset_image = cv2.resize(self.reset_image, (self.RESIZE_WIDTH, self.RESIZE_HEIGHT),
- interpolation=cv2.INTER_NEAREST)
- lower = np.array([0, 0, 0, 1], dtype="uint8")
- upper = np.array([255, 255, 255, 255], dtype="uint8")
- self.reset_mask = cv2.inRange(self.reset_image, lower, upper)
-
- # set split image as BGR
- self.reset_image = cv2.cvtColor(self.reset_image, cv2.COLOR_BGRA2BGR)
-
- # else if there is no mask flag, open image normally. don't interpolate nearest neighbor here so setups before 1.2.0 still work.
- else:
- self.reset_image = cv2.imread(path, cv2.IMREAD_COLOR)
- self.reset_image = cv2.resize(self.reset_image, (self.RESIZE_WIDTH, self.RESIZE_HEIGHT))
-
- def updateSplitImage(self):
-
- # get split image path
- split_image_file = self.split_image_filenames[0 + self.split_image_number]
- self.split_image_path = self.split_image_directory + split_image_file
-
- # get flags
- self.flags = split_parser.flags_from_filename(split_image_file)
-
- # set current split image in UI
- # if flagged as mask, transform transparency into UI's gray BG color
- if (self.flags & 0x02 == 0x02):
- self.split_image_display = cv2.imread(self.split_image_path, cv2.IMREAD_UNCHANGED)
+ # Set current split image in UI
+ # If flagged as mask, transform transparency into UI's gray BG color
+ if (split_image.flags & 0x02 == 0x02):
+ self.split_image_display = cv2.imread(split_image.path, cv2.IMREAD_UNCHANGED)
transparent_mask = self.split_image_display[:, :, 3] == 0
self.split_image_display[transparent_mask] = [240, 240, 240, 255]
self.split_image_display = cv2.cvtColor(self.split_image_display, cv2.COLOR_BGRA2RGB)
self.split_image_display = cv2.resize(self.split_image_display, (240, 180))
- # if not flagged as mask, open normally
+ # If not flagged as mask, open normally
else:
- self.split_image_display = cv2.imread(self.split_image_path, cv2.IMREAD_COLOR)
+ self.split_image_display = cv2.imread(self.split_image.path, cv2.IMREAD_COLOR)
self.split_image_display = cv2.cvtColor(self.split_image_display, cv2.COLOR_BGR2RGB)
self.split_image_display = cv2.resize(self.split_image_display, (240, 180))
@@ -1282,42 +1176,21 @@ def updateSplitImage(self):
self.split_image_display.shape[0], self.split_image_display.shape[1] * 3,
QtGui.QImage.Format_RGB888)
self.updateCurrentSplitImage.emit(qImg)
- self.currentsplitimagefileLabel.setText(split_image_file)
-
- # if theres a mask flag, create a mask
- if (self.flags & 0x02 == 0x02):
-
- # create mask based on resized, nearest neighbor interpolated split image
- self.split_image = cv2.imread(self.split_image_path, cv2.IMREAD_UNCHANGED)
- self.split_image = cv2.resize(self.split_image, (self.RESIZE_WIDTH, self.RESIZE_HEIGHT),
- interpolation=cv2.INTER_NEAREST)
- lower = np.array([0, 0, 0, 1], dtype="uint8")
- upper = np.array([255, 255, 255, 255], dtype="uint8")
- self.mask = cv2.inRange(self.split_image, lower, upper)
+ self.currentsplitimagefileLabel.setText(split_image.filename)
- # set split image as BGR
- self.split_image = cv2.cvtColor(self.split_image, cv2.COLOR_BGRA2BGR)
-
- # else if there is no mask flag, open image normally. don't interpolate nearest neighbor here so setups before 1.2.0 still work.
- else:
- split_image = cv2.imread(self.split_image_path, cv2.IMREAD_COLOR)
- self.split_image = cv2.resize(split_image, (self.RESIZE_WIDTH, self.RESIZE_HEIGHT))
- self.mask = None
+ self.current_split_images[0].get_image(self.RESIZE_WIDTH, self.RESIZE_HEIGHT)
# If the unique parameters are selected, go ahead and set the spinboxes to those values
if self.custompausetimesCheckBox.isChecked():
- self.pauseDoubleSpinBox.setValue(split_parser.pause_from_filename(split_image_file))
+ self.pauseDoubleSpinBox.setValue(split_image.pause)
if self.customthresholdsCheckBox.isChecked():
- self.similaritythresholdDoubleSpinBox.setValue(split_parser.threshold_from_filename(split_image_file))
-
- # Get delay for split, if any
- self.split_delay = split_parser.delay_from_filename(split_image_file)
+ self.similaritythresholdDoubleSpinBox.setValue(split_image.threshold)
- #Set Image Loop #
+ # Set Image Loop #
self.imageloopLabel.setText("Image Loop #: " + str(self.loop_number))
- #need to set split below threshold to false each time an image updates.
+ # Need to set split below threshold to false each time an image updates.
self.split_below_threshold = False
self.similarity = 0
@@ -1341,7 +1214,7 @@ def imageTypeError(self, image):
def regionError(self):
msgBox = QtGui.QMessageBox()
msgBox.setWindowTitle('Error')
- msgBox.setText("No region is selected. Select a region or reload settings while region window is open")
+ msgBox.setText("No region is selected. Select a region or reload settings while region window is open.")
msgBox.exec_()
def regionSizeError(self):
@@ -1377,7 +1250,7 @@ def alphaChannelError(self, image):
def alignRegionImageTypeError(self):
msgBox = QtGui.QMessageBox()
msgBox.setWindowTitle('Error')
- msgBox.setText("File not a valid image file")
+ msgBox.setText("File not a valid image file.")
msgBox.exec_()
def alignmentNotMatchedError(self):
@@ -1407,25 +1280,37 @@ def resetHotkeyError(self):
def pauseHotkeyError(self):
msgBox = QtGui.QMessageBox()
msgBox.setWindowTitle('Error')
- msgBox.setText("Your split image folder contains an image with the {p} flag, but no pause hotkey is set.")
+ msgBox.setText("Your split image folder contains an image marked with pause flag, but no pause hotkey is set.")
msgBox.exec_()
- def dummySplitsError(self):
+ def settingsNotFoundError(self):
msgBox = QtGui.QMessageBox()
msgBox.setWindowTitle('Error')
- msgBox.setText("Group dummy splits when undoing/skipping cannot be checked if any split image has a loop parameter greater than 1")
+ msgBox.setText("No settings file found. The settings file is saved when the program is closed.")
msgBox.exec_()
- def settingsNotFoundError(self):
+ def invalidSettingsError(self):
msgBox = QtGui.QMessageBox()
msgBox.setWindowTitle('Error')
- msgBox.setText("No settings file found. The settings file is saved when the program is closed.")
+ msgBox.setText("The settings file is invalid.")
msgBox.exec_()
- def invalidSettingsError(self):
+ def lastImageHasIncludeNextFlagError(self):
+ msgBox = QtGui.QMessageBox()
+ msgBox.setWindowTitle('Error')
+ msgBox.setText("The last split image in the image folder is marked with include next flag.")
+ msgBox.exec_()
+
+ def resetImageHasIncludeNextFlagError(self):
msgBox = QtGui.QMessageBox()
msgBox.setWindowTitle('Error')
- msgBox.setText("The settings file is invalid")
+ msgBox.setText("The reset image in the image folder is marked with include next flag.")
+ msgBox.exec_()
+
+ def includeNextFlagWithLoopError(self):
+ msgBox = QtGui.QMessageBox()
+ msgBox.setWindowTitle('Error')
+ msgBox.setText("Your split image folder contains an image marked with include next flag followed by an image with a loop value greater than 1.")
msgBox.exec_()
def saveSettings(self):
@@ -1445,25 +1330,10 @@ def saveSettings(self):
self.undo_split_key = str(self.undosplitLineEdit.text())
self.pause_key = str(self.pauseLineEdit.text())
- if self.custompausetimesCheckBox.isChecked():
- self.custom_pause_times_setting = 1
- else:
- self.custom_pause_times_setting = 0
-
- if self.customthresholdsCheckBox.isChecked():
- self.custom_thresholds_setting = 1
- else:
- self.custom_thresholds_setting = 0
-
- if self.groupDummySplitsCheckBox.isChecked():
- self.group_dummy_splits_undo_skip_setting = 1
- else:
- self.group_dummy_splits_undo_skip_setting = 0
-
- if self.loopCheckBox.isChecked():
- self.loop_setting = 1
- else:
- self.loop_setting = 0
+ self.custom_pause_times_setting = int(self.custompausetimesCheckBox.isChecked())
+ self.custom_thresholds_setting = int(self.customthresholdsCheckBox.isChecked())
+ self.group_dummy_splits_undo_skip_setting = int(self.groupDummySplitsCheckBox.isChecked())
+ self.loop_setting = int(self.loopCheckBox.isChecked())
# save settings to settings.pkl
with open(os.path.join(sys.path[0], 'settings.pkl'), 'wb') as f:
@@ -1507,26 +1377,14 @@ def loadSettings(self):
self.comparisonmethodComboBox.setCurrentIndex(self.comparison_index)
self.hwnd = win32gui.FindWindow(None, self.hwnd_title)
- # set custom checkbox's accordingly
- if self.custom_pause_times_setting == 1:
- self.custompausetimesCheckBox.setChecked(True)
- else:
- self.custompausetimesCheckBox.setChecked(False)
-
- if self.custom_thresholds_setting == 1:
- self.customthresholdsCheckBox.setChecked(True)
- else:
- self.customthresholdsCheckBox.setChecked(False)
+ # Set custom checkboxes accordingly
+ self.custompausetimesCheckBox.setChecked(self.custom_pause_times_setting == 1)
+ self.customthresholdsCheckBox.setChecked(self.custom_thresholds_setting == 1)
- if self.group_dummy_splits_undo_skip_setting == 1:
- self.groupDummySplitsCheckBox.setChecked(True)
- else:
- self.groupDummySplitsCheckBox.setChecked(False)
+ # Should be activated by default
+ self.groupDummySplitsCheckBox.setChecked(self.group_dummy_splits_undo_skip_setting != 0)
- if self.loop_setting == 1:
- self.loopCheckBox.setChecked(True)
- else:
- self.loopCheckBox.setChecked(False)
+ self.loopCheckBox.setChecked(self.loop_setting == 1)
# try to set hotkeys from when user last closed the window
try:
@@ -1576,7 +1434,6 @@ def loadSettings(self):
try:
self.pauseLineEdit.setText(str(self.pause_key))
- self.old_pause_key = self.pause_key
except ValueError:
pass
@@ -1594,6 +1451,11 @@ def closeEvent(self, event):
self.saveSettings()
sys.exit()
+class ContinueLoop(Exception):
+ pass
+
+class BreakLoop(Exception):
+ pass
# Widget for dragging screen region
# https://github.com/harupy/snipping-tool
diff --git a/src/split_parser.py b/src/split_parser.py
index ce8d1e59..7d4a55df 100644
--- a/src/split_parser.py
+++ b/src/split_parser.py
@@ -1,142 +1,137 @@
-def threshold_from_filename(filename):
- """
- Retrieve the threshold from the filename, if there is no threshold or the threshold
- doesn't meet the requirements of being between 0.0 and 1.0, then None is returned.
-
- @param filename: String containing the file's name
- @return: A valid threshold, if not then None
- """
-
- # Check to make sure there is a valid floating point number between
- # parentheses of the filename
- try:
- threshold = float(filename.split('(', 1)[1].split(')')[0])
- except:
- return None
-
- # Check to make sure if it is a valid threshold
- if (threshold > 1.0 or threshold < 0.0):
- return None
- else:
- return threshold
-
-def pause_from_filename(filename):
- """
- Retrieve the pause time from the filename, if there is no pause time or the pause time
- isn't a valid number, then None is returned
-
- @param filename: String containing the file's name
- @return: A valid pause time, if not then None
- """
-
- # Check to make sure there is a valid pause time between brackets
- # of the filename
- try:
- pause = float(filename.split('[', 1)[1].split(']')[0])
- except:
- return None
-
- # Pause times should always be positive or zero
- if (pause < 0.0):
- return None
- else:
- return pause
-
-def delay_from_filename(filename):
- """
- Retrieve the delay time from the filename, if there is no delay time or the delay time
- isn't a valid number, then None is returned
-
- @param filename: String containing the file's name
- @return: A valid delay time, if not then 0
- """
-
- # Check to make sure there is a valid delay time between brackets
- # of the filename
- try:
- delay = float(filename.split('#', 1)[1].split('#')[0])
- except:
- return 0
-
- # Delay times should always be positive or zero
- if (delay < 0):
- return 0
- else:
- return delay
-
-def loop_from_filename(filename):
- """
- Retrieve the number of loops from filename, if there is no loop number or the loop number isn't valid,
- then 1 is returned.
-
- @param filename: String containing the file's name
- @return: A valid loop number, if not then 1
- """
-
- # Check to make sure there is a valid loop number between at's
- # of the filename
- try:
- loop = int(filename.split('@', 1)[1].split('@')[0])
- except:
- return 1
-
- # Make loop number 1 if it is less than 1
- if (loop < 1):
- return 1
- else:
- return loop
-
-def flags_from_filename(filename):
- """
- Retrieve the flags from the filename, if there are no flags then 0 is returned
-
- @param filename: String containing the file's name
- @return: The flags as an integer, if invalid flags are found it returns 0
- """
-
- """
- List of flags:
- 'd' = dummy, do nothing when this split is found
- 'm' = mask, use a mask when comparing this split
- 'b' = below threshold, after threshold is met, split when it goes below the threshold.
- 'p' = pause, hit pause key when this split is found
- """
-
- # Check to make sure there are flags between curly braces
- # of the filename
- try:
- flags_str = filename.split('{', 1)[1].split('}')[0]
- except:
- return 0
-
- DUMMY_FLAG = 1 << 0
- MASK_FLAG = 1 << 1
- BELOW_FLAG = 1 << 2
- PAUSE_FLAG = 1 << 3
-
- flags = 0x00
-
- for c in flags_str:
- if c.upper() == 'D':
- flags |= DUMMY_FLAG
- elif c.upper() == 'M':
- flags |= MASK_FLAG
- elif c.upper() == 'B':
- flags |= BELOW_FLAG
- elif c.upper() == 'P':
- flags |= PAUSE_FLAG
+import cv2
+import numpy as np
+
+class SplitImage:
+
+ def __init__(self, path, filename):
+ self.path = path + filename
+ self.filename = filename
+
+ # For the {b} flag
+ self.split_below_threshold = False
+
+ # The index of split_images where the split is found that AutoSplit should jump to
+ # when undoing/skipping a split
+ self.undo_image_index = None
+ self.skip_image_index = None
+
+ # Retrieve the threshold from the filename, if there is no threshold or the threshold
+ # doesn't meet the requirements of being between 0.0 and 1.0, then it is set to None
+
+ # Check to make sure there is a valid floating point number between
+ # parentheses of the filename
+ try:
+ self.threshold = float(self.filename.split('(', 1)[1].split(')')[0])
+
+ # Check to make sure if it is a valid threshold
+ if (self.threshold > 1.0 or self.threshold < 0.0):
+ raise ValueError
+ except:
+ self.threshold = None
+
+
+ # Retrieve the pause time from the filename, if there is no pause time or the pause time
+ # isn't a valid number, then it is set to None
+
+ # Check to make sure there is a valid pause time between brackets
+ # of the filename
+ try:
+ self.pause = float(self.filename.split('[', 1)[1].split(']')[0])
+
+ # Pause times should always be positive or zero
+ if (self.pause < 0.0):
+ raise ValueError
+ except:
+ self.pause = None
+
+
+ # Retrieve the delay time from the filename, if there is no delay time or the delay time
+ # isn't a valid number, then it is set to 0
+
+ # Check to make sure there is a valid delay time between brackets
+ # of the filename
+ try:
+ self.delay = float(self.filename.split('#', 1)[1].split('#')[0])
+
+ # Delay times should always be positive or zero
+ if (self.delay < 0):
+ raise ValueError
+ except:
+ self.delay = 0
+
+
+ # Retrieve the number of loops from filename, if there is no loop number or the loop
+ # number isn't valid, then it is set to 1
+
+ # Check to make sure there is a valid loop number between at's
+ # of the filename
+ try:
+ self.loop = int(self.filename.split('@', 1)[1].split('@')[0])
+
+ # Make loop number 1 if it is less than 1
+ if (self.loop < 1):
+ raise ValueError
+ except:
+ self.loop = 1
+
+
+ # Retrieve the flags from the filename, if there are no flags, then it is set to 0 (no flags set)
+
+ # List of flags:
+ # 'd' = dummy, do nothing when this split is found
+ # 'm' = mask, use a mask when comparing this split
+ # 'b' = below threshold, after threshold is met, split when it goes below the threshold
+ # 'p' = pause, hit pause key when this split is found
+ # 'n' = include next, compares live image with both this image and the one after that simultaneously
+ # 'u' = undo, means that this is the split in this split group that is compared to after you hit undo in the next split group
+
+ # Check to make sure there are flags between curly braces
+ # of the filename and they are all valid
+ try:
+ self.flags = 0
+
+ for c in self.filename.split('{', 1)[1].split('}')[0].upper():
+ if c == 'D':
+ self.flags |= 1 << 0 # Dummy flag {d}: 0x01
+ elif c == 'M':
+ self.flags |= 1 << 1 # Mask flag {m}: 0x02
+ elif c == 'B':
+ self.flags |= 1 << 2 # Below threshold flag {b}: 0x04
+ elif c == 'P':
+ self.flags |= 1 << 3 # Pause flag {p}: 0x08
+ elif c == 'N':
+ self.flags |= 1 << 4 # Include next flag {n}: 0x10
+ elif c == 'U':
+ self.flags |= 1 << 5 # Undo flag {u}: 0x20
+ else:
+ # An invalid flag was caught, this filename was written incorrectly.
+ # Set it to 0, we don't want to interpret any misleading filenames
+ raise ValueError
+ except:
+ self.flags = 0
+
+
+ # Checks if the image is used for resetting
+
+ self.is_reset_image = ('RESET' in self.filename.upper())
+
+ def get_image(self, resize_width, resize_height):
+ if (self.flags & 0x02 == 0x02):
+ # Create mask based on resized, nearest neighbor interpolated split image
+ self.image = cv2.imread(self.path, cv2.IMREAD_UNCHANGED)
+ self.image = cv2.resize(self.image, (resize_width, resize_height),
+ interpolation=cv2.INTER_NEAREST)
+ lower = np.array([0, 0, 0, 1], dtype="uint8")
+ upper = np.array([255, 255, 255, 255], dtype="uint8")
+ self.mask = cv2.inRange(self.image, lower, upper)
+
+ # Set split image as BGR
+ self.image = cv2.cvtColor(self.image, cv2.COLOR_BGRA2BGR)
+
+ # Else if there is no mask flag, open image normally. don't interpolate nearest neighbor here so setups before 1.2.0 still work.
else:
- # An invalid flag was caught, this filename was written incorrectly
- # return 0. We don't want to interpret any misleading filenames
- return 0
+ self.image = cv2.imread(self.path, cv2.IMREAD_COLOR)
+ self.image = cv2.resize(self.image, (resize_width, resize_height))
+ self.mask = None
- return flags
-
-def is_reset_image(filename):
- """
- Checks if the image is used for resetting
-
- @param filename: String containing the file's name
- @return: True if its a reset image
- """
- return ('RESET' in filename.upper())
+ return image
From e5f55f4d42ff891104c6f14329b21ae71eb15f66 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Sun, 19 Apr 2020 13:41:13 +0200
Subject: [PATCH 05/19] Fix settings.pkl not loading
---
src/AutoSplit.py | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index 69ead29d..6491ec61 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -1477,23 +1477,18 @@ def saveSettings(self):
def loadSettings(self):
try:
- with pickle.load(open(os.path.join(sys.path[0], 'settings.pkl'), 'rb')) as f:
- if len(f) == 18:
-
+ with open(os.path.join(sys.path[0], 'settings.pkl'), 'rb') as f:
+ f2 = pickle.load(f)
+ if len(f2) == 18:
# the settings file might not include the pause hotkey yet
- [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting] = f
- else:
- [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.pause_key] = f
+ f2.append('')
+
+ [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
+ self.fps_limit, self.split_key,
+ self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
+ self.hwnd_title,
+ self.custom_pause_times_setting, self.custom_thresholds_setting,
+ self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.pause_key] = f2
self.split_image_directory = str(self.split_image_directory)
self.splitimagefolderLineEdit.setText(self.split_image_directory)
From 546562c23047ae0c1e533c7dd73a222cc971ae1a Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Sun, 19 Apr 2020 16:02:05 +0200
Subject: [PATCH 06/19] Fix settings.pkl not loading again because PyInstaller
---
src/AutoSplit.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index 6491ec61..423e99ed 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -92,6 +92,12 @@ def __init__(self, parent=None):
self.hwnd_title = ''
self.rect = ctypes.wintypes.RECT()
+ # Get the file's path (PyInstaller compatible)
+ if getattr(sys, 'frozen', False):
+ self.file_path = os.path.dirname(os.path.abspath(sys.executable))
+ else:
+ self.file_path = os.path.dirname(os.path.abspath(__file__))
+
# try to load settings
self.loadSettings()
@@ -1466,7 +1472,7 @@ def saveSettings(self):
self.loop_setting = 0
# save settings to settings.pkl
- with open(os.path.join(sys.path[0], 'settings.pkl'), 'wb') as f:
+ with open(os.path.join(self.file_path, 'settings.pkl'), 'wb') as f:
pickle.dump(
[self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
self.fps_limit, self.split_key,
@@ -1477,7 +1483,7 @@ def saveSettings(self):
def loadSettings(self):
try:
- with open(os.path.join(sys.path[0], 'settings.pkl'), 'rb') as f:
+ with open(os.path.join(self.file_path, 'settings.pkl'), 'rb') as f:
f2 = pickle.load(f)
if len(f2) == 18:
# the settings file might not include the pause hotkey yet
From 2b59453ca61dadb32e07d08ed0452d59f80da98f Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Sun, 19 Apr 2020 22:04:01 +0200
Subject: [PATCH 07/19] Fix a lot of bugs
---
res/design.ui | 8 +-
src/AutoSplit.py | 183 ++++++++++++++++++++++----------------------
src/compare.py | 145 +++++++++++------------------------
src/design.py | 6 +-
src/split_parser.py | 2 -
5 files changed, 146 insertions(+), 198 deletions(-)
diff --git a/res/design.ui b/res/design.ui
index abf75c61..56e88d51 100644
--- a/res/design.ui
+++ b/res/design.ui
@@ -405,7 +405,7 @@
- Pause / Unpause
+ Pause
@@ -1159,10 +1159,16 @@
17
+
+
+
Group dummy splits when undoing/skipping
+ true
+
+
false
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index fea74392..b442e85f 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -92,6 +92,12 @@ def __init__(self, parent=None):
self.hwnd_title = ''
self.rect = ctypes.wintypes.RECT()
+ # Get the file's path (PyInstaller compatible)
+ if getattr(sys, 'frozen', False):
+ self.file_path = os.path.dirname(os.path.abspath(sys.executable))
+ else:
+ self.file_path = os.path.dirname(os.path.abspath(__file__))
+
# try to load settings
self.loadSettings()
@@ -408,7 +414,7 @@ def callback():
# try to remove the previously set hotkey if there is one.
try:
keyboard.remove_hotkey(self.split_hotkey)
- except AttributeError:
+ except (AttributeError, KeyError):
pass
# wait until user presses the hotkey, then keyboard module reads the input
@@ -458,7 +464,7 @@ def setResetHotkey(self):
def callback():
try:
keyboard.remove_hotkey(self.reset_hotkey)
- except AttributeError:
+ except (AttributeError, KeyError):
pass
self.reset_key = keyboard.read_hotkey(False)
try:
@@ -494,7 +500,7 @@ def setSkipSplitHotkey(self):
def callback():
try:
keyboard.remove_hotkey(self.skip_split_hotkey)
- except AttributeError:
+ except (AttributeError, KeyError):
pass
self.skip_split_key = keyboard.read_hotkey(False)
@@ -534,7 +540,7 @@ def setUndoSplitHotkey(self):
def callback():
try:
keyboard.remove_hotkey(self.undo_split_hotkey)
- except AttributeError:
+ except (AttributeError, KeyError):
pass
self.undo_split_key = keyboard.read_hotkey(False)
@@ -739,8 +745,7 @@ def autoSplitter(self):
# get split image filenames
self.split_images = []
-
- last_n_flag = False
+ previous_n_flag = False
for image_filename in os.listdir(self.split_image_directory):
self.split_images.append(split_parser.SplitImage(self.split_image_directory, image_filename))
@@ -794,16 +799,17 @@ def autoSplitter(self):
self.splitHotkeyError()
return
- if last_n_flag and self.split_images[-1].loop > 1:
+ if previous_n_flag and self.split_images[-1].loop > 1:
# Error, an image with the {n} flag is followed by an image with a loop > 1
self.includeNextFlagWithLoopError()
return
- last_n_flag = self.split_images[-1].flags & 0x10 = 0x10
+ previous_n_flag = self.split_images[-1].flags & 0x10 == 0x10
# If the last split has the {n} flag, throw an error
if self.split_images[-1].flags & 0x10 == 0x10:
self.lastImageHasIncludeNextFlagError()
+ return
# Find reset image then remove it from the list
self.reset_image = None
@@ -818,6 +824,8 @@ def autoSplitter(self):
return
if self.reset_image is not None:
+ self.reset_image.get_image(self.RESIZE_WIDTH, self.RESIZE_HEIGHT)
+
# If there is no custom threshold for the reset image, throw an error
if self.reset_image.threshold is None:
self.noResetImageThresholdError()
@@ -846,11 +854,11 @@ def autoSplitter(self):
if image.flags & 0x20 == 0x20:
current_group_undo = i
if image.flags & flags == 0:
- for image in self.split_images[current_group_start, i + 1]:
+ for image in self.split_images[current_group_start : i + 1]:
image.undo_image_index = previous_group_undo
if previous_group_start is not None:
- for image in self.split_images[previous_group_start, current_group_start]:
+ for image in self.split_images[previous_group_start : current_group_start]:
image.skip_image_index = i + 1
previous_group_start = current_group_start
@@ -876,6 +884,7 @@ def autoSplitter(self):
# Initialize some settings
self.split_image_index = 0
+ self.split_image_index_changed = True
self.loop_number = 1
self.number_of_split_images = len(self.split_images)
@@ -884,14 +893,17 @@ def autoSplitter(self):
# First while loop: stays in this loop until all of the split images have been split
while self.split_image_index < self.number_of_split_images:
- # Construct list of images that should be compared
- self.current_split_images = []
- for image in self.split_images[self.split_image_index:]:
- self.current_split_images.append(image)
- if image.flags & 0x10 == 0:
- break
+ if self.split_image_index_changed:
+ # Construct list of images that should be compared
+ self.current_split_images = []
+ for image in self.split_images[self.split_image_index :]:
+ image.get_image(self.RESIZE_WIDTH, self.RESIZE_HEIGHT)
+ self.current_split_images.append(image)
+ if image.flags & 0x10 == 0:
+ break
- self.updateSplitImage(self.current_split_images[0])
+ self.updateSplitImage(self.current_split_images[0])
+ self.highest_similarity = 0.001
# second while loop: stays in this loop until similarity threshold is met
# skip loop if we just finished waiting for the split delay and need to press the split key!
@@ -909,29 +921,33 @@ def autoSplitter(self):
reset_masked = (self.reset_image.mask is not None)
capture = self.getCaptureForComparison(reset_masked)
- self.reset_image.similarity = self.compareImage(self.reset_image.image, self.reset_image.mask, capture)
+ self.reset_image.similarity = self.compareImage(self.reset_image, capture)
if self.reset_image.similarity >= self.reset_image.threshold:
keyboard.send(str(self.resetLineEdit.text()))
self.reset()
# loop goes into here if start auto splitter text is "Start Auto Splitter"
if self.startautosplitterButton.text() == 'Start Auto Splitter':
- reset_UI()
+ self.resetUI()
QtGui.QApplication.processEvents()
return
# Reuse capture variable as non-masked capture variable
- self.masked_capture = None
- if reset_masked is not None:
+ masked_capture = None
+ if reset_masked is not None and capture is not None:
masked_capture = capture
capture = None
# Calculate similarities for split images
for image in self.current_split_images:
if image.mask is None:
- self.image.similarity = self.compareImage(image, image.mask, capture)
+ if capture is None:
+ capture = self.getCaptureForComparison(False)
+ image.similarity = self.compareImage(image, capture)
else:
- self.image.similarity = self.compareImage(image, image.mask, masked_capture)
+ if masked_capture is None:
+ masked_capture = self.getCaptureForComparison(True)
+ image.similarity = self.compareImage(image, masked_capture)
# If the similarity becomes higher than highest similarity, set it as such
if image.similarity > self.highest_similarity:
@@ -939,13 +955,13 @@ def autoSplitter(self):
# Show live similarity of first comparison image if the checkbox is checked
if self.showlivesimilarityCheckBox.isChecked():
- self.livesimilarityLabel.setText(str(self.current_split_images[0].similarity)[:4])
+ self.livesimilarityLabel.setText(str(self.current_split_images[0].similarity)[: 4])
else:
self.livesimilarityLabel.setText(' ')
# show live highest similarity if the checkbox is checked
if self.showhighestsimilarityCheckBox.isChecked():
- self.highestsimilarityLabel.setText(str(self.highest_similarity)[:4])
+ self.highestsimilarityLabel.setText(str(self.highest_similarity)[: 4])
else:
self.highestsimilarityLabel.setText(' ')
@@ -1003,7 +1019,7 @@ def autoSplitter(self):
if win32gui.GetWindowText(self.hwnd) == '':
self.reset()
if self.startautosplitterButton.text() == 'Start Auto Splitter':
- self.reset_UI()
+ self.resetUI()
return
# calculate similarity for reset image
@@ -1011,7 +1027,7 @@ def autoSplitter(self):
reset_masked = (self.reset_image.mask is not None)
capture = self.getCaptureForComparison(reset_masked)
- reset_image.similarity = self.compareImage(self.reset_image, self.reset_mask, capture)
+ reset_image.similarity = self.compareImage(self.reset_image, capture)
if reset_image.similarity >= self.reset_image.threshold:
keyboard.send(str(self.resetLineEdit.text()))
self.reset()
@@ -1020,45 +1036,52 @@ def autoSplitter(self):
QtTest.QTest.qWait(1)
# Split key press unless dummy flag is set
- if (self.flags & 0x01 == 0x00):
+ if (self.successful_split_image.flags & 0x01 == 0x00):
keyboard.send(str(self.splitLineEdit.text()))
# Pause key press if pause flag is set
- if (self.flags & 0x08 == 0x08):
+ if (self.successful_split_image.flags & 0x08 == 0x08):
keyboard.send(str(self.pauseLineEdit.text()))
# Increase loop number if needed, set to 1 if it was the last loop
if self.loop_number < self.successful_split_image.loop:
self.loop_number += 1
+ self.split_image_index_changed = False
else:
self.loop_number = 1
+ self.split_image_index_changed = True
number_of_comparison_images = len(self.current_split_images)
- # If loop check box is checked and its the last split, go to first split
- # Else if current loop amount is back to 1, add 1 to split image number
- if self.loopCheckBox.isChecked() and self.split_image_index == self.number_of_split_images - 1 and self.loop_number == 1:
- self.split_image_index = 0
- elif self.loop_number == 1:
- self.split_image_index += number_of_comparison_images
+ if self.loop_number == 1:
+ # If loop check box is checked and its the last split, go to first split
+ # Else if current loop amount is back to 1, add 1 to split image number
+ if self.loopCheckBox.isChecked() and self.successful_split_image.skip_image_index is None:
+ self.split_image_index = 0
+ else:
+ self.split_image_index += number_of_comparison_images
# Set a "pause" split image number. This is done so that it can detect if user hit split/undo split while paused
pause_split_image_index = self.split_image_index
pause_loop_number = self.loop_number
# If it's not the last split image, pause for the amount set by the user
- if self.number_of_split_images != self.split_image_index:
+ if self.successful_split_image.pause > 0:
# Set current split image to none
self.currentSplitImage.setText('none (paused)')
self.currentsplitimagefileLabel.setText(' ')
self.currentSplitImage.setAlignment(QtCore.Qt.AlignCenter)
self.imageloopLabel.setText('Image Loop #: -')
- # If it's the last split image and last loop number, disable the skip split button
- self.skipsplitButton.setEnabled(self.split_images[self.split_image_index].skip_image_index is not None or self.split_images[self.split_image_index].loop != self.loop_number)
+ # Make sure the index doesn't exceed the list
+ if self.split_image_index < len(self.split_images):
+ # If it's the last split image and last loop number, disable the skip split button
+ self.skipsplitButton.setEnabled(self.split_images[self.split_image_index].skip_image_index is not None or self.split_images[self.split_image_index].loop != self.loop_number)
- # If it's the first split image and first loop, disable the undo split button
- self.undosplitButton.setEnabled(self.split_images[self.split_image_index].undo_image_index is not None or self.split_images[self.split_image_index].loop != 1)
+ # If it's the first split image and first loop, disable the undo split button
+ self.undosplitButton.setEnabled(self.split_images[self.split_image_index].undo_image_index is not None or self.split_images[self.split_image_index].loop != 1)
+ else:
+ self.undosplitButton.setEnabled(False)
QtGui.QApplication.processEvents()
@@ -1070,7 +1093,7 @@ def autoSplitter(self):
if win32gui.GetWindowText(self.hwnd) == '':
self.reset()
if self.startautosplitterButton.text() == 'Start Auto Splitter':
- reset_UI()
+ self.resetUI()
return
# check for skip/undo split:
@@ -1082,7 +1105,7 @@ def autoSplitter(self):
reset_masked = (self.reset_mask is not None)
capture = self.getCaptureForComparison(reset_masked)
- reset_image.similarity = self.compareImage(self.reset_image.image, self.reset_image.mask, capture)
+ reset_image.similarity = self.compareImage(self.reset_image, capture)
if reset_image.similarity >= self.reset_image.threshold:
keyboard.send(str(self.resetLineEdit.text()))
self.reset()
@@ -1092,10 +1115,10 @@ def autoSplitter(self):
# loop breaks to here when the last image splits
self.startautosplitterButton.setText('Start Auto Splitter')
- reset_UI()
+ self.resetUI()
QtGui.QApplication.processEvents()
- def reset_UI(self):
+ def resetUI(self):
self.imageloopLabel.setText("Image Loop #:")
self.currentSplitImage.setText(' ')
self.currentsplitimagefileLabel.setText(' ')
@@ -1115,21 +1138,13 @@ def reset_UI(self):
self.customthresholdsCheckBox.setEnabled(True)
self.groupDummySplitsCheckBox.setEnabled(True)
- def compareImage(self, image, mask, capture):
- if mask is None:
- if self.comparisonmethodComboBox.currentIndex() == 0:
- return compare.compare_l2_norm(image, capture)
- elif self.comparisonmethodComboBox.currentIndex() == 1:
- return compare.compare_histograms(image, capture)
- elif self.comparisonmethodComboBox.currentIndex() == 2:
- return compare.compare_phash(image, capture)
- else:
- if self.comparisonmethodComboBox.currentIndex() == 0:
- return compare.compare_l2_norm_masked(image, capture, mask)
- elif self.comparisonmethodComboBox.currentIndex() == 1:
- return compare.compare_histograms_masked(image, capture, mask)
- elif self.comparisonmethodComboBox.currentIndex() == 2:
- return compare.compare_phash_masked(image, capture, mask)
+ def compareImage(self, image, capture):
+ if self.comparisonmethodComboBox.currentIndex() == 0:
+ return compare.compare_l2_norm(image, capture)
+ elif self.comparisonmethodComboBox.currentIndex() == 1:
+ return compare.compare_histograms(image, capture)
+ elif self.comparisonmethodComboBox.currentIndex() == 2:
+ return compare.compare_phash(image, capture)
def getCaptureForComparison(self, masked):
# Grab screenshot of capture region
@@ -1152,6 +1167,9 @@ def shouldCheckResetImage(self):
def updateSplitImage(self, split_image):
+ # Set Image Loop #
+ self.imageloopLabel.setText("Image Loop #: " + str(self.loop_number))
+
if len(self.current_split_images) > 1:
self.currentSplitImage.setText(str(len(self.current_split_images)) + ' images')
self.currentsplitimagefileLabel.setText(' ')
@@ -1180,22 +1198,6 @@ def updateSplitImage(self, split_image):
self.current_split_images[0].get_image(self.RESIZE_WIDTH, self.RESIZE_HEIGHT)
- # If the unique parameters are selected, go ahead and set the spinboxes to those values
- if self.custompausetimesCheckBox.isChecked():
- self.pauseDoubleSpinBox.setValue(split_image.pause)
-
- if self.customthresholdsCheckBox.isChecked():
- self.similaritythresholdDoubleSpinBox.setValue(split_image.threshold)
-
- # Set Image Loop #
- self.imageloopLabel.setText("Image Loop #: " + str(self.loop_number))
-
- # Need to set split below threshold to false each time an image updates.
- self.split_below_threshold = False
-
- self.similarity = 0
- self.highest_similarity = 0.001
-
# Error messages
def splitImageDirectoryError(self):
@@ -1336,7 +1338,7 @@ def saveSettings(self):
self.loop_setting = int(self.loopCheckBox.isChecked())
# save settings to settings.pkl
- with open(os.path.join(sys.path[0], 'settings.pkl'), 'wb') as f:
+ with open(os.path.join(self.file_path, 'settings.pkl'), 'wb') as f:
pickle.dump(
[self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
self.fps_limit, self.split_key,
@@ -1347,23 +1349,18 @@ def saveSettings(self):
def loadSettings(self):
try:
- with pickle.load(open(os.path.join(sys.path[0], 'settings.pkl'), 'rb')) as f:
- if len(f) == 18:
-
- # the settings file might not include the pause hotkey yet
- [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting] = f
- else:
- [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.pause_key] = f
+ with open(os.path.join(self.file_path, 'settings.pkl'), 'rb') as f:
+ f2 = pickle.load(f)
+ if len(f2) == 18:
+ # The settings file might not include the pause hotkey yet
+ f2.append('')
+
+ [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
+ self.fps_limit, self.split_key,
+ self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
+ self.hwnd_title,
+ self.custom_pause_times_setting, self.custom_thresholds_setting,
+ self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.pause_key] = f2
self.split_image_directory = str(self.split_image_directory)
self.splitimagefolderLineEdit.setText(self.split_image_directory)
diff --git a/src/compare.py b/src/compare.py
index 72b62913..627104ab 100644
--- a/src/compare.py
+++ b/src/compare.py
@@ -1,7 +1,7 @@
from PIL import Image
import imagehash
-import numpy
+import numpy as np
import cv2
def compare_histograms(source, capture):
@@ -9,35 +9,17 @@ def compare_histograms(source, capture):
Compares two images by calculating their histograms, normalizing them, and
then comparing them using Bhattacharyya distance.
- @param source: 3 color image of any given width and height
+ @param source: 3 color SplitImage of any given width and height
@param capture: An image matching the dimensions of the source
- @return: The similarity between the histograms as a number 0 to 1.
+ @return: The similarity between the histograms as a number 0 to 1
"""
- source_hist = cv2.calcHist([source], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
- capture_hist = cv2.calcHist([capture], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
-
- cv2.normalize(source_hist, source_hist)
- cv2.normalize(capture_hist, capture_hist)
-
- return 1 - cv2.compareHist(source_hist, capture_hist, cv2.HISTCMP_BHATTACHARYYA)
+ source_hist = cv2.calcHist([source.image], [0, 1, 2], source.mask, [8, 8, 8], [0, 256, 0, 256, 0, 256])
+ capture_hist = cv2.calcHist([capture], [0, 1, 2], source.mask, [8, 8, 8], [0, 256, 0, 256, 0, 256])
-def compare_histograms_masked(source, capture, mask):
- """
- Compares two images by calculating their histograms using a mask, normalizing
- them, and then comparing them using Bhattacharyya distance.
-
- @param source: 3 color image of any given width and height
- @param capture: An image matching the dimensions of the source
- @param mask: An image matching the dimensions of the source, but 1 channel grayscale
- @return: The similarity between the histograms as a number 0 to 1.
- """
- source_hist = cv2.calcHist([source], [0, 1, 2], mask, [8, 8, 8], [0, 256, 0, 256, 0, 256])
- capture_hist = cv2.calcHist([capture], [0, 1, 2], mask, [8, 8, 8], [0, 256, 0, 256, 0, 256])
-
cv2.normalize(source_hist, source_hist)
cv2.normalize(capture_hist, capture_hist)
-
+
return 1 - cv2.compareHist(source_hist, capture_hist, cv2.HISTCMP_BHATTACHARYYA)
def compare_l2_norm(source, capture):
@@ -45,33 +27,22 @@ def compare_l2_norm(source, capture):
Compares two images by calculating the L2 Error (square-root
of sum of squared error)
- @param source: Image of any given shape
+ @param source: SplitImage of any given shape
@param capture: Image matching the dimensions of the source
- @return: The similarity between the images as a number 0 to 1.
+ @return: The similarity between the images as a number 0 to 1
"""
- error = cv2.norm(source, capture, cv2.NORM_L2)
-
- # The L2 Error is summed across all pixels, so this normalizes
- max_error = (source.size ** 0.5) * 255
-
- return 1 - (error/max_error)
-
-def compare_l2_norm_masked(source, capture, mask):
- """
- Compares two images by calculating the L2 Error (square-root
- of sum of squared error)
+ if source.mask is None:
+ error = cv2.norm(source.image, capture, cv2.NORM_L2)
- @param source: Image of any given shape
- @param capture: Image matching the dimensions of the source
- @param mask: An image matching the dimensions of the source, but 1 channel grayscale
- @return: The similarity between the images as a number 0 to 1.
- """
+ # The L2 Error is summed across all pixels, so this normalizes
+ max_error = (source.image.size ** 0.5) * 255
- error = cv2.norm(source, capture, cv2.NORM_L2, mask)
+ else:
+ error = cv2.norm(source.image, capture, cv2.NORM_L2, source.mask)
- # The L2 Error is summed across all pixels, so this normalizes
- max_error = (3 * numpy.count_nonzero(mask) * 255 * 255) ** 0.5
+ # The L2 Error is summed across all pixels, so this normalizes
+ max_error = (3 * np.count_nonzero(source.mask) * 255 * 255) ** 0.5
return 1 - (error / max_error)
@@ -83,77 +54,51 @@ def compare_template(source, capture):
@param source: The subsection being searched for within the capture
@param capture: Capture of an image larger than the source
@return: The best similarity for a region found in the image. This is
- represented as a number from 0 to 1.
- """
-
- result = cv2.matchTemplate(capture, source, cv2.TM_SQDIFF)
- min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
-
- # matchTemplate returns the sum of square differences, this is the max
- # that the value can be. Used for normalizing from 0 to 1.
- max_error = source.size * 255 * 255
-
- return 1 - (min_val/max_error)
-
-def compare_template_masked(source, capture, mask):
+ represented as a number from 0 to 1
"""
- Checks if the source is located within the capture by using
- the sum of square differences. The mask is used to search for
- non-rectangular images within the capture
- @param source: The subsection being searched for within the capture
- @param capture: Capture of an image larger than the source
- @param mask: The mask of the source with the same dimensions
- @return: The best similarity for a region found in the image. This is
- represented as a number from 0 to 1.
- """
+ if source.mask is None:
+ result = cv2.matchTemplate(capture, source.image, cv2.TM_SQDIFF)
+ else:
+ result = cv2.matchTemplate(capture, source.image, cv2.TM_SQDIFF, None, source.mask)
- result = cv2.matchTemplate(capture, source, cv2.TM_SQDIFF, None, mask)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
- return 1 - (min_val/numpy.count_nonzero(mask))
+ if source.mask is None:
+ # matchTemplate returns the sum of square differences, this is the max
+ # that the value can be. Used for normalizing from 0 to 1.
+ max_error = source.size * 255 * 255
+
+ return 1 - (min_val / max_error)
+ else:
+ return 1 - (min_val / np.count_nonzero(source.mask))
def compare_phash(source, capture):
"""
Compares the pHash of the two given images and returns the similarity between
the two.
- @param source: Image of any given shape as a numpy array
- @param capture: Image of any given shape as a numpy array
+ @param source: SplitImage of any given shape
+ @param capture: SplitImage of any given shape
@return: The similarity between the hashes of the image as a number 0 to 1.
"""
- source = Image.fromarray(source)
- capture = Image.fromarray(capture)
+ if source.mask is not None:
+ # Since imagehash doesn't have any masking itself, bitwise_and will allow us
+ # to apply the mask to the source and capture before calculating the pHash for
+ # each of the images. As a result of this, this function is not going to be very
+ # helpful for large masks as the images when shrinked down to 8x8 will mostly be
+ # the same
+ source.image = np.array(source.image)
+ capture = np.array(capture)
- source_hash = imagehash.phash(source)
- capture_hash = imagehash.phash(capture)
-
- return 1 - ((source_hash - capture_hash)/64.0)
-
-def compare_phash_masked(source, capture, mask):
- """
- Compares the pHash of the two given images and returns the similarity between
- the two.
-
- @param source: Image of any given shape as a numpy array
- @param capture: Image of any given shape as a numpy array
- @param mask: An image matching the dimensions of the source, but 1 channel grayscale
- @return: The similarity between the hashes of the image as a number 0 to 1.
- """
+ source.image = cv2.bitwise_and(source.image, source.image, mask=source.mask)
+ capture = cv2.bitwise_and(capture, capture, mask=source.mask)
- # Since imagehash doesn't have any masking itself, bitwise_and will allow us
- # to apply the mask to the source and capture before calculating the pHash for
- # each of the images. As a result of this, this function is not going to be very
- # helpful for large masks as the images when shrinked down to 8x8 will mostly be
- # the same
- source = cv2.bitwise_and(source, source, mask=mask)
- capture = cv2.bitwise_and(capture, capture, mask=mask)
-
- source = Image.fromarray(source)
- capture = Image.fromarray(capture)
+ source.image = Image.fromarray(source.image)
+ capture = Image.fromarray(capture)
- source_hash = imagehash.phash(source)
+ source_hash = imagehash.phash(source.image)
capture_hash = imagehash.phash(capture)
- return 1 - ((source_hash - capture_hash)/64.0)
+ return 1 - ((source_hash - capture_hash) / 64.0)
diff --git a/src/design.py b/src/design.py
index bac1e67d..38df3d7a 100644
--- a/src/design.py
+++ b/src/design.py
@@ -364,7 +364,9 @@ def setupUi(self, MainWindow):
self.alignregionButton.setObjectName(_fromUtf8("alignregionButton"))
self.groupDummySplitsCheckBox = QtGui.QCheckBox(self.centralwidget)
self.groupDummySplitsCheckBox.setGeometry(QtCore.QRect(252, 470, 230, 17))
- self.groupDummySplitsCheckBox.setChecked(False)
+ self.groupDummySplitsCheckBox.setWhatsThis(_fromUtf8(""))
+ self.groupDummySplitsCheckBox.setChecked(True)
+ self.customthresholdsCheckBox.setTristate(False)
self.groupDummySplitsCheckBox.setObjectName(_fromUtf8("groupDummySplitsCheckBox"))
self.selectwindowButton = QtGui.QPushButton(self.centralwidget)
self.selectwindowButton.setGeometry(QtCore.QRect(5, 117, 101, 23))
@@ -503,7 +505,7 @@ def retranslateUi(self, MainWindow):
self.resetLabel.setText(_translate("MainWindow", "Reset", None))
self.skiptsplitLabel.setText(_translate("MainWindow", "Skip Split", None))
self.undosplitLabel.setText(_translate("MainWindow", "Undo Split", None))
- self.pausehotkeyLabel.setText(_translate("MainWindow", "Pause / Unpause", None))
+ self.pausehotkeyLabel.setText(_translate("MainWindow", "Pause", None))
self.setsplithotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
self.setresethotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
self.setskipsplithotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
diff --git a/src/split_parser.py b/src/split_parser.py
index 7d4a55df..c29a9551 100644
--- a/src/split_parser.py
+++ b/src/split_parser.py
@@ -133,5 +133,3 @@ def get_image(self, resize_width, resize_height):
self.image = cv2.imread(self.path, cv2.IMREAD_COLOR)
self.image = cv2.resize(self.image, (resize_width, resize_height))
self.mask = None
-
- return image
From c226115e2d96d8bd2320251dfce92d44f4c0921b Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Sun, 19 Apr 2020 23:25:39 +0200
Subject: [PATCH 08/19] More fixes
---
src/AutoSplit.py | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index b442e85f..9453c46d 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -739,6 +739,9 @@ def autoSplitter(self):
if self.splitimagefolderLineEdit.text() == 'No Folder Selected':
self.splitImageDirectoryError()
return
+ if len(os.listdir(self.split_image_directory)) == 0:
+ self.noSplitImagesError()
+ return
if self.hwnd == 0 or win32gui.GetWindowText(self.hwnd) == '':
self.regionError()
return
@@ -816,7 +819,10 @@ def autoSplitter(self):
for i, image in enumerate(self.split_images):
if image.is_reset_image:
# Check that there's only one reset image
- if reset_image is None:
+ if self.reset_image is None:
+ if len(self.split_images) == 1:
+ self.noSplitImagesError()
+ return
self.reset_image = image
self.split_images.pop(i)
else:
@@ -1051,22 +1057,20 @@ def autoSplitter(self):
self.loop_number = 1
self.split_image_index_changed = True
- number_of_comparison_images = len(self.current_split_images)
-
if self.loop_number == 1:
# If loop check box is checked and its the last split, go to first split
# Else if current loop amount is back to 1, add 1 to split image number
if self.loopCheckBox.isChecked() and self.successful_split_image.skip_image_index is None:
self.split_image_index = 0
else:
- self.split_image_index += number_of_comparison_images
+ self.split_image_index += len(self.current_split_images)
# Set a "pause" split image number. This is done so that it can detect if user hit split/undo split while paused
pause_split_image_index = self.split_image_index
pause_loop_number = self.loop_number
# If it's not the last split image, pause for the amount set by the user
- if self.successful_split_image.pause > 0:
+ if self.successful_split_image.pause > 0 and (self.loopCheckBox.isChecked() or len(self.split_images) < self.split_image_index):
# Set current split image to none
self.currentSplitImage.setText('none (paused)')
self.currentsplitimagefileLabel.setText(' ')
@@ -1101,12 +1105,12 @@ def autoSplitter(self):
break
# calculate similarity for reset image
- if self.shouldCheckResetImage() == True:
- reset_masked = (self.reset_mask is not None)
+ if self.shouldCheckResetImage():
+ reset_masked = (self.reset_image.mask is not None)
capture = self.getCaptureForComparison(reset_masked)
- reset_image.similarity = self.compareImage(self.reset_image, capture)
- if reset_image.similarity >= self.reset_image.threshold:
+ self.reset_image.similarity = self.compareImage(self.reset_image, capture)
+ if self.reset_image.similarity >= self.reset_image.threshold:
keyboard.send(str(self.resetLineEdit.text()))
self.reset()
continue
@@ -1163,7 +1167,7 @@ def getCaptureForComparison(self, masked):
return capture
def shouldCheckResetImage(self):
- return (self.reset_image is not None and time.time() - self.run_start_time > self.reset_image_pause_time)
+ return (self.reset_image is not None and time.time() - self.run_start_time > self.reset_image.pause)
def updateSplitImage(self, split_image):
@@ -1206,6 +1210,12 @@ def splitImageDirectoryError(self):
msgBox.setText("No split image folder is selected.")
msgBox.exec_()
+ def noSplitImagesError(self):
+ msgBox = QtGui.QMessageBox()
+ msgBox.setWindowTitle('Error')
+ msgBox.setText("Your split image folder doesn't contain any splits.")
+ msgBox.exec_()
+
def imageTypeError(self, image):
msgBox = QtGui.QMessageBox()
msgBox.setWindowTitle('Error')
@@ -1270,7 +1280,7 @@ def multipleResetImagesError(self):
def noResetImageThresholdError(self):
msgBox = QtGui.QMessageBox()
msgBox.setWindowTitle('Error')
- msgBox.setText("Reset Image must have a custom threshold. Please set one and check that it is valid")
+ msgBox.setText("Reset image must have a custom threshold. Please set one and check that it is valid.")
msgBox.exec_()
def resetHotkeyError(self):
From b6257ce414d8e1c1f840e7a0625cbacf6cb2ebe5 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Mon, 20 Apr 2020 17:13:15 +0200
Subject: [PATCH 09/19] Add requirements.txt file for pip
---
README.md | 21 ++++++++++++---------
requirements.txt | 21 +++++++++++++++++++++
2 files changed, 33 insertions(+), 9 deletions(-)
create mode 100644 requirements.txt
diff --git a/README.md b/README.md
index 1464a359..6a52656f 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
-# AutoSplit
+# AutoSplit
This program compares split images to a capture region of any window (OBS, xsplit, 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.). The purpose of this program is to remove the need to manually press your split hotkey and also increase the accuracy of your splits.
-
+
# TUTORIAL
@@ -14,15 +14,18 @@ This program compares split images to a capture region of any window (OBS, xspli
- Windows 7 and 10.
### Opening the program
-- Download the [latest version](https://github.com/austinryan/Auto-Split/releases)
+- Download the [latest version](../../releases)
- Extract the file and open `AutoSplit.exe`.
+### Building
+- Read [requirements.txt](/requirements.txt) for information on how to run/build the python code
+
## Split Image Folder
- Supported image file types: `.png`, `.jpg`, `.jpeg`, `.bmp`, and [more](https://docs.opencv.org/3.0-beta/modules/imgcodecs/doc/reading_and_writing_images.html#imread).
- Images can be any size.
- Images are matched in alphanumerical order.
- Recommended filenaming convention: `001_SplitName.png, 002_SplitName.png, 003_SplitName.png`...
-- Custom split image settings are handled in the filename. See how [here](https://github.com/Toufool/Auto-Split#custom-split-image-settings).
+- Custom split image settings are handled in the filename. See how [here](#custom-split-image-settings).
- Images can be created using Print Screen, [Snipping Tool](https://support.microsoft.com/en-us/help/4027213/windows-10-open-snipping-tool-and-take-a-screenshot), or AutoSplit's Take Screenshot button.
## Capture Region
@@ -44,7 +47,7 @@ This program compares split images to a capture region of any window (OBS, xspli
- There are three comparison methods to choose from: L2 Norm, Histograms, and pHash.
- L2 Norm: This method finds the difference between each pixel, squares it, and sums it over the entire image and takes the square root. This is very fast but is a problem if your image is high frequency. Any translational movement or rotation can cause similarity to be very different.
- Histograms: An explanation on Histograms comparison can be found [here](https://mpatacchiola.github.io/blog/2016/11/12/the-simplest-classifier-histogram-intersection.html). This is a great method to use if you are using several masked images.
- - pHash: An explanation on pHash comparison can be found [here](http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html). It is highly recommended to NOT use pHash if you use masked images. It is very inaccurate.
+ - pHash: An explanation on pHash comparison can be found [here](https://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html). It is highly recommended to **_not_** use pHash if you use masked images. It is very inaccurate.
- Note: v1.0 used L2 Norm.
### Show Live Similarity
@@ -84,10 +87,10 @@ This program compares split images to a capture region of any window (OBS, xspli
- `004_SplitName(0.9)_[10]_#3500#_@3@_{b}.png` is the fourth split image with a threshold of 0.9, pause time of 10 seconds, delay split time of 3.5 seconds, will loop 3 times, and will split when similarity is below the threshold rather than above.
### How to Create a Masked Image
-The best way to create a masked image is to set your capture region as the entire game screen, take a screenshot, and use a program like [paint.net](https://www.getpaint.net/) to "erase" (make transparent) everything you don't want the program to compare. More on how to creating images with transparency using paint.net can be found in [this tutorial](https://www.youtube.com/watch?v=v53kkUYFVn8). The last thing you need to do is add `{m}` to the filename. For visualization, here is what the capture region compared to a masked split image looks like if you would want to split on "Shine Get!" text in Super Mario Sunshine:
+The best way to create a masked image is to set your capture region as the entire game screen, take a screenshot, and use a program like [paint.net](https://www.getpaint.net/) to "erase" (make transparent) everything you don't want the program to compare. More on how to creating images with transparency using paint.net can be found in [this tutorial](https://youtu.be/v53kkUYFVn8). The last thing you need to do is add `{m}` to the filename. For visualization, here is what the capture region compared to a masked split image looks like if you would want to split on "Shine Get!" text in Super Mario Sunshine:
-
+
### Reset image
@@ -129,14 +132,14 @@ Each time AutoSplit is closed, it saves a the setting file `settings.pkl` to the
- The window of the capture region cannot be minimized.
## Known Issues
-- When setting your region, you may only see a black image. This is caused by hardware acceleration. You may be able to disable this through the application itself like in [Google Chrome](https://www.technize.net/google-chrome-disable-hardware-acceleration/). If not, this can also be disabled through [Windows](https://www.thewindowsclub.com/hardware-acceleration-windows-7). NOTE: If you notice any computer performance issues after disabling hardware acceleration, re-enable it.
+- When setting your region, you may only see a black image. This is caused by hardware acceleration. You may be able to disable this through the application itself like in [Google Chrome](https://www.technize.net/google-chrome-disable-hardware-acceleration/). If not, this can also be disabled through [Windows](https://www.thewindowsclub.com/hardware-acceleration-windows-7). **NOTE:** If you notice any computer performance issues after disabling hardware acceleration, re-enable it.
- Known to currently have issues selecting a region in Streamlabs OBS (only shows black image).
- Using numpad number keys when numlock is on does not split correctly. Either avoid using numpad or turn numlock off to avoid this issue.
- LiveSplit and wsplit will not split correctly if you are holding shift, ctrl, or alt when a match occurs.
- Numlock on keys are linked to numlock-off keys. For example, if you set your reset hotkey to 2, you can hit arrow down and it will reset and vice versa.
## Resources
-- Still need help? [Open an issue](https://github.com/Toufool/Auto-Split/issues)
+- Still need help? [Open an issue](../../issues)
- Join the [AutoSplit Discord](https://discord.gg/Qcbxv9y)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 00000000..aef025ab
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,21 @@
+# Requirements file for AutoSplit
+#
+# Python: CPython 3.7 (not 3.8 as there is no cp38 wheel for PyQt4)
+#
+# Usage: pip install -r requirements.txt
+#
+# Building AutoSplit with PyInstaller: pyinstaller -w -F src\AutoSplit.py
+#
+# If you are on a 32-bit computer, replace the "_amd64" in the 2nd URL with "32".
+# You can find other wheels here: https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyqt4
+https://download.lfd.uci.edu/pythonlibs/s2jqpv5t/PyQt4-4.11.4-cp37-cp37m-win_amd64.whl
+#
+# Comment this out if you don't want to build AutoSplit:
+PyInstaller
+#
+# Other dependencies:
+opencv-python
+Pillow
+ImageHash
+pywin32
+keyboard
\ No newline at end of file
From 405c51fbfddd022e6fbf5f15ae86550246c48c28 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Mon, 20 Apr 2020 17:15:53 +0200
Subject: [PATCH 10/19] Fix link
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 6a52656f..2e9695ad 100644
--- a/README.md
+++ b/README.md
@@ -76,7 +76,7 @@ This program compares split images to a capture region of any window (OBS, xspli
- **NOTE:** If you think these flags are too hard to understand, don't worry, just ignore the last two ones. They make everything more complicated.
- `{d}` dummy split image. When matched, it moves to the next image without hitting your split hotkey.
- `{b}` split when similarity goes below the threshold rather than above. When a split image filename has this flag, the split image similarity will go above the threshold, do nothing, and then split the next time the similarity goes below the threshold.
- - `{m}` masked split image. This allows you to customize what you want compared in your split image by using transparency. Transparent pixels in the split image are ignored when comparing. This is useful if only a certain part of the capture region is consistent (for example, consistent text on the screen, but the background is always different). These images *must* be `.png` and contain transparency. For more on this, see [How to Create a Masked Image](https://github.com/Toufool/Auto-Split/blob/master/README.md#how-to-create-a-masked-image). Histogram or L2 norm comparison is recommended if you use any masked images. It is highly recommended that you do **_not_** use pHash comparison if you use any masked images, as it is very inaccurate
+ - `{m}` masked split image. This allows you to customize what you want compared in your split image by using transparency. Transparent pixels in the split image are ignored when comparing. This is useful if only a certain part of the capture region is consistent (for example, consistent text on the screen, but the background is always different). These images *must* be `.png` and contain transparency. For more on this, see [How to Create a Masked Image](#how-to-create-a-masked-image). Histogram or L2 norm comparison is recommended if you use any masked images. It is highly recommended that you do **_not_** use pHash comparison if you use any masked images, as it is very inaccurate
- `{p}` pause/unpause timer. This allows you to let AutoSplit remove the loading times. Use dummy flag as well if you only want to pause/unpause but don't want to split. There is no "Pause / Unpause" button in AutoSplit because it doesn't keep track of whether your timer is currently paused.
- `{n}` include next split as well. This allows you to have more than one split image that the live image is compared to simultaneously. You can chain split images marked with this flag together. Only the first image in such a chain can have a loop value greater than 1. This flag is compatible with all other values/flags.
- `{u}` undo split image. This flag is only useful with the "Group dummy splits" checkbox enabled. The split image with this flag is the split in this split group that is chosen when you hit undo while in the split group after this one. This is useful if there are split images at the beginning and the end of a split image group. If multiple split images in the same group have this flag, only the last one will work. Default is the first split of the group.
From a1fc02b150572410a85f636b4630768336433327 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Mon, 20 Apr 2020 23:18:18 +0200
Subject: [PATCH 11/19] Fix bugs
---
src/AutoSplit.py | 23 ++++++++++++++------
src/split_parser.py | 53 +++++++++++++++++++++++++--------------------
2 files changed, 45 insertions(+), 31 deletions(-)
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index 9453c46d..a7a3485c 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -749,9 +749,18 @@ def autoSplitter(self):
# get split image filenames
self.split_images = []
previous_n_flag = False
+ if self.customthresholdsCheckBox.isEnabled():
+ threshold_for_all_images = None
+ else:
+ threshold_for_all_images = self.similaritythresholdDoubleSpinBox.value()
+
+ if self.custompausetimesCheckBox.isEnabled():
+ pause_for_all_images = None
+ else:
+ pause_for_all_images = self.pauseDoubleSpinBox.value()
for image_filename in os.listdir(self.split_image_directory):
- self.split_images.append(split_parser.SplitImage(self.split_image_directory, image_filename))
+ self.split_images.append(split_parser.SplitImage(self.split_image_directory, image_filename, threshold_for_all_images, pause_for_all_images))
# Make sure that each of the images follows the guidelines for correct format
# according to all of the settings selected by the user.
@@ -981,10 +990,10 @@ def autoSplitter(self):
for image in self.current_split_images:
# If the {b} flag is set, let similarity go above threshold first, then split on similarity below threshold
# Otherwise just split when similarity goes above threshold
- if image.flags & 0x04 == 0x04 and image.split_below_threshold == False and image.similarity >= self.similaritythresholdDoubleSpinBox.value():
+ if image.flags & 0x04 == 0x04 and image.split_below_threshold == False and image.similarity >= image.threshold:
image.split_below_threshold = True
raise ContinueLoop
- if (image.flags & 0x04 == 0x04 and image.split_below_threshold == True and image.similarity < self.similaritythresholdDoubleSpinBox.value()) or (image.similarity >= self.similaritythresholdDoubleSpinBox.value() and image.flags & 0x04 == 0):
+ if (image.flags & 0x04 == 0x04 and image.split_below_threshold == True and image.similarity < image.threshold) or (image.similarity >= image.threshold and image.flags & 0x04 == 0):
self.successful_split_image = image
raise BreakLoop
@@ -1070,7 +1079,7 @@ def autoSplitter(self):
pause_loop_number = self.loop_number
# If it's not the last split image, pause for the amount set by the user
- if self.successful_split_image.pause > 0 and (self.loopCheckBox.isChecked() or len(self.split_images) < self.split_image_index):
+ if self.successful_split_image.pause > 0 and (self.loopCheckBox.isChecked() or len(self.split_images) > self.split_image_index):
# Set current split image to none
self.currentSplitImage.setText('none (paused)')
self.currentsplitimagefileLabel.setText(' ')
@@ -1091,8 +1100,8 @@ def autoSplitter(self):
# I have a pause loop here so that it can check if the user presses skip split, undo split, or reset here.
# This should probably eventually be a signal... but it works
- pause_start_time = time.time()
- while time.time() - pause_start_time < self.pauseDoubleSpinBox.value():
+ pause_end_time = time.time() + self.successful_split_image.pause
+ while time.time() < pause_end_time:
# check for reset
if win32gui.GetWindowText(self.hwnd) == '':
self.reset()
@@ -1190,7 +1199,7 @@ def updateSplitImage(self, split_image):
self.split_image_display = cv2.resize(self.split_image_display, (240, 180))
# If not flagged as mask, open normally
else:
- self.split_image_display = cv2.imread(self.split_image.path, cv2.IMREAD_COLOR)
+ self.split_image_display = cv2.imread(split_image.path, cv2.IMREAD_COLOR)
self.split_image_display = cv2.cvtColor(self.split_image_display, cv2.COLOR_BGR2RGB)
self.split_image_display = cv2.resize(self.split_image_display, (240, 180))
diff --git a/src/split_parser.py b/src/split_parser.py
index c29a9551..ac367f0f 100644
--- a/src/split_parser.py
+++ b/src/split_parser.py
@@ -3,7 +3,7 @@
class SplitImage:
- def __init__(self, path, filename):
+ def __init__(self, path, filename, threshold_for_all_images, pause_for_all_images):
self.path = path + filename
self.filename = filename
@@ -15,34 +15,39 @@ def __init__(self, path, filename):
self.undo_image_index = None
self.skip_image_index = None
- # Retrieve the threshold from the filename, if there is no threshold or the threshold
- # doesn't meet the requirements of being between 0.0 and 1.0, then it is set to None
-
- # Check to make sure there is a valid floating point number between
- # parentheses of the filename
- try:
- self.threshold = float(self.filename.split('(', 1)[1].split(')')[0])
+ if threshold_for_all_images is not None:
+ self.threshold = threshold_for_all_images
+ else:
+ # Retrieve the threshold from the filename, if there is no threshold or the threshold
+ # doesn't meet the requirements of being between 0.0 and 1.0, then it is set to None
- # Check to make sure if it is a valid threshold
- if (self.threshold > 1.0 or self.threshold < 0.0):
- raise ValueError
- except:
- self.threshold = None
+ # Check to make sure there is a valid floating point number between
+ # parentheses of the filename
+ try:
+ self.threshold = float(self.filename.split('(', 1)[1].split(')')[0])
+ # Check to make sure if it is a valid threshold
+ if (self.threshold > 1.0 or self.threshold < 0.0):
+ raise ValueError
+ except:
+ self.threshold = None
- # Retrieve the pause time from the filename, if there is no pause time or the pause time
- # isn't a valid number, then it is set to None
+ if pause_for_all_images is not None:
+ self.pause = pause_for_all_images
+ else:
+ # Retrieve the pause time from the filename, if there is no pause time or the pause time
+ # isn't a valid number, then it is set to None
- # Check to make sure there is a valid pause time between brackets
- # of the filename
- try:
- self.pause = float(self.filename.split('[', 1)[1].split(']')[0])
+ # Check to make sure there is a valid pause time between brackets
+ # of the filename
+ try:
+ self.pause = float(self.filename.split('[', 1)[1].split(']')[0])
- # Pause times should always be positive or zero
- if (self.pause < 0.0):
- raise ValueError
- except:
- self.pause = None
+ # Pause times should always be positive or zero
+ if (self.pause < 0.0):
+ raise ValueError
+ except:
+ self.pause = None
# Retrieve the delay time from the filename, if there is no delay time or the delay time
From 624960fe969c65bc16f25e756867a52512dd615d Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Thu, 23 Apr 2020 17:20:56 +0200
Subject: [PATCH 12/19] Fix undoing/skipping
---
src/AutoSplit.py | 44 +++++++++++++++++---------------------------
1 file changed, 17 insertions(+), 27 deletions(-)
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index a7a3485c..c547ccc4 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -693,9 +693,7 @@ def undoSplit(self):
else:
self.split_image_index = self.split_images[self.split_image_index].undo_image_index
- self.updateSplitImage()
-
- return
+ self.split_image_index_changed = True
# skip split button and hotkey connect to here
def skipSplit(self):
@@ -708,9 +706,7 @@ def skipSplit(self):
else:
self.split_image_index = self.split_images[self.split_image_index].skip_image_index
- self.updateSplitImage()
-
- return
+ self.split_image_index_changed = True
# reset button and hotkey connects here.
def reset(self):
@@ -857,7 +853,6 @@ def autoSplitter(self):
return
# Construct groups of splits
- previous_group_start = None
previous_group_undo = None
current_group_start = 0
current_group_undo = 0
@@ -871,12 +866,8 @@ def autoSplitter(self):
if image.flags & flags == 0:
for image in self.split_images[current_group_start : i + 1]:
image.undo_image_index = previous_group_undo
+ image.skip_image_index = i + 1
- if previous_group_start is not None:
- for image in self.split_images[previous_group_start : current_group_start]:
- image.skip_image_index = i + 1
-
- previous_group_start = current_group_start
previous_group_undo = current_group_undo
current_group_start = i + 1
current_group_undo = current_group_start
@@ -908,22 +899,24 @@ def autoSplitter(self):
# First while loop: stays in this loop until all of the split images have been split
while self.split_image_index < self.number_of_split_images:
- if self.split_image_index_changed:
- # Construct list of images that should be compared
- self.current_split_images = []
- for image in self.split_images[self.split_image_index :]:
- image.get_image(self.RESIZE_WIDTH, self.RESIZE_HEIGHT)
- self.current_split_images.append(image)
- if image.flags & 0x10 == 0:
- break
-
- self.updateSplitImage(self.current_split_images[0])
- self.highest_similarity = 0.001
-
# second while loop: stays in this loop until similarity threshold is met
# skip loop if we just finished waiting for the split delay and need to press the split key!
start = time.time()
while True:
+
+ if self.split_image_index_changed:
+ # Construct list of images that should be compared
+ self.current_split_images = []
+ for image in self.split_images[self.split_image_index :]:
+ image.get_image(self.RESIZE_WIDTH, self.RESIZE_HEIGHT)
+ self.current_split_images.append(image)
+ if image.flags & 0x10 == 0:
+ break
+
+ self.updateSplitImage(self.current_split_images[0])
+ self.highest_similarity = 0.001
+ self.split_image_index_changed = False
+
# reset if the set screen region window was closed
if win32gui.GetWindowText(self.hwnd) == '':
self.reset()
@@ -1061,7 +1054,6 @@ def autoSplitter(self):
# Increase loop number if needed, set to 1 if it was the last loop
if self.loop_number < self.successful_split_image.loop:
self.loop_number += 1
- self.split_image_index_changed = False
else:
self.loop_number = 1
self.split_image_index_changed = True
@@ -1209,8 +1201,6 @@ def updateSplitImage(self, split_image):
self.updateCurrentSplitImage.emit(qImg)
self.currentsplitimagefileLabel.setText(split_image.filename)
- self.current_split_images[0].get_image(self.RESIZE_WIDTH, self.RESIZE_HEIGHT)
-
# Error messages
def splitImageDirectoryError(self):
From 7ca783d32721b7dbf67c189250ca9ae2c13e0d74 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Wed, 6 May 2020 22:04:22 +0200
Subject: [PATCH 13/19] Add start image and LiveSplit integration
---
requirements.txt | 4 +-
res/design.ui | 116 +++-
src/AutoSplit.py | 1354 +++++++++++++---------------------------
src/about.py | 8 +-
src/capture_windows.py | 38 --
src/design.py | 57 +-
src/errors.py | 46 ++
src/hotkeys.py | 108 ++++
src/screen_region.py | 326 ++++++++++
src/settings_file.py | 98 +++
src/split_parser.py | 50 +-
11 files changed, 1210 insertions(+), 995 deletions(-)
delete mode 100644 src/capture_windows.py
create mode 100644 src/errors.py
create mode 100644 src/hotkeys.py
create mode 100644 src/screen_region.py
create mode 100644 src/settings_file.py
diff --git a/requirements.txt b/requirements.txt
index aef025ab..be51491f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,13 +4,13 @@
#
# Usage: pip install -r requirements.txt
#
-# Building AutoSplit with PyInstaller: pyinstaller -w -F src\AutoSplit.py
+# Creating AutoSplit.exe with PyInstaller: pyinstaller -w -F src\AutoSplit.py
#
# If you are on a 32-bit computer, replace the "_amd64" in the 2nd URL with "32".
# You can find other wheels here: https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyqt4
https://download.lfd.uci.edu/pythonlibs/s2jqpv5t/PyQt4-4.11.4-cp37-cp37m-win_amd64.whl
#
-# Comment this out if you don't want to build AutoSplit:
+# Comment this out if you don't want to create AutoSplit.exe:
PyInstaller
#
# Other dependencies:
diff --git a/res/design.ui b/res/design.ui
index 56e88d51..44db6124 100644
--- a/res/design.ui
+++ b/res/design.ui
@@ -7,7 +7,7 @@
0
0
612
- 490
+ 523
@@ -19,13 +19,13 @@
612
- 520
+ 523
612
- 520
+ 523
@@ -33,7 +33,7 @@
- ../../VideoAutoSplitter/icon.ico../../VideoAutoSplitter/icon.ico
+ ../icon.ico../icon.ico
@@ -104,7 +104,7 @@
125
- 253
+ 256
121
17
@@ -119,6 +119,28 @@
false
+
+
+ true
+
+
+
+ 500
+ 320
+ 121
+ 17
+
+
+
+ Loop Split Images
+
+
+ false
+
+
+ false
+
+
@@ -170,8 +192,8 @@
- 501
- 455
+ 500
+ 460
101
31
@@ -186,8 +208,8 @@
- 501
- 415
+ 500
+ 420
101
31
@@ -199,11 +221,27 @@
Reset
+
+
+
+ 500
+ 375
+ 101
+ 31
+
+
+
+ Qt::NoFocus
+
+
+ Pause Comparison
+
+
410
- 251
+ 253
61
21
@@ -219,7 +257,7 @@
490
- 251
+ 253
61
21
@@ -369,7 +407,7 @@
Reset
-
+
249
@@ -798,6 +836,19 @@
Current Split Image
+
+
+
+ 362
+ 251
+ 108
+ 20
+
+
+
+ Image Loop #:
+
+
@@ -917,7 +968,7 @@
0
- 30.000000000000000
+ 1.000000000000000
5000.000000000000000
@@ -933,7 +984,7 @@
362
- 271
+ 276
237
20
@@ -949,7 +1000,7 @@
250
- 251
+ 254
91
21
@@ -1188,6 +1239,35 @@
Select Window
+
+
+
+ 10
+ 292
+ 130
+ 16
+
+
+
+ Start image: not found
+
+
+
+
+
+ 142
+ 290
+ 83
+ 21
+
+
+
+ Qt::NoFocus
+
+
+ Reload
+
+
splitimagefolderLabel
splitimagefolderLineEdit
browseButton
@@ -1198,6 +1278,7 @@
similaritythresholdDoubleSpinBox
startautosplitterButton
resetButton
+ pauseComparisonButton
undosplitButton
skipsplitButton
pauseLabel
@@ -1209,7 +1290,7 @@
highestsimilarityLabel
splitLabel
resetLabel
- skiptsplitLabel
+ skipsplitLabel
undosplitLabel
pausehotkeyLabel
splitLineEdit
@@ -1234,6 +1315,7 @@
timerglobalhotkeysLabel
line_right
currentsplitimageLabel
+ imageloopLabel
liveImage
currentSplitImage
widthLabel
@@ -1257,6 +1339,8 @@
alignregionButton
groupDummySplitsCheckBox
selectwindowButton
+ startimageLabel
+ startimagereloadButton
", None))
diff --git a/src/capture_windows.py b/src/capture_windows.py
deleted file mode 100644
index 0778e21b..00000000
--- a/src/capture_windows.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import numpy
-import cv2
-
-import win32ui
-import win32gui
-import win32con
-
-def capture_region(hwnd, rect):
- """
- Captures an image of the region for a window matching the given
- parameters of the bounding box
-
- @param hwnd: Handle to the window being captured
- @param rect: The coordinates of the region
- @return: The image of the region in the window in BGRA format
- """
-
- width = rect.right - rect.left
- height = rect.bottom - rect.top
-
- wDC = win32gui.GetWindowDC(hwnd)
- dcObj = win32ui.CreateDCFromHandle(wDC)
- cDC = dcObj.CreateCompatibleDC()
- bmp = win32ui.CreateBitmap()
- bmp.CreateCompatibleBitmap(dcObj, width, height)
- cDC.SelectObject(bmp)
- cDC.BitBlt((0, 0), (width, height), dcObj, (rect.left, rect.top), win32con.SRCCOPY)
-
- img = bmp.GetBitmapBits(True)
- img = numpy.frombuffer(img, dtype='uint8')
- img.shape = (height, width, 4)
-
- dcObj.DeleteDC()
- cDC.DeleteDC()
- win32gui.ReleaseDC(hwnd, wDC)
- win32gui.DeleteObject(bmp.GetHandle())
-
- return img
diff --git a/src/design.py b/src/design.py
index 38df3d7a..d515b1f4 100644
--- a/src/design.py
+++ b/src/design.py
@@ -25,16 +25,16 @@ def _translate(context, text, disambig):
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
- MainWindow.resize(612, 520)
+ MainWindow.resize(612, 523)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
MainWindow.setSizePolicy(sizePolicy)
- MainWindow.setMinimumSize(QtCore.QSize(612, 520))
- MainWindow.setMaximumSize(QtCore.QSize(612, 520))
+ MainWindow.setMinimumSize(QtCore.QSize(612, 523))
+ MainWindow.setMaximumSize(QtCore.QSize(612, 523))
icon = QtGui.QIcon()
- icon.addPixmap(QtGui.QPixmap(_fromUtf8("../../VideoAutoSplitter/icon.ico")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ icon.addPixmap(QtGui.QPixmap(_fromUtf8("../icon.ico")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
MainWindow.setWhatsThis(_fromUtf8(""))
MainWindow.setLayoutDirection(QtCore.Qt.LeftToRight)
@@ -56,7 +56,7 @@ def setupUi(self, MainWindow):
self.xLabel.setObjectName(_fromUtf8("xLabel"))
self.liveimageCheckBox = QtGui.QCheckBox(self.centralwidget)
self.liveimageCheckBox.setEnabled(True)
- self.liveimageCheckBox.setGeometry(QtCore.QRect(125, 253, 121, 17))
+ self.liveimageCheckBox.setGeometry(QtCore.QRect(125, 256, 121, 17))
self.liveimageCheckBox.setChecked(True)
self.liveimageCheckBox.setTristate(False)
self.liveimageCheckBox.setObjectName(_fromUtf8("liveimageCheckBox"))
@@ -80,23 +80,27 @@ def setupUi(self, MainWindow):
self.similaritythresholdDoubleSpinBox.setProperty("value", 0.9)
self.similaritythresholdDoubleSpinBox.setObjectName(_fromUtf8("similaritythresholdDoubleSpinBox"))
self.startautosplitterButton = QtGui.QPushButton(self.centralwidget)
- self.startautosplitterButton.setGeometry(QtCore.QRect(501, 455, 101, 31))
+ self.startautosplitterButton.setGeometry(QtCore.QRect(500, 460, 101, 31))
self.startautosplitterButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.startautosplitterButton.setObjectName(_fromUtf8("startautosplitterButton"))
self.resetButton = QtGui.QPushButton(self.centralwidget)
- self.resetButton.setGeometry(QtCore.QRect(501, 415, 101, 31))
+ self.resetButton.setGeometry(QtCore.QRect(500, 420, 101, 31))
self.resetButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.resetButton.setObjectName(_fromUtf8("resetButton"))
+ self.pauseComparisonButton = QtGui.QPushButton(self.centralwidget)
+ self.pauseComparisonButton.setGeometry(QtCore.QRect(500, 380, 101, 31))
+ self.pauseComparisonButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.pauseComparisonButton.setObjectName(_fromUtf8("pauseComparisonButton"))
self.reloadsettingsButton = QtGui.QPushButton(self.centralwidget)
self.reloadsettingsButton.setGeometry(QtCore.QRect(500, 350, 101, 21))
self.reloadsettingsButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.reloadsettingsButton.setObjectName(_fromUtf8("takescreenshotButton"))
self.undosplitButton = QtGui.QPushButton(self.centralwidget)
- self.undosplitButton.setGeometry(QtCore.QRect(477, 251, 61, 21))
+ self.undosplitButton.setGeometry(QtCore.QRect(477, 254, 61, 21))
self.undosplitButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.undosplitButton.setObjectName(_fromUtf8("undosplitButton"))
self.skipsplitButton = QtGui.QPushButton(self.centralwidget)
- self.skipsplitButton.setGeometry(QtCore.QRect(541, 251, 61, 21))
+ self.skipsplitButton.setGeometry(QtCore.QRect(541, 254, 61, 21))
self.skipsplitButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.skipsplitButton.setObjectName(_fromUtf8("skipsplitButton"))
self.pauseLabel = QtGui.QLabel(self.centralwidget)
@@ -135,9 +139,9 @@ def setupUi(self, MainWindow):
self.resetLabel = QtGui.QLabel(self.centralwidget)
self.resetLabel.setGeometry(QtCore.QRect(249, 350, 61, 16))
self.resetLabel.setObjectName(_fromUtf8("resetLabel"))
- self.skiptsplitLabel = QtGui.QLabel(self.centralwidget)
- self.skiptsplitLabel.setGeometry(QtCore.QRect(249, 380, 50, 16))
- self.skiptsplitLabel.setObjectName(_fromUtf8("skiptsplitLabel"))
+ self.skipsplitLabel = QtGui.QLabel(self.centralwidget)
+ self.skipsplitLabel.setGeometry(QtCore.QRect(249, 380, 50, 16))
+ self.skipsplitLabel.setObjectName(_fromUtf8("skipsplitLabel"))
self.undosplitLabel = QtGui.QLabel(self.centralwidget)
self.undosplitLabel.setGeometry(QtCore.QRect(249, 410, 61, 16))
self.undosplitLabel.setObjectName(_fromUtf8("undosplitLabel"))
@@ -243,7 +247,7 @@ def setupUi(self, MainWindow):
self.timerglobalhotkeysLabel.setGeometry(QtCore.QRect(313, 296, 101, 20))
self.timerglobalhotkeysLabel.setObjectName(_fromUtf8("timerglobalhotkeysLabel"))
self.line_right = QtGui.QFrame(self.centralwidget)
- self.line_right.setGeometry(QtCore.QRect(489, 296, 2, 163))
+ self.line_right.setGeometry(QtCore.QRect(489, 296, 2, 193))
self.line_right.setFrameShadow(QtGui.QFrame.Plain)
self.line_right.setLineWidth(1)
self.line_right.setFrameShape(QtGui.QFrame.VLine)
@@ -262,7 +266,7 @@ def setupUi(self, MainWindow):
self.currentsplitimageLabel.setObjectName(_fromUtf8("currentsplitimageLabel"))
self.imageloopLabel = QtGui.QLabel(self.centralwidget)
self.imageloopLabel.setGeometry(QtCore.QRect(362, 251, 108, 20))
- self.imageloopLabel.setObjectName(_fromUtf8("Image Loop #:"))
+ self.imageloopLabel.setObjectName(_fromUtf8("imageloopLabel"))
self.widthLabel = QtGui.QLabel(self.centralwidget)
self.widthLabel.setGeometry(QtCore.QRect(14, 177, 31, 16))
self.widthLabel.setObjectName(_fromUtf8("widthLabel"))
@@ -295,18 +299,18 @@ def setupUi(self, MainWindow):
self.fpslimitSpinBox.setGeometry(QtCore.QRect(62, 248, 44, 22))
self.fpslimitSpinBox.setPrefix(_fromUtf8(""))
self.fpslimitSpinBox.setDecimals(0)
- self.fpslimitSpinBox.setMinimum(30.0)
+ self.fpslimitSpinBox.setMinimum(1.0)
self.fpslimitSpinBox.setMaximum(5000.0)
self.fpslimitSpinBox.setSingleStep(1.0)
self.fpslimitSpinBox.setProperty("value", 60.0)
self.fpslimitSpinBox.setObjectName(_fromUtf8("fpslimitSpinBox"))
self.currentsplitimagefileLabel = QtGui.QLabel(self.centralwidget)
- self.currentsplitimagefileLabel.setGeometry(QtCore.QRect(362, 271, 237, 20))
+ self.currentsplitimagefileLabel.setGeometry(QtCore.QRect(362, 276, 237, 20))
self.currentsplitimagefileLabel.setText(_fromUtf8(""))
self.currentsplitimagefileLabel.setAlignment(QtCore.Qt.AlignCenter)
self.currentsplitimagefileLabel.setObjectName(_fromUtf8("currentsplitimagefileLabel"))
self.takescreenshotButton = QtGui.QPushButton(self.centralwidget)
- self.takescreenshotButton.setGeometry(QtCore.QRect(250, 251, 91, 21))
+ self.takescreenshotButton.setGeometry(QtCore.QRect(250, 254, 91, 21))
self.takescreenshotButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.takescreenshotButton.setObjectName(_fromUtf8("takescreenshotButton"))
self.xSpinBox = QtGui.QSpinBox(self.centralwidget)
@@ -372,6 +376,13 @@ def setupUi(self, MainWindow):
self.selectwindowButton.setGeometry(QtCore.QRect(5, 117, 101, 23))
self.selectwindowButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.selectwindowButton.setObjectName(_fromUtf8("selectwindowButton"))
+ self.startimageLabel = QtGui.QLabel(self.centralwidget)
+ self.startimageLabel.setGeometry(QtCore.QRect(10, 292, 130, 16))
+ self.startimageLabel.setObjectName(_fromUtf8("startimageLabel"))
+ self.startimagereloadButton = QtGui.QPushButton(self.centralwidget)
+ self.startimagereloadButton.setGeometry(QtCore.QRect(142, 290, 83, 21))
+ self.startimagereloadButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.startimagereloadButton.setObjectName(_fromUtf8("startimagereloadButton"))
self.splitimagefolderLabel.raise_()
self.splitimagefolderLineEdit.raise_()
self.browseButton.raise_()
@@ -383,6 +394,7 @@ def setupUi(self, MainWindow):
self.similaritythresholdDoubleSpinBox.raise_()
self.startautosplitterButton.raise_()
self.resetButton.raise_()
+ self.pauseComparisonButton.raise_()
self.reloadsettingsButton.raise_()
self.undosplitButton.raise_()
self.skipsplitButton.raise_()
@@ -395,7 +407,7 @@ def setupUi(self, MainWindow):
self.highestsimilarityLabel.raise_()
self.splitLabel.raise_()
self.resetLabel.raise_()
- self.skiptsplitLabel.raise_()
+ self.skipsplitLabel.raise_()
self.undosplitLabel.raise_()
self.pausehotkeyLabel.raise_()
self.splitLineEdit.raise_()
@@ -444,6 +456,8 @@ def setupUi(self, MainWindow):
self.alignregionButton.raise_()
self.groupDummySplitsCheckBox.raise_()
self.selectwindowButton.raise_()
+ self.startimageLabel.raise_()
+ self.startimagereloadButton.raise_()
MainWindow.setCentralWidget(self.centralwidget)
self.menuBar = QtGui.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 612, 21))
@@ -493,6 +507,7 @@ def retranslateUi(self, MainWindow):
self.similaritythresholdLabel.setText(_translate("MainWindow", "Similarity threshold", None))
self.startautosplitterButton.setText(_translate("MainWindow", "Start Auto Splitter", None))
self.resetButton.setText(_translate("MainWindow", "Reset", None))
+ self.pauseComparisonButton.setText(_translate("MainWindow", "Pause Comparison", None))
self.reloadsettingsButton.setText(_translate("MainWindow", "Reload Settings", None))
self.undosplitButton.setText(_translate("MainWindow", "Undo Split", None))
self.skipsplitButton.setText(_translate("MainWindow", "Skip Split", None))
@@ -503,7 +518,7 @@ def retranslateUi(self, MainWindow):
self.showhighestsimilarityCheckBox.setText(_translate("MainWindow", "Show highest similarity", None))
self.splitLabel.setText(_translate("MainWindow", "Start / Split", None))
self.resetLabel.setText(_translate("MainWindow", "Reset", None))
- self.skiptsplitLabel.setText(_translate("MainWindow", "Skip Split", None))
+ self.skipsplitLabel.setText(_translate("MainWindow", "Skip Split", None))
self.undosplitLabel.setText(_translate("MainWindow", "Undo Split", None))
self.pausehotkeyLabel.setText(_translate("MainWindow", "Pause", None))
self.setsplithotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
@@ -513,7 +528,7 @@ def retranslateUi(self, MainWindow):
self.setpausehotkeyButton.setText(_translate("MainWindow", "Set Hotkey", None))
self.timerglobalhotkeysLabel.setText(_translate("MainWindow", "Timer Global Hotkeys", None))
self.currentsplitimageLabel.setText(_translate("MainWindow", "Current Split Image", None))
- self.imageloopLabel.setText(_translate("MainWindow", "Image Loop #:", None))
+ self.imageloopLabel.setText(_translate("MainWindow", "", None))
self.widthLabel.setText(_translate("MainWindow", "Width", None))
self.heightLabel.setText(_translate("MainWindow", "Height", None))
self.captureregionLabel.setText(_translate("MainWindow", "Capture Region", None))
@@ -529,6 +544,8 @@ def retranslateUi(self, MainWindow):
self.alignregionButton.setText(_translate("MainWindow", "Align Region", None))
self.groupDummySplitsCheckBox.setText(_translate("MainWindow", "Group dummy splits when undoing/skipping", None))
self.selectwindowButton.setText(_translate("MainWindow", "Select Window", None))
+ self.startimageLabel.setText(_translate("MainWindow", "Start image: not found", None))
+ self.startimagereloadButton.setText(_translate("MainWindow", "Reload", None))
self.menuHelp.setTitle(_translate("MainWindow", "Help", None))
self.actionView_Help.setText(_translate("MainWindow", "View Help", None))
self.actionAbout.setText(_translate("MainWindow", "About", None))
diff --git a/src/errors.py b/src/errors.py
new file mode 100644
index 00000000..d38c07a3
--- /dev/null
+++ b/src/errors.py
@@ -0,0 +1,46 @@
+# Error messages
+
+# The reason why the errors don't end with "_ERROR" is
+# because the file itself is already called "errors"
+
+SPLIT_IMAGES_DIRECTORY = "No valid split image folder is selected."
+
+NO_SPLIT_IMAGES = "Your split image folder does not contain any splits."
+
+IMAGE_TYPE = '"%s" is not a valid image file or the full image file path contains a special character.'
+
+REGION = "No region is selected. Select a region or reload settings while region window is open."
+
+REGION_SIZE = "Width and height cannot be 0. Please select a larger region."
+
+SPLIT_HOTKEY = "No split hotkey has been set."
+
+CUSTOM_THRESHOLD = '"%s" does not have a valid custom threshold.'
+
+CUSTOM_PAUSE = '"%s" does not have a valid custom pause time.'
+
+ALPHA_CHANNEL = '"%s" is marked with mask flag but it does not have transparency.'
+
+ALIGN_REGION_IMAGE_TYPE = "File is not a valid image file."
+
+ALIGNMENT_NOT_MATCHED = "No area in capture region matched reference image. Alignment failed."
+
+MULTIPLE_IMAGES_WITH_KEYWORD = 'Only one image with the keyword "%s" is allowed.'
+
+FORCED_THRESHOLD = 'The image with the keyword "%s" must have a custom threshold. Please set one and check that it is valid.'
+
+RESET_HOTKEY = "Your split image folder contains a reset image, but no reset hotkey is set."
+
+PAUSE_HOTKEY = "Your split image folder contains an image marked with pause flag, but no pause hotkey is set."
+
+SETTINGS_NOT_FOUND = "No settings file found. The settings file is saved when the program is closed."
+
+INVALID_SETTINGS = "The settings file is invalid."
+
+LAST_IMAGE_HAS_INCLUDE_NEXT_FLAG = "The last split image in the image folder is marked with include next flag."
+
+IMAGE_HAS_INCLUDE_NEXT_FLAG = 'The image with the keyword "%s" is marked with include next flag.'
+
+INCLUDE_NEXT_FLAG_WITH_LOOP = "Your split image folder contains an image marked with include next flag followed by an image with a loop value greater than 1."
+
+NO_START_IMAGE = 'Your split image folder does not contain an image with the keyword "start".'
diff --git a/src/hotkeys.py b/src/hotkeys.py
new file mode 100644
index 00000000..dfba68c1
--- /dev/null
+++ b/src/hotkeys.py
@@ -0,0 +1,108 @@
+import keyboard
+from threading import Thread
+
+def newHotkey (self, hotkey_name, lineEdit):
+ # New thread points to waitForKeyPress. This thread is needed or GUI will freeze
+ # while the program waits for user input on the hotkey
+ def waitForKeyPress():
+ # Disable some buttons
+ self.startautosplitterButton.setEnabled(False)
+ self.setsplithotkeyButton.setEnabled(False)
+ self.setresethotkeyButton.setEnabled(False)
+ self.setskipsplithotkeyButton.setEnabled(False)
+ self.setundosplithotkeyButton.setEnabled(False)
+ self.setpausehotkeyButton.setEnabled(False)
+ self.reloadsettingsButton.setEnabled(False)
+
+ self.hotkeys[hotkey_name].detectUserKeyPress([self.splitLineEdit,
+ self.resetLineEdit,
+ self.skipsplitLineEdit,
+ self.undosplitLineEdit,
+ self.pauseLineEdit])
+
+ if self.hotkeys[hotkey_name].successful:
+ # Set the text into the LineEdit
+ self.updateHotkeyLineEdits()
+ else:
+ self.hotkeys[hotkey_name].key = lineEdit.text()
+ if self.hotkeys[hotkey_name].key_press_function is not None:
+ self.hotkeys[hotkey_name].hotkey = keyboard.add_hotkey(self.hotkeys[hotkey_name].key, self.hotkeys[hotkey_name].key_press_function)
+
+ # Re-enable some buttons and change some text in GUI.
+ self.setsplithotkeyButton.setText('Set Hotkey')
+ self.setresethotkeyButton.setText('Set Hotkey')
+ self.setskipsplithotkeyButton.setText('Set Hotkey')
+ self.setundosplithotkeyButton.setText('Set Hotkey')
+ self.setpausehotkeyButton.setText('Set Hotkey')
+ self.startautosplitterButton.setEnabled(True)
+ self.setsplithotkeyButton.setEnabled(True)
+ self.setresethotkeyButton.setEnabled(True)
+ self.setskipsplithotkeyButton.setEnabled(True)
+ self.setundosplithotkeyButton.setEnabled(True)
+ self.setpausehotkeyButton.setEnabled(True)
+ self.reloadsettingsButton.setEnabled(True)
+
+ Thread(target = waitForKeyPress).start()
+
+def updateHotkeyLineEdits(self):
+ self.splitLineEdit.setText(str(self.hotkeys['split'].key))
+ self.resetLineEdit.setText(str(self.hotkeys['reset'].key))
+ self.skipsplitLineEdit.setText(str(self.hotkeys['skip'].key))
+ self.undosplitLineEdit.setText(str(self.hotkeys['undo'].key))
+ self.pauseLineEdit.setText(str(self.hotkeys['pause'].key))
+
+class Hotkey:
+ def __init__(self, key_press_function = None):
+ self.key_press_function = key_press_function
+
+ def detectUserKeyPress(self, lineEdits):
+ self.successful = False
+
+ # Try to remove the previously set hotkey if there is one
+ try:
+ keyboard.remove_hotkey(self.hotkey)
+ except (AttributeError, KeyError, NameError):
+ pass
+
+ # wait until user presses the hotkey, then keyboard module reads the input
+ self.key = keyboard.read_hotkey(False)
+
+ # If the key the user presses is equal to itself or another hotkey already set,
+ # this causes issues. So here, it catches that, and will make no changes to the hotkey
+ try:
+ for lineEdit in lineEdits:
+ if lineEdit.text() == self.key:
+ return
+ except AttributeError:
+ return
+
+ # The keyboard module allows you to hit multiple keys for a hotkey. They are joined
+ # together by a '+'. If user hits two keys at the same time, make no changes to the
+ # hotkey. A try and except is needed if a hotkey hasn't been set yet. I'm not
+ # allowing for these multiple-key hotkeys because it can cause crashes, and
+ # not many people are going to really use or need this
+ try:
+ if '+' in self.key:
+ return
+ except AttributeError:
+ return
+
+ if self.key_press_function is not None:
+ # Add the key as a hotkey
+ self.hotkey = keyboard.add_hotkey(self.key, self.key_press_function)
+
+ self.successful = True
+
+ def setKeyAndHotkey(self, key):
+ try:
+ try:
+ keyboard.remove_hotkey(self.hotkey)
+ except AttributeError:
+ pass
+
+ self.key = key
+ self.hotkey = keyboard.add_hotkey(key, self.key_press_function)
+
+ # Pass if the key is an empty string (hotkey was never set)
+ except ValueError:
+ pass
\ No newline at end of file
diff --git a/src/screen_region.py b/src/screen_region.py
new file mode 100644
index 00000000..f3303a95
--- /dev/null
+++ b/src/screen_region.py
@@ -0,0 +1,326 @@
+from PyQt4 import QtGui, QtCore, QtTest
+import ctypes
+import ctypes.wintypes
+import win32ui
+import win32gui
+import win32con
+import cv2
+import numpy as np
+
+def selectRegion(self):
+ # Create a screen selector widget
+ selector = SelectRegionWidget()
+
+ # Need to wait until the user has selected a region using the widget before moving on with
+ # selecting the window settings
+ while selector.height == -1 and selector.width == -1:
+ QtTest.QTest.qWait(1)
+
+ # return an error if width or height are zero.
+ if selector.width == 0 or selector.height == 0:
+ self.showBox(errors.REGION_SIZE)
+ return
+
+ # Width and Height of the spinBox
+ self.widthSpinBox.setValue(selector.width)
+ self.heightSpinBox.setValue(selector.height)
+
+ # Grab the window handle from the coordinates selected by the widget
+ self.hwnd = win32gui.WindowFromPoint((selector.left, selector.top))
+ # Want to pull the parent window from the window handle
+ # By using GetAncestor we are able to get the parent window instead
+ # of the owner window.
+ GetAncestor = ctypes.windll.user32.GetAncestor
+ GA_ROOT = 2
+
+ while win32gui.IsChild(win32gui.GetParent(self.hwnd), self.hwnd):
+ self.hwnd = GetAncestor(self.hwnd, GA_ROOT)
+
+ if self.hwnd != 0 or win32gui.GetWindowText(self.hwnd) != '':
+ self.hwnd_title = win32gui.GetWindowText(self.hwnd)
+
+ # Convert the Desktop Coordinates to Window Coordinates
+ DwmGetWindowAttribute = ctypes.windll.dwmapi.DwmGetWindowAttribute
+ DWMWA_EXTENDED_FRAME_BOUNDS = 9
+
+ # Pull the window's coordinates relative to desktop into rect
+ DwmGetWindowAttribute(self.hwnd,
+ ctypes.wintypes.DWORD(DWMWA_EXTENDED_FRAME_BOUNDS),
+ ctypes.byref(self.rect),
+ ctypes.sizeof(self.rect)
+ )
+
+ # On Windows 10 the windows have offsets due to invisible pixels not accounted for in DwmGetWindowAttribute
+ # TODO: Since this occurs on Windows 10, is DwmGetWindowAttribute even required over GetWindowRect alone?
+ # Research needs to be done to figure out why it was used it over win32gui in the first place...
+ # I have a feeling it was due to a misunderstanding and not getting the correct parent window before.
+ offset_left = self.rect.left - win32gui.GetWindowRect(self.hwnd)[0]
+ offset_top = self.rect.top - win32gui.GetWindowRect(self.hwnd)[1]
+
+ self.rect.left = selector.left - (self.rect.left - offset_left)
+ self.rect.top = selector.top - (self.rect.top - offset_top)
+ self.rect.right = self.rect.left + selector.width
+ self.rect.bottom = self.rect.top + selector.height
+
+ self.xSpinBox.setValue(self.rect.left)
+ self.ySpinBox.setValue(self.rect.top)
+
+ # Delete that widget since it is no longer used from here on out
+ del selector
+
+ # check if live image needs to be turned on or just set a single image
+ self.checkLiveImage()
+
+def alignRegion(self):
+ # check to see if a region has been set
+ if self.hwnd == 0 or win32gui.GetWindowText(self.hwnd) == '':
+ self.showBox(errors.REGION)
+ return
+ # This is the image used for aligning the capture region
+ # to the best fit for the user.
+ template_filename = str(QtGui.QFileDialog.getOpenFileName(self, "Select Reference Image", "",
+ "Image Files (*.png *.jpg *.jpeg *.jpe *.jp2 *.bmp *.tiff *.tif *.dib *.webp *.pbm *.pgm *.ppm *.sr *.ras)"))
+
+ # return if the user presses cancel
+ if template_filename == '':
+ return
+
+ template = cv2.imread(template_filename, cv2.IMREAD_COLOR)
+
+ # shouldn't need this, but just for caution, throw a type error if file is not a valid image file
+ if template is None:
+ self.showBox(errors.ALIGN_REGION_IMAGE_TYPE)
+ return
+
+ # Obtaining the capture of a region which contains the
+ # subregion being searched for to align the image.
+ capture = self.captureRegion(self.hwnd, self.rect)
+ capture = cv2.cvtColor(capture, cv2.COLOR_BGRA2BGR)
+
+ # Obtain the best matching point for the template within the
+ # capture. This assumes that the template is actually smaller
+ # than the dimensions of the capture. Since we are using SQDIFF
+ # the best match will be the min_val which is located at min_loc.
+ # The best match found in the image, set everything to 0 by default
+ # so that way the first match will overwrite these values
+ best_match = 0.0
+ best_height = 0
+ best_width = 0
+ best_loc = (0, 0)
+
+ # This tests 50 images scaled from 20% to 300% of the original template size
+ for scale in np.linspace(0.2, 3, num=56):
+ width = int(template.shape[1] * scale)
+ height = int(template.shape[0] * scale)
+
+ # The template can not be larger than the capture
+ if width > capture.shape[1] or height > capture.shape[0]:
+ continue
+
+ resized = cv2.resize(template, (width, height))
+
+ result = cv2.matchTemplate(capture, resized, cv2.TM_SQDIFF)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+
+ # The maximum value for SQ_DIFF is dependent on the size of the template
+ # we need this value to normalize it from 0.0 to 1.0
+ max_error = resized.size * 255 * 255
+ similarity = 1 - (min_val / max_error)
+
+ # Check if the similarity was good enough to get alignment
+ if similarity > best_match:
+ best_match = similarity
+ best_width = width
+ best_height = height
+ best_loc = min_loc
+
+ # Go ahead and check if this satisfies our requirement before setting the region
+ # We don't want a low similarity image to be aligned.
+ if best_match < 0.9:
+ self.showBox(errors.ALIGNMENT_NOT_MATCHED)
+ return
+
+ # The new region can be defined by using the min_loc point and the
+ # height and width of the template.
+ self.rect.left = self.rect.left + best_loc[0]
+ self.rect.top = self.rect.top + best_loc[1]
+ self.rect.right = self.rect.left + best_width
+ self.rect.bottom = self.rect.top + best_height
+
+ self.xSpinBox.setValue(self.rect.left)
+ self.ySpinBox.setValue(self.rect.top)
+ self.widthSpinBox.setValue(best_width)
+ self.heightSpinBox.setValue(best_height)
+
+def selectWindow(self):
+ # Create a screen selector widget
+ selector = SelectWindowWidget()
+
+ # Need to wait until the user has selected a region using the widget before moving on with
+ # selecting the window settings
+ while selector.x == -1 and selector.y == -1:
+ QtTest.QTest.qWait(1)
+
+ # Grab the window handle from the coordinates selected by the widget
+ self.hwnd = None
+ self.hwnd = win32gui.WindowFromPoint((selector.x, selector.y))
+
+ if self.hwnd is None:
+ return
+
+ del selector
+
+ # Want to pull the parent window from the window handle
+ # By using GetAncestor we are able to get the parent window instead
+ # of the owner window.
+ GetAncestor = ctypes.windll.user32.GetAncestor
+ GA_ROOT = 2
+ while win32gui.IsChild(win32gui.GetParent(self.hwnd), self.hwnd):
+ self.hwnd = GetAncestor(self.hwnd, GA_ROOT)
+
+ if self.hwnd != 0 or win32gui.GetWindowText(self.hwnd) != '':
+ self.hwnd_title = win32gui.GetWindowText(self.hwnd)
+
+ # getting window bounds
+ # on windows there are some invisble pixels that are not accounted for
+ # also the top bar with the window name is not accounted for
+ # I hardcoded the x and y coordinates to fix this
+ # This is not an ideal solution because it assumes every window will have a top bar
+ rect = win32gui.GetClientRect(self.hwnd)
+ self.rect.left = 8
+ self.rect.top = 31
+ self.rect.right = 0 + rect[2]
+ self.rect.bottom = 0 + rect[3]
+
+ self.widthSpinBox.setValue(self.rect.right)
+ self.heightSpinBox.setValue(self.rect.bottom)
+ self.xSpinBox.setValue(self.rect.left)
+ self.ySpinBox.setValue(self.rect.top)
+
+ self.checkLiveImage()
+
+def captureRegion(self, hwnd, rect):
+ """
+ Captures an image of the region for a window matching the given
+ parameters of the bounding box
+
+ @param hwnd: Handle to the window being captured
+ @param rect: The coordinates of the region
+ @return: The image of the region in the window in BGRA format
+ """
+
+ width = rect.right - rect.left
+ height = rect.bottom - rect.top
+
+ try:
+ wDC = win32gui.GetWindowDC(hwnd)
+ dcObj = win32ui.CreateDCFromHandle(wDC)
+ cDC = dcObj.CreateCompatibleDC()
+ bmp = win32ui.CreateBitmap()
+ bmp.CreateCompatibleBitmap(dcObj, width, height)
+ cDC.SelectObject(bmp)
+ cDC.BitBlt((0, 0), (width, height), dcObj, (rect.left, rect.top), win32con.SRCCOPY)
+ except:
+ # Error, the window was probably closed
+ return None
+
+ img = bmp.GetBitmapBits(True)
+ img = np.frombuffer(img, dtype='uint8')
+ img.shape = (height, width, 4)
+
+ dcObj.DeleteDC()
+ cDC.DeleteDC()
+ win32gui.ReleaseDC(hwnd, wDC)
+ win32gui.DeleteObject(bmp.GetHandle())
+
+ return img
+
+# Widget for dragging screen region
+# https://github.com/harupy/snipping-tool
+class SelectRegionWidget(QtGui.QWidget):
+ def __init__(self):
+ super(SelectRegionWidget, self).__init__()
+ user32 = ctypes.windll.user32
+ user32.SetProcessDPIAware()
+
+ # We need to pull the monitor information to correctly draw the geometry covering all portions
+ # of the user's screen. These parameters create the bounding box with left, top, width, and height
+ self.SM_XVIRTUALSCREEN = user32.GetSystemMetrics(76)
+ self.SM_YVIRTUALSCREEN = user32.GetSystemMetrics(77)
+ self.SM_CXVIRTUALSCREEN = user32.GetSystemMetrics(78)
+ self.SM_CYVIRTUALSCREEN = user32.GetSystemMetrics(79)
+
+ self.setGeometry(self.SM_XVIRTUALSCREEN, self.SM_YVIRTUALSCREEN, self.SM_CXVIRTUALSCREEN,
+ self.SM_CYVIRTUALSCREEN)
+ self.setWindowTitle(' ')
+
+ self.height = -1
+ self.width = -1
+
+ self.begin = QtCore.QPoint()
+ self.end = QtCore.QPoint()
+ self.setWindowOpacity(0.5)
+ QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
+ self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
+ self.show()
+
+ def paintEvent(self, event):
+ qp = QtGui.QPainter(self)
+ qp.setPen(QtGui.QPen(QtGui.QColor('red'), 2))
+ qp.setBrush(QtGui.QColor('opaque'))
+ qp.drawRect(QtCore.QRect(self.begin, self.end))
+
+ def mousePressEvent(self, event):
+ self.begin = event.pos()
+ self.end = self.begin
+ self.update()
+
+ def mouseMoveEvent(self, event):
+ self.end = event.pos()
+ self.update()
+
+ def mouseReleaseEvent(self, event):
+ QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
+ self.close()
+
+ # The coordinates are pulled relative to the top left of the set geometry,
+ # so the added virtual screen offsets convert them back to the virtual
+ # screen coordinates
+ self.left = min(self.begin.x(), self.end.x()) + self.SM_XVIRTUALSCREEN
+ self.top = min(self.begin.y(), self.end.y()) + self.SM_YVIRTUALSCREEN
+ self.right = max(self.begin.x(), self.end.x()) + self.SM_XVIRTUALSCREEN
+ self.bottom = max(self.begin.y(), self.end.y()) + self.SM_YVIRTUALSCREEN
+
+ self.height = self.bottom - self.top
+ self.width = self.right - self.left
+
+
+# widget to select a window and obtain its bounds
+class SelectWindowWidget(QtGui.QWidget):
+ def __init__(self):
+ super(SelectWindowWidget, self).__init__()
+ user32 = ctypes.windll.user32
+ user32.SetProcessDPIAware()
+
+ self.x = -1
+ self.y = -1
+
+ # We need to pull the monitor information to correctly draw the geometry covering all portions
+ # of the user's screen. These parameters create the bounding box with left, top, width, and height
+ self.SM_XVIRTUALSCREEN = user32.GetSystemMetrics(76)
+ self.SM_YVIRTUALSCREEN = user32.GetSystemMetrics(77)
+ self.SM_CXVIRTUALSCREEN = user32.GetSystemMetrics(78)
+ self.SM_CYVIRTUALSCREEN = user32.GetSystemMetrics(79)
+
+ self.setGeometry(self.SM_XVIRTUALSCREEN, self.SM_YVIRTUALSCREEN, self.SM_CXVIRTUALSCREEN,
+ self.SM_CYVIRTUALSCREEN)
+ self.setWindowTitle(' ')
+
+ self.setWindowOpacity(0.5)
+ self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
+ self.show()
+
+ def mouseReleaseEvent(self, event):
+ self.close()
+ self.x = event.pos().x()
+ self.y = event.pos().y()
diff --git a/src/settings_file.py b/src/settings_file.py
new file mode 100644
index 00000000..0f7a2cd9
--- /dev/null
+++ b/src/settings_file.py
@@ -0,0 +1,98 @@
+from os import path
+import pickle
+import keyboard
+import win32gui
+
+import errors
+
+def loadSettings(self):
+ try:
+ keys = {}
+
+ with open(path.join(self.file_path, 'settings.pkl'), 'rb') as f:
+ f2 = pickle.load(f)
+
+ if len(f2) < 19:
+ # The settings file might not include the pause hotkey yet
+ f2.append('')
+
+ [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
+ self.fps_limit, keys['split'],
+ keys['reset'], keys['skip'], keys['undo'], self.x, self.y, self.width, self.height,
+ self.hwnd_title,
+ self.custom_pause_times_setting, self.custom_thresholds_setting,
+ self.group_dummy_splits_undo_skip_setting, self.loop_setting, keys['pause']] = f2
+
+ self.hwnd = win32gui.FindWindow(None, self.hwnd_title)
+ self.split_image_directory = str(self.split_image_directory)
+ self.splitimagefolderLineEdit.setText(self.split_image_directory)
+ self.similaritythresholdDoubleSpinBox.setValue(self.similarity_threshold)
+ self.pauseDoubleSpinBox.setValue(self.pause)
+ self.fpslimitSpinBox.setValue(self.fps_limit)
+ self.xSpinBox.setValue(self.x)
+ self.ySpinBox.setValue(self.y)
+ self.widthSpinBox.setValue(self.width)
+ self.heightSpinBox.setValue(self.height)
+ self.comparisonmethodComboBox.setCurrentIndex(self.comparison_index)
+
+ # Set custom checkboxes accordingly
+ self.custompausetimesCheckBox.setChecked(self.custom_pause_times_setting == 1)
+ self.customthresholdsCheckBox.setChecked(self.custom_thresholds_setting == 1)
+
+ # Should be activated by default
+ self.groupDummySplitsCheckBox.setChecked(self.group_dummy_splits_undo_skip_setting != 0)
+
+ self.loopCheckBox.setChecked(self.loop_setting == 1)
+
+ self.splitLineEdit.setText(str(keys['split']))
+ self.resetLineEdit.setText(str(keys['reset']))
+ self.skipsplitLineEdit.setText(str(keys['skip']))
+ self.undosplitLineEdit.setText(str(keys['undo']))
+ self.pauseLineEdit.setText(str(keys['pause']))
+
+ # Try to set hotkeys from when user last closed the window
+ if self.is_auto_controlled == False:
+ for key in self.hotkeys:
+ if self.hotkeys[key].key_press_function is None:
+ self.hotkeys[key].key = str(keys[key])
+ else:
+ self.hotkeys[key].setKeyAndHotkey(str(keys[key]))
+
+ except IOError:
+ self.showBox(errors.SETTINGS_NOT_FOUND)
+ pass
+ except Exception:
+ self.showBox(errors.INVALID_SETTINGS)
+ pass
+
+def saveSettings(self):
+ # get values to be able to save settings
+ self.x = self.xSpinBox.value()
+ self.y = self.ySpinBox.value()
+ self.width = self.widthSpinBox.value()
+ self.height = self.heightSpinBox.value()
+ self.split_image_directory = str(self.splitimagefolderLineEdit.text())
+ self.similarity_threshold = self.similaritythresholdDoubleSpinBox.value()
+ self.comparison_index = self.comparisonmethodComboBox.currentIndex()
+ self.pause = self.pauseDoubleSpinBox.value()
+ self.fps_limit = self.fpslimitSpinBox.value()
+ split_key = str(self.splitLineEdit.text())
+ reset_key = str(self.resetLineEdit.text())
+ skip_split_key = str(self.skipsplitLineEdit.text())
+ undo_split_key = str(self.undosplitLineEdit.text())
+ pause_key = str(self.pauseLineEdit.text())
+
+ self.custom_pause_times_setting = int(self.custompausetimesCheckBox.isChecked())
+ self.custom_thresholds_setting = int(self.customthresholdsCheckBox.isChecked())
+ self.group_dummy_splits_undo_skip_setting = int(self.groupDummySplitsCheckBox.isChecked())
+ self.loop_setting = int(self.loopCheckBox.isChecked())
+
+ # Save settings to settings.pkl
+ with open(path.join(self.file_path, 'settings.pkl'), 'wb') as f:
+ pickle.dump(
+ [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
+ self.fps_limit, split_key,
+ reset_key, skip_split_key, undo_split_key, self.x, self.y, self.width, self.height,
+ self.hwnd_title,
+ self.custom_pause_times_setting, self.custom_thresholds_setting,
+ self.group_dummy_splits_undo_skip_setting, self.loop_setting, pause_key], f)
\ No newline at end of file
diff --git a/src/split_parser.py b/src/split_parser.py
index ac367f0f..18c2104f 100644
--- a/src/split_parser.py
+++ b/src/split_parser.py
@@ -1,9 +1,12 @@
+import os
import cv2
import numpy as np
+import errors
+
class SplitImage:
- def __init__(self, path, filename, threshold_for_all_images, pause_for_all_images):
+ def __init__(self, path, filename, threshold_for_all_images = None, pause_for_all_images = None, get_custom_framerate = False):
self.path = path + filename
self.filename = filename
@@ -27,7 +30,7 @@ def __init__(self, path, filename, threshold_for_all_images, pause_for_all_image
self.threshold = float(self.filename.split('(', 1)[1].split(')')[0])
# Check to make sure if it is a valid threshold
- if (self.threshold > 1.0 or self.threshold < 0.0):
+ if self.threshold > 1.0 or self.threshold <= 0.0:
raise ValueError
except:
self.threshold = None
@@ -44,11 +47,20 @@ def __init__(self, path, filename, threshold_for_all_images, pause_for_all_image
self.pause = float(self.filename.split('[', 1)[1].split(']')[0])
# Pause times should always be positive or zero
- if (self.pause < 0.0):
+ if self.pause < 0.0:
raise ValueError
except:
self.pause = None
+ if get_custom_framerate:
+ try:
+ self.framerate = float(self.filename.split('<', 1)[1].split('>')[0])
+
+ # The framerate should always be positive
+ if self.framerate <= 0.0:
+ raise ValueError
+ except:
+ self.framerate = None
# Retrieve the delay time from the filename, if there is no delay time or the delay time
# isn't a valid number, then it is set to 0
@@ -59,7 +71,7 @@ def __init__(self, path, filename, threshold_for_all_images, pause_for_all_image
self.delay = float(self.filename.split('#', 1)[1].split('#')[0])
# Delay times should always be positive or zero
- if (self.delay < 0):
+ if self.delay < 0:
raise ValueError
except:
self.delay = 0
@@ -74,7 +86,7 @@ def __init__(self, path, filename, threshold_for_all_images, pause_for_all_image
self.loop = int(self.filename.split('@', 1)[1].split('@')[0])
# Make loop number 1 if it is less than 1
- if (self.loop < 1):
+ if self.loop < 1:
raise ValueError
except:
self.loop = 1
@@ -120,8 +132,8 @@ def __init__(self, path, filename, threshold_for_all_images, pause_for_all_image
self.is_reset_image = ('RESET' in self.filename.upper())
- def get_image(self, resize_width, resize_height):
- if (self.flags & 0x02 == 0x02):
+ def getImage(self, resize_width, resize_height):
+ if self.flags & 0x02 == 0x02:
# Create mask based on resized, nearest neighbor interpolated split image
self.image = cv2.imread(self.path, cv2.IMREAD_UNCHANGED)
self.image = cv2.resize(self.image, (resize_width, resize_height),
@@ -138,3 +150,27 @@ def get_image(self, resize_width, resize_height):
self.image = cv2.imread(self.path, cv2.IMREAD_COLOR)
self.image = cv2.resize(self.image, (resize_width, resize_height))
self.mask = None
+
+def getImageError(image):
+ # Check to make sure the file is actually an image format that can be opened
+ # according to the mask flag
+ if image.flags & 0x02 == 0x02:
+ source = cv2.imread(image.path, cv2.IMREAD_UNCHANGED)
+
+ if source is None:
+ # Opencv couldn't open this file as an image, this isn't a correct
+ # file format that is supported
+ return errors.IMAGE_TYPE
+
+ if source.shape[2] != 4:
+ # Error, this file doesn't have an alpha channel even
+ # though the flag for masking was added
+ return errors.ALPHA_CHANNEL
+
+ else:
+ if cv2.imread(image.path, cv2.IMREAD_COLOR) is None:
+ # Opencv couldn't open this file as an image, this isn't a correct
+ # file format that is supported
+ return errors.IMAGE_TYPE
+
+ return None
\ No newline at end of file
From ecd51c8113b63ceecc331fdd738e3387406982c1 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Wed, 6 May 2020 22:31:55 +0200
Subject: [PATCH 14/19] Update README
---
README.md | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 2e9695ad..ff73bec4 100644
--- a/README.md
+++ b/README.md
@@ -94,7 +94,10 @@ The best way to create a masked image is to set your capture region as the entir
### Reset image
-You can have one (and only one) image with the keyword `reset` in its name. AutoSplit will press the reset button when it finds this image. This image will only be used for resets and it will not be tied to any split. You can set a probability and pause time for it. A custom threshold MUST be applied to this image. The pause time is the amount of seconds AutoSplit will wait before checking for the reset image once the run starts. Also the image can be masked, for example: `Reset_(0.95)_[10]_{m}.png`.
+You can have one (and only one) image with the keyword `reset` in its name. AutoSplit will press the reset button when it finds this image. This image will only be used for resets and it will not be tied to any split. You can set a probability and pause time for it. A custom threshold **_must_** be applied to this image. The pause time is the amount of seconds AutoSplit will wait before checking for the reset image once the run starts. Also the image can be masked, for example: `Reset_(0.95)_[10]_{m}.png`.
+
+### Start image
+The start image is similar to the reset image. You can only have one start image with the keyword `start_auto_splitter`. The image must always have a custom threshold. You can reload the image using the "Reload" button. You can also specify a custom comparison frequency using `<>`. The pause time is the ymount of seconds AutoSplit will wait before checking for the start image once a run ends/is reset.
### Timer Global Hotkeys
- Click "Set Hotkey" on each hotkey to set the hotkeys to AutoSplit. Start / Split hotkey must be the same as the one used in your preferred timer program in order for the splitting to work properly.
@@ -126,16 +129,18 @@ This option does not loop specific images. See the Custom Split Image Settings s
### Settings
Each time AutoSplit is closed, it saves a the setting file `settings.pkl` to the directory AutoSplit.exe is located in. This settings file is loaded upon opening the program. These settings include split image directory, capture region, capture region dimensions, fps limit, threshold and pause time settings, hotkeys, "Group dummy splits when undoing/skipping" check box, and "Loop Split Images" check box. Settings can be reloaded using the Reload Settings button.
+## LiveSplit integration
+There is a LiveSplit Component available that will directly connect AutoSplit with LiveSplit. You can get it [here](https://github.com/KaDiWa4/LiveSplit.AutoSplitIntegration).
+
## Known Limitations
-- Starting your timer/AutoSplit is still manual.
- For many games, it will be difficult to find a split image for the last split of the run.
- The window of the capture region cannot be minimized.
## Known Issues
- When setting your region, you may only see a black image. This is caused by hardware acceleration. You may be able to disable this through the application itself like in [Google Chrome](https://www.technize.net/google-chrome-disable-hardware-acceleration/). If not, this can also be disabled through [Windows](https://www.thewindowsclub.com/hardware-acceleration-windows-7). **NOTE:** If you notice any computer performance issues after disabling hardware acceleration, re-enable it.
- Known to currently have issues selecting a region in Streamlabs OBS (only shows black image).
-- Using numpad number keys when numlock is on does not split correctly. Either avoid using numpad or turn numlock off to avoid this issue.
-- LiveSplit and wsplit will not split correctly if you are holding shift, ctrl, or alt when a match occurs.
+- Using numpad number keys when numlock is on does not split correctly. Either avoid using numpad, turn numlock off or use the LiveSplit integration to avoid this issue.
+- WSplit will not split correctly if you are holding shift, ctrl, or alt when a match occurs.
- Numlock on keys are linked to numlock-off keys. For example, if you set your reset hotkey to 2, you can hit arrow down and it will reset and vice versa.
## Resources
From d0006a38f8aec4178070307a1a39948904ea505b Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Wed, 6 May 2020 22:33:34 +0200
Subject: [PATCH 15/19] Fix typo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ff73bec4..fb4eb915 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ The best way to create a masked image is to set your capture region as the entir
You can have one (and only one) image with the keyword `reset` in its name. AutoSplit will press the reset button when it finds this image. This image will only be used for resets and it will not be tied to any split. You can set a probability and pause time for it. A custom threshold **_must_** be applied to this image. The pause time is the amount of seconds AutoSplit will wait before checking for the reset image once the run starts. Also the image can be masked, for example: `Reset_(0.95)_[10]_{m}.png`.
### Start image
-The start image is similar to the reset image. You can only have one start image with the keyword `start_auto_splitter`. The image must always have a custom threshold. You can reload the image using the "Reload" button. You can also specify a custom comparison frequency using `<>`. The pause time is the ymount of seconds AutoSplit will wait before checking for the start image once a run ends/is reset.
+The start image is similar to the reset image. You can only have one start image with the keyword `start_auto_splitter`. The image must always have a custom threshold. You can reload the image using the "Reload" button. You can also specify a custom comparison frequency using `<>`. The pause time is the amount of seconds AutoSplit will wait before checking for the start image once a run ends/is reset.
### Timer Global Hotkeys
- Click "Set Hotkey" on each hotkey to set the hotkeys to AutoSplit. Start / Split hotkey must be the same as the one used in your preferred timer program in order for the splitting to work properly.
From d766707bfa6595da9db619c513d46fac55135c5b Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Thu, 7 May 2020 14:58:09 +0200
Subject: [PATCH 16/19] Fix start image delay
---
src/AutoSplit.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index 44cebb56..b73e294e 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -341,7 +341,7 @@ def split():
self.startimageLabel.setText("Start image: started")
if self.start_image.delay > 0:
- threading.Timer(self.start_image.delay, split).start()
+ threading.Timer(self.start_image.delay / 1000, split).start()
else:
split()
From 2ddbdecfbb9b684c7aa0bfc9c1ac13d8f709fda4 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Wed, 20 May 2020 23:40:22 +0200
Subject: [PATCH 17/19] -Remove custom threshold/pause time checkboxes -Fix
FutureWarning -More Fixes
---
res/design.ui | 70 +++-------------------
src/AutoSplit.py | 138 ++++++++++++++++++++++++-------------------
src/design.py | 37 +++---------
src/errors.py | 6 +-
src/settings_file.py | 22 +++----
src/split_parser.py | 54 ++++++++---------
6 files changed, 129 insertions(+), 198 deletions(-)
diff --git a/res/design.ui b/res/design.ui
index 44db6124..f3ddc86f 100644
--- a/res/design.ui
+++ b/res/design.ui
@@ -161,13 +161,14 @@
10
- 408
+ 374
91
- 16
+ 21
- Similarity threshold
+ Similarity threshold
+Default value
@@ -231,7 +232,7 @@
- Qt::NoFocus
+ Qt::ClickFocus
Pause Comparison
@@ -273,13 +274,14 @@
10
- 450
+ 420
111
- 16
+ 21
- Pause time (seconds)
+ Pause time (seconds)
+Default value
@@ -1122,56 +1124,6 @@
10.000000000000000
-
-
- true
-
-
-
- 10
- 465
- 121
- 17
-
-
-
-
-
-
- Custom pause times
-
-
- false
-
-
- false
-
-
-
-
- true
-
-
-
- 10
- 424
- 111
- 17
-
-
-
-
-
-
- Custom thresholds
-
-
- false
-
-
- false
-
-
@@ -1333,8 +1285,6 @@
yLabel
comparisonmethodComboBox
pauseDoubleSpinBox
- custompausetimesCheckBox
- customthresholdsCheckBox
comparisonmethodLabel
alignregionButton
groupDummySplitsCheckBox
@@ -1382,9 +1332,7 @@
comparisonmethodComboBox
showlivesimilarityCheckBox
showhighestsimilarityCheckBox
- customthresholdsCheckBox
similaritythresholdDoubleSpinBox
- custompausetimesCheckBox
pauseDoubleSpinBox
splitLineEdit
resetLineEdit
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index b73e294e..6e4bc642 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -97,12 +97,12 @@ def __init__(self, parent=None):
self.browseButton.clicked.connect(self.browse)
self.selectregionButton.clicked.connect(self.selectRegion)
self.takescreenshotButton.clicked.connect(self.takeScreenshot)
- self.startautosplitterButton.clicked.connect(self.startAutoSplitter)
+ self.startautosplitterButton.clicked.connect(self.autoSplitter)
self.checkfpsButton.clicked.connect(self.checkFPS)
- self.resetButton.clicked.connect(self.startReset)
+ self.resetButton.clicked.connect(self.reset)
self.pauseComparisonButton.clicked.connect(self.pauseComparison)
- self.skipsplitButton.clicked.connect(self.startSkipSplit)
- self.undosplitButton.clicked.connect(self.startUndoSplit)
+ self.skipsplitButton.clicked.connect(self.skipSplit)
+ self.undosplitButton.clicked.connect(self.undoSplit)
self.setsplithotkeyButton.clicked.connect(self.setSplitHotkey)
self.setresethotkeyButton.clicked.connect(self.setResetHotkey)
self.setskipsplithotkeyButton.clicked.connect(self.setSkipSplitHotkey)
@@ -113,12 +113,16 @@ def __init__(self, parent=None):
self.reloadsettingsButton.clicked.connect(self.loadSettings)
self.startimagereloadButton.clicked.connect(self.reloadStartImage)
- # update x, y, width, and height when changing the value of these spinbox's are changed
+ # Update x, y, width, and height when changing the value of these spinboxes
self.xSpinBox.valueChanged.connect(self.updateX)
self.ySpinBox.valueChanged.connect(self.updateY)
self.widthSpinBox.valueChanged.connect(self.updateWidth)
self.heightSpinBox.valueChanged.connect(self.updateHeight)
+ # Update similarity threshold and pause time when changing the value of these spinboxes
+ self.similaritythresholdDoubleSpinBox.valueChanged.connect(self.updateSimilarityThreshold)
+ self.pauseDoubleSpinBox.valueChanged.connect(self.updatePause)
+
# connect signals to functions
self.updateCurrentSplitImage.connect(self.updateSplitImageGUI)
self.startAutoSplitterSignal.connect(self.autoSplitter)
@@ -233,6 +237,7 @@ def reloadStartImage(self):
def loadStartImage(self, started_by_button = False, wait_for_delay = True, is_in_startup = False):
self.timerStartImage.stop()
+ self.currentSplitImage.setText(' ')
self.currentsplitimagefileLabel.setText(' ')
self.startimageLabel.setText("Start image: not found")
QtGui.QApplication.processEvents()
@@ -291,7 +296,7 @@ def loadStartImage(self, started_by_button = False, wait_for_delay = True, is_in
else:
self.check_start_image_timestamp = 0.0
self.startimageLabel.setText("Start image: ready")
- self.updateSplitImage(self.start_image, False)
+ self.updateSplitImage(self.start_image, False, False)
self.highest_similarity = 0.0
@@ -310,9 +315,9 @@ def startImageFunction(self):
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, False)
+ self.updateSplitImage(self.start_image, False, False)
- self.start_image.similarity = self.compareImage(self.start_image)
+ self.start_image.similarity = self.compareImage(self.start_image.similarity, self.start_image)
if self.start_image.similarity == errors.REGION:
self.timerStartImage.stop()
@@ -549,21 +554,12 @@ def autoSplitter(self):
# Get split image filenames
self.split_images = []
previous_n_flag = False
- if self.customthresholdsCheckBox.isChecked():
- threshold_for_all_images = None
- else:
- threshold_for_all_images = self.similaritythresholdDoubleSpinBox.value()
-
- if self.custompausetimesCheckBox.isChecked():
- pause_for_all_images = None
- else:
- pause_for_all_images = self.pauseDoubleSpinBox.value()
for image_filename in os.listdir(self.split_image_directory):
- if 'START_AUTO_SPLITTER' in image_filename.upper():
+ if 'START_AUTO_SPLITTER' in image_filename.upper() and 'RESET' in image_filename.upper() == False:
continue
- self.split_images.append(split_parser.SplitImage(self.split_image_directory, image_filename, threshold_for_all_images, pause_for_all_images))
+ self.split_images.append(split_parser.SplitImage(self.split_image_directory, image_filename, self.default_similarity_threshold, self.default_pause))
# Make sure that each of the images follows the guidelines for correct format
# according to all of the settings selected by the user.
@@ -574,18 +570,6 @@ def autoSplitter(self):
self.showBox(image_error % self.split_images[-1], True)
return
- if self.custompausetimesCheckBox.isChecked() and self.split_images[-1].pause is None:
- # Error, this file doesn't have a pause, but the checkbox was
- # selected for unique pause times
- self.showBox(errors.CUSTOM_PAUSE % self.split_images[-1].filename, True)
- return
-
- if self.customthresholdsCheckBox.isChecked() and self.split_images[-1].threshold is None:
- # Error, this file doesn't have a threshold, but the checkbox
- # was selected for unique thresholds
- self.showBox(errors.CUSTOM_THRESHOLD % self.split_images[-1].filename, True)
- return
-
if self.pauseLineEdit.text() == '' and self.split_images[-1].flags & 0x08 == 0x08 and self.is_auto_controlled == False:
# Error, no pause hotkey set even though pause flag is set
self.showBox(errors.PAUSE_HOTKEY, True)
@@ -674,10 +658,11 @@ def autoSplitter(self):
# Change auto splitter button text and disable/enable some buttons
self.startautosplitterButton.setText('Running...')
self.browseButton.setEnabled(False)
- self.custompausetimesCheckBox.setEnabled(False)
- self.customthresholdsCheckBox.setEnabled(False)
self.groupDummySplitsCheckBox.setEnabled(False)
self.startimagereloadButton.setEnabled(False)
+ self.comparisonmethodComboBox.setEnabled(False)
+ self.similaritythresholdLabel.setText("Similarity threshold\nCurrent value")
+ self.pauseLabel.setText("Pause time (seconds)\nCurrent value")
if self.is_auto_controlled == False:
self.startautosplitterButton.setEnabled(False)
@@ -729,7 +714,7 @@ def autoSplitter(self):
capture = None
if self.shouldCheckResetImage():
- self.reset_image.similarity = self.compareImage(self.reset_image)
+ self.reset_image.similarity = self.compareImage(self.reset_image.similarity, self.reset_image)
if self.reset_image.similarity == errors.REGION or (self.reset_image.similarity is not None and self.reset_image.similarity >= self.reset_image.threshold):
if self.is_auto_controlled:
print("reset", flush = True)
@@ -754,22 +739,22 @@ def autoSplitter(self):
if capture is None:
capture = self.getCaptureForComparison(False)
- if capture == errors.REGION:
+ if type(capture) is str:
self.reset()
self.resetUI()
return
- image.similarity = self.compareImage(image, capture)
+ image.similarity = self.compareImage(image.similarity, image, capture)
else:
if masked_capture is None:
masked_capture = self.getCaptureForComparison(True)
- if capture == errors.REGION:
+ if type(capture) is str:
self.reset()
self.resetUI()
return
- image.similarity = self.compareImage(image, masked_capture)
+ image.similarity = self.compareImage(image.similarity, image, masked_capture)
if image.similarity is None:
break
@@ -844,7 +829,7 @@ def autoSplitter(self):
# calculate similarity for reset image
if self.shouldCheckResetImage():
- self.reset_image.similarity = self.compareImage(self.reset_image)
+ self.reset_image.similarity = self.compareImage(self.reset_image.similarity, self.reset_image)
if self.reset_image.similarity == errors.REGION or (self.reset_image.similarity is not None and self.reset_image.similarity >= self.reset_image.threshold):
if self.is_auto_controlled:
print("reset", flush = True)
@@ -884,15 +869,16 @@ def autoSplitter(self):
self.currentSplitImage.setAlignment(QtCore.Qt.AlignCenter)
self.imageloopLabel.setText(' ')
- # Make sure the index doesn't exceed the list
- if self.split_image_index < len(self.split_images):
- # If it's the last split image and last loop number, disable the skip split button
- self.skipsplitButton.setEnabled(self.successful_split_image.skip_image_index is not None or self.successful_split_image.loop != self.loop_number)
+ if self.is_auto_controlled == False:
+ # Make sure the index doesn't exceed the list
+ if self.split_image_index < len(self.split_images):
+ # If it's the last split image and last loop number, disable the skip split button
+ self.skipsplitButton.setEnabled(self.successful_split_image.skip_image_index is not None or self.successful_split_image.loop != self.loop_number)
- # If it's the first split image and first loop, disable the undo split button
- self.undosplitButton.setEnabled(self.successful_split_image.undo_image_index is not None or self.successful_split_image.loop != 1)
- else:
- self.undosplitButton.setEnabled(False)
+ # If it's the first split image and first loop, disable the undo split button
+ self.undosplitButton.setEnabled(self.successful_split_image.undo_image_index is not None or self.successful_split_image.loop != 1)
+ else:
+ self.undosplitButton.setEnabled(False)
QtGui.QApplication.processEvents()
@@ -913,7 +899,7 @@ def autoSplitter(self):
# calculate similarity for reset image
if self.shouldCheckResetImage():
- self.reset_image.similarity = self.compareImage(self.reset_image)
+ self.reset_image.similarity = self.compareImage(self.reset_image.similarity, self.reset_image)
if self.reset_image.similarity == errors.REGION or (self.reset_image.similarity is not None and self.reset_image.similarity >= self.reset_image.threshold):
if self.is_auto_controlled:
print("reset", flush = True)
@@ -935,10 +921,13 @@ def resetUI(self):
self.livesimilarityLabel.setText(' ')
self.highestsimilarityLabel.setText(' ')
self.browseButton.setEnabled(True)
- self.custompausetimesCheckBox.setEnabled(True)
- self.customthresholdsCheckBox.setEnabled(True)
self.groupDummySplitsCheckBox.setEnabled(True)
self.startimagereloadButton.setEnabled(True)
+ self.comparisonmethodComboBox.setEnabled(True)
+ self.similaritythresholdLabel.setText("Similarity threshold\nDefault value")
+ self.pauseLabel.setText("Pause time (seconds)\nDefault value")
+ self.similaritythresholdDoubleSpinBox.setValue(self.default_similarity_threshold)
+ self.pauseDoubleSpinBox.setValue(self.default_pause)
self.loadStartImage()
if self.is_auto_controlled == False:
@@ -973,22 +962,26 @@ def sendSplitKeyPress(self, flags):
else:
keyboard.send(str(self.pauseLineEdit.text()))
- def compareImage(self, image, capture = None):
+ def compareImage(self, previous_similarity, image, capture = None):
if self.is_comparison_paused:
return None
if capture is None:
capture = self.getCaptureForComparison(image.mask is not None)
- if capture == errors.REGION:
- return errors.REGION
+ if type(capture) is str:
+ return errors.REGION
- if self.comparisonmethodComboBox.currentIndex() == 0:
- return compare.compare_l2_norm(image, capture)
- if self.comparisonmethodComboBox.currentIndex() == 1:
- return compare.compare_histograms(image, capture)
- if self.comparisonmethodComboBox.currentIndex() == 2:
- return compare.compare_phash(image, capture)
+ try:
+ if self.comparisonmethodComboBox.currentIndex() == 0:
+ return compare.compare_l2_norm(image, capture)
+ if self.comparisonmethodComboBox.currentIndex() == 1:
+ return compare.compare_histograms(image, capture)
+ if self.comparisonmethodComboBox.currentIndex() == 2:
+ return compare.compare_phash(image, capture)
+ except Exception as e:
+ sys.stderr.write("Comparison failed: " + str(e) + "\n")
+ return previous_similarity
def getCaptureForComparison(self, masked):
if self.is_comparison_paused:
@@ -1033,7 +1026,7 @@ def updateLiveSimilarity(self, images):
else:
self.highestsimilarityLabel.setText(' ')
- def updateSplitImage(self, image, multiple_images):
+ def updateSplitImage(self, image, multiple_images, update_spinboxes = True):
if image.loop > 1:
# Set Image Loop #
@@ -1041,12 +1034,25 @@ def updateSplitImage(self, image, multiple_images):
else:
self.imageloopLabel.setText(' ')
+ self.similaritythresholdLabel.setEnabled(multiple_images == False)
+ self.pauseLabel.setEnabled(multiple_images == False)
+
if multiple_images:
self.currentSplitImage.setText(str(len(self.current_split_images)) + ' images')
self.currentsplitimagefileLabel.setText(' ')
self.currentSplitImage.setAlignment(QtCore.Qt.AlignCenter)
+
+ self.similaritythresholdDoubleSpinBox.setValue(self.default_similarity_threshold)
+ self.pauseDoubleSpinBox.setValue(self.default_pause)
return
+ if update_spinboxes:
+ if image.threshold is not None:
+ self.similaritythresholdDoubleSpinBox.setValue(image.threshold)
+
+ if image.pause is not None:
+ self.pauseDoubleSpinBox.setValue(image.pause)
+
# Set current split image in UI
# If flagged as mask, transform transparency into UI's gray BG color
if image.flags & 0x02 == 0x02:
@@ -1067,6 +1073,18 @@ def updateSplitImage(self, image, multiple_images):
self.updateCurrentSplitImage.emit(qImg)
self.currentsplitimagefileLabel.setText(image.filename)
+ def updateSimilarityThreshold(self):
+ if self.startautosplitterButton.text() == 'Start Auto Splitter':
+ self.default_similarity_threshold = self.similaritythresholdDoubleSpinBox.value()
+ else:
+ self.split_images[self.split_image_index].threshold = self.similaritythresholdDoubleSpinBox.value()
+
+ def updatePause(self):
+ if self.startautosplitterButton.text() == 'Start Auto Splitter':
+ self.default_pause = self.pauseDoubleSpinBox.value()
+ else:
+ self.split_images[self.split_image_index].pause = self.pauseDoubleSpinBox.value()
+
# Error messages
def showBox(self, message, reset = False):
if reset:
diff --git a/src/design.py b/src/design.py
index d515b1f4..c1af2133 100644
--- a/src/design.py
+++ b/src/design.py
@@ -71,7 +71,7 @@ def setupUi(self, MainWindow):
self.selectregionButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.selectregionButton.setObjectName(_fromUtf8("selectregionButton"))
self.similaritythresholdLabel = QtGui.QLabel(self.centralwidget)
- self.similaritythresholdLabel.setGeometry(QtCore.QRect(10, 408, 91, 16))
+ self.similaritythresholdLabel.setGeometry(QtCore.QRect(10, 412, 91, 22))
self.similaritythresholdLabel.setObjectName(_fromUtf8("similaritythresholdLabel"))
self.similaritythresholdDoubleSpinBox = QtGui.QDoubleSpinBox(self.centralwidget)
self.similaritythresholdDoubleSpinBox.setGeometry(QtCore.QRect(160, 413, 64, 22))
@@ -89,11 +89,11 @@ def setupUi(self, MainWindow):
self.resetButton.setObjectName(_fromUtf8("resetButton"))
self.pauseComparisonButton = QtGui.QPushButton(self.centralwidget)
self.pauseComparisonButton.setGeometry(QtCore.QRect(500, 380, 101, 31))
- self.pauseComparisonButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.pauseComparisonButton.setFocusPolicy(QtCore.Qt.ClickFocus)
self.pauseComparisonButton.setObjectName(_fromUtf8("pauseComparisonButton"))
self.reloadsettingsButton = QtGui.QPushButton(self.centralwidget)
self.reloadsettingsButton.setGeometry(QtCore.QRect(500, 350, 101, 21))
- self.reloadsettingsButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.reloadsettingsButton.setFocusPolicy(QtCore.Qt.ClickFocus)
self.reloadsettingsButton.setObjectName(_fromUtf8("takescreenshotButton"))
self.undosplitButton = QtGui.QPushButton(self.centralwidget)
self.undosplitButton.setGeometry(QtCore.QRect(477, 254, 61, 21))
@@ -104,7 +104,7 @@ def setupUi(self, MainWindow):
self.skipsplitButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.skipsplitButton.setObjectName(_fromUtf8("skipsplitButton"))
self.pauseLabel = QtGui.QLabel(self.centralwidget)
- self.pauseLabel.setGeometry(QtCore.QRect(10, 450, 111, 16))
+ self.pauseLabel.setGeometry(QtCore.QRect(10, 453, 111, 22))
self.pauseLabel.setObjectName(_fromUtf8("pauseLabel"))
self.checkfpsButton = QtGui.QPushButton(self.centralwidget)
self.checkfpsButton.setGeometry(QtCore.QRect(5, 225, 51, 21))
@@ -345,20 +345,6 @@ def setupUi(self, MainWindow):
self.pauseDoubleSpinBox.setSingleStep(1.0)
self.pauseDoubleSpinBox.setProperty("value", 10.0)
self.pauseDoubleSpinBox.setObjectName(_fromUtf8("pauseDoubleSpinBox"))
- self.custompausetimesCheckBox = QtGui.QCheckBox(self.centralwidget)
- self.custompausetimesCheckBox.setEnabled(True)
- self.custompausetimesCheckBox.setGeometry(QtCore.QRect(10, 465, 121, 17))
- self.custompausetimesCheckBox.setWhatsThis(_fromUtf8(""))
- self.custompausetimesCheckBox.setChecked(False)
- self.custompausetimesCheckBox.setTristate(False)
- self.custompausetimesCheckBox.setObjectName(_fromUtf8("custompausetimesCheckBox"))
- self.customthresholdsCheckBox = QtGui.QCheckBox(self.centralwidget)
- self.customthresholdsCheckBox.setEnabled(True)
- self.customthresholdsCheckBox.setGeometry(QtCore.QRect(10, 424, 111, 17))
- self.customthresholdsCheckBox.setWhatsThis(_fromUtf8(""))
- self.customthresholdsCheckBox.setChecked(False)
- self.customthresholdsCheckBox.setTristate(False)
- self.customthresholdsCheckBox.setObjectName(_fromUtf8("customthresholdsCheckBox"))
self.comparisonmethodLabel = QtGui.QLabel(self.centralwidget)
self.comparisonmethodLabel.setGeometry(QtCore.QRect(10, 330, 101, 16))
self.comparisonmethodLabel.setObjectName(_fromUtf8("comparisonmethodLabel"))
@@ -370,7 +356,6 @@ def setupUi(self, MainWindow):
self.groupDummySplitsCheckBox.setGeometry(QtCore.QRect(252, 470, 230, 17))
self.groupDummySplitsCheckBox.setWhatsThis(_fromUtf8(""))
self.groupDummySplitsCheckBox.setChecked(True)
- self.customthresholdsCheckBox.setTristate(False)
self.groupDummySplitsCheckBox.setObjectName(_fromUtf8("groupDummySplitsCheckBox"))
self.selectwindowButton = QtGui.QPushButton(self.centralwidget)
self.selectwindowButton.setGeometry(QtCore.QRect(5, 117, 101, 23))
@@ -450,8 +435,6 @@ def setupUi(self, MainWindow):
self.yLabel.raise_()
self.comparisonmethodComboBox.raise_()
self.pauseDoubleSpinBox.raise_()
- self.custompausetimesCheckBox.raise_()
- self.customthresholdsCheckBox.raise_()
self.comparisonmethodLabel.raise_()
self.alignregionButton.raise_()
self.groupDummySplitsCheckBox.raise_()
@@ -484,10 +467,8 @@ def setupUi(self, MainWindow):
MainWindow.setTabOrder(self.liveimageCheckBox, self.comparisonmethodComboBox)
MainWindow.setTabOrder(self.comparisonmethodComboBox, self.showlivesimilarityCheckBox)
MainWindow.setTabOrder(self.showlivesimilarityCheckBox, self.showhighestsimilarityCheckBox)
- MainWindow.setTabOrder(self.showhighestsimilarityCheckBox, self.customthresholdsCheckBox)
- MainWindow.setTabOrder(self.customthresholdsCheckBox, self.similaritythresholdDoubleSpinBox)
- MainWindow.setTabOrder(self.similaritythresholdDoubleSpinBox, self.custompausetimesCheckBox)
- MainWindow.setTabOrder(self.custompausetimesCheckBox, self.pauseDoubleSpinBox)
+ MainWindow.setTabOrder(self.showhighestsimilarityCheckBox, self.similaritythresholdDoubleSpinBox)
+ MainWindow.setTabOrder(self.similaritythresholdDoubleSpinBox, self.pauseDoubleSpinBox)
MainWindow.setTabOrder(self.pauseDoubleSpinBox, self.splitLineEdit)
MainWindow.setTabOrder(self.splitLineEdit, self.resetLineEdit)
MainWindow.setTabOrder(self.resetLineEdit, self.skipsplitLineEdit)
@@ -504,14 +485,14 @@ def retranslateUi(self, MainWindow):
self.liveimageCheckBox.setText(_translate("MainWindow", "Live Capture Region", None))
self.loopCheckBox.setText(_translate("MainWindow", "Loop Split Images", None))
self.selectregionButton.setText(_translate("MainWindow", "Select Region", None))
- self.similaritythresholdLabel.setText(_translate("MainWindow", "Similarity threshold", None))
+ self.similaritythresholdLabel.setText(_translate("MainWindow", "Similarity threshold\nDefault value:", None))
self.startautosplitterButton.setText(_translate("MainWindow", "Start Auto Splitter", None))
self.resetButton.setText(_translate("MainWindow", "Reset", None))
self.pauseComparisonButton.setText(_translate("MainWindow", "Pause Comparison", None))
self.reloadsettingsButton.setText(_translate("MainWindow", "Reload Settings", None))
self.undosplitButton.setText(_translate("MainWindow", "Undo Split", None))
self.skipsplitButton.setText(_translate("MainWindow", "Skip Split", None))
- self.pauseLabel.setText(_translate("MainWindow", "Pause time (seconds)", None))
+ self.pauseLabel.setText(_translate("MainWindow", "Pause time (seconds)\nDefault value:", None))
self.checkfpsButton.setText(_translate("MainWindow", "Max FPS", None))
self.fpsLabel.setText(_translate("MainWindow", "FPS", None))
self.showlivesimilarityCheckBox.setText(_translate("MainWindow", "Show live similarity", None))
@@ -538,8 +519,6 @@ def retranslateUi(self, MainWindow):
self.comparisonmethodComboBox.setItemText(0, _translate("MainWindow", "L2 Norm", None))
self.comparisonmethodComboBox.setItemText(1, _translate("MainWindow", "Histograms", None))
self.comparisonmethodComboBox.setItemText(2, _translate("MainWindow", "pHash", None))
- self.custompausetimesCheckBox.setText(_translate("MainWindow", "Custom pause times", None))
- self.customthresholdsCheckBox.setText(_translate("MainWindow", "Custom thresholds", None))
self.comparisonmethodLabel.setText(_translate("MainWindow", "Comparison Method", None))
self.alignregionButton.setText(_translate("MainWindow", "Align Region", None))
self.groupDummySplitsCheckBox.setText(_translate("MainWindow", "Group dummy splits when undoing/skipping", None))
diff --git a/src/errors.py b/src/errors.py
index d38c07a3..a1c8d52d 100644
--- a/src/errors.py
+++ b/src/errors.py
@@ -15,10 +15,6 @@
SPLIT_HOTKEY = "No split hotkey has been set."
-CUSTOM_THRESHOLD = '"%s" does not have a valid custom threshold.'
-
-CUSTOM_PAUSE = '"%s" does not have a valid custom pause time.'
-
ALPHA_CHANNEL = '"%s" is marked with mask flag but it does not have transparency.'
ALIGN_REGION_IMAGE_TYPE = "File is not a valid image file."
@@ -43,4 +39,4 @@
INCLUDE_NEXT_FLAG_WITH_LOOP = "Your split image folder contains an image marked with include next flag followed by an image with a loop value greater than 1."
-NO_START_IMAGE = 'Your split image folder does not contain an image with the keyword "start".'
+NO_START_IMAGE = 'Your split image folder does not contain an image with the keyword "start_auto_splitter".'
diff --git a/src/settings_file.py b/src/settings_file.py
index 0f7a2cd9..4bf58f1a 100644
--- a/src/settings_file.py
+++ b/src/settings_file.py
@@ -16,18 +16,18 @@ def loadSettings(self):
# The settings file might not include the pause hotkey yet
f2.append('')
- [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
+ [self.split_image_directory, self.default_similarity_threshold, self.comparison_index, self.default_pause,
self.fps_limit, keys['split'],
keys['reset'], keys['skip'], keys['undo'], self.x, self.y, self.width, self.height,
self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
+ custom_pause_times_setting, custom_thresholds_setting,
self.group_dummy_splits_undo_skip_setting, self.loop_setting, keys['pause']] = f2
self.hwnd = win32gui.FindWindow(None, self.hwnd_title)
self.split_image_directory = str(self.split_image_directory)
self.splitimagefolderLineEdit.setText(self.split_image_directory)
- self.similaritythresholdDoubleSpinBox.setValue(self.similarity_threshold)
- self.pauseDoubleSpinBox.setValue(self.pause)
+ self.similaritythresholdDoubleSpinBox.setValue(self.default_similarity_threshold)
+ self.pauseDoubleSpinBox.setValue(self.default_pause)
self.fpslimitSpinBox.setValue(self.fps_limit)
self.xSpinBox.setValue(self.x)
self.ySpinBox.setValue(self.y)
@@ -35,10 +35,6 @@ def loadSettings(self):
self.heightSpinBox.setValue(self.height)
self.comparisonmethodComboBox.setCurrentIndex(self.comparison_index)
- # Set custom checkboxes accordingly
- self.custompausetimesCheckBox.setChecked(self.custom_pause_times_setting == 1)
- self.customthresholdsCheckBox.setChecked(self.custom_thresholds_setting == 1)
-
# Should be activated by default
self.groupDummySplitsCheckBox.setChecked(self.group_dummy_splits_undo_skip_setting != 0)
@@ -72,9 +68,9 @@ def saveSettings(self):
self.width = self.widthSpinBox.value()
self.height = self.heightSpinBox.value()
self.split_image_directory = str(self.splitimagefolderLineEdit.text())
- self.similarity_threshold = self.similaritythresholdDoubleSpinBox.value()
+ self.default_similarity_threshold = self.similaritythresholdDoubleSpinBox.value()
self.comparison_index = self.comparisonmethodComboBox.currentIndex()
- self.pause = self.pauseDoubleSpinBox.value()
+ self.default_pause = self.pauseDoubleSpinBox.value()
self.fps_limit = self.fpslimitSpinBox.value()
split_key = str(self.splitLineEdit.text())
reset_key = str(self.resetLineEdit.text())
@@ -82,17 +78,15 @@ def saveSettings(self):
undo_split_key = str(self.undosplitLineEdit.text())
pause_key = str(self.pauseLineEdit.text())
- self.custom_pause_times_setting = int(self.custompausetimesCheckBox.isChecked())
- self.custom_thresholds_setting = int(self.customthresholdsCheckBox.isChecked())
self.group_dummy_splits_undo_skip_setting = int(self.groupDummySplitsCheckBox.isChecked())
self.loop_setting = int(self.loopCheckBox.isChecked())
# Save settings to settings.pkl
with open(path.join(self.file_path, 'settings.pkl'), 'wb') as f:
pickle.dump(
- [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
+ [self.split_image_directory, self.default_similarity_threshold, self.comparison_index, self.default_pause,
self.fps_limit, split_key,
reset_key, skip_split_key, undo_split_key, self.x, self.y, self.width, self.height,
self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
+ False, False,
self.group_dummy_splits_undo_skip_setting, self.loop_setting, pause_key], f)
\ No newline at end of file
diff --git a/src/split_parser.py b/src/split_parser.py
index 18c2104f..45f6121c 100644
--- a/src/split_parser.py
+++ b/src/split_parser.py
@@ -6,10 +6,12 @@
class SplitImage:
- def __init__(self, path, filename, threshold_for_all_images = None, pause_for_all_images = None, get_custom_framerate = False):
+ def __init__(self, path, filename, default_threshold = None, default_pause = None, get_custom_framerate = False):
self.path = path + filename
self.filename = filename
+ self.similarity = None
+
# For the {b} flag
self.split_below_threshold = False
@@ -18,39 +20,33 @@ def __init__(self, path, filename, threshold_for_all_images = None, pause_for_al
self.undo_image_index = None
self.skip_image_index = None
- if threshold_for_all_images is not None:
- self.threshold = threshold_for_all_images
- else:
- # Retrieve the threshold from the filename, if there is no threshold or the threshold
- # doesn't meet the requirements of being between 0.0 and 1.0, then it is set to None
+ # Retrieve the threshold from the filename, if there is no threshold or the threshold
+ # doesn't meet the requirements of being between 0.0 and 1.0, then it is set to the default
- # Check to make sure there is a valid floating point number between
- # parentheses of the filename
- try:
- self.threshold = float(self.filename.split('(', 1)[1].split(')')[0])
+ # Check to make sure there is a valid floating point number between
+ # parentheses of the filename
+ try:
+ self.threshold = float(self.filename.split('(', 1)[1].split(')')[0])
- # Check to make sure if it is a valid threshold
- if self.threshold > 1.0 or self.threshold <= 0.0:
- raise ValueError
- except:
- self.threshold = None
+ # Check to make sure if it is a valid threshold
+ if self.threshold > 1.0 or self.threshold <= 0.0:
+ raise ValueError
+ except:
+ self.threshold = default_threshold
- if pause_for_all_images is not None:
- self.pause = pause_for_all_images
- else:
- # Retrieve the pause time from the filename, if there is no pause time or the pause time
- # isn't a valid number, then it is set to None
+ # Retrieve the pause time from the filename, if there is no pause time or the pause time
+ # isn't a valid number, then it is set to the default
- # Check to make sure there is a valid pause time between brackets
- # of the filename
- try:
- self.pause = float(self.filename.split('[', 1)[1].split(']')[0])
+ # Check to make sure there is a valid pause time between brackets
+ # of the filename
+ try:
+ self.pause = float(self.filename.split('[', 1)[1].split(']')[0])
- # Pause times should always be positive or zero
- if self.pause < 0.0:
- raise ValueError
- except:
- self.pause = None
+ # Pause times should always be positive or zero
+ if self.pause < 0.0:
+ raise ValueError
+ except:
+ self.pause = default_pause
if get_custom_framerate:
try:
From 49aafacfa9a7b56aca62f3d5a3ab2101b54dee12 Mon Sep 17 00:00:00 2001
From: KaDiWa
Date: Wed, 20 May 2020 23:47:16 +0200
Subject: [PATCH 18/19] Updatte readme
---
README.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index fb4eb915..a7a148cc 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ This program compares split images to a capture region of any window (OBS, xspli
## DOWNLOAD AND OPEN
-### Compatability
+### Compatibility
- Windows 7 and 10.
### Opening the program
@@ -68,8 +68,8 @@ This program compares split images to a capture region of any window (OBS, xspli
### Custom Split Image Settings
- Each split image can have different thresholds, pause times, delay split times, loop amounts, and can be flagged.
- These settings are handled in the image's filename.
-- Custom thresholds are place between parenthesis `()` in the filename and the custom thresholds checkbox must be checked. All images must have a custom threshold if the box is checked.
-- Custom pause times are placed between square brackets `[]` in the filename and the custom pause times checkbox must be checked. All images must have a custom threshold if the box is checked.
+- Custom thresholds are place between parentheses `()` in the filename.
+- Custom pause times are placed between square brackets `[]` in the filename.
- Custom delay times are placed between hash signs `##` in the filename. Note that these are in milliseconds. For example, a 10 second split delay would be `#10000#`. You cannot skip or undo splits during split delays.
- Image loop amounts are placed between at symbols `@@` in the filename. For example, a specific image that you want to split 5 times in a row would be `@5@`. The current loop # is conveniently located beneath the current split image.
- Flags are placed between curly brackets `{}` in the filename. Multiple flags are placed in the same set of curly brackets. Current available flags:
@@ -79,7 +79,7 @@ This program compares split images to a capture region of any window (OBS, xspli
- `{m}` masked split image. This allows you to customize what you want compared in your split image by using transparency. Transparent pixels in the split image are ignored when comparing. This is useful if only a certain part of the capture region is consistent (for example, consistent text on the screen, but the background is always different). These images *must* be `.png` and contain transparency. For more on this, see [How to Create a Masked Image](#how-to-create-a-masked-image). Histogram or L2 norm comparison is recommended if you use any masked images. It is highly recommended that you do **_not_** use pHash comparison if you use any masked images, as it is very inaccurate
- `{p}` pause/unpause timer. This allows you to let AutoSplit remove the loading times. Use dummy flag as well if you only want to pause/unpause but don't want to split. There is no "Pause / Unpause" button in AutoSplit because it doesn't keep track of whether your timer is currently paused.
- `{n}` include next split as well. This allows you to have more than one split image that the live image is compared to simultaneously. You can chain split images marked with this flag together. Only the first image in such a chain can have a loop value greater than 1. This flag is compatible with all other values/flags.
- - `{u}` undo split image. This flag is only useful with the "Group dummy splits" checkbox enabled. The split image with this flag is the split in this split group that is chosen when you hit undo while in the split group after this one. This is useful if there are split images at the beginning and the end of a split image group. If multiple split images in the same group have this flag, only the last one will work. Default is the first split of the group.
+ - `{u}` undo split image. This flag is only useful with the "Group dummy splits" checkbox enabled. The split image with this flag is the split image in this split image group that is chosen when you hit undo while in the group after that one. This is useful if there are split images at the beginning and the end of a split image group. If multiple split images in the same group have this flag, only the last one will work. Default is the first split of the group.
- Filename examples:
- `001_SplitName_(0.9)_[10].png` is a split image with a threshold of 0.9 and a pause time of 10 seconds.
- `002_SplitName_(0.9)_[10]_{d}.png` is the second split image with a threshold of 0.9, pause time of 10, and is a dummy split.
@@ -105,7 +105,7 @@ The start image is similar to the reset image. You can only have one start image
- All of these actions can also be handled by their corresponding buttons.
### 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 splits normally even if they are dummy splits (this was the normal behavior in versions 1.2.0 and older).
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.
From 7608e51b97429845370bda1b7f531e7b55f692b4 Mon Sep 17 00:00:00 2001
From: KaDiWa4
Date: Thu, 21 May 2020 00:01:34 +0200
Subject: [PATCH 19/19] Fix parentheses
---
src/AutoSplit.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/AutoSplit.py b/src/AutoSplit.py
index 6e4bc642..62a18a68 100644
--- a/src/AutoSplit.py
+++ b/src/AutoSplit.py
@@ -556,7 +556,7 @@ def autoSplitter(self):
previous_n_flag = False
for image_filename in os.listdir(self.split_image_directory):
- if 'START_AUTO_SPLITTER' in image_filename.upper() and 'RESET' in image_filename.upper() == False:
+ if 'START_AUTO_SPLITTER' in image_filename.upper() and ('RESET' in image_filename.upper()) == False:
continue
self.split_images.append(split_parser.SplitImage(self.split_image_directory, image_filename, self.default_similarity_threshold, self.default_pause))