class ElaWidget(ToolWidget): def __init__(self, image, parent=None): super(ElaWidget, self).__init__(parent) params_layout = QHBoxLayout() params_layout.addWidget(QLabel(self.tr('Quality:'))) self.quality_spin = QSpinBox() self.quality_spin.setRange(0, 100) self.quality_spin.setSuffix(self.tr(' %')) self.quality_spin.valueChanged.connect(self.process) params_layout.addWidget(self.quality_spin) params_layout.addWidget(QLabel(self.tr('Scale:'))) self.scale_spin = QSpinBox() self.scale_spin.setRange(1, 100) self.scale_spin.valueChanged.connect(self.process) params_layout.addWidget(self.scale_spin) self.equalize_check = QCheckBox(self.tr('Equalized')) self.equalize_check.stateChanged.connect(self.process) params_layout.addWidget(self.equalize_check) params_layout.addStretch() default_button = QPushButton(self.tr('Default')) default_button.clicked.connect(self.default) params_layout.addWidget(default_button) self.image = image self.viewer = ImageViewer(self.image, self.image) self.default() self.process() main_layout = QVBoxLayout() main_layout.addLayout(params_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) def process(self): equalize = self.equalize_check.isChecked() self.scale_spin.setEnabled(not equalize) start = time() quality = self.quality_spin.value() scale = self.scale_spin.value() compressed = compress_jpeg(self.image, quality) if not equalize: ela = cv.convertScaleAbs(cv.subtract(compressed, self.image), None, scale) else: ela = cv.merge([ cv.equalizeHist(c) for c in cv.split(cv.absdiff(compressed, self.image)) ]) self.viewer.update_processed(ela) self.info_message.emit( self.tr('Error Level Analysis = {}'.format(elapsed_time(start)))) def default(self): self.quality_spin.setValue(75) self.scale_spin.setValue(20)
class TestingPanel(Panel): def __init__(self, maps, threads): super().__init__() self.maps = maps self.rbfn = None self.threads = threads self.__set_execution_ui() self.__set_outputs_ui() self.__set_graphic_ui() self.__set_console_ui() def __set_execution_ui(self): group_box = QGroupBox('Testing Execution') inner_layout = QHBoxLayout() group_box.setLayout(inner_layout) self.map_selector = QComboBox() self.map_selector.addItems(list(self.maps.keys())) self.map_selector.setStatusTip('Select the training dataset.') self.map_selector.currentIndexChanged.connect(self.__change_map) self.start_btn = QPushButton('Test') self.start_btn.setStatusTip( 'Start testing. (available after training)') self.start_btn.setDisabled(True) self.start_btn.clicked.connect(self.__run) self.stop_btn = QPushButton('Stop') self.stop_btn.setStatusTip('Force the testing stop running.') self.stop_btn.setDisabled(True) self.fps = QSpinBox() self.fps.setMinimum(1) self.fps.setMaximum(60) self.fps.setValue(20) self.fps.setStatusTip( "The re-drawing rate for car simulator. High fps " "may cause the plot shows discontinuously.") inner_layout.addWidget(self.map_selector, 1) inner_layout.addWidget(QLabel("FPS:")) inner_layout.addWidget(self.fps) inner_layout.addWidget(self.start_btn) inner_layout.addWidget(self.stop_btn) self._layout.addWidget(group_box) def __set_outputs_ui(self): group_box = QGroupBox("Testing Details") inner_layout = QFormLayout() group_box.setLayout(inner_layout) self.car_position = QLabel('--') self.car_angle = QLabel('--') self.wheel_angle = QLabel('--') self.dist_front = QLabel('--') self.dist_left = QLabel('--') self.dist_right = QLabel('--') self.car_position.setAlignment(Qt.AlignCenter) self.car_angle.setAlignment(Qt.AlignCenter) self.wheel_angle.setAlignment(Qt.AlignCenter) self.dist_front.setAlignment(Qt.AlignCenter) self.dist_left.setAlignment(Qt.AlignCenter) self.dist_right.setAlignment(Qt.AlignCenter) inner_layout.addRow('Car Position:', self.car_position) inner_layout.addRow('Car Angle:', self.car_angle) inner_layout.addRow('Wheel Angle:', self.wheel_angle) inner_layout.addRow('Front Distance:', self.dist_front) inner_layout.addRow('Left Distance:', self.dist_left) inner_layout.addRow('Right Distance:', self.dist_right) self._layout.addWidget(group_box) def __set_graphic_ui(self): self.simulator = CarSimulatorPlot() self.simulator.setStatusTip("Show the graphic of the car controled by " "the result of the PSO in mazz.") self.__change_map() self._layout.addWidget(self.simulator) def __set_console_ui(self): self.__console = QTextEdit() self.__console.setReadOnly(True) self.__console.setStatusTip("Show the logs of status changing.") self._layout.addWidget(self.__console) @Slot() def __init_widgets(self): self.start_btn.setDisabled(True) self.stop_btn.setEnabled(True) self.fps.setDisabled(True) self.map_selector.setDisabled(True) @Slot() def __reset_widgets(self): self.start_btn.setEnabled(True) self.stop_btn.setDisabled(True) self.fps.setEnabled(True) self.map_selector.setEnabled(True) @Slot(str) def print_console(self, text): self.__console.append(text) @Slot(list, list, list) def __show_dists(self, pos, intersections, dists): self.simulator.paint_dist(pos, intersections) self.dist_front.setText(str(dists[0])) self.dist_left.setText(str(dists[1])) self.dist_right.setText(str(dists[2])) @Slot() def __show_car_collided(self): self.simulator.paint_car_collided() def __show_path(self, xdata, ydata): self.simulator.paint_path(xdata, ydata) @Slot() def __change_map(self): self.__current_map = self.maps[self.map_selector.currentText()] self.__car = Car(self.__current_map['start_pos'], self.__current_map['start_angle'], 3, self.__current_map['route_edge']) self.simulator.paint_map(self.__current_map) self.__move_car(self.__current_map['start_pos'], self.__current_map['start_angle']) self.__show_dists(self.__current_map['start_pos'], [self.__current_map['start_pos']] * 3, ['--'] * 3) @Slot(list, float, float) def __move_car(self, pos, angle, wheel_angle=0.0): self.simulator.paint_car(pos, angle) self.car_position.setText("({:.7f}, {:.7f})".format(*pos)) self.car_angle.setText(str(angle)) self.wheel_angle.setText(str(wheel_angle)) @Slot(RBFN) def load_rbfn(self, rbfn): self.rbfn = rbfn self.print_console('New RBFN model has been loaded.') self.start_btn.setEnabled(True) @Slot() def __run(self): # reset the map self.__change_map() # create a QThread if self.rbfn is None: raise TypeError('The RBFN model has not yet loaded.') self.__thread = RunCar(self.__car, self.rbfn, (self.__current_map['end_area_lt'], self.__current_map['end_area_rb']), self.fps.value()) self.threads.append(self.__thread) self.stop_btn.clicked.connect(self.__thread.stop) self.__thread.started.connect(self.__init_widgets) self.__thread.finished.connect(self.__reset_widgets) self.__thread.sig_console.connect(self.print_console) self.__thread.sig_car.connect(self.__move_car) self.__thread.sig_car_collided.connect(self.__show_car_collided) self.__thread.sig_dists.connect(self.__show_dists) self.__thread.sig_results.connect(self.__get_results) self.__thread.start() @Slot(list) def __get_results(self, results): """Get the results of last running and draw the path of it.""" self.simulator.paint_path([d['x'] for d in results], [d['y'] for d in results])
class Widget(QWidget): movie_url_label_text = "粘贴有 dailymotion 版的連續劇网址 (例子: https://dramaq.de/cn191023b/): " def __init__(self): QWidget.__init__(self) self.q = None self.pool = None self.top = QHBoxLayout() self.top.setMargin(10) self.middle = QVBoxLayout() self.middle.setMargin(10) self.radioButtonDailyMotionDrama = QRadioButton( "有 Dailymotion Drama 的網站") self.radioButtonAny = QRadioButton("其它類型網站 (例如 YouTube)") self.top.addWidget(self.radioButtonDailyMotionDrama) self.top.addWidget(self.radioButtonAny) self.url_label = QLabel() self.url = QLineEdit() self.url_label.setBuddy(self.url) self.middle.addWidget(self.url_label) self.middle.addWidget(self.url) self.browse_folder_label = QLabel("下載到:") self.browseFolder = QPushButton("選擇目錄") self.browse_folder_label.setBuddy(self.browseFolder) self.middle.addWidget(self.browse_folder_label) self.middle.addWidget(self.browseFolder) self.browse_folder_value = "" self.bk_cinemae_spin_from = 1 self.bk_cinemae_spin_to = 1 self.fromEpSpinBox = QSpinBox() self.fromEpSpinBox.setMinimum(1) self.fromEpSpinBox.setMaximum(2147483647) self.fromEpLabel = QLabel("&從第幾集開始下載:") self.fromEpLabel.setBuddy(self.fromEpSpinBox) self.toEpSpinBox = QSpinBox() self.toEpSpinBox.setMinimum(1) self.toEpSpinBox.setMaximum(2147483647) self.toEpLabel = QLabel("&到第幾集停止下載:") self.toEpLabel.setBuddy(self.toEpSpinBox) self.cinema_ly = QHBoxLayout() #self.cinema_ly.setMargin(10) self.cinema_ly.addWidget(self.fromEpLabel) self.cinema_ly.addWidget(self.fromEpSpinBox) self.cinema_ly.addWidget(self.toEpLabel) self.cinema_ly.addWidget(self.toEpSpinBox) self.middle.addLayout(self.cinema_ly) self.add = QPushButton("開始下載") self.add.setEnabled(False) self.middle.addWidget(self.add) self.stop_me = QPushButton("停止下載") self.stop_me.setEnabled(False) self.middle.addWidget(self.stop_me) self.log_area = QPlainTextEdit() self.log_area.setReadOnly(True) self.log_area.setMaximumBlockCount(1000) self.middle.addWidget(self.log_area) #self.table_view.setSizePolicy(size) #self.layout.addWidget(self.table) self.layout = QVBoxLayout() self.layout.addLayout(self.top) self.layout.addLayout(self.middle) self.setLayout(self.layout) self.radioButtonDailyMotionDrama.toggled.connect( self.choose_DailyMotionDrama_widgets) self.radioButtonAny.toggled.connect(self.choose_Any_widgets) self.url.textChanged[str].connect(self.check_disable_download) self.browseFolder.clicked.connect(self.add_folder) self.add.clicked.connect(self.start_download) self.stop_me.clicked.connect(self.stop_download) self.radioButtonDailyMotionDrama.setChecked( True) #set default only after .connect above # TESTING PURPOSE ''' self.url.setText('https://journalflash.com/cn191023b/') self.browse_folder_value = 'C:/Users/Administrator/Documents/duboku' ''' #set current process (not queue that one) log handler: logger = logging.getLogger(__name__) handler2 = LoggerWriter() logger.addHandler(handler2) logger.setLevel(logging.INFO) #DEBUG handler2.emitter.sigLog.connect(self.log_area.appendPlainText) sys.stdout = handler2 #LoggerWriter() #sys.stderr = handler2 #Seems no difference #handler2.emitter.sigLog.emit('hihi') @Slot() def choose_DailyMotionDrama_widgets(self): if self.radioButtonDailyMotionDrama.isChecked(): self.fromEpLabel.setEnabled(True) self.toEpLabel.setEnabled(True) self.fromEpSpinBox.setEnabled(True) self.toEpSpinBox.setEnabled(True) self.fromEpSpinBox.setValue(self.bk_cinemae_spin_from) self.toEpSpinBox.setValue(self.bk_cinemae_spin_to) self.fromEpLabel.setDisabled(True) self.toEpLabel.setDisabled(True) @Slot() def choose_Any_widgets(self): if self.radioButtonAny.isChecked(): self.fromEpSpinBox.setDisabled(True) self.toEpSpinBox.setDisabled(True) self.bk_cinemae_spin_from = self.fromEpSpinBox.value() self.bk_cinemae_spin_to = self.toEpSpinBox.value() self.fromEpSpinBox.setValue(1) self.toEpSpinBox.setValue(1) @Slot() def add_folder(self, s): #fname = QFileDialog.getOpenFileName(self, 'Open file', "c:\'", "Image files (*.jpg *.gif)") #fname = QFileDialog.getOpenFileName(self, 'Open file', '', QFileDialog.ShowDirsOnly) fname = QFileDialog.getExistingDirectory(self, '選擇下載至什麼目錄', '', QFileDialog.ShowDirsOnly) #print('repr: ' + repr(fname)) if fname and fname.strip(): fname = fname.strip() self.browse_folder_value = fname #if getOpenFileName, will return ('/home/xiaobai/Pictures/disco.jpg', 'Image files (*.jpg *.gif)') #, while if getExistingDirectory, will return single path string only self.browseFolder.setText(fname) self.check_disable_download(fname) #else: # print('User cancel') @Slot() def check_disable_download(self, s): if self.url.text() and self.browse_folder_value: self.add.setEnabled(True) else: self.add.setEnabled(False) def task_done(self, retVal): self.add.setEnabled(True) self.stop_me.setEnabled(False) @Slot() def stop_download(self): if self.q: self.q.close() if self.pool: self.pool.terminate() self.add.setEnabled(True) self.stop_me.setEnabled(False) print('下載停止。') @Slot() def start_download(self): if self.fromEpSpinBox.value() > self.toEpSpinBox.value(): self.log_area.setPlainText('[!] 從第幾集必須小於或等於到第幾集。') return #No need worry click twice too fast, it seems already handle by PySide2 self.add.setEnabled(False) self.stop_me.setEnabled(True) self.log_area.clear() dest_full_path = self.browse_folder_value ''' print('dest_full_path: ' + repr(dest_full_path)) print('self.url.text(): ' + repr(self.url.text())) print('self.fromEpSpinBox.value(): ' + repr(self.fromEpSpinBox.value())) print('self.toEpSpinBox.value(): ' + repr(self.toEpSpinBox.value())) ''' import drama_dailymotion_console #Windows can't set like that bcoz not update for args.url, must put explicitly #drama_dailymotion_console.redirect_stdout_to_custom_stdout(arg_url, ...etc, LoggerWriter()) #failed other process handler = LogHandlerOtherProcess() handler.emitter.sigLog.connect(self.log_area.appendPlainText) ''' #ref current process: logger = logging.getLogger(__name__) handler2 = LoggerWriter() logger.addHandler(handler2) logger.setLevel(logging.DEBUG) handler2.emitter.sigLog.connect(self.log_area.appendPlainText) sys.stdout = handler2 #LoggerWriter() #handler2.emitter.sigLog.emit('hihi') ''' #handler = LoggerWriter() #handler.emitter.sigLog.connect(self.log_area.appendPlainText) self.q = multiprocessing.Queue() self.ql = QueueListener(self.q, handler) self.ql.start() self.pool = multiprocessing.Pool(1, worker_init, [self.q]) if self.radioButtonDailyMotionDrama.isChecked(): self.pool.apply_async(drama_dailymotion_console.main, args=(dest_full_path, self.fromEpSpinBox.value(), self.toEpSpinBox.value(), self.url.text(), LoggerWriterOtherProcess(), False), callback=self.task_done) else: self.pool.apply_async(drama_dailymotion_console.main, args=(dest_full_path, self.fromEpSpinBox.value(), self.toEpSpinBox.value(), self.url.text(), LoggerWriterOtherProcess(), True), callback=self.task_done)
class MagnifierWidget(ToolWidget): def __init__(self, image, parent=None): super(MagnifierWidget, self).__init__(parent) self.equalize_radio = QRadioButton(self.tr("Equalization")) self.equalize_radio.setToolTip(self.tr("RGB histogram equalization")) self.contrast_radio = QRadioButton(self.tr("Auto Contrast")) self.contrast_radio.setToolTip(self.tr("Compress luminance tonality")) self.centile_spin = QSpinBox() self.centile_spin.setRange(0, 100) self.centile_spin.setValue(20) self.centile_spin.setSuffix(self.tr(" %")) self.centile_spin.setToolTip(self.tr("Histogram percentile amount")) self.channel_check = QCheckBox(self.tr("By channel")) self.channel_check.setToolTip(self.tr("Independent RGB compression")) self.equalize_radio.setChecked(True) self.last_radio = self.equalize_radio self.image = image self.viewer = ImageViewer(self.image, self.image) self.change() self.viewer.viewChanged.connect(self.process) self.equalize_radio.clicked.connect(self.change) self.contrast_radio.clicked.connect(self.change) self.centile_spin.valueChanged.connect(self.change) self.channel_check.stateChanged.connect(self.change) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr("Mode:"))) top_layout.addWidget(self.equalize_radio) top_layout.addWidget(self.contrast_radio) top_layout.addWidget(self.centile_spin) top_layout.addWidget(self.channel_check) top_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) def process(self, rect): y1 = rect.top() y2 = rect.bottom() x1 = rect.left() x2 = rect.right() roi = self.image[y1:y2, x1:x2] if self.equalize_radio.isChecked(): self.centile_spin.setEnabled(False) self.channel_check.setEnabled(False) roi = equalize_img(roi) self.last_radio = self.equalize_radio elif self.contrast_radio.isChecked(): self.centile_spin.setEnabled(True) self.channel_check.setEnabled(True) centile = self.centile_spin.value() / 200 if self.channel_check.isChecked(): roi = cv.merge( [cv.LUT(c, auto_lut(c, centile)) for c in cv.split(roi)]) else: roi = cv.LUT( roi, auto_lut(cv.cvtColor(roi, cv.COLOR_BGR2GRAY), centile)) self.last_radio = self.contrast_radio else: self.last_radio.setChecked(True) return processed = np.copy(self.image) processed[y1:y2, x1:x2] = roi self.viewer.update_processed(processed) def change(self): self.process(self.viewer.get_rect())
class TrainingPanel(Panel): def __init__(self, datasets, testing_panel, threads): super().__init__() if isinstance(testing_panel, TestingPanel): self.testing_panel = testing_panel else: raise TypeError('"testing_panel" must be the instance of ' '"TestingPanel"') self.datasets = datasets self.threads = threads self.__set_execution_ui() self.__set_options_ui() self.__set_outputs_ui() self.__set_graphic_ui() def __set_execution_ui(self): group_box = QGroupBox('Training Execution') inner_layout = QHBoxLayout() group_box.setLayout(inner_layout) self.data_selector = QComboBox() self.data_selector.addItems(list(self.datasets.keys())) self.data_selector.setStatusTip('Select the training dataset.') self.start_btn = QPushButton('Train') self.start_btn.setStatusTip('Start training.') self.start_btn.clicked.connect(self.__run) self.stop_btn = QPushButton('Stop') self.stop_btn.setStatusTip('Force the training stop running.') self.stop_btn.setDisabled(True) self.multicore_cb = QCheckBox('Multicore') self.multicore_cb.setStatusTip('Use multiprocessing in calculating ' 'fitting for populations.') self.multicore_cb.setChecked(True) inner_layout.addWidget(self.data_selector, 1) inner_layout.addWidget(self.start_btn) inner_layout.addWidget(self.stop_btn) inner_layout.addWidget(self.multicore_cb) self._layout.addWidget(group_box) def __set_options_ui(self): group_box = QGroupBox('Training Options') inner_layout = QFormLayout() group_box.setLayout(inner_layout) self.iter_times = QSpinBox() self.iter_times.setRange(1, 1000000) self.iter_times.setValue(200) self.iter_times.setStatusTip('The total iterating times for training.') self.population_size = QSpinBox() self.population_size.setRange(1, 100000) self.population_size.setValue(100) self.population_size.setStatusTip('The population size for the PSO.') self.inertia_weight = QDoubleSpinBox() self.inertia_weight.setRange(0, 50) self.inertia_weight.setValue(1) self.inertia_weight.setSingleStep(0.1) self.inertia_weight.setStatusTip('The inertia weight of the velocity ' ' for each individual.') self.cognitive_const_rand_upper = QDoubleSpinBox() self.cognitive_const_rand_upper.setRange(0, 50) self.cognitive_const_rand_upper.setValue(2) self.cognitive_const_rand_upper.setSingleStep(0.1) self.cognitive_const_rand_upper.setStatusTip( 'The random upper bound for cognitive accelerate constant.') self.social_const_rand_upper = QDoubleSpinBox() self.social_const_rand_upper.setRange(0, 50) self.social_const_rand_upper.setValue(3) self.social_const_rand_upper.setSingleStep(0.1) self.social_const_rand_upper.setStatusTip( 'The random upper bound for social accelerate constant.') self.v_max = QDoubleSpinBox() self.v_max.setRange(0.5, 100) self.v_max.setValue(5) self.v_max.setSingleStep(1) self.v_max.setStatusTip('The maximum of velocity for each individual.') self.nneuron = QSpinBox() self.nneuron.setRange(1, 100) self.nneuron.setValue(6) self.nneuron.setStatusTip('The number of RBFN neuron.') self.sd_max = QDoubleSpinBox() self.sd_max.setRange(0.01, 20) self.sd_max.setValue(10) self.sd_max.setSingleStep(0.1) self.sd_max.setStatusTip('The random range maximum of standard ' 'deviation of each neuron in RBFN (only for ' 'initialization).') inner_layout.addRow('Iterating Times:', self.iter_times) inner_layout.addRow('Population Size:', self.population_size) inner_layout.addRow('Inertia Weight:', self.inertia_weight) inner_layout.addRow('Cognitive Const Upper:', self.cognitive_const_rand_upper) inner_layout.addRow('Social Const Upper:', self.social_const_rand_upper) inner_layout.addRow('Maximum of Velocity:', self.v_max) inner_layout.addRow('Number of Neuron:', self.nneuron) inner_layout.addRow('Maximum of SD:', self.sd_max) self._layout.addWidget(group_box) def __set_outputs_ui(self): group_box = QGroupBox('Training Details') inner_layout = QFormLayout() group_box.setLayout(inner_layout) self.current_iter_time = QLabel('--') self.current_error = QLabel('--') self.avg_error = QLabel('--') self.global_best_error = QLabel('--') self.total_best_error = QLabel('--') self.progressbar = QProgressBar() self.current_iter_time.setAlignment(Qt.AlignCenter) self.current_error.setAlignment(Qt.AlignCenter) self.avg_error.setAlignment(Qt.AlignCenter) self.global_best_error.setAlignment(Qt.AlignCenter) self.total_best_error.setAlignment(Qt.AlignCenter) self.current_iter_time.setStatusTip('The current iterating time of ' 'the PSO.') self.current_error.setStatusTip('The current error from the fitting ' 'function. ("( )": normalized error)') self.avg_error.setStatusTip('The average error from the fitting ' 'function in current iteration. ("( )": ' 'normalized error)') self.global_best_error.setStatusTip( 'The error of global best individual from the fitting function in ' 'current iteration. ("( )": normalized error)') self.total_best_error.setStatusTip( 'The error of total best individual from the fitting function in ' 'training. ("( )": normalized error)') inner_layout.addRow('Current Iterating Time:', self.current_iter_time) inner_layout.addRow('Current Error:', self.current_error) inner_layout.addRow('Average Error:', self.avg_error) inner_layout.addRow('Global Best Error:', self.global_best_error) inner_layout.addRow('Total Best Error:', self.total_best_error) inner_layout.addRow(self.progressbar) self._layout.addWidget(group_box) def __set_graphic_ui(self): group_box = QGroupBox('Error Line Charts:') inner_layout = QVBoxLayout() group_box.setLayout(inner_layout) self.err_chart = ErrorLineChart(1) self.err_chart.setStatusTip('The history of error from the fitting ' 'of the PSO for each data.') self.__err_x = 1 self.iter_err_chart = ErrorLineChart( 3, ('Avg', 'Global Best', 'Total Best')) self.iter_err_chart.setStatusTip('The history of average and least ' 'error from the fitting of the PSO ' 'for each iteration.') self.iter_err_chart.setMinimumHeight(150) inner_layout.addWidget(QLabel('Current Error')) inner_layout.addWidget(self.err_chart) inner_layout.addWidget(QLabel('Average Error')) inner_layout.addWidget(self.iter_err_chart) self._layout.addWidget(group_box) @Slot() def __init_widgets(self): self.start_btn.setDisabled(True) self.stop_btn.setEnabled(True) self.multicore_cb.setDisabled(True) self.data_selector.setDisabled(True) self.iter_times.setDisabled(True) self.population_size.setDisabled(True) self.inertia_weight.setDisabled(True) self.cognitive_const_rand_upper.setDisabled(True) self.social_const_rand_upper.setDisabled(True) self.v_max.setDisabled(True) self.nneuron.setDisabled(True) self.sd_max.setDisabled(True) self.err_chart.clear() self.iter_err_chart.clear() self.__err_x = 1 @Slot() def __reset_widgets(self): self.start_btn.setEnabled(True) self.stop_btn.setDisabled(True) self.multicore_cb.setEnabled(True) self.data_selector.setEnabled(True) self.iter_times.setEnabled(True) self.population_size.setEnabled(True) self.inertia_weight.setEnabled(True) self.cognitive_const_rand_upper.setEnabled(True) self.social_const_rand_upper.setEnabled(True) self.v_max.setEnabled(True) self.nneuron.setEnabled(True) self.sd_max.setEnabled(True) self.progressbar.setMinimum(0) self.progressbar.setMaximum(100) @Slot() def __indicate_busy(self): self.progressbar.setMinimum(0) self.progressbar.setMaximum(0) @Slot(int) def __show_current_iter_time(self, value): self.current_iter_time.setText(str(value + 1)) self.progressbar.setValue(value + 1) @Slot(float) def __show_current_error(self, value): self.current_error.setText('{:.5f} ({:.5f})'.format(value, value / 40)) self.err_chart.append_point(self.__err_x, value) self.__err_x += 1 @Slot(float, float, float) def __show_iter_error(self, avg, glob, total): self.avg_error.setText('{:.5f} ({:.5f})'.format(avg, avg / 40)) self.global_best_error.setText( '{:.5f} ({:.5f})'.format(glob, glob / 40)) self.total_best_error.setText( '{:.5f} ({:.5f})'.format(total, total / 40)) self.iter_err_chart.append_point( int(self.current_iter_time.text()), total, 2) self.iter_err_chart.append_point( int(self.current_iter_time.text()), glob, 1) self.iter_err_chart.append_point( int(self.current_iter_time.text()), avg, 0) def __run(self): self.progressbar.setMaximum(self.iter_times.value()) self.__current_dataset = self.datasets[ self.data_selector.currentText()] self.__pso = PSO(self.iter_times.value(), self.population_size.value(), self.inertia_weight.value(), self.cognitive_const_rand_upper.value(), self.social_const_rand_upper.value(), self.v_max.value(), self.nneuron.value(), self.__current_dataset, self.sd_max.value(), is_multicore=self.multicore_cb.isChecked()) self.threads.append(self.__pso) self.stop_btn.clicked.connect(self.__pso.stop) self.__pso.started.connect(self.__init_widgets) self.__pso.finished.connect(self.__reset_widgets) self.__pso.sig_current_iter_time.connect(self.__show_current_iter_time) self.__pso.sig_current_error.connect(self.__show_current_error) self.__pso.sig_iter_error.connect(self.__show_iter_error) self.__pso.sig_indicate_busy.connect(self.__indicate_busy) self.__pso.sig_console.connect(self.testing_panel.print_console) self.__pso.sig_rbfn.connect(self.testing_panel.load_rbfn) self.__pso.start()
class MinMaxWidget(ToolWidget): def __init__(self, image, parent=None): super(MinMaxWidget, self).__init__(parent) self.chan_combo = QComboBox() self.chan_combo.addItems( [self.tr("Luminance"), self.tr("Red"), self.tr("Green"), self.tr("Blue"), self.tr("RGB Norm")] ) colors = [self.tr("Red"), self.tr("Green"), self.tr("Blue"), self.tr("White"), self.tr("Black")] self.process_button = QPushButton(self.tr("Process")) self.min_combo = QComboBox() self.min_combo.addItems(colors) self.min_combo.setCurrentIndex(1) self.max_combo = QComboBox() self.max_combo.addItems(colors) self.max_combo.setCurrentIndex(0) self.filter_spin = QSpinBox() self.filter_spin.setRange(0, 5) self.filter_spin.setSpecialValueText(self.tr("Off")) self.image = image self.viewer = ImageViewer(self.image, self.image) self.low = self.high = None self.stopped = False self.change() self.process_button.clicked.connect(self.preprocess) self.chan_combo.currentIndexChanged.connect(self.change) self.min_combo.currentIndexChanged.connect(self.process) self.max_combo.currentIndexChanged.connect(self.process) self.filter_spin.valueChanged.connect(self.process) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr("Channel:"))) top_layout.addWidget(self.chan_combo) top_layout.addWidget(self.process_button) top_layout.addWidget(QLabel(self.tr("Minimum:"))) top_layout.addWidget(self.min_combo) top_layout.addWidget(QLabel(self.tr("Maximum:"))) top_layout.addWidget(self.max_combo) top_layout.addWidget(QLabel(self.tr("Filter:"))) top_layout.addWidget(self.filter_spin) top_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) @staticmethod def minmax_dev(patch, mask): c = patch[1, 1] minimum, maximum, _, _ = cv.minMaxLoc(patch, mask) if c < minimum: return -1 if c > maximum: return +1 return 0 @staticmethod def blk_filter(img, radius): result = np.zeros_like(img, np.float32) rows, cols = result.shape block = 2 * radius + 1 for i in range(radius, rows, block): for j in range(radius, cols, block): result[i - radius : i + radius + 1, j - radius : j + radius + 1] = np.std( img[i - radius : i + radius + 1, j - radius : j + radius + 1] ) return cv.normalize(result, None, 0, 127, cv.NORM_MINMAX, cv.CV_8UC1) def change(self): self.min_combo.setEnabled(False) self.max_combo.setEnabled(False) self.filter_spin.setEnabled(False) self.process_button.setEnabled(True) self.viewer.update_processed(self.image) def preprocess(self): start = time() channel = self.chan_combo.currentIndex() if channel == 0: img = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) elif channel == 4: b, g, r = cv.split(self.image.astype(np.float64)) img = cv.sqrt(cv.pow(b, 2) + cv.pow(g, 2) + cv.pow(r, 2)) else: img = self.image[:, :, 3 - channel] kernel = 3 border = kernel // 2 shape = (img.shape[0] - kernel + 1, img.shape[1] - kernel + 1, kernel, kernel) strides = 2 * img.strides patches = np.lib.stride_tricks.as_strided(img, shape=shape, strides=strides) patches = patches.reshape((-1, kernel, kernel)) mask = np.full((kernel, kernel), 255, dtype=np.uint8) mask[border, border] = 0 progress = QProgressDialog( self.tr("Computing deviation..."), self.tr("Cancel"), 0, shape[0] * shape[1] - 1, self ) progress.canceled.connect(self.cancel) progress.setWindowModality(Qt.WindowModal) blocks = [0] * shape[0] * shape[1] for i, patch in enumerate(patches): blocks[i] = self.minmax_dev(patch, mask) progress.setValue(i) if self.stopped: self.stopped = False return output = np.array(blocks).reshape(shape[:-2]) output = cv.copyMakeBorder(output, border, border, border, border, cv.BORDER_CONSTANT) self.low = output == -1 self.high = output == +1 self.min_combo.setEnabled(True) self.max_combo.setEnabled(True) self.filter_spin.setEnabled(True) self.process_button.setEnabled(False) self.process() self.info_message.emit(self.tr(f"Min/Max Deviation = {elapsed_time(start)}")) def cancel(self): self.stopped = True def process(self): minmax = np.zeros_like(self.image) minimum = self.min_combo.currentIndex() maximum = self.max_combo.currentIndex() radius = self.filter_spin.value() if radius > 0: start = time() radius += 3 if minimum < 4: low = self.blk_filter(self.low, radius) if minimum <= 2: minmax[:, :, 2 - minimum] = low else: minmax = np.repeat(low[:, :, np.newaxis], 3, axis=2) if maximum < 4: high = self.blk_filter(self.high, radius) if maximum <= 2: minmax[:, :, 2 - maximum] += high else: minmax += np.repeat(high[:, :, np.newaxis], 3, axis=2) minmax = norm_mat(minmax) self.info_message.emit(self.tr(f"Min/Max Filter = {elapsed_time(start)}")) else: if minimum == 0: minmax[self.low] = [0, 0, 255] elif minimum == 1: minmax[self.low] = [0, 255, 0] elif minimum == 2: minmax[self.low] = [255, 0, 0] elif minimum == 3: minmax[self.low] = [255, 255, 255] if maximum == 0: minmax[self.high] = [0, 0, 255] elif maximum == 1: minmax[self.high] = [0, 255, 0] elif maximum == 2: minmax[self.high] = [255, 0, 0] elif maximum == 3: minmax[self.high] = [255, 255, 255] self.viewer.update_processed(minmax)
class EmptyVolumeUI(QSplitter): def __init__(self, *args, **kwargs): super(EmptyVolumeUI, self).__init__(*args, **kwargs) self.visible = QGraphicsOpacityEffect(self) self.visible.setOpacity(1.0) self.disvisible = QGraphicsOpacityEffect(self) self.disvisible.setOpacity(0.0) main_layout = QHBoxLayout() self.variety_tree = VarietyTree(self) main_layout.addWidget(self.variety_tree) self.right_widget = QWidget(self) right_layout = QVBoxLayout() right_layout.setContentsMargins(QMargins(1, 1, 1, 1)) opts_layout = QHBoxLayout() # 选择分析的目标数据类别 self.radio_button_group = QButtonGroup(self) radio_button_1 = QRadioButton("行情统计", self) radio_button_1.setChecked(True) self.radio_button_group.addButton(radio_button_1) radio_button_2 = QRadioButton("排名持仓", self) self.radio_button_group.addButton(radio_button_2) opts_layout.addWidget(radio_button_1) opts_layout.addWidget(radio_button_2) self.rank_spinbox = QSpinBox(self) self.rank_spinbox.setPrefix("前 ") self.rank_spinbox.setSuffix(" 名") self.rank_spinbox.setRange(1, 20) self.rank_spinbox.setValue(20) self.rank_spinbox.setEnabled(False) opts_layout.addWidget(self.rank_spinbox) # 分割线 vertical_line = QFrame(self) vertical_line.setFrameShape(QFrame.VLine) opts_layout.addWidget(vertical_line) opts_layout.addWidget(QLabel("选择合约:", self)) self.contract_combobox = QComboBox(self) opts_layout.addWidget(self.contract_combobox) self.confirm_button = QPushButton("确定", self) opts_layout.addWidget(self.confirm_button) self.tip_button = QPushButton('左侧选择品种后进行查询 ', self) # 提示文字 opts_layout.addWidget(self.tip_button) self.tip_button.setGraphicsEffect(self.disvisible) opts_layout.addStretch() right_layout.addLayout(opts_layout) self.web_container = QWebEngineView(self) right_layout.addWidget(self.web_container) self.right_widget.setLayout(right_layout) main_layout.addWidget(self.right_widget) self.setStretchFactor(1, 2) self.setStretchFactor(2, 8) self.setHandleWidth(1) self.contract_combobox.setMinimumWidth(80) self.setLayout(main_layout) self.tip_button.setObjectName("tipButton") self.setStyleSheet( "#tipButton{border:none;color:rgb(230,50,50);font-weight:bold;}")
class InputsLayout(QFormLayout): # this signal is connected to print_output from output_layout class. Connection is done in center_layout ga_result = Signal( str ) # a signal that is emitted so it can transfer resulting string to the output_layout class def __init__(self): super(InputsLayout, self).__init__() self.big_font = QFont() self.medium_font = QFont() self.header = QLabel() self.header_general = QLabel() self.header_fitness_remapping = QLabel() self.header_stop = QLabel() self.header_selection = QLabel() self.header_pairing = QLabel() self.header_crossover = QLabel() self.header_mutation = QLabel() self.inp_functions_combo = QComboBox() self.inp_num_variables = QSpinBox() self.inp_extrema_min = QRadioButton("Minimum") self.inp_extrema_max = QRadioButton("Maximum") self.inp_pop_size = QSpinBox() self.inp_lower_bound = QDoubleSpinBox() self.inp_upper_bound = QDoubleSpinBox() # Stopping self.inp_max_iter = QSpinBox() self.inp_similarity_cb = QCheckBox() self.inp_similarity = QSpinBox() self.inp_best_result_cb = QCheckBox() self.inp_best_result = QDoubleSpinBox() self.inp_average_result_cb = QCheckBox() self.inp_average_result = QDoubleSpinBox() # Fitness remapping self.inp_fitness_remapping = QComboBox() # Selection self.inp_selection_method = QComboBox() self.inp_elitism = QDoubleSpinBox() # Pairing self.inp_pairing_method = QComboBox() # Crossover self.inp_crossover_method = QComboBox() self.inp_crossover_fraction = QDoubleSpinBox() self.intermediate_offset = QDoubleSpinBox() # Mutation self.inp_mutation_method = QComboBox() self.inp_mutation_intensity = QDoubleSpinBox() self.inp_mutation_intensity_final = QDoubleSpinBox() self.init_fonts() self.init_header() self.init_row_functions() self.init_row_general() self.init_row_fitness_remapping() self.init_row_stop() self.init_row_selection() self.init_row_pairing() self.init_row_crossover() self.init_row_mutation() def init_fonts(self): self.big_font.setPointSizeF(14) self.medium_font.setPointSizeF(12) def init_header(self): self.header.setFont(self.big_font) self.header.setAlignment(Qt.AlignCenter) self.header.setText("Genetic Algorithm Continuous Optimization") self.addRow(self.header) self.addRow(QHLine()) def init_row_functions(self): self.inp_functions_combo.addItem("Ackley", ackley) self.inp_functions_combo.addItem("Griewank", griewank) self.inp_functions_combo.addItem("Michalewicz", michalewicz) self.inp_extrema_min.setChecked(True) radio_box = QHBoxLayout() radio_box.addWidget(self.inp_extrema_min) radio_box.addWidget(self.inp_extrema_max) self.addRow("Function:", self.inp_functions_combo) self.inp_num_variables.setMaximum(10000) self.inp_num_variables.setValue(10) self.addRow("Number of variables:", self.inp_num_variables) self.addRow("Find:", radio_box) self.addRow(QHLine()) def init_row_general(self): self.header_general.setFont(self.medium_font) self.header_general.setText("General") self.inp_pop_size.setMaximum(10000) self.inp_pop_size.setValue(300) self.inp_lower_bound.setMaximum(1000000) self.inp_lower_bound.setMinimum(-1000000.0) self.inp_lower_bound.setValue(-10) self.inp_upper_bound.setMaximum(1000000) self.inp_upper_bound.setMinimum(-1000000.0) self.inp_upper_bound.setValue(10) self.addRow(self.header_general) self.addRow("Population size", self.inp_pop_size) self.addRow("Lower Bound", self.inp_lower_bound) self.addRow("Upper Bound", self.inp_upper_bound) self.addRow(QHLine()) def init_row_fitness_remapping(self): self.header_fitness_remapping.setFont(self.medium_font) self.header_fitness_remapping.setText("Fitness Remapping") self.inp_fitness_remapping.addItem("Rank Scaling", "Rank Scaling") self.inp_fitness_remapping.addItem("Fitness Scaling", "Fitness Scaling") self.addRow(self.header_fitness_remapping) self.addRow("Fitness remapping", self.inp_fitness_remapping) self.addRow(QHLine()) def init_row_stop(self): self.header_stop.setFont(self.medium_font) self.header_stop.setText("Stopping Criteria") self.inp_max_iter.setMaximum(100000) self.inp_similarity.setMaximum(100000) self.inp_best_result.setMinimum(-100000) self.inp_best_result.setMaximum(100000) self.inp_average_result.setMinimum(-100000) self.inp_average_result.setMaximum(100000) self.inp_max_iter.setValue(500) self.inp_similarity.setValue(80) self.inp_best_result.setValue(-10) self.inp_average_result.setValue(-10000) self.inp_similarity_cb.setText("Similar Results") self.inp_best_result_cb.setText("Best Result") self.inp_average_result_cb.setText("Average Result") self.inp_similarity_cb.stateChanged.connect(self.cb_similarity_signal) self.inp_best_result_cb.stateChanged.connect( self.cb_best_result_signal) self.inp_average_result_cb.stateChanged.connect( self.cb_average_result_signal) self.inp_similarity_cb.setChecked(False) self.inp_best_result_cb.setChecked(False) self.inp_average_result_cb.setChecked(False) self.inp_similarity.setEnabled(True) self.inp_best_result.setEnabled(False) self.inp_best_result.setStyleSheet("background:#555") self.inp_average_result.setEnabled(False) self.inp_average_result.setStyleSheet("background:#555") self.addRow(self.header_stop) self.addRow("Max iter", self.inp_max_iter) self.addRow(self.inp_similarity_cb, self.inp_similarity) self.addRow(self.inp_best_result_cb, self.inp_best_result) self.addRow(self.inp_average_result_cb, self.inp_average_result) self.addRow(QHLine()) def init_row_selection(self): self.header_selection.setFont(self.medium_font) self.header_selection.setText("Selection") self.inp_selection_method.addItem("Fittest Half", "Fittest Half") self.inp_selection_method.addItem("Roulette Wheel", "Roulette Wheel") self.inp_selection_method.addItem("Random", "Random") self.inp_selection_method.addItem("Whole Population", "Whole Population") self.inp_elitism.setMaximum(1) self.inp_elitism.setValue(0.01) self.inp_elitism.setSingleStep(0.01) self.addRow(self.header_selection) self.addRow("Selection Method", self.inp_selection_method) self.addRow("Elitism Percentage", self.inp_elitism) self.addRow(QHLine()) def init_row_pairing(self): self.header_pairing.setFont(self.medium_font) self.header_pairing.setText("Pairing") self.inp_pairing_method.addItem("Random", "Random") self.inp_pairing_method.addItem("Roulette Wheel", "Roulette Wheel") self.inp_pairing_method.addItem("Fittest", "Fittest") self.addRow(self.header_pairing) self.addRow("Pairing Method", self.inp_pairing_method) self.addRow(QHLine()) def init_row_crossover(self): self.header_crossover.setFont(self.medium_font) self.header_crossover.setText("Crossover") self.inp_crossover_method.addItem("Intermediate", "Intermediate") self.inp_crossover_method.addItem("Line Intermediate", "Line Intermediate") self.inp_crossover_method.addItem("Heuristic", "Heuristic") self.inp_crossover_method.addItem("One point", "One point") self.inp_crossover_method.addItem("Two point", "Two point") self.inp_crossover_method.addItem("Random", "Random") self.inp_mutation_method.setCurrentIndex(2) self.inp_crossover_fraction.setMaximum(1) self.inp_crossover_fraction.setValue(0.7) self.inp_crossover_fraction.setSingleStep(0.05) self.intermediate_offset.setMaximum(20) self.intermediate_offset.setValue(1.55) self.intermediate_offset.setSingleStep(0.05) self.addRow(self.header_crossover) self.addRow("Crossover Method", self.inp_crossover_method) self.addRow("Crossover Fraction", self.inp_crossover_fraction) self.addRow("Intermediate Offset", self.intermediate_offset) self.addRow(QHLine()) def init_row_mutation(self): self.header_mutation.setFont(self.medium_font) self.header_mutation.setText("Mutation") self.inp_mutation_method.addItem("Gauss", "Gauss") self.inp_mutation_method.addItem("Random", "Random") self.inp_mutation_intensity.setMaximum(200) self.inp_mutation_intensity.setValue(2) self.inp_mutation_intensity.setDecimals(4) self.inp_mutation_intensity.setSingleStep(0.01) self.inp_mutation_intensity_final.setMaximum(200) self.inp_mutation_intensity_final.setDecimals(4) self.inp_mutation_intensity_final.setValue(0.001) self.inp_mutation_intensity_final.setSingleStep(0.5) self.addRow(self.header_mutation) self.addRow("Mutation Method", self.inp_mutation_method) self.addRow("Mutation Intensity", self.inp_mutation_intensity) self.addRow("Final Mutation Intensity", self.inp_mutation_intensity_final) self.addRow(QHLine()) def get_options(self): function = self.inp_functions_combo.currentData() num_var = self.inp_num_variables.text() if self.inp_extrema_min.isChecked(): extrem = 0 else: extrem = 1 pop_size = self.inp_pop_size.text() low_bound = self.inp_lower_bound.text() upp_bound = self.inp_upper_bound.text() max_iter = self.inp_max_iter.text() sim_results = self.inp_similarity.text() best_res = self.inp_best_result.text() average_res = self.inp_average_result.text() select_method = self.inp_selection_method.currentText() elite_percent = self.inp_elitism.text() pairing = self.inp_pairing_method.currentText() crossover_method = self.inp_crossover_method.currentText() crossover_fraction = self.inp_crossover_fraction.text() intermediate_offset = self.intermediate_offset.text() mutation_method = self.inp_mutation_method.currentText() mutation_intensity = self.inp_mutation_intensity.text() mutation_intensity_final = self.inp_mutation_intensity_final.text() fitness_remapping = self.inp_fitness_remapping.currentText() options = { "function": function, "num_var": num_var, "pop_size": int(pop_size), "max_iter": int(max_iter), "lower_bound": float(low_bound.replace(",", ".")), "upper_bound": float(upp_bound.replace(",", ".")), "find_max": extrem, "prints": 0, "average_result": float(average_res.replace(",", ".")), "best_result": float(best_res.replace(",", ".")), "similarity": float(sim_results.replace(",", ".")), "selection": select_method, "pairing": pairing, "crossover": crossover_method, "crossover_fraction": float(crossover_fraction.replace(",", ".")), "intermediate_offset": float(intermediate_offset.replace(",", ".")), # 0 mean child will be between parents, 1 mean offset is same as two parent distance "mutation": mutation_method, "mutation_intensity": float(mutation_intensity.replace(",", ".")), "mutation_intensity_final": float(mutation_intensity_final.replace(",", ".")), "elitism": float(elite_percent.replace(",", ".")), "fitness_remapping": fitness_remapping } if not self.inp_similarity_cb.isChecked(): options["similarity"] = None if not self.inp_best_result_cb.isChecked(): options["best_result"] = None if not self.inp_average_result_cb.isChecked(): options["average_result"] = None return options def cb_similarity_signal(self): if self.inp_similarity_cb.isChecked(): self.inp_similarity.setEnabled(True) self.inp_similarity.setStyleSheet("") else: self.inp_similarity.setEnabled(False) self.inp_similarity.setStyleSheet("background:#555") def cb_best_result_signal(self): if self.inp_best_result_cb.isChecked(): self.inp_best_result.setEnabled(True) self.inp_best_result.setStyleSheet("") else: self.inp_best_result.setEnabled(False) self.inp_best_result.setStyleSheet("background:#555") def cb_average_result_signal(self): if self.inp_average_result_cb.isChecked(): self.inp_average_result.setEnabled(True) self.inp_average_result.setStyleSheet("") else: self.inp_average_result.setEnabled(False) self.inp_average_result.setStyleSheet("background:#555")
class mainWindow(QObject): signalRun = Signal(SpiderThread) signalRunApi = Signal(apiTester) def __init__(self): QObject.__init__(self) # must init parent QObject,if you want to use signal self.widget = QWidget() self.ipLabel = QLabel(self.widget) self.thread = QThread() self.worker = Worker() self.worker.moveToThread(self.thread) self.man = SpiderThread() self.api = apiTester() # 1 2:loop 3: time self.testStatus = [False,1,0,"ip","mac"] # advance config self.findCoreStop = True self.cleanCacheSet = False self.useApiTest = False self.showTestProgress = True self.saveTestLog = False self.saveLogPath = "" self.chromePath = "" self.showChrome = False self.coreDumpPath = "" # ui form self.passwordLabel = QLabel(self.widget) self.ipLineEdit = QLineEdit(self.widget) self.passwordLineEdit = QLineEdit(self.widget) self.startBtn = QPushButton(self.widget) self.stopBtn = QPushButton(self.widget) self.messageBox = QTextEdit(self.widget) self.messageBox.setReadOnly(True) self.userLabel = QLabel(self.widget) self.userLineEdit = QLineEdit(self.widget) self.intervalLabel = QLabel(self.widget) self.intervalSpinBox = QSpinBox(self.widget) self.loopLabel = QLabel(self.widget) self.loopSpinBox = QSpinBox(self.widget) self.intervalSpinBox.setRange(0,9999) self.loopSpinBox.setRange(1,9999) self.radioReboot = QRadioButton(self.widget) self.radioProvision = QRadioButton(self.widget) self.radioFactory = QRadioButton(self.widget) # self.apiCheckBox = QCheckBox(self.widget) self.menu = QMenu() self.gxpAction = QAction("Classic UI") self.grp2602Action = QAction("Ant Design UI") self.gxpAction.setCheckable(True) self.grp2602Action.setCheckable(True) self.menu.addAction(self.gxpAction) self.menu.addAction(self.grp2602Action) self.webLabel = QLabel(self.widget) self.webBtn = QPushButton(self.widget) self.webBtn.setMenu(self.menu) self.clearBtn = QPushButton(self.widget) self.messageList = deque() self.timer = QTimer() self.advanceBtn = QPushButton(self.widget) self.infoLabel = QLabel(self.widget) # provision widget self.provWidget = QWidget(self.widget) self.ver1Label = QLabel(self.provWidget) self.ver2Label = QLabel(self.provWidget) self.ver3Label = QLabel(self.provWidget) self.ver1LineEdit = QLineEdit(self.provWidget) self.ver2LineEdit = QLineEdit(self.provWidget) self.ver3LineEdit = QLineEdit(self.provWidget) self.dir1Label = QLabel(self.provWidget) self.dir2Label = QLabel(self.provWidget) self.dir3Label = QLabel(self.provWidget) self.dir1LineEdit = QLineEdit(self.provWidget) self.dir2LineEdit = QLineEdit(self.provWidget) self.dir3LineEdit = QLineEdit(self.provWidget) self.radioHttp = QRadioButton(self.provWidget) self.radioHttps = QRadioButton(self.provWidget) self.radioTftp = QRadioButton(self.provWidget) self.radioFtp = QRadioButton(self.provWidget) self.radioFtps = QRadioButton(self.provWidget) self.radioWindow = QRadioButton(self.provWidget) # advance widget self.advanceWidget = QWidget() self.checkCoreBox = QCheckBox(self.advanceWidget) self.cleanCache = QCheckBox(self.advanceWidget) self.checkSaveLogBox = QCheckBox(self.advanceWidget) self.selectDirBtn = QPushButton(self.advanceWidget) self.saveDirLabel = QLabel(self.advanceWidget) self.saveDirLabel.setStyleSheet("background:white") self.checkProgressBox = QCheckBox(self.advanceWidget) self.advanceOkBtn = QPushButton(self.advanceWidget) self.selectChromeBtn = QPushButton(self.advanceWidget) self.chromePathLabel = QLabel(self.advanceWidget) self.chromePathLabel.setStyleSheet("background:white") self.checkShowChromeBox = QCheckBox(self.advanceWidget) self.chromeLabel = QLabel(self.advanceWidget) self.pcapLabel = QLabel(self.advanceWidget) self.apiCheckBox = QCheckBox(self.advanceWidget) self.corePathLabel = QLabel(self.advanceWidget) self.corePathLabel.setStyleSheet("background:white") self.corePathBtn = QPushButton(self.advanceWidget) self.interfaceMenu = QComboBox(self.advanceWidget) self.interfaceMenu.addItem('Default') self.aiOptionBox= QCheckBox(self.advanceWidget) a = IFACES print(a) for i in a.keys(): print(a[i].description) self.interfaceMenu.addItem(a[i].description) # connect singal and slot self.startBtn.clicked.connect(self.clickedStarBtn) self.radioProvision.clicked.connect(self.clickedProvision) self.radioReboot.clicked.connect(self.clickedOthers) self.radioFactory.clicked.connect(self.clickedOthers) self.stopBtn.clicked.connect(self.clickedStopBtn) self.grp2602Action.triggered.connect(self.clickedGrp2602) self.gxpAction.triggered.connect(self.clickedGxpType) self.timer.timeout.connect(self.updateMessage) self.apiCheckBox.stateChanged.connect(self.apiTestBoxCheck) self.clearBtn.clicked.connect(self.clickedClearBtn) self.advanceBtn.clicked.connect(self.clickedAdvanceBtn) self.advanceOkBtn.clicked.connect(self.clickedAdvanceOkBtn) self.checkSaveLogBox.stateChanged.connect(self.saveLogBoxCheck) self.selectChromeBtn.clicked.connect(self.clickedSelectChromeBtn) self.selectDirBtn.clicked.connect(self.clickedSelectDirBtn) self.corePathBtn.clicked.connect(self.clickedCorePathBtn) self.worker.signalJobEnd.connect(self.slotTestStoped) self.worker.apiTestFinished.connect(self.slotTestStoped) self.signalRun.connect(self.worker.dowork) self.signalRunApi.connect(self.worker.doworkApi) # self.man.signalUpdateMessage.connect(self.pushMessage) def setupUI(self): # set text content /value self.widget.setWindowTitle("自动重启升降级测试工具") self.ipLabel.setText("初始IP:") self.passwordLabel.setText("密码:") self.startBtn.setText("开始测试") self.stopBtn.setText("停止测试") self.userLabel.setText("用户名:") self.intervalLabel.setText("间隔时间:") self.loopLabel.setText("测试次数:") self.intervalSpinBox.setValue(130) self.radioFactory.setText("恢复出厂") self.radioProvision.setText("升降级") self.radioReboot.setText("重启") self.ver1Label.setText("版本 1:") self.ver2Label.setText("版本 2:") self.ver3Label.setText("版本 3:") self.dir1Label.setText("路径 1:") self.dir2Label.setText("路径 2:") self.dir3Label.setText("路径 3:") self.radioHttp.setText("Http") self.radioHttps.setText("Https") self.radioTftp.setText("Tftp") self.radioFtp.setText("Ftp") self.radioFtps.setText("Ftps") self.apiCheckBox.setText("使用API测试,配置CoreDump下载路径") self.webLabel.setText("网页类型:") self.webBtn.setText("请选择UI类型") self.clearBtn.setText("清空输入") self.advanceWidget.setWindowTitle("高级设置") self.advanceBtn.setText("高级设置") self.checkCoreBox.setText("发现Core Dump时停止测试") self.cleanCache.setText("清除页面cache") self.checkSaveLogBox.setText("保存测试日志") self.selectDirBtn.setText("浏览") self.checkProgressBox.setText("显示底部状态条") self.advanceOkBtn.setText("OK") self.checkShowChromeBox.setText("测试时显示Chrome浏览器") self.selectChromeBtn.setText("浏览") self.chromeLabel.setText("Chrome浏览器路径") self.infoLabel.setText("未开始测试") self.pcapLabel.setText("Net Interface") self.corePathBtn.setText("浏览") self.radioWindow.setText("网页拖拽文件") # self.aiOptionBox.setText("AI") #init value self.saveDirLabel.hide() self.selectDirBtn.hide() self.gxpAction.setChecked(True) self.radioReboot.click() self.radioHttp.click() self.stopBtn.setEnabled(False) self.passwordLineEdit.setEchoMode(QLineEdit.PasswordEchoOnEdit) # set position------------------------------- xPos = 20 yPos = 30 colum2 = xPos +200 # line 1 self.ipLabel.move(xPos,yPos) self.intervalLabel.move(colum2,yPos) self.intervalSpinBox.move(colum2+60,yPos-2) self.ipLineEdit.move(xPos+50,yPos-2) # line 2 line2 = yPos +40 self.passwordLabel.move(xPos,line2) self.passwordLineEdit.move(xPos+50,line2-2) self.loopLabel.move(colum2,line2) self.loopSpinBox.move(colum2+60,line2-2) # line3 line3 = yPos +80 self.userLabel.move(xPos,line3) self.userLineEdit.move(xPos+50,line3-2) self.radioReboot.move(colum2,line3) self.radioFactory.move(colum2+60,line3) self.radioProvision.move(colum2,line3+30) self.webLabel.move(xPos,line3+40) self.webBtn.move(xPos+60,line3+35) # provWidget self.provWidget.resize(400,130) self.provWidget.move(xPos,line3+70) spaceY = 30 x = 0 y = 0 cl = 200 self.ver1Label.move(x,y) self.ver1LineEdit.move(x+50,y) self.ver2Label.move(x,y+spaceY) self.ver2LineEdit.move(x+50,y+spaceY) self.ver3Label.move(x,y+spaceY*2) self.ver3LineEdit.move(x+50,y+spaceY*2) self.dir1Label.move(cl,y) self.dir1LineEdit.move(cl+50,y) self.dir2Label.move(cl,y+spaceY) self.dir2LineEdit.move(cl+50,y+spaceY) self.dir3Label.move(cl,y+spaceY*2) self.dir3LineEdit.move(cl+50,y+spaceY*2) self.radioHttp.move(x,y+spaceY*3) self.radioHttps.move(x+50,y+spaceY*3) self.radioTftp.move(x+110,y+spaceY*3) self.radioFtp.move(x+160,y+spaceY*3) self.radioFtps.move(x+210,y+spaceY*3) self.radioWindow.move(x+265,y+spaceY*3) # advance widget self.advanceWidget.resize(300,400) x = 20 y = 20 space = 30 self.checkCoreBox.move(x,y) self.cleanCache.move(x,y+space) self.checkProgressBox.move(x,y+space*2) self.checkShowChromeBox.move(x,y+space*3) self.apiCheckBox.move(x,y+space*4) self.corePathBtn.move(x-2,y+space*5-8) self.corePathLabel.move(x+35,y+space*5-8) y += 40 self.chromeLabel.move(x,y+space*4+10) self.selectChromeBtn.move(x-2,y+space*5) self.chromePathLabel.move(x+35,y+space*5+2) self.checkSaveLogBox.move(x,y+space*6+10) self.selectDirBtn.move(x-2,y+space*7) self.saveDirLabel.move(x+35,y+space*7+2) self.advanceOkBtn.move(x+170,y+space*10+10) self.interfaceMenu.move(x-5,y+space*8+10) self.pcapLabel.move(x,y+space*8-2) # self.aiOptionBox.move(x, y+space*9+8) # set size self.messageBox.resize(373,155) # self.widget.resize(410,400) self.loopSpinBox.resize(60,25) self.intervalSpinBox.resize(60,25) self.webBtn.resize(100,25) self.saveDirLabel.resize(185,22) self.selectDirBtn.resize(32,24) self.selectChromeBtn.resize(32,24) self.chromePathLabel.resize(185,22) self.infoLabel.resize(400, 25) self.corePathBtn.resize(32,24) self.corePathLabel.resize(185,22) # self.provWidget.hide() self.changePosition(True) self.widget.show() self.loadCache() # ----------------end of setupUI --------------------- def changePosition(self,hide): xPos = 20 if hide: buttonLine = 200 self.widget.resize(420,415) else: buttonLine = 310 self.widget.resize(420,524) self.startBtn.move(xPos,buttonLine) self.stopBtn.move(xPos+90,buttonLine) self.clearBtn.move(xPos+180,buttonLine) self.advanceBtn.move(xPos+270,buttonLine) self.messageBox.move(xPos,buttonLine+30) boxH = self.messageBox.height() self.infoLabel.move(xPos,buttonLine+boxH+35) def setItemEnable(self,enable): self.provWidget.setEnabled(enable) self.ipLineEdit.setEnabled(enable) self.passwordLineEdit.setEnabled(enable) self.userLineEdit.setEnabled(enable) self.intervalSpinBox.setEnabled(enable) self.loopSpinBox.setEnabled(enable) self.radioFactory.setEnabled(enable) self.radioReboot.setEnabled(enable) self.radioProvision.setEnabled(enable) self.advanceBtn.setEnabled(enable) self.startBtn.setEnabled(enable) self.clearBtn.setEnabled(enable) if self.useApiTest: self.webBtn.setEnabled(False) else: self.webBtn.setEnabled(enable) self.stopBtn.setEnabled(not enable) def outputError(self,str): appstr = "<span style=\"color:red\">" appstr += str + "</span>" self.messageBox.append(appstr) def outputWarning(self,str): appstr = "<span style=\"color:orange\">" appstr += str + "</span>" self.messageBox.append(appstr) def loadCache(self): file = QFile("cache") if not file.open(QIODevice.ReadOnly | QIODevice.Text): return inStream = QTextStream(file) # ip self.ipLineEdit.setText(inStream.readLine()) # passwordLabel self.passwordLineEdit.setText(inStream.readLine()) # user self.userLineEdit.setText(inStream.readLine()) # ver1 self.ver1LineEdit.setText(inStream.readLine()) self.dir1LineEdit.setText(inStream.readLine()) # ver2 self.ver2LineEdit.setText(inStream.readLine()) self.dir2LineEdit.setText(inStream.readLine()) # ver3 self.ver3LineEdit.setText(inStream.readLine()) self.dir3LineEdit.setText(inStream.readLine()) self.intervalSpinBox.setValue(int(inStream.readLine())) self.loopSpinBox.setValue(int(inStream.readLine())) # web type button webType = inStream.readLine() if webType == "gxpAction": self.grp2602Action.setChecked(False) self.gxpAction.setChecked(True) self.webBtn.setText(self.gxpAction.text()) else: self.grp2602Action.setChecked(True) self.gxpAction.setChecked(False) self.webBtn.setText(self.grp2602Action.text()) testType = inStream.readLine() if testType == "reboot": self.radioReboot.setChecked(True) elif testType == "provision": self.radioProvision.setChecked(True) self.changePosition(False) self.provWidget.show() else: self.radioFactory.setChecked(True) serverType = inStream.readLine() if serverType == "Http": self.radioHttp.setChecked(True) elif serverType == "Https": self.radioHttps.setChecked(True) elif serverType == "Tftp": self.radioTftp.setChecked(True) elif serverType == "Ftp": self.radioFtp.setChecked(True) elif serverType == "Ftps": self.radioFtps.setChecked(True) else: self.radioWindow.setChecked(True) if inStream.readLine() == "True": self.findCoreStop = True else: self.findCoreStop = False if inStream.readLine() == "True": self.cleanCacheSet = True else: self.cleanCacheSet = False if inStream.readLine() == "True": self.useApiTest = True self.webBtn.setEnabled(False) else: self.useApiTest = False self.corePathBtn.hide() self.corePathLabel.hide() if inStream.readLine() == "True": self.showTestProgress = True else: self.showTestProgress = False self.infoLabel.hide() if inStream.readLine() == "True": self.showChrome = True else: self.showChrome = False self.chromePath = inStream.readLine() if inStream.readLine() == "True": self.saveTestLog = True else: self.saveTestLog = False self.saveLogPath = inStream.readLine() self.coreDumpPath = inStream.readLine() file.close() def saveCache(self): file = QFile("cache") if not file.open(QIODevice.WriteOnly): return content = self.ipLineEdit.text() + "\n" content += self.passwordLineEdit.text() + "\n" content += self.userLineEdit.text() + "\n" content += self.ver1LineEdit.text() + "\n" content += self.dir1LineEdit.text() + "\n" content += self.ver2LineEdit.text() + "\n" content += self.dir2LineEdit.text() + "\n" content += self.ver3LineEdit.text() + "\n" content += self.dir3LineEdit.text() + "\n" content += str(self.intervalSpinBox.value()) + "\n" content += str(self.loopSpinBox.value()) + "\n" if self.gxpAction.isChecked(): content += "gxpAction\n" else: content += "grp2602Action\n" if self.radioReboot.isChecked(): content += "reboot\n" elif self.radioProvision.isChecked(): content += "provision\n" else: content += "factory\n" if self.radioHttp.isChecked(): content += "Http\n" elif self.radioHttps.isChecked(): content += "Https\n" elif self.radioTftp.isChecked(): content += "Tftp\n" elif self.radioFtp.isChecked(): content += "Ftp\n" elif self.radioFtps.isChecked(): content += "Ftps\n" else : content += "Window\n" content += str(self.findCoreStop) + "\n" content += str(self.cleanCacheSet) + "\n" content += str(self.useApiTest) + "\n" content += str(self.showTestProgress) + "\n" content += str(self.showChrome) + "\n" content += self.chromePath +"\n" content += str(self.saveTestLog) +"\n" content += self.saveLogPath +"\n" content += self.coreDumpPath +"\n" byteArr = bytes(content,"utf-8") file.write(QByteArray(byteArr)) file.close() def checkBeforeRun(self): containError = False #---------check Ip address-------------- if self.ipLineEdit.text() == "": containError = True self.outputError("IP地址不能为空!") else: pattern = re.compile("^((1[0-9][0-9]\.)|(2[0-4][0-9]\.)|(25[0-5]\.)|([1-9][0-9]\.)|([0-9]\.)){3}((1[0-9][0-9])|(2[0-4][0-9])|(25[0-5])|([1-9][0-9])|([0-9]))$") if not pattern.search(self.ipLineEdit.text()): containError = True self.outputError("IP地址格式错误,检查是否含有多余空格!(仅支持IPV4)") #------------------------ if self.passwordLineEdit.text() == "": containError = True self.outputError("密码不能为空!") if self.userLineEdit.text() == "": containError = True self.outputError("用户名不能为空!") if self.intervalSpinBox.value() <= 40: self.outputWarning("间隔时间过短,可能对测试造成影响") if not self.radioProvision.isChecked() and not self.radioReboot.isChecked() and \ not self.radioFactory.isChecked(): containError = True self.outputError("必须选择测试方式(重启,升降级,恢复出厂)") # check provision ---------- if self.radioProvision.isChecked(): if self.ver1LineEdit.text() == "" or self.ver2LineEdit.text() == "" or \ self.dir1LineEdit.text() == "" or self.dir2LineEdit.text() == "": containError = True self.outputError("升降级测试至少填上前两个版本及其路径") bin_name = "" if os.path.exists(os.path.abspath("config.ini") ): f = open(os.path.abspath("config.ini") , "r") line = f.readline() while line: option = line.split("=") if option[0] == "firmbinname": if option[1].strip('"') != "": filenamebin = option[1].strip() bin_name = filenamebin.strip('"') pass line = f.readline() f.close() filename = os.path.join(self.dir1LineEdit.text(), bin_name) if not os.path.exists(filename) and self.radioWindow.isChecked(): containError = True self.outputError("firmware1 文件不存在!" + filename) filename = os.path.join(self.dir2LineEdit.text(), bin_name) if not os.path.exists(filename) and self.radioWindow.isChecked(): containError = True self.outputError("firmware2 文件不存在!" + filename) filename = os.path.join(self.dir3LineEdit.text(), bin_name) if self.ver3LineEdit.text() != "" and self.dir3LineEdit.text() == "": containError = True self.outputError("填写了版本3,但对应路径为空!") if self.dir3LineEdit.text() != "" and self.ver3LineEdit.text() == "": containError = True self.outputError("填写了路径3,但对应版本为空!") elif self.dir3LineEdit.text() != "" and self.radioWindow.isChecked() and not os.path.exists(filename): containError = True self.outputError("firmware3 文件不存在!" + filename) if not self.radioFtp.isChecked() and not self.radioFtps.isChecked() and \ not self.radioHttp.isChecked() and not self.radioHttps.isChecked() and \ not self.radioTftp.isChecked() and not self.radioWindow.isChecked(): containError = True self.outputError("升降级测试必须选择服务器类型(Tftp,Ftp,Ftps,Http,Https)") return containError def startTest(self): ip = self.ipLineEdit.text() passwd = self.passwordLineEdit.text() username = self.userLineEdit.text() ptime = self.intervalSpinBox.value() loop = self.loopSpinBox.value() modelType = self.webBtn.text() if self.gxpAction.isChecked(): device_type = "GXP21XX" elif self.grp2602Action.isChecked(): device_type = "GRP260X" if self.radioReboot.isChecked(): task_type = "reboot" elif self.radioProvision.isChecked(): task_type = "provision" text_ver1 = self.ver1LineEdit.text() text_ver2 = self.ver2LineEdit.text() text_ver3 = self.ver3LineEdit.text() text_dir1 = self.dir1LineEdit.text() text_dir2 = self.dir2LineEdit.text() text_dir3 = self.dir3LineEdit.text() prov_dict = {"ver1": text_ver1.strip(), "dir1": text_dir1.strip(), "ver2": text_ver2.strip(), "dir2": text_dir2.strip(), "ver3": text_ver3.strip(), "dir3": text_dir3.strip()} if self.radioHttp.isChecked(): self.man.update_prov_setting("HTTP", prov_dict) elif self.radioHttps.isChecked(): self.man.update_prov_setting("HTTPS", prov_dict) elif self.radioTftp.isChecked(): self.man.update_prov_setting("TFTP", prov_dict) elif self.radioFtp.isChecked(): self.man.update_prov_setting("FTP", prov_dict) elif self.radioFtps.isChecked(): self.man.update_prov_setting("FTPS", prov_dict) elif self.radioWindow.isChecked(): self.man.update_prov_setting("browser", prov_dict) else: task_type = "reset" coredump_stop = True headless_flag = False clean_cache = False if self.checkCoreBox.isChecked() == False: self.messageBox.append("Find core dump will not stop") coredump_stop = False if self.cleanCache.isChecked() == True: clean_cache = True if self.checkShowChromeBox.isChecked() == True or self.radioWindow.isChecked() == True: headless_flag = False else: headless_flag = True # ai_mode = False # if self.aiOptionBox.isChecked() == True: # ai_mode = True browser_path = "" if self.chromePathLabel.text() != "": browser_path = self.chromePathLabel.text() self.testStatus = [True,1,0,"ip",""] print(self.interfaceMenu.currentText()) self.man.setStatus(self.testStatus) self.man.setMessageList(self.messageList) self.man.update_setting(ip.strip(), username.strip(), passwd.strip(), device_type, \ task_type, loop, ptime, coredump_stop, headless_flag, browser_path, \ clean_cache, self.interfaceMenu.currentText(), False) def startApiTest(self): ip = self.ipLineEdit.text() passwd = self.passwordLineEdit.text() username = self.userLineEdit.text() ptime = self.intervalSpinBox.value() loop = self.loopSpinBox.value() testType = "Reboot" if self.radioProvision.isChecked(): testType = "Provision" self.api.setValue(ip,username,passwd,ptime,loop,testType) v1 = self.ver1LineEdit.text() v2 = self.ver2LineEdit.text() v3 = self.ver3LineEdit.text() d1 = self.dir1LineEdit.text() d2 = self.dir2LineEdit.text() d3 = self.dir3LineEdit.text() self.api.setVersion(v1,v2,v3,d1,d2,d3) self.api.setTestStatus(self.testStatus,self.messageList,self.coreDumpPath) if self.radioHttp.isChecked(): self.api.setServerType("http") elif self.radioHttps.isChecked(): self.api.setServerType("https") elif self.radioTftp.isChecked(): self.api.setServerType("tftp") elif self.radioFtp.isChecked(): self.api.setServerType("ftp") else: #self.radioFtps.isChecked() self.api.setServerType("ftps") self.api.setFoundCoreStop(self.findCoreStop) # slot --------------------------------- def apiTestBoxCheck(self,state): if state == 2: self.corePathBtn.show() self.corePathLabel.show() else: self.corePathBtn.hide() self.corePathLabel.hide() def clickedCorePathBtn(self): dir = QFileDialog.getExistingDirectory(self.advanceWidget,"选择Core Dump存放路径","/home") if dir != "": self.corePathLabel.setText(dir) self.coreDumpPath = dir def clickedSelectDirBtn(self): dir = QFileDialog.getExistingDirectory(self.advanceWidget,"选择日志存放路径","/home") if dir != "": self.saveDirLabel.setText(dir) self.saveLogPath = dir def clickedSelectChromeBtn(self): fileName = QFileDialog.getOpenFileName(self.advanceWidget,"选择谷歌浏览器","/home","Chrome (*.exe)") if fileName != "": self.chromePathLabel.setText(fileName[0]) self.chromePath = fileName[0] def saveLogBoxCheck(self,state): if state == 2: # checked self.selectDirBtn.show() self.saveDirLabel.show() else: self.selectDirBtn.hide() self.saveDirLabel.hide() def clickedAdvanceOkBtn(self): self.findCoreStop = self.checkCoreBox.isChecked() self.cleanCacheSet = self.cleanCache.isChecked() self.useApiTest = self.apiCheckBox.isChecked() self.showTestProgress = self.checkProgressBox.isChecked() self.saveTestLog = self.checkSaveLogBox.isChecked() self.saveLogPath = self.saveDirLabel.text() self.showChrome = self.checkShowChromeBox.isChecked() self.chromePath = self.chromePathLabel.text() self.coreDumpPath = self.corePathLabel.text() if self.useApiTest: self.webBtn.setEnabled(False) self.corePathBtn.show() self.corePathLabel.show() else: self.webBtn.setEnabled(True) self.corePathBtn.hide() self.corePathLabel.hide() if self.showTestProgress: self.infoLabel.show() else: self.infoLabel.hide() self.saveCache() self.advanceWidget.hide() def clickedAdvanceBtn(self): self.advanceWidget.hide() self.checkCoreBox.setChecked(self.findCoreStop) self.cleanCache.setChecked(self.cleanCacheSet) self.apiCheckBox.setChecked(self.useApiTest) self.checkProgressBox.setChecked(self.showTestProgress) self.checkSaveLogBox.setChecked(self.saveTestLog) self.saveDirLabel.setText(self.saveLogPath) self.checkShowChromeBox.setChecked(self.showChrome) self.chromePathLabel.setText(self.chromePath) self.corePathLabel.setText(self.coreDumpPath) self.advanceWidget.show() def slotTestStoped(self): self.testStatus[0] = False self.setItemEnable(True) self.timer.stop() self.updateMessage() self.thread.quit() # save Test log if self.saveTestLog: fileName = time.strftime("%Y_%m_%d.%H_%M_%S.",time.localtime()) if self.radioReboot.isChecked(): fileName += "reboot" elif self.radioProvision: fileName += "provision" else: fileName += "factoryReset" fileName += ".htm" if self.saveLogPath == "": self.outputWarning("日志地址没有设置,无法保存") else: fileName = self.saveLogPath + "\\" + fileName print(fileName) file = QFile(fileName) if not file.open(QIODevice.WriteOnly): self.outputError("打开文件错误,保存日志失败") return byteArr = bytes(self.messageBox.toHtml(),"utf-8") file.write(QByteArray(byteArr)) file.close() def clickedClearBtn(self): self.ipLineEdit.setText("") self.passwordLineEdit.setText("") self.userLineEdit.setText("") self.ver1LineEdit.setText("") self.dir1LineEdit.setText("") self.ver2LineEdit.setText("") self.dir2LineEdit.setText("") self.ver3LineEdit.setText("") self.dir3LineEdit.setText("") def clickedStarBtn(self): if self.checkBeforeRun(): return self.messageBox.clear() self.saveCache() self.messageBox.append("Init Setting...") self.setItemEnable(False) self.timer.start(500) self.thread.start() # deside use what to test if self.useApiTest: if self.radioFactory.isChecked(): self.outputWarning("Api not support Factory Reset, will test as Gxp type web driver") self.clickedGxpType() self.startTest() self.signalRun.emit(self.man) else: self.startApiTest() self.signalRunApi.emit(self.api) else: self.startTest() self.signalRun.emit(self.man) def clickedStopBtn(self): self.stopBtn.setEnabled(False) self.testStatus[0] = False self.man.quit() self.outputWarning("正在停止...") def clickedProvision(self): self.provWidget.show() self.changePosition(False) def clickedOthers(self): self.provWidget.hide() self.changePosition(True) def clickedGxpType(self): self.gxpAction.setChecked(True) self.grp2602Action.setChecked(False) self.webBtn.setText(self.gxpAction.text()) def clickedGrp2602(self): self.gxpAction.setChecked(False) self.grp2602Action.setChecked(True) self.webBtn.setText(self.grp2602Action.text()) def updateMessage(self): while len(self.messageList) >0: info = self.messageList.popleft() self.messageBox.append(info) if self.testStatus[0] == False: self.infoLabel.setText("未开始测试") else: info = "第" + str(self.testStatus[1]) + "次测试 " if self.testStatus[2] > 0: info += "等待:{} 秒".format( self.testStatus[2] ) else: info += "运行中" info += " " + self.testStatus[3] info += " " + self.testStatus[4] self.infoLabel.setText(info)
class EMMAResultChart(QDialog): N_DISPLAY_SAMPLES = 200 def __init__(self, parent=None, toolbar=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("EMMA Result Chart")) self.figure = plt.figure(figsize=(6, 8)) self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0) self.main_layout.addWidget(self.canvas, 1, 0) if not toolbar: self.toolbar.hide() self.supported_scales = [("log-linear", self.tr("Log-linear")), ("log", self.tr("Log")), ("phi", self.tr("φ")), ("linear", self.tr("Linear"))] self.control_group = QGroupBox(self.tr("Control")) self.control_group.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) self.control_layout = QGridLayout(self.control_group) self.main_layout.addWidget(self.control_group, 0, 1, 2, 1) self.scale_label = QLabel(self.tr("Scale")) self.scale_combo_box = QComboBox() self.scale_combo_box.addItems( [name for key, name in self.supported_scales]) self.scale_combo_box.currentIndexChanged.connect(self.update_chart) self.control_layout.addWidget(self.scale_label, 0, 0) self.control_layout.addWidget(self.scale_combo_box, 0, 1) self.supported_distances = ("1-norm", "2-norm", "3-norm", "4-norm", "MSE", "log10MSE", "cosine", "angular") self.distance_label = QLabel(self.tr("Distance")) self.distance_combo_box = QComboBox() self.distance_combo_box.addItems(self.supported_distances) self.distance_combo_box.setCurrentText("log10MSE") self.distance_combo_box.currentIndexChanged.connect(self.update_chart) self.control_layout.addWidget(self.distance_label, 1, 0) self.control_layout.addWidget(self.distance_combo_box, 1, 1) self.animated_checkbox = QCheckBox(self.tr("Animated")) self.animated_checkbox.setChecked(False) self.animated_checkbox.stateChanged.connect(self.on_animated_changed) self.control_layout.addWidget(self.animated_checkbox, 2, 0) self.interval_label = QLabel(self.tr("Interval [ms]")) self.interval_input = QSpinBox() self.interval_input.setRange(0, 10000) self.interval_input.setValue(30) self.interval_input.valueChanged.connect(self.update_chart) self.control_layout.addWidget(self.interval_label, 3, 0) self.control_layout.addWidget(self.interval_input, 3, 1) self.repeat_check_box = QCheckBox(self.tr("Repeat")) self.repeat_check_box.setChecked(False) self.repeat_check_box.stateChanged.connect(self.update_chart) self.save_button = QPushButton(self.tr("Save")) self.save_button.clicked.connect(self.save_animation) self.control_layout.addWidget(self.repeat_check_box, 4, 0) self.control_layout.addWidget(self.save_button, 4, 1) self.animation = None self.last_result = None self.normal_msg = QMessageBox(parent=self) self.file_dialog = QFileDialog(parent=self) self.on_animated_changed() @property def scale(self) -> str: index = self.scale_combo_box.currentIndex() key, name = self.supported_scales[index] return key @property def transfer(self) -> typing.Callable: if self.scale == "log-linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) elif self.scale == "log": return lambda classes_φ: np.log(convert_φ_to_μm(classes_φ)) elif self.scale == "phi": return lambda classes_φ: classes_φ elif self.scale == "linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) @property def xlabel(self) -> str: if self.scale == "log-linear": return self.tr("Grain-size [μm]") elif self.scale == "log": return self.tr("Ln(grain-size in μm)") elif self.scale == "phi": return self.tr("Grain-size [φ]") elif self.scale == "linear": return self.tr("Grain-size [μm]") @property def ylabel(self) -> str: return self.tr("Frequency") @property def xlog(self) -> bool: if self.scale == "log-linear": return True else: return False @property def distance(self) -> str: return self.distance_combo_box.currentText() @property def interval(self) -> float: return self.interval_input.value() @property def repeat(self) -> bool: return self.repeat_check_box.isChecked() def update_chart(self): if self.last_result is not None: self.show_result(self.last_result) def on_animated_changed(self): if self.animated_checkbox.isChecked(): enabled = True else: enabled = False self.interval_label.setEnabled(enabled) self.interval_input.setEnabled(enabled) self.repeat_check_box.setEnabled(enabled) self.save_button.setEnabled(enabled) self.update_chart() def show_final_result(self, result: EMMAResult): self.last_result = result self.figure.clear() if self.animation is not None: self.animation._stop() self.animation = None classes = self.transfer(result.dataset.classes_φ) sample_indexes = np.linspace(1, result.n_samples, result.n_samples) iteration_indexes = np.linspace(1, result.n_iterations, result.n_iterations) first_result = next(result.history) def get_valid(values): values = np.array(values) return values[~np.isinf(values) & ~np.isnan(values)] if self.distance == "cosine": min_class_wise_distance = np.max( get_valid(result.get_class_wise_distance_series( self.distance))) max_class_wise_distance = np.min( get_valid( first_result.get_class_wise_distance_series( self.distance))) min_sample_wise_distance = np.max( get_valid(result.get_sample_wise_distance_series( self.distance))) max_sample_wise_distance = np.min( get_valid( first_result.get_sample_wise_distance_series( self.distance))) else: min_class_wise_distance = np.min( get_valid(result.get_class_wise_distance_series( self.distance))) max_class_wise_distance = np.max( get_valid( first_result.get_class_wise_distance_series( self.distance))) min_sample_wise_distance = np.min( get_valid(result.get_sample_wise_distance_series( self.distance))) max_sample_wise_distance = np.max( get_valid( first_result.get_sample_wise_distance_series( self.distance))) d_class_wise_distance = max_class_wise_distance - min_class_wise_distance min_class_wise_distance -= d_class_wise_distance / 10 max_class_wise_distance += d_class_wise_distance / 10 d_sample_wise_distance = max_sample_wise_distance - min_sample_wise_distance min_sample_wise_distance -= d_sample_wise_distance / 10 max_sample_wise_distance += d_sample_wise_distance / 10 distance_history_axes = self.figure.add_subplot(3, 1, 1) distance_history_axes.plot(iteration_indexes, result.get_distance_series(self.distance)) distance_history_axes.set_xlim(iteration_indexes[0], iteration_indexes[-1]) distance_history_axes.set_xlabel(self.tr("Iteration index")) distance_history_axes.set_ylabel(self.tr("Distance")) distance_history_axes.set_title(self.tr("Distance history")) class_wise_distance_axes = self.figure.add_subplot(3, 2, 3) if self.xlog: class_wise_distance_axes.set_xscale("log") class_wise_distance_axes.plot( classes, result.get_class_wise_distance_series(self.distance)) class_wise_distance_axes.set_xlim(classes[0], classes[-1]) class_wise_distance_axes.set_ylim(min_class_wise_distance, max_class_wise_distance) class_wise_distance_axes.set_xlabel(self.xlabel) class_wise_distance_axes.set_ylabel(self.tr("Distance")) class_wise_distance_axes.set_title(self.tr("Class-wise distances")) sample_wise_distance_axes = self.figure.add_subplot(3, 2, 4) sample_wise_distance_axes.plot( sample_indexes, result.get_sample_wise_distance_series(self.distance)) sample_wise_distance_axes.set_xlim(sample_indexes[0], sample_indexes[-1]) sample_wise_distance_axes.set_ylim(min_sample_wise_distance, max_sample_wise_distance) sample_wise_distance_axes.set_xlabel(self.tr("Sample index")) sample_wise_distance_axes.set_ylabel(self.tr("Distance")) sample_wise_distance_axes.set_title(self.tr("Sample-wise distances")) # get the mode size of each end-members modes = [(i, result.dataset.classes_μm[np.unravel_index( np.argmax(result.end_members[i]), result.end_members[i].shape)]) for i in range(result.n_members)] # sort them by mode size modes.sort(key=lambda x: x[1]) end_member_axes = self.figure.add_subplot(3, 2, 5) if self.xlog: end_member_axes.set_xscale("log") for i_em, (index, _) in enumerate(modes): end_member_axes.plot(classes, result.end_members[index], c=plt.get_cmap()(i_em), label=self.tr("EM{0}").format(i_em + 1)) end_member_axes.set_xlim(classes[0], classes[-1]) end_member_axes.set_ylim(0.0, round(np.max(result.end_members) * 1.2, 2)) end_member_axes.set_xlabel(self.xlabel) end_member_axes.set_ylabel(self.ylabel) end_member_axes.set_title(self.tr("End-members")) if result.n_members < 6: end_member_axes.legend(loc="upper left") if result.n_samples > self.N_DISPLAY_SAMPLES: interval = result.n_samples // self.N_DISPLAY_SAMPLES else: interval = 1 proportion_axes = self.figure.add_subplot(3, 2, 6) bottom = np.zeros(result.n_samples) for i_em, (index, _) in enumerate(modes): proportion_axes.bar(sample_indexes[::interval], result.fractions[:, index][::interval], bottom=bottom[::interval], width=interval, color=plt.get_cmap()(i_em)) bottom += result.fractions[:, index] proportion_axes.set_xlim(sample_indexes[0], sample_indexes[-1]) proportion_axes.set_ylim(0.0, 1.0) proportion_axes.set_xlabel(self.tr("Sample index")) proportion_axes.set_ylabel(self.tr("Proportion")) proportion_axes.set_title(self.tr("EM abundances")) self.figure.tight_layout() self.canvas.draw() def show_history_animation(self, result: EMMAResult): self.last_result = result self.figure.clear() if self.animation is not None: self.animation._stop() self.animation = None classes = self.transfer(result.dataset.classes_φ) sample_indexes = np.linspace(1, result.n_samples, result.n_samples) iteration_indexes = np.linspace(1, result.n_iterations, result.n_iterations) distance_series = result.get_distance_series(self.distance) min_distance, max_distance = np.min(distance_series), np.max( distance_series) first_result = next(result.history) def get_valid(values): values = np.array(values) return values[~np.isinf(values) & ~np.isnan(values)] if self.distance == "cosine": min_class_wise_distance = np.max( get_valid(result.get_class_wise_distance_series( self.distance))) max_class_wise_distance = np.min( get_valid( first_result.get_class_wise_distance_series( self.distance))) min_sample_wise_distance = np.max( get_valid(result.get_sample_wise_distance_series( self.distance))) max_sample_wise_distance = np.min( get_valid( first_result.get_sample_wise_distance_series( self.distance))) else: min_class_wise_distance = np.min( get_valid(result.get_class_wise_distance_series( self.distance))) max_class_wise_distance = np.max( get_valid( first_result.get_class_wise_distance_series( self.distance))) min_sample_wise_distance = np.min( get_valid(result.get_sample_wise_distance_series( self.distance))) max_sample_wise_distance = np.max( get_valid( first_result.get_sample_wise_distance_series( self.distance))) d_class_wise_distance = max_class_wise_distance - min_class_wise_distance min_class_wise_distance -= d_class_wise_distance / 10 max_class_wise_distance += d_class_wise_distance / 10 d_sample_wise_distance = max_sample_wise_distance - min_sample_wise_distance min_sample_wise_distance -= d_sample_wise_distance / 10 max_sample_wise_distance += d_sample_wise_distance / 10 self.distance_history_axes = self.figure.add_subplot(3, 1, 1) self.distance_history_axes.plot(iteration_indexes, distance_series) self.distance_history_axes.set_xlim(iteration_indexes[0], iteration_indexes[-1]) self.distance_history_axes.set_xlabel(self.tr("Iteration index")) self.distance_history_axes.set_ylabel(self.tr("Distance")) self.distance_history_axes.set_title(self.tr("Distance history")) self.class_wise_distance_axes = self.figure.add_subplot(3, 2, 3) if self.xlog: self.class_wise_distance_axes.set_xscale("log") self.class_wise_distance_axes.set_xlim(classes[0], classes[-1]) self.class_wise_distance_axes.set_ylim(min_class_wise_distance, max_class_wise_distance) self.class_wise_distance_axes.set_xlabel(self.xlabel) self.class_wise_distance_axes.set_ylabel(self.tr("Distance")) self.class_wise_distance_axes.set_title( self.tr("Class-wise distances")) self.sample_wise_distance_axes = self.figure.add_subplot(3, 2, 4) self.sample_wise_distance_axes.set_xlim(sample_indexes[0], sample_indexes[-1]) self.sample_wise_distance_axes.set_ylim(min_sample_wise_distance, max_sample_wise_distance) self.sample_wise_distance_axes.set_xlabel(self.tr("Sample index")) self.sample_wise_distance_axes.set_ylabel(self.tr("Distance")) self.sample_wise_distance_axes.set_title( self.tr("Sample-wise distances")) # get the mode size of each end-members self.modes = [(i, result.dataset.classes_μm[np.unravel_index( np.argmax(result.end_members[i]), result.end_members[i].shape)]) for i in range(result.n_members)] # sort them by mode size self.modes.sort(key=lambda x: x[1]) self.end_member_axes = self.figure.add_subplot(3, 2, 5) if self.xlog: self.end_member_axes.set_xscale("log") self.end_member_axes.set_xlim(classes[0], classes[-1]) self.end_member_axes.set_ylim( 0.0, round(np.max(result.end_members) * 1.2, 2)) self.end_member_axes.set_xlabel(self.xlabel) self.end_member_axes.set_ylabel(self.ylabel) self.end_member_axes.set_title(self.tr("End-members")) if result.n_samples > self.N_DISPLAY_SAMPLES: interval = result.n_samples // self.N_DISPLAY_SAMPLES else: interval = 1 self.proportion_axes = self.figure.add_subplot(3, 2, 6) self.proportion_axes.set_xlim(sample_indexes[0] - 0.5, sample_indexes[-1] - 0.5) self.proportion_axes.set_ylim(0.0, 1.0) self.proportion_axes.set_xlabel(self.tr("Sample index")) self.proportion_axes.set_ylabel(self.tr("Proportion")) self.proportion_axes.set_title(self.tr("EM abundances")) # self.figure.tight_layout() # self.canvas.draw() def init(): self.iteration_position_line = self.distance_history_axes.plot( [1, 1], [min_distance, max_distance], c="black")[0] self.class_wise_distance_curve = self.class_wise_distance_axes.plot( classes, result.get_class_wise_distance_series(self.distance), c=plt.get_cmap()(0))[0] self.sample_wise_distance_curve = self.sample_wise_distance_axes.plot( sample_indexes, result.get_sample_wise_distance_series(self.distance), c=plt.get_cmap()(0))[0] self.end_member_curves = [] for i_em, (index, _) in enumerate(self.modes): end_member_curve = self.end_member_axes.plot( classes, result.end_members[index], c=plt.get_cmap()(i_em), label=self.tr("EM{0}").format(i_em + 1))[0] self.end_member_curves.append(end_member_curve) bottom = np.zeros(result.n_samples) self.fraction_bars = [] self.patches = [] for i_em, (index, _) in enumerate(self.modes): bar = self.proportion_axes.bar( sample_indexes[::interval], result.fractions[:, index][::interval], bottom=bottom[::interval], width=interval, color=plt.get_cmap()(i_em)) self.fraction_bars.append(bar) self.patches.extend(bar.patches) bottom += result.fractions[:, index] return self.iteration_position_line, self.class_wise_distance_curve, self.sample_wise_distance_curve, *( self.end_member_curves + self.patches) def animate(args): iteration, current = args self.iteration_position_line.set_xdata([iteration, iteration]) self.class_wise_distance_curve.set_ydata( current.get_class_wise_distance_series(self.distance)) self.sample_wise_distance_curve.set_ydata( current.get_sample_wise_distance_series(self.distance)) for i_em, (index, _) in enumerate(self.modes): self.end_member_curves[i_em].set_ydata( current.end_members[index]) bottom = np.zeros(current.n_samples) for i_em, (index, _) in enumerate(self.modes): for rect, height, y in zip( self.fraction_bars[i_em].patches, current.fractions[:, index][::interval], bottom[::interval]): rect.set_height(height) rect.set_y(y) bottom += current.fractions[:, index] return self.iteration_position_line, self.class_wise_distance_curve, self.sample_wise_distance_curve, *( self.end_member_curves + self.patches) self.animation = FuncAnimation(self.figure, animate, init_func=init, frames=enumerate(result.history), interval=self.interval, blit=True, repeat=self.repeat, repeat_delay=3.0, save_count=result.n_iterations) def show_result(self, result: EMMAResult): if self.animated_checkbox.isChecked(): self.show_history_animation(result) else: self.show_final_result(result) def save_animation(self): if self.last_result is not None: filename, format_str = self.file_dialog.getSaveFileName( self, self.tr("Save the animation of this EMMA result"), None, self. tr("MPEG-4 Video File (*.mp4);;Graphics Interchange Format (*.gif)" )) if filename is None or filename == "": return progress = QProgressDialog(self) progress.setRange(0, 100) progress.setLabelText( self.tr("Saving Animation [{0} Frames]").format( self.last_result.n_iterations)) canceled = False def save_callback(i, n): if progress.wasCanceled(): nonlocal canceled canceled = True raise StopIteration() progress.setValue((i + 1) / n * 100) QCoreApplication.processEvents() self.show_history_animation(self.last_result) # plt.rcParams["savefig.dpi"] = 120.0 if "*.gif" in format_str: if not ImageMagickWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText( self. tr("ImageMagick is not installed, please download and install it from its offical website (https://imagemagick.org/index.php)." )) self.normal_msg.exec_() else: self.animation.save(filename, writer="imagemagick", fps=30, progress_callback=save_callback) elif "*.mp4" in format_str: if not FFMpegWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText( self. tr("FFMpeg is not installed, please download and install it from its offical website (https://ffmpeg.org/)." )) self.normal_msg.exec_() else: self.animation.save(filename, writer="ffmpeg", fps=30, progress_callback=save_callback) # plt.rcParams["savefig.dpi"] = 300.0 if not canceled: progress.setValue(100)
class ControlFrame(QFrame): def __init__(self, dataset, display_panel, threads): super().__init__() if isinstance(display_panel, DisplayFrame): self.display_panel = display_panel else: raise TypeError("'display_panel' must be the instance of " "'DisplayFrame'") self.dataset = dataset self.threads = threads self.__layout = QVBoxLayout() self.setLayout(self.__layout) self.__layout.setContentsMargins(0, 0, 0, 0) self.__set_running_options_ui() self.__set_fuzzy_set_operation_types_ui() self.__set_fuzzy_variables_ui() self.__set_fuzzy_rules_ui() self.__set_console_ui() def __set_running_options_ui(self): group_box = QGroupBox("Running Options") inner_layout = QHBoxLayout() group_box.setLayout(inner_layout) self.data_selector = QComboBox() self.data_selector.addItems(list(self.dataset.keys())) self.data_selector.setStatusTip("Select the road map case.") self.data_selector.currentIndexChanged.connect(self.__change_map) self.fps = QSpinBox() self.fps.setMinimum(1) self.fps.setMaximum(60) self.fps.setValue(20) self.fps.setStatusTip( "The re-drawing rate for car simulator. High fps " "may cause the plot shows discontinuously.") self.start_btn = QPushButton("Run") self.start_btn.setStatusTip("Run the car.") self.start_btn.clicked.connect(self.__run) self.stop_btn = QPushButton("Stop") self.stop_btn.setStatusTip("Force the simulation stop running.") self.stop_btn.setDisabled(True) self.save_btn = QPushButton() self.save_btn.setIcon(QIcon(':/icons/save_icon.png')) self.save_btn.setStatusTip("Save every details for the last time " "running.") self.save_btn.clicked.connect(self.__save_results) self.save_btn.setDisabled(True) self.__change_map() inner_layout.addWidget(self.data_selector, 1) inner_layout.addWidget(QLabel("FPS:")) inner_layout.addWidget(self.fps) inner_layout.addWidget(self.start_btn) inner_layout.addWidget(self.stop_btn) inner_layout.addWidget(self.save_btn) self.__layout.addWidget(group_box) def __set_fuzzy_set_operation_types_ui(self): group_box = QGroupBox("Fuzzy Sets Operation Types") inner_layout = QFormLayout() group_box.setLayout(inner_layout) self.implication_selections = RadioButtonSet([ ("imp_dr", QRadioButton("Dienes-Rescher")), ("imp_l", QRadioButton("Lukasieweicz")), ("imp_z", QRadioButton("Zadel")), ("imp_g", QRadioButton("Godel")), ("imp_m", QRadioButton("Mamdani")), ("imp_p", QRadioButton("Product")) ]) self.combination_vars_selections = RadioButtonSet([ ("tn_min", QRadioButton("Minimum")), ("tn_ap", QRadioButton("Algebraic Product")), ("tn_bp", QRadioButton("Bounded Product")), ("tn_dp", QRadioButton("Drastic Product")) ]) self.combination_rules_selections = RadioButtonSet([ ("tc_max", QRadioButton("Maximum")), ("tc_as", QRadioButton("Algebraic Sum")), ("tc_bs", QRadioButton("Bounded Sum")), ("tc_ds", QRadioButton("Drastic Sum")) ]) self.defuzzifier_selections = RadioButtonSet([ ("gravity_center", QRadioButton("Center of Gravity")), ("maxima_mean", QRadioButton("Mean of Maxima")), ("modified_maxima_mean", QRadioButton("Modified Mean of Maxima")) ]) self.implication_selections.set_selected('imp_m') self.combination_vars_selections.set_selected('tn_min') self.combination_rules_selections.set_selected('tc_max') self.defuzzifier_selections.set_selected('gravity_center') self.implication_selections.setStatusTip("Choose the method for fuzzy " "implication.") self.combination_vars_selections.setStatusTip( "Choose the method of " "combination of multiple " "fuzzy variables.") self.combination_rules_selections.setStatusTip("Choose the method of " "combination of " "multiple fuzzy rules.") self.defuzzifier_selections.setStatusTip("Choose the method for the " "defuzifier.") inner_layout.addRow(QLabel("Implication:"), self.implication_selections) inner_layout.addRow(QLabel("Combination of Variables:"), self.combination_vars_selections) inner_layout.addRow(QLabel("Combination of Rules:"), self.combination_rules_selections) inner_layout.addRow(QLabel("Defuzzifier:"), self.defuzzifier_selections) self.__layout.addWidget(group_box) def __set_fuzzy_variables_ui(self): group_box = QGroupBox("Fuzzy Variables Settings") group_box.setStatusTip("Set the membership functions for each fuzzy " "variable.") inner_layout = QVBoxLayout() self.fuzzyvar_setting_stack = QStackedWidget() self.fuzzyvar_ui_selection = RadioButtonSet([ ("front", QRadioButton("Front Distance Radar")), ("lrdiff", QRadioButton("(Left-Right) Distance Radar")), ("consequence", QRadioButton("Consequence")) ]) self.fuzzyvar_setting_dist_front = FuzzierVarSetting() self.fuzzyvar_setting_dist_front.small.mean.setValue(5) self.fuzzyvar_setting_dist_front.medium.mean.setValue(12) self.fuzzyvar_setting_dist_front.large.mean.setValue(20) self.fuzzyvar_setting_dist_lrdiff = FuzzierVarSetting() self.fuzzyvar_setting_dist_lrdiff.small.mean.setValue(-10) self.fuzzyvar_setting_dist_lrdiff.medium.mean.setValue(0) self.fuzzyvar_setting_dist_lrdiff.large.mean.setValue(10) self.fuzzyvar_setting_consequence = FuzzierVarSetting() self.fuzzyvar_setting_consequence.small.mean.setValue(-12) self.fuzzyvar_setting_consequence.small.sd.setValue(20) self.fuzzyvar_setting_consequence.medium.mean.setValue(0) self.fuzzyvar_setting_consequence.medium.sd.setValue(20) self.fuzzyvar_setting_consequence.large.mean.setValue(12) self.fuzzyvar_setting_consequence.large.sd.setValue(20) inner_layout.addWidget(self.fuzzyvar_ui_selection) inner_layout.addWidget(self.fuzzyvar_setting_stack) group_box.setLayout(inner_layout) self.fuzzyvar_setting_stack.addWidget(self.fuzzyvar_setting_dist_front) self.fuzzyvar_setting_stack.addWidget( self.fuzzyvar_setting_dist_lrdiff) self.fuzzyvar_setting_stack.addWidget( self.fuzzyvar_setting_consequence) self.fuzzyvar_ui_selection.sig_rbtn_changed.connect( self.__change_fuzzyvar_setting_ui_stack) self.__layout.addWidget(group_box) def __set_fuzzy_rules_ui(self): antecedents = ('small', 'medium', 'large') group_box = QGroupBox("Fuzzy Rules Setting") inner_layout = QVBoxLayout() group_box.setStatusTip("Set the rules for the fuzzy system.") self.rules_setting = FuzzyRulesSetting( [p for p in itertools.product(antecedents, repeat=2)]) self.rules_setting.set_consequence_fuzzysets( ('large', 'small', 'small', 'large', 'small', 'small', 'large', 'small', 'small')) inner_layout.addWidget(self.rules_setting) group_box.setLayout(inner_layout) self.__layout.addWidget(group_box) def __set_console_ui(self): self.__console = QTextEdit() self.__console.setReadOnly(True) self.__console.setStatusTip("Show the logs of status changing.") self.__layout.addWidget(self.__console) @Slot(str) def __change_fuzzyvar_setting_ui_stack(self, name): if name == 'front': self.fuzzyvar_setting_stack.setCurrentIndex(0) elif name == 'lrdiff': self.fuzzyvar_setting_stack.setCurrentIndex(1) else: self.fuzzyvar_setting_stack.setCurrentIndex(2) @Slot() def __change_map(self): self.__current_data = self.dataset[self.data_selector.currentText()] self.__car = Car(self.__current_data['start_pos'], self.__current_data['start_angle'], 3, self.__current_data['route_edge']) self.display_panel.change_map(self.__current_data) @Slot(str) def __print_console(self, text): self.__console.append(text) @Slot(list) def __get_results(self, results): """Get the results of last running and draw the path of it.""" self.results = results self.display_panel.show_path([d['x'] for d in results], [d['y'] for d in results]) @Slot() def __save_results(self): save_dir = QFileDialog.getExistingDirectory(self, 'Select Saving Directory') file4d_filepath = os.path.join(save_dir, 'train4D.txt') file6d_filepath = os.path.join(save_dir, 'train6D.txt') with open(file4d_filepath, 'w') as file4d: for result in self.results: file4d.write('{:.7f} {:.7f} {:.7f} {:.7f}\n'.format( result['front_dist'], result['right_dist'], result['left_dist'], result['wheel_angle'])) with open(file6d_filepath, 'w') as file6d: for result in self.results: file6d.write( '{:.7f} {:.7f} {:.7f} {:.7f} {:.7f} {:.7f}\n'.format( result['x'], result['y'], result['front_dist'], result['right_dist'], result['left_dist'], result['wheel_angle'])) self.__print_console('Note: Detailed results have been saved in both' ' "%s" and "%s".' % (file4d_filepath, file6d_filepath)) @Slot() def __init_widgets(self): self.start_btn.setDisabled(True) self.stop_btn.setEnabled(True) self.save_btn.setDisabled(True) self.fps.setDisabled(True) self.data_selector.setDisabled(True) self.implication_selections.setDisabled(True) self.combination_vars_selections.setDisabled(True) self.combination_rules_selections.setDisabled(True) self.defuzzifier_selections.setDisabled(True) self.fuzzyvar_setting_dist_front.setDisabled(True) self.fuzzyvar_setting_dist_lrdiff.setDisabled(True) self.fuzzyvar_setting_consequence.setDisabled(True) self.rules_setting.setDisabled(True) @Slot() def __reset_widgets(self): self.start_btn.setEnabled(True) self.stop_btn.setDisabled(True) self.save_btn.setEnabled(True) self.fps.setEnabled(True) self.data_selector.setEnabled(True) self.implication_selections.setEnabled(True) self.combination_vars_selections.setEnabled(True) self.combination_rules_selections.setEnabled(True) self.defuzzifier_selections.setEnabled(True) self.fuzzyvar_setting_dist_front.setEnabled(True) self.fuzzyvar_setting_dist_lrdiff.setEnabled(True) self.fuzzyvar_setting_consequence.setEnabled(True) self.rules_setting.setEnabled(True) @Slot() def __run(self): # reset the map self.__change_map() # create a QThread self.thread = RunCar(self.__car, self.__create_fuzzy_system(), (self.__current_data['end_area_lt'], self.__current_data['end_area_rb']), self.fps.value()) # Record the new created threads for the closeEvent in gui_base.py # By doing this, user can destroy the QMainWindow elegantly when there # are threads still running. self.threads.append(self.thread) self.stop_btn.clicked.connect(self.thread.stop) self.thread.started.connect(self.__init_widgets) self.thread.finished.connect(self.__reset_widgets) self.thread.sig_console.connect(self.__print_console) self.thread.sig_car.connect(self.display_panel.move_car) self.thread.sig_car_collided.connect( self.display_panel.show_car_collided) self.thread.sig_dists.connect(self.display_panel.show_dists) self.thread.sig_results.connect(self.__get_results) self.thread.start() def __create_fuzzy_system(self): """Create a fuzzy system with the parameter given in control panel.""" dist_front = FuzzyVariable() dist_front.add_membershipf( 'small', get_gaussianf( *self.fuzzyvar_setting_dist_front.small.get_values())) dist_front.add_membershipf( 'medium', get_gaussianf( *self.fuzzyvar_setting_dist_front.medium.get_values())) dist_front.add_membershipf( 'large', get_gaussianf( *self.fuzzyvar_setting_dist_front.large.get_values())) dist_lrdiff = FuzzyVariable() dist_lrdiff.add_membershipf( 'small', get_gaussianf( *self.fuzzyvar_setting_dist_lrdiff.small.get_values())) dist_lrdiff.add_membershipf( 'medium', get_gaussianf( *self.fuzzyvar_setting_dist_lrdiff.medium.get_values())) dist_lrdiff.add_membershipf( 'large', get_gaussianf( *self.fuzzyvar_setting_dist_lrdiff.large.get_values())) consequence = FuzzyVariable() consequence.add_membershipf( 'small', get_gaussianf( *self.fuzzyvar_setting_consequence.small.get_values())) consequence.add_membershipf( 'medium', get_gaussianf( *self.fuzzyvar_setting_consequence.medium.get_values())) consequence.add_membershipf( 'large', get_gaussianf( *self.fuzzyvar_setting_consequence.large.get_values())) fuzzy_system = FuzzySystem(consequence, dist_front, dist_lrdiff) fuzzy_system.set_operation_types( self.implication_selections.get_selected_name(), self.combination_vars_selections.get_selected_name(), self.combination_rules_selections.get_selected_name(), self.defuzzifier_selections.get_selected_name()) for antecendent_names, consequence_name in self.rules_setting.rules.items( ): fuzzy_system.add_rule(consequence_name, antecendent_names) return fuzzy_system
class QTgoLayoutGroupRow(QWidget): group_template_changed = Signal() def __init__(self, force_group: ForceGroup, group: TgoLayoutGroup) -> None: super().__init__() self.grid_layout = QGridLayout() self.setLayout(self.grid_layout) self.grid_layout.setColumnStretch(0, 100) self.amount_selector = QSpinBox() self.unit_selector = QComboBox() self.unit_selector.setMinimumWidth(250) self.group_selector = QCheckBox() # Add all possible units with the price for unit_type in force_group.unit_types_for_group(group): self.unit_selector.addItem( f"{unit_type.name} [${unit_type.price}M]", userData=(unit_type.dcs_unit_type, unit_type.price), ) # Add all possible statics with price = 0 for static_type in force_group.statics_for_group(group): self.unit_selector.addItem(f"{static_type} (Static)", userData=(static_type, 0)) if self.unit_selector.count() == 0: raise LayoutException("No units available for the TgoLayoutGroup") self.unit_selector.adjustSize() self.unit_selector.setEnabled(self.unit_selector.count() > 1) self.grid_layout.addWidget(self.unit_selector, 0, 0, alignment=Qt.AlignRight) self.grid_layout.addWidget(self.amount_selector, 0, 1, alignment=Qt.AlignRight) dcs_unit_type, price = self.unit_selector.itemData( self.unit_selector.currentIndex()) self.group_layout = QTgoLayoutGroup(group, dcs_unit_type, group.group_size, price) self.group_selector.setChecked(self.group_layout.enabled) self.group_selector.setEnabled(self.group_layout.layout.optional) self.amount_selector.setMinimum(1) self.amount_selector.setMaximum(self.group_layout.layout.max_size) self.amount_selector.setValue(self.group_layout.amount) self.amount_selector.setEnabled(self.group_layout.layout.max_size > 1) self.grid_layout.addWidget(self.group_selector, 0, 2, alignment=Qt.AlignRight) self.amount_selector.valueChanged.connect(self.on_group_changed) self.unit_selector.currentIndexChanged.connect(self.on_group_changed) self.group_selector.stateChanged.connect(self.on_group_changed) def on_group_changed(self) -> None: self.group_layout.enabled = self.group_selector.isChecked() unit_type, price = self.unit_selector.itemData( self.unit_selector.currentIndex()) self.group_layout.dcs_unit_type = unit_type self.group_layout.unit_price = price self.group_layout.amount = self.amount_selector.value() self.group_template_changed.emit()
class PilotSettingsBox(QGroupBox): def __init__(self, game: Game) -> None: super().__init__("Pilots and Squadrons") self.game = game layout = QGridLayout() self.setLayout(layout) self.ai_pilot_levelling = QCheckBox() self.ai_pilot_levelling.setChecked(self.game.settings.ai_pilot_levelling) self.ai_pilot_levelling.toggled.connect(self.set_ai_pilot_leveling) ai_pilot_levelling_info = ( "Set whether or not AI pilots will level up after completing a number of" " sorties. Since pilot level affects the AI skill, you may wish to disable" " this, lest you face an Ace!" ) self.ai_pilot_levelling.setToolTip(ai_pilot_levelling_info) ai_pilot_levelling_label = QLabel("Allow AI pilot levelling") ai_pilot_levelling_label.setToolTip(ai_pilot_levelling_info) layout.addWidget(ai_pilot_levelling_label, 0, 0) layout.addWidget(self.ai_pilot_levelling, 0, 1, Qt.AlignRight) enable_squadron_pilot_limits_info = ( "If set, squadrons will be limited to a maximum number of pilots and dead " "pilots will replenish at a fixed rate, each defined with the settings" "below. Auto-purchase may buy aircraft for which there are no pilots" "available, so this feature is still a work-in-progress." ) enable_squadron_pilot_limits_label = QLabel( "Enable per-squadron pilot limtits (WIP)" ) enable_squadron_pilot_limits_label.setToolTip(enable_squadron_pilot_limits_info) enable_squadron_pilot_limits = QCheckBox() enable_squadron_pilot_limits.setToolTip(enable_squadron_pilot_limits_info) enable_squadron_pilot_limits.setChecked( self.game.settings.enable_squadron_pilot_limits ) enable_squadron_pilot_limits.toggled.connect( self.set_enable_squadron_pilot_limits ) layout.addWidget(enable_squadron_pilot_limits_label, 1, 0) layout.addWidget(enable_squadron_pilot_limits, 1, 1, Qt.AlignRight) self.pilot_limit = QSpinBox() self.pilot_limit.setMinimum(12) self.pilot_limit.setMaximum(72) self.pilot_limit.setValue(self.game.settings.squadron_pilot_limit) self.pilot_limit.setEnabled(self.game.settings.enable_squadron_pilot_limits) self.pilot_limit.valueChanged.connect(self.set_squadron_pilot_limit) pilot_limit_info = ( "Sets the maximum number of pilots a squadron may have active. " "Changing this value will not have an immediate effect, but will alter " "replenishment for future turns." ) self.pilot_limit.setToolTip(pilot_limit_info) pilot_limit_label = QLabel("Maximum number of pilots per squadron") pilot_limit_label.setToolTip(pilot_limit_info) layout.addWidget(pilot_limit_label, 2, 0) layout.addWidget(self.pilot_limit, 2, 1, Qt.AlignRight) self.squadron_replenishment_rate = QSpinBox() self.squadron_replenishment_rate.setMinimum(1) self.squadron_replenishment_rate.setMaximum(20) self.squadron_replenishment_rate.setValue( self.game.settings.squadron_replenishment_rate ) self.squadron_replenishment_rate.setEnabled( self.game.settings.enable_squadron_pilot_limits ) self.squadron_replenishment_rate.valueChanged.connect( self.set_squadron_replenishment_rate ) squadron_replenishment_rate_info = ( "Sets the maximum number of pilots that will be recruited to each squadron " "at the end of each turn. Squadrons will not recruit new pilots beyond the " "pilot limit, but each squadron with room for more pilots will recruit " "this many pilots each turn up to the limit." ) self.squadron_replenishment_rate.setToolTip(squadron_replenishment_rate_info) squadron_replenishment_rate_label = QLabel("Squadron pilot replenishment rate") squadron_replenishment_rate_label.setToolTip(squadron_replenishment_rate_info) layout.addWidget(squadron_replenishment_rate_label, 3, 0) layout.addWidget(self.squadron_replenishment_rate, 3, 1, Qt.AlignRight) def set_enable_squadron_pilot_limits(self, checked: bool) -> None: self.game.settings.enable_squadron_pilot_limits = checked self.pilot_limit.setEnabled(checked) self.squadron_replenishment_rate.setEnabled(checked) def set_squadron_pilot_limit(self, value: int) -> None: self.game.settings.squadron_pilot_limit = value def set_squadron_replenishment_rate(self, value: int) -> None: self.game.settings.squadron_replenishment_rate = value def set_ai_pilot_leveling(self, checked: bool) -> None: self.game.settings.ai_pilot_levelling = checked
class QGroundObjectMenu(QDialog): def __init__( self, parent, ground_object: TheaterGroundObject, cp: ControlPoint, game: Game, ): super().__init__(parent) self.setMinimumWidth(350) self.ground_object = ground_object self.cp = cp self.game = game self.setWindowTitle( f"Location - {self.ground_object.obj_name} ({self.cp.name})" ) self.setWindowIcon(EVENT_ICONS["capture"]) self.intelBox = QGroupBox("Units :") self.buildingBox = QGroupBox("Buildings :") self.orientationBox = QGroupBox("Orientation :") self.intelLayout = QGridLayout() self.buildingsLayout = QGridLayout() self.sell_all_button = None self.total_value = 0 self.init_ui() def init_ui(self): self.mainLayout = QVBoxLayout() self.budget = QBudgetBox(self.game) self.budget.setGame(self.game) self.doLayout() if isinstance(self.ground_object, BuildingGroundObject): self.mainLayout.addWidget(self.buildingBox) if self.cp.captured: self.mainLayout.addWidget(self.financesBox) else: self.mainLayout.addWidget(self.intelBox) self.mainLayout.addWidget(self.orientationBox) self.actionLayout = QHBoxLayout() self.sell_all_button = QPushButton("Disband (+" + str(self.total_value) + "M)") self.sell_all_button.clicked.connect(self.sell_all) self.sell_all_button.setProperty("style", "btn-danger") self.buy_replace = QPushButton("Buy/Replace") self.buy_replace.clicked.connect(self.buy_group) self.buy_replace.setProperty("style", "btn-success") if self.ground_object.purchasable: if self.total_value > 0: self.actionLayout.addWidget(self.sell_all_button) self.actionLayout.addWidget(self.buy_replace) if self.cp.captured and self.ground_object.purchasable: self.mainLayout.addLayout(self.actionLayout) self.setLayout(self.mainLayout) def doLayout(self): self.update_total_value() self.intelBox = QGroupBox("Units :") self.intelLayout = QGridLayout() i = 0 for g in self.ground_object.groups: for unit in g.units: self.intelLayout.addWidget( QLabel(f"<b>Unit {str(unit.display_name)}</b>"), i, 0 ) if not unit.alive and unit.repairable and self.cp.captured: price = unit.unit_type.price if unit.unit_type else 0 repair = QPushButton(f"Repair [{price}M]") repair.setProperty("style", "btn-success") repair.clicked.connect( lambda u=unit, p=price: self.repair_unit(u, p) ) self.intelLayout.addWidget(repair, i, 1) i += 1 stretch = QVBoxLayout() stretch.addStretch() self.intelLayout.addLayout(stretch, i, 0) self.buildingBox = QGroupBox("Buildings :") self.buildingsLayout = QGridLayout() j = 0 total_income = 0 received_income = 0 for static in self.ground_object.statics: if static not in FORTIFICATION_BUILDINGS: self.buildingsLayout.addWidget( QBuildingInfo(static, self.ground_object), j / 3, j % 3 ) j = j + 1 if self.ground_object.category in REWARDS.keys(): total_income += REWARDS[self.ground_object.category] if static.alive: received_income += REWARDS[self.ground_object.category] else: logging.warning(self.ground_object.category + " not in REWARDS") self.financesBox = QGroupBox("Finances: ") self.financesBoxLayout = QGridLayout() self.financesBoxLayout.addWidget( QLabel("Available: " + str(total_income) + "M"), 2, 1 ) self.financesBoxLayout.addWidget( QLabel("Receiving: " + str(received_income) + "M"), 2, 2 ) # Orientation Box self.orientationBox = QGroupBox("Orientation :") self.orientationBoxLayout = QHBoxLayout() heading_image = QLabel() heading_image.setPixmap( ICONS["heading"].transformed( QTransform().rotate(self.ground_object.heading.degrees) ) ) self.orientationBoxLayout.addWidget(heading_image) self.headingLabel = QLabel("Heading:") self.orientationBoxLayout.addWidget(self.headingLabel) self.headingSelector = QSpinBox() self.headingSelector.setEnabled(False) # Disable for now self.headingSelector.setMinimum(0) self.headingSelector.setMaximum(360) self.headingSelector.setValue(self.ground_object.heading.degrees) self.orientationBoxLayout.addWidget(self.headingSelector) if self.cp.captured: # TODO Let the user choose the heading with the SpinBox self.headingSelector.setEnabled(False) self.head_to_conflict_button = QPushButton("Head to conflict") heading = ( self.game.theater.heading_to_conflict_from(self.ground_object.position) or self.ground_object.heading ) self.head_to_conflict_button.clicked.connect( lambda: self.rotate_tgo(heading) ) self.orientationBoxLayout.addWidget(self.head_to_conflict_button) else: self.headingSelector.setEnabled(False) # Set the layouts self.financesBox.setLayout(self.financesBoxLayout) self.buildingBox.setLayout(self.buildingsLayout) self.intelBox.setLayout(self.intelLayout) self.orientationBox.setLayout(self.orientationBoxLayout) def do_refresh_layout(self): try: for i in reversed(range(self.mainLayout.count())): item = self.mainLayout.itemAt(i) if item is not None and item.widget() is not None: item.widget().setParent(None) self.sell_all_button.setParent(None) self.buy_replace.setParent(None) self.actionLayout.setParent(None) self.doLayout() if isinstance(self.ground_object, BuildingGroundObject): self.mainLayout.addWidget(self.buildingBox) else: self.mainLayout.addWidget(self.intelBox) self.mainLayout.addWidget(self.orientationBox) self.actionLayout = QHBoxLayout() if self.total_value > 0: self.actionLayout.addWidget(self.sell_all_button) self.actionLayout.addWidget(self.buy_replace) if self.cp.captured and self.ground_object.purchasable: self.mainLayout.addLayout(self.actionLayout) except Exception as e: logging.exception(e) self.update_total_value() def update_total_value(self): if not self.ground_object.purchasable: return self.total_value = self.ground_object.value if self.sell_all_button is not None: self.sell_all_button.setText("Disband (+$" + str(self.total_value) + "M)") def repair_unit(self, unit, price): if self.game.blue.budget > price: self.game.blue.budget -= price unit.alive = True GameUpdateSignal.get_instance().updateGame(self.game) # Remove destroyed units in the vicinity destroyed_units = self.game.get_destroyed_units() for d in destroyed_units: p = Point(d["x"], d["z"], self.game.theater.terrain) if p.distance_to_point(unit.position) < 15: destroyed_units.remove(d) logging.info("Removed destroyed units " + str(d)) logging.info(f"Repaired unit: {unit.unit_name}") self.update_game() def rotate_tgo(self, heading: Heading) -> None: self.ground_object.rotate(heading) self.do_refresh_layout() def sell_all(self): self.update_total_value() self.game.blue.budget += self.total_value self.ground_object.groups = [] self.update_game() def buy_group(self) -> None: self.subwindow = QGroundObjectBuyMenu( self, self.ground_object, self.game, self.total_value ) if self.subwindow.exec_(): self.update_game() def update_game(self) -> None: events = GameUpdateEvents() events.update_tgo(self.ground_object) if any( package.target == self.ground_object for package in self.game.ato_for(player=False).packages ): # Replan if the tgo was a target of the redfor self.game.initialize_turn(events, for_red=True, for_blue=False) EventStream.put_nowait(events) GameUpdateSignal.get_instance().updateGame(self.game) # Refresh the dialog self.do_refresh_layout()
class QFlightSlotEditor(QGroupBox): changed = Signal() def __init__(self, flight, game, planner): super(QFlightSlotEditor, self).__init__("Slots") self.flight = flight self.game = game self.planner = planner self.available = self.planner.get_available_aircraft() if self.flight.unit_type not in self.available: max = self.flight.count else: max = self.flight.count + self.available[self.flight.unit_type] if max > 4: max = 4 layout = QGridLayout() self.aircraft_count = QLabel("Aircraft count :") self.aircraft_count_spinner = QSpinBox() self.aircraft_count_spinner.setMinimum(1) self.aircraft_count_spinner.setMaximum(max) self.aircraft_count_spinner.setValue(flight.count) self.aircraft_count_spinner.valueChanged.connect( self._changed_aircraft_count) self.client_count = QLabel("Client slots count :") self.client_count_spinner = QSpinBox() self.client_count_spinner.setMinimum(0) self.client_count_spinner.setMaximum(max) self.client_count_spinner.setValue(flight.client_count) self.client_count_spinner.valueChanged.connect( self._changed_client_count) if not self.flight.unit_type.flyable: self.client_count_spinner.setValue(0) self.client_count_spinner.setEnabled(False) layout.addWidget(self.aircraft_count, 0, 0) layout.addWidget(self.aircraft_count_spinner, 0, 1) layout.addWidget(self.client_count, 1, 0) layout.addWidget(self.client_count_spinner, 1, 1) self.setLayout(layout) def _changed_aircraft_count(self): self.flight.count = int(self.aircraft_count_spinner.value()) self.changed.emit() # TODO check if enough aircraft are available def _changed_client_count(self): self.flight.client_count = int(self.client_count_spinner.value()) self._cap_client_count() self.changed.emit() def _cap_client_count(self): if self.flight.client_count > self.flight.count: self.flight.client_count = self.flight.count self.client_count_spinner.setValue(self.flight.client_count)
class GradientWidget(ToolWidget): def __init__(self, image, parent=None): super(GradientWidget, self).__init__(parent) self.levels_spin = QSpinBox() self.levels_spin.setRange(-1, 16) self.levels_spin.setSpecialValueText(self.tr('Auto')) self.levels_spin.setValue(-1) self.invert_check = QCheckBox(self.tr('Invert')) self.abs_check = QCheckBox(self.tr('Absolute')) self.grad_viewer = ImageViewer(image, image) self.image = image self.process() self.levels_spin.valueChanged.connect(self.process) self.invert_check.toggled.connect(self.process) self.abs_check.toggled.connect(self.process) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr('Levels:'))) top_layout.addWidget(self.levels_spin) top_layout.addWidget(self.invert_check) top_layout.addWidget(self.abs_check) top_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.grad_viewer) self.setLayout(main_layout) def process(self): intensity = self.levels_spin.value() invert = self.invert_check.isChecked() absolute = self.abs_check.isChecked() self.levels_spin.setEnabled(not absolute) self.invert_check.setEnabled(not absolute) gray = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) diff_x = cv.Scharr(gray, cv.CV_32F, 1, 0) diff_y = cv.Scharr(gray, cv.CV_32F, 0, 1) diff_z = cv.normalize(np.abs(diff_x + diff_y), None, 0, 255, cv.NORM_MINMAX) diff_z = cv.LUT(diff_z.astype(np.uint8), create_lut(0, 90)) grad_x = +diff_x if invert else -diff_x grad_y = +diff_y if invert else -diff_y grad_z = diff_z if not absolute: min_x, max_x, _, _ = cv.minMaxLoc(grad_x) lim_x = max(abs(min_x), abs(max_x)) grad_x = (grad_x / lim_x + 1) * 127 min_y, max_y, _, _ = cv.minMaxLoc(grad_y) lim_y = max(abs(min_y), abs(max_y)) grad_y = (grad_y / lim_y + 1) * 127 else: grad_x = cv.normalize(np.abs(grad_x), None, 0, 255, cv.NORM_MINMAX) grad_y = cv.normalize(np.abs(grad_y), None, 0, 255, cv.NORM_MINMAX) grad_x = grad_x.astype(np.uint8) grad_y = grad_y.astype(np.uint8) if intensity >= 0 and not absolute: max_intensity = self.levels_spin.maximum() low = 127 - (max_intensity - intensity) high = 127 + (max_intensity - intensity) lut_xy = create_lut(low, high) grad_x = cv.LUT(grad_x, lut_xy) grad_y = cv.LUT(grad_y, lut_xy) else: grad_x = cv.equalizeHist(grad_x) grad_y = cv.equalizeHist(grad_y) gradient = np.stack((grad_z, grad_x, grad_y), axis=2) self.grad_viewer.update_processed(gradient)
class MainWindow(QMainWindow): def __init__(self, vehicle_ctl: VehicleCtl, image_stream_server: ImageStreamServer): QMainWindow.__init__(self) self._vehicle_ctl = vehicle_ctl self._image_stream_server = image_stream_server # Our popup window for setting trim self._trim_window = None self.setMinimumSize(QSize(1000, 500)) self.setWindowTitle("Rotor Client") central_widget = QWidget() self.setCentralWidget(central_widget) # Need to allow the central widget to get focus so the key presses are caught correctly central_widget.setFocusPolicy(QtCore.Qt.ClickFocus) # Create all the buttons and widgets self.create_gui() # Create the image viewer self._image_viewer = ImageViewer(self._image_stream_server) # Connect all the push button signals self.add_gui_listeners() # Set the widgets in the grid layout self.add_widgets_to_screen(central_widget) # Give the central widget focus so the key presses work central_widget.setFocus() def game_controller_calibration_dialog(self): self._game_controller_config_window = GameControllerCalibrationDialog() self._game_controller_config_window.show() self._game_controller_config_window.calibration_complete_response = lambda calibration_data: self.game_controller_calibrated_from_window( calibration_data) def game_controller_calibrated_from_window(self, calibration_data): self._game_controller_config_window.close() config_handler = ConfigHandler.get_instance() config_handler.set_config_value('game_controller_calibration', calibration_data.to_json()) self.game_controller_calibrated(calibration_data) def game_controller_calibrated(self, calibration_data): logging.info("CONTROLLER WAS CALIBRATED! " + str(calibration_data.to_json())) self._game_controller = GameController(inputs.devices.gamepads[0], calibration_data) self._game_controller.add_event_response( 'ABS_HAT0X', self.game_controller_direction_pad_response) self._game_controller.add_event_response( 'ABS_RZ', self.game_controller_right_trigger_response) self._game_controller.add_event_response( 'ABS_Z', self.game_controller_left_trigger_response) self._game_controller.add_event_response( 'ABS_X', self.game_controller_left_stick_x) self._game_controller.add_event_response( 'BTN_EAST', self.game_controller_b_button_response) self._game_controller.add_event_response( 'BTN_TR', self.game_controller_right_bumper_response) self._game_controller.start() def game_controller_left_stick_x(self, state): self._vehicle_ctl.set_steering( state / self._game_controller.get_calibration().joystick_boundary) def game_controller_direction_pad_response(self, state): if state == -1: self.left_pressed() elif state == 1: self.right_pressed() else: self._vehicle_ctl.set_steering(0.0) def game_controller_b_button_response(self, state): if state == 1: self.backward_pressed() else: self.backward_released() def game_controller_right_bumper_response(self, state): if state == 0: self.change_gear() def game_controller_right_trigger_response(self, state): acc_value = state / self._game_controller.get_calibration( ).right_trigger_max if self._vehicle_ctl.get_gear() == Gear.DRIVE: self._vehicle_ctl.set_accelerator(acc_value) elif self._vehicle_ctl.get_gear() == Gear.REVERSE: acc_value *= -1 self._vehicle_ctl.set_accelerator(acc_value) def game_controller_left_trigger_response(self, state): acc_value = state / self._game_controller.get_calibration( ).right_trigger_max acc_value *= -1 if self._vehicle_ctl.get_gear() == Gear.DRIVE: self._vehicle_ctl.set_accelerator(acc_value) elif self._vehicle_ctl.get_gear() == Gear.REVERSE: acc_value *= -1 self._vehicle_ctl.set_accelerator(acc_value) def forward_pressed(self): if self._vehicle_ctl.get_gear() is Gear.REVERSE: self._vehicle_ctl.set_accelerator(-1.0) elif self._vehicle_ctl.get_gear() is Gear.DRIVE: self._vehicle_ctl.set_accelerator(1.0) def forward_released(self): self._vehicle_ctl.set_accelerator(0.0) def backward_pressed(self): if self._vehicle_ctl.get_gear() is Gear.REVERSE: self._vehicle_ctl.set_accelerator(1.0) elif self._vehicle_ctl.get_gear() is Gear.DRIVE: self._vehicle_ctl.set_accelerator(-1.0) def backward_released(self): self._vehicle_ctl.set_accelerator(0.0) def right_pressed(self): self._vehicle_ctl.set_steering(1.0) def right_released(self): self._vehicle_ctl.set_steering(0.0) def left_pressed(self): self._vehicle_ctl.set_steering(-1.0) def left_released(self): self._vehicle_ctl.set_steering(0.0) def show_trim_window(self): # Pull in the trim from the vehicle to populate the trim window trim = self._vehicle_ctl.get_trim() self._trim_window = TrimDialog(trim) self._trim_window.setGeometry(QtCore.QRect(100, 100, 400, 200)) self._trim_window.show() # After things have been trimmed, update our command so we can send updated trim values self._trim_window.trim_changed.connect(self.update_trim_from_dialog) def update_trim_from_dialog(self): trim = self._trim_window.get_trim() self._vehicle_ctl.send_trim(trim) def ip_changed(self, ip): port = self._sb_port.value() self._vehicle_ctl.set_endpoint(ip, port) def proxy_address_changed(self, address): self._vehicle_ctl.set_proxy(address, self._vehicle_ctl.vehicle_proxy_port()) def proxy_port_changed(self, port): self._vehicle_ctl.set_proxy(self._vehicle_ctl.vehicle_proxy_address(), port) def use_proxy_toggled(self, state): self._le_proxy_address.setEnabled(state) self._sb_proxy_port.setEnabled(state) if state: self._vehicle_ctl.enable_proxy() else: self._vehicle_ctl.disable_proxy() def port_changed(self, port): ip = self._le_ip.text() self._vehicle_ctl.set_endpoint(ip, port) def mode_changed(self, index): mode_int = self._cbo_mode.currentData() mode = Mode() mode.set_mode(mode_int) self._vehicle_ctl.set_mode(mode) def restart_stream(self): self._vehicle_ctl.restart_stream() def change_gear(self): self._vehicle_ctl.toggle_fw_bw() self._lbl_gear.setText('<b style="color: black;">' + self._vehicle_ctl.get_gear().value + "</b>") def keyPressEvent(self, e): if e.isAutoRepeat(): return super().keyReleaseEvent(e) if e.key() == QtCore.Qt.Key_Up or e.key() == QtCore.Qt.Key_W: self.forward_pressed() if e.key() == QtCore.Qt.Key_Left or e.key() == QtCore.Qt.Key_A: self.left_pressed() if e.key() == QtCore.Qt.Key_Down or e.key() == QtCore.Qt.Key_S: self.backward_pressed() if e.key() == QtCore.Qt.Key_Right or e.key() == QtCore.Qt.Key_D: self.right_pressed() return super().keyPressEvent(e) def keyReleaseEvent(self, e): if e.isAutoRepeat(): return super().keyReleaseEvent(e) if e.key() == QtCore.Qt.Key_Up or e.key() == QtCore.Qt.Key_W: self.forward_released() elif e.key() == QtCore.Qt.Key_Left or e.key() == QtCore.Qt.Key_A: self.left_released() elif e.key() == QtCore.Qt.Key_Down or e.key() == QtCore.Qt.Key_S: self.backward_released() elif e.key() == QtCore.Qt.Key_Right or e.key() == QtCore.Qt.Key_D: self.right_released() elif e.key() == QtCore.Qt.Key_R: self.change_gear() return super().keyReleaseEvent(e) def send_trim(self): self._vehicle_ctl.send_trim(self.trim) def create_gui(self): buttonHeight = 100 self._forward_btn = QPushButton("ACC", self) self._brake_btn = QPushButton("BRAKE", self) self._left_btn = QPushButton("LEFT", self) self._right_btn = QPushButton("RIGHT", self) self._forward_btn.setFixedHeight(buttonHeight) self._brake_btn.setFixedHeight(buttonHeight) self._left_btn.setFixedHeight(buttonHeight) self._right_btn.setFixedHeight(buttonHeight) self._trim_btn = QPushButton("Set Trim", self) self._trim_btn.clicked.connect(self.show_trim_window) self._le_ip = QLineEdit(self._vehicle_ctl.vehicle_ip(), self) self._le_ip.textChanged.connect(self.ip_changed) self._lbl_ip = QLabel("Ip:") self._sb_port = QSpinBox(self) self._sb_port.setMaximum(99999) self._sb_port.setValue(self._vehicle_ctl.vehicle_port()) self._sb_port.valueChanged.connect(self.port_changed) self._lbl_port = QLabel("Port:") self._lbl_mode = QLabel("Mode:") self._cbo_mode = QComboBox(self) self._lbl_proxy_address = QLabel("Proxy:") self._le_proxy_address = QLineEdit( self._vehicle_ctl.vehicle_proxy_address(), self) self._lbl_proxy_port = QLabel("Proxy Port:") self._lbl_gear = QLabel('<b style="color: black;">' + self._vehicle_ctl.get_gear().value + "</b>") f = self._lbl_gear.font() f.setPointSizeF(100) self._lbl_gear.setFont(f) self._lbl_gear.setAlignment(Qt.AlignCenter) self._btn_change_gear = QPushButton("Shift Gear") self._sb_proxy_port = QSpinBox(self) self._sb_proxy_port.setMaximum(99999) self._sb_proxy_port.setValue(self._vehicle_ctl.vehicle_proxy_port()) self._cbo_mode.addItem("NORMAL", int(ModeType.NORMAL)) self._cbo_mode.addItem("TRAIN", int(ModeType.TRAIN)) self._cbo_mode.addItem("AUTO", int(ModeType.AUTO)) self._btn_restart = QPushButton("Restart Stream") self._cb_proxy = QCheckBox() self._lbl_use_proxy = QLabel("Use Proxy") self._cb_proxy.setChecked(self._vehicle_ctl.is_using_proxy()) self._le_proxy_address.setEnabled(self._vehicle_ctl.is_using_proxy()) self._sb_proxy_port.setEnabled(self._vehicle_ctl.is_using_proxy()) def add_gui_listeners(self): self._forward_btn.pressed.connect(self.forward_pressed) self._forward_btn.released.connect(self.forward_released) self._brake_btn.pressed.connect(self.backward_pressed) self._brake_btn.released.connect(self.backward_released) self._right_btn.pressed.connect(self.right_pressed) self._right_btn.released.connect(self.right_released) self._left_btn.pressed.connect(self.left_pressed) self._left_btn.released.connect(self.left_released) self._cbo_mode.activated.connect(self.mode_changed) self._btn_restart.pressed.connect(self.restart_stream) self._cb_proxy.toggled.connect(self.use_proxy_toggled) self._le_proxy_address.textChanged.connect(self.proxy_address_changed) self._sb_proxy_port.valueChanged.connect(self.proxy_port_changed) self._btn_change_gear.released.connect(self.change_gear) def add_widgets_to_screen(self, cw): grid_layout = QGridLayout(cw) controls_layout = QGridLayout() controls_layout.setAlignment(Qt.AlignVCenter) grid_layout.addLayout(controls_layout, 0, 0, 2, 3) controls_layout.addWidget(self._forward_btn, 0, 1) controls_layout.addWidget(self._left_btn, 1, 0) controls_layout.addWidget(self._brake_btn, 1, 1) controls_layout.addWidget(self._right_btn, 1, 2) grid_layout.addWidget(self._lbl_ip, 2, 0) grid_layout.addWidget(self._le_ip, 2, 1, 1, 2) # Stretch the line edit into two cells grid_layout.addWidget(self._lbl_port, 3, 0) grid_layout.addWidget(self._sb_port, 3, 1, 1, 2) # Stretch the spinbox into two cells grid_layout.addWidget(self._lbl_use_proxy, 4, 0) grid_layout.addWidget(self._cb_proxy, 4, 1) grid_layout.addWidget(self._lbl_proxy_address, 5, 0) grid_layout.addWidget(self._le_proxy_address, 5, 1, 1, 2) grid_layout.addWidget(self._lbl_proxy_port, 6, 0) grid_layout.addWidget(self._sb_proxy_port, 6, 1, 1, 2) grid_layout.addWidget(self._lbl_mode, 7, 0) grid_layout.addWidget(self._cbo_mode, 7, 1, 1, 2) grid_layout.addWidget(self._image_viewer, 0, 3, 5, 1) grid_layout.addWidget(self._btn_restart, 5, 3) vehicle_layout = QVBoxLayout() grid_layout.addLayout(vehicle_layout, 0, 4) vehicle_layout.addWidget(self._lbl_gear) vehicle_layout.addWidget(self._btn_change_gear) vehicle_layout.addWidget(self._trim_btn)
class GradientWidget(ToolWidget): def __init__(self, image, parent=None): super(GradientWidget, self).__init__(parent) self.intensity_spin = QSpinBox() self.intensity_spin.setRange(0, 100) self.intensity_spin.setValue(95) self.intensity_spin.setSuffix(self.tr(" %")) self.intensity_spin.setToolTip(self.tr("Tonality compression amount")) self.blue_combo = QComboBox() self.blue_combo.addItems([ self.tr("None"), self.tr("Flat"), self.tr("Abs"), self.tr("Norm") ]) self.blue_combo.setCurrentIndex(2) self.blue_combo.setToolTip(self.tr("Blue component inclusion mode")) self.invert_check = QCheckBox(self.tr("Invert")) self.invert_check.setToolTip(self.tr("Reverse lighting direction")) self.equalize_check = QCheckBox(self.tr("Equalize")) self.equalize_check.setToolTip(self.tr("Apply histogram equalization")) self.image = image self.viewer = ImageViewer(self.image, self.image) self.dx, self.dy = cv.spatialGradient( cv.cvtColor(self.image, cv.COLOR_BGR2GRAY)) self.process() self.intensity_spin.valueChanged.connect(self.process) self.blue_combo.currentIndexChanged.connect(self.process) self.invert_check.stateChanged.connect(self.process) self.equalize_check.stateChanged.connect(self.process) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr("Intensity:"))) top_layout.addWidget(self.intensity_spin) top_layout.addWidget(QLabel(self.tr("Blue channel:"))) top_layout.addWidget(self.blue_combo) top_layout.addWidget(self.invert_check) top_layout.addWidget(self.equalize_check) top_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) def process(self): start = time() intensity = int(self.intensity_spin.value() / 100 * 127) invert = self.invert_check.isChecked() equalize = self.equalize_check.isChecked() self.intensity_spin.setEnabled(not equalize) blue_mode = self.blue_combo.currentIndex() if invert: dx = (-self.dx).astype(np.float32) dy = (-self.dy).astype(np.float32) else: dx = (+self.dx).astype(np.float32) dy = (+self.dy).astype(np.float32) dx_abs = np.abs(dx) dy_abs = np.abs(dy) red = ((dx / np.max(dx_abs) * 127) + 127).astype(np.uint8) green = ((dy / np.max(dy_abs) * 127) + 127).astype(np.uint8) if blue_mode == 0: blue = np.zeros_like(red) elif blue_mode == 1: blue = np.full_like(red, 255) elif blue_mode == 2: blue = norm_mat(dx_abs + dy_abs) elif blue_mode == 3: blue = norm_mat(np.linalg.norm(cv.merge((red, green)), axis=2)) else: blue = None gradient = cv.merge([blue, green, red]) if equalize: gradient = equalize_img(gradient) elif intensity > 0: gradient = cv.LUT(gradient, create_lut(intensity, intensity)) self.viewer.update_processed(gradient) self.info_message.emit( self.tr(f"Luminance Gradient = {elapsed_time(start)}"))
class AddRelationshipClassesDialog(GetObjectClassesMixin, AddItemsDialog): """A dialog to query user's preferences for new relationship classes.""" def __init__(self, parent, db_mngr, *db_maps, object_class_one_name=None, force_default=False): """ Args: parent (SpineDBEditor) db_mngr (SpineDBManager) *db_maps: DiffDatabaseMapping instances object_class_one_name (str): default object_class name force_default (bool): if True, defaults are non-editable """ super().__init__(parent, db_mngr, *db_maps) self.setWindowTitle("Add relationship classes") self.model = EmptyRowModel(self) self.model.force_default = force_default self.table_view.setModel(self.model) self.dimension_count_widget = QWidget(self) layout = QHBoxLayout(self.dimension_count_widget) layout.addWidget(QLabel("Number of dimensions")) self.spin_box = QSpinBox(self) self.spin_box.setMinimum(1) layout.addWidget(self.spin_box) layout.addStretch() self.layout().addWidget(self.dimension_count_widget, 0, 0, 1, -1) self.layout().addWidget(self.table_view, 1, 0) self.layout().addWidget(self.remove_rows_button, 2, 0) self.layout().addWidget(self.button_box, 3, 0, -1, -1) self.remove_rows_button.setIcon( QIcon(":/icons/menu_icons/cubes_minus.svg")) self.table_view.setItemDelegate( ManageRelationshipClassesDelegate(self)) self.number_of_dimensions = 1 self.connect_signals() self.model.set_horizontal_header_labels([ 'object_class name (1)', 'relationship_class name', 'description', 'databases' ]) self.db_map_obj_cls_lookup = self.make_db_map_obj_cls_lookup() if object_class_one_name: default_db_maps = [ db_map for db_map, names in self.db_map_obj_cls_lookup.items() if object_class_one_name in names ] db_names = ",".join([ db_name for db_name, db_map in self.keyed_db_maps.items() if db_map in default_db_maps ]) else: db_names = ",".join(list(self.keyed_db_maps.keys())) self.model.set_default_row(**{ 'object_class name (1)': object_class_one_name, 'databases': db_names }) self.model.clear() def connect_signals(self): """Connect signals to slots.""" super().connect_signals() self.spin_box.valueChanged.connect(self._handle_spin_box_value_changed) @Slot(int) def _handle_spin_box_value_changed(self, i): self.spin_box.setEnabled(False) if i > self.number_of_dimensions: self.insert_column() elif i < self.number_of_dimensions: self.remove_column() self.spin_box.setEnabled(True) self.resize_window_to_columns() def insert_column(self): column = self.number_of_dimensions self.number_of_dimensions += 1 column_name = "object_class name ({0})".format( self.number_of_dimensions) self.model.insertColumns(column, 1) self.model.insert_horizontal_header_labels(column, [column_name]) self.table_view.resizeColumnToContents(column) def remove_column(self): self.number_of_dimensions -= 1 column = self.number_of_dimensions self.model.header.pop(column) self.model.removeColumns(column, 1) @Slot("QModelIndex", "QModelIndex", "QVector") def _handle_model_data_changed(self, top_left, bottom_right, roles): if Qt.EditRole not in roles: return top = top_left.row() left = top_left.column() bottom = bottom_right.row() right = bottom_right.column() for row in range(top, bottom + 1): for column in range(left, right + 1): if column >= self.number_of_dimensions: break else: col_data = lambda j: self.model.index(row, j).data() # pylint: disable=cell-var-from-loop obj_cls_names = [ col_data(j) for j in range(self.number_of_dimensions) if col_data(j) ] if len(obj_cls_names) == 1: relationship_class_name = obj_cls_names[0] + "__" else: relationship_class_name = "__".join(obj_cls_names) self.model.setData( self.model.index(row, self.number_of_dimensions), relationship_class_name) @Slot() def accept(self): """Collect info from dialog and try to add items.""" db_map_data = dict() name_column = self.model.horizontal_header_labels().index( "relationship_class name") description_column = self.model.horizontal_header_labels().index( "description") db_column = self.model.horizontal_header_labels().index("databases") for i in range(self.model.rowCount() - 1): # last row will always be empty row_data = self.model.row_data(i) relationship_class_name = row_data[name_column] if not relationship_class_name: self.parent().msg_error.emit( "Relationship class missing at row {}".format(i + 1)) return description = row_data[description_column] pre_item = { 'name': relationship_class_name, 'description': description } db_names = row_data[db_column] if db_names is None: db_names = "" for db_name in db_names.split(","): if db_name not in self.keyed_db_maps: self.parent().msg_error.emit( "Invalid database {0} at row {1}".format( db_name, i + 1)) return db_map = self.keyed_db_maps[db_name] object_classes = self.db_map_obj_cls_lookup[db_map] object_class_id_list = list() for column in range( name_column): # Leave 'name' column outside object_class_name = row_data[column] if object_class_name not in object_classes: self.parent().msg_error.emit( "Invalid object_class '{}' for db '{}' at row {}". format(object_class_name, db_name, i + 1)) return object_class_id = object_classes[object_class_name]["id"] object_class_id_list.append(object_class_id) item = pre_item.copy() item['object_class_id_list'] = object_class_id_list db_map_data.setdefault(db_map, []).append(item) if not db_map_data: self.parent().msg_error.emit("Nothing to add") return self.db_mngr.add_relationship_classes(db_map_data) super().accept()
class PredefinedModelsWidget(QWidget): def __init__(self, predefined_models_window, parent: "QWidget" = None): super().__init__(parent) # Model picker size (left) self._predefined_models_window = predefined_models_window self._predefined_models_group = QGroupBox("Predefined Models:") self._predefined_models_layout = QHBoxLayout() self._predefined_models_layout.setContentsMargins(0, 0, 0, 0) self._predefined_models_group.setLayout(self._predefined_models_layout) self._predefined_models_layout.addWidget( self._predefined_models_window) # Description Side (right) self._model_name = QLabel("") self._include_top = QCheckBox("Include") self._include_top.setChecked(True) self._shape_textbox = QLineEdit("(224, 224, 3)") self._shape_textbox.setEnabled(False) self._classes_intbox = QSpinBox() self._classes_intbox.setMinimum(2) self._classes_intbox.setMaximum(999999) self._classes_intbox.setValue(1000) self._weights_combobox = QComboBox() self._weights_combobox.addItem("Imagenet", "imagenet") self._weights_combobox.addItem("None", None) # Add widgets to layout self._description_group = QGroupBox("Description:") self._description_layout = QFormLayout() self._description_group.setLayout(self._description_layout) self._description_layout.addRow("Name:", self._model_name) self._description_layout.addRow("Include Top:", self._include_top) self._description_layout.addRow("Input Shape:", self._shape_textbox) self._description_layout.addRow("Classes:", self._classes_intbox) self._description_layout.addRow("Weights:", self._weights_combobox) self._main_layout = QHBoxLayout() self._main_layout.addWidget(self._predefined_models_group) self._main_layout.addWidget(self._description_group) self.setLayout(self._main_layout) sp_left = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sp_left.setHorizontalStretch(1) self._predefined_models_group.setSizePolicy(sp_left) sp_right = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sp_right.setHorizontalStretch(2.5) self._description_group.setSizePolicy(sp_right) self._include_top.stateChanged.connect( lambda: self._update_enabled_widgets()) self._weights_combobox.currentIndexChanged[int].connect( lambda: self._update_enabled_widgets()) self._predefined_models_window.selected_model_changed.connect( self._update_description_labels) self._update_enabled_widgets() def get_model(self) -> Optional["Model"]: try: LOGGER.debug("Include Top: %s", self._include_top.isChecked()) LOGGER.debug("Classes: %s", self._classes_intbox.value()) LOGGER.debug("Weights: %s", self._weights_combobox.currentData()) if not self._include_top.isChecked(): input_shape = tuple( map( int, re.sub("[()]", "", self._shape_textbox.text()).split(","))) LOGGER.debug("Input tuple: %s", input_shape) return self._predefined_models_window.get_selected_model( )["loader"]( include_top=False, input_shape=input_shape, weights=self._weights_combobox.currentData(), ) return self._predefined_models_window.get_selected_model( )["loader"]( include_top=True, classes=self._classes_intbox.value(), weights=self._weights_combobox.currentData(), ) except TypeError as err: LOGGER.exception(err) return None def get_image_transformations(self) -> List[Callable]: try: transformations = self._predefined_models_window.get_selected_model( )["transformations"] return transformations except KeyError as err: LOGGER.exception(err) return None def _update_description_labels(self, model_desc: dict): self._model_name.setText(model_desc["name"]) def _update_enabled_widgets(self): self._shape_textbox.setEnabled(not self._include_top.isChecked()) if (self._include_top.isChecked() and self._weights_combobox.currentText() == "None"): self._classes_intbox.setEnabled(True) else: self._classes_intbox.setEnabled(False) def sizeHint(self) -> "QSize": return QSize(400, 170)
class NoiseWidget(ToolWidget): def __init__(self, image, parent=None): super(NoiseWidget, self).__init__(parent) self.mode_combo = QComboBox() self.mode_combo.addItems([ self.tr('Median'), self.tr('Gaussian'), self.tr('BoxBlur'), self.tr('Bilateral'), self.tr('NonLocal') ]) self.radius_spin = QSpinBox() self.radius_spin.setRange(1, 10) self.radius_spin.setSuffix(self.tr(' px')) self.radius_spin.setValue(1) self.sigma_spin = QSpinBox() self.sigma_spin.setRange(1, 200) self.sigma_spin.setValue(3) self.levels_spin = QSpinBox() self.levels_spin.setRange(0, 255) self.levels_spin.setSpecialValueText(self.tr('Equalized')) self.levels_spin.setValue(32) self.gray_check = QCheckBox(self.tr('Grayscale')) self.denoised_check = QCheckBox(self.tr('Denoised')) self.image = image self.viewer = ImageViewer(self.image, self.image) self.process() params_layout = QHBoxLayout() params_layout.addWidget(QLabel(self.tr('Mode:'))) params_layout.addWidget(self.mode_combo) params_layout.addWidget(QLabel(self.tr('Radius:'))) params_layout.addWidget(self.radius_spin) params_layout.addWidget(QLabel(self.tr('Sigma:'))) params_layout.addWidget(self.sigma_spin) params_layout.addWidget(QLabel(self.tr('Levels:'))) params_layout.addWidget(self.levels_spin) params_layout.addWidget(self.gray_check) params_layout.addWidget(self.denoised_check) params_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(params_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) self.mode_combo.currentTextChanged.connect(self.process) self.radius_spin.valueChanged.connect(self.process) self.sigma_spin.valueChanged.connect(self.process) self.levels_spin.valueChanged.connect(self.process) self.gray_check.stateChanged.connect(self.process) self.denoised_check.stateChanged.connect(self.process) def process(self): start = time() grayscale = self.gray_check.isChecked() if grayscale: original = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) else: original = self.image radius = self.radius_spin.value() kernel = radius * 2 + 1 sigma = self.sigma_spin.value() choice = self.mode_combo.currentText() if choice == self.tr('Median'): self.sigma_spin.setEnabled(False) denoised = cv.medianBlur(original, kernel) elif choice == self.tr('Gaussian'): self.sigma_spin.setEnabled(False) denoised = cv.GaussianBlur(original, (kernel, kernel), 0) elif choice == self.tr('BoxBlur'): self.sigma_spin.setEnabled(False) denoised = cv.blur(original, (kernel, kernel)) elif choice == self.tr('Bilateral'): self.sigma_spin.setEnabled(True) denoised = cv.bilateralFilter(original, kernel, sigma, sigma) elif choice == self.tr('NonLocal'): if grayscale: denoised = cv.fastNlMeansDenoising(original, None, kernel) else: denoised = cv.fastNlMeansDenoisingColored( original, None, kernel, kernel) else: denoised = None if self.denoised_check.isChecked(): self.levels_spin.setEnabled(False) result = denoised else: self.levels_spin.setEnabled(True) noise = cv.absdiff(original, denoised) levels = self.levels_spin.value() if levels == 0: if grayscale: result = cv.equalizeHist(noise) else: result = equalize_img(noise) else: result = cv.LUT(noise, create_lut(0, 255 - levels)) if grayscale: result = cv.cvtColor(result, cv.COLOR_GRAY2BGR) self.viewer.update_processed(result) self.info_message.emit( self.tr('Noise estimation = {}'.format(elapsed_time(start))))
class QFlightSlotEditor(QGroupBox): changed = Signal() def __init__(self, package_model: PackageModel, flight: Flight, game: Game): super().__init__("Slots") self.package_model = package_model self.flight = flight self.game = game self.inventory = self.game.aircraft_inventory.for_control_point( flight.from_cp) available = self.inventory.available(self.flight.unit_type) max_count = self.flight.count + available if max_count > 4: max_count = 4 layout = QGridLayout() self.aircraft_count = QLabel("Aircraft count:") self.aircraft_count_spinner = QSpinBox() self.aircraft_count_spinner.setMinimum(1) self.aircraft_count_spinner.setMaximum(max_count) self.aircraft_count_spinner.setValue(flight.count) self.aircraft_count_spinner.valueChanged.connect( self._changed_aircraft_count) self.client_count = QLabel("Client slots count:") self.client_count_spinner = QSpinBox() self.client_count_spinner.setMinimum(0) self.client_count_spinner.setMaximum(max_count) self.client_count_spinner.setValue(flight.client_count) self.client_count_spinner.valueChanged.connect( self._changed_client_count) if not self.flight.unit_type.flyable: self.client_count_spinner.setValue(0) self.client_count_spinner.setEnabled(False) layout.addWidget(self.aircraft_count, 0, 0) layout.addWidget(self.aircraft_count_spinner, 0, 1) layout.addWidget(self.client_count, 1, 0) layout.addWidget(self.client_count_spinner, 1, 1) self.setLayout(layout) def _changed_aircraft_count(self): self.game.aircraft_inventory.return_from_flight(self.flight) old_count = self.flight.count self.flight.count = int(self.aircraft_count_spinner.value()) try: self.game.aircraft_inventory.claim_for_flight(self.flight) except ValueError: # The UI should have prevented this, but if we ran out of aircraft # then roll back the inventory change. difference = self.flight.count - old_count available = self.inventory.available(self.flight.unit_type) logging.error( f"Could not add {difference} additional aircraft to " f"{self.flight} because {self.flight.from_cp} has only " f"{available} {self.flight.unit_type} remaining") self.flight.count = old_count self.game.aircraft_inventory.claim_for_flight(self.flight) self.changed.emit() def _changed_client_count(self): self.flight.client_count = int(self.client_count_spinner.value()) self._cap_client_count() self.package_model.update_tot() self.changed.emit() def _cap_client_count(self): if self.flight.client_count > self.flight.count: self.flight.client_count = self.flight.count self.client_count_spinner.setValue(self.flight.client_count)
class StatisticsOptions(QWidget): polymorphic_states = { 'Regression': {'dtext': 'Tipo de Regressão:', 'xtext': 'Selecione a coluna dos valores correspondentes à variável independente:', 'ytext': 'Selecione a coluna dos valores correspondentes à variável dependente:', 'dropdown': ['Linear', 'Exponencial', 'Logarítmica', 'Potencial', 'Polinomial'], 'ctext':''}, 'Statistics': {'dtext': 'Tipo de análise estatística:', 'xtext': 'Selecione a coluna dos valores para realizar a análise:', 'ytext': '', 'dropdown': ['Média aritmética', 'Mediana', 'Moda', 'Variância Pop.', 'Variância Am.', 'Desvio Padrão Pop.', 'Desvio Padrão Am.', 'Máximo', 'Mínimo', 'Amplitude', 'Quartil', 'Percentil'], 'ctext':''}, 'Histogram': {'dtext': '', 'xtext': 'Selecione a coluna com os valores:', 'ytext': '', 'dropdown': [], 'ctext':''}, 'Boxplot': {'dtext': 'Orientação do gráfico:', 'ctext': 'Selecione as colunas onde se encontram as séries para o Boxplot (i.e. B, B:C, A:C:F:G):', 'ytext': '', 'dropdown': ['Vertical', 'Horizontal'], 'xtext':''}, } ready2calculate = Signal(bool) def __init__(self, _mode='Regression'): super().__init__() self.mode = _mode self.subtitle = QLabel(str(_mode)) self.dropdown_text = QLabel(self.polymorphic_states[self.mode]['dtext']) self.dropdown_text.setWordWrap(True) self.dropdown = Dropdown(self.polymorphic_states[self.mode]['dropdown']) self.x_column_text = QLabel(self.polymorphic_states[self.mode]['xtext']) self.x_column_text.setWordWrap(True) self.x_column = QLineEdit() self.y_column_text = QLabel(self.polymorphic_states[self.mode]['ytext']) self.y_column_text.setWordWrap(True) self.y_column = QLineEdit() self.column_range_text = QLabel(self.polymorphic_states[self.mode]['ctext']) self.column_range_text.setWordWrap(True) self.column_range = QLineEdit() self.output_text = QLabel('Select Output:') self.output_text.setWordWrap(True) self.table_button = QRadioButton('Tabela (Planilha)') self.plot_button = QRadioButton('Gráfico (Plot)') self.output_destination_text = QLabel('Selecione a coluna onde deve-se armazenar os resultados:') self.output_destination_text.setWordWrap(True) self.output_destination = QLineEdit() self.run_button = QPushButton('Começar Análise') self.selected_output = '' self.degree_l = QLabel('Ordem:') self.degree = QSpinBox() self.degree.setRange(1, 6) self.quartile_l = QLabel('Quartil:') self.quartile = QSpinBox() self.quartile.setRange(1, 4) self.percentile_l = QLabel('Percentil:') self.percentile = QSpinBox() self.percentile.setRange(1, 100) self.default_degree_stylesheet = self.degree.styleSheet() self.default_quartile_stylesheet = self.quartile.styleSheet() self.default_percentile_stylesheet = self.percentile.styleSheet() self.degree.setDisabled(True) self.results_l = QLabel('Resultados da análise: ') self.results = QTextEdit() self.payload = [] self.buildLayout() self.connections() self.initialState() self.setStyleSheet('color:white; font-family: Bahnschrift SemiLight Condensed; font-size: 14px;') self.x_column.setStyleSheet('background-color: white; color: black') self.y_column.setStyleSheet('background-color: white; color: black') self.column_range.setStyleSheet('background-color: white; color: black') self.results.setStyleSheet('background-color: white; color: black') def buildLayout(self): layout = QFormLayout() if len(self.dropdown_text.text()) > 0: layout.addWidget(self.dropdown_text) layout.addWidget(self.dropdown) if len(self.x_column_text.text()) > 0: layout.addWidget(self.x_column_text) layout.addWidget(self.x_column) if len(self.y_column_text.text()) > 0: layout.addWidget(self.y_column_text) layout.addWidget(self.y_column) if len(self.column_range_text.text()) > 0: layout.addWidget(self.column_range_text) layout.addWidget(self.column_range) if self.mode == 'Statistics': layout.addWidget(self.quartile_l) layout.addWidget(self.quartile) layout.addWidget(self.percentile_l) layout.addWidget(self.percentile) if self.mode == 'Regression': layout.addWidget(self.degree_l) layout.addWidget(self.degree) layout.addWidget(self.run_button) if self.mode != 'Boxplot' and self.mode != "Histogram": layout.addWidget(self.results_l) layout.addWidget(self.results) self.setLayout(layout) def initialState(self): self.output_destination.setDisabled(True) def connections(self): self.plot_button.clicked.connect(self.plotSelected) self.table_button.clicked.connect(self.tableSelected) self.run_button.clicked.connect(self.collectPayload) self.dropdown.currentTextChanged.connect(self.enableDegreeBox) def columnRangeDecomposition(self, text): try: return text.split(sep=':') except Exception: print('A problem happened in decomposing the column ranges. Probable bad user input.') def plotSelected(self): self.output_destination.setDisabled(True) self.selected_output = 'Plot' def enableDegreeBox(self, text): if text == 'Polinomial': self.degree.setStyleSheet('background-color: white; color: black') self.degree.setEnabled(True) else: self.degree.setStyleSheet(self.default_degree_stylesheet) self.degree.setDisabled(True) if text == 'Quartil': self.quartile.setStyleSheet('background-color: white; color: black') self.quartile.setEnabled(True) else: self.quartile.setStyleSheet(self.default_degree_stylesheet) self.quartile.setDisabled(True) if text == 'Percentil': self.percentile.setStyleSheet('background-color: white; color: black') self.percentile.setEnabled(True) else: self.percentile.setStyleSheet(self.default_degree_stylesheet) self.percentile.setDisabled(True) def tableSelected(self): self.output_destination.setDisabled(False) self.selected_output = 'Table' def collectPayload(self): if len(self.column_range_text.text()) > 0: a = self.columnRangeDecomposition(self.column_range.text()) b = 'V' else: a = None b = self.y_column.text() payload = { 'calculate': self.dropdown.currentText(), 'x_column': self.x_column.text(), 'y_column': b, 'z_column': None, 'column_range': a, 'output_selection': self.selected_output, 'output_column': self.output_destination.text() } self.payload = payload self.ready2calculate.emit(True)
class Widget(QWidget): cinema_url_label_text = "粘贴独播库的连续剧/综艺/动漫 URL: " movie_url_label_text = "粘贴独播库的电影 URL: " cinema_dest_label_text = "输入连续剧/综艺/动漫名 (用来命名下载的目录): " movie_dest_label_text = "输入电影名 (用来命名下载的文件): " def __init__(self): QWidget.__init__(self) self.q = None self.pool = None self.top = QHBoxLayout() self.top.setMargin(10) self.radioButtonMov = QRadioButton("电影") self.radioButtonCinema = QRadioButton("连续剧/综艺/动漫") self.top.addWidget(self.radioButtonMov) self.top.addWidget(self.radioButtonCinema) self.middle = QVBoxLayout() self.middle.setMargin(10) self.url_label = QLabel() self.url = QLineEdit() self.url_label.setBuddy(self.url) self.middle.addWidget(self.url_label) self.middle.addWidget(self.url) self.browse_folder_label = QLabel("下载到:") self.browseFolder = QPushButton("选择目录") self.browse_folder_label.setBuddy(self.browseFolder) self.middle.addWidget(self.browse_folder_label) self.middle.addWidget(self.browseFolder) self.browse_folder_value = "" self.dest_file_label = QLabel() # "输入电影/电视剧名 (用来命名下载的文件/目录): " title set by choose_movie_widgets() later self.folder_or_filename = QLineEdit() self.dest_file_label.setBuddy(self.folder_or_filename) self.middle.addWidget(self.dest_file_label) self.middle.addWidget(self.folder_or_filename) self.bk_cinemae_spin_from = 1 self.bk_cinemae_spin_to = 1 self.fromEpSpinBox = QSpinBox() self.fromEpSpinBox.setMinimum(1) self.fromEpSpinBox.setMaximum(2147483647) self.fromEpLabel = QLabel("&从第几集开始下载:") self.fromEpLabel.setBuddy(self.fromEpSpinBox) self.toEpSpinBox = QSpinBox() self.toEpSpinBox.setMinimum(1) self.toEpSpinBox.setMaximum(2147483647) self.toEpLabel = QLabel("&到第几集停止下载:") self.toEpLabel.setBuddy(self.toEpSpinBox) self.cinema_ly = QHBoxLayout() #self.cinema_ly.setMargin(10) self.cinema_ly.addWidget(self.fromEpLabel) self.cinema_ly.addWidget(self.fromEpSpinBox) self.cinema_ly.addWidget(self.toEpLabel) self.cinema_ly.addWidget(self.toEpSpinBox) self.middle.addLayout(self.cinema_ly) self.proxy_label = QLabel("(如有)代理:") self.proxy = QLineEdit() self.proxy_label.setBuddy(self.proxy) self.middle.addWidget(self.proxy_label) self.middle.addWidget(self.proxy) self.add = QPushButton("开始下载") self.add.setEnabled(False) self.middle.addWidget(self.add) self.stop_me = QPushButton("停止下载") self.stop_me.setEnabled(False) self.middle.addWidget(self.stop_me) self.log_area = QPlainTextEdit() self.log_area.setReadOnly(True) self.log_area.setMaximumBlockCount(1000) self.middle.addWidget(self.log_area) #self.table_view.setSizePolicy(size) #self.layout.addWidget(self.table) self.layout = QVBoxLayout() self.layout.addLayout(self.top) self.layout.addLayout(self.middle) self.setLayout(self.layout) self.radioButtonMov.toggled.connect(self.choose_movie_widgets) self.radioButtonCinema.toggled.connect(self.choose_cinema_widgets) self.url.textChanged[str].connect(self.check_disable_download) self.browseFolder.clicked.connect(self.add_folder) self.folder_or_filename.textChanged[str].connect( self.check_disable_download) self.add.clicked.connect(self.start_download) self.stop_me.clicked.connect(self.stop_download) self.radioButtonMov.setChecked( True) #set default only after .connect above # TESTING PURPOSE ''' self.radioButtonMov.setChecked(False) self.url.setText('https://www.duboku.net/voddetail-969.html') self.browse_folder_value = 'C:/Users/Administrator/Documents/duboku' self.folder_or_filename.setText('初恋') ''' #set current process (not queue that one) log handler: logger = logging.getLogger(__name__) handler2 = LoggerWriter() logger.addHandler(handler2) logger.setLevel(logging.INFO) #DEBUG handler2.emitter.sigLog.connect(self.log_area.appendPlainText) sys.stdout = handler2 #LoggerWriter() #sys.stderr = handler2 #Seems no difference #handler2.emitter.sigLog.emit('hihi') @Slot() def choose_movie_widgets(self): if self.radioButtonMov.isChecked(): self.url_label.setText(self.movie_url_label_text) self.dest_file_label.setText(self.movie_dest_label_text) self.fromEpLabel.setDisabled(True) self.toEpLabel.setDisabled(True) self.fromEpSpinBox.setDisabled(True) self.toEpSpinBox.setDisabled(True) self.bk_cinemae_spin_from = self.fromEpSpinBox.value() self.bk_cinemae_spin_to = self.toEpSpinBox.value() self.fromEpSpinBox.setValue(1) self.toEpSpinBox.setValue(1) @Slot() def choose_cinema_widgets(self): if self.radioButtonCinema.isChecked(): self.url_label.setText(self.cinema_url_label_text) self.dest_file_label.setText(self.cinema_dest_label_text) self.fromEpLabel.setEnabled(True) self.toEpLabel.setEnabled(True) self.fromEpSpinBox.setEnabled(True) self.toEpSpinBox.setEnabled(True) self.fromEpSpinBox.setValue(self.bk_cinemae_spin_from) self.toEpSpinBox.setValue(self.bk_cinemae_spin_to) @Slot() def add_folder(self, s): #fname = QFileDialog.getOpenFileName(self, 'Open file', "c:\'", "Image files (*.jpg *.gif)") #fname = QFileDialog.getOpenFileName(self, 'Open file', '', QFileDialog.ShowDirsOnly) fname = QFileDialog.getExistingDirectory(self, '选择下载至什么目录', '', QFileDialog.ShowDirsOnly) #print('repr: ' + repr(fname)) if fname and fname.strip(): fname = fname.strip() self.browse_folder_value = fname #if getOpenFileName, will return ('/home/xiaobai/Pictures/disco.jpg', 'Image files (*.jpg *.gif)') #, while if getExistingDirectory, will return single path string only self.browseFolder.setText(fname) self.check_disable_download(fname) #else: # print('User cancel') @Slot() def check_disable_download(self, s): if self.url.text().strip( ) and self.browse_folder_value and self.folder_or_filename.text(): self.add.setEnabled(True) else: self.add.setEnabled(False) def task_done(self, retVal): self.add.setEnabled(True) self.stop_me.setEnabled(False) @Slot() def stop_download(self): if self.q: self.q.close() if self.pool: self.pool.terminate() self.add.setEnabled(True) self.stop_me.setEnabled(False) print('下载停止。') @Slot() def start_download(self): if self.fromEpSpinBox.value() > self.toEpSpinBox.value(): self.log_area.setPlainText('[!] 从第几集必须小于或等于到第几集。') return #No need worry click twice too fast, it seems already handle by PySide2 self.add.setEnabled(False) self.stop_me.setEnabled(True) self.log_area.clear() dest_full_path = os.path.join(self.browse_folder_value, self.folder_or_filename.text()) ''' print('dest_full_path: ' + repr(dest_full_path)) print('self.url.text(): ' + repr(self.url.text())) print('self.fromEpSpinBox.value(): ' + repr(self.fromEpSpinBox.value())) print('self.toEpSpinBox.value(): ' + repr(self.toEpSpinBox.value())) ''' import duboku_console #Windows can't set like that bcoz not update for args.url, must put explicitly #duboku_console.redirect_stdout_to_custom_stdout(arg_url, ...etc, LoggerWriter()) #failed other process handler = LogHandlerOtherProcess() handler.emitter.sigLog.connect(self.log_area.appendPlainText) ''' #ref current process: logger = logging.getLogger(__name__) handler2 = LoggerWriter() logger.addHandler(handler2) logger.setLevel(logging.DEBUG) handler2.emitter.sigLog.connect(self.log_area.appendPlainText) sys.stdout = handler2 #LoggerWriter() #handler2.emitter.sigLog.emit('hihi') ''' #handler = LoggerWriter() #handler.emitter.sigLog.connect(self.log_area.appendPlainText) self.q = multiprocessing.Queue() self.ql = QueueListener(self.q, handler) self.ql.start() self.pool = multiprocessing.Pool(1, worker_init, [self.q]) if self.radioButtonMov.isChecked(): self.pool.apply_async(duboku_console.main, args=(None, dest_full_path, self.fromEpSpinBox.value(), self.toEpSpinBox.value(), self.url.text().strip(), LoggerWriterOtherProcess(), False, self.proxy.text()), callback=self.task_done) else: self.pool.apply_async(duboku_console.main, args=(dest_full_path, None, self.fromEpSpinBox.value(), self.toEpSpinBox.value(), self.url.text().strip(), LoggerWriterOtherProcess(), False, self.proxy.text()), callback=self.task_done)
class MixedDistributionChart(QDialog): def __init__(self, parent=None, show_mode=True, toolbar=False, use_animation=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("Mixed Distribution Chart")) self.figure = plt.figure(figsize=(4, 3)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) if not toolbar: self.toolbar.hide() self.supported_scales = [("log-linear", self.tr("Log-linear")), ("log", self.tr("Log")), ("phi", self.tr("φ")), ("linear", self.tr("Linear"))] self.scale_label = QLabel(self.tr("Scale")) self.scale_combo_box = QComboBox() self.scale_combo_box.addItems([name for key, name in self.supported_scales]) self.scale_combo_box.currentIndexChanged.connect(self.update_chart) self.main_layout.addWidget(self.scale_label, 2, 0) self.main_layout.addWidget(self.scale_combo_box, 2, 1) self.interval_label = QLabel(self.tr("Interval [ms]")) self.interval_input = QSpinBox() self.interval_input.setRange(0, 10000) self.interval_input.setValue(30) self.interval_input.valueChanged.connect(self.update_animation) self.main_layout.addWidget(self.interval_label, 3, 0) self.main_layout.addWidget(self.interval_input, 3, 1) self.repeat_check_box = QCheckBox(self.tr("Repeat")) self.repeat_check_box.setChecked(False) self.repeat_check_box.stateChanged.connect(self.update_animation) self.save_button = QPushButton(self.tr("Save")) self.save_button.clicked.connect(self.save_animation) self.main_layout.addWidget(self.repeat_check_box, 4, 0) self.main_layout.addWidget(self.save_button, 4, 1) self.show_mode = show_mode self.animation = None self.last_model = None self.last_result = None if not use_animation: self.interval_label.setVisible(False) self.interval_input.setVisible(False) self.repeat_check_box.setVisible(False) self.save_button.setVisible(False) self.normal_msg = QMessageBox(parent=self) self.file_dialog = QFileDialog(parent=self) @property def scale(self) -> str: index = self.scale_combo_box.currentIndex() key, name = self.supported_scales[index] return key @property def transfer(self) -> typing.Callable: if self.scale == "log-linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) elif self.scale == "log": return lambda classes_φ: np.log(convert_φ_to_μm(classes_φ)) elif self.scale == "phi": return lambda classes_φ: classes_φ elif self.scale == "linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) @property def xlabel(self) -> str: if self.scale == "log-linear": return self.tr("Grain-size [μm]") elif self.scale == "log": return self.tr("Ln(grain-size in μm)") elif self.scale == "phi": return self.tr("Grain-size [φ]") elif self.scale == "linear": return self.tr("Grain-size [μm]") @property def ylabel(self) -> str: return self.tr("Frequency") @property def xlog(self) -> bool: if self.scale == "log-linear": return True else: return False @property def interval(self) -> float: return self.interval_input.value() @property def repeat(self) -> bool: return self.repeat_check_box.isChecked() def show_demo(self): demo_model = get_demo_view_model() self.show_model(demo_model) def update_chart(self): if self.last_model is not None: self.show_model(self.last_model) elif self.last_result is not None: self.show_result(self.last_result) def update_animation(self): if self.last_result is not None: self.show_result(self.last_result) def show_model(self, model: SSUViewModel, quick=False): if self.animation is not None: self.animation._stop() self.animation = None if not quick: self.last_result = None self.last_model = model self.interval_label.setEnabled(False) self.interval_input.setEnabled(False) self.repeat_check_box.setEnabled(False) self.save_button.setEnabled(False) self.axes.clear() x = self.transfer(model.classes_φ) if self.xlog: self.axes.set_xscale("log") self.axes.set_title(model.title) self.axes.set_xlabel(self.xlabel) self.axes.set_ylabel(self.ylabel) self.target = self.axes.plot(x, model.target, c="#ffffff00", marker=".", ms=8, mfc="black", mew=0.0, label=self.tr("Target"))[0] # scatter can not be modified from the tool bar # self.target = self.axes.scatter(x, model.target, c="black", s=1) self.axes.set_xlim(x[0], x[-1]) self.axes.set_ylim(0.0, round(np.max(model.target)*1.2, 2)) self.mixed = self.axes.plot(x, model.mixed, c="black", label=self.tr("Mixed"))[0] self.components = [self.axes.plot(x, distribution*fraction, c=plt.get_cmap()(i), label=model.component_prefix+str(i+1))[0] for i, (distribution, fraction) in enumerate(zip(model.distributions, model.fractions))] if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] colors = [plt.get_cmap()(i) for i in range(model.n_components)] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) if model.n_components < 6: self.axes.legend(loc="upper left") self.figure.tight_layout() self.canvas.draw() else: self.mixed.set_ydata(model.mixed) for comp, distribution, fraction in zip(self.components, model.distributions, model.fractions): comp.set_ydata(distribution*fraction) if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines.set_offsets(modes) self.canvas.draw() def show_result(self, result: SSUResult): if self.animation is not None: self.animation._stop() self.animation = None self.last_model = None self.last_result = result self.interval_label.setEnabled(True) self.interval_input.setEnabled(True) self.repeat_check_box.setEnabled(True) self.save_button.setEnabled(True) models = iter(result.view_models) first = next(models) x = self.transfer(first.classes_φ) self.axes.cla() if self.xlog: self.axes.set_xscale("log") self.axes.set_title(first.title) self.axes.set_xlabel(self.xlabel) self.axes.set_ylabel(self.ylabel) self.target = self.axes.plot(x, first.target, c="#ffffff00", marker=".", ms=8, mfc="black", mew=0.0)[0] self.axes.set_xlim(x[0], x[-1]) self.axes.set_ylim(0.0, round(np.max(first.target)*1.2, 2)) self.figure.tight_layout() # self.canvas.draw() colors = [plt.get_cmap()(i) for i in range(first.n_components)] def init(): model = first self.mixed = self.axes.plot(x, model.mixed, c="black")[0] self.components = [self.axes.plot(x, distribution*fraction, c=plt.get_cmap()(i))[0] for i, (distribution, fraction) in enumerate(zip(model.distributions, model.fractions))] if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) return self.mixed, self.vlines, *self.components def animate(current): model = current self.mixed.set_ydata(current.mixed) for line, distribution, fraction in zip(self.components, model.distributions, model.fractions): line.set_ydata(distribution*fraction) if self.show_mode: self.vlines.remove() modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) return self.mixed, self.vlines, *self.components self.animation = FuncAnimation(self.figure, animate, frames=models, init_func=init, interval=self.interval, blit=True, repeat=self.repeat, repeat_delay=3.0, save_count=result.n_iterations) def save_animation(self): if self.last_result is not None: filename, format_str = self.file_dialog.getSaveFileName(self, self.tr("Save the animation of this SSU result"), None, self.tr("MPEG-4 Video File (*.mp4);;Graphics Interchange Format (*.gif)")) if filename is None or filename == "": return progress = QProgressDialog(self) progress.setRange(0, 100) progress.setLabelText(self.tr("Saving Animation [{0} Frames]").format(self.last_result.n_iterations)) canceled = False def save_callback(i, n): if progress.wasCanceled(): nonlocal canceled canceled = True raise StopIteration() progress.setValue((i+1)/n*100) QCoreApplication.processEvents() self.show_result(self.last_result) # plt.rcParams["savefig.dpi"] = 120.0 if "*.gif" in format_str: if not ImageMagickWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText(self.tr("ImageMagick is not installed, please download and install it from its offical website (https://imagemagick.org/index.php).")) self.normal_msg.exec_() else: self.animation.save(filename, writer="imagemagick", fps=30, progress_callback=save_callback) elif "*.mp4" in format_str: if not FFMpegWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText(self.tr("FFMpeg is not installed, please download and install it from its offical website (https://ffmpeg.org/).")) self.normal_msg.exec_() else: self.animation.save(filename, writer="ffmpeg", fps=30, progress_callback=save_callback) # plt.rcParams["savefig.dpi"] = 300.0 if not canceled: progress.setValue(100)
class RenameOptionsView(QWidget): """View responsible for holding renaming options. Attributes: layout (QVBoxLayout): Main layout of view. frame_layout (QVBoxLayout: Layout of frame which holds options. frame (QFrame): Frame surrounding options. prefix_h_layout (QHBoxLayout): Layout holding prefix options. complete_rename_h_layout (QHBoxLayout): Layout holding complete rename options. search_and_replace_h_layout (QHBoxLayout): Layout holding search and replace options. renumber_h_layout (QHBoxLayout): Layout holding renumber options. remove_ext_h_layout (QHBoxLayout): Layout holding remove options. change_ext_h_layout (QHBoxLayout): Layout holding change extension options. create_backup_h_layout (QHBoxLayout): Layout holding backup options. preview_h_layout (QHBoxLayout): Layout holding preview options. start_lbl (QLabel): Label for renumbering start. padding_lbl (QLabel): Label for renumbering padding. add_prefix_cb (QCheckBox): Used to signify the user wants to add a prefix to the renaming. prefix (QLineEdit): prefix to add. complete_rename_cb (QCheckBox): Used to signify the user wants to completely rename the file. new_name (QLineEdit): New name used when renaming. search_and_replace_cb (QCheckBox): Used to signify the user wants to partially rename files. find (QLineEdit): When searching and replacing this is what the user wants to search for. replace (QLineEdit): When searching and replacing this is what the user wants to replace with. renumber_cb (QCheckBox): Used to signify the user wants to renumber while renaming. start_num (QSpinBox): Number to start with when renumbering files. padding (QComboBox): Padding to apply to renaming when renumbering files. dot_cb (QCheckBox): When checked a dot will be used to separate the renumber from the name. remove_ext_cb (QCheckBox): Used to signify the user wants to remove extensions when renaming. backup_files_cb (QCheckBox): Used to signify the user wants to backup old files before renaming. change_ext_cb (QCheckBox): Used to signify the user wants to change the extension while renaming. change_ext (QLineEdit): New extension to add to the renamed file. preview_cb (QCheckBox): Used to signify the user wants to preview the rename before renaming. """ def __init__(self): super(RenameOptionsView, self).__init__() self.layout = QVBoxLayout() self.frame_layout = QVBoxLayout() self.options_lbl = QLabel(prefs.OPTIONS) self.frame = QFrame() self.prefix_h_layout = QHBoxLayout() self.complete_rename_h_layout = QHBoxLayout() self.search_and_replace_h_layout = QHBoxLayout() self.renumber_h_layout = QHBoxLayout() self.remove_ext_h_layout = QHBoxLayout() self.change_ext_h_layout = QHBoxLayout() self.create_backup_h_layout = QHBoxLayout() self.preview_h_layout = QHBoxLayout() self.start_lbl = QLabel(prefs.START_NUM) self.padding_lbl = QLabel(prefs.PADDING) self.add_prefix_cb = QCheckBox(prefs.PREFIX) self.prefix = QLineEdit(prefs.PREFIX_DEFAULT) self.complete_rename_cb = QCheckBox(prefs.COMPLETE_RENAME) self.new_name = QLineEdit(prefs.COMPLETE_RENAME_DEFAULT) self.search_and_replace_cb = QCheckBox(prefs.SEARCH_AND_REPLACE) self.find = QLineEdit(prefs.SEARCH_AND_REPLACE_DEFAULT) self.replace = QLineEdit(prefs.REPLACE_WITH_DEFAULT) self.renumber_cb = QCheckBox(prefs.RENUMBER) self.start_num = QSpinBox() self.padding = QComboBox() self.dot_cb = QCheckBox(prefs.USE_DOT) self.remove_ext_cb = QCheckBox(prefs.REMOVE_EXT) self.backup_files_cb = QCheckBox(prefs.BACKUP) self.change_ext_cb = QCheckBox(prefs.CHANGE_EXT) self.change_ext = QLineEdit(prefs.CHANGE_EXT_DEFAULT) self.preview_cb = QCheckBox(prefs.PREVIEW) self._configure() def _configure(self) -> None: """Configure the RenameOptionsView.""" self.frame.setLayout(self.frame_layout) self.frame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.layout.addWidget(self.options_lbl) self.layout.addWidget(self.frame) self.add_prefix_cb.setToolTip(prefs.PREFIX_TOOLTIP) self.prefix.setDisabled(True) self.prefix.setMaximumWidth(prefs.PREFIX_WIDTH) self.prefix.setMinimumWidth(prefs.PREFIX_WIDTH) self.complete_rename_cb.setToolTip(prefs.COMPLETE_RENAME_TOOLTIP) self.new_name.setDisabled(True) self.new_name.setMaximumWidth(prefs.NEW_NAME_WIDTH) self.new_name.setMinimumWidth(prefs.NEW_NAME_WIDTH) self.search_and_replace_cb.setToolTip(prefs.SEARCH_AND_REPLACE_TOOLTIP) self.find.setDisabled(True) self.find.setMinimumWidth(prefs.FIND_WIDTH) self.find.setMaximumWidth(prefs.FIND_WIDTH) self.replace.setDisabled(True) self.replace.setMaximumWidth(prefs.REPLACE_WIDTH) self.replace.setMinimumWidth(prefs.REPLACE_WIDTH) self.renumber_cb.setToolTip(prefs.RENUMBER_TOOLTIP) self.start_num.setToolTip(prefs.START_NUM_TOOLTIP) self.start_num.setDisabled(True) self.start_num.setValue(prefs.START_NUM_DEFAULT) self.start_num.setMinimumWidth(prefs.START_NUM_MIN_WIDTH) self.padding.setToolTip(prefs.PADDING_TOOLTIP) self.padding.setDisabled(True) self.padding.addItems([str(x) for x in range(10)]) self.padding.setCurrentIndex(4) self.padding.setMinimumWidth(prefs.PADDING_MIN_WIDTH) self.dot_cb.setToolTip(prefs.USE_DOT_TOOLTIP) self.dot_cb.setDisabled(True) self.dot_cb.setChecked(True) self.dot_cb.setMinimumWidth(prefs.DOT_WIDTH) self.dot_cb.setMaximumWidth(prefs.DOT_WIDTH) self.remove_ext_cb.setToolTip(prefs.REMOVE_EXT_TOOLTIP) self.change_ext.setToolTip(prefs.CHANGE_EXT_TOOLTIP) self.change_ext.setDisabled(True) self.backup_files_cb.setToolTip(prefs.BACKUP_TOOLTIP) self.backup_files_cb.setChecked(True) self.preview_cb.setToolTip(prefs.PREVIEW_TOOLTIP) self.preview_cb.setChecked(True) self.prefix_h_layout.addWidget(self.add_prefix_cb) self.prefix_h_layout.addWidget(self.prefix) self.frame_layout.addLayout(self.prefix_h_layout) self.complete_rename_h_layout.addWidget(self.complete_rename_cb) self.complete_rename_h_layout.addWidget(self.new_name) self.frame_layout.addLayout(self.complete_rename_h_layout) self.search_and_replace_h_layout.addWidget(self.search_and_replace_cb) self.search_and_replace_h_layout.addWidget(self.find) self.search_and_replace_h_layout.addWidget(self.replace) self.frame_layout.addLayout(self.search_and_replace_h_layout) self.renumber_h_layout.addWidget(self.renumber_cb) self.renumber_h_layout.addStretch(1) self.renumber_h_layout.addWidget(self.start_lbl) self.renumber_h_layout.addWidget(self.start_num) self.renumber_h_layout.addSpacerItem(QSpacerItem(*prefs.SPACER_SIZE)) self.renumber_h_layout.addWidget(self.padding_lbl) self.renumber_h_layout.addWidget(self.padding) self.renumber_h_layout.addSpacerItem(QSpacerItem(*prefs.SPACER_SIZE)) self.renumber_h_layout.addWidget(self.dot_cb) self.frame_layout.addLayout(self.renumber_h_layout) self.change_ext_h_layout.addWidget(self.change_ext_cb) self.change_ext_h_layout.addWidget(self.change_ext) self.frame_layout.addLayout(self.change_ext_h_layout) self.remove_ext_h_layout.addWidget(self.remove_ext_cb) self.frame_layout.addLayout(self.remove_ext_h_layout) self.create_backup_h_layout.addWidget(self.backup_files_cb) self.frame_layout.addLayout(self.create_backup_h_layout) self.preview_h_layout.addWidget(self.preview_cb) self.frame_layout.addLayout(self.preview_h_layout) self.frame_layout.addSpacerItem(QSpacerItem(*prefs.SPACER_SIZE)) self.setLayout(self.layout) def disable_change_ext(self) -> None: """Disable change extension.""" self.change_ext.setDisabled(True) def disable_dot(self) -> None: """Disable dot checkbox.""" self.dot_cb.setDisabled(True) def disable_find(self) -> None: """Disable find.""" self.find.setDisabled(True) def disable_new_name(self) -> None: """Disable new name.""" print("disable new name") self.new_name.setDisabled(True) def disable_padding(self) -> None: """Disable padding.""" self.padding.setDisabled(True) def disable_prefix(self) -> None: """Disable prefix.""" self.prefix.setDisabled(True) def disable_start_num(self) -> None: """Disable start num.""" self.start_num.setDisabled(True) def disable_replace(self) -> None: """Disable replace.""" self.replace.setDisabled(True) def enable_change_ext(self) -> None: """Disable change extension.""" self.change_ext.setDisabled(False) def enable_dot(self) -> None: """Enable dot checkbox.""" self.dot_cb.setEnabled(True) def enable_find(self) -> None: """Enable find.""" self.find.setEnabled(True) def enable_new_name(self) -> None: """Enable new name.""" print("enable new name.") self.new_name.setEnabled(True) def enable_padding(self) -> None: """Enable padding.""" self.padding.setEnabled(True) def enable_prefix(self) -> None: """Enable prefix.""" self.prefix.setEnabled(True) def enable_replace(self) -> None: """Enable replace.""" self.replace.setEnabled(True) def enable_start_num(self) -> None: """Enable start num.""" self.start_num.setEnabled(True) def get_add_prefix(self) -> bool: """Return if end user wants to add a prefix and it is not the default value.""" result = self.get_prefix_checked() if result and self.get_prefix() == prefs.PREFIX_DEFAULT: result = False return result def get_do_backup(self) -> bool: """Return if end user wants to backup files.""" return self.backup_files_cb.isChecked() def get_change_ext(self) -> bool: """Return if the change extension checkbox is checked.""" return self.change_ext_cb.isChecked() def get_do_complete_rename(self) -> bool: """Get if end user wants to completely rename.""" return self.complete_rename_cb.isChecked() def get_dot(self) -> str: """Return dot string based on end users configuration. Note: If the end user has not enable using dot separators an empty string will be returned. """ return "." if self.get_do_dot() else "" def get_do_dot(self) -> bool: """Return if the end user wants to use dot separators when renaming.""" return self.dot_cb.isChecked() def get_do_change_ext(self) -> bool: """Return if the end user wants to change the extension.""" result = self.change_ext_cb.isChecked() if self.get_new_ext() == prefs.CHANGE_EXT_DEFAULT: return False return result def get_do_padding(self) -> bool: """Return if the end user wants to add padding.""" return False if self.get_padding() == 0 else True def get_do_preview(self) -> bool: """Return if the end user wants to preview changes.""" return self.preview_cb.isChecked() def get_do_rename(self) -> bool: """Return if end user wants to rename the full item and it is not the default value.""" result = self.complete_rename_cb.isChecked() if result and self.get_new_name() == prefs.COMPLETE_RENAME_DEFAULT: result = False return result def get_do_renumber(self) -> bool: """Return if the end user wants to renumber.""" return self.renumber_cb.isChecked() def get_do_search(self) -> bool: """Return if end user wants to perform a search and replace AND it is not the default values respectfully. Note: If you only want to know if search and replace is checked use get_search_and_replace. """ result = self.search_and_replace_cb.isChecked() if result and (self.get_find() == prefs.SEARCH_AND_REPLACE_DEFAULT or self.get_replace() == prefs.REPLACE_WITH_DEFAULT): result = False return result def get_do_search_and_replace(self) -> bool: """Return if end user wants to perform a search and replace.""" return self.search_and_replace_cb.isChecked() def get_find(self) -> str: """Return find value.""" return str(self.find.text()) def get_new_ext(self) -> str: """Return new ext.""" return str(self.change_ext.text()) def get_new_name(self) -> str: """Return new_name value.""" return str(self.new_name.text()) def get_padding(self) -> int: """Return the current padding value.""" return int(self.padding.currentText()) def get_prefix_checked(self) -> bool: """Return if the prefix checkbox is checked.""" return self.add_prefix_cb.isChecked() def get_prefix(self) -> str: """Return the current prefix value end user has entered.""" return str(self.prefix.text()) def get_remove_ext(self) -> bool: """Return if end user has checked the remove extension checkbox.""" return self.remove_ext_cb.isChecked() def get_replace(self) -> str: """Return the current replace value end user has entered.""" return str(self.replace.text()) def get_start_num(self) -> int: """Return start number from view.""" return int(self.start_num.value()) def set_change_ext_style(self, style: str) -> None: """Set style of change extension. Args: style: Style sheet applied to change extension. """ self.change_ext.setStyleSheet(style) def set_disabled(self) -> None: """Disable View.""" self.setDisabled(True) def set_enable(self) -> None: """Enable View.""" self.setEnabled(True) def set_find(self, value: str) -> None: """Set the value of find. Args: value: Value applied to find """ self.find.setText(value) def set_find_style(self, style: str) -> None: """Set style of find. Args: style: Style sheet applied to find. """ self.find.setStyleSheet(style) def set_new_name(self, value: str) -> None: """Set the value of new name. Args: value: Value applied to new_name """ self.new_name.setText(value) def set_new_name_style(self, style: str) -> None: """Set style of new_name. Args: style: Style sheet applied to new_name. """ self.new_name.setStyleSheet(style) def set_prefix(self, value: str) -> None: """Set the value of prefix. Args: value: Value applied to prefix """ self.prefix.setText(value) def set_prefix_style(self, style: str) -> None: """Set style of prefix. Args: style: Style sheet applied to prefix. """ self.prefix.setStyleSheet(style) def set_remove_ext(self, state: bool) -> None: """Set the remove_ext checkbox as checked or unchecked. Args: state: Check state of remove_ext. """ self.remove_ext_cb.setCheckState(Qt.Checked if state else Qt.Unchecked) def set_replace(self, value: str) -> None: """Set the value of replace. Args: value: Value applied to replace """ self.replace.setText(value) def set_replace_style(self, style: str) -> None: """Set style of replace. Args: style: Style sheet applied to replace. """ self.replace.setStyleSheet(style)
class Config(SignalNode.Config): """Config widget displayed for BandpassFilter.""" def __init__(self, parent=None): super().__init__(parent=parent) # Upper bound ---------------------------------------------------------------------------------------------- self.lower_bound_enable = QCheckBox() self.lower_bound_enable.setChecked(True) self.lower_bound_enable.stateChanged.connect(self.updateModel) self.lower_bound = QDoubleSpinBox() self.lower_bound.valueChanged.connect(self.updateModel) self.lower_bound.setMinimum(0) self.lower_bound.setMaximum(250) self.lower_bound.setSuffix(" Hz") layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) lower_bound_widget = QWidget() lower_bound_widget.setContentsMargins(0, 0, 0, 0) lower_bound_widget.setLayout(layout) layout.addWidget(self.lower_bound_enable) layout.addWidget(self.lower_bound) # Lower bound ---------------------------------------------------------------------------------------------- self.upper_bound_enable = QCheckBox() self.upper_bound_enable.setChecked(True) self.upper_bound_enable.stateChanged.connect(self.updateModel) self.upper_bound = QDoubleSpinBox() self.upper_bound.valueChanged.connect(self.updateModel) self.upper_bound.setMinimum(0) self.upper_bound.setMaximum(250) self.upper_bound.setSuffix(" Hz") layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) upper_bound_widget = QWidget() upper_bound_widget.setContentsMargins(0, 0, 0, 0) upper_bound_widget.setLayout(layout) layout.addWidget(self.upper_bound_enable) layout.addWidget(self.upper_bound) # Filter type and length ----------------------------------------------------------------------------------- self.filter_type = QComboBox() for name in BandpassFilter.filter_name_to_type: self.filter_type.addItem(name) self.filter_type.currentTextChanged.connect(self.updateModel) self.filter_length = QSpinBox() self.filter_length.setMinimum(2) self.filter_length.setMaximum(1000000) self.filter_length.setValue(1000) self.filter_length.valueChanged.connect(self.updateModel) self.filter_order = QSpinBox() self.filter_order.setRange(1, 4) self.filter_order.valueChanged.connect(self.updateModel) # ---------------------------------------------------------------------------------------------------------- layout = QFormLayout() layout.addRow("Lower bound:", lower_bound_widget) layout.addRow("Upper bound:", upper_bound_widget) layout.addRow("Filter type:", self.filter_type) layout.addRow("Filter order:", self.filter_order) layout.addRow("Filter length:", self.filter_length) self.setLayout(layout) def updateModel(self): n = self.node() if n is None: return if self.lower_bound_enable.isChecked(): lower_bound = self.lower_bound.value() else: lower_bound = None if self.upper_bound_enable.isChecked(): upper_bound = self.upper_bound.value() else: upper_bound = None filter_type = n.filter_name_to_type[self.filter_type.currentText()] filter_length = self.filter_length.value() filter_order = self.filter_order.value() n.setLowerBound(lower_bound) n.setUpperBound(upper_bound) n.setFilterType(filter_type) n.setFilterLength(filter_length) n.setFilterOrder(filter_order) def updateView(self): n = self.node() if n is None: return # Prevent view fields from emitting signals while they are updated self.lower_bound.blockSignals(True) self.upper_bound.blockSignals(True) self.filter_type.blockSignals(True) self.filter_length.blockSignals(True) self.filter_order.blockSignals(True) if n.upperBound() is None: self.upper_bound_enable.setChecked(False) else: self.upper_bound_enable.setChecked(True) self.upper_bound.setValue(n.upperBound()) if n.lowerBound() is None: self.lower_bound_enable.setChecked(False) else: self.lower_bound_enable.setChecked(True) self.lower_bound.setValue(n.lowerBound()) self.filter_type.setCurrentText( n.filter_type_to_name[n.filterType()]) self.filter_length.setValue(n.filterLength()) self.filter_order.setValue(n.filterOrder()) # Release the block and call adjust self.lower_bound.blockSignals(False) self.upper_bound.blockSignals(False) self.filter_type.blockSignals(False) self.filter_length.blockSignals(False) self.filter_order.blockSignals(False) self._adjust() def _adjust(self): """Adjust displayed values and limits in response to changes.""" # Enable spinbox widgets based on their checkbox self.lower_bound.setEnabled(self.lower_bound_enable.isChecked()) self.upper_bound.setEnabled(self.upper_bound_enable.isChecked()) # Adjust min and max so that lower_bound is never higher than upper_bound if self.lower_bound_enable.isChecked(): self.upper_bound.setMinimum(self.lower_bound.value()) else: self.upper_bound.setMinimum(0) if self.upper_bound_enable.isChecked(): self.lower_bound.setMaximum(self.upper_bound.value()) else: self.lower_bound.setMaximum(250) if self.filter_type.currentText() == "Butterworth": self.filter_order.setEnabled(True) else: self.filter_order.setEnabled(False)
class SSUResolverPanel(QDialog): def __init__(self, parent=None): super().__init__(parent=parent, f=Qt.Window) self.setWindowTitle(self.tr("SSU Resolver")) self.distribution_types = [ (DistributionType.Normal, self.tr("Normal")), (DistributionType.Weibull, self.tr("Weibull")), (DistributionType.SkewNormal, self.tr("Skew Normal")) ] self.load_dataset_dialog = LoadDatasetDialog(parent=self) self.load_dataset_dialog.dataset_loaded.connect(self.on_dataset_loaded) self.classic_setting = ClassicResolverSettingWidget(parent=self) self.neural_setting = NNResolverSettingWidget(parent=self) self.manual_panel = ManualFittingPanel(parent=self) self.manual_panel.manual_fitting_finished.connect( self.on_fitting_succeeded) self.async_worker = AsyncWorker() self.async_worker.background_worker.task_succeeded.connect( self.on_fitting_succeeded) self.async_worker.background_worker.task_failed.connect( self.on_fitting_failed) self.normal_msg = QMessageBox(self) self.dataset = None self.task_table = {} self.task_results = {} self.failed_task_ids = [] self.__continuous_flag = False self.init_ui() def init_ui(self): self.setAttribute(Qt.WA_StyledBackground, True) self.main_layout = QGridLayout(self) # self.main_layout.setContentsMargins(0, 0, 0, 0) # control group self.control_group = QGroupBox(self.tr("Control")) self.control_layout = QGridLayout(self.control_group) self.resolver_label = QLabel(self.tr("Resolver")) self.resolver_combo_box = QComboBox() self.resolver_combo_box.addItems(["classic", "neural"]) self.control_layout.addWidget(self.resolver_label, 0, 0) self.control_layout.addWidget(self.resolver_combo_box, 0, 1) self.load_dataset_button = QPushButton(qta.icon("fa.database"), self.tr("Load Dataset")) self.load_dataset_button.clicked.connect(self.on_load_dataset_clicked) self.configure_fitting_button = QPushButton( qta.icon("fa.gears"), self.tr("Configure Fitting Algorithm")) self.configure_fitting_button.clicked.connect( self.on_configure_fitting_clicked) self.control_layout.addWidget(self.load_dataset_button, 1, 0) self.control_layout.addWidget(self.configure_fitting_button, 1, 1) self.distribution_label = QLabel(self.tr("Distribution Type")) self.distribution_combo_box = QComboBox() self.distribution_combo_box.addItems( [name for _, name in self.distribution_types]) self.component_number_label = QLabel(self.tr("N<sub>components</sub>")) self.n_components_input = QSpinBox() self.n_components_input.setRange(1, 10) self.n_components_input.setValue(3) self.control_layout.addWidget(self.distribution_label, 2, 0) self.control_layout.addWidget(self.distribution_combo_box, 2, 1) self.control_layout.addWidget(self.component_number_label, 3, 0) self.control_layout.addWidget(self.n_components_input, 3, 1) self.n_samples_label = QLabel(self.tr("N<sub>samples</sub>")) self.n_samples_display = QLabel(self.tr("Unknown")) self.control_layout.addWidget(self.n_samples_label, 4, 0) self.control_layout.addWidget(self.n_samples_display, 4, 1) self.sample_index_label = QLabel(self.tr("Sample Index")) self.sample_index_input = QSpinBox() self.sample_index_input.valueChanged.connect( self.on_sample_index_changed) self.sample_index_input.setEnabled(False) self.control_layout.addWidget(self.sample_index_label, 5, 0) self.control_layout.addWidget(self.sample_index_input, 5, 1) self.sample_name_label = QLabel(self.tr("Sample Name")) self.sample_name_display = QLabel(self.tr("Unknown")) self.control_layout.addWidget(self.sample_name_label, 6, 0) self.control_layout.addWidget(self.sample_name_display, 6, 1) self.manual_test_button = QPushButton(qta.icon("fa.sliders"), self.tr("Manual Test")) self.manual_test_button.setEnabled(False) self.manual_test_button.clicked.connect(self.on_manual_test_clicked) self.load_reference_button = QPushButton(qta.icon("mdi.map-check"), self.tr("Load Reference")) self.load_reference_button.clicked.connect( lambda: self.reference_view.load_dump(mark_ref=True)) self.control_layout.addWidget(self.manual_test_button, 7, 0) self.control_layout.addWidget(self.load_reference_button, 7, 1) self.single_test_button = QPushButton(qta.icon("fa.play-circle"), self.tr("Single Test")) self.single_test_button.setEnabled(False) self.single_test_button.clicked.connect(self.on_single_test_clicked) self.continuous_test_button = QPushButton( qta.icon("mdi.playlist-play"), self.tr("Continuous Test")) self.continuous_test_button.setEnabled(False) self.continuous_test_button.clicked.connect( self.on_continuous_test_clicked) self.control_layout.addWidget(self.single_test_button, 8, 0) self.control_layout.addWidget(self.continuous_test_button, 8, 1) self.test_previous_button = QPushButton( qta.icon("mdi.skip-previous-circle"), self.tr("Test Previous")) self.test_previous_button.setEnabled(False) self.test_previous_button.clicked.connect( self.on_test_previous_clicked) self.test_next_button = QPushButton(qta.icon("mdi.skip-next-circle"), self.tr("Test Next")) self.test_next_button.setEnabled(False) self.test_next_button.clicked.connect(self.on_test_next_clicked) self.control_layout.addWidget(self.test_previous_button, 9, 0) self.control_layout.addWidget(self.test_next_button, 9, 1) # chart group self.chart_group = QGroupBox(self.tr("Chart")) self.chart_layout = QGridLayout(self.chart_group) self.result_chart = MixedDistributionChart(show_mode=True, toolbar=False) self.chart_layout.addWidget(self.result_chart, 0, 0) # table group self.table_group = QGroupBox(self.tr("Table")) self.reference_view = ReferenceResultViewer() self.result_view = FittingResultViewer(self.reference_view) self.result_view.result_marked.connect( lambda result: self.reference_view.add_references([result])) self.table_tab = QTabWidget() self.table_tab.addTab(self.result_view, qta.icon("fa.cubes"), self.tr("Result")) self.table_tab.addTab(self.reference_view, qta.icon("fa5s.key"), self.tr("Reference")) self.result_layout = QGridLayout(self.table_group) self.result_layout.addWidget(self.table_tab, 0, 0) # pack all group self.splitter1 = QSplitter(Qt.Orientation.Vertical) self.splitter1.addWidget(self.control_group) self.splitter1.addWidget(self.chart_group) self.splitter2 = QSplitter(Qt.Orientation.Horizontal) self.splitter2.addWidget(self.splitter1) self.splitter2.addWidget(self.table_group) self.main_layout.addWidget(self.splitter2, 0, 0) @property def distribution_type(self) -> DistributionType: distribution_type, _ = self.distribution_types[ self.distribution_combo_box.currentIndex()] return distribution_type @property def n_components(self) -> int: return self.n_components_input.value() 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_load_dataset_clicked(self): self.load_dataset_dialog.show() def on_dataset_loaded(self, dataset: GrainSizeDataset): self.dataset = dataset self.n_samples_display.setText(str(dataset.n_samples)) self.sample_index_input.setRange(1, dataset.n_samples) self.sample_index_input.setEnabled(True) self.manual_test_button.setEnabled(True) self.single_test_button.setEnabled(True) self.continuous_test_button.setEnabled(True) self.test_previous_button.setEnabled(True) self.test_next_button.setEnabled(True) def on_configure_fitting_clicked(self): if self.resolver_combo_box.currentText() == "classic": self.classic_setting.show() else: self.neural_setting.show() def on_sample_index_changed(self, index): self.sample_name_display.setText(self.dataset.samples[index - 1].name) def generate_task(self, query_ref=True): sample_index = self.sample_index_input.value() - 1 sample = self.dataset.samples[sample_index] resolver = self.resolver_combo_box.currentText() if resolver == "classic": setting = self.classic_setting.setting else: setting = self.neural_setting.setting query = self.reference_view.query_reference(sample) # type: SSUResult if not query_ref or query is None: task = SSUTask(sample, self.distribution_type, self.n_components, resolver=resolver, resolver_setting=setting) else: keys = ["mean", "std", "skewness"] # reference = [{key: comp.logarithmic_moments[key] for key in keys} for comp in query.components] task = SSUTask( sample, query.distribution_type, query.n_components, resolver=resolver, resolver_setting=setting, # reference=reference) initial_guess=query.last_func_args) return task def on_fitting_succeeded(self, fitting_result: SSUResult): # update chart self.result_chart.show_model(fitting_result.view_model) self.result_view.add_result(fitting_result) self.task_results[fitting_result.task.uuid] = fitting_result if self.__continuous_flag: if self.sample_index_input.value() == self.dataset.n_samples: self.on_continuous_test_clicked() else: self.sample_index_input.setValue( self.sample_index_input.value() + 1) self.do_test() return self.manual_test_button.setEnabled(True) self.single_test_button.setEnabled(True) self.continuous_test_button.setEnabled(True) self.test_previous_button.setEnabled(True) self.test_next_button.setEnabled(True) def on_fitting_failed(self, failed_info: str, task: SSUTask): self.failed_task_ids.append(task.uuid) if self.__continuous_flag: self.on_continuous_test_clicked() self.manual_test_button.setEnabled(True) self.single_test_button.setEnabled(True) self.continuous_test_button.setEnabled(True) self.test_previous_button.setEnabled(True) self.test_next_button.setEnabled(True) self.show_error(failed_info) def do_test(self): self.manual_test_button.setEnabled(False) self.single_test_button.setEnabled(False) self.test_previous_button.setEnabled(False) self.test_next_button.setEnabled(False) if not self.__continuous_flag: self.continuous_test_button.setEnabled(False) task = self.generate_task() self.task_table[task.uuid] = task self.async_worker.execute_task(task) def on_manual_test_clicked(self): task = self.generate_task(query_ref=False) self.manual_panel.setup_task(task) self.manual_panel.show() def on_single_test_clicked(self): self.do_test() def on_continuous_test_clicked(self): if self.__continuous_flag: self.__continuous_flag = not self.__continuous_flag self.continuous_test_button.setText(self.tr("Continuous Test")) else: self.continuous_test_button.setText(self.tr("Cancel")) self.__continuous_flag = not self.__continuous_flag self.do_test() def on_test_previous_clicked(self): current = self.sample_index_input.value() if current == 1: return self.sample_index_input.setValue(current - 1) self.do_test() def on_test_next_clicked(self): current = self.sample_index_input.value() if current == self.dataset.n_samples: return self.sample_index_input.setValue(current + 1) self.do_test()
class ElaWidget(ToolWidget): def __init__(self, image, parent=None): super(ElaWidget, self).__init__(parent) self.quality_spin = QSpinBox() self.quality_spin.setRange(0, 100) self.quality_spin.setSuffix(self.tr(' %')) self.quality_spin.setToolTip(self.tr('JPEG reference quality level')) self.scale_spin = QSpinBox() self.scale_spin.setRange(1, 100) self.scale_spin.setSuffix(' %') self.scale_spin.setToolTip(self.tr('Output multiplicative gain')) self.contrast_spin = QSpinBox() self.contrast_spin.setRange(0, 100) self.contrast_spin.setSuffix(' %') self.contrast_spin.setToolTip(self.tr('Output tonality compression')) self.equalize_check = QCheckBox(self.tr('Equalized')) self.equalize_check.setToolTip(self.tr('Apply histogram equalization')) self.gray_check = QCheckBox(self.tr('Grayscale')) self.gray_check.setToolTip(self.tr('Desaturated output')) default_button = QPushButton(self.tr('Default')) default_button.setToolTip(self.tr('Revert to default parameters')) params_layout = QHBoxLayout() params_layout.addWidget(QLabel(self.tr('Quality:'))) params_layout.addWidget(self.quality_spin) params_layout.addWidget(QLabel(self.tr('Scale:'))) params_layout.addWidget(self.scale_spin) params_layout.addWidget(QLabel(self.tr('Contrast:'))) params_layout.addWidget(self.contrast_spin) params_layout.addWidget(self.equalize_check) params_layout.addWidget(self.gray_check) params_layout.addWidget(default_button) params_layout.addStretch() self.image = image self.original = image.astype(np.float32) / 255 self.viewer = ImageViewer(self.image, self.image) self.default() self.quality_spin.valueChanged.connect(self.process) self.scale_spin.valueChanged.connect(self.process) self.contrast_spin.valueChanged.connect(self.process) self.equalize_check.stateChanged.connect(self.process) self.gray_check.stateChanged.connect(self.process) default_button.clicked.connect(self.default) main_layout = QVBoxLayout() main_layout.addLayout(params_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) def process(self): start = time() quality = self.quality_spin.value() scale = self.scale_spin.value() / 20 contrast = int(self.contrast_spin.value() / 100 * 128) equalize = self.equalize_check.isChecked() grayscale = self.gray_check.isChecked() self.scale_spin.setEnabled(not equalize) self.contrast_spin.setEnabled(not equalize) compressed = compress_img(self.image, quality).astype(np.float32) / 255 difference = cv.absdiff(self.original, compressed) if equalize: ela = equalize_img((difference * 255).astype(np.uint8)) else: ela = cv.convertScaleAbs(cv.sqrt(difference) * 255, None, scale) ela = cv.LUT(ela, create_lut(contrast, contrast)) if grayscale: ela = desaturate(ela) self.viewer.update_processed(ela) self.info_message.emit( self.tr('Error Level Analysis = {}'.format(elapsed_time(start)))) def default(self): self.blockSignals(True) self.equalize_check.setChecked(False) self.gray_check.setChecked(False) self.quality_spin.setValue(75) self.scale_spin.setValue(50) self.contrast_spin.setValue(20) self.process() self.blockSignals(False)
class OscGui(QWidget): triggerEnable = 0 ADCEnable = 0 def __init__(self, device): QWidget.__init__(self) self.setWindowIcon(QIcon("icon/silabslogo.png")) self.device = device screen = QDesktopWidget() self.m_width = screen.width() self.m_height = screen.height() self.resize(self.m_width, self.m_height) self.showMaximized() self.setWindowTitle("Silabs") self.setObjectName("mainWindow") self.setStyleSheet("#mainWindow{background-color:rgb(54, 56, 60);}") self.layout = QtWidgets.QHBoxLayout() self.layout.setContentsMargins(self.m_width / 200, self.m_width / 200, self.m_width / 200, self.m_width / 20) self.layout.setSpacing(10) self.create_left_frame() self.create_right_frame() self.layout.setStretch(0, 3) self.layout.setStretch(1, 1) self.setLayout(self.layout) if self.device: logging.debug("device name:" + str(self.device)) self.readThread = USBReadThread(self.device) self.readThread.updateWaveForm.connect(self.update_canvas) self.readThread.updateFrequency.connect(self.update_frequency) self.readThread.singleTrigger.connect(self.single_trigger_event) self.readThread.start() self.frequencyGet = Protocol.PREAMBLE.value \ + Protocol.FREQUENCY_GET.value \ + '\x00' # '\xAA' + '\x41' + '\x00' self.setThread = USBWriteThread(self.device, self.frequencyGet) self.setThread.start() def create_left_frame(self): self.leftlayout = QtWidgets.QVBoxLayout() self.leftlayout.setContentsMargins(self.m_width / 200, self.m_width / 200, self.m_width / 200, self.m_width / 200) self.canvasFrame = CanvasFrame() # self.canvasFrame.setFrameStyle(QtWidgets.QFrame.StyledPanel | QtWidgets.QFrame.Plain) self.canvasFrame.setStyleSheet( "border-radius:10px;background-color:rgb(40, 38, 39);") self.leftlayout.addWidget(self.canvasFrame) self.navigationBarFrame = QtWidgets.QFrame() self.navigationBarFrame.setObjectName("NBF") self.navigationBarFrame.setStyleSheet( "#NBF{border-radius:10px;" "background-color:rgb(200, 200, 200);}") self.navigationBarLayout = QtWidgets.QVBoxLayout( self.navigationBarFrame) self.playBarLayout = QtWidgets.QHBoxLayout(self.navigationBarFrame) self.playBarLayout.setSpacing(self.m_width / 40) self.playBarLayout.addStretch(1) self.zoomInButton = QtWidgets.QPushButton() self.zoomInButton.setObjectName("zoomInButton") self.zoomInButton.setMaximumSize(self.m_width / 40, self.m_width / 40) self.zoomInButton.setMinimumSize(self.m_width / 40, self.m_width / 40) self.zoomInButton.setStyleSheet( "QPushButton{border-radius:25px;" "border-image:url(icon/ZoomIn.png);}" "QPushButton:hover{border-radius:25px;" "border-image:url(icon/ZoomIn2.png);" "background-colora:rgb(50, 50, 50,0);}") self.zoomInButton.clicked.connect(self.zoom_in_event) self.playBarLayout.addWidget(self.zoomInButton) self.zoomOutButton = QtWidgets.QPushButton() self.zoomOutButton.setObjectName("zoomOutButton") self.zoomOutButton.setMaximumSize(self.m_width / 40, self.m_width / 40) self.zoomOutButton.setMinimumSize(self.m_width / 40, self.m_width / 40) self.zoomOutButton.setIcon(QIcon("icon/ZoomOut.png")) self.zoomOutButton.setIconSize( QSize(self.m_width / 40, self.m_width / 40)) self.zoomOutButton.setStyleSheet( "QPushButton{border-radius:%dpx;}" "QPushButton:hover{border:1 solid red;" "border-radius:%dpx;" "background-color:rgb(250, 100, 100);}" % (self.m_width / 80, self.m_width / 80)) self.zoomOutButton.clicked.connect(self.zoom_out_event) self.playBarLayout.addWidget(self.zoomOutButton) self.playButton = QtWidgets.QPushButton() self.playButton.setObjectName("playButton") self.playButton.setFixedSize(self.m_width / 20, self.m_width / 20) # self.playButton.setStyleSheet("border-radius:%dpx;" % self.m_width/40) self.playButton.setStyleSheet("QPushButton{border:none;" "border-radius:%dpx;" "background-color:" "rgb(200, 100, 100);}" "QPushButton:hover{border:0;" "border-radius:%dpx;" "background-color:rgb(250, 100, 100);}" % (self.m_width / 40, self.m_width / 40)) self.playButton.clicked.connect(self.play_button_event) self.playBarLayout.addWidget(self.playButton) self.triggerLayout = QtWidgets.QHBoxLayout(self.navigationBarFrame) self.triggerLayout.setContentsMargins(0, 0, 0, 0) self.triggerLayout.setSpacing(1) self.triggerEnable = 0 self.triggerButton = QtWidgets.QPushButton(self.navigationBarFrame) # self.triggerButton.setObjectName("triggerButton") self.triggerButton.setFixedSize(self.m_width / 12, self.m_height / 20) self.triggerButton.setFont(QFont("New Time Roman", 10)) self.triggerButton.setText("Single Trigger") self.triggerButton.setStyleSheet( "QPushButton{border-top-left-radius:5px;" "border-bottom-left-radius: 5px;" "background-color:rgba(100, 100, 100,255);" "color:rgb(200, 200, 200);}" "QPushButton:hover{color: rgb(100, 200, 100);font-weight:bold}") self.triggerButton.clicked.connect(self.trigger_event) self.triggerLayout.addWidget(self.triggerButton) self.triggerValue = 0 self.triggerSpinBox = QSpinBox(self.navigationBarFrame) self.triggerSpinBox.setFixedSize(self.m_width / 10, self.m_height / 20) self.triggerSpinBox.setMaximum(3300) self.triggerSpinBox.setMinimum(1) self.triggerSpinBox.setFont(QFont("New Time Roman", 10)) self.triggerSpinBox.setStyleSheet( "QSpinBox{border-top-right-radius:5px;" "border-bottom-left-right: 5px;" "background-color:rgb(100, 100, 100);" "color:rgb(200, 200, 200);}" "QSpinBox:drop{subcontrol-origin: padding;" "subcontrol-position: top right;" "width: 50px;border-left-style:solid;" "border-top-right-radius: 3px;" "border-bottom-right-radius: 3px;" "border-left: 2px solid gray;" "background-color: rgba(100, 25, 100, 0);}") self.triggerSpinBox.setMinimumWidth(30) self.triggerLayout.addWidget(self.triggerSpinBox) self.playBarLayout.addLayout(self.triggerLayout) self.playBarLayout.addStretch(1) self.navigationBarLayout.addLayout(self.playBarLayout) self.navigationBarLine = QtWidgets.QFrame(self.navigationBarFrame) self.navigationBarLine.setAutoFillBackground(True) self.navigationBarLine.setFixedHeight(1) self.navigationBarLine.setStyleSheet( "background-color:rgb(150, 150, 150);") self.navigationBarLayout.addWidget(self.navigationBarLine) # self.navigationBarLayout.addStretch(1) self.leftlayout.addWidget(self.navigationBarFrame) self.leftlayout.setStretch(0, 5) self.leftlayout.setStretch(1, 1) self.leftlayout.setStretch(2, 1) self.leftlayout.setSpacing(self.m_width / 40) self.layout.addLayout(self.leftlayout) def create_right_frame(self): self.rightlayout = QtWidgets.QVBoxLayout() self.rightlayout.setContentsMargins(self.m_width / 200, self.m_width / 200, self.m_width / 200, self.m_width / 200) self.dialogFrame = QtWidgets.QFrame() self.dialogFrame.setObjectName("dialogFrame") # self.dialogFrame.setAutoFillBackground(True) # self.dialogFrame.setStyleSheet("#dialogFrame{border-radius:10px;" # "background-color:rgb(255, 100, 100);}") self.dialogFrame.setStyleSheet("#dialogFrame{border-radius:10px;" "background-color:rgb(10, 10, 10);}") self.dialoglayout = QtWidgets.QVBoxLayout(self.dialogFrame) self.dialoglayout.setContentsMargins(0, self.m_width / 40, 0, self.m_width / 40) # left, top, right, bottom self.dialoglayout.setSpacing(self.m_width / 40) self.dialoglayout.addStretch(2) self.ledLayout = QtWidgets.QHBoxLayout(self.dialogFrame) self.ledLayout.addStretch(1) self.redLedState = 0 self.redLedSwitch = QtWidgets.QPushButton(self.dialogFrame) self.redLedSwitch.setObjectName("redLedSwitch") self.redLedSwitch.setIcon(QIcon("icon/switchOFF.png")) self.redLedSwitch.setIconSize( QSize(self.m_width / 40, self.m_width / 40 / 1.75)) self.redLedSwitch.setFixedSize(self.m_width / 40, self.m_width / 40 / 1.75) self.redLedSwitch.setStyleSheet("#redLedSwitch{border:none;}") self.redLedSwitch.clicked.connect(self.red_led_switch_event) self.ledLayout.addWidget(self.redLedSwitch) self.ledLayout.addStretch(1) self.greenLedState = 0 self.greenLedSwitch = QtWidgets.QPushButton(self.dialogFrame) self.greenLedSwitch.setObjectName("greenLedSwitch") self.greenLedSwitch.setIcon(QIcon("icon/switchOFF.png")) self.greenLedSwitch.setIconSize( QSize(self.m_width / 40, self.m_width / 40 / 1.75)) self.greenLedSwitch.setFixedSize(self.m_width / 40, self.m_width / 40 / 1.75) self.greenLedSwitch.setStyleSheet("#greenLedSwitch{border:none;}") self.greenLedSwitch.clicked.connect(self.green_led_switch_event) self.ledLayout.addWidget(self.greenLedSwitch) self.ledLayout.addStretch(1) self.blueLedState = 0 self.blueLedSwitch = QtWidgets.QPushButton(self.dialogFrame) self.blueLedSwitch.setObjectName("blueLedSwitch") self.blueLedSwitch.setIcon(QIcon("icon/switchOFF.png")) self.blueLedSwitch.setIconSize( QSize(self.m_width / 40, self.m_width / 40 / 1.75)) self.blueLedSwitch.setFixedSize(self.m_width / 40, self.m_width / 40 / 1.75) self.blueLedSwitch.setStyleSheet("#blueLedSwitch{border:none;}") self.blueLedSwitch.clicked.connect(self.blue_led_switch_event) self.ledLayout.addWidget(self.blueLedSwitch) self.ledLayout.addStretch(1) self.dialoglayout.addLayout(self.ledLayout) self.dialoglayout.addStretch(1) self.dialogLine = QtWidgets.QFrame(self.dialogFrame) self.dialogLine.setAutoFillBackground(True) self.dialogLine.setFixedHeight(1) self.dialogLine.setStyleSheet("background-color:rgb(50, 50, 50);") self.dialoglayout.addWidget(self.dialogLine) self.channelLayout = QtWidgets.QHBoxLayout(self.dialogFrame) self.channelLayout.setSpacing(0) self.channelLayout.setContentsMargins(self.m_width / 100, 0, self.m_width / 100, 0) # self.channelLayout.addStretch(1) self.channel1Enable = 0 self.channel1Button = QPushButton(self.dialogFrame) self.channel1Button.setFixedHeight(self.m_width / 40) self.channel1Button.setStyleSheet( "QPushButton{border:1px solid rgb(200,200,200);" "border-top-left-radius:5px;" "border-bottom-left-radius: 5px;" "background-color:rgba(100, 100, 100,0);" "color:rgb(200, 200, 200);" "padding: 1px 20px;}" "QPushButton:hover{font-weight:bold;}") self.channel1Button.setFont(QFont("New Time Roman", 10)) self.channel1Button.setText("Channel_1") self.channel1Button.clicked.connect(self.channel1_button_event) self.channelLayout.addWidget(self.channel1Button) self.channel2Enable = 0 self.channel2Button = QPushButton(self.dialogFrame) self.channel2Button.setFixedHeight(self.m_width / 40) self.channel2Button.setStyleSheet( "QPushButton{border:1px solid rgb(200,200,200);" "border-top-right-radius: 5px;" "border-bottom-right-radius:5px;" "background-color:rgba(100, 100, 100,0);" "color:rgb(200, 200, 200);" "padding: 1px 20px;}" "QPushButton:hover{font-weight:bold;}") self.channel2Button.setFont(QFont("New Time Roman", 10)) self.channel2Button.setText("Channel_2") self.channel2Button.clicked.connect(self.channel2_button_event) self.channelLayout.addWidget(self.channel2Button) # self.channelLayout.addStretch(1) self.dialoglayout.addLayout(self.channelLayout) self.dialogLine2 = QtWidgets.QFrame(self.dialogFrame) self.dialogLine2.setAutoFillBackground(True) self.dialogLine2.setFixedHeight(1) self.dialogLine2.setStyleSheet("background-color:rgb(50, 50, 50);") self.dialoglayout.addWidget(self.dialogLine2) self.configutatorLayout = QtWidgets.QVBoxLayout(self.dialogFrame) self.configutatorLayout.setContentsMargins(20, 0, 20, 0) self.configutatorLayout.setSpacing(self.m_width / 100) self.frenquencyComboBox = QtWidgets.QComboBox() self.frenquencyComboBox.setObjectName("frenquencyComboBox") self.frenquencyComboBox.setFixedHeight(self.m_width / 40) self.frenquencyComboBox.setFont(QFont("New Time Roman", 10)) self.frenquencyComboBox.setStyleSheet( "QComboBox{border-radius:5px;" "background-color:rgb(200, 200, 200);" "color:rgb(0, 0, 0);" "padding: 1px 20px;}" "QComboBox:drop-down{subcontrol-origin: padding;" "subcontrol-position: top right;" "width: 50px;border-left-style:solid;" "border-top-right-radius: 3px;" "border-bottom-right-radius: 3px;" "border-left: 2px solid gray;" "background-color: rgba(100, 100, 100, 0);}" "QComboBox:down-arrow{border-image:url(icon/arrow-1.png);}") self.frenquencyComboBox.addItem("4Hz") self.frenquencyComboBox.addItem("10Hz") self.frenquencyComboBox.addItem("100Hz") self.frenquencyComboBox.addItem("1000Hz") self.frenquencyComboBox.setCurrentText("1000Hz") self.frenquencyComboBox.setFont(QFont("New Time Roman", 10)) self.configutatorLayout.addWidget(self.frenquencyComboBox) self.setButton = QPushButton(self.dialogFrame) self.setButton.setObjectName("setButton") self.setButton.setFixedHeight(self.m_width / 40) self.setButton.setStyleSheet( "QPushButton{border-radius:%dpx;" "background-color:rgb(150, 255, 150);" "color:rgb(0, 0, 0);" "text-align: center center;}" "QPushButton:hover{background-color:" "rgb(100, 255, 100);color:rgb(255, 100, 100);" "font-size:20px}" % (self.m_width / 80)) self.setButton.setFont(QFont("New Time Roman", 12, QFont.Bold)) self.setButton.setText("set") self.setButton.clicked.connect(self.set_button_event) self.setButton.setEnabled(True) self.configutatorLayout.addWidget(self.setButton) self.dialoglayout.addLayout(self.configutatorLayout) self.dialoglayout.addStretch(1) self.rightlayout.addWidget(self.dialogFrame) self.stateFrame = QtWidgets.QFrame() self.stateFrame.setObjectName("stateFrame") # self.dialogFrame.setAutoFillBackground(True) self.stateFrame.setStyleSheet( "QFrame{border:2px solid rgb(200, 200, 200);" "border-radius:10px;" "background-color:rgb(200, 200, 200);}") self.statelayout = QtWidgets.QGridLayout(self.stateFrame) self.statelayout.setContentsMargins(self.m_width / 40, self.m_width / 40, self.m_width / 40, self.m_width / 40) self.enableLabelKey = QLabel(self.stateFrame) self.enableLabelKey.setText("state:") self.enableLabelKey.setFont(QFont("New Time Roman", 10, QFont.Bold)) self.statelayout.addWidget(self.enableLabelKey, 0, 0) self.enableLabelValue = QLabel(self.stateFrame) self.enableLabelValue.setText("Stop") self.enableLabelValue.setFont(QFont("New Time Roman", 10, QFont.Bold)) self.statelayout.addWidget(self.enableLabelValue, 0, 1) self.frequencyLabelKey = QLabel(self.stateFrame) self.frequencyLabelKey.setText("frequency:") self.frequencyLabelKey.setFont(QFont("New Time Roman", 10, QFont.Bold)) self.statelayout.addWidget(self.frequencyLabelKey, 1, 0) self.frequencyLabelValue = QLabel(self.stateFrame) self.frequencyLabelValue.setText( str(self.canvasFrame.frequency) + "Hz") self.frequencyLabelValue.setFont( QFont("New Time Roman", 10, QFont.Bold)) self.statelayout.addWidget(self.frequencyLabelValue, 1, 1) self.bitModeLabelKey = QLabel(self.stateFrame) self.bitModeLabelKey.setText("BitMode:") self.bitModeLabelKey.setFont(QFont("New Time Roman", 10, QFont.Bold)) self.statelayout.addWidget(self.bitModeLabelKey, 2, 0) self.bitModeLabelValue = QLabel(self.stateFrame) self.bitModeLabelValue.setText("8 bit") self.bitModeLabelValue.setFont(QFont("New Time Roman", 10, QFont.Bold)) self.statelayout.addWidget(self.bitModeLabelValue, 2, 1) self.rightlayout.addWidget(self.stateFrame) self.rightlayout.addStretch(1) self.layout.addLayout(self.rightlayout) def trigger_event(self): if self.device is None: logging.error("no device!!!") return if self.triggerEnable: self.triggerButton.setStyleSheet( "QPushButton{border-top-left-radius:5px;" "border-bottom-left-radius: 5px;" "background-color:rgba(100, 100, 100,255);}" "QPushButton:hover{color: rgb(100, 200, 100);font-weight:bold}" ) self.triggerEnable = 0 self.triggerValue = 0 self.readThread.triggerEnable = 0 self.readThread.triggerValue = 0 self.triggerSpinBox.setEnabled(True) self.triggerSpinBox.setStyleSheet( "QSpinBox{border-top-right-radius:5px;" "border-bottom-left-right: 5px;" "background-color:rgb(100, 100, 100);" "color:rgb(200, 200, 200);}") else: self.triggerButton.setStyleSheet( "QPushButton{border:1px solid rgb(200,200,200);" "border-top-left-radius:5px;" "border-bottom-left-radius: 5px;" "background-color:rgba(100, 200, 100);}" "QPushButton:hover{color: rgb(100, 100, 100);font-weight:bold}" ) self.triggerEnable = 1 self.triggerValue = self.triggerSpinBox.value() self.readThread.triggerEnable = 1 self.readThread.triggerValue = self.triggerValue self.triggerSpinBox.setEnabled(False) self.triggerSpinBox.setStyleSheet( "QSpinBox{border-top-right-radius:5px;" "border-bottom-left-right: 5px;" "background-color:rgb(150, 150, 150);}") logging.debug("triggerButtonEvent:" + str(self.readThread.triggerEnable)) def zoom_in_event(self): if self.canvasFrame.scaleRatio == 1: self.canvasFrame.scaleRatio = 1 else: self.canvasFrame.scaleRatio = self.canvasFrame.scaleRatio - 1 self.canvasFrame.update() logging.debug("zoom_in_event, scaleRatio= " + str(self.canvasFrame.scaleRatio)) def zoom_out_event(self): if self.canvasFrame.scaleRatio > 5: self.canvasFrame.scaleRatio = 6 else: self.canvasFrame.scaleRatio = self.canvasFrame.scaleRatio + 1 self.canvasFrame.update() logging.debug("zoom_out_event, scaleRatio= " + str(self.canvasFrame.scaleRatio)) def play_button_event(self): logging.debug("playButtonEvent") if self.device is None: logging.error("no device!!!") return if self.ADCEnable: self.channel1Button.setEnabled(True) self.channel2Button.setEnabled(True) self.playButton.setStyleSheet( "QPushButton{border-radius:%dpx;" "background-color:rgb(200, 100, 100);}" "QPushButton:hover{border:0;" "border-radius:%dpx;" "background-color:rgb(250, 100, 100);}" % (self.m_width / 40, self.m_width / 40)) self.command = Protocol.PREAMBLE.value \ + Protocol.ENABLE_ADC.value \ + '\x01' \ + Protocol.DISABLE.value # '\xAA'+'\xF1'+'\x01'+'\x00' self.playthread = USBWriteThread(self.device, self.command) self.playthread.start() self.ADCEnable = 0 self.enableLabelValue.setText("Stop") self.setButton.setStyleSheet( "QPushButton{border-radius:%dpx;" "background-color:rgb(150, 255, 150);" "color:rgb(0, 0, 0);" "text-align: center center;}" "QPushButton:hover{background-color:rgb(100, 255, 100);" "color:rgb(255, 100, 100);" "font-size:20px;}" % (self.m_width / 80)) self.setButton.setEnabled(True) self.canvasFrame.dragEnable = True else: if self.channel1Enable == 0 and self.channel2Enable == 0: return if self.canvasFrame.frequency == 1000: self.freq = Protocol.FREQUENCY_1000HZ.value elif self.canvasFrame.frequency == 100: self.freq = Protocol.FREQUENCY_100HZ.value elif self.canvasFrame.frequency == 10: self.freq = Protocol.FREQUENCY_10HZ.value elif self.canvasFrame.frequency == 4: self.freq = Protocol.FREQUENCY_4HZ.value else: self.freq = Protocol.FREQUENCY_1000HZ.value self.frequencyCommand = Protocol.PREAMBLE.value \ + Protocol.FREQUENCY_SET.value \ + '\x01' \ + self.freq # '\xAA' + '\x43' + '\x01' + '\x03' self.frequencyThread = USBWriteThread(self.device, self.frequencyCommand) self.frequencyThread.start() self.channel1Button.setEnabled(False) self.channel2Button.setEnabled(False) self.playButton.setStyleSheet( "QPushButton{border-radius:%dpx;" "background-color:rgb(100, 200, 100);}" "QPushButton:hover{border:0;" "border-radius:%dpx;" "background-color:rgb(100, 250, 100);}" % (self.m_width / 40, self.m_width / 40)) self.command = Protocol.PREAMBLE.value \ + Protocol.ENABLE_ADC.value \ + '\x01' \ + Protocol.ENABLE.value # '\xAA'+'\xF1'+'\x01'+'\x01' self.playthread = USBWriteThread(self.device, self.command) self.playthread.start() self.ADCEnable = 1 self.enableLabelValue.setText("Runing") self.setButton.setStyleSheet("QPushButton{border-radius:%dpx;" "background-color:rgb(150, 150, 150);" "color:rgb(0, 0, 0);" "text-align: center center;}" % (self.m_width / 80)) self.setButton.setEnabled(False) self.canvasFrame.dragEnable = False self.canvasFrame.dragBias = 0 self.canvasFrame.dragBias_t = 0 def red_led_switch_event(self): if self.device is None: logging.error("no device!!!") return logging.debug("redled is pressed") if self.redLedState: self.redLedSwitch.setIcon(QIcon("icon/switchOFF.png")) self.redLedState = 0 self.ledCommand = Protocol.PREAMBLE.value \ + Protocol.LED_SET.value \ + '\x02' \ + Protocol.LED_RED.value \ + Protocol.LED_OFF.value # '\xAA' + '\x13' + '\x02' + '\x01' + '\x00' else: self.redLedSwitch.setIcon(QIcon("icon/switchRedOn.png")) self.redLedState = 1 self.ledCommand = Protocol.PREAMBLE.value \ + Protocol.LED_SET.value \ + '\x02' + Protocol.LED_RED.value \ + Protocol.LED_ON.value # '\xAA' + '\x13' + '\x02' + '\x01' + '\x01' self.ledThread = USBWriteThread(self.device, self.ledCommand) self.ledThread.start() def green_led_switch_event(self): if self.device is None: logging.error("no device!!!") return logging.debug("greenled is pressed") if self.greenLedState: self.greenLedSwitch.setIcon(QIcon("icon/switchOFF.png")) self.greenLedState = 0 self.ledCommand = Protocol.PREAMBLE.value \ + Protocol.LED_SET.value \ + '\x02' \ + Protocol.LED_GREEN.value \ + Protocol.LED_OFF.value # '\xAA' + '\x13' + '\x02' + '\x02' + '\x00' else: self.greenLedSwitch.setIcon(QIcon("icon/switchGreenOn.png")) self.greenLedState = 1 self.ledCommand = Protocol.PREAMBLE.value \ + Protocol.LED_SET.value \ + '\x02' \ + Protocol.LED_GREEN.value \ + Protocol.LED_ON.value # '\xAA' + '\x13' + '\x02' + '\x02' + '\x01' self.ledThread = USBWriteThread(self.device, self.ledCommand) self.ledThread.start() def blue_led_switch_event(self): if self.device is None: logging.error("no device!!!") return logging.debug("blueled is pressed") if self.blueLedState: self.blueLedSwitch.setIcon(QIcon("icon/switchOFF.png")) self.blueLedState = 0 self.ledCommand = Protocol.PREAMBLE.value \ + Protocol.LED_SET.value \ + '\x02' \ + Protocol.LED_BLUE.value \ + Protocol.LED_OFF.value # '\xAA' + '\x13' + '\x02' + '\x03' + '\x00' else: self.blueLedSwitch.setIcon(QIcon("icon/switchBlueOn.png")) self.blueLedState = 1 self.ledCommand = Protocol.PREAMBLE.value \ + Protocol.LED_SET.value \ + '\x02' \ + Protocol.LED_BLUE.value \ + Protocol.LED_ON.value # '\xAA' + '\x13' + '\x02' + '\x03' + '\x01' self.ledThread = USBWriteThread(self.device, self.ledCommand) self.ledThread.start() def channel1_button_event(self): if self.device is None: logging.error("no device!!!") return if self.channel1Enable: del self.canvasFrame.Channel1List[:] self.command = Protocol.PREAMBLE.value \ + Protocol.ENABLE_CHANNEL.value \ + '\x02' \ + Protocol.CHANNEL1.value \ + Protocol.DISABLE.value # '\xAA'+'\xF1'+'\x01'+'\x00' self.channel1thread = USBWriteThread(self.device, self.command) self.channel1thread.start() self.channel1Enable = 0 self.canvasFrame.channel1Enable = 0 self.channel1Button.setStyleSheet( "QPushButton{border:1px solid rgb(200,200,200);" "border-top-left-radius:5px;" "border-bottom-left-radius: 5px;" "background-color:rgba(100, 100, 100,0);" "color:rgb(200, 200, 200);" "padding: 1px 20px;}" "QPushButton:hover{font-weight:bold;}") logging.debug("channel1 disable") else: self.command = Protocol.PREAMBLE.value \ + Protocol.ENABLE_CHANNEL.value \ + '\x02' \ + Protocol.CHANNEL1.value \ + Protocol.ENABLE.value # '\xAA'+'\xF1'+'\x01'+'\x00' self.channel1thread = USBWriteThread(self.device, self.command) self.channel1thread.start() self.channel1Enable = 1 self.canvasFrame.channel1Enable = 1 self.channel1Button.setStyleSheet( "QPushButton{border:1px solid rgb(200,200,200);" "border-top-left-radius:5px;" "border-bottom-left-radius: 5px;" "background-color:rgba(200, 100, 100,255);" "color:rgb(200, 200, 200);" "padding: 1px 20px;}" "QPushButton:hover{font-weight:bold;}") logging.debug("channel1 enable") def channel2_button_event(self): if self.device is None: logging.error("no device!!!") return if self.channel2Enable: del self.canvasFrame.Channel2List[:] self.command2 = Protocol.PREAMBLE.value \ + Protocol.ENABLE_CHANNEL.value \ + '\x02' \ + Protocol.CHANNEL2.value \ + Protocol.DISABLE.value # '\xAA'+'\xF1'+'\x01'+'\x00' self.channel2thread = USBWriteThread(self.device, self.command2) self.channel2thread.start() self.channel2Enable = 0 self.canvasFrame.channel2Enable = 0 self.channel2Button.setStyleSheet( "QPushButton{border:1px solid rgb(200,200,200);" "border-top-right-radius:5px;" "border-bottom-right-radius: 5px;" "background-color:rgba(100, 100, 100,0);" "color:rgb(200, 200, 200);" "padding: 1px 20px;}" "QPushButton:hover{font-weight:bold;}") logging.debug("channel2 disable") else: self.command2 = Protocol.PREAMBLE.value \ + Protocol.ENABLE_CHANNEL.value \ + '\x02' \ + Protocol.CHANNEL2.value \ + Protocol.ENABLE.value # '\xAA'+'\xF1'+'\x01'+'\x00' self.channel2thread = USBWriteThread(self.device, self.command2) self.channel2thread.start() self.channel2Enable = 1 self.canvasFrame.channel2Enable = 1 self.channel2Button.setStyleSheet( "QPushButton{border:1px solid rgb(200,200,200);" "border-top-right-radius:5px;" "border-bottom-right-radius: 5px;" "background-color:rgba(0, 200, 255,255);" "color:rgb(200, 200, 200);" "padding: 1px 20px;}" "QPushButton:hover{font-weight:bold;}") logging.debug("channel2 enable") def set_button_event(self): if self.device is None: logging.error("no device!!!") return logging.debug("setButton is pressed" + str(self.frenquencyComboBox.currentIndex()) + '+' + self.frenquencyComboBox.currentText()) if self.frenquencyComboBox.currentText() == '1000Hz': self.canvasFrame.frequency = 1000 self.readThread.frequency = 1000 elif self.frenquencyComboBox.currentText() == '100Hz': self.canvasFrame.frequency = 100 self.readThread.frequency = 100 elif self.frenquencyComboBox.currentText() == '10Hz': self.canvasFrame.frequency = 10 self.readThread.frequency = 10 elif self.frenquencyComboBox.currentText() == '4Hz': self.canvasFrame.frequency = 4 self.readThread.frequency = 4 else: logging.warning("error frequency value:" + self.frenquencyComboBox.currentText()) self.frequencyLabelValue.setText(self.frenquencyComboBox.currentText()) def update_canvas(self, state): logging.debug("update_canvas:" + state) self.canvasFrame.update() def update_frequency(self, frequency): logging.debug("update_frequency") if self.readThread: self.frequencyLabelValue.setText(str(frequency) + "(HZ)") if frequency == 0: logging.warning("receive wrong frequency:0") else: self.canvasFrame.frequency = frequency def single_trigger_event(self, state): logging.debug("single_trigger_event:" + state) if self.ADCEnable == 0: return self.command = Protocol.PREAMBLE.value \ + Protocol.ENABLE_ADC.value \ + '\x01' \ + Protocol.DISABLE.value # '\xAA' + '\xF2' + '\x01' + '\x00' self.playThread = USBWriteThread(self.device, self.command) self.playThread.start() self.ADCEnable = 0 self.enableLabelValue.setText("Stop") self.playButton.setStyleSheet("QPushButton{border:none;" "border-radius:%dpx;" "background-color:rgb(200, 100, 100);}" "QPushButton:hover{border:0;" "border-radius:%dpx;" "background-color:rgb(250, 100, 100);}" % (self.m_width / 40, self.m_width / 40)) self.setButton.setStyleSheet( "QPushButton{border-radius:%dpx;" "background-color:rgb(150, 255, 150);" "color:rgb(0, 0, 0);" "text-align: center center;}" "QPushButton:hover{background-color:rgb(100, 255, 100);" "color:rgb(255, 100, 100);" "font-size:20px}" % (self.m_width / 80)) self.setButton.setEnabled(True) self.channel1Button.setEnabled(True) self.channel2Button.setEnabled(True) logging.debug("trigger!!! sample stop") def closeEvent(self, *args, **kwargs): logging.debug("this closeEvent -------------") if self.device: self.device.close() self.readThread.terminate() self.readThread.wait() def __del__(self): logging.debug("this del -------------")