Went bug huntin' Squished a bunch of 'em little critters.
This commit is contained in:
@@ -76,7 +76,8 @@ class ActionList(Gtk.Window):
|
||||
self.set_skip_taskbar_hint(True)
|
||||
self.set_skip_pager_hint(True)
|
||||
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
|
||||
self.set_window_position(Gtk.WindowPosition.MOUSE)
|
||||
# Note: set_window_position is deprecated, using move() instead
|
||||
self.move(100, 100) # Position window at reasonable location
|
||||
self.set_default_size(400, 300)
|
||||
|
||||
# Create scrolled window
|
||||
@@ -158,9 +159,9 @@ class ActionPresenter:
|
||||
self._obj = None
|
||||
self._window = None
|
||||
|
||||
# Initialize handlers and bindings
|
||||
# Initialize handlers
|
||||
self._handlers = self.get_handlers(True)
|
||||
self._bindings = keybindings.KeyBindings()
|
||||
# _bindings will be initialized lazily in get_bindings()
|
||||
|
||||
def get_handlers(self, refresh: bool = False) -> dict:
|
||||
"""Returns a dictionary of input event handlers."""
|
||||
@@ -207,7 +208,7 @@ class ActionPresenter:
|
||||
self._bindings = keybindings.KeyBindings()
|
||||
self._bindings.add(keybindings.KeyBinding(
|
||||
"a",
|
||||
keybindings.DEFAULT_MODIFIER_MASK,
|
||||
keybindings.defaultModifierMask,
|
||||
keybindings.CTHULHU_SHIFT_MODIFIER_MASK,
|
||||
self._handlers["show_actions_list"]))
|
||||
|
||||
@@ -221,7 +222,7 @@ class ActionPresenter:
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
reason = "Action Presenter list is being destroyed"
|
||||
app = AXUtilities.get_application(self._obj)
|
||||
app = AXObject.get_application(self._obj)
|
||||
script = script_manager.getManager().getScript(app, self._obj)
|
||||
script_manager.getManager().setActiveScript(script, reason)
|
||||
|
||||
@@ -247,48 +248,77 @@ class ActionPresenter:
|
||||
result = AXObject.do_named_action(self._obj, action)
|
||||
tokens = ["ACTION PRESENTER: Performing", action, "on", self._obj, "succeeded:", result]
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
if not result:
|
||||
full_message = messages.ACTION_NOT_PERFORMED % action
|
||||
cthulhu.presentMessage(full_message)
|
||||
|
||||
self._gui.destroy()
|
||||
|
||||
# Use idle_add for asynchronous destruction to allow action to complete
|
||||
GLib.idle_add(self._gui.destroy)
|
||||
|
||||
def present_with_time(self, obj, start_time: float) -> bool:
|
||||
"""Presents accessible actions for the given object with timing."""
|
||||
|
||||
if self._gui is not None:
|
||||
msg = "ACTION PRESENTER: GUI already exists."
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
try:
|
||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: present_with_time called with obj: {obj}", True)
|
||||
|
||||
if self._gui is not None:
|
||||
msg = "ACTION PRESENTER: GUI already exists."
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
return False
|
||||
|
||||
if obj is None:
|
||||
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: obj is None, using locusOfFocus", True)
|
||||
obj = cthulhu_state.locusOfFocus
|
||||
|
||||
if obj is None:
|
||||
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: No object found, presenting NO_ACCESSIBLE_ACTIONS", True)
|
||||
full_message = messages.NO_ACCESSIBLE_ACTIONS
|
||||
cthulhu.presentMessage(full_message)
|
||||
return False
|
||||
|
||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Getting actions for object: {obj}", True)
|
||||
actions = AXObject.get_action_names(obj)
|
||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Found {len(actions) if actions else 0} actions: {actions}", True)
|
||||
|
||||
if not actions:
|
||||
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: No actions found, presenting NO_ACCESSIBLE_ACTIONS", True)
|
||||
full_message = messages.NO_ACCESSIBLE_ACTIONS
|
||||
cthulhu.presentMessage(full_message)
|
||||
return False
|
||||
|
||||
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: Creating GUI", True)
|
||||
self._obj = obj
|
||||
self._window = cthulhu_state.activeWindow
|
||||
|
||||
self._gui = ActionList(self)
|
||||
self._gui.populate_actions(actions)
|
||||
|
||||
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: GUI created successfully", True)
|
||||
return True
|
||||
except Exception as e:
|
||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: ERROR in present_with_time: {e}", True)
|
||||
import traceback
|
||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Traceback: {traceback.format_exc()}", True)
|
||||
return False
|
||||
|
||||
if obj is None:
|
||||
obj = cthulhu_state.locusOfFocus
|
||||
|
||||
if obj is None:
|
||||
full_message = messages.NO_ACCESSIBLE_ACTIONS
|
||||
cthulhu.presentMessage(full_message)
|
||||
return False
|
||||
|
||||
actions = AXObject.get_action_names(obj)
|
||||
if not actions:
|
||||
full_message = messages.NO_ACCESSIBLE_ACTIONS
|
||||
cthulhu.presentMessage(full_message)
|
||||
return False
|
||||
|
||||
self._obj = obj
|
||||
self._window = cthulhu_state.activeWindow
|
||||
|
||||
self._gui = ActionList(self)
|
||||
self._gui.populate_actions(actions)
|
||||
|
||||
return True
|
||||
|
||||
def show_actions_list(self, script: script.Script = None, input_event: input_event.InputEvent = None) -> bool:
|
||||
"""Shows the accessible actions list."""
|
||||
|
||||
start_time = time.time()
|
||||
return self.present_with_time(input_event.get_object(), start_time)
|
||||
try:
|
||||
msg = f"ACTION PRESENTER: show_actions_list called! input_event: {input_event}"
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
# Get object from input event if available, otherwise let present_with_time handle it
|
||||
obj = input_event.getObject() if input_event else None
|
||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Object from input_event: {obj}", True)
|
||||
|
||||
result = self.present_with_time(obj, start_time)
|
||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: show_actions_list returning: {result}", True)
|
||||
return result
|
||||
except Exception as e:
|
||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: ERROR in show_actions_list: {e}", True)
|
||||
import traceback
|
||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Traceback: {traceback.format_exc()}", True)
|
||||
return False
|
||||
|
||||
|
||||
_presenter = ActionPresenter()
|
||||
|
@@ -142,14 +142,14 @@ class APIHelper:
|
||||
bindings.add(binding)
|
||||
|
||||
# Register key grab at the system level
|
||||
grab_ids = self.app.addKeyGrab(binding)
|
||||
grab_ids = addKeyGrab(binding)
|
||||
logger.info(f"Key grab IDs: {grab_ids}")
|
||||
|
||||
# For later removal
|
||||
if grab_ids:
|
||||
binding._grab_ids = grab_ids
|
||||
else:
|
||||
logger.warning("No active script available - binding stored for later registration")
|
||||
debug.printMessage(debug.LEVEL_INFO, "No active script available - binding stored for later registration", True)
|
||||
|
||||
debug.printMessage(debug.LEVEL_INFO, f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}", True)
|
||||
logger.info("=== APIHelper.registerGestureByString completed ===")
|
||||
@@ -674,17 +674,19 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False):
|
||||
_storeXmodmap(_cthulhuModifiers)
|
||||
_createCthulhuXmodmap()
|
||||
|
||||
activePlugins = list(_settingsManager.getSetting('activePlugins'))
|
||||
cthulhuApp.getPluginSystemManager().setActivePlugins(activePlugins)
|
||||
|
||||
# Activate core systems FIRST before loading plugins
|
||||
_scriptManager.activate()
|
||||
_eventManager.activate()
|
||||
|
||||
# Refresh keybindings to include plugin bindings (after script manager is active)
|
||||
cthulhuApp.getPluginSystemManager().refresh_active_script_keybindings()
|
||||
|
||||
cthulhuApp.getSignalManager().emitSignal('load-setting-begin')
|
||||
# cthulhuApp.getPluginSystemManager().register_plugin_keybindings_with_active_script()
|
||||
|
||||
# NOW load plugins after script system is ready
|
||||
activePlugins = list(_settingsManager.getSetting('activePlugins'))
|
||||
debug.printMessage(debug.LEVEL_INFO, f'CTHULHU: Loading active plugins: {activePlugins}', True)
|
||||
cthulhuApp.getPluginSystemManager().setActivePlugins(activePlugins)
|
||||
|
||||
# Refresh keybindings to include plugin bindings (after script manager is active)
|
||||
cthulhuApp.getPluginSystemManager().refresh_active_script_keybindings()
|
||||
|
||||
cthulhuApp.getSignalManager().emitSignal('load-setting-completed')
|
||||
|
||||
|
@@ -135,7 +135,8 @@ class PluginSystemManager:
|
||||
active_script = cthulhu_state.activeScript
|
||||
for binding in bindings.keyBindings:
|
||||
active_script.getKeyBindings().add(binding)
|
||||
grab_ids = self.app.addKeyGrab(binding)
|
||||
from . import cthulhu
|
||||
grab_ids = cthulhu.addKeyGrab(binding)
|
||||
if grab_ids:
|
||||
binding._grab_ids = grab_ids
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
import logging
|
||||
from cthulhu.plugin import Plugin, cthulhu_hookimpl
|
||||
from cthulhu import cthulhuVersion
|
||||
from cthulhu import debug
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -21,8 +22,9 @@ class DisplayVersion(Plugin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize the plugin."""
|
||||
super().__init__(*args, **kwargs)
|
||||
logger.info("DisplayVersion plugin initialized")
|
||||
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin initialized", True)
|
||||
self._kb_binding = None
|
||||
self._activated = False
|
||||
|
||||
@cthulhu_hookimpl
|
||||
def activate(self, plugin=None):
|
||||
@@ -31,32 +33,24 @@ class DisplayVersion(Plugin):
|
||||
if plugin is not None and plugin is not self:
|
||||
return
|
||||
|
||||
try:
|
||||
logger.info("=== DisplayVersion plugin activation starting ===")
|
||||
logger.info(f"Plugin name: {self.name}")
|
||||
logger.info(f"App reference: {self.app}")
|
||||
# Prevent duplicate activation
|
||||
if self._activated:
|
||||
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Already activated, skipping", True)
|
||||
return
|
||||
|
||||
# Also write to a debug file
|
||||
with open('/tmp/displayversion_debug.log', 'a') as f:
|
||||
f.write("=== DisplayVersion plugin activation starting ===\n")
|
||||
f.write(f"Plugin name: {self.name}\n")
|
||||
f.write(f"App reference: {self.app}\n")
|
||||
try:
|
||||
# Use debug.printMessage to ensure it goes to the main debug log
|
||||
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin activation starting", True)
|
||||
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Plugin name: {self.name}", True)
|
||||
|
||||
# Check if we have access to the API helper
|
||||
if not self.app:
|
||||
logger.error("DisplayVersion: No app reference available!")
|
||||
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: ERROR - No app reference available!", True)
|
||||
return
|
||||
|
||||
api_helper = self.app.getAPIHelper()
|
||||
if not api_helper:
|
||||
logger.error("DisplayVersion: No API helper available!")
|
||||
return
|
||||
|
||||
logger.info(f"API helper: {api_helper}")
|
||||
|
||||
# Register keyboard shortcut
|
||||
gesture_string = 'kb:cthulhu+shift+v'
|
||||
logger.info(f"DisplayVersion: Attempting to register gesture: {gesture_string}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Registering gesture: {gesture_string}", True)
|
||||
|
||||
self._kb_binding = self.registerGestureByString(
|
||||
self.speakText,
|
||||
@@ -64,30 +58,17 @@ class DisplayVersion(Plugin):
|
||||
gesture_string
|
||||
)
|
||||
|
||||
logger.info(f"DisplayVersion: Registered keybinding result: {self._kb_binding}")
|
||||
if self._kb_binding:
|
||||
logger.info(f"Binding keysymstring: {self._kb_binding.keysymstring}")
|
||||
logger.info(f"Binding modifiers: {self._kb_binding.modifiers}")
|
||||
logger.info(f"Binding modifier_mask: {self._kb_binding.modifier_mask}")
|
||||
logger.info(f"Binding handler: {self._kb_binding.handler}")
|
||||
|
||||
with open('/tmp/displayversion_debug.log', 'a') as f:
|
||||
f.write(f"SUCCESS: Keybinding created!\n")
|
||||
f.write(f" keysymstring: {self._kb_binding.keysymstring}\n")
|
||||
f.write(f" modifiers: {self._kb_binding.modifiers}\n")
|
||||
f.write(f" modifier_mask: {self._kb_binding.modifier_mask}\n")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Keybinding registered successfully", True)
|
||||
else:
|
||||
logger.error("DisplayVersion: Failed to create keybinding!")
|
||||
with open('/tmp/displayversion_debug.log', 'a') as f:
|
||||
f.write("ERROR: Failed to create keybinding!\n")
|
||||
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: ERROR - Failed to create keybinding!", True)
|
||||
|
||||
logger.info("=== DisplayVersion plugin activation completed ===")
|
||||
with open('/tmp/displayversion_debug.log', 'a') as f:
|
||||
f.write("=== DisplayVersion plugin activation completed ===\n\n")
|
||||
self._activated = True
|
||||
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin activation completed", True)
|
||||
except Exception as e:
|
||||
logger.error(f"Error activating DisplayVersion plugin: {e}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: ERROR activating plugin: {e}", True)
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: {traceback.format_exc()}", True)
|
||||
|
||||
@cthulhu_hookimpl
|
||||
def deactivate(self, plugin=None):
|
||||
@@ -96,12 +77,13 @@ class DisplayVersion(Plugin):
|
||||
if plugin is not None and plugin is not self:
|
||||
return
|
||||
|
||||
logger.info("Deactivating DisplayVersion plugin")
|
||||
self._activated = False
|
||||
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin deactivated", True)
|
||||
|
||||
def speakText(self, script=None, inputEvent=None):
|
||||
"""Speak the Cthulhu version when shortcut is pressed."""
|
||||
try:
|
||||
logger.info("DisplayVersion plugin: speakText called")
|
||||
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: speakText called", True)
|
||||
if self.app:
|
||||
state = self.app.getDynamicApiManager().getAPI('CthulhuState')
|
||||
if state.activeScript:
|
||||
@@ -111,5 +93,5 @@ class DisplayVersion(Plugin):
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error in DisplayVersion speakText: {e}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: ERROR in speakText: {e}", True)
|
||||
return False
|
||||
|
@@ -44,7 +44,8 @@ class IndentationAudio(Plugin):
|
||||
self._tone_duration = 0.15 # Seconds
|
||||
self._max_frequency = 1200 # Cap frequency to avoid harsh sounds
|
||||
|
||||
logger.info("IndentationAudio plugin initialized")
|
||||
self._activated = False
|
||||
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin initialized", True)
|
||||
|
||||
@cthulhu_hookimpl
|
||||
def activate(self, plugin=None):
|
||||
@@ -52,8 +53,13 @@ class IndentationAudio(Plugin):
|
||||
if plugin is not None and plugin is not self:
|
||||
return
|
||||
|
||||
# Prevent duplicate activation
|
||||
if self._activated:
|
||||
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Already activated, skipping", True)
|
||||
return
|
||||
|
||||
try:
|
||||
logger.info("=== IndentationAudio plugin activation starting ===")
|
||||
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin activation starting", True)
|
||||
|
||||
# Initialize sound player
|
||||
if SOUND_AVAILABLE:
|
||||
@@ -69,11 +75,12 @@ class IndentationAudio(Plugin):
|
||||
# Connect to text caret movement events
|
||||
self._connect_to_events()
|
||||
|
||||
logger.info("IndentationAudio plugin activated successfully")
|
||||
self._activated = True
|
||||
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin activated successfully", True)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to activate IndentationAudio plugin: {e}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: ERROR activating plugin: {e}", True)
|
||||
return False
|
||||
|
||||
@cthulhu_hookimpl
|
||||
@@ -83,7 +90,7 @@ class IndentationAudio(Plugin):
|
||||
return
|
||||
|
||||
try:
|
||||
logger.info("=== IndentationAudio plugin deactivation starting ===")
|
||||
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin deactivation starting", True)
|
||||
|
||||
# Disconnect from events
|
||||
self._disconnect_from_events()
|
||||
@@ -91,11 +98,12 @@ class IndentationAudio(Plugin):
|
||||
# Clear tracking data
|
||||
self._last_indentation_level.clear()
|
||||
|
||||
logger.info("IndentationAudio plugin deactivated successfully")
|
||||
self._activated = False
|
||||
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin deactivated successfully", True)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to deactivate IndentationAudio plugin: {e}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: ERROR deactivating plugin: {e}", True)
|
||||
return False
|
||||
|
||||
def _register_keybinding(self):
|
||||
|
@@ -35,8 +35,9 @@ class PluginManager(Plugin):
|
||||
self._kb_binding = None
|
||||
self._dialog = None
|
||||
self._plugin_checkboxes = {}
|
||||
self._activated = False
|
||||
|
||||
logger.info("PluginManager plugin initialized")
|
||||
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin initialized", True)
|
||||
|
||||
@cthulhu_hookimpl
|
||||
def activate(self, plugin=None):
|
||||
@@ -44,17 +45,23 @@ class PluginManager(Plugin):
|
||||
if plugin is not None and plugin is not self:
|
||||
return
|
||||
|
||||
# Prevent duplicate activation
|
||||
if self._activated:
|
||||
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Already activated, skipping", True)
|
||||
return
|
||||
|
||||
try:
|
||||
logger.info("=== PluginManager plugin activation starting ===")
|
||||
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin activation starting", True)
|
||||
|
||||
# Register keybinding for opening plugin manager (Cthulhu+Shift+P)
|
||||
self._register_keybinding()
|
||||
|
||||
logger.info("PluginManager plugin activated successfully")
|
||||
self._activated = True
|
||||
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin activated successfully", True)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to activate PluginManager plugin: {e}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR activating plugin: {e}", True)
|
||||
return False
|
||||
|
||||
@cthulhu_hookimpl
|
||||
@@ -64,25 +71,26 @@ class PluginManager(Plugin):
|
||||
return
|
||||
|
||||
try:
|
||||
logger.info("=== PluginManager plugin deactivation starting ===")
|
||||
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin deactivation starting", True)
|
||||
|
||||
# Close dialog if open
|
||||
if self._dialog:
|
||||
self._dialog.destroy()
|
||||
self._dialog = None
|
||||
|
||||
logger.info("PluginManager plugin deactivated successfully")
|
||||
self._activated = False
|
||||
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin deactivated successfully", True)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to deactivate PluginManager plugin: {e}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR deactivating plugin: {e}", True)
|
||||
return False
|
||||
|
||||
def _register_keybinding(self):
|
||||
"""Register the Cthulhu+Shift+P keybinding for opening plugin manager."""
|
||||
try:
|
||||
if not self.app:
|
||||
logger.error("PluginManager: No app reference available for keybinding")
|
||||
debug.printMessage(debug.LEVEL_INFO, "PluginManager: ERROR - No app reference available for keybinding", True)
|
||||
return
|
||||
|
||||
# Register Cthulhu+Shift+P keybinding
|
||||
@@ -96,13 +104,12 @@ class PluginManager(Plugin):
|
||||
)
|
||||
|
||||
if self._kb_binding:
|
||||
logger.info(f"PluginManager: Registered keybinding {gesture_string}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Registered keybinding {gesture_string}", True)
|
||||
else:
|
||||
logger.error(f"PluginManager: Failed to register keybinding {gesture_string}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR - Failed to register keybinding {gesture_string}", True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"PluginManager: Error registering keybinding: {e}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR registering keybinding: {e}", True)
|
||||
|
||||
def _open_plugin_manager(self, script, inputEvent=None):
|
||||
"""Open the plugin manager dialog."""
|
||||
|
Reference in New Issue
Block a user