├── .gitignore ├── command_wizard.talon ├── docs └── video-demo-thumb.png ├── command_wizard_open.talon ├── TODO ├── LICENSE ├── marker_ui_open.talon ├── marker_ui_actions.py ├── README.md ├── ui_widgets.py ├── marker_ui.py ├── blob_detector.py ├── command_wizard.py ├── mouse_helper.py └── overlays.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | -------------------------------------------------------------------------------- /command_wizard.talon: -------------------------------------------------------------------------------- 1 | command wizard show: user.command_wizard_show() 2 | -------------------------------------------------------------------------------- /docs/video-demo-thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splondike/talon_ui_helper/HEAD/docs/video-demo-thumb.png -------------------------------------------------------------------------------- /command_wizard_open.talon: -------------------------------------------------------------------------------- 1 | tag: user.command_wizard_showing 2 | - 3 | choose : user.command_wizard_choose_option(number_small) 4 | command wizard hide: user.command_wizard_hide() 5 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * GUI macro mode, perhaps saves the macro as a talon file in a given location. 2 | * Voice command to open the most likely .talon file that you want to add a voice command to (based on the title). 3 | * Keyboard commands to adjust the minimum spacing in the blob detector 4 | * Keyboard commands to adjust the thresholding in the image selector 5 | * Action to attempt to automatically remove unused templates in the image_templates directory by parsing usage from .talon files. 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Stefan Schneider 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /marker_ui_open.talon: -------------------------------------------------------------------------------- 1 | tag: user.marker_ui_showing 2 | - 3 | marker hide: 4 | user.marker_ui_hide() 5 | 6 | jump : 7 | user.marker_ui_mouse_move(marker_ui_label) 8 | user.marker_ui_hide() 9 | 10 | touch : 11 | user.marker_ui_mouse_move(marker_ui_label) 12 | mouse_click(0) 13 | user.marker_ui_hide() 14 | 15 | touch restore: 16 | user.mouse_helper_position_save() 17 | user.marker_ui_mouse_move(marker_ui_label) 18 | mouse_click(0) 19 | user.marker_ui_hide() 20 | user.mouse_helper_position_restore() 21 | 22 | righty : 23 | user.marker_ui_mouse_move(marker_ui_label) 24 | mouse_click(1) 25 | user.marker_ui_hide() 26 | 27 | midclick : 28 | user.marker_ui_mouse_move(marker_ui_label) 29 | mouse_click(2) 30 | user.marker_ui_hide() 31 | 32 | # Versions that don't close the overlay 33 | 34 | jump more: 35 | user.marker_ui_mouse_move(marker_ui_label) 36 | 37 | touch more: 38 | user.marker_ui_mouse_move(marker_ui_label) 39 | mouse_click(0) 40 | 41 | touch more restore: 42 | user.mouse_helper_position_save() 43 | user.marker_ui_mouse_move(marker_ui_label) 44 | mouse_click(0) 45 | user.mouse_helper_position_restore() 46 | 47 | righty more: 48 | user.marker_ui_mouse_move(marker_ui_label) 49 | mouse_click(1) 50 | 51 | midclick more: 52 | user.marker_ui_mouse_move(marker_ui_label) 53 | mouse_click(2) 54 | -------------------------------------------------------------------------------- /marker_ui_actions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Exposes the Marker UI and associated functionality as actions. 3 | """ 4 | 5 | from typing import List 6 | 7 | from talon import Module, Context, actions, settings 8 | from talon.types import Rect as TalonRect 9 | 10 | from .marker_ui import MarkerUi 11 | 12 | 13 | mod = Module() 14 | mod.tag("marker_ui_showing", desc="The marker UI labels are showing") 15 | setting_labels = mod.setting( 16 | "marker_ui_labels", 17 | type=str, 18 | desc="Space separated labels to use in the marker UI. See also marker_ui_label capture if overwriting.", 19 | default=" ".join("abcdefghijklmnopqrstuvwxyz0123456789") 20 | ) 21 | 22 | ctx = Context() 23 | 24 | marker_ui = None 25 | 26 | 27 | @mod.capture(rule=" | ") 28 | def marker_ui_label(m) -> str: 29 | """ 30 | Capture for the labels used in the marker UI. See also marker_ui_labels setting if you want 31 | to override it. 32 | """ 33 | 34 | return str(m) 35 | 36 | 37 | @mod.action_class 38 | class MarkerUiActions: 39 | """ 40 | Actions related to showing, hiding, and using the marker UI interface. 41 | """ 42 | 43 | def marker_ui_show(rects: List[TalonRect]): 44 | """ 45 | Shows the given markers in the Marker UI. They can then be clicked or moved 46 | to using other actions in this class. 47 | """ 48 | 49 | global marker_ui 50 | 51 | if marker_ui is not None: 52 | marker_ui.destroy() 53 | 54 | markers = [ 55 | MarkerUi.Marker(rect, label) 56 | for rect, label in zip( 57 | rects, settings.get("user.marker_ui_labels").split(" ") 58 | ) 59 | ] 60 | 61 | marker_ui = MarkerUi(markers) 62 | 63 | marker_ui.show() 64 | ctx.tags = ["user.marker_ui_showing"] 65 | 66 | def marker_ui_hide(): 67 | """ 68 | Hides any visible marker UI 69 | """ 70 | 71 | global marker_ui 72 | 73 | if marker_ui is not None: 74 | marker_ui.destroy() 75 | 76 | marker_ui = None 77 | ctx.tags = [] 78 | 79 | def marker_ui_mouse_move(label: str): 80 | """ 81 | Moves the mouse cursor to the label corresponding to the given label 82 | """ 83 | 84 | global marker_ui 85 | 86 | if marker_ui is None: 87 | return 88 | 89 | rect = marker_ui.find_rect(label) 90 | 91 | if rect is None: 92 | return 93 | 94 | actions.mouse_move( 95 | rect.x + rect.width / 2, 96 | rect.y + rect.height / 2, 97 | ) 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a project related to [Talon](https://talonvoice.com/). It is a set of utility actions and a command generation wizard UI aimed at making it much easier to build new mouse based voice commands for applications. 2 | 3 | # Overview of features 4 | 5 | There are several features provided by this package: 6 | 7 | 1. A set of actions in `mouse_helper.py` that make it easier to build mouse based commands in Talon. For example there are actions to save and restore the mouse position, to move the mouse cursor relative to the active window, or to find a saved image on the screen. 8 | 2. A UI for indicating positions on the screen in `marker_ui.py` and voice commands to move the cursor to them or click them. 9 | 3. A voice command wizard in `command_wizard.py` which lets you more easily build voice commands based on the above features using a GUI. 10 | 11 | # Usage 12 | 13 | You can use the exposed actions directly if you like, otherwise you most likely want to use the command wizard. The video linked below shows an overview of how to do this. 14 | 15 | [![Demonstration video](docs/video-demo-thumb.png)](https://talon-ui-helper.s3.ap-southeast-2.amazonaws.com/talon-ui-helper-demo.webm) 16 | 17 | The video shows the following: 18 | 19 | 1. The GUI can be brought up by saying "command wizard show". This then presents a menu of voice command templates you can build. The button text in the menu is also mapped as a voice command (e.g. "choose 1"). 20 | 2. After selecting one of the menu options you are then shown a full screen overlay allowing you to select the relevant region of the screen to operate on. The overlay can be interracted with via the mouse or the keyboard shortcuts displayed on the screen. The mouse grid built in to knausj\_talon works quite well here also. 21 | 3. After confirming your selection with the enter key a command is added to your clipboard which you can then paste directly in to a .talon file. After that you only need to choose the actual voice command you want to use to trigger it. 22 | 23 | ## Available builders 24 | 25 | * Single image selection. This builder will allow you to choose a region of the screen to save as an image. This image will be clicked by your voice command. 26 | * Multi image selection. This builder draws a label on each of the matches it finds allowing you to move to the image in question using the 'jump