Example #1
0
class PieChart(QQuickPaintedItem):
    def __init__(self, parent=None):
        QQuickPaintedItem.__init__(self, parent)
        self._name = u''

    def paint(self, painter):
        pen = QPen(self.color, 2)
        painter.setPen(pen)
        painter.setRenderHints(QPainter.Antialiasing, True)
        painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16,
                        290 * 16)

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def getName(self):
        return self._name

    def setName(self, value):
        self._name = value

    nameChanged = Signal()

    color = Property(QColor, getColor, setColor)
    name = Property(str, getName, setName, notify=nameChanged)
Example #2
0
class PieChart(QQuickPaintedItem):
    def __init__(self, parent=None):
        QQuickPaintedItem.__init__(self, parent)
        self._name = u''

    def paint(self, painter):
        pen = QPen(self.color, 2)
        painter.setPen(pen)
        painter.setRenderHints(QPainter.Antialiasing, True)
        painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16,
                        290 * 16)

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def getName(self):
        return self._name

    def setName(self, value):
        self._name = value

    color = Property(QColor, getColor, setColor)
    name = Property(str, getName, setName)
    chartCleared = Signal()

    @Slot()  # This should be something like @Invokable
    def clearChart(self):
        self.setColor(Qt.transparent)
        self.update()
        self.chartCleared.emit()
Example #3
0
class PieSlice(QQuickPaintedItem):
    def __init__(self, parent=None):
        QQuickPaintedItem.__init__(self, parent)
        self._color = QColor()
        self._fromAngle = 0
        self._angleSpan = 0

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    def getFromAngle(self):
        return self._angle

    def setFromAngle(self, value):
        self._fromAngle = value

    def getAngleSpan(self):
        return self._angleSpan

    def setAngleSpan(self, value):
        self._angleSpan = value

    color = Property(QColor, getColor, setColor)
    fromAngle = Property(int, getFromAngle, setFromAngle)
    angleSpan = Property(int, getAngleSpan, setAngleSpan)

    def paint(self, painter):
        pen = QPen(self._color, 2)
        painter.setPen(pen)
        painter.setRenderHints(QPainter.Antialiasing, True)
        painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1),
                        self._fromAngle * 16, self._angleSpan * 16)
class ClassFilterProxyModel(QSortFilterProxyModel, QObject):
    _changed_front_page_ = Signal(bool)
    _changed_filter_class_ = Signal(str)

    _front_page_ = True
    _filter_class_ = 'all'

    def __init__(self):
        super().__init__()

    def get_front_page(self):
        return self._front_page_

    def set_front_page(self, fp):
        if self._front_page_ != fp:
            self._front_page_ = fp
            self._changed_front_page_.emit(self._front_page_)
            self.invalidateFilter()

    def get_filter_class(self):
        return self._filter_class_

    def set_filter_class(self, new_filter):
        if self._filter_class_ != new_filter:
            # _logger.debug(f'class filter: set_filter_class {new_filter}')
            self._filter_class_ = new_filter
            self._changed_filter_class_.emit(self._filter_class_)
            self.invalidateFilter()

    front_page = Property(bool,
                          get_front_page,
                          set_front_page,
                          notify=_changed_front_page_)
    filter_class = Property(str,
                            get_filter_class,
                            set_filter_class,
                            notify=_changed_filter_class_)

    def filterAcceptsRow(self, source_row, source_parent: QModelIndex):
        index = self.sourceModel().index(source_row, 0, source_parent)
        if self.front_page:
            return True
        else:
            if self._filter_class_ == 'all':
                return True
            else:
                device_class_id = index.data(DeviceRoles.DEVICE_CLASS_ID)
                cb_filter = device_class_id == self._filter_class_
                return cb_filter
Example #5
0
class OptionalCurrencyComboBox(QWidget):
    changed = Signal()
    name_updated = Signal(str)

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self._id = 0

        self.layout = QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.null_flag = QCheckBox(parent)
        self.null_flag.setChecked(False)
        self.null_flag.setText(self.tr("Currency"))
        self.layout.addWidget(self.null_flag)
        self.currency = CurrencyComboBox(parent)
        self.currency.setEnabled(False)
        self.layout.addWidget(self.currency)
        self.setLayout(self.layout)

        self.setFocusProxy(self.null_flag)
        self.null_flag.clicked.connect(self.onClick)
        self.currency.changed.connect(self.onCurrencyChange)

    def setText(self, text):
        self.null_flag.setText(text)

    def getId(self):
        return self._id if self._id else None

    def setId(self, new_value):
        if self._id == new_value:
            return
        self._id = new_value
        self.updateView()
        name = JalDB().get_asset_name(self._id)
        self.name_updated.emit('' if name is None else name)

    currency_id = Property(int, getId, setId, notify=changed, user=True)

    def updateView(self):
        has_value = True if self._id else False
        if has_value:
            self.currency.selected_id = self._id
        self.null_flag.setChecked(has_value)
        self.currency.setEnabled(has_value)

    @Slot()
    def onClick(self):
        if self.null_flag.isChecked():
            if self.currency.selected_id == 0:
                self.currency.selected_id = JalSettings().getValue('BaseCurrency')
            self.currency_id = self.currency.selected_id
        else:
            self.currency_id = 0
        self.changed.emit()

    @Slot()
    def onCurrencyChange(self, _id):
        self.currency_id = self.currency.selected_id
        self.changed.emit()
Example #6
0
def ConstProperty(classvars, typename, name):
    '''
        This function adds a QProperty named 'name' to a class's vars() dictionary.
        It create the getter.
        *Important* a member variable named '_name' will be expected by the getter.
        A QProperty is exposed to QML.
    '''
    goc_member_variable(classvars, name)
    classvars[f'{name}'] = Property(typename, Getter(name), constant = True)
Example #7
0
class OrbitTransformController(QObject):
    def __init__(self, parent):
        super(OrbitTransformController, self).__init__(parent)
        self._target = None
        self._matrix = QMatrix4x4()
        self._radius = 1
        self._angle = 0

    def setTarget(self, t):
        self._target = t

    def getTarget(self):
        return self._target

    def setRadius(self, radius):
        if self._radius != radius:
            self._radius = radius
            self.updateMatrix()
            self.radiusChanged.emit()

    def getRadius(self):
        return self._radius

    def setAngle(self, angle):
        if self._angle != angle:
            self._angle = angle
            self.updateMatrix()
            self.angleChanged.emit()

    def getAngle(self):
        return self._angle

    def updateMatrix(self):
        self._matrix.setToIdentity()
        self._matrix.rotate(self._angle, QVector3D(0, 1, 0))
        self._matrix.translate(self._radius, 0, 0)
        if self._target is not None:
            self._target.setMatrix(self._matrix)

    angleChanged = Signal()
    radiusChanged = Signal()
    angle = Property(float, getAngle, setAngle, notify=angleChanged)
    radius = Property(float, getRadius, setRadius, notify=radiusChanged)
Example #8
0
def RWProperty(classvars, typename, name, callback = None):
    '''
        This function adds a QProperty named 'name' to a class's vars() dictionary.
        It create the getter, setter, and signal named 'nameChanged'.
        *Important* a member variable named '_name' will be expected by the getter and setter.
        A QProperty is exposed to QML.
    '''
    goc_member_variable(classvars, name)
    notify = classvars[f'{name}Changed'] = Signal()
    classvars[f'{name}'] = Property(typename, Getter(name), Setter(name, callback, classvars), notify = notify)
Example #9
0
def ROProperty(classvars, typename, name, callback = None):
    '''
        This function adds a QProperty named 'name' to a class's vars() dictionary.
        It creates the getter, and signal named 'nameChanged'. It also creates
        a set_name() setter outside of the Qt property system.
        *Important* a member variable named '_name' will be expected by the getter.
        A QProperty is exposed to QML.
    '''
    goc_member_variable(classvars, name)
    notify = classvars[f'{name}Changed'] = Signal()
    classvars[f'{name}'] = Property(typename, Getter(name), notify = notify)
    classvars[f'set_{name}'] = Setter(name)
Example #10
0
class PieChart(QQuickItem):
    def __init__(self, parent=None):
        QQuickItem.__init__(self, parent)
        self._name = None
        self._pieSlice = None

    def getName(self):
        return self._name

    def setName(self, value):
        self._name = value

    name = Property(str, getName, setName)

    def getPieSlice(self):
        return self._pieSlice

    def setPieSlice(self, value):
        self._pieSlice = value
        self._pieSlice.setParentItem(self)

    pieSlice = Property(PieSlice, getPieSlice, setPieSlice)
Example #11
0
def InputProperty(classvars, typename, name, callback = None):
    '''
        This function adds a QProperty named 'name' to a class's vars() dictionary.
        It create the getter, setter, and signal named 'nameChanged'.
        *Important* a member variable named '_name' will be expected by the getter and setter.
        'callback' will be called if
        (and only if) a new value is set. see InputSetter for more information on 'callback'
        A QProperty is exposed to QML.
        An InputProperty is a property that turns a product dirty when needed.
        It can be a primitive type (e.g. int, string, bool, etc) or a Product,
        or a collection containing products
    '''
    goc_member_variable(classvars, name)
    notify = classvars[f'{name}Changed'] = Signal()
    classvars[f'{name}'] = Property(typename, Getter(name), InputSetter(name, callback, classvars), notify = notify)
Example #12
0
class CurrencyComboBox(QComboBox):
    changed = Signal(int)

    def __init__(self, parent):
        QComboBox.__init__(self, parent)
        self.p_selected_id = 0
        self.model = None
        self.activated.connect(self.OnUserSelection)

        self.query = QSqlQuery(db=db_connection())
        self.query.prepare(f"SELECT id, symbol FROM currencies")
        self.query.exec()
        self.model = QSqlTableModel(db=db_connection())
        self.model.setQuery(self.query)
        self.model.select()
        self.setModel(self.model)
        self.setModelColumn(self.model.fieldIndex("symbol"))

    def isCustom(self):
        return True

    def getId(self):
        return self.p_selected_id

    def setId(self, new_id):
        if self.p_selected_id == new_id:
            return
        self.p_selected_id = new_id
        name = readSQL("SELECT symbol FROM currencies WHERE id=:id",
                       [(":id", self.p_selected_id)])
        if self.currentIndex() == self.findText(name):
            return
        self.setCurrentIndex(self.findText(name))

    selected_id = Property(int, getId, setId, notify=changed, user=True)

    def setIndex(self, index):
        if index is not None:
            self.selected_id = index
            self.changed.emit(self.selected_id)

    @Slot()
    def OnUserSelection(self, _selected_index):
        self.selected_id = self.model.record(self.currentIndex()).value("id")
        self.changed.emit(self.selected_id)
Example #13
0
class PieChart(QQuickItem):
    def __init__(self, parent=None):
        QQuickItem.__init__(self, parent)
        self._name = u''
        self._slices = []

    def getName(self):
        return self._name

    def setName(self, value):
        self._name = value

    name = Property(str, getName, setName)

    def appendSlice(self, _slice):
        _slice.setParentItem(self)
        self._slices.append(_slice)

    slices = ListProperty(PieSlice, appendSlice)
Example #14
0
class PieSlice(QQuickPaintedItem):
    def __init__(self, parent=None):
        QQuickPaintedItem.__init__(self, parent)
        self._color = QColor()

    def getColor(self):
        return self._color

    def setColor(self, value):
        self._color = value

    color = Property(QColor, getColor, setColor)

    def paint(self, painter):
        pen = QPen(self._color, 2)
        painter.setPen(pen)
        painter.setRenderHints(QPainter.Antialiasing, True)
        painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16,
                        290 * 16)
Example #15
0
class AccountButton(QPushButton):
    changed = Signal(int)

    def __init__(self, parent):
        QPushButton.__init__(self, parent)
        self.p_account_id = 0

        self.Menu = QMenu(self)
        self.Menu.addAction(self.tr("Choose account"), self.ChooseAccount)
        self.Menu.addAction(self.tr("Any account"), self.ClearAccount)
        self.setMenu(self.Menu)

        self.dialog = AccountListDialog()
        self.setText(self.dialog.SelectedName)

    def getId(self):
        return self.p_account_id

    def setId(self, account_id):
        self.p_account_id = account_id
        if self.p_account_id:
            self.setText(JalDB().get_account_name(account_id))
        else:
            self.setText(self.tr("ANY"))
        self.changed.emit(self.p_account_id)

    account_id = Property(int, getId, setId, notify=changed)

    def ChooseAccount(self):
        ref_point = self.mapToGlobal(self.geometry().bottomLeft())
        self.dialog.setGeometry(ref_point.x(), ref_point.y(),
                                self.dialog.width(), self.dialog.height())
        self.dialog.setFilter()
        res = self.dialog.exec(enable_selection=True)
        if res:
            self.account_id = self.dialog.selected_id

    def ClearAccount(self):
        self.account_id = 0
Example #16
0
class ClickModel(QObject):
    """ClickModel is the model class for the GUI. It holds the counter property
     and handles event generated by the click on the button."""
    def __init__(self):
        # Initialize the parent object. If omitted, GUI will not start
        QObject.__init__(self)
        # Initialize the counter internal value. Because we propagate count as
        # a property to QML, getter, setter and notifier must be made
        self._count = 0

    def get_count(self):
        """Getter for the count property"""
        return self._count

    def set_count(self, val):
        """Setter for the count property"""
        print("Current: {}, new: {}".format(self._count, val))
        # We set new value and notify of change only if the value
        # is really changed.
        if val != self._count:
            # Change internal value
            self._count = val
            # Notify the GUI that the value had changed
            self.counter_changed.emit()

    # Declare a notification method
    counter_changed = Signal()

    # Add a new property to ClickModel object. It can be used as an attribute
    # from Python.
    count = Property(int, get_count, set_count, notify=counter_changed)

    @Slot()
    def increase(self):
        """Handler for the button click. Increases counter by one."""
        print("Increasing")
        # Use property as an attribute. Setter is called automatically and
        # notifies the GUI about the changed value.
        self.count = self.count + 1
Example #17
0
class DbLookupComboBox(QComboBox):
    def __init__(self, parent=None):
        QComboBox.__init__(self, parent)
        self._model = None
        self._table = ''
        self._key_field = ''
        self._field = ''
        self._selected_id = -1

    def getKey(self):
        return readSQL(
            f"SELECT {self._key_field} FROM {self._table} WHERE {self._field}='{self.currentText()}'"
        )

    def setKey(self, selected_id):
        if self._selected_id == selected_id:
            return
        self._selected_id = selected_id
        value = readSQL(
            f"SELECT {self._field} FROM {self._table} WHERE {self._key_field}={selected_id}"
        )
        self.setCurrentIndex(self.findText(value))

    key = Property(int, getKey, setKey, user=True)

    def setupDb(self, table, key_field, field):
        self._table = table
        self._field = field
        self._key_field = key_field
        self._model = QSqlTableModel(parent=self, db=db_connection())
        self._model.setTable(table)
        field_idx = self._model.fieldIndex(field)
        self._model.setSort(field_idx, Qt.AscendingOrder)
        self._model.select()
        self.setModel(self._model)
        self.setModelColumn(field_idx)
Example #18
0
def Q_ENUMS_mock(classvars, enumclass): #do not use, PySide2 workaround

    values = [a for a in dir(enumclass) if not a.startswith('__') and not callable(getattr(enumclass,a))]

    for v in values:
        classvars[f'{v}'] = Property(int, ConstGetter(getattr(enumclass,v)), constant = True)
Example #19
0
class DevicesModel(QAbstractListModel, QObject):
    """ List model that accesses the devices for the view """
    def __init__(self, args, env: (ToolEnvironmentObject, None)):
        super().__init__()

        self.args = args
        self.env = env
        self._device_count_ = self.env.devices.device_count
        self._category_ = 'Ultimarc Configurations'
        self._ui_dev_info_ = []

        self.setup_info()

    def setup_info(self):
        """ setup up meta data for the devices and configurations """
        for dev in self.get_devices():
            tmp = UIDeviceInfo(product_name=dev.product_name, device_class=dev.class_descr,
                               product_key=dev.dev_key)
            tmp.setup_icon(dev.class_id)
            self._ui_dev_info_.append(tmp)

        # Configuration for non connected devices
        for device_class in DeviceClassID:
            tmp = UIDeviceInfo(False, device_class=device_class.name)
            tmp.setup_icon(device_class.value)
            self._ui_dev_info_.append(tmp)

    def get_devices(self):
        """ Return a list of devices we should show information for. """
        return self.env.devices.filter()

    def roleNames(self):
        """ Just return the DeviceRolePropertyMap dict, but convert key values to byte arrays first for QT. """
        # TODO: Add device information to role dict
        roles = OrderedDict()
        for k, v in DeviceRolePropertyMap.items():
            roles[k] = v.encode('utf-8')
        return roles

    def rowCount(self, parent):
        if parent.isValid():
            return 0
        return len(self._ui_dev_info_)

    def data(self, index: QModelIndex, role):
        if not index.isValid():
            return None

        if role == DeviceRoles.CATEGORY:
            return 'main' if index.row() < self._device_count_ else self._category_

        for x in range(len(self._ui_dev_info_)):
            if x == index.row():
                dev_cls = self._ui_dev_info_[x]
                return getattr(dev_cls, DeviceRolePropertyMap[role])
        return None

    def setData(self, index: QModelIndex, value, role: int = ...):
        # TODO: Implement for writing GUI -> Device
        return False

    def get_category(self):
        return self._category_

    def get_device_count(self):
        return self._device_count_ if self._device_count_ < 4 else 4

    device_count = Property(int, get_device_count, constant=True)
    category = Property(str, get_category, constant=True)
Example #20
0
class DateRangeSelector(QWidget):
    changed = Signal(int, int)   # emits signal when one or both dates were changed, "from" and "to" timestamps are sent

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.report_ranges = {
            'week': (self.tr("Week"), ManipulateDate.PreviousWeek),
            'month': (self.tr("Month"), ManipulateDate.PreviousMonth),
            'quarter': (self.tr("Quarter"), ManipulateDate.PreviousQuarter),
            'year': (self.tr("Year"), ManipulateDate.PreviousYear),
            'QTD': (self.tr("Quarter to date"), ManipulateDate.QuarterToDate),
            'YTD': (self.tr("Year to date"), ManipulateDate.YearToDate),
            'this_year': (self.tr("This year"), ManipulateDate.ThisYear),
            'last_year': (self.tr("Previous year"), ManipulateDate.LastYear),
            'all': (self.tr("All dates"), ManipulateDate.AllDates),
        }

        self._begin = 0
        self._end = 0
        self._items = []
        self.changing_range = False

        self.layout = QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)

        self.range_combo = QComboBox(self)
        self.layout.addWidget(self.range_combo)

        self.from_label = QLabel(self.tr("From:"), parent=self)
        self.layout.addWidget(self.from_label)

        self.from_date = QDateEdit()
        self.from_date.setDisplayFormat("dd/MM/yyyy")
        self.from_date.setCalendarPopup(True)
        self.from_date.setTimeSpec(Qt.UTC)
        self.layout.addWidget(self.from_date)

        self.from_label = QLabel(self.tr("To:"), parent=self)
        self.layout.addWidget(self.from_label)

        self.to_date = QDateEdit()
        self.to_date.setDisplayFormat("dd/MM/yyyy")
        self.to_date.setCalendarPopup(True)
        self.to_date.setTimeSpec(Qt.UTC)
        self.layout.addWidget(self.to_date)

        self.setLayout(self.layout)

        self.setFocusProxy(self.range_combo)

        self.connect_signals_and_slots()

    def getConfig(self):
        return ';'.join(self._items)

    def setConfig(self, items_list):
        try:
            self._items = items_list.split(';')
        except AttributeError:
            self._items = []
        for item in self._items:
            try:
                item_name = self.report_ranges[item][ITEM_NAME]
                self.range_combo.addItem(item_name, item)
            except KeyError:
                continue

    ItemsList = Property(str, getConfig, setConfig)

    def connect_signals_and_slots(self):
        self.range_combo.currentIndexChanged.connect(self.onRangeChange)
        self.from_date.dateChanged.connect(self.onFromChange)
        self.to_date.dateChanged.connect(self.onToChange)

    @Slot()
    def onRangeChange(self, index):
        item = self.range_combo.itemData(index)
        self._begin, self._end = self.report_ranges[item][ITEM_METHOD]()
        self.changing_range = True
        self.from_date.setDateTime(QDateTime.fromSecsSinceEpoch(self._begin, spec=Qt.UTC))
        self.to_date.setDateTime(QDateTime.fromSecsSinceEpoch(self._end, spec=Qt.UTC))
        self.changing_range = False
        self.changed.emit(self._begin, self._end)

    @Slot()
    def onFromChange(self):
        self._begin = self.from_date.date().startOfDay(Qt.UTC).toSecsSinceEpoch()
        if not self.changing_range:
            self.changed.emit(self._begin, self._end)

    @Slot()
    def onToChange(self):
        self._end = self.to_date.date().startOfDay(Qt.UTC).toSecsSinceEpoch()
        if not self.changing_range:
            self.changed.emit(self._begin, self._end)

    def setCurrentIndex(self, index):
        if index == self.range_combo.currentIndex():
            self.onRangeChange(index)
        else:
            self.range_combo.setCurrentIndex(index)
Example #21
0
class ReferenceDataDialog(QDialog, Ui_ReferenceDataDialog):
    # tree_view - table will be displayed as hierarchical tree with help of 2 columns: 'id', 'pid' in sql table
    def __init__(self):
        QDialog.__init__(self)
        self.setupUi(self)

        self.model = None
        self._previous_row = -1
        self.selected_id = 0
        self.p_selected_name = ''
        self._filter_text = ''
        self.selection_enabled = False
        self.group_id = None
        self.group_key_field = None
        self.group_key_index = None
        self.group_fkey_field = None
        self.filter_field = None
        self._filter_value = ''
        self.toggle_state = False
        self.toggle_field = None
        self.search_field = None
        self.search_text = ""
        self.tree_view = False
        self.toolbar = None
        self.custom_editor = False

        self.AddChildBtn.setVisible(False)
        self.GroupLbl.setVisible(False)
        self.GroupCombo.setVisible(False)
        self.SearchFrame.setVisible(False)

        self.AddBtn.setIcon(load_icon("add.png"))
        self.AddChildBtn.setIcon(load_icon("add_child.png"))
        self.RemoveBtn.setIcon(load_icon("delete.png"))
        self.CommitBtn.setIcon(load_icon("accept.png"))
        self.RevertBtn.setIcon(load_icon("cancel.png"))

        self.SearchString.textChanged.connect(self.OnSearchChange)
        self.GroupCombo.currentIndexChanged.connect(self.OnGroupChange)
        self.Toggle.stateChanged.connect(self.OnToggleChange)
        self.AddBtn.clicked.connect(self.OnAdd)
        self.AddChildBtn.clicked.connect(self.OnChildAdd)
        self.RemoveBtn.clicked.connect(self.OnRemove)
        self.CommitBtn.clicked.connect(self.OnCommit)
        self.RevertBtn.clicked.connect(self.OnRevert)
        self.DataView.doubleClicked.connect(self.OnDoubleClicked)
        self.DataView.clicked.connect(self.OnClicked)
        self.TreeView.doubleClicked.connect(self.OnDoubleClicked)
        self.TreeView.clicked.connect(self.OnClicked)

    def _init_completed(self):
        self.DataView.setVisible(not self.tree_view)
        self.TreeView.setVisible(self.tree_view)
        if self.tree_view:
            self.TreeView.selectionModel().selectionChanged.connect(
                self.OnRowSelected)
        else:
            self.DataView.selectionModel().selectionChanged.connect(
                self.OnRowSelected)
            self.DataView.setContextMenuPolicy(Qt.CustomContextMenu)
            self.DataView.customContextMenuRequested.connect(
                self.onDataViewContextMenu)
        self.model.dataChanged.connect(self.OnDataChanged)
        self.setFilter()

    def onDataViewContextMenu(self, pos):
        if not self.group_id:
            return
        index = self.DataView.indexAt(pos)
        menu_title = QWidgetAction(self.DataView)
        title_lbl = QLabel()
        title_lbl.setText(self.tr("Change type to:"))
        menu_title.setDefaultWidget(title_lbl)
        contextMenu = QMenu(self.DataView)
        contextMenu.addAction(menu_title)
        contextMenu.addSeparator()
        combo_model = self.GroupCombo.model()
        for i in range(self.GroupCombo.count()):
            type_id = combo_model.data(
                combo_model.index(
                    i, combo_model.fieldIndex(self.group_fkey_field)))
            contextMenu.addAction(self.GroupCombo.itemText(i),
                                  partial(self.updateItemType, index, type_id))
        contextMenu.popup(self.DataView.viewport().mapToGlobal(pos))

    @Slot()
    def updateItemType(self, index, new_type):
        self.model.updateItemType(index, new_type)
        self.CommitBtn.setEnabled(True)
        self.RevertBtn.setEnabled(True)

    @Slot()
    def closeEvent(self, event):
        if self.CommitBtn.isEnabled(
        ):  # There are uncommitted changed in a table
            if QMessageBox().warning(
                    self, self.tr("Confirmation"),
                    self.tr(
                        "You have uncommitted changes. Do you want to close?"),
                    QMessageBox.Yes, QMessageBox.No) == QMessageBox.No:
                event.ignore()
                return
            else:
                self.model.revertAll()
        event.accept()

    # Overload ancestor method to activate/deactivate filters for table view
    def exec(self, enable_selection=False, selected=0):
        self.selection_enabled = enable_selection
        self.setFilter()
        if enable_selection:
            self.locateItem(selected)
        res = super().exec()
        self.resetFilter()
        return res

    def getSelectedName(self):
        if self.selected_id == 0:
            return self.tr("ANY")
        else:
            return self.p_selected_name

    def setSelectedName(self, selected_id):
        pass

    @Signal
    def selected_name_changed(self):
        pass

    SelectedName = Property(str,
                            getSelectedName,
                            setSelectedName,
                            notify=selected_name_changed)

    @Slot()
    def OnDataChanged(self):
        self.CommitBtn.setEnabled(True)
        self.RevertBtn.setEnabled(True)

    @Slot()
    def OnAdd(self):
        if self.custom_editor:
            editor = self.customEditor()
            editor.createNewRecord()
            editor.exec()
            self.model.select(
            )  # TODO better to make self.beginInsertRows/endInsertRows
        else:
            if self.tree_view:
                idx = self.TreeView.selectionModel().selection().indexes()
            else:
                idx = self.DataView.selectionModel().selection().indexes()
            current_index = idx[0] if idx else self.model.index(0, 0)
            self.model.addElement(current_index, in_group=self.group_id)
            self.CommitBtn.setEnabled(True)
            self.RevertBtn.setEnabled(True)

    @Slot()
    def OnChildAdd(self):
        if self.tree_view:
            idx = self.TreeView.selectionModel().selection().indexes()
            current_index = idx[0] if idx else self.model.index(0, 0)
            self.model.addChildElement(current_index)
            self.CommitBtn.setEnabled(True)
            self.RevertBtn.setEnabled(True)

    @Slot()
    def OnRemove(self):
        if self.tree_view:
            idx = self.TreeView.selectionModel().selection().indexes()
        else:
            idx = self.DataView.selectionModel().selection().indexes()
        current_index = idx[0] if idx else self.model.index(0, 0)
        self.model.removeElement(current_index)
        self.CommitBtn.setEnabled(True)
        self.RevertBtn.setEnabled(True)

    @Slot()
    def OnCommit(self):
        if not self.model.submitAll():
            return
        self.CommitBtn.setEnabled(False)
        self.RevertBtn.setEnabled(False)

    @Slot()
    def OnRevert(self):
        self.model.revertAll()
        self.CommitBtn.setEnabled(False)
        self.RevertBtn.setEnabled(False)

    def setFilterValue(self, filter_value):
        if self.filter_field is None:
            return
        self._filter_value = filter_value
        self.setFilter()

    def resetFilter(self):
        self.model.setFilter("")

    def setFilter(self):
        conditions = []
        if self.search_text and self.search_field is not None:
            search = self.search_field.split('-')
            if len(search) == 1:  # Simple search by given text field
                conditions.append(
                    f"{self.search_field} LIKE '%{self.search_text}%'")
            elif len(search
                     ) == 4:  # Complex search by relation from another table
                # Here search[0] is a field in current table that binds with search[2] field in lookup table search[1]
                # search[3] is a name in lookup table which is used for searching.
                # I.e. self.search_field has format: f_key-lookup_table_name-lookup_id-lookup_field
                conditions.append(
                    f"{search[0]} IN (SELECT {search[2]} FROM {search[1]} "
                    f"WHERE {search[3]} LIKE '%{self.search_text}%')")
            else:
                assert False, f"Unsupported format of search field: {self.search_field}"

        if self.group_id:
            conditions.append(
                f"{self.table}.{self.group_key_field}={self.group_id}")

        if self.filter_field is not None and self._filter_value:
            conditions.append(
                f"{self.table}.{self.filter_field} = {self._filter_value}")
            # completion model needs only this filter, others are for dialog
            self.model.completion_model.setFilter(
                f"{self.table}.{self.filter_field} = {self._filter_value}")

        if self.toggle_field:
            if not self.toggle_state:
                conditions.append(f"{self.table}.{self.toggle_field}=1")

        self._filter_text = ""
        for line in conditions:
            self._filter_text += line + " AND "
        self._filter_text = self._filter_text[:-len(" AND ")]

        self.model.setFilter(self._filter_text)

    @Slot()
    def OnSearchChange(self):
        self.search_text = self.SearchString.text()
        self.setFilter()

    @Slot()
    def OnRowSelected(self, selected, _deselected):
        idx = selected.indexes()
        if idx:
            self.selected_id = self.model.getId(idx[0])
            self.p_selected_name = self.model.getName(idx[0])

    @Slot()
    def OnClicked(self, index):
        if self.custom_editor:
            if self._previous_row == index.row():
                editor = self.customEditor()
                editor.selected_id = self.selected_id
                editor.exec()
            else:
                self._previous_row = index.row()

    @Slot()
    def OnDoubleClicked(self, index):
        self.selected_id = self.model.getId(index)
        self.p_selected_name = self.model.getName(index)
        if self.selection_enabled:
            self.setResult(QDialog.Accepted)
            self.close()

    @Slot()
    def OnGroupChange(self, list_id):
        self.OnRevert()  # Discard all possible changes

        model = self.GroupCombo.model()
        self.group_id = model.data(
            model.index(list_id, model.fieldIndex(self.group_fkey_field)))
        self.setFilter()

    @Slot()
    def OnToggleChange(self, state):
        if state == 0:
            self.toggle_state = False
        else:
            self.toggle_state = True
        self.setFilter()

    def locateItem(self, item_id):
        raise NotImplementedError(
            "locateItem() method is not defined in subclass ReferenceDataDialog"
        )
Example #22
0
class AssetDialog(QDialog, Ui_AssetDialog):
    def __init__(self):
        QDialog.__init__(self)
        self.setupUi(self)
        self._asset_id = -1
        # Custom model to allow common submit errors handling and error message display
        self._model = AssetsListModel("assets", self)

        self._mapper = QDataWidgetMapper(self._model)
        self._mapper.setModel(self._model)
        self._mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)

        self._mapper.addMapping(self.NameEdit,
                                self._model.fieldIndex("full_name"))
        self._mapper.addMapping(self.isinEdit, self._model.fieldIndex("isin"))
        self._mapper.addMapping(self.TypeCombo,
                                self._model.fieldIndex("type_id"))
        self._mapper.addMapping(self.CountryCombo,
                                self._model.fieldIndex("country_id"))
        self._mapper.addMapping(self.BaseAssetSelector,
                                self._model.fieldIndex("base_asset"))

        self._model.select()

        self._symbols_model = SymbolsListModel("asset_tickers",
                                               self.SymbolsTable)
        self.SymbolsTable.setModel(self._symbols_model)
        self._symbols_model.select()
        self._symbols_model.configureView()

        self._data_model = ExtraDataModel("asset_data", self.DataTable)
        self.DataTable.setModel(self._data_model)
        self._data_model.select()
        self._data_model.configureView()

        self.AddSymbolButton.setIcon(load_icon("add.png"))
        self.RemoveSymbolButton.setIcon(load_icon("delete.png"))
        self.AddDataButton.setIcon(load_icon("add.png"))
        self.RemoveDataButton.setIcon(load_icon("delete.png"))
        self.OkButton.setIcon(load_icon("accept.png"))
        self.CancelButton.setIcon(load_icon("cancel.png"))

        self.TypeCombo.currentIndexChanged.connect(self.onTypeUpdate)
        self.AddSymbolButton.clicked.connect(self.onAddSymbol)
        self.RemoveSymbolButton.clicked.connect(self.onRemoveSymbol)
        self.AddDataButton.clicked.connect(self.onAddData)
        self.RemoveDataButton.clicked.connect(self.onRemoveData)

    def getSelectedId(self):
        return self._asset_id

    def setSelectedId(self, asset_id):
        self._asset_id = asset_id
        self._model.setFilter(f"id={self._asset_id}")
        self._mapper.toFirst()
        self._symbols_model.filterBy("asset_id", self._asset_id)
        self._data_model.filterBy("asset_id", self._asset_id)
        self.onTypeUpdate(
            0)  # need to update manually as it isn't triggered from mapper

    selected_id = Property(str, getSelectedId, setSelectedId)

    def createNewRecord(self):
        self._asset_id = 0
        self._model.setFilter(f"id={self._asset_id}")
        new_record = self._model.record()
        new_record.setNull("id")
        assert self._model.insertRows(0, 1)
        self._model.setRecord(0, new_record)
        self._mapper.toLast()
        self._symbols_model.filterBy("asset_id", self._asset_id)
        self._data_model.filterBy("asset_id", self._asset_id)

    def accept(self) -> None:
        if not self._model.submitAll():
            return
        asset_id = self._model.data(
            self._model.index(0, self._model.fieldIndex("id")))
        if asset_id is None:  # we just have saved new asset record and need last inserted id
            asset_id = self._model.query().lastInsertId()
        for model in [self._data_model, self._symbols_model]:
            for row in range(model.rowCount()):
                model.setData(model.index(row, model.fieldIndex("asset_id")),
                              asset_id)
            if not model.submitAll():
                return
        super().accept()

    def reject(self) -> None:
        for model in [self._data_model, self._symbols_model, self._model]:
            model.revertAll()
        super().reject()

    def onTypeUpdate(self, _index):
        if self.TypeCombo.key == PredefinedAsset.Derivative:
            self.BaseAssetSelector.setEnabled(True)
            self.isinEdit.setEnabled(False)
        elif self.TypeCombo.key == PredefinedAsset.Money or self.TypeCombo.key == PredefinedAsset.Commodity:
            self.BaseAssetSelector.setEnabled(False)
            self.isinEdit.setEnabled(False)
        else:
            self.BaseAssetSelector.setEnabled(False)
            self.isinEdit.setEnabled(True)

    def onAddSymbol(self):
        idx = self.SymbolsTable.selectionModel().selection().indexes()
        current_index = idx[0] if idx else self._symbols_model.index(0, 0)
        self._symbols_model.addElement(current_index)

    def onRemoveSymbol(self):
        idx = self.SymbolsTable.selectionModel().selection().indexes()
        current_index = idx[0] if idx else self._symbols_model.index(0, 0)
        self._symbols_model.removeElement(current_index)

    def onAddData(self):
        idx = self.DataTable.selectionModel().selection().indexes()
        current_index = idx[0] if idx else self._data_model.index(0, 0)
        self._data_model.addElement(current_index)

    def onRemoveData(self):
        idx = self.DataTable.selectionModel().selection().indexes()
        current_index = idx[0] if idx else self._data_model.index(0, 0)
        self._data_model.removeElement(current_index)
Example #23
0
class AbstractReferenceSelector(QWidget):
    changed = Signal()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.completer = None
        self.p_selected_id = 0

        self.layout = QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.name = QLineEdit()
        self.name.setText("")
        self.layout.addWidget(self.name)
        self.details = QLabel()
        self.details.setText("")
        self.details.setVisible(False)
        self.layout.addWidget(self.details)
        self.button = QPushButton("...")
        self.button.setFixedWidth(
            self.button.fontMetrics().horizontalAdvance("XXXX"))
        self.layout.addWidget(self.button)
        self.setLayout(self.layout)

        self.setFocusProxy(self.name)

        self.button.clicked.connect(self.on_button_clicked)

        if self.details_field:
            self.name.setFixedWidth(
                self.name.fontMetrics().horizontalAdvance("X") * 15)
            self.details.setVisible(True)
        self.completer = QCompleter(self.dialog.model.completion_model)
        self.completer.setCompletionColumn(
            self.dialog.model.completion_model.fieldIndex(self.selector_field))
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.name.setCompleter(self.completer)
        self.completer.activated[QModelIndex].connect(self.on_completion)

    def getId(self):
        return self.p_selected_id

    def setId(self, selected_id):
        if self.p_selected_id == selected_id:
            return
        self.p_selected_id = selected_id
        self.name.setText(
            self.dialog.model.getFieldValue(selected_id, self.selector_field))
        if self.details_field:
            self.details.setText(
                self.dialog.model.getFieldValue(selected_id,
                                                self.details_field))

    selected_id = Property(int, getId, setId, notify=changed, user=True)

    def setFilterValue(self, filter_value):
        self.dialog.setFilterValue(filter_value)

    def on_button_clicked(self):
        ref_point = self.mapToGlobal(self.name.geometry().bottomLeft())
        self.dialog.setGeometry(ref_point.x(), ref_point.y(),
                                self.dialog.width(), self.dialog.height())
        res = self.dialog.exec(enable_selection=True,
                               selected=self.selected_id)
        if res:
            self.selected_id = self.dialog.selected_id
            self.changed.emit()

    @Slot(QModelIndex)
    def on_completion(self, index):
        model = index.model()
        self.selected_id = model.data(model.index(index.row(), 0),
                                      Qt.DisplayRole)
        self.changed.emit()

    def isCustom(self):
        return True
Example #24
0
class WelcomePage(QWizardPage):
    butt_signal = Signal(str)  # Make a signal, pass it

    def __init__(self, parent=None):
        QWizardPage.__init__(self, parent)
        self.setTitle("Select the data source(s) to analyze:")

        self.label = QLabel("<b>Images are not required.</b><br> However, if they"+
                            " are not present, then bounded metrics will be calculated from the bounding rectangle"
                            " of the provided coordinates.")
        self.label.setWordWrap(True)
        self.label.setTextFormat(Qt.RichText)
        self.label.setAlignment(Qt.AlignCenter)

        self.file_dir_form = QGridLayout()

        self.coord_label = QLineEdit()
        self.coord_label.setReadOnly(True)
        self.coord_label.text()
        self.coord_butt = QPushButton("Select...")

        self._coord_path = ""

        self.image_label = QLineEdit()
        self.image_label.setReadOnly(True)
        #self.image_label.setMaximumWidth()
        self.image_butt = QPushButton("Select...")
        self.image_path = ""

        self.file_dir_form.addWidget(self.coord_label, 0, 0)
        self.file_dir_form.addWidget(self.coord_butt, 0, 1)
        self.file_dir_form.addWidget(self.image_label, 1, 0)
        self.file_dir_form.addWidget(self.image_butt, 1, 1)

        self.v_layout = QVBoxLayout()
        self.v_layout.setSpacing(32)
        self.v_layout.addWidget(self.label)
        self.v_layout.addLayout(self.file_dir_form)

        self.file_dir_form.setSpacing(4)

        self.setLayout(self.v_layout)

        self.butt_signal
        self.coord_butt.clicked.connect(self.select_coord_path)
        self.image_butt.clicked.connect(self.select_image_path)



    def readCoordPath(self):
        return self._coord_path

    def setCoordPath(self, val):
        self._coord_path = val

    coord_path = Property(str, readCoordPath, setCoordPath, notify=butt_signal)

    @Slot()
    def select_coord_path(self):
        self._coord_path = QFileDialog.getExistingDirectory(parent=self,
                                                           caption="Select the folder containing the coordinates of interest.",
                                                           options=QFileDialog.ShowDirsOnly)


    @Slot()
    def select_image_path(self):

        self.image_path = QFileDialog.getExistingDirectory(parent=self,
                                                           caption="Select the folder containing the images of interest.",
                                                           options=QFileDialog.ShowDirsOnly)
Example #25
0
class Units(QObject):
    _changed_grid_unit = Signal(float)
    _changed_spacing = Signal()

    def __init__(self):
        super().__init__()
        self._grid_unit = -1.0
        self._small_spacing = -1.0
        self._large_spacing = -1.0
        self._device_pixel_patio = -1.0

        self.update()
        self.update_device_pixel_ratio()

    def eventFilter(self, watched, event):
        if watched == QCoreApplication.instance():
            if event.type() == QEvent.ApplicationFontChange:
                self.update()

        return QObject.eventFilter(watched, event)

    def update(self):
        grid_unit = QFontMetrics(
            QGuiApplication.font()).boundingRect('M').height()

        if grid_unit % 2 != 0:
            grid_unit += 1

        if grid_unit != self._grid_unit:
            self._grid_unit = grid_unit
            self._changed_grid_unit.emit(self._grid_unit)

        if grid_unit != self._large_spacing:
            self._small_spacing = max(2, int(
                grid_unit / 4))  # 1 / 4 of grid_unit, at least 2
            self._large_spacing = self._small_spacing * 2
            self._changed_spacing.emit()

    def update_device_pixel_ratio(self):
        # Using QGuiApplication::devicePixelRatio() gives too coarse values,
        # i.e.it directly jumps from 1.0 to 2.0.  We want tighter control on
        # sizing, so we compute the exact ratio and use that.
        # TODO: make it possible to adapt to the dpi for the current screen dpi
        #   instead of assuming that all of them use the same dpi which applies for
        #   X11 but not for other systems.

        primary = QGuiApplication.primaryScreen()
        if primary:
            return

        dpi = primary.logicalDotsPerInchX()
        # Usual "default" is 96 dpi
        # that magic ratio follows the definition of "device independent pixel" by Microsoft
        self._device_pixel_patio = dpi / 96
        self._changed_spacing.emit()

    def get_grid_unit(self):
        return self._grid_unit

    def get_small_spacing(self):
        return self._small_spacing

    def get_large_spacing(self):
        return self._large_spacing

    grid_unit = Property(float, get_grid_unit, constant=True)
    small_spacing = Property(int, get_small_spacing, constant=True)
    large_spacing = Property(int, get_large_spacing, constant=True)
Example #26
0
class TaxWidget(MdiWidget, Ui_TaxWidget):
    def __init__(self, parent):
        MdiWidget.__init__(self)
        self.setupUi(self)

        self.Year.setValue(datetime.now().year -
                           1)  # Set previous year by default
        self.XlsSelectBtn.pressed.connect(partial(self.OnFileBtn, 'XLS'))
        self.DlsgSelectBtn.pressed.connect(partial(self.OnFileBtn, 'DLSG'))
        self.SaveButton.pressed.connect(self.SaveReport)

        # center dialog with respect to parent window
        x = parent.x() + parent.width() / 2 - self.width() / 2
        y = parent.y() + parent.height() / 2 - self.height() / 2
        self.setGeometry(x, y, self.width(), self.height())

    @Slot()
    def OnFileBtn(self, type):
        if type == 'XLS':
            selector = (self.tr("Save tax reports to:"),
                        self.tr("Excel files (*.xlsx)"), '.xlsx',
                        self.XlsFileName)
        elif type == 'DLSG':
            last_digit = self.year % 10
            selector = (self.tr("Save tax form to:"),
                        self.tr(f"Tax form (*.dc{last_digit})"),
                        f".dc{last_digit}", self.DlsgFileName)
        else:
            raise ValueError
        filename = QFileDialog.getSaveFileName(self, selector[0], ".",
                                               selector[1])
        if filename[0]:
            if filename[1] == selector[
                    1] and filename[0][-len(selector[2]):] != selector[2]:
                selector[3].setText(filename[0] + selector[2])
            else:
                selector[3].setText(filename[0])

    def getYear(self):
        return self.Year.value()

    def getXlsFilename(self):
        return self.XlsFileName.text()

    def getAccount(self):
        return self.AccountWidget.selected_id

    def getDlsgState(self):
        return self.DlsgGroup.isChecked()

    def getDslgFilename(self):
        return self.DlsgFileName.text()

    def getBrokerAsIncomeName(self):
        return self.IncomeSourceBroker.isChecked()

    def getDividendsOnly(self):
        return self.DividendsOnly.isChecked()

    def getNoSettlement(self):
        return self.NoSettlement.isChecked()

    year = Property(int, fget=getYear)
    xls_filename = Property(str, fget=getXlsFilename)
    account = Property(int, fget=getAccount)
    update_dlsg = Property(bool, fget=getDlsgState)
    dlsg_filename = Property(str, fget=getDslgFilename)
    dlsg_broker_as_income = Property(bool, fget=getBrokerAsIncomeName)
    dlsg_dividends_only = Property(bool, fget=getDividendsOnly)
    no_settelement = Property(bool, fget=getNoSettlement)

    def SaveReport(self):
        taxes = TaxesRus()
        tax_report = taxes.prepare_tax_report(
            self.year, self.account, use_settlement=(not self.no_settelement))

        reports_xls = XLSX(self.xls_filename)
        templates = {
            "Дивиденды": "tax_rus_dividends.json",
            "Акции": "tax_rus_trades.json",
            "Облигации": "tax_rus_bonds.json",
            "ПФИ": "tax_rus_derivatives.json",
            "Корп.события": "tax_rus_corporate_actions.json",
            "Комиссии": "tax_rus_fees.json",
            "Проценты": "tax_rus_interests.json"
        }
        parameters = {
            "period":
            f"{datetime.utcfromtimestamp(taxes.year_begin).strftime('%d.%m.%Y')}"
            f" - {datetime.utcfromtimestamp(taxes.year_end - 1).strftime('%d.%m.%Y')}",
            "account":
            f"{taxes.account_number} ({taxes.account_currency})",
            "currency":
            taxes.account_currency,
            "broker_name":
            taxes.broker_name,
            "broker_iso_country":
            taxes.broker_iso_cc
        }
        for section in tax_report:
            if section not in templates:
                continue
            reports_xls.output_data(tax_report[section], templates[section],
                                    parameters)
        reports_xls.save()

        logging.info(
            self.tr("Tax report saved to file ") + f"'{self.xls_filename}'")

        if self.update_dlsg:
            tax_forms = DLSG(self.year,
                             broker_as_income=self.dlsg_broker_as_income,
                             only_dividends=self.dlsg_dividends_only)
            tax_forms.update_taxes(tax_report, parameters)
            try:
                tax_forms.save(self.dlsg_filename)
            except:
                logging.error(
                    self.tr("Can't write tax form into file ") +
                    f"'{self.dlsg_filename}'")
Example #27
0
class Product(QObject):
    '''
    classdocs
    '''
    productDirty = Signal()
    productClean = Signal()

    def __init__(self, parent=None):
        super(Product, self).__init__(parent)
        '''
        Constructor
        '''
        self._dirty = False
        self._dependsOn = []
        self._dependencies = []
        self._error = None
        self._producer = None
        self._autoUpdate = False

    @Property(QObject, notify = productClean)
    def bind(self):
        return self

    def set_dirty(self, d):
        if self._dirty != d:
            self._dirty = d
            self.dirtyChanged.emit()
        if self._dirty and self._autoUpdate:
            QTimer.singleShot(0, self.update) # schedule an update as soon as we go back to event loop, but not before
    dirtyChanged = Signal()
    dirty = Property(bool, Getter('dirty'), set_dirty, dirtyChanged)

    RWProperty(vars(), bool, 'autoUpdate')

    def _update(self):
        '''
        update function to override
        '''
        pass

    @Slot()
    def update(self):

        if self.dirty:

            self._error = None

            for d in self._dependencies:
                if not d.update():
                    self._error = d._error
                    return False
            try:
                self._update()
            except Exception as e:
                self._error = e
                print(traceback.format_exc())

            self.makeClean()

        return self._error is None

    @Slot()
    def makeDirty(self):
        if not self.dirty:
            self.dirty = True
            self.productDirty.emit()

    @Slot()
    def makeClean(self):
        if self.dirty:
            self.dirty = False
            self.productClean.emit()

    def set_dependsOn(self, v):
        '''
            *Important" this property is meant to be used only from QML.
            Use add/remove_dependency() from python.
        '''
        old = self._dependsOn
        if assign_input(self, "dependsOn", v):
            for d in old:
                self.remove_dependency(d)
            for d in self._dependsOn:
                self.add_dependency(d)

    dependsOnChanged = Signal()
    dependsOn = Property(list, Getter('dependsOn'), set_dependsOn, dependsOnChanged)

    def add_dependency(self, d):
        if d is not None:
            self._dependencies.append(d)
            d.productDirty.connect(self.makeDirty)
            self.makeDirty()

    def remove_dependency(self, d):
        if d is not None:
            self._dependencies.remove(d)
            d.productDirty.disconnect(self.makeDirty)
            self.makeDirty()

    def set_producer(self, producer):
        if self._producer is not None:
            raise RuntimeError("Error: tried to call set a set_producer() twice on " + str(self) + ".")
        assert(issubclass(type(producer), Product))
        self._producer = producer
        self.add_dependency(producer)
        producer.productClean.connect(self.makeClean)
Example #28
0
class QtBubbleLabel(QWidget):
    BackgroundColor = QColor(195, 195, 195)
    BorderColor = QColor(150, 150, 150)

    def __init__(self, *args, **kwargs):
        super(QtBubbleLabel, self).__init__(*args, **kwargs)
        self.setWindowFlags(Qt.Window | Qt.Tool | Qt.FramelessWindowHint
                            | Qt.WindowStaysOnTopHint
                            | Qt.X11BypassWindowManagerHint)
        self.setMinimumWidth(200)
        self.setMinimumHeight(48)
        self.setAttribute(Qt.WA_TranslucentBackground, True)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(8, 8, 8, 16)
        self.label = QLabel(self)
        layout.addWidget(self.label)
        self.animationGroup = QParallelAnimationGroup(self)

    def setText(self, text):
        self.label.setText(text)

    def text(self):
        return self.label.text()

    def stop(self):
        self.hide()
        self.animationGroup.stop()
        self.animationGroup.clear()
        self.close()

    def show(self):
        super(QtBubbleLabel, self).show()
        x = self.parent().geometry().x()
        y = self.parent().geometry().y()
        x2 = self.parent().size().width()
        y2 = self.parent().size().height()
        startPos = QPoint(x + int(x2 / 2) - int(self.width() / 2),
                          y + int(y2 / 2))
        endPos = QPoint(x + int(x2 / 2) - int(self.width() / 2),
                        y + int(y2 / 2) - self.height() * 3 - 5)
        self.move(startPos)
        # 初始化动画
        self.initAnimation(startPos, endPos)

    def initAnimation(self, startPos, endPos):
        # 透明度动画
        opacityAnimation = QPropertyAnimation(self, b"opacity")
        opacityAnimation.setStartValue(1.0)
        opacityAnimation.setEndValue(0.0)
        # 设置动画曲线
        opacityAnimation.setEasingCurve(QEasingCurve.InQuad)
        opacityAnimation.setDuration(3000)  # 在4秒的时间内完成
        # 往上移动动画
        moveAnimation = QPropertyAnimation(self, b"pos")
        moveAnimation.setStartValue(startPos)
        moveAnimation.setEndValue(endPos)
        moveAnimation.setEasingCurve(QEasingCurve.InQuad)
        moveAnimation.setDuration(4000)  # 在5秒的时间内完成
        # 并行动画组(目的是让上面的两个动画同时进行)
        self.animationGroup.addAnimation(opacityAnimation)
        self.animationGroup.addAnimation(moveAnimation)
        self.animationGroup.finished.connect(self.close)  # 动画结束时关闭窗口
        self.animationGroup.start()

    def paintEvent(self, event):
        super(QtBubbleLabel, self).paintEvent(event)
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)  # 抗锯齿

        rectPath = QPainterPath()  # 圆角矩形

        height = self.height() - 8  # 往上偏移8
        rectPath.addRoundedRect(QRectF(0, 0, self.width(), height), 5, 5)
        x = self.width() / 5 * 4
        # 边框画笔
        painter.setPen(
            QPen(self.BorderColor, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        # 背景画刷
        painter.setBrush(self.BackgroundColor)
        # 绘制形状
        painter.drawPath(rectPath)

    def windowOpacity(self):
        return super(QtBubbleLabel, self).windowOpacity()

    def setWindowOpacity(self, opacity):
        super(QtBubbleLabel, self).setWindowOpacity(opacity)

    opacity = Property(float, windowOpacity, setWindowOpacity)

    def ShowMsg(self, text):
        self.stop()
        self.setText(text)
        self.setStyleSheet("color:black")
        self.show()

    @staticmethod
    def ShowMsgEx(owner, text):
        data = QtBubbleLabel(owner)
        data.setText(text)
        data.setStyleSheet("color:black")
        data.show()

    def ShowError(self, text):
        self.stop()
        self.setText(text)
        self.setStyleSheet("color:red")
        self.show()

    @staticmethod
    def ShowErrorEx(owner, text):
        data = QtBubbleLabel(owner)
        data.setText(text)
        data.setStyleSheet("color:red")
        data.show()
class FigureCanvasItemQQuickAgg(QQuickItem):

    BUTTON_MAP = {
        Qt.LeftButton: MouseButton.LEFT,
        Qt.MiddleButton: MouseButton.MIDDLE,
        Qt.RightButton: MouseButton.RIGHT,
        Qt.XButton1: MouseButton.BACK,
        Qt.XButton2: MouseButton.FORWARD
    }

    def __init__(self, parent=None):
        self._node = None
        self._figure = None
        super().__init__(parent)
        self._tools = None
        self.setFlag(QQuickItem.ItemHasContents, True)
        self.setAcceptedMouseButtons(Qt.AllButtons)
        self.setAcceptHoverEvents(True)

    def updatePaintNode(self, node, data):
        if not self._figure:
            return

        if not self._figure.canvas:
            return

        if not self._figure.canvas.renderer:
            return

        image = QImage(self._figure.canvas.buffer_rgba(),
                       self._figure.canvas.renderer.width,
                       self._figure.canvas.renderer.height,
                       QImage.Format_RGBA8888)

        texture = self.window().createTextureFromImage(image)

        if not self._node:
            self._node = QSGSimpleTextureNode()
            self._node.setFiltering(QSGTexture.Linear)

        self._node.setTexture(texture)
        self._node.setRect(0, 0, self.width(), self.height())

        return self._node

    def get_figure(self):
        return self._figure

    def set_figure(self, value):
        self._figure = value
        self._tools = Tools(self._figure.canvas)
        self._tools.zoom()
        self._figure.canvas.mpl_connect("draw_event",
                                        lambda event: self.update())
        self.figure_changed.emit()

    def map_from_qt_to_matplotlib(self, pos):
        return pos.x(), self._figure.bbox.height - pos.y()

    def hoverEnterEvent(self, event):
        self._figure.canvas.enter_notify_event(
            xy=self.map_from_qt_to_matplotlib(event.pos()), guiEvent=event)

    def hoverLeaveEvent(self, event):
        self._figure.canvas.leave_notify_event(guiEvent=event)

    def hoverMoveEvent(self, event):
        x, y = self.map_from_qt_to_matplotlib(event.pos())
        self._figure.canvas.motion_notify_event(x, y, guiEvent=event)

    def mouseMoveEvent(self, event):
        x, y = self.map_from_qt_to_matplotlib(event.pos())
        self._figure.canvas.motion_notify_event(x, y, guiEvent=event)

    def mousePressEvent(self, event):
        x, y = self.map_from_qt_to_matplotlib(event.pos())
        button = self.BUTTON_MAP.get(event.button())

        if not button:
            return

        self._figure.canvas.button_press_event(x, y, button, guiEvent=event)

    def mouseReleaseEvent(self, event):
        x, y = self.map_from_qt_to_matplotlib(event.pos())
        button = self.BUTTON_MAP.get(event.button())

        if not button:
            return

        self._figure.canvas.button_release_event(x, y, button, guiEvent=event)

    def mouseDoubleClickEvent(self, event):
        x, y = self.map_from_qt_to_matplotlib(event.pos())
        button = self.BUTTON_MAP.get(event.button())

        if not button:
            return

        self._figure.canvas.button_press_event(x,
                                               y,
                                               button,
                                               dblclick=True,
                                               guiEvent=event)

    figure_changed = Signal()
    figure = Property("QVariant",
                      get_figure,
                      set_figure,
                      notify=figure_changed)
class DevicesFilterProxyModel(QSortFilterProxyModel, QObject):
    _changed_front_page_ = Signal(bool)
    _changed_filter_class_ = Signal(str)
    _changed_filter_text_ = Signal(str)
    _changed_selected_index_ = Signal()

    _front_page_ = True
    _filter_text_ = ''
    _filter_class_ = 0
    _selected_index_ = -1

    def __init__(self):
        super().__init__()

    def get_front_page(self):
        return self._front_page_

    def set_front_page(self, fp):
        if self._front_page_ != fp:
            self._front_page_ = fp
            self._changed_front_page_.emit(self._front_page_)
            self.invalidateFilter()

    def get_filter_text(self):
        return self._filter_text_

    def set_filter_text(self, new_filter):
        if self._filter_text_ != new_filter:
            self._filter_text_ = new_filter
            self._changed_filter_text_.emit(self._filter_text_)
            self.invalidateFilter()

    def get_filter_class(self):
        return self._filter_class_

    def set_filter_class(self, new_filter):
        if self._filter_class_ != new_filter:
            self._filter_class_ = new_filter
            self._changed_filter_class_.emit(self._filter_class_)
            self.invalidateFilter()

    def set_selected_index(self, row):
        if self._selected_index_ != row:
            self._selected_index_ = row
            self._changed_selected_index_.emit()

    def get_selected_index(self):
        return self._selected_index_

    @Slot(str, result=str)
    def get_property(self, p):
        index = self.sourceModel().index(self._selected_index_, 0)
        # _logger.debug(f'get_property slot: {p}, {DeviceRoles[p]}, {index.data(DeviceRoles[p])}')
        return index.data(DeviceRoles[p])

    selected_index = Property(int,
                              get_selected_index,
                              set_selected_index,
                              notify=_changed_selected_index_)
    front_page = Property(bool,
                          get_front_page,
                          set_front_page,
                          notify=_changed_front_page_)
    filter_text = Property(str,
                           get_filter_text,
                           set_filter_text,
                           notify=_changed_filter_text_)

    def filterAcceptsRow(self, source_row, source_parent: QModelIndex):
        index = self.sourceModel().index(source_row, 0, source_parent)
        if self.front_page:
            connected = index.data(DeviceRoles.CONNECTED)
            return True if connected and source_row < 4 else False
        else:
            if len(self._filter_text_) == 0:
                return True
            else:
                product_name = index.data(DeviceRoles.PRODUCT_NAME)
                device_class = index.data(DeviceRoles.DEVICE_CLASS)
                product_key = index.data(DeviceRoles.PRODUCT_KEY)

                re_name = re.search(self._filter_text_, product_name,
                                    re.IGNORECASE) is not None
                re_class = re.search(self._filter_text_, device_class,
                                     re.IGNORECASE) is not None
                re_key = re.search(self._filter_text_, product_key,
                                   re.IGNORECASE) is not None
                re_filter = re_name or re_class or re_key
                # _logger.debug(f're filter ({name}: {re_filter}')
                return re_filter