Beispiel #1
0
class MieterwechselView(QWidget, ModifyInfo):  # IccView ):
    """
    Enthält ein paar Felder für das aktive ("alte") Mietverhältnis,
    sowie eine MietverhaeltnisView für das Folge-MV.
    Die Methode onChange() der MietverhaeltnisView wird umgebogen auf die onChange()-Methode dieser View.
    """
    mieterwechsel_save = Signal()

    def __init__(self):
        QWidget.__init__(self)
        ModifyInfo.__init__(self)
        self._layout = QGridLayout()
        self._btnSave = QPushButton()
        # altes Mietverhältnis
        self._edAlterMieter = BaseEdit()
        self._edAlteNettomiete = FloatEdit()
        self._edAlteNKV = FloatEdit()
        self._edAlteKaution = IntEdit()
        self._sdEndeMietverh = SmartDateEdit()
        self._sdEndeMietverh.textChanged.connect(self.onChange)
        # neues Mietverhältnis
        self._neuesMietvhView = MietverhaeltnisView(enableBrowsing=False)
        self._neuesMietvhView.dataChanged.connect(self.onChange)
        self._mieterwechsel: XMieterwechsel = None

        self._createGui()
        self.connectWidgetsToChangeSlot()

    def onChange(self):
        if not self._btnSave.isEnabled():
            self.setSaveButtonEnabled()

    def isChanged(self) -> bool:
        return super().isChanged() or self._neuesMietvhView.isChanged()

    def resetChangeFlag(self):
        super().resetChangeFlag()
        self._neuesMietvhView.resetChangeFlag()

    def setSaveButtonEnabled(self, enabled: bool = True):
        self._btnSave.setEnabled(enabled)

    def _createGui(self):
        self._createSaveButton()
        r = self._createGuiAlterMieter()
        r += 1
        self._layout.addWidget(QLabel(""), r, 0)
        r += 1
        self._createGuiNeuerMieter(r)
        self.setLayout(self._layout)

    def _createSaveButton(self):
        btn = self._btnSave
        btn.clicked.connect(self.mieterwechsel_save.emit)
        btn.setFlat(True)
        btn.setEnabled(False)
        btn.setToolTip("Änderungen dieser View speichern")
        icon = QIcon("./images/save_30.png")
        btn.setIcon(icon)
        size = QSize(32, 32)
        btn.setFixedSize(size)
        iconsize = QSize(30, 30)
        btn.setIconSize(iconsize)
        hbox = QHBoxLayout()
        hbox.addWidget(btn)
        self._layout.addLayout(hbox, 0, 0, alignment=Qt.AlignLeft)

    def _createGuiAlterMieter(self) -> int:
        l = self._layout
        r = 1
        c = 0
        lbl = BaseLabel("Aktuelles / letztes Mietverhältnis ")
        lbl.setFont(QFont("Arial", 12, QFont.Bold))
        l.addWidget(lbl, r, c, 1, 2)

        r += 1
        lbl = QLabel("Mieter: ")
        l.addWidget(lbl, r, c)
        self._edAlterMieter.setReadOnly(True)
        l.addWidget(self._edAlterMieter, r, c + 1)

        r += 1
        lbl = BaseLabel("Nettomiete / NKV : ")
        l.addWidget(lbl, r, c)

        c += 1
        hbox = QHBoxLayout()
        self._edAlteNettomiete.setReadOnly(True)
        self._edAlteNettomiete.setMaximumWidth(100)
        hbox.addWidget(self._edAlteNettomiete)

        self._edAlteNKV.setReadOnly(True)
        self._edAlteNKV.setMaximumWidth(100)
        hbox.addWidget(self._edAlteNKV)
        l.addLayout(hbox, r, c, Qt.AlignLeft)

        c = 0
        r += 1
        lbl = BaseLabel("Kaution: ")
        l.addWidget(lbl, r, c)

        c += 1
        self._edAlteKaution.setReadOnly(True)
        self._edAlteKaution.setMaximumWidth(100)
        l.addWidget(self._edAlteKaution, r, c)

        c = 0
        r += 1
        lbl = QLabel("Ende des Mietverhältnisses: ")
        l.addWidget(lbl, r, c)

        self._sdEndeMietverh.setMaximumWidth(100)
        l.addWidget(self._sdEndeMietverh, r, c + 1)
        return r

    def _createGuiNeuerMieter(self, r: int):
        c = 0
        l = self._layout
        lbl = BaseLabel("Neues Mietverhältnis")
        lbl.setFont(QFont("Arial", 12, QFont.Bold))
        l.addWidget(lbl, r, c)

        r += 1
        l.addWidget(self._neuesMietvhView, r, c, 1, 2)

    def setMieterwechselData(self, xmieterwechsel: XMieterwechsel):
        self._mieterwechsel = xmieterwechsel
        self._setAltesMietverhaeltnisFields(xmieterwechsel.mietverhaeltnis_alt)
        self._setNeuesMietverhaeltnisFields(
            xmieterwechsel.mietverhaeltnis_next)
        self.setSaveButtonEnabled(False)
        self.resetChangeFlag()

    def getMieterwechselData(self) -> XMieterwechsel:
        return self._mieterwechsel

    def _setAltesMietverhaeltnisFields(self, xmv: XMietverhaeltnis):
        self._edAlterMieter.setText(xmv.name + ", " + xmv.vorname)
        self._edAlteNettomiete.setFloatValue(xmv.nettomiete)
        self._edAlteNKV.setFloatValue(xmv.nkv)
        if xmv.kaution:
            self._edAlteKaution.setIntValue(xmv.kaution)
        self._sdEndeMietverh.setDateFromIsoString(xmv.bis)

    def _setNeuesMietverhaeltnisFields(self, xmv: XMietverhaeltnis):
        self._neuesMietvhView.setMietverhaeltnisData(xmv)
        self._neuesMietvhView.resetChangeFlag()

    def getMieterwechselDataCopyWithChanges(self) -> XMieterwechsel:
        xmwcopy = copy.copy(self._mieterwechsel)
        mvneu: XMietverhaeltnis = self._neuesMietvhView.getMietverhaeltnisCopyWithChanges(
        )
        xmwcopy.mietverhaeltnis_next = mvneu
        xmwcopy.mietverhaeltnis_alt.bis = self._sdEndeMietverh.getDate()
        return xmwcopy

    def applyChanges(self):
        if self.isChanged():
            self._neuesMietvhView.applyChanges()
            # nur das Ende-Datum ist bei den Daten des alten MV änderbar
            self._mieterwechsel.mietverhaeltnis_alt.bis = self._sdEndeMietverh.getDate(
            )

    def getModel(self):
        return None
Beispiel #2
0
class BrowserWin(QWidget):
    def __init__(self, *args, **kwargs):

        super(BrowserWin, self).__init__(*args, **kwargs)

        # parent Maya window
        self.setParent(mainWindow)
        self.setWindowFlags(Qt.Window)
        # Window settings
        self.setWindowTitle('AC_AssetBrowser')

        # Build window
        self.mainLayout = QVBoxLayout()
        self.btnLayout = QHBoxLayout()
        self.radioLayout = QHBoxLayout()

        # radio buttons load import
        self.radioLabel = QLabel("Action: ")
        self.importRadioBtn = QRadioButton("Import File")
        self.openRadioBtn = QRadioButton("Open File")
        self.saveRadioBtn = QRadioButton("Save File")

        # Find asset directories to load from and populate the drop down
        self.fileType = QComboBox()
        self.__populate_list(self.fileType)
        self.curr_cat = self.fileType.currentText()

        # list of assets in self.list
        self.fileList = QListWidget()
        self.fileList.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.__populate_list(self.fileList,
                             directory=os.path.join(DIRECTORY, self.curr_cat))

        self.fileName = QLineEdit()

        self.loadBtn = QPushButton("Load Asset")
        self.publishBtn = QPushButton("Publish")
        self.closeBtn = QPushButton("Close")

        # Add widgets to layouts
        self.radioLayout.addWidget(self.radioLabel)
        self.radioLayout.addWidget(self.importRadioBtn)
        self.radioLayout.addWidget(self.openRadioBtn)
        self.radioLayout.addWidget(self.saveRadioBtn)

        self.mainLayout.addLayout(self.radioLayout)

        self.mainLayout.addWidget(self.fileType)
        self.mainLayout.addWidget(self.fileList)
        self.mainLayout.addWidget(self.fileName)

        self.btnLayout.addWidget(self.loadBtn)
        self.btnLayout.addWidget(self.publishBtn)
        self.btnLayout.addWidget(self.closeBtn)

        self.mainLayout.addLayout(self.btnLayout)
        self.setLayout(self.mainLayout)

        # Set state of widgets
        self.importRadioBtn.toggle()
        self.fileName.setPlaceholderText("file_name")
        self.fileName.setEnabled(False)
        self.publishBtn.setEnabled(False)

        # Signals
        self.fileType.currentIndexChanged.connect(self.selectionChanged)
        self.loadBtn.clicked.connect(self.loadBtnCmd)
        self.publishBtn.clicked.connect(self.publishBtnCmd)
        self.closeBtn.clicked.connect(self.closeBtnCmd)
        self.importRadioBtn.toggled.connect(self.onImportToggled)
        self.openRadioBtn.toggled.connect(self.onOpenToggled)
        self.saveRadioBtn.toggled.connect(self.onSaveToggled)

    def __populate_list(self, destination, directory=DIRECTORY):
        _dirs = os.listdir(directory)
        _items = [_dir for _dir in _dirs]

        return destination.addItems(_items)

    def selectionChanged(self):
        self.curr_cat = self.fileType.currentText()
        self.fileList.clear()
        self.__populate_list(self.fileList,
                             directory=os.path.join(DIRECTORY, self.curr_cat))

    def loadBtnCmd(self):
        if self.importRadioBtn.isChecked():
            selected_files = self.fileList.selectedItems()
            for _file in selected_files:
                asset_file = os.path.join(DIRECTORY, self.curr_cat,
                                          _file.text())
                cmds.file(asset_file, i=True)
        elif self.openRadioBtn.isChecked():
            selected_file = self.fileList.currentItem()
            asset_file = os.path.join(DIRECTORY, self.curr_cat,
                                      selected_file.text())
            cmds.file(asset_file, o=True, force=True)
        else:
            print("Did you mean to publish this asset?")

    def publishBtnCmd(self):
        if self.saveRadioBtn.isChecked() and self.fileName.text() is not None:
            path_to_save = os.path.join(DIRECTORY, self.curr_cat,
                                        self.fileName.text())
            cmds.file(rn="{}.ma".format(path_to_save))
            cmds.file(save=True)
            self.fileList.clear()
            self.__populate_list(self.fileList,
                                 directory=os.path.join(
                                     DIRECTORY, self.curr_cat))

    def closeBtnCmd(self):
        self.close()

    def onSaveToggled(self):
        items = self.fileList.selectedItems()
        for item in items:
            item.setSelected(False)
        self.fileName.setEnabled(not self.fileName.isEnabled())
        self.publishBtn.setEnabled(not self.publishBtn.isEnabled())

    def onImportToggled(self):
        if self.importRadioBtn.isChecked():
            self.fileList.setSelectionMode(QAbstractItemView.ExtendedSelection)

    def onOpenToggled(self):
        if self.openRadioBtn.isChecked():
            items = self.fileList.selectedItems()
            items.pop()
            for item in items:
                item.setSelected(False)
            self.fileList.setSelectionMode(QAbstractItemView.SingleSelection)
Beispiel #3
0
class RandomDatasetGenerator(QDialog):
    logger = logging.getLogger("root.ui.RandomGeneratorWidget")
    gui_logger = logging.getLogger("GUI")

    def __init__(self, parent=None):
        super().__init__(parent=parent, f=Qt.Window)
        self.setWindowTitle(self.tr("Dataset Generator"))
        self.last_n_components = 0
        self.components = []  # typing.List[RandomGeneratorComponentWidget]
        self.component_series = []
        self.init_ui()
        self.target = LOESS
        self.minimum_size_input.setValue(0.02)
        self.maximum_size_input.setValue(2000.0)
        self.n_classes_input.setValue(101)
        self.precision_input.setValue(4)

        self.file_dialog = QFileDialog(parent=self)
        self.update_timer = QTimer()
        self.update_timer.timeout.connect(lambda: self.update_chart(True))
        self.cancel_flag = False

    def init_ui(self):
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.main_layout = QGridLayout(self)
        # self.main_layout.setContentsMargins(0, 0, 0, 0)

        self.sampling_group = QGroupBox(self.tr("Sampling"))
        # self.control_group.setFixedSize(400, 160)
        self.control_layout = QGridLayout(self.sampling_group)
        self.minimum_size_label = QLabel(self.tr("Minimum Size [μm]"))
        self.minimum_size_input = QDoubleSpinBox()
        self.minimum_size_input.setDecimals(2)
        self.minimum_size_input.setRange(1e-4, 1e6)
        self.minimum_size_input.setValue(0.0200)
        self.maximum_size_label = QLabel(self.tr("Maximum Size [μm]"))
        self.maximum_size_input = QDoubleSpinBox()
        self.maximum_size_input.setDecimals(2)
        self.maximum_size_input.setRange(1e-4, 1e6)
        self.maximum_size_input.setValue(2000.0000)
        self.control_layout.addWidget(self.minimum_size_label, 0, 0)
        self.control_layout.addWidget(self.minimum_size_input, 0, 1)
        self.control_layout.addWidget(self.maximum_size_label, 0, 2)
        self.control_layout.addWidget(self.maximum_size_input, 0, 3)
        self.n_classes_label = QLabel(self.tr("N<sub>classes</sub>"))
        self.n_classes_input = QSpinBox()
        self.n_classes_input.setRange(10, 1e4)
        self.n_classes_input.setValue(101)
        self.precision_label = QLabel(self.tr("Data Precision"))
        self.precision_input = QSpinBox()
        self.precision_input.setRange(2, 8)
        self.precision_input.setValue(4)
        self.control_layout.addWidget(self.n_classes_label, 1, 0)
        self.control_layout.addWidget(self.n_classes_input, 1, 1)
        self.control_layout.addWidget(self.precision_label, 1, 2)
        self.control_layout.addWidget(self.precision_input, 1, 3)
        self.component_number_label = QLabel(self.tr("N<sub>components</sub>"))
        self.component_number_input = QSpinBox()
        self.component_number_input.setRange(1, 10)
        self.component_number_input.valueChanged.connect(
            self.on_n_components_changed)
        self.preview_button = QPushButton(qta.icon("mdi.animation-play"),
                                          self.tr("Preview"))
        self.preview_button.clicked.connect(self.on_preview_clicked)
        self.control_layout.addWidget(self.component_number_label, 2, 0)
        self.control_layout.addWidget(self.component_number_input, 2, 1)
        self.control_layout.addWidget(self.preview_button, 2, 2, 1, 2)
        self.main_layout.addWidget(self.sampling_group, 0, 0)

        self.save_group = QGroupBox(self.tr("Save"))
        # self.save_group.setFixedHeight(160)
        self.save_layout = QGridLayout(self.save_group)
        self.n_samples_label = QLabel(self.tr("N<sub>samples</sub>"))
        self.n_samples_input = QSpinBox()
        self.n_samples_input.setRange(100, 100000)
        self.save_layout.addWidget(self.n_samples_label, 0, 0)
        self.save_layout.addWidget(self.n_samples_input, 0, 1)

        self.cancel_button = QPushButton(qta.icon("mdi.cancel"),
                                         self.tr("Cancel"))
        self.cancel_button.setEnabled(False)
        self.cancel_button.clicked.connect(self.on_cancel_clicked)
        self.generate_button = QPushButton(qta.icon("mdi.cube-send"),
                                           self.tr("Generate"))
        self.generate_button.clicked.connect(self.on_generate_clicked)
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setOrientation(Qt.Horizontal)
        self.progress_bar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.save_layout.addWidget(self.cancel_button, 1, 0)
        self.save_layout.addWidget(self.generate_button, 1, 1)
        self.save_layout.addWidget(self.progress_bar, 2, 0, 1, 2)
        self.main_layout.addWidget(self.save_group, 0, 1)

        self.param_group = QGroupBox("Random Parameter")
        # self.param_group.setFixedWidth(400)
        self.param_layout = QGridLayout(self.param_group)
        self.main_layout.addWidget(self.param_group, 1, 0)

        self.preview_group = QGroupBox(self.tr("Preview"))
        self.chart_layout = QGridLayout(self.preview_group)

        self.chart = MixedDistributionChart(parent=self, toolbar=False)
        self.chart_layout.addWidget(self.chart, 0, 0)
        self.chart.setSizePolicy(QSizePolicy.MinimumExpanding,
                                 QSizePolicy.MinimumExpanding)
        self.main_layout.addWidget(self.preview_group, 1, 1)

    @staticmethod
    def to_points(x, y):
        return [QPointF(x_value, y_value) for x_value, y_value in zip(x, y)]

    def on_n_components_changed(self, n_components: int):
        if self.last_n_components < n_components:
            for component_index in range(self.last_n_components, n_components):
                component = RandomGeneratorComponentWidget(
                    name=f"AC{component_index+1}")
                component.value_changed.connect(self.on_value_changed)
                self.param_layout.addWidget(component, component_index + 1, 0)
                self.components.append(component)

        if self.last_n_components > n_components:
            for i in range(n_components, self.last_n_components):
                before_component = self.components[i]
                before_component.value_changed.disconnect(
                    self.on_value_changed)
                self.param_layout.removeWidget(before_component)
                # if not hide, the widget will still display on screen
                before_component.hide()
                self.components.pop(n_components)

        self.last_n_components = n_components

    def on_preview_clicked(self):
        if self.update_timer.isActive():
            self.preview_button.setText(self.tr("Preview"))
            self.update_timer.stop()
            self.update_chart()
        else:
            self.preview_button.setText(self.tr("Stop"))
            self.update_timer.start(200)

    def on_cancel_clicked(self):
        self.cancel_flag = True

    def on_generate_clicked(self):
        if self.update_timer.isActive():
            self.preview_button.setText(self.tr("Preview"))
            self.update_timer.stop()
            self.update_chart()

        filename, _ = self.file_dialog.getSaveFileName(
            self, self.tr("Choose a filename to save the generated dataset"),
            None, "Microsoft Excel (*.xlsx)")
        if filename is None or filename == "":
            return
        n_samples = self.n_samples_input.value()
        dataset = self.get_random_dataset(n_samples)
        # generate samples
        self.cancel_button.setEnabled(True)
        self.generate_button.setEnabled(False)
        format_str = self.tr("Generating {0} samples: %p%").format(n_samples)
        self.progress_bar.setFormat(format_str)
        self.progress_bar.setValue(0)

        def cancel():
            self.progress_bar.setFormat(self.tr("Task canceled"))
            self.progress_bar.setValue(0)
            self.cancel_button.setEnabled(False)
            self.generate_button.setEnabled(True)
            self.cancel_flag = False

        samples = []
        for i in range(n_samples):
            if self.cancel_flag:
                cancel()
                return
            sample = dataset.get_sample(i)
            samples.append(sample)
            progress = (i + 1) / n_samples * 50
            self.progress_bar.setValue(progress)
            QCoreApplication.processEvents()

        # save file to excel file
        format_str = self.tr("Writing {0} samples to excel file: %p%").format(
            n_samples)
        self.progress_bar.setFormat(format_str)
        self.progress_bar.setValue(50)

        wb = openpyxl.Workbook()
        prepare_styles(wb)

        ws = wb.active
        ws.title = self.tr("README")
        description = \
            """
            This Excel file was generated by QGrain ({0}).

            Please cite:
            Liu, Y., Liu, X., Sun, Y., 2021. QGrain: An open-source and easy-to-use software for the comprehensive analysis of grain size distributions. Sedimentary Geology 423, 105980. https://doi.org/10.1016/j.sedgeo.2021.105980

            It contanins n_components + 3 sheets:
            1. The first sheet is the random settings which were used to generate random parameters.
            2. The second sheet is the generated dataset.
            3. The third sheet is random parameters which were used to calulate the component distributions and their mixture.
            4. The left sheets are the component distributions of all samples.

            Artificial dataset
                Using skew normal distribution as the base distribution of each component (i.e. end-member).
                Skew normal distribution has three parameters, shape, location and scale.
                Where shape controls the skewness, location and scale are simliar to that of the Normal distribution.
                When shape = 0, it becomes Normal distribution.
                The weight parameter controls the fraction of the component, where fraction_i = weight_i / sum(weight_i).
                By assigning the mean and std of each parameter, random parameters was generate by the `scipy.stats.truncnorm.rvs` function of Scipy.

            Sampling settings
                Minimum size [μm]: {1},
                Maximum size [μm]: {2},
                N_classes: {3},
                Precision: {4},
                Noise: {5},
                N_samples: {6}

            """.format(QGRAIN_VERSION,
                       self.minimum_size_input.value(),
                       self.maximum_size_input.value(),
                       self.n_classes_input.value(),
                       self.precision_input.value(),
                       self.precision_input.value()+1,
                       n_samples)

        def write(row, col, value, style="normal_light"):
            cell = ws.cell(row + 1, col + 1, value=value)
            cell.style = style

        lines_of_desc = description.split("\n")
        for row, line in enumerate(lines_of_desc):
            write(row, 0, line, style="description")
        ws.column_dimensions[column_to_char(0)].width = 200

        ws = wb.create_sheet(self.tr("Random Settings"))
        write(0, 0, self.tr("Parameter"), style="header")
        ws.merge_cells(start_row=1, start_column=1, end_row=2, end_column=1)
        write(0, 1, self.tr("Shape"), style="header")
        ws.merge_cells(start_row=1, start_column=2, end_row=1, end_column=3)
        write(0, 3, self.tr("Location"), style="header")
        ws.merge_cells(start_row=1, start_column=4, end_row=1, end_column=5)
        write(0, 5, self.tr("Scale"), style="header")
        ws.merge_cells(start_row=1, start_column=6, end_row=1, end_column=7)
        write(0, 7, self.tr("Weight"), style="header")
        ws.merge_cells(start_row=1, start_column=8, end_row=1, end_column=9)
        ws.column_dimensions[column_to_char(0)].width = 16
        for col in range(1, 9):
            ws.column_dimensions[column_to_char(col)].width = 16
            if col % 2 == 0:
                write(1, col, self.tr("Mean"), style="header")
            else:
                write(1, col, self.tr("STD"), style="header")
        for row, comp_params in enumerate(self.target, 2):
            if row % 2 == 1:
                style = "normal_dark"
            else:
                style = "normal_light"
            write(row, 0, self.tr("Component{0}").format(row - 1), style=style)
            for i, key in enumerate(["shape", "loc", "scale", "weight"]):
                mean, std = comp_params[key]
                write(row, i * 2 + 1, mean, style=style)
                write(row, i * 2 + 2, std, style=style)

        ws = wb.create_sheet(self.tr("Dataset"))
        write(0, 0, self.tr("Sample Name"), style="header")
        ws.column_dimensions[column_to_char(0)].width = 24
        for col, value in enumerate(dataset.classes_μm, 1):
            write(0, col, value, style="header")
            ws.column_dimensions[column_to_char(col)].width = 10
        for row, sample in enumerate(samples, 1):
            if row % 2 == 0:
                style = "normal_dark"
            else:
                style = "normal_light"
            write(row, 0, sample.name, style=style)
            for col, value in enumerate(sample.distribution, 1):
                write(row, col, value, style=style)

            if self.cancel_flag:
                cancel()
                return
            progress = 50 + (row / n_samples) * 10
            self.progress_bar.setValue(progress)
            QCoreApplication.processEvents()

        ws = wb.create_sheet(self.tr("Parameters"))
        write(0, 0, self.tr("Sample Name"), style="header")
        ws.merge_cells(start_row=1, start_column=1, end_row=2, end_column=1)
        ws.column_dimensions[column_to_char(0)].width = 24
        for i in range(dataset.n_components):
            write(0,
                  4 * i + 1,
                  self.tr("Component{0}").format(i + 1),
                  style="header")
            ws.merge_cells(start_row=1,
                           start_column=4 * i + 2,
                           end_row=1,
                           end_column=4 * i + 5)
            for j, header_name in enumerate([
                    self.tr("Shape"),
                    self.tr("Location"),
                    self.tr("Scale"),
                    self.tr("Weight")
            ]):
                write(1, 4 * i + 1 + j, header_name, style="header")
                ws.column_dimensions[column_to_char(4 * i + 1 + j)].width = 16
        for row, sample in enumerate(samples, 2):
            if row % 2 == 1:
                style = "normal_dark"
            else:
                style = "normal_light"
            write(row, 0, sample.name, style=style)
            for i, comp_param in enumerate(sample.parameter.components):
                write(row, 4 * i + 1, comp_param.shape, style=style)
                write(row, 4 * i + 2, comp_param.loc, style=style)
                write(row, 4 * i + 3, comp_param.scale, style=style)
                write(row, 4 * i + 4, comp_param.weight, style=style)
            if self.cancel_flag:
                cancel()
                return
            progress = 60 + (row / n_samples) * 10
            self.progress_bar.setValue(progress)
            QCoreApplication.processEvents()

        for i in range(dataset.n_components):
            ws = wb.create_sheet(self.tr("Component{0}").format(i + 1))
            write(0, 0, self.tr("Sample Name"), style="header")
            ws.column_dimensions[column_to_char(0)].width = 24
            for col, value in enumerate(dataset.classes_μm, 1):
                write(0, col, value, style="header")
                ws.column_dimensions[column_to_char(col)].width = 10
            for row, sample in enumerate(samples, 1):
                if row % 2 == 0:
                    style = "normal_dark"
                else:
                    style = "normal_light"
                write(row, 0, sample.name, style=style)
                for col, value in enumerate(sample.components[i].distribution,
                                            1):
                    write(row, col, value, style=style)
            if self.cancel_flag:
                cancel()
                return
            progress = 70 + (
                (i * n_samples + row) / n_samples * dataset.n_components) * 30
            self.progress_bar.setValue(progress)
            QCoreApplication.processEvents()
        wb.save(filename)
        wb.close()

        self.progress_bar.setValue(100)
        self.progress_bar.setFormat(self.tr("Task finished"))
        self.cancel_button.setEnabled(False)
        self.generate_button.setEnabled(True)

    @property
    def target(self):
        return [comp.target for comp in self.components]

    @target.setter
    def target(self, values):
        if len(values) != len(self.components):
            self.component_number_input.setValue(len(values))
        for comp, comp_target in zip(self.components, values):
            comp.blockSignals(True)
            comp.target = comp_target
            comp.blockSignals(False)
        self.update_chart()

    def get_random_sample(self):
        dataset = self.get_random_dataset(n_samples=1)
        sample = dataset.get_sample(0)
        sample.name = self.tr("Artificial Sample")
        return sample

    def get_random_mean(self):
        dataset = self.get_random_dataset(n_samples=1)
        random_setting = RandomSetting(self.target)
        sample = dataset.get_sample_by_params(self.tr("Artificial Sample"),
                                              random_setting.mean_param)
        return sample

    def get_random_dataset(self, n_samples):
        min_μm = self.minimum_size_input.value()
        max_μm = self.maximum_size_input.value()
        n_classes = self.n_classes_input.value()
        if min_μm == max_μm:
            return
        if min_μm > max_μm:
            min_μm, max_μm = max_μm, min_μm
        precision = self.precision_input.value()
        noise = precision + 1

        dataset = get_random_dataset(target=self.target,
                                     n_samples=n_samples,
                                     min_μm=min_μm,
                                     max_μm=max_μm,
                                     n_classes=n_classes,
                                     precision=precision,
                                     noise=noise)
        return dataset

    def on_value_changed(self):
        self.update_chart()

    def update_chart(self, random=False):
        if not random:
            sample = self.get_random_mean()
        else:
            sample = self.get_random_sample()
        self.chart.show_model(sample.view_model)

    def closeEvent(self, event):
        if self.cancel_button.isEnabled():
            self.on_cancel_clicked()
        event.accept()
Beispiel #4
0
class WorkspaceConfigurationEditor(QDialog):

    def __init__(self, workpsaceConfiguration, mainApplicatoin, switch=False):
        super(WorkspaceConfigurationEditor, self).__init__()
        self.setWindowIcon(QIcon(main.resource_path("resources/app_icon")))
        self.workspaceConfiguration: WorkspaceConfiguration = workpsaceConfiguration
        self.mainApplication = mainApplicatoin
        self.switch = switch
        self.workspaceDirectory = None
        self.setWindowTitle("Choose a workspace directory")
        self.setStyleSheet("background-color: #2D2D30; color: white;")
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
        #self.setWindowFlag(Qt.WindowStaysOnTopHint, True)
        self.setFixedSize(500, 150)
        self.comboBox = QComboBox()
        self.updateComboBox()
        self.btnOpen = QPushButton("Open workspace")
        self.cbDefault = QCheckBox("Set as default workspace")
        self.btnBrowse = QPushButton("Browse...")
        self.vbox = QVBoxLayout()
        self.hbox = QHBoxLayout()
        self.hbox2 = QHBoxLayout()
        self.hbox2.addWidget(self.comboBox, 4)
        self.hbox2.addWidget(self.btnBrowse, 1)
        self.vbox.addLayout(self.hbox2)
        self.hbox.addWidget(self.cbDefault)
        self.hbox.addWidget(self.btnOpen)
        self.vbox.addSpacing(20)
        self.vbox.addLayout(self.hbox)
        self.setLayout(self.vbox)
        self.btnOpen.clicked.connect(self.loadWorkpace)
        self.btnBrowse.clicked.connect(self.browseWorkspace)
        self.btnOpen.setFocus()
        if len(self.workspaceConfiguration.getWorkspaces()) == 0:
            self.btnOpen.setEnabled(False)

    def updateComboBox(self):
        self.comboBox.clear()
        self.comboBox.addItems(list(self.workspaceConfiguration.getWorkspaces()))
        if self.workspaceConfiguration.defaultWorkspace:
            self.comboBox.setCurrentText(self.workspaceConfiguration.getDefaultWorkspace())

    def browseWorkspace(self):
        directory = QFileDialog.getExistingDirectory()
        if directory == "":
            return
        wsname = directory[directory.rindex(os.path.sep) + 1:]
        regex = re.compile('[@!#$%^&*()<>?/\|}{~:]')
        if ' ' in directory or regex.search(wsname):
            msg = QMessageBox()
            msg.setStyleSheet("background-color: #2D2D30; color: white;")
            msg.setModal(True)
            msg.setIcon(QMessageBox.Critical)
            msg.setText("Workspace path/name cannot contain whitespace special characters.")
            msg.setWindowTitle("Workspace creation error")
            msg.exec_()
            return
        self.workspaceConfiguration.addWorkspace(directory)
        self.updateComboBox()
        if not self.btnOpen.isEnabled():
            self.btnOpen.setEnabled(True)

    def loadWorkpace(self):
        if self.cbDefault.isChecked():
            self.workspaceConfiguration.setDefaultWorkspace(self.comboBox.currentText())
        if not self.switch:
            success = self.mainApplication.openWorkspaceAction(self.comboBox.currentText())
            if not success:
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("Failed to load '{}'.".format(self.comboBox.currentText()))
                msg.setWindowTitle("Failed to load workspace.")
                msg.exec_()
                self.workspaceConfiguration.removeWorkspace(self.comboBox.currentText())
                self.updateComboBox()
                return
        else:
            self.workspaceDirectory = self.comboBox.currentText()
        self.accept()

    def closeEvent(self, arg__1):
        self.reject()
Beispiel #5
0
class Login(QWidget):
    device = None
    deviceList = []

    def __init__(self):
        QWidget.__init__(self)

        self.theme = 2

        screen = QDesktopWidget()
        logging.info("screen size:" + str(screen.width()) + "," +
                     str(screen.height()))

        self.m_width = screen.height() / 2
        self.m_height = screen.height() / 2
        self.resize(self.m_width, self.m_height)
        # self.setFixedSize(self.m_width, self.m_height)
        size = self.geometry()
        self.move((screen.width() - size.width()) / 2,
                  (screen.height() - size.height()) / 2)

        # delete title box
        self.setWindowFlags(Qt.FramelessWindowHint)
        # self.setAttribute(Qt.WA_TranslucentBackground)

        logging.info("login widget size:" + str(self.size()))
        self.bitmap = QBitmap(self.size())
        painter = QPainter(self.bitmap)

        painter.fillRect(self.rect(), Qt.white)

        painter.setBrush(QColor(0, 0, 0))
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.drawRoundedRect(self.rect(), 10, 10)
        self.setMask(self.bitmap)

        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, self.m_height / 15)
        self.layout.setSpacing(self.m_height / 15)
        self.topframe = QFrame(self)
        self.topframe.setObjectName("topframe")
        if self.theme == 1:
            self.topframe.setStyleSheet(
                "#topframe{background-color:qlineargradient("
                "spread:pad,"
                "x1:0,y1:0,x2:1,y2:1,"
                "stop:0 #5efce8,"
                "stop:1 #736efe);}")
        elif self.theme == 2:
            self.topframe.setStyleSheet(
                "#topframe{background-color:qlineargradient("
                "spread:pad,"
                "x1:0,y1:0,x2:1,y2:0,"
                "stop:0 #3a6186,"
                "stop:1 #89253e);}")
        elif self.theme == 3:
            self.topframe.setStyleSheet(
                "#topframe{background-color:qlineargradient("
                "spread:pad,"
                "x1:0,y1:0,x2:0,y2:1,"
                "stop:0 #19547b,"
                "stop:1 #ffd89b);}")
        else:
            self.topframe.setStyleSheet(
                "#topframe{background-color:qlineargradient("
                "spread:pad,"
                "x1:0,y1:0,x2:1,y2:1,"
                "stop:0 #ff8177,"
                "stop:1 #b12a5b);}")
        self.layout.addWidget(self.topframe)

        # setup checkbox
        self.combobox = QComboBox(self)
        self.combobox.setObjectName("combobox")
        self.combobox.setFixedSize(self.m_width / 1.5, self.m_height / 10)
        self.combobox.setStyleSheet(
            "QComboBox{border: 2px solid gray;"
            "border-radius:5px;"
            "background-color:rgb(255, 255, 255);"
            "color:rgb(0, 0, 0);"
            "padding: 1px 20px;}"
            "QComboBox:drop-down{subcontrol-origin: padding;"
            "subcontrol-position: top right;"
            "width: 50px;border-left-style:solid;"
            "border-top-right-radius: 3px;"
            "border-bottom-right-radius: 3px;"
            "border-left: 2px solid gray;"
            "background-color: rgba(100, 25, 100, 0);}"
            "QComboBox:down-arrow{border-image:url(icon/arrow-1.png);}"
            "QComboBox:item:selected{background: rgb(232, 241, 250);color: rgb(2, 65, 132);}"
            "QStyledItemDelegate{border: 100px solid rgb(161,161,161);}")

        # self.combobox.move(200, 200)
        self.layout.addWidget(self.combobox, 0, Qt.AlignHCenter)

        # setup login button
        self.loginbtn = QPushButton("ENTER", self)
        self.loginbtn.setObjectName("loginbtn")
        self.loginbtn.setFixedSize(self.m_width / 1.5, self.m_height / 10)
        self.loginbtn.setContentsMargins(200, 20, 20, 20)
        self.loginbtn.clicked.connect(self.login_event)
        self.loginbtn.setStyleSheet(
            "QPushButton{border-radius:%dpx;"
            "background-color:#89253e;"
            "color:rgb(0, 0, 0);}"
            "QPushButton:hover{color:rgb(0, 255, 0);}" % (self.m_height / 20))
        self.layout.addWidget(self.loginbtn, 0, Qt.AlignHCenter)

        # setup exit button
        self.exitbtn = QPushButton(self)
        # self.exitbtn.setText("Close")
        self.exitbtn.setToolTip("Close the widget")
        self.exitbtn.setFixedSize(40, 40)
        self.exitbtn.setIcon(QIcon("icon/close.png"))
        self.exitbtn.setIconSize(QSize(40, 40))
        self.exitbtn.clicked.connect(self.exit_event)
        logging.info("topframesize:" + str(self.topframe.size()))
        self.exitbtn.move(self.width() - 40, 0)
        # self.exitbtn.setGeometry(self.topframe.width()-40, 0, 100, 40)
        self.exitbtn.setStyleSheet("background-color: #600")
        self.exitbtn.setStyleSheet("background-color: transparent")
        self.exitbtn.isEnabled()

        self.logoImage = QPixmap("icon/silabslogo.png")
        self.logo = self.logoImage.scaled(self.m_width / 5, self.m_height / 5,
                                          Qt.KeepAspectRatio,
                                          Qt.SmoothTransformation)

        self.logoLable = QLabel(self)
        self.logoLable.setObjectName("logoLable")
        self.logoLable.setAlignment(Qt.AlignCenter)
        self.logoLable.setPixmap(self.logo)
        self.logoLable.setFixedSize(self.m_width / 4, self.m_height / 4)
        # self.logo.setScaledContents(True)
        self.logoLable.setStyleSheet("#logoLable{border: 2px solid gray;"
                                     "border-radius:75px;"
                                     "background-color:rgb(100, 100, 100);"
                                     "color:rgb(0, 0, 0);}")
        self.logoLable.move(self.m_width / 2 - self.m_width / 8,
                            self.m_height / 6)

        self.m_drag = False
        self.m_DragPosition = 0
        self.MainWindow = 0

    def login_event(self):
        if self.combobox.currentText() == "None":
            return
        self.hide()
        self.device = self.deviceList[self.combobox.currentIndex()]
        self.device.open()
        command = '\xAA' + '\xF2' + '\x01' + '\x00'
        play_thread = USBWriteThread(self.device, command)
        play_thread.start()
        self.MainWindow = OscGui(self.device)
        self.MainWindow.show()

    def exit_event(self):
        exit(0)

    def mousePressEvent(self, event):

        if event.button() == Qt.LeftButton:
            self.m_drag = True
            self.m_DragPosition = event.globalPos() - self.pos()
            event.accept()
            self.setCursor(QCursor(Qt.OpenHandCursor))

    def mouseMoveEvent(self, event):
        if Qt.LeftButton and self.m_drag:
            self.move(event.globalPos() - self.m_DragPosition)
            event.accept()

    def mouseReleaseEvent(self, event):
        self.m_drag = False
        self.setCursor(QCursor(Qt.ArrowCursor))
class PurchaseGroup(QGroupBox, Generic[TransactionItemType]):
    def __init__(
        self,
        item: TransactionItemType,
        recruiter: UnitTransactionFrame[TransactionItemType],
    ) -> None:
        super().__init__()
        self.item = item
        self.recruiter = recruiter

        self.setProperty("style", "buy-box")
        self.setMaximumHeight(72)
        self.setMinimumHeight(36)
        layout = QHBoxLayout()
        self.setLayout(layout)

        self.sell_button = QPushButton("-")
        self.sell_button.setProperty("style", "btn-sell")
        self.sell_button.setDisabled(not recruiter.enable_sale(item))
        self.sell_button.setMinimumSize(16, 16)
        self.sell_button.setMaximumSize(16, 16)
        self.sell_button.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        )

        self.sell_button.clicked.connect(
            lambda: self.recruiter.recruit_handler(RecruitType.SELL, self.item)
        )

        self.amount_bought = QLabel()
        self.amount_bought.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        )

        self.buy_button = QPushButton("+")
        self.buy_button.setProperty("style", "btn-buy")
        self.buy_button.setDisabled(not recruiter.enable_purchase(item))
        self.buy_button.setMinimumSize(16, 16)
        self.buy_button.setMaximumSize(16, 16)

        self.buy_button.clicked.connect(
            lambda: self.recruiter.recruit_handler(RecruitType.BUY, self.item)
        )
        self.buy_button.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))

        layout.addWidget(self.sell_button)
        layout.addWidget(self.amount_bought)
        layout.addWidget(self.buy_button)

        self.update_state()

    @property
    def pending_units(self) -> int:
        return self.recruiter.pending_delivery_quantity(self.item)

    def update_state(self) -> None:
        self.buy_button.setEnabled(self.recruiter.enable_purchase(self.item))
        self.buy_button.setToolTip(
            self.recruiter.purchase_tooltip(self.buy_button.isEnabled())
        )
        self.sell_button.setEnabled(self.recruiter.enable_sale(self.item))
        self.sell_button.setToolTip(
            self.recruiter.sell_tooltip(self.sell_button.isEnabled())
        )
        self.amount_bought.setText(f"<b>{self.pending_units}</b>")
class MietverhaeltnisView(QWidget, ModifyInfo):
    save = Signal()
    dataChanged = Signal()
    nextMv = Signal()
    prevMv = Signal()

    def __init__(self,
                 mietverhaeltnis: XMietverhaeltnis = None,
                 withSaveButton: bool = False,
                 enableBrowsing=False,
                 parent=None):
        QWidget.__init__(self, parent)
        ModifyInfo.__init__(self)
        self._mietverhaeltnis: XMietverhaeltnis = None
        self._withSaveButton = withSaveButton
        self._enableBrowsing = enableBrowsing  # ob die Browse-Buttons angezeigt werden
        self._layout = QGridLayout()
        self._btnSave = QPushButton()
        self._btnVor = QPushButton()
        self._btnRueck = QPushButton()
        self._sdBeginnMietverh = SmartDateEdit()
        self._sdEndeMietverh = SmartDateEdit()
        self._edMieterName_1 = BaseEdit()
        self._edMieterVorname_1 = BaseEdit()
        self._edMieterName_2 = BaseEdit()
        self._edMieterVorname_2 = BaseEdit()
        self._edMieterTelefon = BaseEdit()
        self._edMieterMobil = BaseEdit()
        self._edMieterMailto = BaseEdit()
        self._edAnzPers = IntEdit()
        self._edNettomiete = FloatEdit()
        self._edNkv = FloatEdit()
        self._edKaution = IntEdit()
        self._sdKautionBezahltAm = SmartDateEdit()
        self._txtBemerkung1 = MultiLineEdit()
        self._txtBemerkung2 = MultiLineEdit()

        self._createGui()
        if mietverhaeltnis:
            self.setMietverhaeltnisData(mietverhaeltnis)
        #self.connectWidgetsToChangeSlot( self.onChange )
        self.connectWidgetsToChangeSlot(self.onChange, self.onResetChangeFlag)

    def onChange(self, newcontent: str = None):
        if not self._btnSave.isEnabled():
            self.setSaveButtonEnabled()
        self.dataChanged.emit()

    def onResetChangeFlag(self):
        self.setSaveButtonEnabled(False)

    def setSaveButtonEnabled(self, enabled: bool = True):
        self._btnSave.setEnabled(enabled)

    def _createGui(self):
        hbox = QHBoxLayout()
        if self._withSaveButton:
            self._createSaveButton(hbox)
        if self._enableBrowsing:
            self._createVorRueckButtons(hbox)
        self._layout.addLayout(hbox, 0, 0, alignment=Qt.AlignLeft)
        self._addHorizontalLine(1)
        self._createFelder(2)
        self.setLayout(self._layout)

    def _createSaveButton(self, hbox):
        btn = self._btnSave
        btn.clicked.connect(self.save.emit)
        btn.setFlat(True)
        btn.setEnabled(False)
        btn.setToolTip("Änderungen am Mietverhältnis speichern")
        icon = ImageFactory.inst().getSaveIcon()
        #icon = QIcon( "./images/save_30.png" )
        btn.setIcon(icon)
        size = QSize(32, 32)
        btn.setFixedSize(size)
        iconsize = QSize(30, 30)
        btn.setIconSize(iconsize)
        hbox.addWidget(btn)

    def _createVorRueckButtons(self, hbox):
        self._prepareButton(self._btnRueck,
                            ImageFactory.inst().getPrevIcon(),
                            "Zum vorigen Mietverhältnis blättern", self.prevMv)
        hbox.addWidget(self._btnRueck)
        self._prepareButton(self._btnVor,
                            ImageFactory.inst().getNextIcon(),
                            "Zum nächsten Mietverhältnis blättern",
                            self.nextMv)
        hbox.addWidget(self._btnVor)

    def _prepareButton(self, btn: QPushButton, icon: QIcon, tooltip: str,
                       signal: Signal):
        btn.setFlat(True)
        btn.setEnabled(True)
        btn.setToolTip(tooltip)
        btn.setIcon(icon)
        size = QSize(32, 32)
        btn.setFixedSize(size)
        iconsize = QSize(30, 30)
        btn.setIconSize(iconsize)
        btn.clicked.connect(signal.emit)

    def _addHorizontalLine(self, r: int):
        hline = HLine()
        self._layout.addWidget(hline, r, 0, 1, 2)
        return r + 1

    def _createFelder(self, r):
        c = 0
        l = self._layout

        lbl = QLabel("Beginn: ")
        l.addWidget(lbl, r, c)
        c += 1
        self._sdBeginnMietverh.setMaximumWidth(100)
        l.addWidget(self._sdBeginnMietverh, r, c)

        c = 0
        r += 1
        l.addWidget(BaseLabel("Ende: "), r, c)
        c += 1
        self._sdEndeMietverh.setMaximumWidth(100)
        l.addWidget(self._sdEndeMietverh, r, c)

        c = 0
        r += 1
        lbl = BaseLabel("Name / Vorname 1. Mieter: ")
        l.addWidget(lbl, r, c)
        c += 1
        hbox = QHBoxLayout()
        hbox.addWidget(self._edMieterName_1)
        hbox.addWidget(self._edMieterVorname_1)
        l.addLayout(hbox, r, c)

        c = 0
        r += 1
        lbl = BaseLabel("Name / Vorname 2. Mieter: ")
        l.addWidget(lbl, r, c)
        c += 1
        hbox = QHBoxLayout()
        hbox.addWidget(self._edMieterName_2)
        hbox.addWidget(self._edMieterVorname_2)
        l.addLayout(hbox, r, c)

        c = 0
        r += 1
        l.addWidget(BaseLabel("Telefon: "), r, c)
        c += 1
        l.addWidget(self._edMieterTelefon, r, c)

        c = 0
        r += 1
        l.addWidget(BaseLabel("Mobil: "), r, c)
        c += 1
        l.addWidget(self._edMieterMobil, r, c)

        c = 0
        r += 1
        l.addWidget(BaseLabel("Mailadresse: "), r, c)
        c += 1
        l.addWidget(self._edMieterMailto, r, c)

        c = 0
        r += 1
        l.addWidget(BaseLabel("Anzahl Personen i.d. Whg: "), r, c)
        c += 1
        self._edAnzPers.setMaximumWidth(20)
        l.addWidget(self._edAnzPers, r, c)

        c = 0
        r += 1
        l.addWidget(BaseLabel("Nettomiete / NKV: "), r, c)

        c += 1
        self._edNettomiete.setMaximumWidth(100)
        #self._edNettomiete.setEnabled( False )
        self._edNkv.setMaximumWidth(100)
        #self._edNkv.setEnabled( False )
        hbox = QHBoxLayout()
        hbox.addWidget(self._edNettomiete)
        hbox.addWidget(self._edNkv)
        hbox.addWidget(
            BaseLabel(
                "  Änderungen der Miete und NKV über Dialog 'Sollmiete'"))
        l.addLayout(hbox, r, c, alignment=Qt.AlignLeft)

        c = 0
        r += 1
        l.addWidget(BaseLabel("Kaution: "), r, c)
        c += 1
        self._edKaution.setMaximumWidth(100)
        l.addWidget(self._edKaution, r, c)

        c = 0
        r += 1
        l.addWidget(BaseLabel("Kaution bezahlt am: "), r, c)
        c += 1
        self._sdKautionBezahltAm.setMaximumWidth(100)
        l.addWidget(self._sdKautionBezahltAm, r, c)

        c = 0
        r += 1
        l.addWidget(BaseLabel(""), r, c)

        r += 1
        l.addWidget(BaseLabel("Bemerkungen: "), r, c)
        c += 1
        hbox = QHBoxLayout()
        hbox.addWidget(self._txtBemerkung1)
        hbox.addWidget(self._txtBemerkung2)
        l.addLayout(hbox, r, c)

        # cols = self._layout.columnCount()
        # print( cols, " columns" )

    def setNettoUndNkvEnabled(self, enabled: bool = True):
        self._edNettomiete.setEnabled(enabled)
        self._edNkv.setEnabled(enabled)

    def _guiToData(self, x: XMietverhaeltnis):
        """
        Überträgt die Änderungen, die der User im GUI gemacht hat, in das
        übergebene XMietverhaeltnis-Objekt
        :param x: XMietverhaeltnis-Objekt, in das die geänderten Daten übertragen werden
        :return:
        """
        x.von = self._sdBeginnMietverh.getDate()
        x.bis = self._sdEndeMietverh.getDate()
        x.name = self._edMieterName_1.text()
        x.vorname = self._edMieterVorname_1.text()
        x.name2 = self._edMieterName_2.text()
        x.vorname2 = self._edMieterVorname_2.text()
        x.telefon = self._edMieterTelefon.text()
        x.mobil = self._edMieterMobil.text()
        x.mailto = self._edMieterMailto.text()
        x.anzahl_pers = self._edAnzPers.getIntValue()
        x.nettomiete = self._edNettomiete.getFloatValue()
        x.nkv = self._edNkv.getFloatValue()
        x.kaution = self._edKaution.getIntValue()
        x.kaution_bezahlt_am = self._sdKautionBezahltAm.getDate()
        x.bemerkung1 = self._txtBemerkung1.toPlainText()
        x.bemerkung2 = self._txtBemerkung2.toPlainText()

    def getMietverhaeltnisCopyWithChanges(self) -> XMietverhaeltnis:
        """
        gibt eine Kopie der Mietverhaeltnis-Schnittstellendaten mit Änderungen zurück.
        Diese Kopie kann für Validierungszwecke verwendet werden.
        :return: Kopie von XMietverhaeltnis
        """
        mvcopy = copy.copy(self._mietverhaeltnis)
        self._guiToData(mvcopy)
        return mvcopy

    def applyChanges(self):
        """
        überträgt die Änderungen, die der User im GUI gemacht hat, in das
        originale XMietverhaeltnis-Objekt.
        """
        if self.isChanged():
            self._guiToData(self._mietverhaeltnis)

    def setMietverhaeltnisData(self, mv: XMietverhaeltnis):
        """
        Daten, die im GUI angezeigt und geändert werden können.
        :param mv:
        :return:
        """
        self._mietverhaeltnis = mv
        if mv.von:
            self._sdBeginnMietverh.setDateFromIsoString(mv.von)
        if mv.bis:
            self._sdEndeMietverh.setDateFromIsoString(mv.bis)
        self._edMieterName_1.setText(mv.name)
        self._edMieterVorname_1.setText(mv.vorname)
        self._edMieterName_2.setText(mv.name2)
        self._edMieterVorname_2.setText(mv.vorname2)
        self._edMieterTelefon.setText(mv.telefon)
        self._edMieterMobil.setText(mv.mobil)
        self._edMieterMailto.setText(mv.mailto)
        self._edAnzPers.setIntValue(mv.anzahl_pers)
        self._edNettomiete.setFloatValue(mv.nettomiete)
        self._edNkv.setFloatValue(mv.nkv)
        if mv.kaution:
            self._edKaution.setIntValue(mv.kaution)
        if mv.kaution_bezahlt_am:
            self._sdKautionBezahltAm.setDateFromIsoString(
                mv.kaution_bezahlt_am)
        self._txtBemerkung1.setText(mv.bemerkung1)
        self._txtBemerkung2.setText(mv.bemerkung2)
        self.resetChangeFlag()

    def clear(self):
        self._sdBeginnMietverh.clear()
        self._sdEndeMietverh.clear()
        self._edMieterName_1.clear()
        self._edMieterVorname_1.clear()
        self._edMieterName_2.clear()
        self._edMieterVorname_2.clear()
        self._edMieterTelefon.clear()
        self._edMieterMobil.clear()
        self._edMieterMailto.clear()
        self._edAnzPers.clear()
        self._edNettomiete.clear()
        self._edNkv.clear()
        self._edKaution.clear()
        self._sdKautionBezahltAm.clear()
        self._txtBemerkung1.clear()
        self._txtBemerkung2.clear()
class PlainTextFindReplaceDialog(QDialog):
    """
    Modeless, stay-above-parent dialog that supports find and replace.

    Allows for searching case (in)sensitively, and whole-word.
    Find triggered by Enter / Shift+Enter, or corresponding button (Next / Previous), or if Replace clicked before
    Next / Previous.
    Find wraps.
    Highlights all matches, operating on the closest-to-user's-cursor selection first,
    in the user-selected direction (Next / Previous).
    Highlighting / found cursors retained on navigation back to text editor, and cleared on re-find/replace if
    user modified the document.
    Presents an info label (e.g. "x of y", "No matches found", ...)

    While no members have a leading underscore, the only explicit public interface is the static method `find_all`.
    I couldn't find a reason to need to interface with anything in here, so I didn't differentiate everything as
    "private".
    """
    def __init__(self, plain_text_edit: QPlainTextEdit, parent=None):
        """
        Sets up the dialog.

        :param plain_text_edit: Text Editor to operate on.
        :param parent: QWidget parent
        """
        super().__init__(parent=parent, f=Qt.Tool)
        self.plain_text_edit = plain_text_edit
        self.cursors_needed = True
        self.find_flags = QTextDocument.FindFlags()
        self.found_cursors: List[QTextCursor] = []
        self.current_cursor = QTextCursor()

        # UI
        layout = QVBoxLayout()
        find_and_replace_layout = QGridLayout()
        layout.addLayout(
            find_and_replace_layout
        )  # if QGL is sub-layout, add to parent layout before doing stuff.

        self.find_line_edit = QLineEdit()
        find_label = QLabel("&Find")
        find_label.setBuddy(self.find_line_edit)

        options_layout = QHBoxLayout()
        self.match_case_check_box = QCheckBox("Match Case")
        self.whole_word_check_box = QCheckBox("Whole Word")
        options_layout.addWidget(self.match_case_check_box)
        options_layout.addWidget(self.whole_word_check_box)
        options_layout.addStretch()

        self.found_info_label = QLabel()

        self.replace_line_edit = QLineEdit()
        replace_label = QLabel("&Replace")
        replace_label.setBuddy(self.replace_line_edit)

        find_and_replace_layout.addWidget(find_label, 0, 0)
        find_and_replace_layout.addWidget(self.find_line_edit, 0, 1)
        find_and_replace_layout.addLayout(options_layout, 1, 1)
        find_and_replace_layout.setRowMinimumHeight(2, 20)
        find_and_replace_layout.addWidget(self.found_info_label, 2, 1)
        find_and_replace_layout.addWidget(replace_label, 3, 0)
        find_and_replace_layout.addWidget(self.replace_line_edit, 3, 1)

        self.btn_box = QDialogButtonBox(QDialogButtonBox.Close)
        self.find_next_btn = QPushButton("Next")
        self.find_next_btn.setEnabled(False)
        self.find_prev_btn = QPushButton("Previous")
        self.find_prev_btn.setEnabled(False)
        self.replace_btn = QPushButton("Replace")
        self.replace_btn.setEnabled(False)
        self.replace_all_btn = QPushButton("Replace All")
        self.replace_all_btn.setEnabled(False)
        self.btn_box.addButton(self.replace_btn, QDialogButtonBox.ActionRole)
        self.btn_box.addButton(self.replace_all_btn,
                               QDialogButtonBox.ActionRole)
        self.btn_box.addButton(self.find_prev_btn, QDialogButtonBox.ActionRole)
        self.btn_box.addButton(self.find_next_btn, QDialogButtonBox.ActionRole)

        layout.addWidget(self.btn_box)
        self.setLayout(layout)
        # End UI

        self.btn_box.rejected.connect(self.reject)
        self.find_line_edit.textEdited.connect(self.handle_text_edited)
        self.find_next_btn.clicked.connect(self.next)
        self.find_prev_btn.clicked.connect(self.prev)
        self.replace_btn.clicked.connect(self.replace)
        self.replace_all_btn.clicked.connect(self.replace_all)
        self.plain_text_edit.document().contentsChanged.connect(
            self.set_cursors_needed_true)
        self.whole_word_check_box.stateChanged.connect(
            self.toggle_whole_word_flag)
        self.match_case_check_box.stateChanged.connect(
            self.toggle_match_case_flag)

    # SLOTS
    def next(self):
        """
        Finds all matches to the user's search. First highlight after cursor. Consecutive calls advance through matches.

        If there are matches or not, it says so. The user's cursor advances along with the current selection. Loops back
        to start after the last match.
        :return: Side effect: Highlights all matches, differentiating (maybe moving fwd) the current selection.
        """
        if self.cursors_needed:
            self.init_find()

        if not self.found_cursors:
            self.found_info_label.setText("No matches found")
            self.found_info_label.repaint()
            return

        if self.current_cursor >= self.found_cursors[
                -1]:  # loop back to start. cursor equality based on position.
            self.current_cursor = self.found_cursors[0]
        else:
            for cur in self.found_cursors:
                if cur > self.current_cursor:  # next in order
                    self.current_cursor = cur
                    break

        self.update_visuals()

    def prev(self):
        """
        Finds all matches to user's search. First highlight before cursor. Consecutive calls retreat through matches.

        If there are matches or not, it says so. The user's cursor moves along with the current selection. Loops back
        to end after the last (first in doc) match.
        :return: Side effect: Highlights all matches, differentiating (maybe moving back) the current selection.
        """
        if self.cursors_needed:
            self.init_find()

        if not self.found_cursors:
            self.found_info_label.setText("No matches found")
            self.found_info_label.repaint()
            return

        if self.current_cursor <= self.found_cursors[0]:  # loop back to end.
            self.current_cursor = self.found_cursors[-1]
        else:
            for cur in reversed(self.found_cursors):
                if cur < self.current_cursor:  # prev in order
                    self.current_cursor = cur
                    break

        self.update_visuals()

    def replace(self):
        """
        Replaces the word under focus by `next`.

        Replaces with the Replace line edit's text, and advances to next word. If no word under focus via this dialog,
        calls `next`.
        :return: Side effect: replaces word in text edit
        """
        if self.cursors_needed:
            self.next()
            return

        if not self.found_cursors:
            return

        self.plain_text_edit.document().contentsChanged.disconnect(
            self.set_cursors_needed_true)  # don't dup work.
        self.current_cursor.insertText(self.replace_line_edit.text())
        self.plain_text_edit.document().contentsChanged.connect(
            self.set_cursors_needed_true)
        self.found_cursors.remove(self.current_cursor)
        self.next()

    def replace_all(self):
        """
        Replaces all instances of Find's text with Replace's text.

        :return: Side effect: replaces words in text edit. Indicates success to user via info label on dialog.
        """
        if self.cursors_needed:
            self.init_find()

        for cur in self.found_cursors:
            cur.insertText(self.replace_line_edit.text())

        self.found_info_label.setText("Made {} replacements".format(
            len(self.found_cursors)))
        self.found_info_label.repaint()

    def handle_text_edited(self, text):
        """
        Modifies button states, clears info text, and sets self.cursors_needed to True.

        :param text: The find_line_edit's text.
        :return: Side effect: btn enabled / default
        """
        self.found_info_label.clear()

        self.cursors_needed = True

        find_enabled = text != ""
        self.find_next_btn.setEnabled(find_enabled)
        self.find_prev_btn.setEnabled(find_enabled)
        self.replace_btn.setEnabled(find_enabled)
        self.replace_all_btn.setEnabled(find_enabled)

        self.find_next_btn.setDefault(find_enabled)
        self.btn_box.button(
            QDialogButtonBox.Close).setDefault(not find_enabled)

    def set_cursors_needed_true(self):
        self.cursors_needed = True

    def toggle_match_case_flag(self, state: int):
        self.found_info_label.clear(
        )  # User will be performing a new search upon toggle, so want this reset.
        self.cursors_needed = True

        if state == Qt.Unchecked:
            self.find_flags &= ~QTextDocument.FindCaseSensitively
        elif state == Qt.Checked:
            self.find_flags |= QTextDocument.FindCaseSensitively

    def toggle_whole_word_flag(self, state: int):
        self.found_info_label.clear(
        )  # User will be performing a new search upon toggle, so want this reset.
        self.cursors_needed = True

        if state == Qt.Unchecked:
            self.find_flags &= ~QTextDocument.FindWholeWords
        elif state == Qt.Checked:
            self.find_flags |= QTextDocument.FindWholeWords

    # END SLOTS

    def init_find(self):
        """Sets up internal state for the case when cursors are needed (e.g. first find, user modifies doc...)"""
        self.found_cursors = self.find_all(self.find_line_edit.text(),
                                           self.plain_text_edit.document(),
                                           self.find_flags)
        self.cursors_needed = False
        self.current_cursor = self.plain_text_edit.textCursor(
        )  # returns copy of
        self.plain_text_edit.setExtraSelections([])

    def update_visuals(self):
        """
        Moves text editor's cursor to match currently highlighted `find` word, performs `find` highlighting,
        indicates index on dialog.
        """
        # x of y words indicator
        idx = self.found_cursors.index(self.current_cursor) + 1
        self.found_info_label.setText("{} of {} matches".format(
            idx, len(self.found_cursors)))
        self.found_info_label.repaint()

        # move along text editor's viewport
        next_pte_cursor = QTextCursor(self.current_cursor)
        next_pte_cursor.clearSelection()
        self.plain_text_edit.setTextCursor(next_pte_cursor)

        #highlighting
        normal_color = QColor(Qt.yellow).lighter()
        current_color = QColor(Qt.magenta).lighter()
        extra_selections: List[QTextEdit.ExtraSelection] = []
        for cur in self.found_cursors:
            selection = QTextEdit.ExtraSelection()
            selection.cursor = cur
            if cur == self.current_cursor:
                selection.format.setBackground(current_color)
            else:
                selection.format.setBackground(normal_color)
            extra_selections.append(selection)
        self.plain_text_edit.setExtraSelections(extra_selections)

    @staticmethod
    def find_all(
        text: str, document: QTextDocument, flags=QTextDocument.FindFlags()
    ) -> List[QTextCursor]:
        """
        Finds all occurrences of `text` in `document`, in order.

        :param text: Text to find.
        :param document: Document to search.
        :param flags: Conditions to set on the search: none or (whole word and/or match case)
        :return: Ordered list of all found instances.
        """
        cursor = QTextCursor(document)  # default pos == 0
        found: List[QTextCursor] = []

        while True:
            cursor = document.find(text, cursor, flags)
            if cursor.isNull():
                return found
            else:
                found.append(cursor)

    def done(self, arg__1: int):
        self.plain_text_edit.setExtraSelections([])
        super().done(arg__1)

    def keyPressEvent(self, arg__1: QKeyEvent):
        # Shift+Enter triggers find previous, if the corresponding btn is enabled.
        if (arg__1.key() in [Qt.Key_Return, Qt.Key_Enter]
                and arg__1.modifiers() == Qt.ShiftModifier
                and self.find_prev_btn.isEnabled()):
            self.prev()
        else:
            super().keyPressEvent(arg__1)
Beispiel #9
0
class QGroundObjectTemplateLayout(QGroupBox):
    close_dialog_signal = Signal()

    def __init__(
            self,
            game: Game,
            ground_object: TheaterGroundObject,
            layout: QTgoLayout,
            layout_changed_signal: Signal(QTgoLayout),
            current_group_value: int,
    ):
        super().__init__()
        # Connect to the signal to handle template updates
        self.game = game
        self.ground_object = ground_object
        self.layout_changed_signal = layout_changed_signal
        self.layout_model = layout
        self.layout_changed_signal.connect(self.load_for_layout)

        self.current_group_value = current_group_value

        self.buy_button = QPushButton("Buy")
        self.buy_button.setEnabled(False)
        self.buy_button.clicked.connect(self.buy_group)

        self.template_layout = QGridLayout()
        self.setLayout(self.template_layout)

        self.template_grid = QGridLayout()
        self.template_layout.addLayout(self.template_grid, 0, 0, 1, 2)
        self.template_layout.addWidget(self.buy_button, 1, 1)
        stretch = QVBoxLayout()
        stretch.addStretch()
        self.template_layout.addLayout(stretch, 2, 0)

        # Load Layout
        self.load_for_layout(self.layout_model)

    def load_for_layout(self, layout: QTgoLayout) -> None:
        self.layout_model = layout
        # Clean the current grid
        self.layout_model.groups = defaultdict(list)
        for id in range(self.template_grid.count()):
            self.template_grid.itemAt(id).widget().deleteLater()
        for group_name, groups in self.layout_model.layout.groups.items():
            self.add_theater_group(group_name, self.layout_model.force_group,
                                   groups)
        self.group_template_changed()

    @property
    def cost(self) -> int:
        return self.layout_model.price - self.current_group_value

    @property
    def affordable(self) -> bool:
        return self.cost <= self.game.blue.budget

    def add_theater_group(self, group_name: str, force_group: ForceGroup,
                          groups: list[TgoLayoutGroup]) -> None:
        group_box = QGroupBox(group_name)
        vbox_layout = QVBoxLayout()
        for group in groups:
            try:
                group_row = QTgoLayoutGroupRow(force_group, group)
            except LayoutException:
                continue
            self.layout_model.groups[group_name].append(group_row.group_layout)
            group_row.group_template_changed.connect(
                self.group_template_changed)
            vbox_layout.addWidget(group_row)
        group_box.setLayout(vbox_layout)
        self.template_grid.addWidget(group_box)

    def group_template_changed(self) -> None:
        price = self.layout_model.price
        self.buy_button.setText(
            f"Buy [${price}M][-${self.current_group_value}M]")
        self.buy_button.setEnabled(self.affordable)
        if self.buy_button.isEnabled():
            self.buy_button.setToolTip(f"Buy the group for ${self.cost}M")
        else:
            self.buy_button.setToolTip("Not enough money to buy this group")

    def buy_group(self) -> None:
        if not self.affordable:
            # Something went wrong. Buy button should be disabled!
            logging.error("Not enough money to buy the group")
            return

        # Change the heading of the new group to head to the conflict
        self.ground_object.heading = (
            self.game.theater.heading_to_conflict_from(
                self.ground_object.position) or self.ground_object.heading)
        self.game.blue.budget -= self.cost
        self.ground_object.groups = []
        for group_name, groups in self.layout_model.groups.items():
            for group in groups:
                self.layout_model.force_group.create_theater_group_for_tgo(
                    self.ground_object,
                    group.layout,
                    f"{self.ground_object.name} ({group_name})",
                    self.game,
                    group.dcs_unit_type,  # Forced Type
                    group.amount,  # Forced Amount
                )
        self.close_dialog_signal.emit()