def connect_to_signal(signal: pyqtSignal, callbacks): if not callbacks: return if callable(callbacks): callbacks = [callbacks] for callback in callbacks: signal.connect(callback)
def connect(signal: pyqtSignal, callback: Callable): """ By default calling ``signal.connect(callback)`` will dispose of context information. Practically, this leads to single line tracebacks when ``signal.emit()`` is invoked. This is very hard to debug. This function wraps the ``connect()`` call to give you additional traceback information, if the callback does crash. :param signal: the signal to ``connect()`` to. :param callback: the callback to connect (will be called after ``signal.emit(...)``. """ # Step 1: At time of calling this function: get the stack frames. # We reconstruct the stack as a ``traceback`` object. source = None for frame in list(inspect.stack())[1:]: source = types.TracebackType(source, frame.frame, frame.index or 0, frame.lineno) # Step 2: construct a lightweight StackSummary object which does not contain # actual frames or locals, to avoid memory leak try: summary = traceback.StackSummary.extract(traceback.walk_tb(source), capture_locals=False) finally: del source # Step 3: Wrap the ``callback`` and inject our creation stack if an error occurs. # The BaseException instead of Exception is intentional: this makes sure interrupts of infinite loops show # the source callback stack for debugging. def trackback_wrapper(*args, **kwargs): try: callback(*args, **kwargs) except BaseException as exc: traceback_str = '\n' + ''.join(summary.format()) raise exc from CreationTraceback(traceback_str) try: setattr(callback, "tb_wrapper", trackback_wrapper) except AttributeError: # This is not a free function, but either an external library or a method bound to an instance. if not hasattr(callback, "tb_wrapper") and hasattr( callback, "__self__") and hasattr(callback, "__func__"): # methods are finicky: you can't set attributes on them. # Instead, we inject the handlers for each method in a dictionary on the instance. bound_obj = callback.__self__ if not hasattr(bound_obj, "tb_mapping"): setattr(bound_obj, "tb_mapping", {}) bound_obj.tb_mapping[ callback.__func__.__name__] = trackback_wrapper else: logging.warning( "Unable to hook up connect() info to %s. Probably a 'builtin_function_or_method'.", repr(callback)) # Step 3: Connect our wrapper to the signal. signal.connect(trackback_wrapper)
def __init__(self, conditions: List[SelectionCondition], control: QWidget, signal: pyqtSignal, action: Callable, activate_action: Callable = None, deactivate_action: Callable = None, model_change_handler: Callable = None): self.__conditions = conditions self.__control = control self.__action = action self.__activate_action = activate_action self.__deactivate_action = deactivate_action self.__model_change_handler = model_change_handler signal.connect(lambda *args: action(self._selection, *args)) self.update_selection([])
def __init__(self, settings: Settings, db: DB, on_create: Callable, on_date_click: Callable, tasks_changed_event: qtc.pyqtSignal, factory: TaskWidgetFactory): super().__init__() tasks_changed_event.connect(self._redraw_tasks) self._db = db self._factory = factory self._settings = settings main_layout = qtw.QVBoxLayout() self.setLayout(main_layout) self._current_date = date.today() self._date_button = qtw.QPushButton(clicked=on_date_click) font = self._date_button.font() font.setPixelSize(20) self._date_button.setFont(font) self._update_date_label() tasks_scroll_area = qtw.QScrollArea(widgetResizable=True) tasks_scroll_area_content = qtw.QWidget() tasks_scroll_area.setWidget(tasks_scroll_area_content) self._tasks_layout = qtw.QVBoxLayout(tasks_scroll_area_content) self._tasks_layout.setAlignment(qtc.Qt.AlignTop) self._prev_day_button = qtw.QPushButton( text=settings.PREVIOUS_DAY_BUTTON_TEXT, clicked=lambda: self._change_day(-1)) self._create_task_button = qtw.QPushButton( text=settings.CREATE_TASK_BUTTON_TEXT, clicked=on_create) self._next_day_button = qtw.QPushButton( text=settings.NEXT_DAY_BUTTON_TEXT, clicked=lambda: self._change_day(1)) buttons_layout = qtw.QHBoxLayout() buttons_layout.addWidget(self._prev_day_button) buttons_layout.addWidget(self._create_task_button) buttons_layout.addWidget(self._next_day_button) main_layout.addWidget(self._date_button) main_layout.addWidget(tasks_scroll_area) main_layout.addLayout(buttons_layout)
def is_connected(signal: pyqtSignal, slot: pyqtSlot) -> bool: """ Determine if a (bound) signal is connected to a slot. :param signal: signal (a return value from a call to pyqtSignal(...)) :param slot: slot (a method on a QObject, decorated with pyqtSlot) :return: True if connected, False otherwise """ try: signal.connect(slot, Qt.UniqueConnection) except TypeError as exc: assert str(exc) in ( 'connection is not unique', 'connect() failed between MyObj.sig_test[] and meth()') return True else: signal.disconnect(slot) return False