Example #1
0
class LabelWindow(QDialog):
    def __init__(self, parent):
        super(LabelWindow, self).__init__(parent)

        self.test_layout = QGridLayout()
        label = QLabel("Label")
        self.test_layout.addWidget(label, 0, 0)
        self.setLayout(self.test_layout)
        self._destroyCalled = False

    def replace(self, unit):
        old_item = self.test_layout.itemAtPosition(0, 0)
        old_label = old_item.widget()
        ref = weakref.ref(old_item, self._destroyed)

        self.test_layout.removeWidget(old_label)
        unit.assertRaises(RuntimeError, old_item.widget)
        del old_item

        label = QLabel("Label New")
        old_label.deleteLater()
        label.setAlignment(Qt.AlignCenter)
        self.test_layout.addWidget(label, 0, 0)

    def _destroyed(self, obj):
        self._destroyCalled = True
class Widget_Matplotlib(QWidget) :
    ''' Class comprenant l'affichage global de l'interface comprend également l'affichage du graph 3D MatplotLib

    lien : correspond au chemin du fichier stl par defaut ''
    d1,d2,d3 sont liés aux changements de valeur des potentiometres avec maj du graph 3D

    '''
    def __init__(self,lien='') :
        QWidget.__init__(self)
        self.setWindowTitle('STL BOAT')
        #self.setFixedSize(1000,800)
        self.box=QGridLayout()
        self.lien=lien

        #image
        # self.__image=QLabel()
        # self.__image.setPixmap(QtGui.QPixmap('png/helm.png'))
        # self.__image.setWindowOpacity(10)

        # partie Gauche
        self.partie_gauche=Widget_Gauche(self.lien)
        self.partie_gauche.button_load.clicked.connect(self.push_load)
        self.partie_gauche.button_save.clicked.connect(self.push_save)
        # partie Droite
        self.partie_droite=Widget_Droit(self.lien)
        self.partie_droite.button_compute.clicked.connect(self.push_compute)
        self.kx=0
        self.ky=0
        self.kz=0


        # PLOT 3D
        self.fichier=mesh.Mesh.from_file(self.lien)
        self.fichierr=mesh.Mesh.from_file(self.lien)
        self.figure= pyplot.figure()
        self.init_widget(self.fichier)

        # Connexion des potentiometres
        self.potentiometre=Potentiometre()
        self.potentiometre.dial1.valueChanged.connect(self.d1)
        self.potentiometre.dial2.valueChanged.connect(self.d2)
        self.potentiometre.dial3.valueChanged.connect(self.d3)

        '''Association Layout'''
        self.box.addWidget(self.potentiometre,0,1)
        #self.box.addWidget(self.canvas,2,1,0,1)
        self.box.addWidget(self.partie_gauche,0,0)
        self.box.addWidget(self.partie_droite,0,2,0,4)
        self.setLayout(self.box)
        self.show()

    def init_widget(self,fichier):
        ''' Initialisation de l'affichage 3D Matplotlib '''
        pyplot.close()

        self.figure= pyplot.figure()
        scale = self.fichier.points.flatten()
        self.axes=mplot3d.Axes3D(self.figure)
        self.axes.auto_scale_xyz(scale, scale, scale)
        self.axes.add_collection3d(mplot3d.art3d.Poly3DCollection(fichier.vectors,color='red'))
        self.canvas = FigureCanvas(self.figure)
        self.box.addWidget(self.canvas,2,1,4,1)
        self.axes.set_xlabel('X',fontsize=20)
        self.axes.set_ylabel('Y',fontsize=20)
        self.axes.set_zlabel('Z',fontsize=20)
        self.partie_gauche.calcul_caracteristiques(fichier.vectors)
        #mer
        x=np.linspace(-5,5,5)
        y=np.linspace(-5,5,5)
        X, Y = np.meshgrid(x, y)
        z=0*X+0*Y
        self.axes.plot_wireframe(X,Y,z)
    def d1(self):
        self.fichier.translate([0,0,(self.potentiometre.dial1.value()-self.kx)/10])
        self.kx=self.potentiometre.dial1.value()
        self.box.removeWidget(self.canvas)
        self.init_widget(self.fichier)

    def d2(self):
        self.fichier.rotate([1, 0.0, 0.0],math.radians(self.potentiometre.dial2.value()-self.ky))
        self.ky=self.potentiometre.dial2.value()
        self.box.removeWidget(self.canvas)
        self.init_widget(self.fichier)

    def d3(self):
        self.fichier.rotate([0.0, 1, 0.0],math.radians(self.potentiometre.dial3.value()-self.kz))
        self.kz=self.potentiometre.dial3.value()
        self.box.removeWidget(self.canvas)
        self.init_widget(self.fichier)
    def push_load(self):
        '''
        Methode lors de l'appui du bouton 'load'

=> Permet de changer de fichier STL en ouvrant une nouvelle fenêtre

        '''

        Ouverture = QFileDialog.getOpenFileName(self,
                "Ouvrir un fichier",
                "../Documents",
                "STL (*.stl);; TIFF (*.tif);; All files (*.*)")


        print(Ouverture[0])
        self.__lien=str(Ouverture[0])
        window=Widget_Matplotlib(self.__lien)
        window.exec_()

    def push_compute(self):
        '''
        Methode lors de l'appui du bouton 'compute'

=> Permet de lancer l'algorithme de dicotomie situé dans Calcul.py
=> Change la valeur sur l'écran LCD en affichant le Tirant d'Eau 10^-2 près
=> création du graph de dicotomie
=> Verification de la valeur de la précision/tolérance
=> oblige la translation d'être supérieur à 2
        '''

        #verif tolérance
        if float(self.partie_droite.precision) >= 1 or float(self.partie_droite.precision)<=0 :
            self.message_box_erreur('''                                         Tolérance\n
Erreur : la tolérance doit être inferieur à 1 et positive
Erreur : la tolérance doit être un nombre''')
            return
        if self.partie_droite.eau_de_mer.isChecked() == True :
            self.partie_droite.rho=1025
        else :
            self.partie_droite.rho=1000

        if self.partie_droite.text_poids.text() == '' :
            self.message_box_erreur('''                                   Masse\n
Erreur : Entrez une valeur différente de 0''')
            return

        # verification de la translation
        translation=abs(self.potentiometre.dial1.value()/10)
        if translation <=2 :
            translation=2
            #self.message_box_erreur('La translation est definie à 2')

        # graph
        self.graph = Widget_Graph(self.fichier,float(self.partie_droite.precision),float(self.partie_droite.rho),float(self.partie_droite.masse),translation,(self.potentiometre.dial1.value())/10)
        self.partie_droite.LCD.display(abs(self.graph.hauteur))
        self.partie_droite.layout.addWidget(self.graph,13,0,2,0)
        self.potentiometre.dial1.setValue(0)
        self.hide()
        self.show()
        self.fichier=self.fichierr
        self.init_widget(self.fichierr)

    def message_box_erreur(self,text):
        '''Fenetre Pop-Up affichant un message d'erreur'''
        message=QMessageBox()
        message.setText(text)
        message.setWindowTitle('Erreur')
        icon=(QtGui.QPixmap('091-notification.png'))
        message.setWindowIcon(icon)
        message.exec_()

    def push_save(self):
        print('save')

        Ouverture = QFileDialog.getSaveFileName(self,
                "Sauvegarde",
                "Name")
        url=Ouverture[0]+'.txt'
        fichier = open(url, "w")
        fichier.write('Compte rendu du test sur : '+self.lien+'\n\n\nCaracteristiques : \n'+self.partie_gauche.retour_caracteristiques()+
                      '\n\nDéplacement : \n'+'Translation Z : '+str((self.potentiometre.dial1.value())/10)+'\nRotation Y : '+str((self.potentiometre.dial2.value())/10)+
                      '\nRotation Y : '+str((self.potentiometre.dial3.value())/10)+'''\n\nTirant d'eau: '''+str(self.partie_droite.LCD.value())+'\nMasse : '+str(self.partie_droite.masse)
                      +'\nPrecision : '+str(self.partie_droite.precision))
class Widget_Matplotlib(QWidget):
    ''' Class comprenant l'affichage global de l'interface comprend également l'affichage du graph 3D MatplotLib

    lien : correspond au chemin du fichier stl par defaut ''
    d1,d2,d3 sont liés aux changements de valeur des potentiometres avec maj du graph 3D

    '''
    def __init__(self, lien=''):
        QWidget.__init__(self)
        self.setWindowTitle('STL BOAT')
        #self.setFixedSize(1000,800)
        self.box = QGridLayout()
        self.lien = lien

        A = QFont("DIN Condensed", 70)
        self.titre = QLabel("S T L   B O A T")
        self.titre.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter)
        #self.titre.adjustSize()
        self.titre.setFont(A)
        # partie Gauche
        self.partie_gauche = Widget_Gauche(self.lien)
        self.partie_gauche.button_load.clicked.connect(self.push_load)
        # partie Droite
        self.partie_droite = Widget_Droit(self.lien)
        self.partie_droite.button_compute.clicked.connect(self.push_compute)
        self.kx = 0
        self.ky = 0
        self.kz = 0

        # PLOT 3D
        self.fichier = mesh.Mesh.from_file(self.lien)
        self.fichier2 = self.fichier
        self.figure = pyplot.figure()
        self.fichier = mesh.Mesh.from_file(self.lien)
        self.init_widget(self.fichier)

        # Connexion des potentiometres
        self.potentiometre = Potentiometre()
        self.potentiometre.dial1.valueChanged.connect(self.d1)
        self.potentiometre.dial2.valueChanged.connect(self.d2)
        self.potentiometre.dial3.valueChanged.connect(self.d3)
        '''Association Layout'''
        self.box.addWidget(self.titre, 0, 1)
        self.box.addWidget(self.potentiometre, 1, 1)
        self.box.addWidget(self.canvas, 2, 1)
        self.box.addWidget(self.partie_gauche, 0, 0)
        self.box.addWidget(self.partie_droite, 0, 2, 0, 2)
        self.setLayout(self.box)
        self.show()

    def init_widget(self, fichier):
        ''' Initialisation de l'affichage 3D Matplotlib '''
        pyplot.close()

        self.figure = pyplot.figure()
        scale = self.fichier.points.flatten()
        self.axes = mplot3d.Axes3D(self.figure)
        self.axes.auto_scale_xyz(scale, scale, scale)
        self.axes.add_collection3d(
            mplot3d.art3d.Poly3DCollection(fichier.vectors, color='red'))
        self.canvas = FigureCanvas(self.figure)
        self.box.addWidget(self.canvas, 2, 1)
        self.axes.set_xlabel('X', fontsize=20)
        self.axes.set_ylabel('Y', fontsize=20)
        self.axes.set_zlabel('Z', fontsize=20)
        self.partie_gauche.calcul_caracteristiques(fichier.vectors)
        #mer
        x = np.linspace(-5, 5, 5)
        y = np.linspace(-5, 5, 5)
        X, Y = np.meshgrid(x, y)
        z = 0 * X + 0 * Y
        self.axes.plot_wireframe(X, Y, z)

    def d1(self):
        self.fichier.translate(
            [0, 0, (self.potentiometre.dial1.value() - self.kx) / 10])
        self.kx = self.potentiometre.dial1.value()
        self.box.removeWidget(self.canvas)
        self.init_widget(self.fichier)

    def d2(self):
        self.fichier.rotate([1, 0.0, 0.0],
                            math.radians(self.potentiometre.dial2.value() -
                                         self.ky))
        self.ky = self.potentiometre.dial2.value()
        self.box.removeWidget(self.canvas)
        self.init_widget(self.fichier)

    def d3(self):
        self.fichier.rotate([0.0, 1, 0.0],
                            math.radians(self.potentiometre.dial3.value() -
                                         self.kz))
        self.kz = self.potentiometre.dial3.value()
        self.box.removeWidget(self.canvas)
        self.init_widget(self.fichier)

    def push_load(self):
        '''
        Methode lors de l'appui du bouton 'load'

=> Permet de changer de fichier STL en ouvrant une nouvelle fenêtre

        '''

        Ouverture = QFileDialog.getOpenFileName(
            self, "Ouvrir un fichier", "../Documents",
            "STL (*.stl);; TIFF (*.tif);; All files (*.*)")
        print(Ouverture[0])
        self.__lien = str(Ouverture[0])
        window = Widget_Matplotlib(self.__lien)
        window.exec()
        self.close()
        #self.__load_object.setText('Object : '+self.__lien)

    def push_compute(self):
        '''
        Methode lors de l'appui du bouton 'compute'

=> Permet de lancer l'algorithme de dicotomie situé dans Calcul.py
=> Change la valeur sur l'écran LCD en affichant le Tirant d'Eau 10^-2 près
=> création du graph de dicotomie
=> Verification de la valeur de la précision/tolérance
=> oblige la translation d'être supérieur à 2
        '''

        #verif tolérance
        if float(self.partie_droite.precision) >= 1 or float(
                self.partie_droite.precision) <= 0:
            self.message_box_erreur('''Tolérance
Erreur : la tolérance doit être inferieur à 1 et positive
Erreur : la tolérance doit être un nombre''')
            return
        if self.partie_droite.eau_de_mer.isChecked() == True:
            self.partie_droite.rho = 1025
        else:
            self.partie_droite.rho = 1000

        # verification de la translation
        translation = abs(self.potentiometre.dial1.value() / 10)
        if translation <= 2:
            translation = 2
            self.message_box_erreur('La translation est definie à 2')

        #print((self.potentiometre.line1.text()),(self.potentiometre.line1.text()),(self.partie_droite.precision),(self.partie_droite.rho),(self.partie_droite.masse))
        a = Dichotomie(translation, -translation,
                       float(self.partie_droite.precision),
                       self.fichier2.vectors, self.fichier2.normals,
                       float(self.partie_droite.rho),
                       float(self.partie_droite.masse))
        #print('retour dico',a[0])

        self.partie_droite.LCD.display(abs(a[0]))
        self.potentiometre.dial1.setValue(a[0] * 10)

        # graph

        self.graph = Widget_Graph(self.lien,
                                  float(self.partie_droite.precision),
                                  float(self.partie_droite.rho),
                                  float(self.partie_droite.masse), translation)
        self.partie_droite.layout.addWidget(self.graph, 13, 0, 2, 0)
        self.showFullScreen()

    def message_box_erreur(self, text):
        '''Fenetre Pop-Up affichant un message d'erreur'''
        message = QMessageBox()
        message.setText(text)
        message.setWindowTitle('Erreur')
        icon = (QtGui.QPixmap('091-notification.png'))
        message.setWindowIcon(icon)
        message.exec_()
class SerialDevicesGui(QWidget):
    def __init__(self):
        super().__init__()

        self.serial_device_list = []
        self.current_serial_device = None
        self.monotonic_device_num = 0

        self.layout = QGridLayout()
        self._setup()
        self.setLayout(self.layout)

    def update_device_list(self):
        self.serial_devices.clear()
        device_names = [d.name for d in self.serial_device_list]
        self.serial_devices.addItems(device_names)
        if len(self.serial_device_list) == 0:
            self.new_device()
        self.set_device(self.current_serial_device)

    def new_device(self):
        new_device = SerialDeviceGui(self,
                                     f"device_{self.monotonic_device_num}")
        self.serial_device_list.append(new_device)
        self.current_serial_device = new_device
        self.monotonic_device_num += 1
        self.update_device_list()

    def remove_device(self, device):
        self.serial_device_list.remove(device)
        self.layout.removeWidget(device)
        device.setParent(None)
        self.layout.update()
        self.update_device_list()

    def set_device(self, device):
        if len(self.serial_device_list) > 1:
            self.layout.removeWidget(self.current_serial_device)
            self.current_serial_device.setParent(None)
        self.current_serial_device = device
        self.layout.addWidget(device, 2, 0, -1, -1)
        self.layout.update()

    def select_device(self):
        device = self.serial_devices.currentText()
        for d in self.serial_device_list:
            if d.name == device:
                self.set_device(d)

    def _setup(self):
        new_device = QPushButton("New Device")
        new_device.clicked.connect(self.new_device)

        self.serial_devices = QComboBox()
        self.serial_devices.setInsertPolicy(QComboBox.InsertAtBottom)
        self.serial_devices.currentIndexChanged.connect(self.select_device)

        n = 0
        self.layout.addWidget(new_device, n, 0, 1, 1)
        n += 1
        self.layout.addWidget(self.serial_devices, n, 0, 1, 1)

        self.new_device()
Example #5
0
class ManualFittingPanel(QDialog):
    manual_fitting_finished = Signal(SSUResult)

    def __init__(self, parent=None):
        super().__init__(parent=parent, f=Qt.Window)
        self.setWindowTitle(self.tr("SSU Manual Fitting Panel"))
        self.control_widgets = []
        self.input_widgets = []
        self.last_task = None
        self.last_result = None
        self.async_worker = AsyncWorker()
        self.async_worker.background_worker.task_succeeded.connect(
            self.on_task_succeeded)
        self.initialize_ui()
        self.normal_msg = QMessageBox(self)
        self.chart_timer = QTimer()
        self.chart_timer.timeout.connect(self.update_chart)
        self.chart_timer.setSingleShot(True)

    def initialize_ui(self):
        self.main_layout = QGridLayout(self)

        self.chart_group = QGroupBox(self.tr("Chart"))
        self.chart_layout = QGridLayout(self.chart_group)
        self.chart = MixedDistributionChart(show_mode=True, toolbar=False)
        self.chart_layout.addWidget(self.chart)

        self.control_group = QGroupBox(self.tr("Control"))
        self.control_layout = QGridLayout(self.control_group)
        self.try_button = QPushButton(qta.icon("mdi.test-tube"),
                                      self.tr("Try"))
        self.try_button.clicked.connect(self.on_try_clicked)
        self.control_layout.addWidget(self.try_button, 1, 0, 1, 4)
        self.confirm_button = QPushButton(qta.icon("ei.ok-circle"),
                                          self.tr("Confirm"))
        self.confirm_button.clicked.connect(self.on_confirm_clicked)
        self.control_layout.addWidget(self.confirm_button, 2, 0, 1, 4)

        self.splitter = QSplitter(Qt.Horizontal)
        self.splitter.addWidget(self.chart_group)
        self.splitter.addWidget(self.control_group)
        self.main_layout.addWidget(self.splitter)

    def change_n_components(self, n_components: int):
        for widget in self.control_widgets:
            self.control_layout.removeWidget(widget)
            widget.hide()
        self.control_widgets.clear()
        self.input_widgets.clear()

        widgets = []
        slider_range = (0, 1000)
        input_widgets = []
        mean_range = (-5, 15)
        std_range = (0.0, 10)
        weight_range = (0, 10)
        names = [self.tr("Mean"), self.tr("STD"), self.tr("Weight")]
        ranges = [mean_range, std_range, weight_range]
        slider_values = [500, 100, 100]
        input_values = [0.0, 1.0, 1.0]

        for i in range(n_components):
            group = QGroupBox(f"C{i+1}")
            group.setMinimumWidth(200)
            group_layout = QGridLayout(group)
            inputs = []
            for j, (name, range_, slider_value, input_value) in enumerate(
                    zip(names, ranges, slider_values, input_values)):
                label = QLabel(name)
                slider = QSlider()
                slider.setRange(*slider_range)
                slider.setValue(slider_value)
                slider.setOrientation(Qt.Horizontal)
                input_ = QDoubleSpinBox()
                input_.setRange(*range_)
                input_.setDecimals(3)
                input_.setSingleStep(0.01)
                input_.setValue(input_value)
                slider.valueChanged.connect(self.on_value_changed)
                input_.valueChanged.connect(self.on_value_changed)
                slider.valueChanged.connect(
                    lambda x, input_=input_, range_=range_: input_.setValue(
                        x / 1000 * (range_[-1] - range_[0]) + range_[0]))
                input_.valueChanged.connect(
                    lambda x, slider=slider, range_=range_: slider.setValue(
                        (x - range_[0]) / (range_[-1] - range_[0]) * 1000))

                group_layout.addWidget(label, j, 0)
                group_layout.addWidget(slider, j, 1)
                group_layout.addWidget(input_, j, 2)
                inputs.append(input_)

            self.control_layout.addWidget(group, i + 5, 0, 1, 4)
            widgets.append(group)
            input_widgets.append(inputs)

        self.control_widgets = widgets
        self.input_widgets = input_widgets

    @property
    def n_components(self) -> int:
        return len(self.input_widgets)

    @property
    def expected(self):
        reference = []
        weights = []
        for i, (mean, std, weight) in enumerate(self.input_widgets):
            reference.append(
                dict(mean=mean.value(), std=std.value(), skewness=0.0))
            weights.append(weight.value())
        weights = np.array(weights)
        fractions = weights / np.sum(weights)
        return reference, fractions

    def show_message(self, title: str, message: str):
        self.normal_msg.setWindowTitle(title)
        self.normal_msg.setText(message)
        self.normal_msg.exec_()

    def show_info(self, message: str):
        self.show_message(self.tr("Info"), message)

    def show_warning(self, message: str):
        self.show_message(self.tr("Warning"), message)

    def show_error(self, message: str):
        self.show_message(self.tr("Error"), message)

    def on_confirm_clicked(self):
        if self.last_result is not None:
            for component, (mean, std,
                            weight) in zip(self.last_result.components,
                                           self.input_widgets):
                mean.setValue(component.logarithmic_moments["mean"])
                std.setValue(component.logarithmic_moments["std"])
                weight.setValue(component.fraction * 10)
            self.manual_fitting_finished.emit(self.last_result)

            self.last_result = None
            self.last_task = None
            self.try_button.setEnabled(False)
            self.confirm_button.setEnabled(False)
            self.hide()

    def on_task_failed(self, info: str, task: SSUTask):
        self.show_error(info)

    def on_task_succeeded(self, result: SSUResult):
        self.chart.show_model(result.view_model)
        self.last_result = result
        self.confirm_button.setEnabled(True)

    def on_try_clicked(self):
        if self.last_task is None:
            return
        new_task = copy.copy(self.last_task)
        reference, fractions = self.expected
        initial_guess = BaseDistribution.get_initial_guess(
            self.last_task.distribution_type, reference, fractions=fractions)
        new_task.initial_guess = initial_guess
        self.async_worker.execute_task(new_task)

    def on_value_changed(self):
        self.chart_timer.stop()
        self.chart_timer.start(10)

    def update_chart(self):
        if self.last_task is None:
            return
        reference, fractions = self.expected
        for comp_ref in reference:
            if comp_ref["std"] == 0.0:
                return
        # print(reference)
        initial_guess = BaseDistribution.get_initial_guess(
            self.last_task.distribution_type, reference, fractions=fractions)
        result = SSUResult(self.last_task, initial_guess)
        self.chart.show_model(result.view_model, quick=True)

    def setup_task(self, task: SSUTask):
        self.last_task = task
        self.try_button.setEnabled(True)
        if self.n_components != task.n_components:
            self.change_n_components(task.n_components)
        reference, fractions = self.expected
        initial_guess = BaseDistribution.get_initial_guess(
            task.distribution_type, reference, fractions=fractions)
        result = SSUResult(task, initial_guess)
        self.chart.show_model(result.view_model, quick=False)
Example #6
0
class ManuelleEingabe(QWidget):
    instance = None

    def __init__(self, data_handler, parent):
        super(ManuelleEingabe, self).__init__(parent)
        ManuelleEingabe.set_instance(self)

        self.data_handler = data_handler

        self.layout = QGridLayout()
        self.layout.setContentsMargins(30, 10, 0, 0)
        self.layout_buttons = QHBoxLayout()
        self.layout_buttons.setMargin(0)

        self.input = None
        self.current_datatype = None

        # title label
        self.label_title = QLabel(self)
        self.label_title.setObjectName("title")
        self.label_title.setSizePolicy(QSizePolicy.Preferred,
                                       QSizePolicy.Maximum)

        # init buttons
        self.button_add = QPushButton(self)
        self.button_cancle = QPushButton(self)

        # add text
        self.button_add.setText("Hinzufügen")
        self.button_cancle.setText("Abbrechen")

        # add object name
        self.button_add.setObjectName("add")
        self.button_cancle.setObjectName("cancle")

        # add button action
        self.button_add.clicked.connect(self.__add)
        self.button_cancle.clicked.connect(self.__cancle)

        # add to layout

        self.layout_buttons.addWidget(self.button_add)
        self.layout_buttons.addWidget(self.button_cancle)

        self.layout.addWidget(self.label_title, 0, 0, 1, 4)
        self.layout.addLayout(self.layout_buttons, 2, 0, 1, 1)
        self.layout.setAlignment(Qt.AlignTop)

        self.setLayout(self.layout)

        self.setStyleSheet(
            "QLabel {"
            "font: 400 13px;"
            "}"
            "QLabel#title {"
            "font-size: 20px;"
            "font-weight: bold;"
            "}"
            "QLineEdit {"
            "border-radius: 2;"
            "border: 1px solid #DEDEDF;"
            "padding: 3px;"
            "}"
            "QLineEdit:focus {"
            "border: 1px solid #0078d7;"
            "}"
            "QTimeEdit {"
            "border-radius: 2;"
            "border: 1px solid #DEDEDF;"
            "padding: 3px;"
            "}"
            "QTimeEdit:focus {"
            "border: 1px solid #0078d7;"
            "}"
            "QTimeEdit::up-button {"
            "border: 1px solid #adadad;"
            "background-color: #DEDEDF;"
            "border-top-right-radius:2px;"
            "border-top-left-radius:2px;"
            "image: url(resources/images/chevron-up-solid.svg);"
            "width: 30px;"
            "}"
            "QTimeEdit::up-button:hover {"
            "border: 1px solid #0078d7;"
            "background-color: #e5f1fb;"
            "}"
            "QTimeEdit::down-button {"
            "border: 1px solid #adadad;"
            "background-color: #DEDEDF;"
            "border-bottom-right-radius:2px;"
            "border-bottom-left-radius:2px;"
            "image: url(resources/images/chevron-down-solid.svg);"
            "width: 30px;"
            "}"
            "QTimeEdit::down-button:hover {"
            "border: 1px solid #0078d7;"
            "background-color: #e5f1fb;"
            "}"
            "QDateEdit {"
            "border-radius: 2;"
            "border: 1px solid #DEDEDF;"
            "padding: 3px;"
            "}"
            "QDateEdit:focus {"
            "border: 1px solid #0078d7;"
            "}"
            "QDateEdit::up-button {"
            "border: 1px solid #adadad;"
            "background-color: #DEDEDF;"
            "border-top-right-radius:2px;"
            "border-top-left-radius:2px;"
            "image: url(resources/images/chevron-up-solid.svg);"
            "width: 30px;"
            "}"
            "QDateEdit::up-button:hover {"
            "border: 1px solid #0078d7;"
            "background-color: #e5f1fb;"
            "}"
            "QDateEdit::down-button {"
            "border: 1px solid #adadad;"
            "background-color: #DEDEDF;"
            "border-bottom-right-radius:2px;"
            "border-bottom-left-radius:2px;"
            "image: url(resources/images/chevron-down-solid.svg);"
            "width: 30px;"
            "}"
            "QDateEdit::down-button:hover {"
            "border: 1px solid #0078d7;"
            "background-color: #e5f1fb;"
            "}"
            "QComboBox {"
            "border-radius: 2;"
            "border: 1px solid #DEDEDF;"
            "padding: 3px;"
            "}"
            "QComboBox::drop-down {"
            "border: None;"
            "background-color: None;"
            "image: url(resources/images/chevron-down-solid.svg);"
            "width: 10px;"
            "margin-right: 10px"
            "}"
            "QComboBox::drop-down:on {"
            "image: url(resources/images/chevron-up-solid.svg);"
            "}"
            "QComboBox:hover {"
            "border: 1px solid #0078d7;"
            "background-color: #e5f1fb;"
            "}"
            "QPushButton#add {"
            "border: None;"
            "background-color: #2185D0;"
            "border-radius: 2;"
            "padding: 3px;"
            "font: bold 12px;"
            "color: white;"
            "padding: 5px;"
            "}"
            "QPushButton#add:hover {"
            "background-color: white;"
            "color: #2185D0;"
            "}"
            "QPushButton#cancle {"
            "border: None;"
            "background-color: #E0E1E2;"
            "border-radius: 2;"
            "padding: 3px;"
            "font: bold 12px;"
            "color: #676767;"
            "padding: 5px;"
            "}"
            "QPushButton#cancle:hover {"
            "background-color: #676767;"
            "color: #E0E1E2;"
            "}")

    def init_manuelle_eingabe_raum(self):
        """
        inits manuelle eingabe raum
        :return:
        """
        # delete old
        if not (self.input is None):
            self.layout.removeWidget(self.input)
            self.input.deleteLater()

        # add new
        self.input = ManuelleEingabeRaum(self)
        self.layout.addWidget(self.input, 1, 0, 1, 3)
        self.current_datatype = datatypes.Raum

        self.__add_mode()

    def init_manuelle_eingabe_aufsichtsperson(self):
        """
        inits manuelle eingabe aufsichtsperson
        :return:
        """
        # delete old
        if not (self.input is None):
            self.layout.removeWidget(self.input)
            self.input.deleteLater()

        # add new
        self.input = ManuelleEingabeAufsichtsperson(self)
        self.layout.addWidget(self.input, 1, 0, 1, 2)
        self.current_datatype = datatypes.Aufsichtsperson

        self.__add_mode()

    def init_manuelle_eingabe_pruefung(self):
        """
        inits manuelle eingabe pruefung
        :return:
        """
        # delete old
        if not (self.input is None):
            self.layout.removeWidget(self.input)
            self.input.deleteLater()

        # add new
        self.input = ManuelleEingabePruefung(self)
        self.layout.addWidget(self.input, 1, 0, 1, 2)
        self.current_datatype = datatypes.Pruefung

        self.__add_mode()

    def init_bearbeiten(self, data_object):
        """
        inits bearbeiten_raum
        :param data_object:
        :return:
        """
        # delete old
        if not (self.input is None):
            self.layout.removeWidget(self.input)
            self.input.deleteLater()

        # add new

        if isinstance(data_object, datatypes.Raum):
            self.input = ManuelleEingabeRaum(self, data_object)
        elif isinstance(data_object, datatypes.Aufsichtsperson):
            self.input = ManuelleEingabeAufsichtsperson(self, data_object)
        elif isinstance(data_object, datatypes.Pruefung):
            self.input = ManuelleEingabePruefung(self, data_object)

        self.layout.addWidget(self.input, 1, 0, 1, 2)
        self.current_datatype = data_object.__class__

        self.__edit_mode()

    def __add_mode(self):
        self.__mode_is_add = True
        self.button_add.setText("Hinzufügen")

    def __edit_mode(self):
        self.__mode_is_add = False
        self.button_add.setText("Speichern")

    def __add(self):
        """
        fuegt das data_object dem data_handler hinzu
        :return:
        """
        data_object = self.input.create_object()
        if data_object == False:
            YesDialog("manuelle Eingabe",
                      "Es wurden nicht alle Felder ausgefüllt.", None, self,
                      "OK").exec_()
        else:
            if self.__mode_is_add:
                self.data_handler.add(data_object)
                self.input.clear_inputs()
            else:
                self.data_handler.overwrite(self.input.data_object,
                                            data_object)
                main_window.MainWindow.get_instance().centralWidget(
                ).init_overview(self.current_datatype)

    def __cancle(self):
        """
        geht zurueck zur overview
        :return:
        """
        main_window.MainWindow.get_instance().centralWidget().init_overview(
            self.current_datatype)

    @staticmethod
    def set_instance(instance):
        ManuelleEingabe.instance = instance

    @staticmethod
    def get_instance():
        return ManuelleEingabe.instance
Example #7
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()
Example #8
0
class SequenceRecordsWindow(QWidget):
    def __init__(self, parent):
        super(SequenceRecordsWindow, self).__init__(parent)
        self.grid_layout = QGridLayout()
        self.grid_layout.setContentsMargins(0, 0, 0, 0)
        self.grid_layout.setSpacing(0)
        self.setLayout(self.grid_layout)

        self.seq_font = QFont()
        self.seq_font.setFamily("Noto Sans Mono")
        self.seq_font.setPointSize(12)
        self.seq_font.setFixedPitch(True)
        self.seq_font.setStyleHint(QFont.Monospace)

        self.seq_h_scroll_bar = QScrollBar(self, self.parent())
        self.seq_h_scroll_bar.setOrientation(Qt.Horizontal)
        self.seq_h_scroll_bar.setMinimum(0)
        self.seq_h_scroll_bar.setMaximum(self.longest_seq_len - self.char_nb)
        self.seq_h_scroll_bar.valueChanged.connect(self.move_seqs)
        self.grid_layout.addWidget(self.seq_h_scroll_bar,
                                   self.grid_layout.rowCount(), 5)

        self.lower_spacer_item = QSpacerItem(1, 1, QSizePolicy.Minimum,
                                             QSizePolicy.MinimumExpanding)
        self.grid_layout.addItem(self.lower_spacer_item)

        self.seq_record_items = []

    def sizeHint(self):  # Workaroud QTBUG-70305
        return self.parent().parent().size()

    def populate(self, seq_records):
        self.grid_layout.removeWidget(self.seq_h_scroll_bar)
        self.grid_layout.removeItem(self.lower_spacer_item)

        for seq_record in seq_records:
            new_row = self.grid_layout.rowCount()
            self.seq_record_items.append(
                SequenceRecordItem(self, seq_record, self.seq_font))
            for widget_index in range(0,
                                      len(self.seq_record_items[-1].widgets)):
                col = widget_index
                self.seq_record_items[-1].seqLabel.installEventFilter(self)
                self.grid_layout.addWidget(
                    self.seq_record_items[-1].widgets[widget_index], new_row,
                    col)

            if len(seq_record) > self.longest_seq_len:
                self.longest_seq_len = len(seq_record)

        self.update_char_nb()
        self.grid_layout.addWidget(self.seq_h_scroll_bar,
                                   self.grid_layout.rowCount(), 5)
        self.grid_layout.addItem(self.lower_spacer_item)
        self.display_all_seq()

    def clear(self):
        # TODO
        pass

    def eventFilter(self, watched, event):
        if event.type() == QEvent.Resize:
            self.update_char_nb()
            self.update_scrollbar()
            self.display_all_seq()
        return super(SequenceRecordsWindow, self).eventFilter(watched, event)

    def display_all_seq(self):
        for seq_record_item in self.seq_record_items:
            seq_record_item.seqLabel.display_seq(
                seq_record_item.seq_record.seq, self.display_begin,
                self.char_nb)

    def update_char_nb(self):
        font_metrics = QFontMetrics(self.seq_font)
        px_wide_char = font_metrics.width("A")
        label_width = self.seq_record_items[0].seqLabel.width(
        )  # width of first seq label = all seq labels
        approx_char_nb = label_width // px_wide_char

        test_str = "A" * approx_char_nb

        while font_metrics.width(
                test_str) < label_width:  # fontMetrics not precise at all...
            test_str += "A"

        while font_metrics.width(
                test_str) >= label_width:  # fontMetrics not precise at all...
            test_str = test_str[:-1]

        self.char_nb = len(test_str)

    def update_scrollbar(self):
        self.seq_h_scroll_bar.setMaximum(self.longest_seq_len - self.char_nb +
                                         12)

    def move_seqs(self, value):
        print(value)
        self.display_begin = value
        self.display_all_seq()

    char_nb = 0
    longest_seq_len = 0
    display_begin = 0
Example #9
0
class BucketGame(QWidget):
    def __init__(self):
        super().__init__()
        self.buckets = []
        self.add = QPushButton("+")
        self.solve = QPushButton("Solve")
        self.edit = QPushButton("Edit")
        self.reset = QPushButton("Reset")
        self.layout = QGridLayout()
        self.main_layout = QVBoxLayout()

        self.n_move = 0
        self.title = QLabel()
        self.main_layout.addWidget(self.title)

        self.btn_layout = QHBoxLayout()

        self.main_layout.addLayout(self.layout)

        self.btn_layout.addWidget(self.add)
        self.btn_layout.addWidget(self.solve)
        self.btn_layout.addWidget(self.edit)
        self.btn_layout.addWidget(self.reset)
        self.main_layout.addLayout(self.btn_layout)

        self.setLayout(self.main_layout)
        self.update_layout()
        self.bucket_selected = None
        self.add.clicked.connect(self.new_bucket)
        self.solve.clicked.connect(self.to_solve)
        self.reset.clicked.connect(self.reset_all)
        self.edit.clicked.connect(self.edit_all)

    def reset_all(self):
        for b in self.buckets:
            b.reset()

    def edit_all(self):
        if self.is_possible():
            for b in self.buckets:
                b.switch_editable()
            self.n_move = 0
        else:
            QMessageBox.warning(self, "Attention",
                                f"Le problème est insoluble")

    def is_possible(self):
        total_init = sum([b.init for b in self.buckets])
        total_goal = sum([b.goal for b in self.buckets])
        return total_init == total_goal

    def update_layout(self):
        i = 0
        max_columns = 4
        self.layout.removeWidget(self.add)
        for bucket in self.buckets:
            self.layout.removeWidget(bucket)
            self.layout.addWidget(bucket, i // max_columns, i % max_columns)
            i += 1
        if i % max_columns > 0:
            self.layout.addItem(
                QSpacerItem(1, 1, QSizePolicy.Expanding,
                            QSizePolicy.Expanding), i // max_columns,
                i % max_columns, i // max_columns, max_columns)

    def add_bucket(self, bucket):
        self.buckets.append(bucket)
        bucket.selected.connect(self.select)
        bucket.removed.connect(self.remove)
        self.update_layout()
        self.update_title()

    def new_bucket(self):
        self.add_bucket(Bucket(0, 0, 0))

    def remove(self, bucket):
        self.buckets.remove(bucket)
        self.layout.removeWidget(bucket)
        bucket.hide()
        self.update_layout()

    def select(self, bucket):
        if self.bucket_selected is None:
            if bucket.current == 0:
                bucket.changed_color(True, Qt.red)
                QTimer.singleShot(1000, bucket.changed_color)
            else:
                bucket.changed_color(True)
                self.bucket_selected = bucket
        elif self.bucket_selected == bucket:
            bucket.changed_color(False)
            self.bucket_selected = None
        elif bucket.full():
            bucket.changed_color(True, Qt.red)
            QTimer.singleShot(1000, bucket.changed_color)
        else:
            self.bucket_selected.emptying(bucket)
            self.bucket_selected.changed_color(False)
            self.bucket_selected = None
            self.n_move += 1
            bucket.changed_color(False)
        self.update_title()

    def is_victory(self):
        for b in self.buckets:
            if not b.good():
                return False
        return True

    def update_title(self):
        if self.is_victory():
            self.title.setText(f"Victoire en {self.n_move} tours")
            #for bucket in self.buckets:
            #    bucket.changed_color(True, Qt.green)
        else:
            self.title.setText(f"Tours {self.n_move}")

    def move_auto(self, transfered, total_steps, i=1):
        transfer = transfered[i]
        self.select(self.buckets[transfer[0] - 1])
        self.select(self.buckets[transfer[1] - 1])
        self.update_layout()
        self.update_title()
        if i != total_steps:
            QTimer.singleShot(
                500, lambda: self.move_auto(transfered, total_steps, i + 1))

    def to_solve(self):
        from minizinc import Instance, Model, Solver
        seaux = Model("./seaux.mzn")
        gecode = Solver.lookup("gecode")
        instance = Instance(gecode, seaux)
        instance["N"] = len(self.buckets)
        instance["init"] = [b.current for b in self.buckets]
        instance["storage"] = [b.capacity for b in self.buckets]
        instance["goal"] = [b.goal for b in self.buckets]
        result = instance.solve()
        if result is not None:
            print(result)
            #QMessageBox.information(self, "Solution", f"Le problème peut être résoulu en {result['total_steps']}")
            self.move_auto(result["transfered"], result["total_steps"] - 1)
        else:
            QMessageBox.information(self, "Solution",
                                    f"Le problème est insoluble")
Example #10
0
class Window(QMainWindow):
    def __init__(self):
        # Application Settings:
        self.app = QApplication(sys.argv)
        self.app.setStyle("Fusion")
        #self.app.setWindowIcon(QIcon("gear_drop.ico"))
        super(Window, self).__init__()
        self.setWindowTitle("Nerd Lab - HTML Replace")
        self.main_widget = QWidget(self)
        self.main_layout = QGridLayout(self.main_widget)
        self.setStyleSheet("background-color: #333333; color: #dedede;")

        self.exit_command = QAction("Exit", self)
        self.exit_command.triggered.connect(self.close)

        self.main_menu = self.menuBar()
        self.file_menu = self.main_menu.addMenu("File")
        self.file_menu.addAction(self.exit_command)
        self.help_menu = self.main_menu.addMenu("Help")

        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll_widget = QWidget()
        self.scroll_area.setWidget(self.scroll_widget)
        QScroller.grabGesture(self.scroll_area.viewport(),
                              QScroller.LeftMouseButtonGesture)

        html_dir_row = QWidget()
        html_dir_layout = QGridLayout(html_dir_row)
        self.html_dir_label = QLabel("Directory containing the Html files:")
        self.html_dir_entry = QLineEdit()
        self.html_dir_entry.setStyleSheet(
            "background-color: #434343; selection-background-color: darkgray;")
        self.html_dir_entry.setReadOnly(True)
        self.html_dir_entry.setCursor(Qt.IBeamCursor)
        self.html_dir_button = QPushButton("Browse")
        self.html_dir_button.clicked.connect(self.setHTMLDir)
        html_dir_layout.addWidget(self.html_dir_label, 0, 0)
        html_dir_layout.addWidget(self.html_dir_entry, 1, 0)
        html_dir_layout.addWidget(self.html_dir_button, 1, 1)

        self.html_files = []
        self.dir_name = "Replaced Html Files"

        self.scroll_layout = QGridLayout(self.scroll_widget)

        self.add_replace_button = QPushButton("Add Replace Entry")
        self.add_replace_button.clicked.connect(self.addReplaceRow)
        self.rmv_replace_button = QPushButton("Remove Replace Entry")
        self.rmv_replace_button.clicked.connect(self.removeReplaceRow)
        replace_button_row = QWidget()
        replace_button_row_layout = QGridLayout(replace_button_row)
        replace_button_row_layout.addWidget(self.add_replace_button, 0, 0)
        replace_button_row_layout.addWidget(self.rmv_replace_button, 0, 1)
        replace_button_row_layout.addWidget(self.filler(False), 0, 2)

        self.status_box = QTextEdit()
        self.status_box.setReadOnly(True)
        self.status_box.setCursor(Qt.IBeamCursor)
        self.status_box.setStyleSheet("background-color: #434343;")

        replace_row = QWidget()
        replace_layout = QGridLayout(replace_row)
        self.replace_button = QPushButton("Replace")
        self.replace_button.clicked.connect(self.replaceHTML)
        self.replace_button.setDisabled(True)
        replace_layout.addWidget(self.filler(False), 0, 0)
        replace_layout.addWidget(self.replace_button, 0, 1)

        self.replace_grid = QWidget()
        self.replace_grid_layout = QGridLayout(self.replace_grid)
        self.current_row = 0
        self.scroll_layout.addWidget(self.replace_grid)
        self.scroll_layout.addWidget(self.filler())

        self.main_layout.addWidget(html_dir_row)
        self.main_layout.addWidget(
            QLabel(
                'Enter the exact line of Html you want to replace in the "Find What:" entry.\nIn the "Replace With:" entry, enter the Html that will be replacing the found line of Html.'
            ))
        self.main_layout.addWidget(self.scroll_area)
        self.main_layout.addWidget(replace_button_row)
        self.main_layout.addWidget(self.status_box)
        self.main_layout.addWidget(replace_row)

        self.setCentralWidget(self.main_widget)

        self.replace_rows = []
        self.addReplaceRow()
        self.rmv_replace_button.setDisabled(True)
        self.createReplaceFolder()

    def start(self):
        self.showMaximized()
        sys.exit(self.app.exec_())

    def createReplaceFolder(self):
        try:
            if os.path.isdir(self.dir_name):
                for html_file in os.listdir(self.dir_name):
                    os.remove(self.dir_name + '/' + html_file)
            else:
                os.mkdir(self.dir_name)

        except Exception as e:
            print(str(e) + " on line {}".format(sys.exc_info()[-1].tb_lineno))

    def setHTMLDir(self):
        try:
            html_dir = QFileDialog.getExistingDirectory()
            if html_dir != "":
                self.html_dir_entry.setText(html_dir)
                self.replace_button.setEnabled(True)

        except Exception as e:
            print(str(e) + " on line {}".format(sys.exc_info()[-1].tb_lineno))

    def replaceHTML(self):
        try:
            extension = ""
            html_dir_path = self.html_dir_entry.text()
            first_line = True
            replace_count = 0
            self.status_box.clear()
            if html_dir_path != "":
                self.html_files = list(os.listdir(html_dir_path))
                for i in range(0, len(self.html_files)):
                    extension = os.path.splitext(self.html_files[i])[1]
                    if extension == ".html" or extension == ".htm":
                        if first_line:
                            replace_count = self.replaceHTMLInFile(
                                self.html_files[i])
                            self.status_box.insertHtml(self.html_files[i] +
                                                       "  <b>" +
                                                       str(replace_count) +
                                                       "</b> lines replaced.")
                            first_line = False
                        else:
                            replace_count = self.replaceHTMLInFile(
                                self.html_files[i])
                            self.status_box.insertHtml("<br />" +
                                                       self.html_files[i] +
                                                       "  <b>" +
                                                       str(replace_count) +
                                                       "</b> lines replaced.")

        except Exception as e:
            print(str(e) + " on line {}".format(sys.exc_info()[-1].tb_lineno))

    def findLineInHTML(self, html_line):
        for replace_row in self.replace_rows:
            replace_html_line = replace_row[1].text()
            replace_html_line = replace_html_line.strip()
            if html_line == replace_html_line:
                new_line = replace_row[3].text()
                return new_line.strip()
        return ""

    def replaceHTMLInFile(self, html_file):
        try:
            replace_html_line = ""
            replace_counts = 0
            with open(self.html_dir_entry.text() + '/' + html_file,
                      'r',
                      encoding='UTF-8') as h_f, open(
                          self.dir_name + '/' + html_file,
                          'w',
                          encoding='UTF-8') as new_h_f:
                for line in h_f:
                    replace_html_line = self.findLineInHTML(line.strip())
                    if replace_html_line == "":
                        new_h_f.write(line)
                    else:
                        new_h_f.write(replace_html_line)
                        replace_counts += 1

            return replace_counts

        except Exception as e:
            print(str(e) + " on line {}".format(sys.exc_info()[-1].tb_lineno))

    def addReplaceRow(self):
        try:
            replace_row = [
                QLabel(str(self.current_row + 1) + ")  Find What: "),
                QLineEdit(),
                QLabel("   Replace With: "),
                QLineEdit()
            ]
            replace_row[1].setStyleSheet(
                "background-color: #434343; selection-background-color: darkgray;"
            )
            replace_row[3].setStyleSheet(
                "background-color: #434343; selection-background-color: darkgray;"
            )
            column = 0
            for widget in replace_row:
                self.replace_grid_layout.addWidget(widget, self.current_row,
                                                   column)
                column += 1

            self.replace_rows.append(replace_row)
            self.current_row += 1
            if len(self.replace_rows) > 1:
                self.rmv_replace_button.setEnabled(True)

        except Exception as e:
            print(str(e) + " on line {}".format(sys.exc_info()[-1].tb_lineno))

    def removeReplaceRow(self):
        try:
            if len(self.replace_rows) > 1:
                for widget in self.replace_rows[len(self.replace_rows) - 1]:
                    self.replace_grid_layout.removeWidget(widget)
                    widget.deleteLater()

                del self.replace_rows[len(self.replace_rows) - 1]
                self.current_row -= 1
                if len(self.replace_rows) == 1:
                    self.rmv_replace_button.setDisabled(True)

        except Exception as e:
            print(str(e) + " on line {}".format(sys.exc_info()[-1].tb_lineno))

    def filler(self, vertical_filler=True):
        filler = QWidget()
        if vertical_filler:
            filler.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            return filler
        filler.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        return filler

    def breakLine(self):
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        return line
Example #11
0
class CentralWidgetOverview(QWidget):
    """
    CentralWidget of MainWindow
    """

    __selected_stylesheet = ("QPushButton {"
                           "border: 1px solid #adadad;"
                           "background-color: #ffffff;"
                           "padding-top: 3px;"
                           "padding-bottom: 3px;"
                           "border-top-left-radius:5px;"
                           "border-top-right-radius:5px;"
                           "}"
                           "QPushButton:hover {"
                           "border: 1px solid #0078d7;"
                           "background-color: #e5f1fb;"
                           "}")

    __normal_stylesheet = ("QPushButton {"
                           "border: 1px solid #adadad;"
                           "background-color: #D9D9DA;"
                           "padding-top: 3px;"
                           "padding-bottom: 3px;"
                           "border-top-left-radius:5px;"
                           "border-top-right-radius:5px;"
                           "}"
                           "QPushButton:hover {"
                           "border: 1px solid #0078d7;"
                           "background-color: #e5f1fb;"
                           "}")
    current_datatype = datatypes.Raum
    instance = None

    def __init__(self, data_handler, get_overview_funct, current_datatype=None):
        super(CentralWidgetOverview, self).__init__()
        CentralWidgetOverview.set_instance(self)

        self.data_handler = data_handler
        self.get_overview_funct = get_overview_funct
        self.current_overview = None

        if not (current_datatype is None):
            CentralWidgetOverview.set_current_datatype(current_datatype)

        # tabs
        self.tab_raeume = QPushButton("Räume")
        self.tab_aufsichtspersonen = QPushButton("Aufsichtspersonen")
        self.tab_pruefungen = QPushButton("Prüfungen")

        self.tab_raeume.clicked.connect(lambda: self.update_current_datatype(datatypes.Raum))
        self.tab_aufsichtspersonen.clicked.connect(lambda: self.update_current_datatype(datatypes.Aufsichtsperson))
        self.tab_pruefungen.clicked.connect(lambda: self.update_current_datatype(datatypes.Pruefung))

        self.tab_raeume.setStyleSheet(self.__normal_stylesheet)
        self.tab_aufsichtspersonen.setStyleSheet(self.__normal_stylesheet)
        self.tab_pruefungen.setStyleSheet(self.__normal_stylesheet)


        #layout
        self.grid_layout = QGridLayout()
        self.setLayout(self.grid_layout)

        self.grid_layout.setHorizontalSpacing(0)
        self.grid_layout.setVerticalSpacing(0)

        self.grid_layout.addWidget(self.tab_raeume,0,0)
        self.grid_layout.addWidget(self.tab_aufsichtspersonen,0,1)
        self.grid_layout.addWidget(self.tab_pruefungen,0,2)
        self.grid_layout.setMargin(0)

        #overview
        self.update_current_datatype(CentralWidgetOverview.get_current_datatype(), force_update=True)

        # add update listener
        data_handler.add_update_listener(self.data_updated)



    def data_updated(self, datatype):
        """
        called when data updated
        :param datatype: class of datatypes.py
        :return:
        """
        if CentralWidgetOverview.get_current_datatype() == datatype:
            # remove old overview
            if self.current_overview != None:
                self.grid_layout.removeWidget(self.current_overview)
                self.current_overview.deleteLater()

            self.set_current_overview()
        else:
            self.update_current_datatype(datatype)


    def update_current_datatype(self, datatype, force_update = False):
        """
        updates the widget based on datatype
        :param datatype: class of datatypes.py
        :return: None
        """
        if CentralWidgetOverview.get_current_datatype() != datatype or force_update:
            # update button paletts
            if CentralWidgetOverview.get_current_datatype() == datatypes.Raum:
                self.tab_raeume.setStyleSheet(self.__normal_stylesheet)
            elif CentralWidgetOverview.get_current_datatype() == datatypes.Aufsichtsperson:
                self.tab_aufsichtspersonen.setStyleSheet(self.__normal_stylesheet)
            elif CentralWidgetOverview.get_current_datatype() == datatypes.Pruefung:
                self.tab_pruefungen.setStyleSheet(self.__normal_stylesheet)

            if datatype == datatypes.Raum:
                self.tab_raeume.setStyleSheet(self.__selected_stylesheet)
            elif datatype == datatypes.Aufsichtsperson:
                self.tab_aufsichtspersonen.setStyleSheet(self.__selected_stylesheet)
            elif datatype == datatypes.Pruefung:
                self.tab_pruefungen.setStyleSheet(self.__selected_stylesheet)

            #remove old overview
            if self.current_overview != None:
                self.grid_layout.removeWidget(self.current_overview)
                self.current_overview.deleteLater()

            #update overview
            CentralWidgetOverview.set_current_datatype(datatype)
            self.set_current_overview()

    def set_current_overview(self):
        """
        sets the overview and adds it to layout
        :return: None
        """
        self.current_overview = self.get_overview_funct(self.data_handler, CentralWidgetOverview.get_current_datatype(), self)
        self.grid_layout.addWidget(self.current_overview,1,0,1,6)

    def paintEvent(self, event:QPaintEvent):
        """
        draws background
        :param event:
        :return: None
        """
        color = QColor(240,240,240)
        white = QColor(255,255,255)
        custom_painter = QPainter(self)
        custom_painter.fillRect(self.rect(), white)

    @staticmethod
    def set_current_datatype(datatype):
        CentralWidgetOverview.current_datatype = datatype
    @staticmethod
    def get_current_datatype():
        return CentralWidgetOverview.current_datatype


    @staticmethod
    def set_instance(instance):
        CentralWidgetOverview.instance = instance
    @staticmethod
    def get_instance():
        return CentralWidgetOverview.instance