Source code for twinpy.ui.tc_base

"""This file defines abstract and helper classes for TcWidgets.
"""

from typing import Optional, Any
from abc import ABCMeta
import os
from PyQt5.QtWidgets import QWidget, QMessageBox, QApplication
from PyQt5.QtCore import QTimer
from pyads import ADSError

from ..element.tc_element import TcElement, TcTimer
from ..twincat.symbols import Symbol


PACKAGE_DIR = os.path.abspath(os.path.dirname(__file__) + "/../..")


class QABCMeta(ABCMeta, type(QWidget)):
    """Create a meta class that combines ABC and the Qt meta class."""


[docs]class TcWidget(TcElement, metaclass=QABCMeta): """Abstract class, to be multi-inherited together with a Qt item.""" def __init__(self, *args, **kwargs): """ It is important to call this init() as late as possible from a subclass! The order should be: 1. Subclass specific stuff (e.g. number formatting) 2. Call to super().__init__(...) - Now the QWidget stuff has been made ready 3. QWidget related stuff :param args: :param kwargs: See :meth:`TcElement.__init__` """ super().__init__(*args, **kwargs) # Disable widget if needed if self.greyed and self._symbol is None: self.setDisabled(True)
[docs] def connect_symbol(self, new_symbol: Optional[Symbol] = None, **kwargs) -> bool: """Connect a symbol (copy is left as property). :param new_symbol: Symbol to link to (set None to only clear the previous) :param kwargs: See :meth:`TcElement.connect_symbol` """ connected = super().connect_symbol(new_symbol, **kwargs) if self.greyed: self.setDisabled(not connected) return connected
[docs] def twincat_send(self, value: Any): """Set value in symbol (and send to TwinCAT). Method is safe: if symbol is not connected, nothing will happen. """ try: super().twincat_send(value) except ADSError as err: TcWidget.close_with_error(err)
[docs] @staticmethod def close_with_error(err: Exception): """Show an error popup and close the active application. This uses `QApplication.instance()` to find the current application and won't work perfectly. """ print(err) msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("ADS Error") msg.setInformativeText(str(err)) msg.setWindowTitle("ADS Error") msg.exec_() app = QApplication.instance() if app is not None: app.exit(1)
[docs] @staticmethod def make_timer(plc, interval) -> TcTimer: """Use QTimer based timer instead.""" return TcQTimer(plc, interval)
class TcQTimer(TcTimer): """TwinCAT timer object based on the PyQt QTimer.""" def make_timer(self) -> QTimer: self._timer = QTimer() self._timer.setInterval(self.interval) self._timer.timeout.connect(self.on_timeout) # `on_timeout` has no @pyqtSlot, but since TcTimer is no QObject that's # necessary return self._timer def on_error(self, error: ADSError): TcWidget.close_with_error(error) def start(self): """Start underlying timer""" self._timer.start() self._is_active = True def stop(self): """Stop the underlying timer""" self._timer.stop() self._is_active = False