Add global delay settings to Scenario and MainWindow for customizable action delays

This commit is contained in:
2025-08-07 13:38:18 +02:00
parent 506a6d59c8
commit d0a9aee619
+109 -5
View File
@@ -650,25 +650,39 @@ class Scenario:
Represents a scenario, which consists of a name and a list of steps. Represents a scenario, which consists of a name and a list of steps.
Handles saving/loading scenario data to/from disk. Handles saving/loading scenario data to/from disk.
""" """
def __init__(self, name, steps=None): def __init__(self, name, steps=None, global_delay=None):
""" """
Initialize a Scenario with a name and optional steps. Initialize a Scenario with a name, optional steps, and global delay settings.
""" """
self.name = name self.name = name
self.steps = steps or [] # List of Step dicts self.steps = steps or [] # List of Step dicts
# Global delay settings - delays in milliseconds
self.global_delay = global_delay or {
'before_action': 0, # Delay before each action in ms
'after_action': 0 # Delay after each action in ms
}
def to_dict(self): def to_dict(self):
""" """
Return a dictionary representation of the scenario. Return a dictionary representation of the scenario.
""" """
return {'name': self.name, 'steps': self.steps} return {
'name': self.name,
'steps': self.steps,
'global_delay': self.global_delay
}
@staticmethod @staticmethod
def from_dict(data): def from_dict(data):
""" """
Create a Scenario object from a dictionary. Create a Scenario object from a dictionary.
""" """
return Scenario(data['name'], data.get('steps', [])) # Handle backward compatibility for scenarios without global_delay
global_delay = data.get('global_delay', {
'before_action': 0,
'after_action': 0
})
return Scenario(data['name'], data.get('steps', []), global_delay)
def get_scenario_dir(self): def get_scenario_dir(self):
""" """
@@ -1454,10 +1468,19 @@ class MainWindow(QtWidgets.QMainWindow):
def _perform_step_action(self, action, loc, shape): def _perform_step_action(self, action, loc, shape):
""" """
Perform a single step action (click, key, scroll, delay) at the given location. Perform a single step action (click, key, scroll, delay) at the given location.
Applies global delays before and after each action.
""" """
act_type = action['type'] act_type = action['type']
params = action['params'] params = action['params']
logger.debug(f'Performing step action: {act_type}, params={params}, loc={loc}, shape={shape}') logger.debug(f'Performing step action: {act_type}, params={params}, loc={loc}, shape={shape}')
# Apply global before-action delay
if self.current_scenario and self.current_scenario.global_delay:
before_delay = self.current_scenario.global_delay.get('before_action', 0)
if before_delay > 0:
logger.debug(f'Applying global before-action delay: {before_delay}ms')
time.sleep(before_delay / 1000.0)
try: try:
if act_type == 'click': if act_type == 'click':
pos_type = params.get('pos_type', 'center') pos_type = params.get('pos_type', 'center')
@@ -1493,6 +1516,13 @@ class MainWindow(QtWidgets.QMainWindow):
time.sleep(duration) time.sleep(duration)
except Exception as e: except Exception as e:
logger.error(f'Error performing step action {act_type}: {e}') logger.error(f'Error performing step action {act_type}: {e}')
# Apply global after-action delay
if self.current_scenario and self.current_scenario.global_delay:
after_delay = self.current_scenario.global_delay.get('after_action', 0)
if after_delay > 0:
logger.debug(f'Applying global after-action delay: {after_delay}ms')
time.sleep(after_delay / 1000.0)
def set_state(self, text): def set_state(self, text):
""" """
Update the state label in the UI. Update the state label in the UI.
@@ -3048,6 +3078,39 @@ class MainWindow(QtWidgets.QMainWindow):
self.combo.currentIndexChanged.connect(self._log_combo_scenario) self.combo.currentIndexChanged.connect(self._log_combo_scenario)
scenario_group_layout.addWidget(self.combo) scenario_group_layout.addWidget(self.combo)
# Global delay settings
global_delay_layout = QtWidgets.QHBoxLayout()
global_delay_layout.setSpacing(8)
global_delay_layout.setContentsMargins(0, 5, 0, 5)
# Before action delay
before_label = QtWidgets.QLabel('Before Action (ms):')
before_label.setMinimumWidth(100)
self.before_action_spinbox = QtWidgets.QSpinBox()
self.before_action_spinbox.setRange(0, 10000)
self.before_action_spinbox.setValue(0)
self.before_action_spinbox.setSuffix(' ms')
self.before_action_spinbox.setToolTip('Delay before each action in milliseconds')
self.before_action_spinbox.valueChanged.connect(self._on_global_delay_changed)
# After action delay
after_label = QtWidgets.QLabel('After Action (ms):')
after_label.setMinimumWidth(95)
self.after_action_spinbox = QtWidgets.QSpinBox()
self.after_action_spinbox.setRange(0, 10000)
self.after_action_spinbox.setValue(0)
self.after_action_spinbox.setSuffix(' ms')
self.after_action_spinbox.setToolTip('Delay after each action in milliseconds')
self.after_action_spinbox.valueChanged.connect(self._on_global_delay_changed)
global_delay_layout.addWidget(before_label)
global_delay_layout.addWidget(self.before_action_spinbox)
global_delay_layout.addWidget(after_label)
global_delay_layout.addWidget(self.after_action_spinbox)
global_delay_layout.addStretch()
scenario_group_layout.addLayout(global_delay_layout)
# Window selection dropdown # Window selection dropdown
self.window_combo = QtWidgets.QComboBox() self.window_combo = QtWidgets.QComboBox()
self.window_combo.setMinimumWidth(90) self.window_combo.setMinimumWidth(90)
@@ -3288,6 +3351,23 @@ class MainWindow(QtWidgets.QMainWindow):
logger.info(f"UI: Scenario combo changed to index {idx} ({self.combo.currentText()})") logger.info(f"UI: Scenario combo changed to index {idx} ({self.combo.currentText()})")
self.select_scenario() self.select_scenario()
def _on_global_delay_changed(self):
"""
Handle changes to global delay settings and auto-save scenario.
"""
if self.current_scenario:
before_delay = self.before_action_spinbox.value()
after_delay = self.after_action_spinbox.value()
self.current_scenario.global_delay = {
'before_action': before_delay,
'after_action': after_delay
}
# Auto-save the scenario with new delay settings
self.current_scenario.save()
logger.info(f"Global delay updated: before={before_delay}ms, after={after_delay}ms")
def _log_combo_window(self, idx): def _log_combo_window(self, idx):
""" """
Log window combo box change and call save_selected_window. Log window combo box change and call save_selected_window.
@@ -3984,11 +4064,35 @@ class MainWindow(QtWidgets.QMainWindow):
def refresh_lists(self): def refresh_lists(self):
""" """
Refresh the step list widget with the current scenario's steps. Refresh the step list widget with the current scenario's steps.
Also updates global delay controls.
""" """
logger.debug('Refreshing steps list.') logger.debug('Refreshing steps list.')
self.steps_list.clear() self.steps_list.clear()
if not self.current_scenario:
# Update global delay controls
if self.current_scenario:
# Temporarily disconnect signals to prevent triggering auto-save during loading
self.before_action_spinbox.valueChanged.disconnect()
self.after_action_spinbox.valueChanged.disconnect()
# Load global delay settings
global_delay = self.current_scenario.global_delay
self.before_action_spinbox.setValue(global_delay.get('before_action', 0))
self.after_action_spinbox.setValue(global_delay.get('after_action', 0))
# Reconnect signals
self.before_action_spinbox.valueChanged.connect(self._on_global_delay_changed)
self.after_action_spinbox.valueChanged.connect(self._on_global_delay_changed)
else:
# Reset to default values when no scenario is selected
self.before_action_spinbox.valueChanged.disconnect()
self.after_action_spinbox.valueChanged.disconnect()
self.before_action_spinbox.setValue(0)
self.after_action_spinbox.setValue(0)
self.before_action_spinbox.valueChanged.connect(self._on_global_delay_changed)
self.after_action_spinbox.valueChanged.connect(self._on_global_delay_changed)
return return
for step in self.current_scenario.steps: for step in self.current_scenario.steps:
name = step.get('name', 'Unnamed Step') name = step.get('name', 'Unnamed Step')
cond = step.get('condition', 'OR') cond = step.get('condition', 'OR')