Beispiel #1
0
class MainWidget(QWidget):
    def __init__(self, parent=None):
        super(MainWidget, self).__init__(parent)

        # define tree view
        self.treeView = QTreeView(self)

        # insert line edit
        self.lineEdit1 = QLineEdit(self)
        self.lineEdit2 = QLineEdit(self)

        # insert a button group widget
        self.buttonGroupWidget1 = ButtonGroupWidget(self)
        self.buttonGroupWidget1.setTitle("Select option")
        self.buttonGroupWidget1.addRadioButton("Option 1", 1)
        self.buttonGroupWidget1.addRadioButton("Option 2", 2)
        self.buttonGroupWidget1.addRadioButton("Option 3", 3)

        layoutMain = QVBoxLayout(self)
        layoutMain.addWidget(self.treeView)
        layoutMain.addWidget(self.lineEdit1)
        layoutMain.addWidget(self.lineEdit2)
        layoutMain.addWidget(self.buttonGroupWidget1)

        # Create the data model and map the model to the widgets.
        self._model = DataModel()
        self.treeView.setModel(self._model)

        self._dataMapper = QDataWidgetMapper()
        self._dataMapper.setModel(self._model)
        # the mapping works fine for line edits and combo boxes
        self._dataMapper.addMapping(self.lineEdit1, 0)
        self._dataMapper.addMapping(self.lineEdit2, 1)
        # mapping to custom property
        self._dataMapper.addMapping(self.buttonGroupWidget1, 1,
                                    "selectedOption")

        self.treeView.selectionModel().currentChanged.connect(
            self.setSelection)

    def setSelection(self, current):
        parent = current.parent()
        # self._dataMapper.setRootIndex(parent)
        self._dataMapper.setCurrentModelIndex(current)
Beispiel #2
0
class PropertyEditor(QTreeWidget):
    def __init__(self, model, nodeFactory, fieldFactory):
        super(PropertyEditor, self).__init__()
        self._dataMapper = QDataWidgetMapper()
        self._dataMapper.setModel(model)
        self._dataMapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit)
        self._model = model
        self._currentIndex = None
        self._fieldFactory = fieldFactory
        self._nodeFactory = nodeFactory
        # setup ui
        self.setColumnCount(2)
        self.setHeaderLabels(['Name', 'Value'])
        self.setSelectionMode(QAbstractItemView.NoSelection)
        self.setAlternatingRowColors(True)
        self.setFocusPolicy(Qt.NoFocus)
        # create all items per component
        self._createAllItems()
        self.resizeColumnToContents(0)
        self.setColumnWidth(0, self.columnWidth(0) + 20)

    def _createAllItems(self):
        for type, properties in self._allProperties().items():
            topItem = self._createComponentItem(type)
            if type == ComponentType.General:
                self._createNodeTypeItem(topItem)
            for property in properties:
                item = PropertyEditorItem(topItem)
                item.setText(0, property.label())
                field = self._fieldFactory.create(property)
                self.setItemWidget(item, 1, field)

    def _allProperties(self):
        result = {}
        for type in NodeType.All():
            node = self._nodeFactory.create(type)
            for component in node.components().values():
                result[component.type()] = component.propertyMap()
        return result

    def _createComponentItem(self, type):
        names = ComponentType.Names()
        item = PropertyEditorItem()
        item.setText(0, names[type])
        item.setData(0, Qt.UserRole, type)
        self.addTopLevelItem(item)
        item.setExpanded(True)
        return item

    def _createNodeTypeItem(self, topItem):
        self._nodeTypeItem = PropertyEditorItem(topItem)
        self._nodeTypeItem.setText(0, 'Type')
        font = self._nodeTypeItem.font(
            1
        )  # don't ask, this is how it will be the same size as in the editors
        font.setPointSize(font.pointSize())
        self._nodeTypeItem.setFont(1, font)

    def changeSelection(self, current, prev):
        node = current.internalPointer()
        self._currentIndex = current
        items = self._itemsForNode(node)
        self._setMapping(current, items)

    def _itemsForNode(self, node):
        result = []
        names = NodeType.Names()
        self._nodeTypeItem.setText(1, names[node.type()])
        for i in range(self.topLevelItemCount()):
            item = self.topLevelItem(i)
            type = item.data(0, Qt.UserRole)
            component = node.component(type)
            item.setHidden(not component)
            if component is None:
                continue
            result.extend(self._itemsForComponent(component, item))
        return result

    def _itemsForComponent(self, component, parentItem):
        result = []
        for column, property in enumerate(component.propertyMap()):
            offset = 1 if component.type(
            ) == ComponentType.General else 0  # the first item of General is Type
            item = parentItem.child(column + offset)
            item.setProperty(property)
            result.append(item)
        return result

    def _setMapping(self, current, items):
        self._dataMapper.clearMapping()
        fields = []
        for item in items:
            fields.append(self._mapField(current, item))
        parent = current.parent()
        self._dataMapper.setRootIndex(parent)
        self._dataMapper.setCurrentModelIndex(current)
        for field in fields:
            field.changed.connect(
                self._dataMapper.submit
            )  # this MUST be after the setCurrentModelIndex

    def _mapField(self, current, item):
        property = item.property()
        if not property:  # General/Type doesn't have a property
            return
        field = self.itemWidget(item, 1)
        parent = current.parent()
        readOnly = property.readOnly(
        ) or not parent.isValid()  # the top level nodes are read only
        field.setReadOnly(readOnly)
        self._dataMapper.addMapping(field, property.column(), b'valueProperty')
        return field
class AbstractOperationDetails(QWidget):
    dbUpdated = Signal()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.model = None
        self.table_name = ''
        self.mapper = None
        self.modified = False
        self.name = "N/A"

        self.layout = QGridLayout(self)
        self.layout.setContentsMargins(2, 2, 2, 2)

        self.bold_font = QFont()
        self.bold_font.setBold(True)
        self.bold_font.setWeight(75)

        self.main_label = QLabel(self)
        self.main_label.setFont(self.bold_font)
        self.layout.addWidget(self.main_label, 0, 0, 1, 1, Qt.AlignLeft)

        self.commit_button = QPushButton(self)
        self.commit_button.setEnabled(False)
        self.commit_button.setText("✔")
        self.commit_button.setFont(self.bold_font)
        self.commit_button.setFixedWidth(
            self.commit_button.fontMetrics().width("XXX"))
        self.revert_button = QPushButton(self)
        self.revert_button.setEnabled(False)
        self.revert_button.setText("✖️")
        self.revert_button.setFont(self.bold_font)
        self.revert_button.setFixedWidth(
            self.revert_button.fontMetrics().width("XXX"))

        self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum,
                                          QSizePolicy.Expanding)
        self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                            QSizePolicy.Minimum)

    def _init_db(self, table_name):
        self.table_name = table_name
        self.model = QSqlTableModel(parent=self, db=db_connection())
        self.model.setTable(table_name)
        self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)

        self.mapper = QDataWidgetMapper(self.model)
        self.mapper.setModel(self.model)
        self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)

        self.model.dataChanged.connect(self.onDataChange)
        self.commit_button.clicked.connect(self.saveChanges)
        self.revert_button.clicked.connect(self.revertChanges)

    def isCustom(self):
        return True

    def setId(self, id):
        self.model.setFilter(f"id={id}")
        self.mapper.setCurrentModelIndex(self.model.index(0, 0))

    @Slot()
    def onDataChange(self, _index_start, _index_stop, _role):
        self.modified = True
        self.commit_button.setEnabled(True)
        self.revert_button.setEnabled(True)

    @Slot()
    def saveChanges(self):
        if not self.model.submitAll():
            logging.fatal(
                g_tr('AbstractOperationDetails', "Operation submit failed: ") +
                self.model.lastError().text())
            return False
        self.modified = False
        self.commit_button.setEnabled(False)
        self.revert_button.setEnabled(False)
        self.dbUpdated.emit()
        return True

    @Slot()
    def revertChanges(self):
        self.model.revertAll()
        self.modified = False
        self.commit_button.setEnabled(False)
        self.revert_button.setEnabled(False)

    def createNew(self, account_id=0):
        self.mapper.submit(
        )  # FIXME there is check for uncommited call before - do we need submit() here?
        self.model.setFilter(f"{self.table_name}.id = 0")
        new_record = self.prepareNew(account_id)
        assert self.model.insertRows(0, 1)
        self.model.setRecord(0, new_record)
        self.mapper.toLast()

    def prepareNew(self, account_id):
        new_record = self.model.record()
        return new_record

    def copyNew(self):
        row = self.mapper.currentIndex()
        new_record = self.copyToNew(row)
        self.model.setFilter(f"{self.table_name}.id = 0")
        assert self.model.insertRows(0, 1)
        self.model.setRecord(0, new_record)
        self.mapper.toLast()

    def copyToNew(self, row):
        new_record = self.model.record(row)
        return new_record