Exemple #1
0
class _ExtractSeriesWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        # {series name: {frame name: [ (Attribute, QPersistentModelIndex) ]
        self.seriesOptions: Dict[str, Dict[str, List[Tuple[
            int, QPersistentModelIndex]]]] = dict()
        # {frame name: attribute model}
        self.models: Dict[str, CustomProxyAttributeModel] = dict()
        self.seriesView = CustomSignalView(parent=self)
        self.seriesModel = CustomStringListModel(self)
        self.seriesModel.setHeaderLabel('Series name')
        self.addSeriesButton = QPushButton('Add', self)
        self.removeSeriesButton = QPushButton('Remove', self)
        self.addSeriesButton.clicked.connect(self.addSeries)
        self.removeSeriesButton.clicked.connect(self.removeSeries)
        self.seriesView.setModel(self.seriesModel)
        self.seriesView.setDragDropMode(QTableView.InternalMove)
        self.seriesView.setDragDropOverwriteMode(False)
        self.seriesView.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.seriesView.verticalHeader().hide()

        # Connect selection to change
        self.seriesView.selectedRowChanged[str, str].connect(
            self.onSeriesSelectionChanged)
        # When a series is added it should be immediately edited
        self.seriesModel.rowAppended.connect(self.editSeriesName)
        self.seriesModel.rowsInserted.connect(self.checkNoSeries)
        self.seriesModel.rowsRemoved.connect(self.checkNoSeries)

        self.workbenchView = WorkbenchView(self, editable=False)
        self.workbench: WorkbenchModel = None
        self.workbenchView.selectedRowChanged[str, str].connect(
            self.onFrameSelectionChanged)

        self.attributesView = SearchableAttributeTableWidget(
            self, True, False, False, [Types.Numeric, Types.Ordinal])

        firstRowLayout = QHBoxLayout()
        firstRowLayout.setSpacing(5)

        selectionGroup = QGroupBox(
            title=
            'Select a time series. Then select the columns to add from the current '
            'datasets',
            parent=self)
        firstRowLayout.addWidget(self.seriesView)
        buttonLayout = QVBoxLayout()
        buttonLayout.addWidget(self.addSeriesButton)
        buttonLayout.addWidget(self.removeSeriesButton)
        firstRowLayout.addLayout(buttonLayout)
        firstRowLayout.addSpacing(30)
        firstRowLayout.addWidget(self.workbenchView)
        firstRowLayout.addSpacing(30)
        firstRowLayout.addWidget(self.attributesView)
        selectionGroup.setLayout(firstRowLayout)

        # Time axis labels model with add/remove buttons
        self.timeAxisModel = CustomStringListModel(self)
        self.timeAxisModel.setHeaderLabel('Time labels')
        self.timeAxisView = CustomSignalView(self)
        self.timeAxisView.setModel(self.timeAxisModel)
        self.addTimeButton = QPushButton('Add', self)
        self.removeTimeButton = QPushButton('Remove', self)
        self.addTimeButton.clicked.connect(self.addTimeLabel)
        self.removeTimeButton.clicked.connect(self.removeTimeLabel)
        self.timeAxisView.setDragDropMode(QTableView.InternalMove)
        self.timeAxisView.setDragDropOverwriteMode(False)
        self.timeAxisView.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.timeAxisView.verticalHeader().hide()
        self.timeAxisModel.rowAppended.connect(self.editTimeLabelName)

        # Concatenation model
        self.timeSeriesDataModel = ConcatenatedModel(self)
        self.timeSeriesDataView = QTableView(self)
        self.timeSeriesDataView.setSelectionMode(QTableView.NoSelection)
        self.timeSeriesDataView.setItemDelegateForColumn(
            1, ComboBoxDelegate(self.timeAxisModel, self.timeSeriesDataView))
        self.timeSeriesDataView.setEditTriggers(QTableView.CurrentChanged
                                                | QTableView.DoubleClicked)
        self.timeSeriesDataView.verticalHeader().hide()
        # Update the label column when some label changes in the label table
        self.timeAxisModel.dataChanged.connect(
            self.timeSeriesDataModel.timeAxisLabelChanged)

        groupTime = QGroupBox(
            title=
            'Add the time points (ordered) and set the correspondence to every selected column',
            parent=self)
        secondRowLayout = QHBoxLayout()
        secondRowLayout.setSpacing(5)
        # labelLayout = QVBoxLayout()
        # lab = QLabel('Here you should define every time point, in the correct order. After adding '
        #              'double-click a row to edit the point name and drag rows to reorder them', self)
        # lab.setWordWrap(True)
        # labelLayout.addWidget(lab)
        # labelLayout.addWidget(self.timeAxisView)
        secondRowLayout.addWidget(self.timeAxisView)
        timeButtonLayout = QVBoxLayout()
        timeButtonLayout.addWidget(self.addTimeButton)
        timeButtonLayout.addWidget(self.removeTimeButton)
        secondRowLayout.addLayout(timeButtonLayout)
        secondRowLayout.addSpacing(30)
        # labelLayout = QVBoxLayout()
        # lab = QLabel('Every selected column for the current series will be listed here. Click the right '
        #              'column of the table to set the time label associated with every original column',
        #              self)
        # lab.setWordWrap(True)
        # labelLayout.addWidget(lab)
        secondRowLayout.addWidget(self.timeSeriesDataView)
        # secondRowLayout.addLayout(labelLayout)
        groupTime.setLayout(secondRowLayout)

        self.outputName = QLineEdit(self)
        self.warningLabel = MessageLabel(text='',
                                         color='orange',
                                         icon=QMessageBox.Warning,
                                         parent=self)
        lastRowLayout = QFormLayout()
        lastRowLayout.addRow('Output variable name:', self.outputName)
        self.outputName.setPlaceholderText('Output name')
        lastRowLayout.setVerticalSpacing(0)
        lastRowLayout.addRow('', self.warningLabel)
        lastRowLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
        self.warningLabel.hide()
        self.outputName.textChanged.connect(self.checkOutputName)

        layout = QVBoxLayout(self)
        layout.addWidget(selectionGroup)
        layout.addWidget(groupTime)
        layout.addLayout(lastRowLayout)
        self.checkNoSeries()

    def setWorkbench(self, w: WorkbenchModel) -> None:
        """
        Sets the workbench and initialises every attribute model (one for each frame)
        """
        self.workbench = w
        self.workbenchView.setModel(w)
        # Set a default name for output
        if self.workbench:
            name = 'time_series_{:d}'
            n = 1
            name_n = name.format(n)
            while name_n in self.workbench.names:
                n += 1
                name_n = name.format(n)
            self.outputName.setText(name_n)

    def addSourceFrameModel(self, frameName: str) -> None:
        if self.workbench:
            dfModel = self.workbench.getDataframeModelByName(frameName)
            # Create an attribute model with checkboxes
            standardModel = AttributeTableModel(self,
                                                checkable=True,
                                                editable=False,
                                                showTypes=True)
            standardModel.setFrameModel(dfModel)
            # Create a proxy to filter data in the concatenation
            customProxy = CustomProxyAttributeModel(self)
            customProxy.setSourceModel(standardModel)
            # Add proxy to the list of models
            self.models[frameName] = customProxy
            # Add proxy as source model
            self.timeSeriesDataModel.addSourceModel(customProxy)

    @Slot()
    def checkNoSeries(self) -> None:
        if not self.seriesModel.rowCount():
            self.workbenchView.setEnabled(False)
            self.attributesView.setEnabled(False)
            self.timeAxisView.setEnabled(False)
            self.addTimeButton.setEnabled(False)
            self.removeTimeButton.setEnabled(False)
            self.timeSeriesDataView.setEnabled(False)
        else:
            self.workbenchView.setEnabled(True)
            self.attributesView.setEnabled(True)
            self.timeAxisView.setEnabled(True)
            self.addTimeButton.setEnabled(True)
            self.removeTimeButton.setEnabled(True)
            self.timeSeriesDataView.setEnabled(True)

    def persistOptionsSetForSeries(self, seriesName: str) -> None:
        if seriesName:
            seriesValues: Dict[str,
                               List[Tuple[int,
                                          QPersistentModelIndex]]] = dict()
            for r in range(self.timeSeriesDataModel.rowCount()):
                column0Index: QModelIndex = self.timeSeriesDataModel.index(
                    r, 0, QModelIndex())
                column1Index: QModelIndex = self.timeSeriesDataModel.index(
                    r, 1, QModelIndex())
                sourceIndex: QModelIndex = self.timeSeriesDataModel.mapToSource(
                    column0Index)
                proxy: CustomProxyAttributeModel = sourceIndex.model()
                frameName: str = proxy.sourceModel().frameModel().name
                attrIndexInFrame: int = proxy.mapToSource(sourceIndex).row()
                timeLabelIndex: QPersistentModelIndex = column1Index.data(
                    Qt.DisplayRole)
                if seriesValues.get(frameName, None):
                    seriesValues[frameName].append(
                        (attrIndexInFrame, timeLabelIndex))
                else:
                    seriesValues[frameName] = [(attrIndexInFrame,
                                                timeLabelIndex)]
            self.seriesOptions[seriesName] = seriesValues

    @Slot(str, str)
    def onSeriesSelectionChanged(self, new: str, old: str) -> None:
        # Save current set options
        self.persistOptionsSetForSeries(old)
        if new:
            # Get options of new selection
            newOptions: Dict[str, List[Tuple[int, QPersistentModelIndex]]] = \
                self.seriesOptions.get(new, dict())
            for frameName, proxyModel in self.models.items():
                frameOptions = newOptions.get(frameName, None)
                self.setOptionsForFrame(frameName, frameOptions)
                # Update proxy view on the time label columns
                proxyModel.dataChanged.emit(
                    proxyModel.index(0, 1, QModelIndex()),
                    proxyModel.index(proxyModel.rowCount() - 1, 1,
                                     QModelIndex()),
                    [Qt.DisplayRole, Qt.EditRole])
        # Every time series change clear frame selection in workbench
        self.workbenchView.clearSelection()

    @Slot(str, str)
    def onFrameSelectionChanged(self, newFrame: str, _: str) -> None:
        if not newFrame:
            # Nothing is selected
            return self.attributesView.setAttributeModel(
                AttributeTableModel(self))
        # Check if frame is already in the source models
        if newFrame not in self.models.keys():
            # Create a new proxy and add it to source models
            self.addSourceFrameModel(newFrame)
            if len(self.models) == 1:
                # If it is the first model added then set up the view
                self.timeSeriesDataView.setModel(self.timeSeriesDataModel)
                self.timeSeriesDataView.horizontalHeader(
                ).setSectionResizeMode(0, QHeaderView.Stretch)
                self.timeSeriesDataView.horizontalHeader(
                ).setSectionResizeMode(1, QHeaderView.Stretch)
        # Update the attribute table
        self.attributesView.setAttributeModel(
            self.models[newFrame].sourceModel())

    def setOptionsForFrame(
            self, frameName: str,
            options: Optional[List[Tuple[int,
                                         QPersistentModelIndex]]]) -> None:
        customProxyModel = self.models[frameName]
        attributeTableModel = customProxyModel.sourceModel()
        attributeTableModel.setAllChecked(False)
        if options:
            proxySelection: Dict[int, QPersistentModelIndex] = {
                i: pmi
                for i, pmi in options
            }
            customProxyModel.attributes = proxySelection
            attributeTableModel.setChecked(list(proxySelection.keys()),
                                           value=True)
        else:
            customProxyModel.attributes = dict()

    @Slot()
    def addSeries(self) -> None:
        # Append new row
        self.seriesModel.appendEmptyRow()
        # In oder to avoid copying previous options
        for frameName, proxyModel in self.models.items():
            self.setOptionsForFrame(frameName, None)

    @Slot()
    def removeSeries(self) -> None:
        selected: List[QModelIndex] = self.seriesView.selectedIndexes()
        if selected:
            seriesName: str = selected[0].data(Qt.DisplayRole)
            # Remove row
            self.seriesModel.removeRow(selected[0].row())
            # Remove options for series if they exists
            self.seriesOptions.pop(seriesName, None)

    @Slot()
    def addTimeLabel(self) -> None:
        self.timeAxisModel.appendEmptyRow()

    @Slot()
    def removeTimeLabel(self) -> None:
        selected: List[QModelIndex] = self.timeAxisView.selectedIndexes()
        if selected:
            self.timeAxisModel.removeRow(selected[0].row())
            # Update model
            self.timeSeriesDataModel.dataChanged.emit(
                self.timeSeriesDataModel.index(0, 1, QModelIndex()),
                self.timeSeriesDataModel.index(
                    self.timeSeriesDataModel.rowCount() - 1, 1, QModelIndex()),
                [Qt.DisplayRole, Qt.EditRole])

    @Slot()
    def editSeriesName(self) -> None:
        index = self.seriesModel.index(self.seriesModel.rowCount() - 1, 0,
                                       QModelIndex())
        self.seriesView.setCurrentIndex(index)
        self.seriesView.edit(index)

    @Slot()
    def editTimeLabelName(self) -> None:
        index = self.timeAxisModel.index(self.timeAxisModel.rowCount() - 1, 0,
                                         QModelIndex())
        self.timeAxisView.setCurrentIndex(index)
        self.timeAxisView.edit(index)

    @Slot(str)
    def checkOutputName(self, text: str) -> None:
        if self.workbench and text in self.workbench.names:
            self.warningLabel.setText(
                'Variable {:s} will be overwritten'.format(text))
            self.warningLabel.show()
        else:
            self.warningLabel.hide()
class FieldNameListEditor(QWidget):
    """A widget to edit foreign keys' field name lists."""

    data_committed = Signal(name="data_committed")

    def __init__(self, parent, option, index):
        """Initialize class."""
        super().__init__(parent)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.model = MinimalTableModel(self)
        self.model.flags = self.model_flags
        self.view = QTableView(self)
        self.view.setModel(self.model)
        self.view.verticalHeader().hide()
        self.view.horizontalHeader().hide()
        self.view.setShowGrid(False)
        check_box_delegate = CheckBoxDelegate(self)
        self.view.setItemDelegateForColumn(0, check_box_delegate)
        check_box_delegate.data_committed.connect(
            self._handle_check_box_data_committed)
        self.button = QPushButton("Ok", self)
        self.button.setFlat(True)
        self.view.verticalHeader().setDefaultSectionSize(option.rect.height())
        self.button.setFixedHeight(option.rect.height())
        layout.addWidget(self.view)
        layout.addWidget(self.button)
        self.button.clicked.connect(self._handle_ok_button_clicked)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)
        x_offset = parent.parent().columnViewportPosition(index.column())
        y_offset = parent.parent().rowViewportPosition(index.row())
        self.position = parent.mapToGlobal(QPoint(0, 0)) + QPoint(
            x_offset, y_offset)

    def model_flags(self, index):
        """Return index flags."""
        if not index.isValid():
            return Qt.NoItemFlags
        if index.column() != 0:
            return ~Qt.ItemIsEditable
        return Qt.ItemIsEditable

    @Slot("QModelIndex", name="_handle_check_box_data_committed")
    def _handle_check_box_data_committed(self, index):
        """Called when checkbox delegate wants to edit data. Toggle the index's value."""
        data = index.data(Qt.EditRole)
        self.model.setData(index, not data)

    @Slot("bool", name="_handle_ok_button_clicked")
    def _handle_ok_button_clicked(self, checked=False):
        """Called when user pressed Ok."""
        self.data_committed.emit()

    def set_data(self, field_names, current_field_names):
        """Set values to show in the 'menu'. Reset model using those values and update geometry."""
        data = [[name in current_field_names, name] for name in field_names]
        self.model.reset_model(data)
        self.view.resizeColumnsToContents()
        width = self.view.horizontalHeader().length() + qApp.style(
        ).pixelMetric(QStyle.PM_ScrollBarExtent)
        self.setFixedWidth(width + 2)
        height = self.view.verticalHeader().length() + self.button.height()
        parent_height = self.parent().height()
        self.setFixedHeight(min(height, parent_height / 2) + 2)
        self.move(self.position)

    def data(self):
        return ",".join(
            [name for checked, name in self.model._main_data if checked])
Exemple #3
0
class AutoFilterWidget(QWidget):
    """A widget to show the auto filter 'menu'."""
    def __init__(self, parent):
        """Initialize class."""
        super().__init__(parent)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.model = MinimalTableModel(self)
        self.model.flags = self.model_flags
        self.view = QTableView(self)
        self.view.setModel(self.model)
        self.view.verticalHeader().hide()
        self.view.horizontalHeader().hide()
        self.view.setShowGrid(False)
        check_box_delegate = CheckBoxDelegate(self)
        self.view.setItemDelegateForColumn(0, check_box_delegate)
        check_box_delegate.data_committed.connect(
            self._handle_check_box_data_committed)
        self.button = QPushButton("Ok", self)
        self.button.setFlat(True)
        layout.addWidget(self.view)
        layout.addWidget(self.button)
        self.button.clicked.connect(self.hide)
        self.hide()
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)

    def model_flags(self, index):
        """Return index flags."""
        if not index.isValid():
            return Qt.NoItemFlags
        if index.column() == 1:
            return ~Qt.ItemIsEditable
        return Qt.ItemIsEditable

    @Slot("QModelIndex", name="_handle_check_box_data_committed")
    def _handle_check_box_data_committed(self, index):
        """Called when checkbox delegate wants to edit data. Toggle the index's value."""
        data = index.data(Qt.EditRole)
        model_data = self.model._main_data
        row_count = self.model.rowCount()
        if index.row() == 0:
            # Ok row
            value = data in (None, False)
            for row in range(row_count):
                model_data[row][0] = value
            self.model.dataChanged.emit(self.model.index(0, 0),
                                        self.model.index(row_count - 1, 0))
        else:
            # Data row
            self.model.setData(index, not data)
            self.set_ok_index_data()

    def set_ok_index_data(self):
        """Set data for ok index based on data from all other indexes."""
        ok_index = self.model.index(0, 0)
        true_count = 0
        for row_data in self.model._main_data[1:]:
            if row_data[0] == True:
                true_count += 1
        if true_count == len(self.model._main_data) - 1:
            self.model.setData(ok_index, True)
        elif true_count == 0:
            self.model.setData(ok_index, False)
        else:
            self.model.setData(ok_index, None)

    def set_values(self, values):
        """Set values to show in the 'menu'. Reset model using those values and update geometry."""
        self.model.reset_model([[None, "All"]] + values)
        self.set_ok_index_data()
        self.view.horizontalHeader().hideSection(
            2)  # Column 2 holds internal data (cls_id_set)
        self.view.resizeColumnsToContents()
        width = self.view.horizontalHeader().length() + qApp.style(
        ).pixelMetric(QStyle.PM_ScrollBarExtent)
        self.setFixedWidth(width + 2)
        height = self.view.verticalHeader().length() + self.button.height()
        parent_height = self.parent().height()
        self.setFixedHeight(min(height, parent_height / 2) + 2)

    def set_section_height(self, height):
        """Set vertical header default section size as well as button height."""
        self.view.verticalHeader().setDefaultSectionSize(height)
        self.button.setFixedHeight(height)
Exemple #4
0
class IncomeSpendingWidget(AbstractOperationDetails):
    def __init__(self, parent=None):
        AbstractOperationDetails.__init__(self, parent)
        self.name = "Income/Spending"

        self.details_model = None
        self.category_delegate = CategorySelectorDelegate()
        self.tag_delegate = TagSelectorDelegate()
        self.float_delegate = FloatDelegate(2)

        self.date_label = QLabel(self)
        self.details_label = QLabel(self)
        self.account_label = QLabel(self)
        self.peer_label = QLabel(self)

        self.main_label.setText(g_tr("IncomeSpendingWidget", "Income / Spending"))
        self.date_label.setText(g_tr("IncomeSpendingWidget", "Date/Time"))
        self.details_label.setText(g_tr("IncomeSpendingWidget", "Details"))
        self.account_label.setText(g_tr("IncomeSpendingWidget", "Account"))
        self.peer_label.setText(g_tr("IncomeSpendingWidget", "Peer"))

        self.timestamp_editor = QDateTimeEdit(self)
        self.timestamp_editor.setCalendarPopup(True)
        self.timestamp_editor.setTimeSpec(Qt.UTC)
        self.timestamp_editor.setFixedWidth(self.timestamp_editor.fontMetrics().width("00/00/0000 00:00:00") * 1.25)
        self.timestamp_editor.setDisplayFormat("dd/MM/yyyy hh:mm:ss")
        self.account_widget = AccountSelector(self)
        self.peer_widget = PeerSelector(self)
        self.a_currency = OptionalCurrencyComboBox(self)
        self.a_currency.setText(g_tr("IncomeSpendingWidget", "Paid in foreign currency:"))
        self.add_button = QPushButton(self)
        self.add_button.setText(" +️ ")
        self.add_button.setFont(self.bold_font)
        self.add_button.setFixedWidth(self.add_button.fontMetrics().width("XXX"))
        self.del_button = QPushButton(self)
        self.del_button.setText(" — ️")
        self.del_button.setFont(self.bold_font)
        self.del_button.setFixedWidth(self.del_button.fontMetrics().width("XXX"))
        self.copy_button = QPushButton(self)
        self.copy_button.setText(" >> ️")
        self.copy_button.setFont(self.bold_font)
        self.copy_button.setFixedWidth(self.copy_button.fontMetrics().width("XXX"))
        self.details_table = QTableView(self)
        self.details_table.horizontalHeader().setFont(self.bold_font)
        self.details_table.setAlternatingRowColors(True)
        self.details_table.verticalHeader().setVisible(False)
        self.details_table.verticalHeader().setMinimumSectionSize(20)
        self.details_table.verticalHeader().setDefaultSectionSize(20)

        self.layout.addWidget(self.date_label, 1, 0, 1, 1, Qt.AlignLeft)
        self.layout.addWidget(self.details_label, 2, 0, 1, 1, Qt.AlignLeft)

        self.layout.addWidget(self.timestamp_editor, 1, 1, 1, 4)
        self.layout.addWidget(self.add_button, 2, 1, 1, 1)
        self.layout.addWidget(self.copy_button, 2, 2, 1, 1)
        self.layout.addWidget(self.del_button, 2, 3, 1, 1)

        self.layout.addWidget(self.account_label, 1, 5, 1, 1, Qt.AlignRight)
        self.layout.addWidget(self.peer_label, 2, 5, 1, 1, Qt.AlignRight)

        self.layout.addWidget(self.account_widget, 1, 6, 1, 1)
        self.layout.addWidget(self.peer_widget, 2, 6, 1, 1)

        self.layout.addWidget(self.a_currency, 1, 7, 1, 1)

        self.layout.addWidget(self.commit_button, 0, 9, 1, 1)
        self.layout.addWidget(self.revert_button, 0, 10, 1, 1)

        self.layout.addWidget(self.details_table, 4, 0, 1, 11)
        self.layout.addItem(self.horizontalSpacer, 1, 8, 1, 1)

        self.add_button.clicked.connect(self.addChild)
        self.del_button.clicked.connect(self.delChild)

        super()._init_db("actions")
        self.mapper.setItemDelegate(IncomeSpendingWidgetDelegate(self.mapper))

        self.details_model = DetailsModel(self.details_table, db_connection())
        self.details_model.setTable("action_details")
        self.details_model.setEditStrategy(QSqlTableModel.OnManualSubmit)
        self.details_table.setModel(self.details_model)
        self.details_model.dataChanged.connect(self.onDataChange)

        self.account_widget.changed.connect(self.mapper.submit)
        self.peer_widget.changed.connect(self.mapper.submit)
        self.a_currency.changed.connect(self.mapper.submit)
        self.a_currency.updated.connect(self.details_model.setAltCurrency)

        self.mapper.addMapping(self.timestamp_editor, self.model.fieldIndex("timestamp"))
        self.mapper.addMapping(self.account_widget, self.model.fieldIndex("account_id"))
        self.mapper.addMapping(self.peer_widget, self.model.fieldIndex("peer_id"))
        self.mapper.addMapping(self.a_currency, self.model.fieldIndex("alt_currency_id"))

        self.details_table.setItemDelegateForColumn(2, self.category_delegate)
        self.details_table.setItemDelegateForColumn(3, self.tag_delegate)
        self.details_table.setItemDelegateForColumn(4, self.float_delegate)
        self.details_table.setItemDelegateForColumn(5, self.float_delegate)

        self.model.select()
        self.details_model.select()
        self.details_model.configureView()

    def setId(self, id):
        super().setId(id)
        self.details_model.setFilter(f"action_details.pid = {id}")

    @Slot()
    def addChild(self):
        new_record = self.details_model.record()
        new_record.setNull("tag_id")
        new_record.setValue("amount", 0)
        new_record.setValue("amount_alt", 0)
        if not self.details_model.insertRecord(-1, new_record):
            logging.fatal(
                g_tr('AbstractOperationDetails', "Failed to add new record: ") + self.details_model.lastError().text())
            return

    @Slot()
    def delChild(self):
        idx = self.details_table.selectionModel().selection().indexes()
        selected_row = idx[0].row()
        self.details_model.removeRow(selected_row)
        self.details_table.setRowHidden(selected_row, True)

    @Slot()
    def saveChanges(self):
        if not self.model.submitAll():
            logging.fatal(
                g_tr('AbstractOperationDetails', "Operation submit failed: ") + self.model.lastError().text())
            return
        pid = self.model.data(self.model.index(0, self.model.fieldIndex("id")))
        if pid is None:  # we just have saved new action record and need last inserted id
            pid = self.model.query().lastInsertId()
        for row in range(self.details_model.rowCount()):
            self.details_model.setData(self.details_model.index(row, self.details_model.fieldIndex("pid")), pid)
        if not self.details_model.submitAll():
            logging.fatal(g_tr('AbstractOperationDetails', "Operation details submit failed: ")
                          + self.details_model.lastError().text())
            return
        self.modified = False
        self.commit_button.setEnabled(False)
        self.revert_button.setEnabled(False)
        self.dbUpdated.emit()
        return

    def createNew(self, account_id=0):
        super().createNew(account_id)
        self.details_model.setFilter(f"action_details.pid = 0")

    def prepareNew(self, account_id):
        new_record = self.model.record()
        new_record.setNull("id")
        new_record.setValue("timestamp", int(datetime.now().replace(tzinfo=tz.tzutc()).timestamp()))
        new_record.setValue("account_id", account_id)
        new_record.setValue("peer_id", 0)
        new_record.setValue("alt_currency_id", None)
        return new_record

    def copyNew(self):
        old_id = self.model.record(self.mapper.currentIndex()).value(0)
        super().copyNew()
        self.details_model.setFilter(f"action_details.pid = 0")
        query = executeSQL("SELECT * FROM action_details WHERE pid = :pid ORDER BY id DESC",
                           [(":pid", old_id)])
        while query.next():
            new_record = query.record()
            new_record.setNull("id")
            new_record.setNull("pid")
            assert self.details_model.insertRows(0, 1)
            self.details_model.setRecord(0, new_record)

    def copyToNew(self, row):
        new_record = self.model.record(row)
        new_record.setNull("id")
        new_record.setValue("timestamp", int(datetime.now().replace(tzinfo=tz.tzutc()).timestamp()))
        return new_record
Exemple #5
0
        return False

    def setModelData(self, editor, model, index):
        '''
        The user wanted to change the old state in the opposite.
        '''
        model.setData(index, 1 if int(index.data()) == 0 else 0,
                      QtCore.Qt.EditRole)


if __name__ == '__main__':

    import sys

    app = QApplication(sys.argv)

    model = QStandardItemModel(4, 3)
    tableView = QTableView()
    tableView.setModel(model)

    delegate = CheckBoxDelegate(None)
    tableView.setItemDelegateForColumn(1, delegate)
    for row in range(4):
        for column in range(3):
            index = model.index(row, column, QModelIndex())
            model.setData(index, 1)

    tableView.setWindowTitle("Check Box Delegate")
    tableView.show()
    sys.exit(app.exec_())
class MainWin(QWidget):
    def __init__(self):
        super().__init__()
        self.dbConnector = cPl.DBConnector("timePlanner.db", "Task",
                                           ormMapping)
        self.taskStorage = cPl.TaskStorage(self.dbConnector, ormMapping)
        self.currentView = "Work"
        self.checkBox = QCheckBox('Minimize to Tray')
        self.checkBox.setChecked(True)
        self.createModels()
        self.grid = QGridLayout()
        self.tray_icon = QSystemTrayIcon()
        self.tray_icon.setToolTip("Time Planner")
        self.tray_icon.setIcon(self.style().standardIcon(
            QStyle.SP_ComputerIcon))
        traySignal = "activated(QSystemTrayIcon::ActivationReason)"
        QObject.connect(self.tray_icon, SIGNAL(traySignal),
                        self.__icon_activated)
        #appendFunc = anonFuncString("Total time")
        appendDataFinished = [
            cPl.AppendDataView(self.finishedModel, 3,
                               self.taskStorage.getTotalWorkTime,
                               workTimeFormat),
            cPl.AppendDataView(self.finishedModel, 2, anonFuncString)
        ]
        self.finishedModel.setAppendData(appendDataFinished)

        self.initUI()

    def hideEvent(self, event):
        if self.checkBox.isChecked():
            event.ignore()
            self.hide()
            self.tray_icon.show()

    def __icon_activated(self, reason):
        if reason == QSystemTrayIcon.Trigger:
            self.show()

    def __resizeView(self):
        self.view.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.Stretch)
        if self.currentView == "Work":
            self.view.horizontalHeader().setSectionResizeMode(
                1, QHeaderView.Fixed)
            self.view.horizontalHeader().setSectionResizeMode(
                2, QHeaderView.Fixed)
            self.view.horizontalHeader().setSectionResizeMode(
                3, QHeaderView.ResizeToContents)
            self.view.setColumnWidth(1, 140)
            self.view.setColumnWidth(2, 70)

            self.view.setColumnWidth(self.model.getHeaderLenght(), 70)
            self.view.setColumnWidth(self.model.getHeaderLenght() + 1, 70)
            self.view.setColumnWidth(self.model.getHeaderLenght() + 2, 70)
            self.view.setColumnWidth(self.model.getHeaderLenght() + 3, 70)

        else:
            self.view.horizontalHeader().setSectionResizeMode(
                1, QHeaderView.Fixed)
            self.view.horizontalHeader().setSectionResizeMode(
                2, QHeaderView.Fixed)
            self.view.horizontalHeader().setSectionResizeMode(
                3, QHeaderView.ResizeToContents)
            self.view.setColumnWidth(1, 140)
            self.view.setColumnWidth(2, 140)

    def initUI(self):
        self.taskStorage.Model = self.model
        grid = self.grid

        self.setLayout(grid)

        buttonCurrent = QPushButton('Current Task')
        buttonCurrent.setCheckable(True)
        buttonFinished = QPushButton('Finished Task')
        buttonFinished.setCheckable(True)
        buttonCurrent.setMaximumWidth(100)
        buttonFinished.setMaximumWidth(100)

        self.move(300, 150)
        self.setMinimumWidth(1100)
        self.setMinimumHeight(400)
        self.setWindowTitle('Time Planner')
        top_Panel = self.createTop()
        grid.addWidget(top_Panel, 0, 0)

        self.view = QTableView()
        self.view.setModel(self.model)
        stylesheet = "QHeaderView::section{color: grey; border: 2px solid #6c6c6c; border-width: 0px 0px 2px 0px; " \
                     "border-style: dotted; border-color: black} "
        self.view.setStyleSheet(stylesheet)
        self.__resizeView()
        #Create here else garbage collector clear it
        self.buttonStart = cPl.StartButtonDelegate(self.taskStorage, "taskId")
        self.buttonPause = cPl.PauseButtonDelegate(self.taskStorage, "taskId")
        self.buttonFinish = cPl.FinishButtonDelegate(self.taskStorage,
                                                     "taskId")
        self.buttonDelete = cPl.DeleteButtonDelegate(self.taskStorage,
                                                     "taskId", self)

        if self.currentView == "Work":
            self.view.setItemDelegateForColumn(self.model.getHeaderLenght(),
                                               self.buttonStart)
            self.view.setItemDelegateForColumn(
                self.model.getHeaderLenght() + 1, self.buttonPause)
            self.view.setItemDelegateForColumn(
                self.model.getHeaderLenght() + 2, self.buttonFinish)
            self.view.setItemDelegateForColumn(
                self.model.getHeaderLenght() + 3, self.buttonDelete)
            buttonCurrent.setChecked(True)
            buttonFinished.setChecked(False)
        else:
            self.view.setItemDelegateForColumn(self.model.getHeaderLenght(),
                                               self.buttonDelete)
            buttonCurrent.setChecked(False)
            buttonFinished.setChecked(True)

        viewPanel = self.createTaskView(self.view)
        grid.addWidget(viewPanel, 1, 0)
        buttonPanel = self.createButtonView()
        grid.addWidget(buttonPanel, 1, 1)

        grid.addWidget(self.checkBox, 2, 0)
        self.show()

    # Top panel - LineEdit for task name  + Add new task button
    def createTop(self):
        topPanel = QWidget()
        topPanel.setContentsMargins(0, 0, 0, 0)
        hBox = QHBoxLayout()
        vBox = QVBoxLayout()
        lineEdit = QLineEdit('')
        lineEdit.setPlaceholderText("Enter new task name")
        buttonAdd = QPushButton('Add Task')
        buttonAdd.clicked.connect(self.add_newTask(lineEdit))
        hBox.addWidget(lineEdit)
        lineEdit.setMinimumWidth(250)
        hBox.addWidget(buttonAdd)
        hBox.addStretch(1)
        vBox.addLayout(hBox)
        topPanel.setLayout(vBox)
        return topPanel

    def createTaskView(self, view):
        hBox = QHBoxLayout()
        viewPanel = QWidget()
        hBox.addWidget(view)
        viewPanel.setLayout(hBox)
        return viewPanel

    def createButtonView(self):
        buttons = []
        datePickers = []
        buttonPanel = QWidget()
        buttonPanel.setContentsMargins(0, 0, 0, 0)
        buttonCurrent = QPushButton('Current tasks')
        buttonCurrent.setCheckable(True)
        buttonFinished = QPushButton('Finished tasks')
        buttonFinished.setCheckable(True)
        buttonFilter = QPushButton("Filter by end date")
        buttonApply = QPushButton("Apply")
        buttonFilter.setCheckable(True)
        buttonCurrent.setMaximumWidth(100)
        buttonFinished.setMaximumWidth(100)
        buttonFilter.setMaximumWidth(100)
        buttonApply.setMaximumWidth(100)
        buttonCurrent.setMinimumWidth(100)
        buttonFinished.setMinimumWidth(100)
        buttonFilter.setMinimumWidth(100)
        buttonApply.setMinimumWidth(100)
        buttons.append(buttonFilter)

        #
        labelStart = QLabel("Date start")
        datePickers.append(labelStart)
        dateStart = QDateEdit()
        dateStart.setDisplayFormat('dd/MM/yyyy')
        dateStart.setCalendarPopup(True)
        dateStart.setDate(QDate.currentDate())

        datePickers.append(dateStart)
        labelEnd = QLabel("Date end")
        datePickers.append(labelEnd)
        dateEnd = QDateEdit()
        dateEnd.setDisplayFormat('dd/MM/yyyy')
        dateEnd.setCalendarPopup(True)
        dateEnd.setDate(QDate.currentDate())
        datePickers.append(dateEnd)
        datePickers.append(buttonApply)

        buttonCurrent.clicked.connect(
            self.switchCurrentTask(buttonFinished, buttonCurrent, buttons))
        buttonFinished.clicked.connect(
            self.switchFinishedTask(buttonCurrent, buttonFinished, buttons))
        buttonFilter.clicked.connect(
            self.openDatePickerFilter(datePickers, buttonFilter))
        buttonApply.clicked.connect(
            self.ApplyDateFilterArchive(dateStart, dateEnd))
        buttonCurrent.setChecked(True)

        vBox = QVBoxLayout()
        vBox.addWidget(buttonCurrent)
        vBox.addWidget(buttonFinished)
        vBox.addSpacing(30)
        for bnt in buttons:
            bnt.setVisible(False)
            vBox.addWidget(bnt)
        for datePicker in datePickers:
            datePicker.setVisible(False)
            vBox.addWidget(datePicker)

        vBox.addStretch(1)
        buttonPanel.setLayout(vBox)
        return buttonPanel

    def add_newTask(self, lineEdit):
        def call_sql():
            if lineEdit.text() != "":
                self.taskStorage.addTask(lineEdit.text())
                self.model.refresh()

        return call_sql

    def openDatePickerFilter(self, filterElements, clickBtn):
        def call():
            if not clickBtn.isChecked():
                for filterElement in filterElements:
                    filterElement.setVisible(False)
                    self.taskStorage.viewAllFinishedTask()
                    self.finishedModel.refresh()
            else:
                for filterElement in filterElements:
                    filterElement.setVisible(True)

        return call

    def ApplyDateFilterArchive(self, dateStart, dateEnd):
        def call():
            start = datetime.combine(dateStart.date().toPython(),
                                     datetime.min.time())
            end = datetime.combine(dateEnd.date().toPython(),
                                   datetime.min.time())
            startUTC = int(float(start.timestamp()))
            endUTC = int(float(end.timestamp()))
            self.finishedModel.switchToFilterData(startUTC, endUTC)
            self.finishedModel.refresh()

        return call

    def switchCurrentTask(self, buttonInAct, btnAct, btnsFilter):
        def call():
            if self.currentView != "Work":
                self.model.setAllTaskView()
                self.view.setModel(self.model)
                self.view.setItemDelegateForColumn(
                    self.model.getHeaderLenght(), self.buttonStart)
                self.view.setItemDelegateForColumn(
                    self.model.getHeaderLenght() + 1, self.buttonPause)
                self.view.setItemDelegateForColumn(
                    self.model.getHeaderLenght() + 2, self.buttonFinish)
                btnAct.setChecked(True)
                buttonInAct.setChecked(False)
                for btn in btnsFilter:
                    btn.setVisible(False)
                self.currentView = "Work"
                self.__resizeView()

        return call

    def switchFinishedTask(self, buttonInAct, btnAct, btnsFilter):
        def call():
            if self.currentView != "Archive":
                self.finishedModel.switchToAllDataView()
                self.view.setModel(self.finishedModel)
                self.view.setItemDelegateForColumn(
                    self.model.getHeaderLenght(), self.buttonDelete)
                btnAct.setChecked(True)
                buttonInAct.setChecked(False)
                for btn in btnsFilter:
                    btn.setVisible(True)
                self.currentView = "Archive"
                self.__resizeView()

        return call

    def createModels(self):
        self.model = cPl.TaskModel(self.taskStorage, ormMapping, 0, buttonData,
                                   self)  # передаем хранилище задач в модель
        self.finishedModel = cPl.TaskModel(self.taskStorage,
                                           ormMappingFinished, 1,
                                           buttonDataFinish, self)

    def updateView(self):
        self.view.update()