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()
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)
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
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()
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
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")
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
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