From d6a8bb80ace8c037c872c05d0708ace846d79954 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sat, 22 Apr 2023 17:25:18 -0400 Subject: [PATCH 01/10] Fix D3D missing a param --- src/AutoSplit.py | 2 +- src/capture_method/CaptureMethodBase.py | 2 +- src/capture_method/DesktopDuplicationCaptureMethod.py | 4 ++-- src/capture_method/VideoCaptureDeviceCaptureMethod.py | 2 +- src/capture_method/__init__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/AutoSplit.py b/src/AutoSplit.py index 332e02f6..46243004 100644 --- a/src/AutoSplit.py +++ b/src/AutoSplit.py @@ -78,7 +78,7 @@ class AutoSplit(QMainWindow, design.Ui_MainWindow): # pylint: disable=too-many- split_image_number = 0 split_images_and_loop_number: list[tuple[AutoSplitImage, int]] = [] split_groups: list[list[int]] = [] - capture_method = CaptureMethodBase() + capture_method = CaptureMethodBase(None) is_running = False # Last loaded settings empty and last successful loaded settings file path to None until we try to load them diff --git a/src/capture_method/CaptureMethodBase.py b/src/capture_method/CaptureMethodBase.py index e75caf07..41e6d935 100644 --- a/src/capture_method/CaptureMethodBase.py +++ b/src/capture_method/CaptureMethodBase.py @@ -11,7 +11,7 @@ class CaptureMethodBase(): - def __init__(self, autosplit: AutoSplit | None = None): + def __init__(self, autosplit: AutoSplit | None): # Some capture methods don't need an initialization process pass diff --git a/src/capture_method/DesktopDuplicationCaptureMethod.py b/src/capture_method/DesktopDuplicationCaptureMethod.py index 7ee1dec0..6c322a49 100644 --- a/src/capture_method/DesktopDuplicationCaptureMethod.py +++ b/src/capture_method/DesktopDuplicationCaptureMethod.py @@ -16,8 +16,8 @@ class DesktopDuplicationCaptureMethod(BitBltCaptureMethod): # pylint: disable=too-few-public-methods - def __init__(self): - super().__init__() + def __init__(self, autosplit: AutoSplit | None): + super().__init__(autosplit) # Must not set statically as some laptops will throw an error self.desktop_duplication = d3dshot.create(capture_output="numpy") diff --git a/src/capture_method/VideoCaptureDeviceCaptureMethod.py b/src/capture_method/VideoCaptureDeviceCaptureMethod.py index 8e60f584..cf031854 100644 --- a/src/capture_method/VideoCaptureDeviceCaptureMethod.py +++ b/src/capture_method/VideoCaptureDeviceCaptureMethod.py @@ -68,7 +68,7 @@ def __read_loop(self, autosplit: AutoSplit): ) def __init__(self, autosplit: AutoSplit): - super().__init__() + super().__init__(autosplit) filter_graph = dshow_graph.FilterGraph() filter_graph.add_video_input_device(autosplit.settings_dict["capture_device_id"]) width, height = filter_graph.get_input_device().get_current_format() diff --git a/src/capture_method/__init__.py b/src/capture_method/__init__.py index 35dd93ee..bb90e4db 100644 --- a/src/capture_method/__init__.py +++ b/src/capture_method/__init__.py @@ -147,7 +147,7 @@ def get(self, __key: CaptureMethodEnum): implementation=BitBltCaptureMethod, ) -try: +try: # Test for laptop cross-GPU Desktop Duplication issue import d3dshot d3dshot.create(capture_output="numpy") except (ModuleNotFoundError, COMError): From b1af5cbd96245345c77e665932e7503401bca944 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sat, 22 Apr 2023 20:07:28 -0400 Subject: [PATCH 02/10] Fix somewhat consistant BitBlt error when closing a dolphin game (but not the emulator itself) --- README.md | 6 ++++-- src/capture_method/BitBltCaptureMethod.py | 8 +++++--- src/utils.py | 10 ++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d394e844..556c6039 100644 --- a/README.md +++ b/README.md @@ -230,8 +230,10 @@ The AutoSplit LiveSplit Component will directly connect AutoSplit with LiveSplit ## Resources Still need help? - -- [Check if your issue already exists or open a new one](../../issues) + +- [Check if your issue already exists](../../issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) + - If it does, upvote it 👍 + - If it doesn't, create a new one - Join the [AutoSplit Discord ![AutoSplit Discord](https://badgen.net/discord/members/Qcbxv9y)](https://discord.gg/Qcbxv9y) diff --git a/src/capture_method/BitBltCaptureMethod.py b/src/capture_method/BitBltCaptureMethod.py index 9eca085b..385bc7f1 100644 --- a/src/capture_method/BitBltCaptureMethod.py +++ b/src/capture_method/BitBltCaptureMethod.py @@ -12,7 +12,7 @@ from win32 import win32gui from capture_method.CaptureMethodBase import CaptureMethodBase -from utils import get_window_bounds, is_valid_hwnd +from utils import get_window_bounds, is_valid_hwnd, try_delete_dc if TYPE_CHECKING: from AutoSplit import AutoSplit @@ -58,10 +58,12 @@ def get_frame(self, autosplit: AutoSplit) -> tuple[cv2.Mat | None, bool]: image = np.frombuffer(cast(bytes, bitmap.GetBitmapBits(True)), dtype=np.uint8) image.shape = (selection["height"], selection["width"], 4) except (win32ui.error, pywintypes.error): + # Invalid handle or the window was closed while it was being manipulated return None, False + # Cleanup DC and handle - dc_object.DeleteDC() - compatible_dc.DeleteDC() + try_delete_dc(dc_object) + try_delete_dc(compatible_dc) win32gui.ReleaseDC(hwnd, window_dc) win32gui.DeleteObject(bitmap.GetHandle()) return image, False diff --git a/src/utils.py b/src/utils.py index 00059d58..f9ecc55b 100644 --- a/src/utils.py +++ b/src/utils.py @@ -11,6 +11,7 @@ from typing import TYPE_CHECKING, Any, TypeVar, cast import cv2 +import win32ui from win32 import win32gui from winsdk.windows.ai.machinelearning import LearningModelDevice, LearningModelDeviceKind from winsdk.windows.media.capture import MediaCapture @@ -18,6 +19,8 @@ from gen.build_vars import AUTOSPLIT_BUILD_NUMBER, AUTOSPLIT_GITHUB_REPOSITORY if TYPE_CHECKING: + # Source does not exist, keep this under TYPE_CHECKING + from _win32typing import PyCDC # pyright: ignore[reportMissingModuleSource] from typing_extensions import ParamSpec, TypeGuard P = ParamSpec("P") @@ -64,6 +67,13 @@ def first(iterable: Iterable[T]) -> T: return next(iter(iterable)) +def try_delete_dc(dc: PyCDC): + try: + dc.DeleteDC() + except win32ui.error: + pass + + def get_window_bounds(hwnd: int) -> tuple[int, int, int, int]: extended_frame_bounds = ctypes.wintypes.RECT() ctypes.windll.dwmapi.DwmGetWindowAttribute( From 7ca1cf52d16fec1965fc5b21a695e6b27a2aa629 Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 24 Apr 2023 14:04:58 -0400 Subject: [PATCH 03/10] Better handle some camera errors - Cameras not being ready - OBS Virtual Camera --- .../VideoCaptureDeviceCaptureMethod.py | 12 ++++++++---- src/capture_method/__init__.py | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/capture_method/VideoCaptureDeviceCaptureMethod.py b/src/capture_method/VideoCaptureDeviceCaptureMethod.py index cf031854..d53c8ded 100644 --- a/src/capture_method/VideoCaptureDeviceCaptureMethod.py +++ b/src/capture_method/VideoCaptureDeviceCaptureMethod.py @@ -38,12 +38,16 @@ def __read_loop(self, autosplit: AutoSplit): result, image = self.capture_device.read() except cv2.error as error: if not ( - error.code == cv2.Error.STS_ERROR and error.msg.endswith( - "in function 'cv::VideoCapture::grab'\n", + error.code == cv2.Error.STS_ERROR + and ( + # Likely means the camera is occupied + error.msg.endswith("in function 'cv::VideoCapture::grab'\n") + # Some capture cards we cannot use directly + # https://github.com/opencv/opencv/issues/23539 + or error.msg.endswith("in function 'cv::VideoCapture::retrieve'\n") ) ): raise - # STS_ERROR most likely means the camera is occupied result = False image = None if not result: @@ -80,8 +84,8 @@ def __init__(self, autosplit: AutoSplit): try: self.capture_device.set(cv2.CAP_PROP_FRAME_WIDTH, width) self.capture_device.set(cv2.CAP_PROP_FRAME_HEIGHT, height) - # Some cameras don't allow changing the resolution except cv2.error: + # Some cameras don't allow changing the resolution pass self.stop_thread = Event() self.capture_thread = Thread(target=lambda: self.__read_loop(autosplit)) diff --git a/src/capture_method/__init__.py b/src/capture_method/__init__.py index bb90e4db..e2588702 100644 --- a/src/capture_method/__init__.py +++ b/src/capture_method/__init__.py @@ -248,6 +248,9 @@ async def get_camera_info(index: int, device_name: str): *[ get_camera_info(index, name) for index, name in enumerate(named_video_inputs) + # Will crash when trying to resize, and does not work to begin with + # TODO: Should be fixed in next release of OpenCV (4.8) + if name != "OBS Virtual Camera" ], ) From 6720b22132b5ba463b7db53cb6d68fc25416d3fd Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 24 Apr 2023 15:32:52 -0400 Subject: [PATCH 04/10] Move documentation in `docs` + add CONTRIBUTING.md --- README.md | 42 +++++++++++++----- {res => docs}/2.0.0_gif.gif | Bin docs/CONTRIBUTING.md | 33 ++++++++++++++ .../D3DDD-Note-Laptops.md | 0 .../build instructions.md | 14 +++--- {res => docs}/mask_example_image.png | Bin scripts/requirements.txt | 9 +--- src/capture_method/__init__.py | 2 +- 8 files changed, 75 insertions(+), 25 deletions(-) rename {res => docs}/2.0.0_gif.gif (100%) create mode 100644 docs/CONTRIBUTING.md rename D3DDD-Note-Laptops.md => docs/D3DDD-Note-Laptops.md (100%) rename build instructions.md => docs/build instructions.md (61%) rename {res => docs}/mask_example_image.png (100%) diff --git a/README.md b/README.md index 556c6039..3d8c2504 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Easy to use image comparison based auto splitter for speedrunning on console or This program can be used to automatically start, split, and reset your preferred speedrun timer by comparing images to a capture region. This allows you to focus more on your speedrun and less on managing your timer. It also improves the accuracy of your splits. It can be used in tandem with any speedrun timer that accepts hotkeys (LiveSplit, wsplit, etc.), and can be integrated with LiveSplit. -![Example](res/2.0.0_gif.gif) +![Example](/docs/2.0.0_gif.gif) # TUTORIAL @@ -24,13 +24,8 @@ This program can be used to automatically start, split, and reset your preferred ### Compatibility -- Python 3.9+ - Windows 10 and 11. - -### Building - -(This is not required for normal use) -Refer to the [build instructions](build%20instructions.md) if you'd like to build the application yourself or run it directly in Python. +- Python 3.9+ (Not requried for normal use. Refer to the [build instructions](/docs/build%20instructions.md) if you'd like run the application directly in Python). ## OPTIONS @@ -84,11 +79,11 @@ Refer to the [build instructions](build%20instructions.md) if you'd like to buil It can record OpenGL and Hardware Accelerated windows. About 10-15x slower than BitBlt. Not affected by window size. overlapping windows will show up and can't record across displays. - This option may not be available for hybrid GPU laptops, see [D3DDD-Note-Laptops.md](/D3DDD-Note-Laptops.md) for a solution. + This option may not be available for hybrid GPU laptops, see [D3DDD-Note-Laptops.md](/docs/D3DDD-Note-Laptops.md) for a solution. - **Force Full Content Rendering** (very slow, can affect rendering) Uses BitBlt behind the scene, but passes a special flag to PrintWindow to force rendering the entire desktop. About 10-15x slower than BitBlt based on original window size and can mess up some applications' rendering pipelines. -- **Video Capture Device** +- **Video Capture Device** Uses a Video Capture Device, like a webcam, virtual cam, or capture card. If you want to use this with OBS' Virtual Camera, use the [Virtualcam plugin](https://github.com/Avasam/obs-virtual-cam/releases) instead. @@ -179,7 +174,7 @@ Masked images are very useful if only a certain part of the capture region is co 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 creating images with transparency using paint.net can be found in [this tutorial](https://www.youtube.com/watch?v=v53kkUYFVn8). 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: -![Mask Example](res/mask_example_image.png) +![Mask Example](/docs/mask_example_image.png) ### Reset image @@ -226,6 +221,7 @@ The AutoSplit LiveSplit Component will directly connect AutoSplit with LiveSplit - 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. +- OBS' integrated Virtual Camera does not work and makes AutoSplit crash. ## Resources @@ -237,6 +233,32 @@ Still need help? - Join the [AutoSplit Discord ![AutoSplit Discord](https://badgen.net/discord/members/Qcbxv9y)](https://discord.gg/Qcbxv9y) +## Contributing + +See [CONTRIBUTING.md](/docs/CONTRIBUTING.md) for our contributing standards. +Refer to the [build instructions](/docs/build%20instructions.md) if you're interested in building the application yourself or running it in Python. + +Not a developper? You can still help through the following methods: + +- Donating (see link below) +- [Upvoting feature requests](../../issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement) you are interested in +- Sharing AutoSplit with other speedrunners +- Upvoting the following upstream issues in libraries and tools we use: + - + - + - + - + - + - + - + - + - + - + - + - + - + - + ## Credits - Created by [Toufool](https://twitter.com/Toufool) and [Faschz](https://twitter.com/faschz). diff --git a/res/2.0.0_gif.gif b/docs/2.0.0_gif.gif similarity index 100% rename from res/2.0.0_gif.gif rename to docs/2.0.0_gif.gif diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 00000000..fbe45ef0 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,33 @@ + + +# Contributing guidelines + +## Python Setup and Building + +Refer to the [build instructions](/docs/build%20instructions.md) if you're interested in building the application yourself or running it in Python. + +## Linting and formatting + +The project is setup to automatically configure VSCode witht he proper extensions and settings. Linters and formatters will be run on save. +If you use a different IDE or for some reason cannot / don't want to use the recommended extensions, you can run `scripts/lint.ps1`. + +If you like to use pre-commit hooks, `.pre-commit-config.yaml` is setup for such uses. + +The CI will automatically fix and commit any autofixable issue your changes may have. + +## Pull Request Guidelines + +If your pull request is meant to address an open issue, please link it as part of your Pull Request description. If it would close said issue, please use a [closing keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword). + +Try not to Force Push once your Pull Request is open, unless absolutely necessary. It is easier for reviewers to keep track of reviewed and new changes if you don't. The Pull Request should be squashed-merged anyway. + +Your Pull Request has to pass all checks ot be accepted. If it is still a work-in-progress, please [mark it as draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#converting-a-pull-request-to-a-draft). + +## Coding Standards + +Most coding standards will be enforced by automated tooling. +As time goes on, project-specific standards and "gotchas" in the frameworks we use will be listed here. + +## Testing + +None 😦 Please help us create test suites, we lack the time, but we really want (need!) them. diff --git a/D3DDD-Note-Laptops.md b/docs/D3DDD-Note-Laptops.md similarity index 100% rename from D3DDD-Note-Laptops.md rename to docs/D3DDD-Note-Laptops.md diff --git a/build instructions.md b/docs/build instructions.md similarity index 61% rename from build instructions.md rename to docs/build instructions.md index d40c8317..dc85df98 100644 --- a/build instructions.md +++ b/docs/build instructions.md @@ -17,9 +17,11 @@ ## Install and Build steps -- Read [requirements.txt](/scripts/requirements.txt) for more information on how to install, run and build the python code. - - Run `./scripts/install.ps1` to install all dependencies. - - Run the app directly with `./scripts/start.ps1 [--auto-controlled]`. - - Or debug by pressing `F5` in VSCode - - Run `./scripts/build.ps1` or press `CTRL+Shift+B` in VSCode to build an executable. -- Recompile resources after modifications by running `./scripts/compile_resources.ps1`. +- Run `./scripts/install.ps1` to install all dependencies. + - If you're having issues with the PySide generated code, you might want to first run `pip uninstall -y shiboken6 PySide PySide-Essentials` +- Run the app directly with `./scripts/start.ps1 [--auto-controlled]`. + - Or debug by pressing `F5` in VSCode. + - The `--auto-controlled` flag is passed when AutoSplit is started by LiveSplit. +- Run `./scripts/build.ps1` or press `CTRL+Shift+B` in VSCode to build an executable. +- Optional: Recompile resources after modifications by running `./scripts/compile_resources.ps1`. + - This should be done automatically by other scripts diff --git a/res/mask_example_image.png b/docs/mask_example_image.png similarity index 100% rename from res/mask_example_image.png rename to docs/mask_example_image.png diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 62c64e99..23ff057e 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,13 +1,6 @@ # Requirements file for AutoSplit # -# Python: CPython 3.9+ -# -# Usage: ./scripts/install.ps1 -# -# If you're having issues with the libraries, you might want to first run: -# pip uninstall -y -r ./scripts/requirements-dev.txt -# -# Creating an AutoSplit executable with PyInstaller: ./scripts/build.ps1 +# Read /docs/build%20instructions.md for more information on how to install, run and build the python code. # # Dependencies: certifi diff --git a/src/capture_method/__init__.py b/src/capture_method/__init__.py index e2588702..12067412 100644 --- a/src/capture_method/__init__.py +++ b/src/capture_method/__init__.py @@ -162,7 +162,7 @@ def get(self, __key: CaptureMethodEnum): "\nAbout 10-15x slower than BitBlt. Not affected by window size. " "\nOverlapping windows will show up and can't record across displays. " "\nThis option may not be available for hybrid GPU laptops, " - "\nsee D3DDD-Note-Laptops.md for a solution. " + "\nsee /docs/D3DDD-Note-Laptops.md for a solution. " f"\nhttps://www.github.com/{GITHUB_REPOSITORY}#capture-method " ), implementation=DesktopDuplicationCaptureMethod, From 9cf2e936f55a4daa5c452b42fddf55124b5ee558 Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 24 Apr 2023 19:03:35 -0400 Subject: [PATCH 05/10] Don't request image twice for preview and ensure timers respect FPS limit --- src/AutoSplit.py | 21 ++++++++++++++++----- src/menu_bar.py | 10 +++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/AutoSplit.py b/src/AutoSplit.py index 46243004..ac3d444d 100644 --- a/src/AutoSplit.py +++ b/src/AutoSplit.py @@ -206,8 +206,8 @@ def __init__(self, parent: QWidget | None = None): # pylint: disable=too-many-s self.pause_signal.connect(self.pause) # live image checkbox - self.timer_live_image.timeout.connect(self.__live_image_function) - self.timer_live_image.start(int(1000 / 60)) + self.timer_live_image.timeout.connect(lambda: self.__update_live_image_details(None, True)) + self.timer_live_image.start(int(1000 / self.settings_dict["fps_limit"])) # Automatic timer start self.timer_start_image.timeout.connect(self.__start_image_function) @@ -244,16 +244,26 @@ def __browse(self): self.split_image_folder_input.setText(f"{new_split_image_directory}/") self.load_start_image_signal.emit() - def __live_image_function(self): + def __update_live_image_details(self, capture: cv2.Mat | None, called_from_timer: bool = False): + # HACK: Since this is also called in __get_capture_for_comparison, + # we don't need to update anything if the app is running + if called_from_timer: + if self.is_running or self.start_image: + return + else: + capture, _ = self.capture_method.get_frame(self) + + # Update title from target window or Capture Device name capture_region_window_label = self.settings_dict["capture_device_name"] \ if self.settings_dict["capture_method"] == CaptureMethodEnum.VIDEO_CAPTURE_DEVICE \ else self.settings_dict["captured_window_title"] self.capture_region_window_label.setText(capture_region_window_label) + + # Simply clear if "live capture region" setting is off if not (self.settings_dict["live_capture_region"] and capture_region_window_label): self.live_image.clear() return - # Set live image in UI - capture, _ = self.capture_method.get_frame(self) + set_preview_image(self.live_image, capture, False) def __load_start_image(self, started_by_button: bool = False, wait_for_delay: bool = True): @@ -803,6 +813,7 @@ def __get_capture_for_comparison(self): if recovered: capture, _ = self.capture_method.get_frame(self) + self.__update_live_image_details(capture) return capture, is_old_image def __reset_if_should(self, capture: cv2.Mat | None): diff --git a/src/menu_bar.py b/src/menu_bar.py index fe980639..fa6a3c6e 100644 --- a/src/menu_bar.py +++ b/src/menu_bar.py @@ -176,6 +176,12 @@ def __capture_device_changed(self): # Re-initializes the VideoCaptureDeviceCaptureMethod change_capture_method(CaptureMethodEnum.VIDEO_CAPTURE_DEVICE, self.autosplit) + def __fps_limit_changed(self, value: int): + value = self.fps_limit_spinbox.value() + self.autosplit.settings_dict["fps_limit"] = value + self.autosplit.timer_live_image.setInterval(int(1000 / value)) + self.autosplit.timer_live_image.setInterval(int(1000 / value)) + @fire_and_forget def __set_all_capture_devices(self): self.__video_capture_devices = asyncio.run(get_all_video_capture_devices()) @@ -293,9 +299,7 @@ def hotkey_connect(hotkey: Hotkey): # endregion # region Binding # Capture Settings - self.fps_limit_spinbox.valueChanged.connect( - lambda: self.__set_value("fps_limit", self.fps_limit_spinbox.value()), - ) + self.fps_limit_spinbox.valueChanged.connect(self.__fps_limit_changed) self.live_capture_region_checkbox.stateChanged.connect( lambda: self.__set_value("live_capture_region", self.live_capture_region_checkbox.isChecked()), ) From 790a4fdf3b82cb62c98fb06a9f3c77f877eb407b Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 24 Apr 2023 19:08:28 -0400 Subject: [PATCH 06/10] Don't accidentally round up numbers in viewer --- src/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/utils.py b/src/utils.py index f9ecc55b..137d7fbd 100644 --- a/src/utils.py +++ b/src/utils.py @@ -28,9 +28,8 @@ def decimal(value: int | float): - # NOTE: The coeficient (1000) has to be above what's mathematically necessary (100) - # because of python float rounding errors (ie: xx.99999999999999) - return f"{int(value * 1000) / 1000:.2f}" + # Using ljust instead of :2f because of python float rounding errors + return f"{int(value * 100) / 100}".ljust(4, "0") def is_digit(value: str | int | None): From cb1c98d2b54c6bebef16c8b947f0a641f9a914b0 Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 24 Apr 2023 19:14:38 -0400 Subject: [PATCH 07/10] Fix pHash returning 0 on a perfect match --- src/compare.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/compare.py b/src/compare.py index cac03ec1..4faec749 100644 --- a/src/compare.py +++ b/src/compare.py @@ -102,8 +102,6 @@ def compare_phash(source: cv2.Mat, capture: cv2.Mat, mask: cv2.Mat | None = None source_hash = imagehash.phash(Image.fromarray(source)) capture_hash = imagehash.phash(Image.fromarray(capture)) hash_diff = source_hash - capture_hash - if not hash_diff: - return 0.0 return 1 - (hash_diff / 64.0) From 11b9a9143041aa3f17bb479089525de64a0bf8f8 Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 24 Apr 2023 19:36:25 -0400 Subject: [PATCH 08/10] Remove redundant and incorrect loop_splits check --- src/AutoSplit.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/AutoSplit.py b/src/AutoSplit.py index ac3d444d..651f045a 100644 --- a/src/AutoSplit.py +++ b/src/AutoSplit.py @@ -516,10 +516,7 @@ def __check_for_reset_state_update_ui(self): Check if AutoSplit is started, if not either restart (loop splits) or update the GUI """ if not self.is_running: - if self.settings_dict["loop_splits"]: - self.start_auto_splitter_signal.emit() - else: - self.gui_changes_on_reset(True) + self.gui_changes_on_reset(True) return True return False From b3150d62a0bc6334297b7bae92879edd0b7ef737 Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 24 Apr 2023 19:48:50 -0400 Subject: [PATCH 09/10] Support dummy flag for start image #222 --- src/AutoSplit.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/AutoSplit.py b/src/AutoSplit.py index 651f045a..b15c3f2f 100644 --- a/src/AutoSplit.py +++ b/src/AutoSplit.py @@ -347,23 +347,24 @@ def __start_image_function(self): self.timer_start_image.stop() self.split_below_threshold = False - # delay start image if needed - if self.start_image.get_delay_time(self) > 0: - self.start_image_status_value_label.setText("delaying start...") - delay_start_time = time() - start_delay = self.start_image.get_delay_time(self) / 1000 - time_delta = 0 - while time_delta < start_delay: - delay_time_left = start_delay - time_delta - self.current_split_image.setText( - f"Delayed Before Starting:\n {seconds_remaining_text(delay_time_left)}", - ) - # Wait 0.1s. Doesn't need to be shorter as we only show 1 decimal - QTest.qWait(100) - time_delta = time() - delay_start_time + if not self.start_image.check_flag(DUMMY_FLAG): + # Delay start image if needed + if self.start_image.get_delay_time(self) > 0: + self.start_image_status_value_label.setText("delaying start...") + delay_start_time = time() + start_delay = self.start_image.get_delay_time(self) / 1000 + time_delta = 0 + while time_delta < start_delay: + delay_time_left = start_delay - time_delta + self.current_split_image.setText( + f"Delayed Before Starting:\n {seconds_remaining_text(delay_time_left)}", + ) + # Wait 0.1s. Doesn't need to be shorter as we only show 1 decimal + QTest.qWait(100) + time_delta = time() - delay_start_time + send_command(self, "start") self.start_image_status_value_label.setText("started") - send_command(self, "start") self.start_auto_splitter() # update x, y, width, height when spinbox values are changed From d273ca343ff8113e74a7f4b08ee8cbc6b27ba7ff Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 24 Apr 2023 19:56:05 -0400 Subject: [PATCH 10/10] Remove "Auto Start On Reset" from doc as it no longer exists --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 3d8c2504..6fd98ffd 100644 --- a/README.md +++ b/README.md @@ -139,11 +139,6 @@ If this option is enabled, when the last split meets the threshold and splits, A If this option is disabled, when the last split meets the threshold and splits, AutoSplit will stop running comparisons. This option does not loop single, specific images. See the Custom Split Image Settings section above for this feature. -#### Auto Start On Reset - -If this option is enabled, when the reset hotkey is hit, the reset button is pressed, or the reset split image meets its threshold, AutoSplit will reset and automatically start again back at the first split image. -If this option is disabled, when the reset hotkey is hit, the reset button is pressed, or the reset split image meets its threshold, AutoSplit will stop running comparisons. - ### Custom Split Image Settings - Each split image can have different thresholds, pause times, delay split times, loop amounts, and can be flagged.