class UIApplication: def __init__(self): self.ui = None self.thread_pool = None self.main_window = None def start_window(self): app = QtWidgets.QApplication(sys.argv) file = QFile("ui/style.qss") file.open(QFile.ReadOnly | QFile.Text) stream = QTextStream(file) app.setStyleSheet(stream.readAll()) self.main_window = QtWidgets.QMainWindow() self.ui = UIMainWindow() self.ui.setupUi(self.main_window) self.ui.actionOpen.triggered.connect(self.create_new_media_set_tab) self.main_window.show() self.thread_pool = QThreadPool() print("Multithreading with maximum %d threads" % self.thread_pool.maxThreadCount()) sys.exit(app.exec_()) def create_new_media_set_tab(self): dir_path = str( QFileDialog.getExistingDirectory(None, "Select Directory")) new_tab = UIMediaSetTab( Path(dir_path).name, dir_path, self.thread_pool) self.ui.media_set_tab.addTab(new_tab, Path(dir_path).name) new_tab.load()
class appWin(QMainWindow): def __init__(self): super(appWin, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.threadpool = QThreadPool() print('Multithreading with maximum {} thread'.format( self.threadpool.maxThreadCount())) self.timer = QTimer() self.timer.setInterval(0.1) self.timer.timeout.connect(self.on_timer_timeout) self.timer.start() self.count = 0 self.ui.pushButton.clicked.connect(self.on_btn) def on_timer_timeout(self): self.count += 1 self.ui.label.setText(str(self.count)) def on_btn(self): # for i in range(5): # # QApplication.processEvents() # self.ui.label_2.setText(str(i+1)) # time.sleep(1) worker1 = Worker([1, 2, 3, 4], 'Ali', ui=self.ui, test='pp') self.threadpool.start(worker1)
class LivePlot(QtWidgets.QWidget): ''' A basic PyQt Widget for plotting live data received from Mantis Data handling is done by a new thread using the Worker Class, as otherwise the UI freezes and doesn't update the plot. ''' def __init__(self): super().__init__() self.w = QtGui.QWidget() # Plot Widgets self.plotWidget = pg.PlotWidget() self.plotWidget2 = pg.PlotWidget() # Plot objects self.trace = self.plotWidget.plot() self.avgTrace = self.plotWidget.plot(pen='c') self.stats = self.plotWidget2.plot(pen='r', symbol='o', symbolPen='r', symbolBrush='r') # Add data properties to plot objects to store data self.stats.data = [] # Button for initiating sockets with Mantis self.commsButton = QtWidgets.QPushButton('Start Mantis Comms', self) self.commsButton.clicked.connect(self.startPlot) # UI layout self.layout = QtGui.QGridLayout() self.w.setLayout(self.layout) self.layout.addWidget(self.plotWidget) self.layout.addWidget(self.plotWidget2) self.layout.addWidget(self.commsButton) self.w.show() # Ininiate the thread pool self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) @pyqtSlot() def startPlot(self): ''' Slot for sockets the initiation button ''' worker = Worker(mantisComms, plotLiveData, [self.trace, self.stats]) self.threadpool.start(worker) print('push') def testFunc(self): print("test")
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.counter = 0 layout = QVBoxLayout() self.l = QLabel("Start") b = QPushButton("DANGER!") b.pressed.connect(self.oh_no) layout.addWidget(self.l) layout.addWidget(b) w = QWidget() w.setLayout(layout) self.setCentralWidget(w) self.show() self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) def progress_fn(self, progress): p, m = (progress) print("%d%% done %s" % (p, m)) def execute_this_fn(self, progress_callback): for n in range(0, 5): time.sleep(1) progress_callback.emit((n * 100 / 4, 'blabla')) return "Done." def print_output(self, s): print(s) def thread_complete(self): print("THREAD COMPLETE!") def oh_no(self): # Pass the function to execute worker = Worker( self.execute_this_fn ) # Any other args, kwargs are passed to the run function worker.signals.result.connect(self.print_output) worker.signals.finished.connect(self.thread_complete) worker.signals.progress.connect(self.progress_fn) # Execute self.threadpool.start(worker)
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.counter = 0 self.l = QLabel("Start") b = QPushButton("DANGER!") b.pressed.connect(self.oh_no) layout = QVBoxLayout() layout.addWidget(self.l) layout.addWidget(b) w = QWidget() w.setLayout(layout) self.setCentralWidget(w) self.timer = QTimer() self.timer.setInterval(1000) self.timer.timeout.connect(self.recurring_timer) self.timer.start() self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) def oh_no(self): worker = Worker(iterations=random.randint(10, 50)) worker.signals.result.connect(self.worker_output) worker.signals.finished.connect(self.worker_complete) worker.signals.error.connect(self.worker_error) self.threadpool.start(worker) # Worker ouptut function/slot def worker_output(self, s): print("RESULT", s) # worker complete function/slot def worker_complete(self): print("THREAD COMPLETE!") # worker error function/slot def worker_error(self, e): print("ERROR: %s" % e) def recurring_timer(self): self.counter += 1 self.l.setText("Counter: %d" % self.counter)
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.urls = [ "https://www.learnpyqt.com/", "https://www.mfitzp.com/", "https://www.google.com", "https://www.udemy.com/create-simple-gui-applications-with-python-and-qt/", ] layout = QVBoxLayout() self.text = QPlainTextEdit() self.text.setReadOnly(True) button = QPushButton("GO GET EM!") button.pressed.connect(self.execute) layout.addWidget(self.text) layout.addWidget(button) w = QWidget() w.setLayout(layout) self.setCentralWidget(w) self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) self.parsers = { # Regular expression parsers, to extract data from the HTML. "title": re.compile(r"<title.*?>(.*?)<\/title>", re.M | re.S), "h1": re.compile(r"<h1.*?>(.*?)<\/h1>", re.M | re.S), "h2": re.compile(r"<h2.*?>(.*?)<\/h2>", re.M | re.S), } def execute(self): for n, url in enumerate(self.urls): worker = Worker(n, url, self.parsers) worker.signals.data.connect(self.display_output) # Execute self.threadpool.start(worker) def display_output(self, data): id, s = data self.text.appendPlainText("WORKER %d: %s" % (id, s))
def run(self): class ProcessRunnable(QRunnable): def __init__(self, run_task): super().__init__() self.setAutoDelete(True) self.task = run_task self.results = [] def run(self): print('processing...', self.task.filename) if self.task.load_data(): print('data loaded...', self.task.filename) else: print('data not loaded...', self.task.filename) # Emit signal that task may have results self.task.results_ready.emit(QVariant(self.task)) print('BackgroundProcessForImporters starting load threads') pool = QThreadPool() # Disable expiration pool.setExpiryTimeout(-1) print('MaxThread count: ', pool.maxThreadCount()) # Starting all threads for importation runnable_list = [] for task in self.tasks: # Connect signals task.update_progress.connect(self.update_current_task_progress) # Add to list runnable_list.append(ProcessRunnable(task)) # Connect results ready signals task.results_ready.connect(self.results_ready) # Start last inserted runnable pool.start(runnable_list[-1], priority=QThread.NormalPriority) # Wait for all runnable threads if pool.waitForDone(): print('All threads done!')
class Fuzzer(QObject): communication = None def __init__(self, communication, max_thread_number, inputs, wordlist): super(Fuzzer, self).__init__() self.communication = communication self.max_thread_number = max_thread_number self.pool = QThreadPool() self.pool.globalInstance() self.inputs = inputs self.wordlist = wordlist self.socket_data = [inputs[0], inputs[1], inputs[2]] def start(self): num_of_chunks = len(self.wordlist) // int(self.max_thread_number) new_fuzz_list = list(self.listChunks(self.wordlist, num_of_chunks)) self.pool.setMaxThreadCount(self.max_thread_number) for sub_list in new_fuzz_list: QCoreApplication.processEvents() for word in sub_list: QCoreApplication.processEvents() if self.pool.activeThreadCount() < self.pool.maxThreadCount(): fuzz_thread = Fuzz(self.communication, self.socket_data, self.inputs[3], str(word), self.inputs[5]) fuzz_thread.setAutoDelete(True) self.pool.start(fuzz_thread) else: self.pool.waitForDone() def __del__(self): pass def listChunks(self, myList, numOfChunks): for i in range(0, len(myList), numOfChunks): yield myList[i:i + numOfChunks]
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.counter = 0 self.l = QLabel("Start") b = QPushButton("DANGER!") b.pressed.connect(self.oh_no) layout = QVBoxLayout() layout.addWidget(self.l) layout.addWidget(b) w = QWidget() w.setLayout(layout) self.setCentralWidget(w) self.timer = QTimer() self.timer.setInterval(1000) self.timer.timeout.connect(self.recurring_timer) self.timer.start() self.threadpool = QThreadPool() print( "Multithreading with maximum %d threads" % self.threadpool.maxThreadCount() ) def oh_no(self): worker = Worker("some", "args", k=2) self.threadpool.start(worker) def recurring_timer(self): self.counter += 1 self.l.setText("Counter: %d" % self.counter)
class MainWindow(QMainWindow): """ You can use @pyqtSlot(int) syntax (with parameters), or you can pass this, but it make code more readable. Вы можете использовать синтаксис объявления слотов @pyqtSlot(int) с указанием типа передаваемых значений, или опустить его вовсе, однако это делает код нагляднее и позволяет быстро понять, что слот, а что - функция. """ def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self._counter = 0 self.init_ui() self._threadpool = QThreadPool() #self._threadpool = QtCore.QThreadPool.globalInstance() #self._threadpool.setMaxThreadCount(2) print("Multithreading with maximum {} threads" .format(self._threadpool.maxThreadCount())) self._timer = QTimer() self._timer.setInterval(1000) self._timer.timeout.connect(self.recurring_timer) self._timer.start() def init_ui(self): layout = QVBoxLayout() self._label = QLabel("Start") b = QPushButton("Start QRunnable") b.pressed.connect(self.start_new_runnable) layout.addWidget(self._label) layout.addWidget(b) w = QWidget() w.setLayout(layout) self.setCentralWidget(w) @pyqtSlot(int) def thread_progress_fn(self, n): print("{}% done".format(n)) @pyqtSlot(object) def thread_print_output(self, s): print('Result: {}'.format(s)) @pyqtSlot() def thread_complete(self): print("QRunnable worker COMPLETE!") @pyqtSlot(tuple) def thread_error(self, err): QMessageBox.warning(self, "Warning!", err[1], QMessageBox.Ok) print('Error {}\n{}'.format(err[1], err[2])) @pyqtSlot() def start_new_runnable(self): # Pass the function to execute worker = Worker(1, debug=True) # Any other args, kwargs are passed to the run function worker.signals.result.connect(self.thread_print_output) worker.signals.finished.connect(self.thread_complete) worker.signals.progress.connect(self.thread_progress_fn) worker.signals.error.connect(self.thread_error) worker.setAutoDelete(True) # Execute (tryStart() better than start() ) if self._threadpool.tryStart(worker) is False: print("Can't create worker!") QMessageBox.warning(self, "Warning!", "Can't create worker!", QMessageBox.Ok) @pyqtSlot() def recurring_timer(self): self._counter += 1 self._label.setText("Counter: {}".format(self._counter)) print('Active thread count: {}'.format(self._threadpool.activeThreadCount())) def closeEvent(self, event): """Main window closed, override PyQt5 widget function""" print('Try to exit, active thread count: {}'.format(self._threadpool.activeThreadCount())) reply = QMessageBox.question(self, 'Message', "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self._threadpool.waitForDone() self._timer.stop() event.accept() else: event.ignore()
class Run_gui(QMainWindow): def __init__(self): super().__init__() self.cwd = os.getcwd() self.load_() # Enable antialiasing for prettier plots self.initUI() def initUI(self): ################### MENU BARS START ################## MyBar = QMenuBar(self) fileMenu = MyBar.addMenu("File") self.fileSave = fileMenu.addAction("Save thinfilm config") self.fileSave.triggered.connect(self.save_) self.fileSave.setShortcut('Ctrl+S') self.fileLoadAs = fileMenu.addAction("Load config section") self.fileLoadAs.triggered.connect(self.load_config_dialog) fileClose = fileMenu.addAction("Close") fileClose.triggered.connect(self.close) # triggers closeEvent() fileClose.setShortcut('Ctrl+X') self.loadMenu = MyBar.addMenu("Load data") loadSubOlis = self.loadMenu.addAction("OLIS sub") loadSubFilmOlis = self.loadMenu.addAction("OLIS sub + thin film") loadSubFTIR = self.loadMenu.addAction("FTIR sub") loadSubFilmFTIR = self.loadMenu.addAction("FTIR sub + thin film") loadSubOlis.triggered.connect(self.loadSubOlisDialog) loadSubFilmOlis.triggered.connect(self.loadSubFilmOlisDialog) loadSubFTIR.triggered.connect(self.loadSubFTIRDialog) loadSubFilmFTIR.triggered.connect(self.loadSubFilmFTIRDialog) self.emailMenu = MyBar.addMenu("E-mail") self.emailSettings = self.emailMenu.addAction("E-mail settings") self.emailSettings.triggered.connect(self.email_set_dialog) self.emailData = self.emailMenu.addAction("E-mail data") self.emailData.triggered.connect(self.email_data_dialog) helpMenu = MyBar.addMenu("Help") helpParam = helpMenu.addAction("Instructions") helpParam.triggered.connect(self.helpParamDialog) contact = helpMenu.addAction("Contact") contact.triggered.connect(self.contactDialog) ################### MENU BARS END ################## # status info which button has been pressed Start_lbl = QLabel("PLOTS and analysis steps", self) Start_lbl.setStyleSheet("color: blue") self.Step0_Button = QPushButton("Raw data", self) self.Step0_Button.setToolTip("STEP 0. Plot raw data for OLIS and FTIR") self.button_style(self.Step0_Button, 'black') self.Step1_Button = QPushButton("Tmin and Tmax", self) self.Step1_Button.setToolTip( "STEP 1. Find all the minima and maxima positions using Gaussian filter" ) self.button_style(self.Step1_Button, 'black') self.Step2_Button = QPushButton("Std.Dev. in d", self) self.Step2_Button.setToolTip( "STEP 2. Minimize standard deviation in the film thickness d") self.button_style(self.Step2_Button, 'black') self.Step3_Button = QPushButton("Index n", self) self.Step3_Button.setToolTip( "STEP 3. Plot refractive indicies n1 and n2") self.button_style(self.Step3_Button, 'black') self.Step4_Button = QPushButton("Absorption alpha", self) self.Step4_Button.setToolTip( "STEP 4. Plot abosorption alpha based on n2") self.button_style(self.Step4_Button, 'black') self.Step5_Button = QPushButton("Wavenumber k", self) self.Step5_Button.setToolTip("STEP 5. Plot wavenumber k based on n2") self.button_style(self.Step5_Button, 'black') #################################################### # status info which button has been pressed self.NewFiles = QLabel('No files created yet!', self) self.NewFiles.setStyleSheet("color: blue") newfont = QFont("Times", 10, QFont.Normal) self.NewFiles.setFont(newfont) ''' self.NewFiles = numpy.zeros(5,dtype=object) for i in range(4): self.NewFiles[i] = QLabel(''.join([str(i+1),': ']), self) self.NewFiles[i].setStyleSheet("color: magenta") ''' #################################################### loads_lbl = QLabel("RAW data files", self) loads_lbl.setStyleSheet("color: blue") configFile_lbl = QLabel("Current thinfilm", self) self.config_file_lbl = QLabel("", self) self.config_file_lbl.setStyleSheet("color: green") loadSubOlis_lbl = QLabel("OLIS sub", self) self.loadSubOlisFile_lbl = QLabel("", self) self.loadSubOlisFile_lbl.setStyleSheet("color: magenta") loadSubFilmOlis_lbl = QLabel("OLIS sub + thin film", self) self.loadSubFilmOlisFile_lbl = QLabel("", self) self.loadSubFilmOlisFile_lbl.setStyleSheet("color: magenta") loadSubFTIR_lbl = QLabel("FTIR sub", self) self.loadSubFTIRFile_lbl = QLabel("", self) self.loadSubFTIRFile_lbl.setStyleSheet("color: magenta") loadSubFilmFTIR_lbl = QLabel("FTIR sub + thin film", self) self.loadSubFilmFTIRFile_lbl = QLabel("", self) self.loadSubFilmFTIRFile_lbl.setStyleSheet("color: magenta") self.cb_sub_olis = QCheckBox('', self) self.cb_sub_olis.toggle() self.cb_subfilm_olis = QCheckBox('', self) self.cb_subfilm_olis.toggle() self.cb_sub_ftir = QCheckBox('', self) self.cb_sub_ftir.toggle() self.cb_subfilm_ftir = QCheckBox('', self) self.cb_subfilm_ftir.toggle() plot_X_lbl = QLabel("Plot X axis in", self) self.combo2 = QComboBox(self) self.mylist2 = ["eV", "nm"] self.combo2.addItems(self.mylist2) self.combo2.setFixedWidth(70) #################################################### lbl1 = QLabel("GAUSSIAN filter settings", self) lbl1.setStyleSheet("color: blue") interpol_lbl = QLabel("Interpolation method", self) self.combo4 = QComboBox(self) self.mylist4 = ["spline", "linear"] self.combo4.setToolTip( "Interpolation method for local minima Tmin and local maxima Tmax can only be linear or spline." ) self.combo4.addItems(self.mylist4) self.combo4.setFixedWidth(70) factors_lbl = QLabel("Gaussian factors", self) self.factorsEdit = QLineEdit("", self) self.factorsEdit.setToolTip( "HIGH gaussian factor = broadband noise filtering.\nLOW gaussian factor = narrowband noise filtering.\nHigh gauissian factors (>2) will result in relatively large deviation from the raw data.\nGauissian factors of zero or near zero (<0.5) will closely follow trend of the raw data." ) self.factorsEdit.setFixedWidth(200) borders_lbl = QLabel("Gaussian borders [eV]", self) self.bordersEdit = QLineEdit("", self) self.bordersEdit.setToolTip( "Gaussian borders should be typed in ascending order and the number of\nborders is always one more compared with the number of Gaussian factors." ) self.bordersEdit.setFixedWidth(200) ############################################## lbl2 = QLabel("ABSORPTION alpha and n1 and n2", self) lbl2.setStyleSheet("color: blue") poly_lbl = QLabel("Polyfit order", self) self.combo1 = QComboBox(self) self.mylist1 = ["1", "2", "3", "4", "5"] self.combo1.addItems(self.mylist1) self.combo1.setFixedWidth(70) polybord_lbl = QLabel("Polyfit range(s) [eV]", self) self.poly_bordersEdit = QLineEdit("", self) self.poly_bordersEdit.setFixedWidth(140) self.cb_polybord = QCheckBox('', self) self.cb_polybord.toggle() ignore_data_lbl = QLabel("No. of ignored points", self) self.ignore_data_ptsEdit = QLineEdit("", self) self.ignore_data_ptsEdit.setFixedWidth(140) corr_slit_lbl = QLabel("Correction slit width [nm]", self) self.corr_slitEdit = QLineEdit("", self) self.corr_slitEdit.setToolTip( "Finite spectrometer bandwidth (slit width) in the transmission spectrum." ) self.corr_slitEdit.setFixedWidth(140) ############################################## lbl4 = QLabel("STORAGE location (folder/file)", self) lbl4.setStyleSheet("color: blue") self.filenameEdit = QLineEdit("", self) #self.filenameEdit.setFixedWidth(180) self.cb_save_figs = QCheckBox('Save figs', self) self.cb_save_figs.toggle() ############################################## self.lcd = QLCDNumber(self) self.lcd.setStyleSheet("color: red") self.lcd.setFixedHeight(60) self.lcd.setSegmentStyle(QLCDNumber.Flat) self.lcd.setToolTip("Timetrace for saving files") self.lcd.setNumDigits(11) ############################################## self.initUI_() ############################################## # Add all widgets g1_0 = QGridLayout() g1_0.addWidget(MyBar, 0, 0) g1_1 = QGridLayout() g1_1.addWidget(loads_lbl, 0, 0) g1_1.addWidget(configFile_lbl, 1, 0) g1_1.addWidget(self.config_file_lbl, 1, 1) g1_1.addWidget(loadSubOlis_lbl, 2, 0) g1_1.addWidget(self.loadSubOlisFile_lbl, 2, 1) g1_1.addWidget(self.cb_sub_olis, 2, 2) g1_1.addWidget(loadSubFilmOlis_lbl, 3, 0) g1_1.addWidget(self.loadSubFilmOlisFile_lbl, 3, 1) g1_1.addWidget(self.cb_subfilm_olis, 3, 2) g1_1.addWidget(loadSubFTIR_lbl, 4, 0) g1_1.addWidget(self.loadSubFTIRFile_lbl, 4, 1) g1_1.addWidget(self.cb_sub_ftir, 4, 2) g1_1.addWidget(loadSubFilmFTIR_lbl, 5, 0) g1_1.addWidget(self.loadSubFilmFTIRFile_lbl, 5, 1) g1_1.addWidget(self.cb_subfilm_ftir, 5, 2) g1_1.addWidget(plot_X_lbl, 6, 0) g1_1.addWidget(self.combo2, 6, 1) g1_2 = QGridLayout() g1_2.addWidget(lbl1, 0, 0) g1_3 = QGridLayout() g1_3.addWidget(interpol_lbl, 0, 0) g1_3.addWidget(self.combo4, 0, 1) g1_3.addWidget(factors_lbl, 1, 0) g1_3.addWidget(self.factorsEdit, 1, 1) g1_3.addWidget(borders_lbl, 2, 0) g1_3.addWidget(self.bordersEdit, 2, 1) g1_4 = QGridLayout() g1_4.addWidget(lbl2, 0, 0) g1_5 = QGridLayout() g1_5.addWidget(poly_lbl, 0, 0) g1_5.addWidget(self.combo1, 0, 1) g1_5.addWidget(polybord_lbl, 1, 0) g1_5.addWidget(self.poly_bordersEdit, 1, 1) g1_5.addWidget(self.cb_polybord, 1, 2) g1_5.addWidget(ignore_data_lbl, 2, 0) g1_5.addWidget(self.ignore_data_ptsEdit, 2, 1) g1_5.addWidget(corr_slit_lbl, 3, 0) g1_5.addWidget(self.corr_slitEdit, 3, 1) g4_0 = QGridLayout() g4_0.addWidget(lbl4, 0, 0) g4_0.addWidget(self.cb_save_figs, 0, 1) g4_1 = QGridLayout() g4_1.addWidget(self.filenameEdit, 0, 0) v1 = QVBoxLayout() v1.addLayout(g1_0) v1.addLayout(g1_1) v1.addLayout(g1_2) v1.addLayout(g1_3) v1.addLayout(g1_4) v1.addLayout(g1_5) v1.addLayout(g4_0) v1.addLayout(g4_1) ################################################### g1_6 = QGridLayout() g1_6.addWidget(Start_lbl, 0, 0) g1_7 = QGridLayout() g1_7.addWidget(self.Step0_Button, 0, 0) g1_7.addWidget(self.Step1_Button, 1, 0) g1_7.addWidget(self.Step2_Button, 2, 0) g1_7.addWidget(self.Step3_Button, 3, 0) g1_7.addWidget(self.Step4_Button, 4, 0) g1_7.addWidget(self.Step5_Button, 5, 0) g1_8 = QGridLayout() g1_8.addWidget(self.NewFiles, 0, 0) g1_8.addWidget(self.lcd, 1, 0) v0 = QVBoxLayout() v0.addLayout(g1_6) v0.addLayout(g1_7) v0.addLayout(g1_8) # SET ALL VERTICAL COLUMNS TOGETHER hbox = QHBoxLayout() hbox.addLayout(v1) hbox.addLayout(v0) ############################################################################### # reacts to choises picked in the menu self.combo1.activated[str].connect(self.onActivated1) self.combo2.activated[str].connect(self.onActivated2) self.combo4.activated[str].connect(self.onActivated4) # reacts to choises picked in the menu self.Step0_Button.clicked.connect(self.set_run) self.Step1_Button.clicked.connect(self.set_run) self.Step2_Button.clicked.connect(self.set_run) self.Step3_Button.clicked.connect(self.set_run) self.Step4_Button.clicked.connect(self.set_run) self.Step5_Button.clicked.connect(self.set_run) # reacts to choises picked in the checkbox self.cb_sub_olis.stateChanged.connect(self.sub_olis_check) self.cb_subfilm_olis.stateChanged.connect(self.subfilm_olis_check) self.cb_sub_ftir.stateChanged.connect(self.sub_ftir_check) self.cb_subfilm_ftir.stateChanged.connect(self.subfilm_ftir_check) self.cb_save_figs.stateChanged.connect(self.save_figs_check) self.cb_polybord.stateChanged.connect(self.polybord_check) self.threadpool = QThreadPool() print("Multithreading in TEST_gui_v1 with maximum %d threads" % self.threadpool.maxThreadCount()) self.isRunning = False self.move(0, 0) #self.setGeometry(50, 50, 800, 500) hbox.setSizeConstraint(hbox.SetFixedSize) self.setWindowTitle("Swanepoel method for thin film analysis") w = QWidget() w.setLayout(hbox) self.setCentralWidget(w) self.show() def bool_(self, txt): if txt == "True": return True elif txt == "False": return False def initUI_(self): self.config_file_lbl.setText(self.last_used_scan) self.loadSubOlisFile_lbl.setText(self.loadSubOlis_str) self.loadSubFilmOlisFile_lbl.setText(self.loadSubFilmOlis_str) self.loadSubFTIRFile_lbl.setText(self.loadSubFTIR_str) self.loadSubFilmFTIRFile_lbl.setText(self.loadSubFilmFTIR_str) ############################################## self.sub_olis_check(self.loadSubOlis_check) self.cb_sub_olis.setChecked(self.loadSubOlis_check) self.subfilm_olis_check(self.loadSubFilmOlis_check) self.cb_subfilm_olis.setChecked(self.loadSubFilmOlis_check) self.sub_ftir_check(self.loadSubFTIR_check) self.cb_sub_ftir.setChecked(self.loadSubFTIR_check) self.subfilm_ftir_check(self.loadSubFilmFTIR_check) self.cb_subfilm_ftir.setChecked(self.loadSubFilmFTIR_check) self.save_figs_check(self.save_figs) self.cb_save_figs.setChecked(self.save_figs) self.filenameEdit.setText(self.filename_str) ############################################## if len(self.fit_poly_ranges) == 0: self.fit_poly_ranges_check = False self.polybord_check(self.fit_poly_ranges_check) self.cb_polybord.setChecked(self.fit_poly_ranges_check) else: self.polybord_check(self.fit_poly_ranges_check) self.cb_polybord.setChecked(self.fit_poly_ranges_check) ############################################## self.factorsEdit.setText(self.gaussian_factors) self.bordersEdit.setText(self.gaussian_borders) ############################################## self.combo1.setCurrentIndex(self.mylist1.index(self.fit_poly_order)) self.combo2.setCurrentIndex(self.mylist2.index(self.plot_X)) self.combo4.setCurrentIndex(self.mylist4.index(self.fit_linear_spline)) ############################################## self.poly_bordersEdit.setText(self.fit_poly_ranges) self.ignore_data_ptsEdit.setText(self.ignore_data_pts) self.corr_slitEdit.setText(self.corr_slit) ############################################## self.NewFiles.setToolTip(''.join([ "Display newly created and saved files in ", os.sep, self.filename_str, os.sep ])) self.lcd.display(self.timetrace) def button_style(self, button, color): button.setStyleSheet(''.join([ 'QPushButton {background-color: lightblue; font-size: 18pt; color: ', color, '}' ])) button.setFixedWidth(260) button.setFixedHeight(65) def loadSubOlisDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog files, _ = QFileDialog.getOpenFileNames( self, "Open files", ''.join(['data', os.sep]), "All Files (*);;Dat Files (*.dat);;Text Files (*.txt)", options=options) for afile in files: head, tail = os.path.split(str(afile)) self.loadSubOlis_str = tail self.loadSubOlisFile_lbl.setText(tail) self.cb_sub_olis.setEnabled(True) self.loadSubOlis_check = True self.cb_sub_olis(self.loadSubOlis_check) def loadSubFilmOlisDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog files, _ = QFileDialog.getOpenFileNames( self, "Open files", ''.join(['data', os.sep]), "All Files (*);;Dat Files (*.dat);;Text Files (*.txt)", options=options) for afile in files: head, tail = os.path.split(str(afile)) self.loadSubFilmOlis_str = tail self.loadSubFilmOlisFile_lbl.setText(tail) self.cb_subfilm_olis.setEnabled(True) self.loadSubFilmOlis_check = True self.cb_subfilm_olis(self.loadSubFilmOlis_check) def loadSubFTIRDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog files, _ = QFileDialog.getOpenFileNames( self, "Open files", ''.join(['data', os.sep]), "All Files (*);;Dat Files (*.dat);;Text Files (*.txt)", options=options) for afile in files: head, tail = os.path.split(str(afile)) self.loadSubFTIR_str = tail self.loadSubFTIRFile_lbl.setText(tail) self.cb_sub_ftir.setEnabled(True) self.loadSubFTIR_check = True self.cb_sub_ftir(self.loadSubFTIR_check) def loadSubFilmFTIRDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog files, _ = QFileDialog.getOpenFileNames( self, "Open files", ''.join(['data', os.sep]), "All Files (*);;Dat Files (*.dat);;Text Files (*.txt)", options=options) for afile in files: head, tail = os.path.split(str(afile)) self.loadSubFilmFTIR_str = tail self.loadSubFilmFTIRFile_lbl.setText(tail) self.cb_subfilm_ftir.setEnabled(True) self.loadSubFilmFTIR_check = True self.cb_subfilm_ftir.setChecked(self.loadSubFilmFTIR_check) def load_config_dialog(self): self.Load_config_dialog = Load_config_dialog.Load_config_dialog( self, self.config, self.load_, self.initUI_, self.cwd) self.Load_config_dialog.exec() def email_data_dialog(self): self.Send_email_dialog = Send_email_dialog.Send_email_dialog( self, self.cwd) self.Send_email_dialog.exec() def email_set_dialog(self): self.Email_dialog = Email_settings_dialog.Email_dialog( self, self.lcd, self.cwd) self.Email_dialog.exec() def helpParamDialog(self): helpfile = '' with open('config_Swanepoel_forklaringer.py', 'r') as f: for line in f: helpfile = helpfile + line msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText( "Apply the Swanepoel method using the following analysis steps:") msg.setInformativeText(helpfile) msg.setWindowTitle("Help") #msg.setDetailedText(helpfile) msg.setStandardButtons(QMessageBox.Ok) #msg.setGeometry(1000, 0, 1000+250, 350) msg.exec_() def contactDialog(self): QMessageBox.information( self, "Contact information", "Suggestions, comments or bugs can be reported to [email protected]" ) def onActivated1(self, text): self.fit_poly_order = int(text) def onActivated2(self, text): self.plot_X = str(text) def onActivated4(self, text): self.fit_linear_spline = str(text) def save_figs_check(self, state): if state in [Qt.Checked, True]: self.save_figs = True else: self.save_figs = False def sub_olis_check(self, state): if state in [Qt.Checked, True]: self.loadSubOlis_check = True self.loadSubOlisFile_lbl.setStyleSheet("color: magenta") self.cb_sub_olis.setText('incl') else: self.loadSubOlis_check = False self.loadSubOlisFile_lbl.setStyleSheet("color: grey") self.cb_sub_olis.setText('exc') def subfilm_olis_check(self, state): if state in [Qt.Checked, True]: self.loadSubFilmOlis_check = True self.loadSubFilmOlisFile_lbl.setStyleSheet("color: magenta") self.cb_subfilm_olis.setText('incl') else: self.loadSubFilmOlis_check = False self.loadSubFilmOlisFile_lbl.setStyleSheet("color: grey") self.cb_subfilm_olis.setText('exc') def sub_ftir_check(self, state): if state in [Qt.Checked, True]: self.loadSubFTIR_check = True self.loadSubFTIRFile_lbl.setStyleSheet("color: magenta") self.cb_sub_ftir.setText('incl') else: self.loadSubFTIR_check = False self.loadSubFTIRFile_lbl.setStyleSheet("color: grey") self.cb_sub_ftir.setText('exc') def subfilm_ftir_check(self, state): if state in [Qt.Checked, True]: self.loadSubFilmFTIR_check = True self.loadSubFilmFTIRFile_lbl.setStyleSheet("color: magenta") self.cb_subfilm_ftir.setText('incl') else: self.loadSubFilmFTIR_check = False self.loadSubFilmFTIRFile_lbl.setStyleSheet("color: grey") self.cb_subfilm_ftir.setText('exc') def polybord_check(self, state): if state in [Qt.Checked, True]: self.fit_poly_ranges_check = True self.poly_bordersEdit.setEnabled(True) self.cb_polybord.setText('incl') else: self.fit_poly_ranges_check = False self.poly_bordersEdit.setEnabled(False) self.cb_polybord.setText('exc') ############################################################ # Check input if a number, ie. digits or fractions such as 3.141 # Source: http://www.pythoncentral.io/how-to-check-if-a-string-is-a-number-in-python-including-unicode/ def is_int(self, s): try: int(s) return True except ValueError: return False def is_number(self, s): try: float(s) return True except ValueError: pass try: import unicodedata unicodedata.numeric(s) return True except (TypeError, ValueError): pass return False def create_file(self, mystr): head, ext = os.path.splitext(mystr) totalpath = ''.join([self.cwd, os.sep, head, '_', self.timetrace]) my_dir = os.path.dirname(totalpath) if not os.path.isdir(my_dir): QMessageBox.warning( self, "Message", "".join(["Folder(s) named ", my_dir, " will be created!"])) try: os.makedirs(my_dir, exist_ok=True) except Exception as e: QMessageBox.critical( self, "Message", "".join(["Folder named ", head, " not valid!\n\n", str(e)])) return "" return totalpath def set_run(self): # Save all the currently changed varaibles self.save_() # Register the sender of the command (a button or similar) sender = self.sender() ## gaussian_borders and gaussian_factors warnings and errors gaus_bord = str(self.bordersEdit.text()).split(',') for tal in gaus_bord: if not self.is_number(tal): QMessageBox.critical(self, 'Message', "Gaussian borders must be real numbers!") return elif float(tal) < 0.0: QMessageBox.critical( self, 'Message', "Gaussian borders must be positive or zero!") return if len(gaus_bord) < 2: QMessageBox.critical( self, 'Message', "You must enter at least 2 gaussian borders!") return if not numpy.array_equal([numpy.float(i) for i in gaus_bord], numpy.sort( [numpy.float(i) for i in gaus_bord])): QMessageBox.critical( self, 'Message', "The gaussian borders must be entered in the ascending order!") return gaus_fact = str(self.factorsEdit.text()).split(',') for tal in gaus_fact: if not self.is_number(tal): QMessageBox.critical(self, 'Message', "Gaussian factors must be real numbers!") return elif float(tal) < 0.0: QMessageBox.critical( self, 'Message', "Gaussian factors must be positive or zero!") return if len(gaus_fact) < 1: QMessageBox.critical(self, 'Message', "You must enter at least 1 gaussian factor!") return if len(gaus_bord) != len(gaus_fact) + 1: QMessageBox.critical( self, 'Message', "The number of gaussian factors is exactly one less than the number of gaussian borders!" ) return ## ignored data points warnings and errors ign_pts = str(self.ignore_data_ptsEdit.text()) if not self.is_int(ign_pts): QMessageBox.critical( self, 'Message', "The number of ignored points is an integer!") return elif int(ign_pts) < 0: QMessageBox.critical( self, 'Message', "The number of ignored points is a positive integer!") return ## correction slit width warnings and errors corr_pts = str(self.corr_slitEdit.text()) if not self.is_number(corr_pts): QMessageBox.critical( self, 'Message', "The correction slit width is a real number!") return elif float(corr_pts) < 0: QMessageBox.critical( self, 'Message', "The correction slit width is a positive number!") return ## fit_poly_ranges warnings and errors if self.fit_poly_ranges_check == True: polyfit_bord = str(self.poly_bordersEdit.text()).split(',') for tal in polyfit_bord: if not self.is_number(tal): QMessageBox.critical( self, 'Message', "The polyfit range enteries must be real numbers!") return elif float(tal) < 0.0: QMessageBox.critical( self, 'Message', "The polyfit range enteries must be positive or zero!") return if len(polyfit_bord) < 2 or len(polyfit_bord) % 2 != 0: QMessageBox.critical( self, 'Message', "The polyfit range list accepts minimum 2 or even number of enteries!" ) return if not numpy.array_equal( [numpy.float(i) for i in polyfit_bord], numpy.sort([numpy.float(i) for i in polyfit_bord])): QMessageBox.critical( self, 'Message', "The polyfit range list must be entered in ascending order!" ) return # When all user defined enteries are approved save the data self.create_file(str(self.filenameEdit.text())) if sender.text() != 'Raw data': ## raw data files warnings and errors if not self.loadSubOlis_check and not self.loadSubFilmOlis_check: pass elif self.loadSubOlis_check and self.loadSubFilmOlis_check: pass else: QMessageBox.critical( self, 'Message', "Select both OLIS data files subfilmRAW and subRAW!") return if not self.loadSubFTIR_check and not self.loadSubFilmFTIR_check: pass elif self.loadSubFTIR_check and self.loadSubFilmFTIR_check: pass else: QMessageBox.critical( self, 'Message', "Select both FTIR data files subfilmRAW and subRAW!") return if not self.loadSubOlis_check and not self.loadSubFilmOlis_check and not self.loadSubFTIR_check and not self.loadSubFilmFTIR_check: QMessageBox.critical(self, 'Message', "No data files selected!") return if sender.text() == 'Raw data': if not self.loadSubOlis_check and not self.loadSubFilmOlis_check and not self.loadSubFTIR_check and not self.loadSubFilmFTIR_check: QMessageBox.critical(self, 'Message', "No raw data files selected!") return self.button_style(self.Step0_Button, 'red') self.button_style(self.Step1_Button, 'grey') self.button_style(self.Step2_Button, 'grey') self.button_style(self.Step3_Button, 'grey') self.button_style(self.Step4_Button, 'grey') self.button_style(self.Step5_Button, 'grey') elif sender.text() == 'Tmin and Tmax': self.button_style(self.Step1_Button, 'red') self.button_style(self.Step0_Button, 'grey') self.button_style(self.Step2_Button, 'grey') self.button_style(self.Step3_Button, 'grey') self.button_style(self.Step4_Button, 'grey') self.button_style(self.Step5_Button, 'grey') elif sender.text() == 'Std.Dev. in d': self.button_style(self.Step2_Button, 'red') self.button_style(self.Step0_Button, 'grey') self.button_style(self.Step1_Button, 'grey') self.button_style(self.Step3_Button, 'grey') self.button_style(self.Step4_Button, 'grey') self.button_style(self.Step5_Button, 'grey') elif sender.text() == 'Index n': self.button_style(self.Step3_Button, 'red') self.button_style(self.Step0_Button, 'grey') self.button_style(self.Step1_Button, 'grey') self.button_style(self.Step2_Button, 'grey') self.button_style(self.Step4_Button, 'grey') self.button_style(self.Step5_Button, 'grey') elif sender.text() == 'Absorption alpha': self.button_style(self.Step4_Button, 'red') self.button_style(self.Step0_Button, 'grey') self.button_style(self.Step1_Button, 'grey') self.button_style(self.Step2_Button, 'grey') self.button_style(self.Step3_Button, 'grey') self.button_style(self.Step5_Button, 'grey') elif sender.text() == 'Wavenumber k': self.button_style(self.Step5_Button, 'red') self.button_style(self.Step0_Button, 'grey') self.button_style(self.Step1_Button, 'grey') self.button_style(self.Step2_Button, 'grey') self.button_style(self.Step3_Button, 'grey') self.button_style(self.Step4_Button, 'grey') else: return worker = Worker(sender.text(), self.cwd) worker.signals.pass_plots.connect(self.pass_plots) worker.signals.critical.connect(self.critical) worker.signals.finished.connect(self.finished) # Execute self.threadpool.start(worker) self.isRunning = True def pass_plots(self, obj): self.my_plots, sender = obj my_str = 'Data files:\n' try: self.datafiles = self.my_plots.make_plots() for i, ii in zip(self.datafiles, range(len(self.datafiles))): head, tail = os.path.split(i) my_str += ''.join([str(ii + 1), ': ', tail, '\n']) self.NewFiles.setText(my_str) self.my_plots.show_plots() except Exception as inst: QMessageBox.critical(self, 'Message', str(inst)) def load_(self): # Initial read of the config file self.config = configparser.ConfigParser() try: self.config.read(''.join([self.cwd, os.sep, "config.ini"])) self.last_used_scan = self.config.get('LastScan', 'last_used_scan') self.loadSubOlis_str = self.config.get( self.last_used_scan, "loadsubolis").strip().split(':')[0] self.loadSubOlis_check = self.bool_( self.config.get(self.last_used_scan, 'loadsubolis').strip().split(':')[1]) self.loadSubFilmOlis_str = self.config.get( self.last_used_scan, 'loadsubfilmolis').strip().split(':')[0] self.loadSubFilmOlis_check = self.bool_( self.config.get(self.last_used_scan, 'loadsubfilmolis').strip().split(':')[1]) self.loadSubFTIR_str = self.config.get( self.last_used_scan, 'loadsubftir').strip().split(':')[0] self.loadSubFTIR_check = self.bool_( self.config.get(self.last_used_scan, 'loadsubftir').strip().split(':')[1]) self.loadSubFilmFTIR_str = self.config.get( self.last_used_scan, 'loadsubfilmftir').strip().split(':')[0] self.loadSubFilmFTIR_check = self.bool_( self.config.get(self.last_used_scan, 'loadsubfilmftir').strip().split(':')[1]) self.fit_linear_spline = self.config.get(self.last_used_scan, 'fit_linear_spline') self.gaussian_factors = self.config.get(self.last_used_scan, 'gaussian_factors') self.gaussian_borders = self.config.get(self.last_used_scan, 'gaussian_borders') self.ignore_data_pts = self.config.get(self.last_used_scan, 'ignore_data_pts') self.corr_slit = self.config.get(self.last_used_scan, 'corr_slit') self.fit_poly_order = self.config.get(self.last_used_scan, 'fit_poly_order') self.fit_poly_ranges = self.config.get( self.last_used_scan, 'fit_poly_ranges').strip().split(':')[0] self.fit_poly_ranges_check = self.bool_( self.config.get(self.last_used_scan, 'fit_poly_ranges').strip().split(':')[1]) self.filename_str = self.config.get(self.last_used_scan, 'filename') self.timetrace = self.config.get(self.last_used_scan, 'timetrace') self.save_figs = self.bool_( self.config.get(self.last_used_scan, 'save_figs')) self.plot_X = self.config.get(self.last_used_scan, 'plot_x') self.emailset_str = self.config.get(self.last_used_scan, 'emailset').strip().split(',') self.emailrec_str = self.config.get(self.last_used_scan, 'emailrec').strip().split(',') except configparser.NoOptionError as nov: QMessageBox.critical( self, 'Message', ''.join([ "Main FAULT while reading the config.ini file\n", str(nov) ])) raise def save_(self): self.timetrace = time.strftime("%y%m%d-%H%M") self.lcd.display(self.timetrace) self.config.read(''.join([self.cwd, os.sep, "config.ini"])) self.config.set('LastScan', "last_used_scan", self.last_used_scan) self.config.set( self.last_used_scan, "loadSubOlis", ':'.join([self.loadSubOlis_str, str(self.loadSubOlis_check)])) self.config.set( self.last_used_scan, "loadSubFilmOlis", ':'.join( [self.loadSubFilmOlis_str, str(self.loadSubFilmOlis_check)])) self.config.set( self.last_used_scan, "loadSubFTIR", ':'.join([self.loadSubFTIR_str, str(self.loadSubFTIR_check)])) self.config.set( self.last_used_scan, "loadSubFilmFTIR", ':'.join( [self.loadSubFilmFTIR_str, str(self.loadSubFilmFTIR_check)])) self.config.set(self.last_used_scan, "fit_linear_spline", self.fit_linear_spline) self.config.set(self.last_used_scan, "gaussian_factors", str(self.factorsEdit.text())) self.config.set(self.last_used_scan, "gaussian_borders", str(self.bordersEdit.text())) self.config.set(self.last_used_scan, "ignore_data_pts", str(self.ignore_data_ptsEdit.text())) self.config.set(self.last_used_scan, "corr_slit", str(self.corr_slitEdit.text())) self.config.set(self.last_used_scan, "fit_poly_order", str(self.fit_poly_order)) self.config.set( self.last_used_scan, "fit_poly_ranges", ':'.join([ str(self.poly_bordersEdit.text()), str(self.fit_poly_ranges_check) ])) self.config.set(self.last_used_scan, "filename", str(self.filenameEdit.text())) self.config.set(self.last_used_scan, "timetrace", self.timetrace) self.config.set(self.last_used_scan, "save_figs", str(self.save_figs)) self.config.set(self.last_used_scan, "plot_x", self.plot_X) with open(''.join([self.cwd, os.sep, "config.ini"]), "w") as configfile: self.config.write(configfile) def finished(self): self.my_plots.close_plots() self.load_() if self.emailset_str[1] == "yes": self.send_notif() if self.emailset_str[2] == "yes": self.send_data() self.button_style(self.Step0_Button, 'black') self.button_style(self.Step1_Button, 'black') self.button_style(self.Step2_Button, 'black') self.button_style(self.Step3_Button, 'black') self.button_style(self.Step4_Button, 'black') self.button_style(self.Step5_Button, 'black') self.isRunning = False def allButtons_torf(self, trueorfalse, *argv): if argv[0] == 'allfalse': self.cb_sub_olis.setEnabled(False) self.cb_subfilm_olis.setEnabled(False) self.cb_sub_ftir.setEnabled(False) self.cb_subfilm_ftir.setEnabled(False) self.poly_bordersEdit.setEnabled(False) self.fileSave.setEnabled(trueorfalse) self.loadMenu.setEnabled(trueorfalse) self.emailMenu.setEnabled(trueorfalse) self.cb_save_figs.setEnabled(trueorfalse) self.cb_polybord.setEnabled(trueorfalse) self.Step0_Button.setEnabled(trueorfalse) self.Step1_Button.setEnabled(trueorfalse) self.Step2_Button.setEnabled(trueorfalse) self.Step3_Button.setEnabled(trueorfalse) self.Step4_Button.setEnabled(trueorfalse) self.Step5_Button.setEnabled(trueorfalse) self.combo1.setEnabled(trueorfalse) self.combo2.setEnabled(trueorfalse) self.combo4.setEnabled(trueorfalse) self.factorsEdit.setEnabled(trueorfalse) self.bordersEdit.setEnabled(trueorfalse) self.ignore_data_ptsEdit.setEnabled(trueorfalse) self.corr_slitEdit.setEnabled(trueorfalse) self.filenameEdit.setEnabled(trueorfalse) def warning(self, mystr): QMessageBox.warning(self, "Message", mystr) def send_notif(self): contents = [ "The scan is done. Please visit the experiment site and make sure that all light sources are switched off." ] subject = "The scan is done" obj = type( "obj", (object, ), { "subject": subject, "contents": contents, "settings": self.emailset_str, "receivers": self.emailrec_str }) worker = Send_Email_Worker(obj) worker.signals.critical.connect(self.critical) worker.signals.warning.connect(self.warning) worker.signals.finished.connect(self.finished1) # Execute self.md = Indicator_dialog.Indicator_dialog( self, "...sending notification...", "indicators/ajax-loader-ball.gif") self.threadpool.start(worker) self.isRunning = True def send_data(self): contents = [ "The scan is done and the logged data is attached to this email. Please visit the experiment site and make sure that all light sources are switched off." ] contents.extend(self.datafiles) subject = "The scan data from the latest scan!" obj = type( "obj", (object, ), { "subject": subject, "contents": contents, "settings": self.emailset_str, "receivers": self.emailrec_str }) worker = Send_Email_Worker(obj) worker.signals.critical.connect(self.critical) worker.signals.warning.connect(self.warning) worker.signals.finished.connect(self.finished1) # Execute self.md = Indicator_dialog.Indicator_dialog( self, "...sending files...", "indicators/ajax-loader-ball.gif") self.threadpool.start(worker) self.isRunning = True def finished1(self): self.isRunning = False self.md.close_() def critical(self, mystr): QMessageBox.critical(self, 'Message', mystr) def closeEvent(self, event): reply = QMessageBox.question(self, 'Message', "Quit now?", QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: if self.isRunning: QMessageBox.warning( self, 'Message', "Analysis in progress. Wait the analysis to finish and then quit!" ) event.ignore() else: event.accept() elif reply == QMessageBox.No: event.ignore()
class Main(QMainWindow): #MAIN WINDOW def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.dealerStep = DM.DealerStepper() #setup motors self.dealerDC = DM.DealerDC() self.display(0) self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) self.ui.page1label1.setText("ARTOPS: Blackjack") self.ui.page3hitButton.clicked.connect(lambda: self.roundMainTrigger(0)) #setup the UI button functions self.ui.page3stickButton.clicked.connect(lambda: self.roundMainTrigger(1)) self.ui.page7statsButton.clicked.connect(lambda: self.statisticsPage()) self.ui.page7resetButton.clicked.connect(lambda: self.resetTrigger()) self.ui.page8resetButton.clicked.connect(lambda: self.resetTrigger()) self.ui.pageErrorButton1.clicked.connect(lambda: self.resetTrigger()) self.shortcut = QShortcut(QKeySequence("Ctrl+E"), self) self.shortcut.activated.connect(self.exitApp) self.shortcut = QShortcut(QKeySequence("Ctrl+R"), self) self.shortcut.activated.connect(self.resetTrigger) self.showFullScreen() self.initialise() def initialise(self): """Ran at start of each game - resets variables and text""" self.playerScore,self.dealerScore,self.playerCards,self.dealerCards = 0,0,0,0 self.playerCardList = [] self.dealerCardList = [] self.playerCamera = 0 self.dealerCamera = 2 self.roundNum = 0 self.ui.page2label1.setText("Shuffling and dealing...") self.display(0) worker = Worker(self.waitForStart) self.threadpool.start(worker) def waitForStart(self): """wait for button press to start""" GPIO.output(7, 1) #button LED on while True: button_state = GPIO.input(15) if button_state == False: print('Button Pressed...') GPIO.output(7, 0) #button LED off return self.shuffle() def shuffle(self): """gui side shuffle""" self.display(1) self.shuffleComplete = False # Pass the function to execute worker = Worker(self.shuffleThread) self.threadpool.start(worker) def shuffleThread(self): """multithreaded shuffle""" #Connect to Arduino ser = serial.Serial('/dev/ttyACM0',9600, timeout = 0.1) time.sleep(1) ser.flushInput() #Send Request to Shuffle Cards ser.write(b'1') print('Shuffling') #Wait until Confirmation (handshake) self.shuffleComplete = False while self.shuffleComplete == False: data = ser.readline() if data: print('Shuffled') self.shuffleComplete = True self.dealerDC.pullBack() #pull in the cards self.startingDeal() def startingDeal(self): """Deal initial cards, and prepare for hit/stick page""" #deal first two cards and read them for both sides self.dealerStep.gotoPlayerSide() self.dealCard('playerSide') #deal two cards self.ui.page2label1.setText("Please take your cards") self.dealerStep.gotoDealerSide() self.dealCard('dealerSide') #set 'My first card was a ...' text on hit/stick page if self.dealerCardList[0] == 1: self.ui.page3label2.setText('Ace') elif self.dealerCardList[0] == 11: self.ui.page3label2.setText('Jack') elif self.dealerCardList[0] == 12: self.ui.page3label2.setText('Queen') elif self.dealerCardList[0] == 13: self.ui.page3label2.setText('King') else: self.ui.page3label2.setText(str(self.dealerCardList[0])) #switch to hit or stick worker = Worker(self.dealerStep.gotoPlayerSide) self.threadpool.start(worker) self.display(2) def dealCard(self,X): """general single card deal then read method.""" worker = Worker(self.dealerDC.dealOneCard) self.threadpool.start(worker) if X == 'playerSide': #card detection for playerside card deal try: openCV = CD.CardDetection(self.playerCamera) except: self.dealCard(X) if openCV.card_rank > 10: self.playerScore += 10 #if the card is a jack queen or king add ten to the score else: self.playerScore += openCV.card_rank self.playerCards += 1 self.playerCardList.append(openCV.card_rank) if self.playerCards<2: #if its the first round call the read card function again self.dealCard(X) print(self.playerCardList) elif X == 'dealerSide': #card detection for dealerside card deal try: openCV = CD.CardDetection(self.dealerCamera) except: self.dealCard(X) if openCV.card_rank > 10: self.dealerScore += 10 else: self.dealerScore += openCV.card_rank self.dealerCards += 1 self.dealerCardList.append(openCV.card_rank) if self.dealerCards<2: self.dealCard(X) print(self.dealerCardList) def errorPage(self): """general error trigger""" self.display(8) def roundMainTrigger(self, playerAction): """GUI side main round trigger""" self.ui.page4label2.setText('???') self.display(3) self.playerAction = playerAction worker = Worker(self.roundMain) self.threadpool.start(worker) def roundMain(self): """main round method""" print('round main') #playerAction 0 is hit, 1 is stick self.roundNum += 1 if self.playerAction == 0: self.dealerStep.gotoPlayerSide() self.dealCard('playerSide') self.dealerStep.gotoDealerSide() decision = self.decision(self.dealerCardList) #run decision method on dealers current cards if decision == 0: self.ui.page4label2.setText('HIT!') self.dealCard('dealerSide') elif decision == 1: self.ui.page4label2.setText('STICK!') time.sleep(1) self.dealerStep.gotoPlayerSide() if self.playerAction == 1 and decision == 1: #if both players choose to stick self.display(4) time.sleep(4) return self.finishGame() elif self.playerScore > 21 or self.dealerScore > 21: #if either player has gone bust return self.finishGame() else: return self.display(2) #return to hit or stick page def decision(self,cards): """Dealer decision method, provides 1(hit) or 0(stick) from current cards""" if cards == []: return 1 total_val = 340 #total value of all cards score = 0 for i in cards: if i > 10: #score calculator left from previous file i = 10 score += i print(score) total_val -= score prediction = total_val/(52-len(cards)) #find the average remaining card score if score + prediction <= 21: return 0 #hit else: return 1 #stick def finishGame(self): """finish the game and update running total""" self.calculateScore() os.getcwd f = open("running_total.txt", 'r+') #reading then updating running_total txt file for total games won self.dealerTotal = int(f.readline()) self.dealerTotal += self.dealerResult self.playerTotal = int(f.readline()) self.playerTotal += self.playerResult f.truncate(0) f.seek(0,0) f.write(str(self.dealerTotal)) f.write('\n' + str(self.playerTotal)) f.close() self.ui.page8label3.setText(str(self.dealerTotal)) #setup results page self.ui.page8label5.setText(str(self.playerTotal)) print(self.playerCardList, self.dealerCardList) self.display(5) def calculateScore(self): """calculte the player and dealers score and find the winner. Update the gui """ if self.dealerScore >21: self.dealerScore = 0 self.ui.page7label3.setText('BUST') else: self.ui.page7label3.setText(str(self.dealerScore)) if self.playerScore >21: self.playerScore = 0 self.ui.page7label5.setText('BUST') else: self.ui.page7label5.setText(str(self.playerScore)) if self.dealerScore > self.playerScore: self.dealerResult = 1 self.playerResult = 0 self.ui.page7label6.setText('I Win!') elif self.playerScore > self.dealerScore: self.dealerResult = 0 self.playerResult = 1 self.ui.page7label6.setText('You Win!') else: self.dealerResult = 0 self.playerResult = 0 self.ui.page7label6.setText('Draw!') return def statisticsPage(self): """stats page control""" print("stats") self.display(6) def resetTrigger(self): """reset gui trigger""" print("PLAY AGAIN") self.display(7) #display collect cards message worker = Worker(self.reset) self.threadpool.start(worker) def reset(self): """reset game for playing again""" self.dealerStep.gotoPlayerSide() #deal remaining pack onto player side self.dealerDC.dealWholePack() time.sleep(10) self.dealerStep.gotoShuffler() self.initialise() def display(self,i): """display the chosen stacked widget ie the page of the gui""" print('setting index', i) self.ui.stackedWidget.setCurrentIndex(i) @pyqtSlot() def exitApp(self): #exit method self.dealerStep.gotoShuffler GPIO.cleanup() sys.exit()
class RCMMainWindow(QMainWindow): def __init__(self): super().__init__() pack_info = rcm_utils.pack_info() title = "Remote Connection Manager - CINECA - v" + pack_info.rcmVersion self.setWindowTitle(title) width = 1000 height = 370 screen_width = QDesktopWidget().width() screen_height = QDesktopWidget().height() self.setGeometry((screen_width / 2) - (width / 2), (screen_height / 2) - (height / 2), width, height) self.setMinimumHeight(height) self.setMinimumWidth(width) self.build_menu() self.main_widget = MainWidget(self) self.setCentralWidget(self.main_widget) self.thread_pool = QThreadPool() logger.info("Welcome to RCM!") logger.debug("Multithreading with maximum %d threads" % self.thread_pool.maxThreadCount()) def build_menu(self): """ build and add menu to the application :return: """ # Create new action new_action = QAction(QIcon(resource_path('gui/icons/new.png')), '&New', self) new_action.setShortcut('Ctrl+N') new_action.setStatusTip('New VNC session') new_action.triggered.connect(self.new_vnc_session) # Create new action open_action = QAction(QIcon(resource_path('gui/icons/open.png')), '&Open', self) open_action.setShortcut('Ctrl+O') open_action.setStatusTip('Open VNC session') open_action.triggered.connect(self.open_vnc_session) # Create exit action exit_action = QAction(QIcon(resource_path('gui/icons/exit.png')), '&Exit', self) exit_action.setShortcut('Ctrl+Q') exit_action.setStatusTip('Exit application') exit_action.triggered.connect(self.exit) # Create the settings action edit_settings_action = QAction('&Settings', self) edit_settings_action.setShortcut('Ctrl+S') edit_settings_action.setStatusTip('Custom the application settings') edit_settings_action.triggered.connect(self.edit_settings) # Create the about action about_action = QAction('&About', self) about_action.setShortcut('Ctrl+A') about_action.setStatusTip('About the application') about_action.triggered.connect(self.about) # Create menu bar and add actions menu_bar = self.menuBar() file_menu = menu_bar.addMenu('&File') file_menu.addAction(new_action) file_menu.addAction(open_action) file_menu.addAction(exit_action) edit_menu = menu_bar.addMenu('&Edit') edit_menu.addAction(edit_settings_action) help_menu = menu_bar.addMenu('&Help') help_menu.addAction(about_action) def new_vnc_session(self): last_tab_id = self.main_widget.tabs.count() - 1 last_tab_uuid = self.main_widget.tabs.widget(last_tab_id).uuid kill_btn = QPushButton() kill_btn.setIcon(self.style().standardIcon( QStyle.SP_DialogCloseButton)) kill_btn.clicked.connect(lambda: self.on_close(last_tab_uuid)) kill_btn.setToolTip('Close session') self.main_widget.tabs.setTabText(last_tab_id, "Login...") self.main_widget.tabs.tabBar().setTabButton(last_tab_id, QTabBar.RightSide, kill_btn) self.main_widget.add_new_tab("", False) def open_vnc_session(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog filename, _ = QFileDialog.getOpenFileName( self, "Open...", "", "VNC Files (*.vnc);;All Files (*)", options=options) current_session_widget = self.main_widget.tabs.currentWidget() if filename: # check if session needs tunneling file = open(filename, 'r') if 'rcm_tunnel' in file.read(): file.seek(0) lines = file.readlines() for line in lines: if 'rcm_tunnel' in line: node = line.split('=')[1].rstrip() if not current_session_widget.is_logged: logger.error( "You are not logged in the current session. Please log in." ) return if node == current_session_widget.host: user = current_session_widget.user else: logger.error( "The host of the current session (" + current_session_widget.host + ") is different from the host of the vnc file (" + node + ")") return if 'host' in line: hostname = line.split('=')[1].rstrip() if 'port' in line: port = line.split('=')[1].rstrip() display = int(port) - 5900 if 'password' in line: password = line.split('=')[1].rstrip() session = rcm.rcm_session(node=hostname, tunnel='y', display=display, nodelogin=node, username=user, vncpassword=password) current_session_widget.remote_connection_manager.vncsession( session=session) logger.info("Connected to remote display " + str(display) + " on " + node + " as " + str(user) + " with tunnel") else: current_session_widget.remote_connection_manager.vncsession( configFile=filename) def closeEvent(self, QCloseEvent): self.exit() def exit(self): """ kill all the pending threads, then close the application """ for tab_id in range(0, self.main_widget.tabs.count()): widget = self.main_widget.tabs.widget(tab_id) widget.kill_all_threads() self.close() def edit_settings(self): edit_settings_dlg = QEditSettingsDialog(self) edit_settings_dlg.setModal(True) edit_settings_dlg.exec() def about(self): QMessageBox.about(self, "RCM", self.windowTitle()) return @pyqtSlot() def on_close(self, uuid): # loop over the tabs and found the tab with the right uuid for tab_id in range(0, self.main_widget.tabs.count()): widget = self.main_widget.tabs.widget(tab_id) if widget.uuid == uuid: if self.main_widget.tabs.currentIndex( ) == self.main_widget.tabs.count() - 2: self.main_widget.tabs.setCurrentIndex(tab_id - 1) self.main_widget.tabs.removeTab(tab_id) return
class GridOperator(QObject): update_logger = pyqtSignal(name='update_logger') exec_pending = pyqtSignal(name='exec_pending') def __init__(self, grid): super().__init__() logging.debug('__init__() called on GridOperator') self.grid = grid self.stop_flag = False self.retry_counter = 0 self.delay = 0 self.threadpool = QThreadPool() self.b_debug_window = False self.pending_return = [] self.exec_pending.connect(self.checkPending) mp.set_start_method('spawn') logging.debug('__init__() GridOperator, threadCount: {}'.format( self.threadpool.maxThreadCount())) def startExec(self, start_pos, record=None): logging.debug('startExec() called, start_pos = {}'.format(start_pos)) try: element = self.grid.itemAtPosition(*start_pos).widget() except AttributeError as e: return if self.stop_flag: return self.update_logger.emit() executor = Executor(element, record, self.delay) executor.signals.finished.connect(self.execDone) element.highlightStart() self.threadpool.start(executor) def execDone(self, prg_return): logging.debug('execDone() called GridOperator from {}'.format( prg_return.source)) element = self.grid.itemAtPosition(*prg_return.source).widget() if (issubclass(prg_return.record_0.__class__, BaseException)): logging.error('Target {}|{} Exception found: {}'.format( prg_return.source[0], alphabet[prg_return.source[1]], prg_return.record_0)) element.highlightException() self.exceptwindow = ExceptWindow(str(prg_return.record_0), prg_return.source) self.exceptwindow.window_closed.connect(self.highlightStop) return # when the log fiel is set if prg_return.log: if prg_return.log_txt: logging.info('Message {}|{} : {}'.format( prg_return.source[0], alphabet[prg_return.source[1]], prg_return.log_txt)) if prg_return.log_output: log = prg_return.log_output else: log = prg_return.record_0 logging.info('Output {}|{} : {}'.format( prg_return.source[0], alphabet[prg_return.source[1]], log)) # when the log button is enabled if element.b_debug: if prg_return.log_output: log_message = prg_return.log_output else: log_message = str(prg_return.record_0) logging.debug('execDone() b_debug_window = {}'.format( self.b_debug_window)) if not self.b_debug_window: self.debugWindow = DebugWindow(log_message, prg_return.source) self.debugWindow.proceed_execution.connect( lambda: self.proceedExec(prg_return)) self.debugWindow.raiseWindow() self.b_debug_window = True else: # Aktuellen stand für erneute ausführung vormerken self.pending_return.append(prg_return) else: # highlight stop =! element.highlightStop() self.goNext(prg_return) def checkPending(self): logging.debug('checkPending() called') if self.pending_return: prg_return = self.pending_return.pop(0) self.execDone(prg_return) def proceedExec(self, prg_return): element = self.grid.itemAtPosition(*prg_return.source).widget() element.highlightStop() self.b_debug_window = False self.exec_pending.emit() self.goNext(prg_return) def goNext(self, prg_return): if prg_return.target_0: logging.debug('goNext() called with next target_0: {}'.format( prg_return.target_0)) logging.debug('goNext() called with record_0: {}'.format( prg_return.record_0)) self.startExec(prg_return.target_0, prg_return.record_0) if prg_return.target_1: logging.debug( 'goNext() called with additional target_1: {}'.format( prg_return.target_1)) logging.debug('goNext() called with record_1: {}'.format( prg_return.record_1)) self.startExec(prg_return.target_1, prg_return.record_1) def highlightStop(self, position): logging.debug( 'highlightStop() called for position {}'.format(position)) element = self.grid.itemAtPosition(*position).widget() element.highlightStop() def stop_execution(self): logging.debug('stop_execution() called') self.stop_flag = True
class JetsonWindow(QMainWindow): # class constructor def __init__(self): # call QWidget constructor super().__init__() self.ui = Ui_JetsonWindow() self.ui.setupUi(self) self.root = tk.Tk() self.w = self.root.winfo_screenwidth() self.h = self.root.winfo_screenheight() self.queue = Queue() self.image = None self.box = None self.shortcut = QShortcut(QKeySequence("Ctrl+Esc"), self) self.shortcut.activated.connect(self.closeApp) self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) # self.server_url = 'http://192.168.1.3:5001/' self.p = pyaudio.PyAudio() self.stream = self.p.open(rate=16000, channels=1, format=pyaudio.paInt16, input=True, frames_per_buffer=512, input_device_index=11, stream_callback=self.callback) self.stream.start_stream() self.list = [] with open('DATA.csv', newline='') as f: reader = list(csv.reader(f)) for data in reader: self.list.append(data[0]) self.server_url = self.list.pop() self.list.append('NOT IDENTIFIED') print(f"Server url: {self.server_url}") print(f"ID List: {self.list}") # self.list = ['1752015', '1752259', '1752041', 'NOT INDENTIFIED'] # create a timer self.timer = QTimer() # set timer timeout callback function that check temporary ID self.timer.timeout.connect(self.Attendance) #Run Face Recognition self.cap = cv2.VideoCapture(self.gstreamer_pipeline(), cv2.CAP_GSTREAMER) self.flag = 0 self.timer.start(20) def closeApp(self): app.quit() def callback(self, in_data, frame_count, time_info, status): self.queue.put(in_data) return (in_data, pyaudio.paContinue) def gstreamer_pipeline( self, capture_width=1280, capture_height=720, display_width=1280, display_height=720, framerate=90, flip_method=0, ): return ( "nvarguscamerasrc ! " "video/x-raw(memory:NVMM), " "width=(int)%d, height=(int)%d, " "format=(string)NV12, framerate=(fraction)%d/1 ! " "nvvidconv flip-method=%d ! " "video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! " "videoconvert ! " "video/x-raw, format=(string)BGR ! appsink" % ( capture_width, capture_height, framerate, flip_method, display_width, display_height, )) def progress_fn(self, n): n = int(n) print("%d%% done" % n) if n == 0: self.ui.infor_label.setText("Recording |") if n == 20: self.ui.infor_label.setText("Recording /") if n == 40: self.ui.infor_label.setText("Recording -") if n == 60: self.ui.infor_label.setText("Recording \\") if n == 80: self.ui.infor_label.setText("Recording |") if n == 91: self.ui.infor_label.setText("Done Recording") def execute_voice(self, progress_callback): # main function voiceframes = [] p = pyaudio.PyAudio() stream = p.open( rate=16000, channels=1, format=pyaudio.paInt16, input=True, frames_per_buffer=512, input_device_index=11, ) stream.start_stream() for i in range(0, int(16000 / 512 * 3)): voiceframes.append(stream.read(512)) progress_callback.emit(i) p.terminate() stream.close() return voiceframes def print_output(self, voice): # send server voice = list(voice) print("Output printing...") encapsulate_face = pickle.dumps(self.image, protocol=pickle.HIGHEST_PROTOCOL) encapsulate_voice = pickle.dumps(voice, protocol=pickle.HIGHEST_PROTOCOL) face_response = requests.post(self.server_url + 'face', data=encapsulate_face).json() face_result = np.array(face_response['data']) voice_response = requests.post(self.server_url + 'voice', data=encapsulate_voice).json()['data'] voice_result = np.array(voice_response) # result = np.add(face_result*0.8, voice_result*0.2) result = 2 * face_result * voice_result / (face_result + voice_result) if np.any(result > 0.9): name = "Device 1 \n" + self.list[result.argmax( )] + f': {round(result[result.argmax()] * 100 , 1)}' requests.post(url_check, data={ 'roomId': 'A4405', 'stuId': self.list[result.argmax()] }) else: name = "Device 1 \n" + self.list[-1] self.ui.infor_label.setText(name) def thread_complete(self): print("THREAD COMPLETE!") self.p = pyaudio.PyAudio() self.stream = self.p.open(rate=16000, channels=1, format=pyaudio.paInt16, input=True, frames_per_buffer=512, input_device_index=11, stream_callback=self.callback) self.stream.start_stream() def recording(self): self.p.terminate() self.stream.close() # Pass the function to execute record = Record( self.execute_voice ) # Any other args, kwargs are passed to the run function record.signals.result.connect(self.print_output) record.signals.finished.connect(self.thread_complete) record.signals.progress.connect(self.progress_fn) # Execute self.threadpool.start(record) def Attendance(self): ret, self.image = self.cap.read() img = self.image # cv2.rectangle(img, (480, 180), (800, 600), (0, 0, 0), thickness=2) procimage = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # get image infos height, width, channel = procimage.shape step = channel * width # create QImage from image qImg = QImage(procimage.data, width, height, step, QImage.Format_RGB888) # show image in img_label wi = self.ui.image_label.width() he = self.ui.image_label.height() self.ui.image_label.setPixmap( QPixmap.fromImage(qImg).scaled(wi, he, QtCore.Qt.KeepAspectRatio)) if self.queue.qsize() > 0: while self.queue.qsize() > 32: self.queue.get() buff = [] if self.queue.qsize() >= 32: while self.queue.qsize() > 0: buff.append(self.queue.get()) ans = detection.detector.RunDetection(b''.join(buff)) if ans == 1: print("success") self.recording() return 0
# -*- coding: utf-8 -*- # async guidance from # https://martinfitzpatrick.name/article/multithreading-pyqt-applications-with-qthreadpool/ # (didn't use inbuilt python async/await since it requires an event/thread loop which it seems # qt itself isn't playing nice with... maybe?) from PyQt5.QtCore import QRunnable, pyqtSlot, QThreadPool threadpool = QThreadPool() print("Multithreading with maximum %d threads" % threadpool.maxThreadCount()) class Worker(QRunnable): ''' Worker thread Inherits from QRunnable to handler worker thread setup, signals and wrap-up. :param callback: The function callback to run on this worker thread. Supplied args and kwargs will be passed through to the runner. :type callback: function :param args: Arguments to pass to the callback function :param kwargs: Keywords to pass to the callback function ''' def __init__(self, fn, *args, **kwargs): super(Worker, self).__init__() # Store constructor arguments (re-used for processing) self.fn = fn
class Ui(QtWidgets.QMainWindow, ihesync_app.Ui_MainWindow): def __init__(self, context, parent=None): super(Ui, self).__init__(parent) self.logger = logging.getLogger() self.network_available = False self.network_watchdog = None self.setupUi(self) self.context = context self.changed = False self.label_ihewebsite.setText('Visit IHE Website : <a href="http://www.ihe.net">www.ihe.net</a>') self.label_ihewebsite.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) self.label_ihewebsite.setOpenExternalLinks(True) self.modifed_label = QLabel("Status: No change") self.network_label = QLabel("No network!") self.statusBar().setStyleSheet('border: 0; background-color: #FFF8DC;') self.statusBar().setStyleSheet("QStatusBar::item {border: none;}") self.statusBar().addPermanentWidget(VLine()) self.statusBar().addPermanentWidget(self.network_label) self.statusBar().addPermanentWidget(VLine()) self.statusBar().addPermanentWidget(self.modifed_label) self.threadpool = QThreadPool() self.threadpool.setMaxThreadCount(4) self.doc_model = documents_model.DocumentsModel([]) self.tableView.setModel(self.doc_model) self.tableView.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.ResizeToContents ) icon_delegate = OpenFolderDelegate(self.tableView) self.tableView.setItemDelegateForColumn(4, icon_delegate) self.tableView.clicked.connect(self.open_documents_folder) self.logger.info("Starts with %d threads" % self.threadpool.maxThreadCount()) self.tabWidget.currentChanged.connect(self.current_tab_changed) if platform.system() in STYLES: self.setStyleSheet(STYLES[platform.system()]) def main(self): conf_loaded = self.context.load_configuration() # default size (w, h) = (800, 700) if platform.system() in MAIN_WINDOW_DEFAULT_SIZE: (w, h) = MAIN_WINDOW_DEFAULT_SIZE[platform.system()] # overwritten size = user prefs if self.context.sync.geometry != (0, 0): (w, h) = self.context.sync.geometry self.centralwidget.parentWidget().resize(w, h) self.show() self.start_network_watchdog() if not conf_loaded: self.change_status("Configuration loading error : check network") self.refresh_configuration() self.refresh_information_counts() self.refresh_domain_list() self.refresh_public_comment() if self.context.local_file_count_ondisk != self.context.local_file_count: self.msgbox_out_of_sync() # -- Messages boxes --- # --------------------- def msgbox_out_of_sync(self): msg = QMessageBox.about( self, "Information...", ( f"Out of sync ! Local files on disk {self.context.local_file_count_ondisk}" f" vs in configuration {self.context.local_file_count}\n" f" Sync needed" ), ) msg.setIcon(QMessageBox.Warning) def msgbox_network_unavailable(self): QMessageBox.critical(self, "Network unavailable", ( f"No network" f" Check\n"), ) # -- Messages boxes --- # -- Check Network def update_network_status(self, data): (ip, self.network_available) = data self.logger.debug(f"network status updated availaible = {self.network_available}") if self.network_available: self.network_label.setText("Connected") self.network_label.setStyleSheet('border: 0; color: green;') else: self.network_label.setText("no Network!") self.network_label.setStyleSheet('border: 0; color: red;') def start_network_watchdog(self): self.network_watchdog = sync_worker.NetworkWorker(ihesync.sync.IHE_URL, self.context.sync.proxy, self.context.sync.ping_delay) self.network_watchdog.signals.progress.connect(self.update_network_status) self.threadpool.start(self.network_watchdog) def stop_network_watchdog(self): self.logger.debug("Stop network watchdog...") self.network_watchdog.abort() # -- Refresh counts def refresh_public_comment(self): state = ( QtCore.Qt.Checked if self.context.sync.public_comment else QtCore.Qt.Unchecked ) self.checkComments.setCheckState(state) def refresh_last_checked(self): """ Refresh date of the last check :return: - """ if self.context.sync.last_check: self.labelLastCheckDate.setText( self.context.sync.last_check.strftime("%Y-%m-%d %H:%M") ) def refresh_domain_list(self) -> None: """ Refresh the domain table - sorted by domain name - get the count of local files :return: """ self.logger.info("refresh_domain_list") self.doc_model.log() self.labelDomainCountValue.setText(str(len(self.context.domains))) self.doc_model.set_documents(None) data = [] for domain in sorted(self.context.domains, key=lambda v: v["name"]): local_count = self.context.sync.count_local_files(domain["name"]) self.logger.debug(f"> domain >> {domain} - local cout = {local_count}") data.append( { "checked": domain["name"] in self.context.selected_domains or domain["checked"], "domain": domain["name"], "title": DOMAIN_DICT[domain["name"]] if domain["name"] in DOMAIN_DICT else "", "down": local_count, "total": domain["files"], "link": local_count > 0, "local": local_count, "error": 0 } ) self.doc_model.set_documents(data) self.tableView.model().layoutChanged.emit() def refresh_documents_directory_geometry(self): # get configuration directory geometry for height (conf_x, conf_y, conf_w, conf_h) = self.textConfDir.geometry().getRect() (x, y, w, h) = self.textDocDir.geometry().getRect() if len(self.context.doc_directory) > 60: h = conf_h * 2 else: h = conf_h self.textDocDir.setGeometry(x, y, w, h) def refresh_configuration(self) -> None: """ Refresh the configuration tab :return: - """ self.textConfDir.setText(str(self.context.conf_directory)) self.textDocDir.setText(str(self.context.doc_directory)) self.refresh_documents_directory_geometry() self.newDocsGroupBox.setVisible(False) self.textLoggingFilename.setText(str(self.context.sync.log_filename)) rad = dict(INFO=self.infoRadioButton, ERROR=self.errorRadioButton, DEBUG=self.debugRadioButton) if self.context.sync.log_level in rad: rad[self.context.sync.log_level].setChecked(True) self.textPingDelay.setText(str(self.context.sync.ping_delay)) if self.context.sync.proxy: self.textProxyAddress.setText(self.context.sync.proxy['address']) self.textProxyPort.setText(self.context.sync.proxy['port']) if self.context.sync.proxy["active"]: self.specificProxyRadioButton.setChecked(self.context.sync.proxy["active"]) self.noProxyRadioButton.setChecked(not self.context.sync.proxy["active"]) self.update_proxy_state() else: self.noProxyRadioButton.setChecked(True) def refresh_information_counts(self) -> None: """ Refresh counters and date last checked :return: """ self.logger.debug("refresh_information_counts") self.newDocsGroupBox.setVisible(False) self.refresh_last_checked() self.context.scan_local_dirs() self.context.refresh_counts_current() self.labelDocumentCountValue.setText(str(self.context.file_count)) self.labelLocalFilesCountValue.setText(str("{}/{}" .format(self.context.local_file_count_ondisk, self.context.local_file_count))) diff = self.context.check_updates_available() if diff > 0: self.newDocLabel.setText(f"{diff} document changes") self.newDocsGroupBox.setVisible(True) def change_status(self, msg=None, changed=None, duration=3000): if changed: self.changed = changed self.modifed_label.setText("Status: Changed !" if self.changed else "Status: No change") if msg: self.statusbar.showMessage(msg, duration) # -- UI callback @pyqtSlot() def on_checkComments_clicked(self): self.context.sync.public_comment = self.checkComments.checkState() == QtCore.Qt.Checked self.change_status(changed=True) @pyqtSlot() def on_aboutPushButton_clicked(self): dlg = dialogs.AboutDialog(self) dlg.main() @pyqtSlot() def on_infoRadioButton_clicked(self): self.context.sync.update_logger_config(level="INFO") if self.context.sync.log_level != "INFO": self.change_status(changed=True) @pyqtSlot() def on_errorRadioButton_clicked(self): self.context.sync.update_logger_config(level="ERROR") if self.context.sync.log_level != "ERROR": self.change_status(changed=True) @pyqtSlot() def on_debugRadioButton_clicked(self): self.context.sync.update_logger_config(level="DEBUG") if self.context.sync.log_level != "DEBUG": self.change_status(changed=True) @pyqtSlot() def on_noProxyRadioButton_clicked(self): self.change_status(changed=True) self.context.sync.proxy["active"] = False self.textProxyAddress.setDisabled(True) self.textProxyPort.setDisabled(True) @pyqtSlot() def on_specificProxyRadioButton_clicked(self): self.change_status(changed=True) self.context.sync.proxy["active"] = True self.textProxyAddress.setDisabled(False) self.textProxyPort.setDisabled(False) @pyqtSlot() def on_changeProxyPushButton_clicked(self): self.change_status(changed=True) self.context.sync.proxy['address'] = str(self.textProxyAddress.toPlainText()) self.context.sync.proxy['port'] = str(self.textProxyPort.toPlainText()) self.update_proxy_state() @pyqtSlot() def on_changeLogPushButton_clicked(self): self.context.sync.update_logger_config(filename=self.textLoggingFilename.toPlainText()) self.change_status(changed=True) @pyqtSlot() def on_deleteLogPushButton_clicked(self): try: os.remove(self.textLoggingFilename.toPlainText()) except OSError as e: self.logger.error(f"Can't remove {self.textLoggingFilename.toPlainText()} : {str(e)}") @pyqtSlot() def on_openLogPushButton_clicked(self): if os.path.exists(self.textLoggingFilename.toPlainText()): webbrowser.open_new(f"file://{self.textLoggingFilename.toPlainText()}") else: self.logger.error(f"Can't open file {self.textLoggingFilename.toPlainText()} which does not exist!") @pyqtSlot() def on_textConfDir_textChanged(self): self.context.conf_directory = self.textConfDir.toPlainText() self.change_status(changed=True) @pyqtSlot() def on_textDocDir_textChanged(self): self.context.doc_directory = self.textDocDir.toPlainText() self.change_status(changed=True) @pyqtSlot() def on_confSelectButton_clicked(self): previous = self.textConfDir.toPlainText() confdir = QFileDialog.getExistingDirectory(self, "Select Directory", previous, QFileDialog.ShowDirsOnly) if len(confdir): self.context.conf_directory = str(confdir) self.textConfDir.setText(self.context.conf_directory) self.change_status(changed=True) @pyqtSlot() def on_docSelectButton_clicked(self): previous = self.textDocDir.toPlainText() docdir = QFileDialog.getExistingDirectory(self, "Select Directory", previous, QFileDialog.ShowDirsOnly) if len(docdir): self.context.doc_directory = str(docdir) self.textDocDir.setText(self.context.doc_directory) self.change_status(changed=True) self.refresh_documents_directory_geometry() @pyqtSlot() def on_syncButton_clicked(self): if self.network_available: self.prepare_synchronization() else: self.msgbox_network_unavailable() @pyqtSlot() def on_changeConnectionPushButton_clicked(self): # get values before change new_delay = self.context.sync.ping_delay try: new_delay = int(self.textPingDelay.toPlainText()) except ValueError as d_err: self.change_status("Delay value must be numeric !!!") self.logger.error(f"Configuration error while setting non numeric value for delay {d_err}") self.textPingDelay.setText(str(new_delay)) if new_delay != self.context.sync.ping_delay: self.context.sync.ping_delay = new_delay self.stop_network_watchdog() self.start_network_watchdog() self.change_status("Ping informations changed.") @pyqtSlot() def on_synchronize_confirmed(self): self.logger.debug("on_synchronize_confirmed") self.do_synchronization() @pyqtSlot() def on_synchronize_rejected(self): self.context.revert_sync() def current_tab_changed(self, tab_number): if tab_number == 0: self.refresh_information_counts() self.refresh_domain_list() def closeEvent(self, event): # save new data self.logger.debug(f"Close - change ? {self.changed}") if self.changed or self.context.no_config_file: self.context.sync.save_infos() self.context.sync.save_configuration() else: self.logger.info("No changes") event.accept() self.stop_network_watchdog() def resizeEvent(self, event): h = self.centralwidget.parentWidget().size().height() w = self.centralwidget.parentWidget().size().width() self.context.sync.geometry = (w, h) # -- > Actions -- # -------------- def prepare_synchronization(self): """ get the selected domains prepare elements to sync launch the UI dialog showing the compute of sync :return: """ # get selected domains self.logger.debug("prepare_synchronization") domains = self.doc_model.checked() self.logger.info(domains) self.context.prepare_sync(domains) pd = dialogs.ProgressSyncDialog( dialogs.ProgressSyncDialog.REMOTE_INFO_TEXT, parent=self ) worker = sync_worker.PrepareWorker(self.context) worker.signals.finished.connect(pd.accept) worker.signals.finished.connect(self.synchronize_dialog) worker.signals.aborted.connect(pd.reject) pd.main(worker) self.threadpool.start(worker) def synchronize_dialog(self): """ Launch UI for synchro :return: """ if self.network_available: sd = dialogs.SyncDialog(parent=self) sd.confirm_signal.connect(self.on_synchronize_confirmed) sd.reject_signal.connect(self.on_synchronize_rejected) sd.old_domains = self.context.infos["old_domain"] sd.new_domains = self.context.infos["new_domain"] sd.old_docs = self.context.infos["to_del"] sd.new_docs = self.context.infos["to_download"] if len(sd.old_domains) > 0 or len(sd.new_docs) > 0: self.change_status(changed=True) sd.main() else: self.msgbox_network_unavailable() def do_synchronization(self): self.doc_model.log() self.context.sync.display_available_docs() sd = dialogs.ProgressSyncDialog( dialogs.ProgressSyncDialog.SYNC_INFO_TEXT, parent=self ) worker = sync_worker.SyncWorker(self.context) worker.signals.finished.connect(sd.accept) worker.signals.finished.connect(self.sync_finished) worker.signals.progress.connect(self.doc_model.update_documents) worker.signals.aborted.connect(sd.reject) sd.main(worker) self.threadpool.start(worker) self.context.confirm_sync() def sync_finished(self): """ Syncho done. - display information in status bar - refresh informations counts - refresh doc table information """ downloaded, error = self.doc_model.summary() self.change_status(f"{downloaded} download(s), {error} error(s)") self.refresh_information_counts() self.tableView.model().layoutChanged.emit() # -- < Actions def open_documents_folder(self, index: QtCore.QModelIndex) -> None: docinfo = index.model().docs[index.row()] if docinfo['link'] and index.column() == 4: dom = self.context.local_path_domain(docinfo['domain']) webbrowser.open_new(f"file://{dom}") def update_proxy_state(self): self.stop_network_watchdog() self.network_watchdog.set_proxy(self.context.sync.proxy) self.start_network_watchdog()
class BCWorkerPool(QObject): """A worker pool allows to process data using pyqt multithreading """ __MAP_MODE_OFF = 0 __MAP_MODE_ALL = 1 __MAP_MODE_NONONE = 2 __MAP_MODE_AGGREGATE = 3 def __init__(self, maxWorkerCount=None): super(BCWorkerPool, self).__init__() self.__threadpool = QThreadPool() #self.__threadpool = QThreadPool.globalInstance() if isinstance( maxWorkerCount, int ) and maxWorkerCount >= 1 and maxWorkerCount <= self.__threadpool.maxThreadCount( ): self.__maxWorkerCount = maxWorkerCount else: self.__maxWorkerCount = self.__threadpool.maxThreadCount() self.__current = 0 self.__locked = 0 self.__started = 0 self.__allStarted = False self.__size = 0 self.__nbWorkers = self.__threadpool.maxThreadCount() self.__workers = [] self.__stopProcess = False self.__dataList = [] self.__results = [] self.__mapResults = BCWorkerPool.__MAP_MODE_OFF self.signals = BCWorkerSignals() def __lock(self): """Lock ensure that no worker will try to access to same item""" while self.__locked: BCTimer.sleep(1) self.__locked = True def __unlock(self): self.__locked = False def __onProcessed(self, processedNfo): """an item has been processed""" if self.__mapResults != BCWorkerPool.__MAP_MODE_OFF: index, item = processedNfo if self.__mapResults == BCWorkerPool.__MAP_MODE_ALL and not index is None: self.__results[index] = item elif self.__mapResults == BCWorkerPool.__MAP_MODE_NONONE and not item is None: self.__results.append(item) elif self.__mapResults == BCWorkerPool.__MAP_MODE_AGGREGATE and isinstance( item, dict): for key in item: self.__results[key] += item[key] self.signals.processed.emit(processedNfo) def __onFinished(self): """Do something.. ?""" self.__started -= 1 if self.__allStarted and self.__started == 0: self.__workers.clear() self.signals.finished.emit() def stopProcessingAsked(self): return self.__stopProcess def getNext(self): """Get next item to process""" self.__lock() if self.__current is None: self.__unlock() return (None, None) returnedIndex = self.__current self.__current += 1 if self.__current >= self.__size: self.__current = None self.__unlock() return (returnedIndex, self.__dataList[returnedIndex]) def startProcessing(self, dataList, callback, *callbackArgv): """Start all current thread execution""" # ensure to stop current processing before creating a new one if self.__stopProcess == True: return else: self.stopProcessing() if not (isinstance(dataList, list) or isinstance(dataList, set) or isinstance(dataList, tuple)): raise EInvalidType('Given `dataList` must be a list') self.__size = len(dataList) if self.__size == 0: return self.__dataList = [v for v in dataList] if self.__mapResults == BCWorkerPool.__MAP_MODE_ALL: self.__results = [None] * self.__size elif self.__mapResults != BCWorkerPool.__MAP_MODE_AGGREGATE: # already initialised by aggregate() method self.__results = [] # if number of items to process is less than number of possible threads, # don't use all threads self.__nbWorkers = min(self.__size, self.__maxWorkerCount) self.__started = 0 self.__current = 0 self.__workers.clear() # for test, force to 1 thread only #self.__nbWorkers = 1 self.__allStarted = False for index in range(self.__nbWorkers): self.__workers.append(BCWorker(self, callback, *callbackArgv)) self.__workers[index].signals.processed.connect(self.__onProcessed) self.__workers[index].signals.finished.connect(self.__onFinished) self.__workers[index].setAutoDelete(True) self.__started += 1 self.__threadpool.start(self.__workers[index]) self.__allStarted = True if self.__started == 0: self.__workers.clear() self.signals.finished.emit() self.__allStarted = False def stopProcessing(self): """Stop all current thread execution""" if self.__started > 0: self.__stopProcess = True while self.__started > 0: # check every 5ms if all thread are finished BCTimer.sleep(5) self.__stopProcess = False def waitProcessed(self): """Wait until all items in pool are processed""" # why self.__threadpool.waitForDone() don't work?? while self.__started > 0: BCTimer.sleep(1) def map(self, dataList, callback, *callbackArgv): """Apply `callback` function to each item `datalist` list and return a list Similar to python map() method, but for Qt threads https://docs.python.org/3/library/multiprocessing.html#multiprocessing.pool.Pool.map """ if len(dataList) == 0: return [] self.__mapResults = BCWorkerPool.__MAP_MODE_ALL self.startProcessing(dataList, callback, *callbackArgv) self.waitProcessed() self.__mapResults = BCWorkerPool.__MAP_MODE_OFF return self.__results def mapNoNone(self, dataList, callback, *callbackArgv): """Apply `callback` function to each item `datalist` list and return a list If callback return None value, value is not added to result Similar to python map() method, but for Qt threads https://docs.python.org/3/library/multiprocessing.html#multiprocessing.pool.Pool.map """ if len(dataList) == 0: return [] self.__mapResults = BCWorkerPool.__MAP_MODE_NONONE self.startProcessing(dataList, callback, *callbackArgv) self.waitProcessed() self.__mapResults = BCWorkerPool.__MAP_MODE_OFF return self.__results def aggregate(self, dataList, returnedStruct, callback, *callbackArgv): """Apply `callback` function to each item `datalist` list and return a dictionary with aggregated results """ if len(dataList) == 0: return returnedStruct self.__mapResults = BCWorkerPool.__MAP_MODE_AGGREGATE self.__results = returnedStruct self.startProcessing(dataList, callback, *callbackArgv) self.waitProcessed() self.__mapResults = BCWorkerPool.__MAP_MODE_OFF return self.__results
class WizardUIClass(Ui_Wizard): def __init__(self): '''Initialize the super class ''' super().__init__() self.inputFileHandler = FileHandler() self.outputFileHandler = FileHandler() self.timer = QTimer(self) self.timer.timeout.connect(self.updateProgressBar) self.threadpool = QThreadPool() self.failed = False print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) def setupUi(self, W): ''' Setup the UI of the super class, and add here code that relates to the way we want our UI to operate. ''' super().setupUi(W) # Register mandatory fields self.wizardPage1.registerField("lineEdit_ApiKey*", self.lineEdit_ApiKey) self.wizardPage1.registerField("lineEdit_InputFilePath*", self.lineEdit_InputFilePath) self.wizardPage1.registerField("lineEdit_OutputFilePath*", self.lineEdit_OutputFilePath) self.wizardPage2.registerField( "progressBar*", self.progressBar, property="value", changedSignal=self.progressBar.valueChanged) # close the lower part of the splitter to hide the # debug window under normal operations self.splitter.setSizes([300, 0]) # Set the initial value of the progress bar self.progress = 0 self.progressBar.setValue(self.progress) def debugPrint(self, msg): '''Print the message in the text edit at the bottom of the horizontal splitter. ''' self.debugTextBrowser.append(msg) def refreshAll(self): ''' Updates the widgets whenever an interaction happens. Typically some interaction takes place, the UI responds, and informs the model of the change. Then this method is called, pulling from the model information that is updated in the GUI. ''' self.lineEdit_InputFilePath.setText( self.inputFileHandler.getFileName()) self.lineEdit_OutputFilePath.setText( self.outputFileHandler.getFileName()) self.lineEdit_ReadOnlyOutPath.setText( self.outputFileHandler.getFileName()) # slot def testKeyPressedSlot(self): self.debugPrint('Test Key Button Pressed') api_key = self.lineEdit_ApiKey.text() try: sps.spsf.createLocator(api_key) m = QtWidgets.QMessageBox() m.setWindowTitle("Valid API key") m.setText("API Key accepted.") m.setIcon(QtWidgets.QMessageBox.Information) m.setStandardButtons(QtWidgets.QMessageBox.Ok) ret = m.exec_() self.debugPrint("Valid API Key") except: m = QtWidgets.QMessageBox() m.setWindowTitle("Invalid API key") m.setText("Invalid API Key!") m.setInformativeText(" Please check that you have entered a valid \ Google Cloud Geocoding API key. For more \ details, check the User Manual.") m.setIcon(QtWidgets.QMessageBox.Warning) m.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) m.setDefaultButton(QtWidgets.QMessageBox.Cancel) ret = m.exec_() self.lineEdit_ApiKey.setText("") self.debugPrint("Invalid API Key") # slot def inputReturnPressedSlot(self): ''' Called when the user enters a string in the line edit and presses the ENTER key. ''' self.debugPrint("RETURN key pressed in input LineEdit widget") fileName = self.lineEdit_InputFilePath.text() if self.inputFileHandler.isValid(fileName): try: self.inputFileHandler.assertFormat(fileName) except Exception as e: m = QtWidgets.QMessageBox() m.setWindowTitle("Error Reading File!") m.setText("Invalid file!") m.setInformativeText(str(e)) m.setIcon(QtWidgets.QMessageBox.Warning) m.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) m.setDefaultButton(QtWidgets.QMessageBox.Cancel) ret = m.exec_() self.lineEdit_InputFilePath.setText("") self.refreshAll() self.debugPrint("Invalid file specified: " + fileName) else: self.inputFileHandler.setFileName( self.lineEdit_InputFilePath.text()) self.refreshAll() else: m = QtWidgets.QMessageBox() m.setWindowTitle("Error Reading File!") m.setText("Invalid file name!\n" + fileName) m.setIcon(QtWidgets.QMessageBox.Warning) m.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) m.setDefaultButton(QtWidgets.QMessageBox.Cancel) ret = m.exec_() self.lineEdit_InputFilePath.setText("") self.refreshAll() self.debugPrint("Invalid file specified: " + fileName) # slot def inputBrowseSlot(self): ''' Called when the user presses the Browse button ''' self.debugPrint("Input Browse button pressed") options = QtWidgets.QFileDialog.Options() #options |= QtWidgets.QFileDialog.DontUseNativeDialog fileName, _ = QtWidgets.QFileDialog.getOpenFileName( None, "Open Placment and Student Data", "", "Excel Files (*.xlsx);;All Files (*)", options=options) if fileName: try: self.inputFileHandler.assertFormat(fileName) except Exception as e: m = QtWidgets.QMessageBox() m.setWindowTitle("Error Reading File!") m.setText("Invalid file!") m.setInformativeText(str(e)) m.setIcon(QtWidgets.QMessageBox.Warning) m.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) m.setDefaultButton(QtWidgets.QMessageBox.Cancel) ret = m.exec_() self.lineEdit_InputFilePath.setText("") self.refreshAll() self.debugPrint("Invalid file specified: " + fileName) else: self.debugPrint("setting file name: " + fileName) self.inputFileHandler.setFileName(fileName) self.refreshAll() # slot def outputReturnPressedSlot(self): ''' Called when the user enters a string in the line edit and presses the ENTER key. ''' self.debugPrint("RETURN key pressed in output LineEdit widget") fileName = self.lineEdit_InputFilePath.text() if self.outputFileHandler.isValid(fileName): self.outputFileHandler.setFileName( self.lineEdit_OutputFilePath.text()) self.refreshAll() else: m = QtWidgets.QMessageBox() m.setWindowTitle("Error Reading File!") m.setText("Invalid file name!\n" + fileName) m.setIcon(QtWidgets.QMessageBox.Warning) m.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) m.setDefaultButton(QtWidgets.QMessageBox.Cancel) ret = m.exec_() self.lineEdit_OutputFilePath.setText("") self.refreshAll() self.debugPrint("Invalid file specified: " + fileName) # slot def outputBrowseSlot(self): ''' Called when the user presses the Browse button ''' self.debugPrint("Output Browse button pressed") options = QtWidgets.QFileDialog.Options() #options |= QtWidgets.QFileDialog.DontUseNativeDialog fileName, _ = QtWidgets.QFileDialog.getSaveFileName( None, "Save Destination", "", "Excel Files (*.xlsx);;All Files (*)", options=options) self.debugPrint("Chosen filename: " + fileName) if fileName: self.debugPrint("setting file name: " + fileName) self.outputFileHandler.setFileName(fileName, isnewfile=True) self.refreshAll() # slot def startSlot(self): ''' Called when the use presses the Start button. Begins the algorithm for sorting the students and updates the progress bar. ''' input_filepath = str(self.wizardPage1.field("lineEdit_InputFilePath")) output_filepath = str( self.wizardPage1.field("lineEdit_OutputFilePath")) api_key = str(self.wizardPage1.field("lineEdit_ApiKey")) self.startButton.setText("Calculating...") self.startButton.setEnabled(False) print("Starting algorithm with " + str(input_filepath)) self.startWorker(sps.sortPlacements, input_filepath, output_filepath, api_key) self.timer.start(300) def startWorker(self, func, *args, **kwargs): # Pass the function to execute worker = Worker( func, *args, **kwargs) # Any other args, kwargs are passed to the run function worker.signals.error.connect(self.algorithmErrorHandler) worker.signals.finished.connect(self.threadComplete) worker.signals.progress.connect(self.updateProgressBar) # Execute self.threadpool.start(worker) def algorithmErrorHandler(self, error): exctype, value, traceback = error self.timer.stop() self.failed = True m = QtWidgets.QMessageBox() m.setWindowTitle("Error Sorting Students!") m.setText("An error occured!") m.setInformativeText( "There was an error while using the data provided. " + "Please check the data is formatted according to " + "the User Manual, then try again.") m.setIcon(QtWidgets.QMessageBox.Critical) m.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) m.setDefaultButton(QtWidgets.QMessageBox.Cancel) ret = m.exec_() sys.exit() def threadComplete(self): print("THREAD COMPLETE!") if not self.failed: m = QtWidgets.QMessageBox() m.setWindowTitle("Sorting Complete") m.setText("Your data has been sorted. Click 'Next' to continue.") m.setIcon(QtWidgets.QMessageBox.Information) m.setStandardButtons(QtWidgets.QMessageBox.Ok) ret = m.exec_() def updateProgressBar(self, progress_callback=None): if progress_callback: self.progress = progress_callback else: self.progress += 1 if self.progress >= 100: self.timer.stop() self.progressBar.setValue(self.progress)
class svd_interface(pydm.Display): displaySignal = pyqtSignal(dict) def __init__(self, parent=None, args=None, macros=None): self.threadpool = QThreadPool( maxThreadCount=12) # should be before super.__init__. Why? print('Threadpool with {} threads.'.format( self.threadpool.maxThreadCount())) super(svd_interface, self).__init__(parent=parent, args=args, macros=macros) # for l in self.__dir__(): # if 'waveform' in l: # print(l) self._ana_count = 0 # Signals for workers (multitreading) self.newDataSignal = WorkerSignal_ndarray() self.newFitSignal = WorkerSignal_dict() # connect stuff together self.waveformGraph.connect_attr('newDataSignal', self.newDataSignal) self.regressorWidget.connect_attr('graph', self.waveformGraph) # Setup the analysis timer self.timer = QTimer(interval=int(1 / config.RATE * 1000)) # timer in ms self.timer.timeout.connect(self.waveformGraph.get_data) self.timer.start() # Processing self.newDataSignal.signal.connect(self.fit_data) self.newFitSignal.signal.connect(self.trigger_display) # Stripcharts self.stripchartsView.make_stripcharts(2, useRemote=False) self.stripcharts = Svd_stripchart(stripchartsView=self.stripchartsView) self.regressorWidget.newRegressorSignal.connect( self.stripcharts.make_ravgs) self.newFitSignal.signal.connect(self.stripcharts.update_ravgs) # self.timer.timeout.connect(self.stripchartsView.update_test) # Update display self.displaySignal.connect(self.waveformGraph.display_data_fit) self.displaySignal.connect(self.stripcharts.update_stripchartsView) return def ui_filename(self): return 'main.ui' def ui_filepath(self): return path.join(path.dirname(path.realpath(__file__)), self.ui_filename()) @pyqtSlot(np.ndarray) def fit_data(self, data): self.worker = FitWfWorker(data=data, roi=self.waveformGraph.get_roi(), regressor=self.regressorWidget.regressor, signal=self.newFitSignal) self.threadpool.tryStart(self.worker) return @pyqtSlot(dict) def trigger_display(self, data_dict): if self._ana_count < config.DISPLAY_RATE_RATIO: self._ana_count += 1 else: self._ana_count = 0 self.displaySignal.emit(data_dict) # def make_stripchart(self, n=0, ts_len=100, alpha=None, n_pulse=1): # self.stripcharts = Svd_stripchart( # n=n, ts_len=ts_len, alpha=alpha, n_pulse=n_pulse, stripchartsView=self.stripchartsView) # return def print_time(self): time = QDateTime.currentDateTime() print(time.toString('yyyy-MM-dd hh:mm:ss dddd')) return # if __name__ == '__main__': # app = QApplication(sys.argv) # thisapp = svd_interface() # thisapp.show() # sys.exit(app.exec_())
class MainPage(QMainWindow): def __init__(self): super(MainPage, self).__init__() loadUi('./ui/ui_mainwindow.ui', self) self.mainapp = MainApp() # main.py 에서 연결 self.mainapp.gui_framework = self self.threadpool = QThreadPool() self._stopflag = False # main app 정지 신호 self._exitflag = False # main app 종료 신호 self.EXIT_CODE_REBOOT = -123456789 # 초기 출력 self.print_setting() # mainwindow 의 textBrowser_setting 에 현재 설정 출력 self.textBrowser.append("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) # 시그널/슬롯 생성 self.actionSimulationSetting.triggered.connect( self.executeConfigPage) # 페이지(윈도우) 연결 self.actionRun.triggered.connect(self.run_program) # Run mainapp self.actionStop.triggered.connect(self.stop_program) # self.actionSaveAgent.triggered.connect(self.save_agent) self.actionLoadAgent.triggered.connect(self.load_agent) self.actionLoadAni.triggered.connect(self.load_animation) self.actionReboot.triggered.connect(self.reboot_program) def closeEvent(self, event): reply = QMessageBox.question( self, "Message", "Are you sure you want to quit? Any unsaved work will be lost.", QMessageBox.Close | QMessageBox.Cancel, QMessageBox.Cancel) if reply == QMessageBox.Close: self._stopflag = True self._exitflag = True event.accept() else: event.ignore() def run_program(self): worker = Worker(self.mainapp) # 정지설정 False / run & 설정버튼 비활성화 self._stopflag = False self.actionRun.setEnabled(False) self.actionSimulationSetting.setEnabled(False) # Execute self.threadpool.start(worker) self.write_console("Multithreading with %d of %d threads" % (self.threadpool.activeThreadCount(), self.threadpool.maxThreadCount())) # Alternative: def run_program2(self): # 외부에서 self.actionRun.triggered.connect(self.run_program2) # # self.thread = Thread(target=self.mainapp.run_main) # self.thread.daemon = True # self.thread.start() def stop_program(self): self._stopflag = True self.actionRun.setEnabled(True) self.actionSimulationSetting.setEnabled(True) def reboot_program(self): reply = QMessageBox.question( self, "Message", "Are you sure you want to reboot? Any unsaved work will be lost.", QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) if reply == QMessageBox.Ok: self._stopflag = True self._exitflag = True qApp.exit(self.EXIT_CODE_REBOOT) # def save_agent(self): # if not self.mainapp.env: # QMessageBox.warning(self, "Message", "Any agent/environment isn't loaded.") # elif self.mainapp.agent.name == 'rl': # self.mainapp.agent.save_file(self.mainapp.log_dir, self.mainapp.iter) # QMessageBox.information(self, "Message", "RL agent file is saved") # elif self.mainapp.agent.name == 'greedy': # QMessageBox.warning(self, "Message", "Greedy agent file CANNOT be saved") def load_agent(self): options = QFileDialog.Options() # filter: "All Files (*)", "Python Files (*.py)", "PKL Files (*.pkl)" fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "PKL Files (*.pkl)", options=options) if fileName: QMessageBox.information(self, "Message", "agent file is loaded \n %s" % fileName) self.mainapp.agent_name = fileName self.print_setting() def load_animation(self): options = QFileDialog.Options() # filter: "All Files (*)", "Python Files (*.py)", "PKL Files (*.pkl)" fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "PKL Files (*.pkl)", options=options) if fileName: QMessageBox.information( self, "Message", "animation file is loaded \n %s" % fileName) with open(fileName, 'rb') as file: # james.p 파일을 바이너리 읽기 모드(rb)로 열기 ani_data = pickle.load(file) temp_ani = GraphicDisplay(ani_data['width'], ani_data['height'], unit_pixel=ani_data['unit']) temp_ani.data = ani_data['data'] temp_ani.mainloop() def print_setting(self): self.textBrowser_setting.setText("[ Current Setting ]\n") if self.mainapp.agent_name.lower() in ('rl', 'reinforcement learning'): self.textBrowser_setting.append("Agent: Reinforcement Learning") elif self.mainapp.agent_name.lower() == 'greedy': self.textBrowser_setting.append("Agent: %s" % self.mainapp.agent_name) else: self.textBrowser_setting.append("Agent: Loaded %s" % self.mainapp.agent_name) self.textBrowser_setting.append("Task: %s" % self.mainapp.task) self.textBrowser_setting.append("Num Battery: %d" % self.mainapp.b_num) self.textBrowser_setting.append("Num Flight: %d" % self.mainapp.f_num) self.textBrowser_setting.append("Flight Time Interval: %.2f" % self.mainapp.f_interval) self.textBrowser_setting.append("Defense range (map width): %.2f" % self.mainapp.map_width) self.textBrowser_setting.append("Termination Type: %s" % str(self.mainapp.termination[0])) self.textBrowser_setting.append("Termination Criteria: %s" % str(self.mainapp.termination[1])) self.textBrowser_setting.append("Autosave cycle: %d iter" % self.mainapp.autosave_iter) def executeConfigPage(self): simconfig_page = SimconfigPage(self, self.mainapp) simconfig_page.exec_() def write_console(self, text, box='textBrowser'): if box == 'textBrowser_setting': self.textBrowser_setting.append(str(text)) self.textBrowser_setting.moveCursor(QtGui.QTextCursor.End) else: self.textBrowser.append(str(text)) self.textBrowser.moveCursor(QtGui.QTextCursor.End)
class MainWindow(QMainWindow): """ This window is for the control of the monochromator""" def __init__(self, *args, **kwargs): super(MainWindow, self).__init__( *args, **kwargs) # This runs the init method of QMainWindow # Load the ui.py file and prepare the UI self.ui = Ui_MainWindow() self.ui.setupUi(self) # ------------------------------- initialize attribute values - ------------------------------------------------ # These are attributes of the MainWindow class, not of the ui instance (which is an instance of Ui_MainWindow) # They are used as attributes of the program. self.mono_instance = None self.resource = None self.gr_dens = 1200 self.backlash_compensation = True self.debug_mode = False self.select_resource_text = '----- Select Resource -----' self.backlash_amount = 10 self.speed = 50 self.initialized = False self.stop_scan = False self.zero_wavelength = 0 # ------------------------------- Initialize GUI Object States ------------------------------------------------- # These are methods/attributes of the ui instance. They all refer to specific objects which are part of the ui # e.g. buttons, spinners, comboboxes, etc. # self.ui.visa_resource_combobox.addItem('test') self.ui.backlash_checkbox.setCheckState(QtCore.Qt.Checked) self.ui.groove_density_combobox.setCurrentIndex(2) self.ui.tab_container.setCurrentIndex(0) self.ui.visa_resource_combobox.addItem(self.select_resource_text) self.ui.backlash_amount_spinner.setValue(self.backlash_amount) self.ui.speed_value_spinner.setValue(self.speed) # ------------------------------------ Run any initialization Functions ---------------------------------------- self.check_resources() # Find all com ports with attached resources. # ------------------------ MULTI-THREADING STUFF --------------------------------------------------------------- self.thread_pool = QThreadPool() print("Multithreading with maximum %d threads" % self.thread_pool.maxThreadCount()) # ------------------------------------- Non Slot Method Definitions ------------------------------------------------ def check_resources( self ): # If this is only called once, isn't it better not to make it a function? rm = pyvisa.ResourceManager() all_resources = rm.list_resources() if len(all_resources) > 0: for ii in all_resources: self.ui.visa_resource_combobox.addItem(ii) else: return def update_wavelength(self): ii = 1 while self.mono_instance.continue_updating is True: time.sleep(0.01) self.ui.current_wl_output_lineedit.setText( str(self.mono_instance.current_wavelength)) ii += 1 def check_for_status_updates(self): print('in the check_for_status_updates fn') tmp_message = '' ii = 1 counter = 0 while True: if tmp_message == self.mono_instance.status_message: counter += 1 else: counter = 0 time.sleep(0.1) if counter > 100: self.mono_instance.status_message = '' counter = 0 tmp_message = self.mono_instance.status_message self.ui.statusbar.showMessage(tmp_message, 10000) ii += 1 def initialization_tasks_thread(self): try: self.mono_instance.initialize_mono() self.ui.statusbar.showMessage('Setting Parameters', 5000) print('Initialization Success, setting parameters...') # self.ui.statusbar.showMessage('Mono Initialized', 5000) # self.mono_instance.status_message = 'Setting Speed' self.mono_instance.set_speed(self.speed) self.initialized = True self.mono_instance.status_message = 'Initialization Complete - Remember to Set Current Wavelength' except: # Fix this to make it less broad sometime print( 'Exception Occurred During Initialization - Add this error to code' ) print(str(sys.exc_info()[0])) print(str(sys.exc_info()[1])) print(str(sys.exc_info()[2])) self.ui.current_wl_output_lineedit.setText( str(self.mono_instance.current_wavelength)) print('self.mono_instance.connected: ' + str(self.mono_instance.connected)) self.mono_instance.busy = False self.ui.statusbar.showMessage( 'Initialization Complete - Remember to Set Home Wavelength', 5000) print( '-------------------------------------initialization Complete------------------------------------------' ) def scan_worker(self, scan_points, cycles, delay): num_wls = len(scan_points) for jj in range(0, cycles): if self.stop_scan is True: break else: for ii in range(0, num_wls): if self.stop_scan is True: break else: self.ui.statusbar.showMessage( 'Moving to ' + str(scan_points[ii]) + ' nm', 5000) self.mono_instance.go_to_wavelength( scan_points[ii], self.mono_instance.speed, self.backlash_amount, self.backlash_compensation) self.ui.statusbar.showMessage( 'Pausing at ' + str(scan_points[ii]) + ' nm', 5000) time.sleep(delay) time.sleep(0.1) # This just felt right self.ui.statusbar.showMessage('Scan Complete', 5000) self.mono_instance.open_visa() self.mono_instance.current_wavelength = self.mono_instance.get_current_pos( ) self.mono_instance.close_visa() self.ui.current_wl_output_lineedit.setText( str(self.mono_instance.current_wavelength)) self.stop_scan = False self.mono_instance.busy = False print( '----------------------------------Scanning Complete ---------------------------------------------' ) # ------------------------------------------------ HOOK UP SLOTS --------------------------------------------------- # SET MONO TAB ------------------------------------- @QtCore.pyqtSlot( ) # It's unclear if these decorators are actually needed, I think it works without them def set_com_port(self): print( '-----------------------------------Set Com Port ----------------------------------------------' ) self.resource = self.ui.visa_resource_combobox.currentText() if self.resource == self.select_resource_text: self.resource = None self.ui.statusbar.showMessage(self.resource, 2000) @QtCore.pyqtSlot() def set_groove_density(self): print( '-------------------------------------Set Groove Density-------------------------------------' ) gr_dens_str = self.ui.groove_density_combobox.currentText() self.gr_dens = int(gr_dens_str) self.ui.statusbar.showMessage( 'Groove Density set to ' + gr_dens_str + ' gr/mm', 5000) if isinstance(self.mono_instance, MonoDriver): self.mono_instance.groove_density = self.gr_dens self.mono_instance.get_k_number() @QtCore.pyqtSlot() def clicked_initialize_button(self): print( '-----------------------------------------Initializing-------------------------------------------------' ) if isinstance( self.resource, str): # Ideally the conditions here would verify more clearly self.mono_instance = MonoDriver(self.resource, self.gr_dens) self.mono_instance.busy = True self.ui.statusbar.showMessage('Initializing...') # If first time initializing, add an updates thread to monitor status constantly # This is nice in this script, but it is not exactly the best use of a thread... # if self.initialized is False: # print('beginning constant status updates') # updates_thread = Worker(self.check_for_status_updates) # self.thread_pool.start(updates_thread) initialization_thread = Worker(self.initialization_tasks_thread) self.thread_pool.start(initialization_thread) else: self.mono_instance.status_message = 'INITIALIZATION FAILED - Resource Selection Failed' # self.ui.statusbar.showMessage('INITIALIZATION FAILED - Resource Selection Failed', 5000) return @check_mono_instance @QtCore.pyqtSlot() def clicked_home_button(self): """ This sets the position (in steps), that the monochromator considers " 0 steps ". Absolute moves are with respect to this position. I think it's ok if you move to a different wavelength than the "natural zero" i.e. the one it naturally arrives at after initialization, you just have to set the value to what wavelength the mono reads """ print( '------------------------------------Setting Home ------------------------------------' ) if self.mono_instance.busy is False: print('self.mono_instance.connected inside home btn: ' + str(self.mono_instance.connected)) self.zero_wavelength = self.ui.calib_wl_spinner.value() self.mono_instance.calibration_wavelength = self.zero_wavelength self.mono_instance.set_zero_position() self.mono_instance.current_wavelength = self.mono_instance.calibration_wavelength print('Home wavelength is: ' + str(self.zero_wavelength)) self.ui.current_wl_output_lineedit.setText( str(self.mono_instance.current_wavelength)) self.ui.statusbar.showMessage('Home Wavelength Set', 5000) @QtCore.pyqtSlot() def state_changed_bl_compensation(self): self.backlash_compensation = self.ui.backlash_checkbox.isChecked() @QtCore.pyqtSlot() def value_changed_backlash_spinner(self): self.backlash_amount = self.ui.backlash_amount_spinner.value() # SETUP TAB ------------------------------------- @check_mono_instance @QtCore.pyqtSlot() def clicked_go_to_wl_button(self): print( '------------------------------------go to wavelength--------------------------------------' ) destination = self.ui.goto_wl_spinner.value() if self.mono_instance.busy is False: self.mono_instance.busy = True self.mono_instance.open_visa() worker = Worker(self.mono_instance.go_to_wavelength, destination, self.mono_instance.speed, self.backlash_amount, self.backlash_compensation) self.thread_pool.start(worker) time.sleep(0.2) self.mono_instance.continue_updating = True get_pos_worker = Worker(self.update_wavelength) self.thread_pool.start(get_pos_worker) @check_mono_instance @QtCore.pyqtSlot() def clicked_nudge_down_button(self): print( '---------------------------------------nudge down---------------------------------------------' ) if self.mono_instance.busy is False: self.mono_instance.busy = True nudge_amount = self.ui.nudge_amount_spinner.value() nudge_thread = Worker(self.mono_instance.nudge, amount_nm=nudge_amount, higher=False) self.thread_pool.start(nudge_thread) self.ui.statusbar.showMessage(self.mono_instance.status_message, 5000) self.ui.current_wl_output_lineedit.setText( str(self.mono_instance.current_wavelength)) time.sleep(0.2) print('trying to update_wavelength') self.mono_instance.continue_updating = True get_pos_worker = Worker(self.update_wavelength) self.thread_pool.start(get_pos_worker) @check_mono_instance @QtCore.pyqtSlot() def clicked_nudge_up_button(self): print( '---------------------------------------nudge up---------------------------------------' ) if self.mono_instance.busy is False: self.mono_instance.busy = True nudge_amount = self.ui.nudge_amount_spinner.value() nudge_thread = Worker(self.mono_instance.nudge, amount_nm=nudge_amount, higher=True) self.thread_pool.start(nudge_thread) self.ui.statusbar.showMessage(self.mono_instance.status_message, 5000) self.ui.current_wl_output_lineedit.setText( str(self.mono_instance.current_wavelength)) time.sleep(0.2) self.mono_instance.continue_updating = True get_pos_worker = Worker(self.update_wavelength) self.thread_pool.start(get_pos_worker) @check_mono_instance @QtCore.pyqtSlot() def clicked_stop_nudge_button(self): print( '---------------------------------------stop nudge---------------------------------------' ) self.mono_instance.stop_motion_bool = True self.mono_instance.stop_motion() self.ui.current_wl_output_lineedit.setText( str(self.mono_instance.current_wavelength)) self.mono_instance.busy = False @check_mono_instance @QtCore.pyqtSlot() def clicked_speed_set_button(self): print( '---------------------------------------speed set---------------------------------------' ) speed = self.ui.speed_value_spinner.value() self.mono_instance.set_speed(speed) self.ui.statusbar.showMessage(self.mono_instance.status_message, 5000) # SCAN TAB ------------------------------------- @check_mono_instance @QtCore.pyqtSlot() def clicked_start_scan_button(self): print( '----------------------------------Start Scan--------------------------------------------' ) start_wl = self.ui.scan_start_wl_spinner.value() stop_wl = self.ui.scan_stop_wl_spinner.value() step_wl = self.ui.scan_step_spinner.value() delay = self.ui.scan_pause_spinner.value() cycles = self.ui.scan_cycles_spinner.value() self.stop_scan = False scan_points, number_wavelengths = calculate_scan_points( start_wl, stop_wl, step_wl) if self.mono_instance.busy is False: self.mono_instance.busy = True worker = Worker(self.scan_worker, scan_points, cycles, delay) self.thread_pool.start(worker) @check_mono_instance @QtCore.pyqtSlot() def clicked_stop_scan_button(self): print( '-------------------------------------Stop Scan------------------------------------------' ) self.stop_scan = True # DEBUG TAB ---------------------------------- @check_mono_instance @QtCore.pyqtSlot() def clicked_debug_write_button(self): print( '---------------------------------------debug write---------------------------------------' ) string_to_write = self.ui.debug_write_str_textbox.toPlainText() self.mono_instance.open_visa() self.mono_instance.write_str(string_to_write) print('closing visa (debug write)') self.mono_instance.close_visa() self.ui.debug_read_textbox.setPlainText(self.mono_instance.readout) @QtCore.pyqtSlot() def state_changed_debug_checkbox(self): self.debug_mode = self.ui.debug_checkbox.isChecked() if self.debug_mode is True: self.ui.visa_resource_combobox.addItem('test') self.ui.statusbar.showMessage('Now in Debug mode', 5000) if self.debug_mode is False: test_idx = self.ui.visa_resource_combobox.findText('test') self.ui.visa_resource_combobox.removeItem(test_idx) self.ui.statusbar.showMessage('Leaving Debug Mode', 5000)
class ExampleThread(Qt.QWidget): def __init__(self, parent=None): super(ExampleThread, self).__init__(parent) layout = Qt.QVBoxLayout(self) self.lbl = Qt.QLabel("Start") layout.addWidget(self.lbl) self.btnA = Qt.QPushButton("Запустить AThread(QThread)") layout.addWidget(self.btnA) self.btnB = Qt.QPushButton("Запустить SomeObject(QObject)") layout.addWidget(self.btnB) self.btnC = Qt.QPushButton("Запустить Worker(QRunnable)") layout.addWidget(self.btnC) self.progressBar = Qt.QProgressBar() self.progressBar.setProperty("value", 0) layout.addWidget(self.progressBar) self.setGeometry(550, 65, 300, 300) self.setWindowTitle('3 разных и простых способа работы с потоками.') self.btnA.clicked.connect(self.using_q_thread) self.btnB.clicked.connect(self.using_move_to_thread) self.btnC.clicked.connect(self.using_q_runnable) self.msg = MsgBoxAThread() self.thread = None self.msgSomeObject = MsgBoxSomeObject() self.objThread = None self.counter = 0 self.timer = Qt.QTimer() self.timer.setInterval(1000) # -------- timeout -------> def recurring_timer(self): self.timer.timeout.connect(self.recurring_timer) self.timer.start() self.threadpool = QThreadPool() print("Max потоков, кот. будут использоваться=`%d`" % self.threadpool.maxThreadCount()) self.msgWorker = MsgBoxWorker() self.threadtest = QThread(self) self.idealthreadcount = self.threadtest.idealThreadCount() print("Ваша машина может обрабатывать `{}` потокa оптимально.".format(self.idealthreadcount)) def recurring_timer(self): self.counter += 1 self.lbl.setText("СЧЁТЧИК цикл GUI: %d" % self.counter) # ---- AThread(QThread) -----------# def using_q_thread(self): if self.thread is None: self.thread = AThread() self.thread.threadSignalAThread.connect(self.on_threadSignalAThread) self.thread.finished.connect(self.finishedAThread) self.thread.start() self.btnA.setText("Stop AThread(QThread)") else: self.thread.terminate() self.thread = None self.btnA.setText("Start AThread(QThread)") def finishedAThread(self): self.thread = None self.btnA.setText("Start AThread(QThread)") def on_threadSignalAThread(self, value): self.msg.label.setText(str(value)) # Восстанавливаем визуализацию потокового окна, если его закрыли. Поток работает. # .setVisible(true) или .show() устанавливает виджет в видимое состояние, # если видны все его родительские виджеты до окна. if not self.msg.isVisible(): self.msg.show() # --END-- AThread(QThread) -------------------# # ---- SomeObject(QObject) -------------------# def using_move_to_thread(self): if self.objThread is None: self.objThread = QThread() self.obj = SomeObject() self.obj.moveToThread(self.objThread) # Переместить в поток для выполнения self.obj.threadSignalSomeObject.connect(self.on_threadSignalSomeObject) self.obj.finishedSomeObject.connect(self.finishedSomeObject) self.objThread.started.connect(self.obj.long_running) self.objThread.start() self.btnB.setText("Wait SomeObject(QObject)") self.btnB.setEnabled(False) else: pass def finishedSomeObject(self): self.objThread.terminate() self.objThread.wait(1) self.objThread = None self.btnB.setEnabled(True) self.btnB.setText("Start SomeObject(QObject)") def on_threadSignalSomeObject(self, value): self.msgSomeObject.label.setText(str(value)) # Восстанавливаем визуализацию потокового окна, если его закрыли. Поток работает. if not self.msgSomeObject.isVisible(): self.msgSomeObject.show() # --END-- SomeObject(QObject) -------------------# # ---- Worker(QRunnable) ------------------------# def using_q_runnable(self): # Передайте функцию для выполнения # Любые другие аргументы, kwargs передаются функции run worker = Worker(self.execute_this_fn) worker.signals.result.connect(self.print_output) worker.signals.finish.connect(self.thread_complete) worker.signals.progress.connect(self.progress_fn) self.threadpool.start(worker) def progress_fn(self, n): self.progressBar.setValue(n) self.msgWorker.label.setText(str(n)) # Восстанавливаем визуализацию потокового окна, если его закрыли. Поток работает. if not self.msgWorker.isVisible(): self.msgWorker.show() def execute_this_fn(self, progress_callback): for n in range(0, 11): Qt.QThread.msleep(600) progress_callback.emit(n * 100 / 10) return "Готово." def print_output(self, s): print("\ndef print_output(self, s):", s) def thread_complete(self): print("\nTHREAD ЗАВЕРШЕН!, self->", self) # --END-- Worker QRunnable) -------------------# # ==============================================### # потоки или процессы должны быть завершены ### def closeEvent(self, event): reply = Qt.QMessageBox.question \ (self, 'Информация', "Вы уверены, что хотите закрыть приложение?", Qt.QMessageBox.Yes, Qt.QMessageBox.No) if reply == Qt.QMessageBox.Yes: if self.thread: self.thread.quit() del self.thread self.msg.close() if self.objThread: self.objThread.setTerminationEnabled(True) self.objThread.terminate() self.objThread.wait(1) self.msgSomeObject.close() # закрыть поток Worker(QRunnable) self.msgWorker.close() super(ExampleThread, self).closeEvent(event) else: event.ignore()
class GUI(QMainWindow, Ui_MainWindow, Ui_extras): def __init__(self, app): super(GUI, self).__init__() self.app = app self.setupUi(self) self.setWindowTitle("Tacotron2 + Waveglow GUI v%s" % 0.2) self.drawGpuSwitch(self) self.initWidgets(self) self.GpuSwitch.toggled.connect(self.set_cuda) self.model = None self.waveglow = None self.hparams = None self.current_thread = None self.t_1 = None self.TTModelCombo.currentIndexChanged.connect( self.set_reload_model_flag) self.WGModelCombo.currentIndexChanged.connect( self.set_reload_model_flag) self.TTSDialogButton.clicked.connect(self.start_synthesis) self.TTSSkipButton.clicked.connect(self.skip_infer_playback) self.logs = [] self.logs2 = [] self.max_log_lines = 3 self.max_log2_lines = 100 self.TTmodel_dir = [] # Stores list of paths self.WGmodel_dir = [] self.reload_model_flag = True # Because of bug in streamelements timestamp filter, need 2 variables for previous time self.startup_time = datetime.datetime.utcnow().isoformat() #self.startup_time = '0' # For debugging self.prev_time = datetime.datetime.utcnow().isoformat() #self.prev_time = '0' # for debugging self.offset = 0 self.ClientSkipBtn.clicked.connect(self.skip_wav) self.channel_id = '' self.client_flag = False self.LoadTTButton.clicked.connect(self.add_TTmodel_path) self.LoadWGButton.clicked.connect(self.add_WGmodel_path) self.update_log_window("Begin by loading a model") pygame.mixer.quit() pygame.mixer.init(frequency=22050, size=-16, channels=1) self.channel = pygame.mixer.Channel(0) self.ClientStartBtn.clicked.connect(self.start) self.ClientStopBtn.clicked.connect(self.stop) self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) self.signals = GUISignals() self.signals.progress.connect(self.update_log_bar) self.signals.elapsed.connect(self.on_elapsed) self.se_opts = { 'approve only': 2, 'block large numbers': 0, 'read dono amount': 2, } # Callback functions self.fns = { 'GUI: start of polling loop': self.fns_gui_startpolling, 'GUI: end of polling loop': self.fns_gui_endpolling, 'Wav: playback': self.fns_wav_playback, 'Var: offset': self.fns_var_offset, 'Var: prev_time': self.fns_var_prevtime, 'GUI: progress bar 2 text': self.fns_gui_pbtext } self.OptLimitCpuBtn.stateChanged.connect(self.toggle_cpu_limit) self.OptLimitCpuCombo.currentIndexChanged.connect( self.change_cpu_limit) self.OptApproveDonoBtn.stateChanged.connect(self.toggle_approve_dono) self.OptBlockNumberBtn.stateChanged.connect(self.toggle_block_number) self.OptDonoNameAmountBtn.stateChanged.connect(self.toggle_dono_amount) self.py_opts = { 'cpu limit': None, } @pyqtSlot(int) def toggle_cpu_limit(self, state): self.label_10.setEnabled(state) self.OptLimitCpuCombo.setEnabled(state) @pyqtSlot(int) def change_cpu_limit(self, indx): num_thread = indx + 1 torch.set_num_threads(num_thread) self.py_opts['cpu limit'] = num_thread os.environ['OMP_NUM_THREADS'] = str(num_thread) @pyqtSlot(int) def toggle_approve_dono(self, state): self.se_opts['approve only'] = state @pyqtSlot(int) def toggle_block_number(self, state): self.se_opts['block large numbers'] = state @pyqtSlot(int) def toggle_dono_amount(self, state): self.se_opts['read dono amount'] = state def fns_gui_startpolling(self, arg=None): self.ClientStartBtn.setDisabled(True) self.ClientStopBtn.setEnabled(True) self.ClientSkipBtn.setEnabled(True) self.tab.setDisabled(True) self.ClientAmountLine.setDisabled(True) def fns_gui_endpolling(self, arg=None): self.update_log_bar2(0) self.progressBar2Label.setText('') self.ClientStartBtn.setEnabled(True) self.ClientStopBtn.setDisabled(True) self.ClientSkipBtn.setDisabled(True) self.tab.setEnabled(True) self.ClientAmountLine.setEnabled(True) def fns_wav_playback(self, wav): if self.tabWidget.currentIndex() == 0: self.TTSSkipButton.setEnabled(True) else: self.ClientSkipBtn.setEnabled(True) if wav.dtype != np.int16: # Convert from float32 or float16 to signed int16 for pygame wav = (wav / np.amax(wav) * 32767).astype(np.int16) sound = pygame.mixer.Sound(wav) self.channel.queue(sound) def fns_var_offset(self, arg): self.offset = arg def fns_var_prevtime(self, arg): self.prev_time = arg def fns_gui_pbtext(self, tup): current, total = tup self.progressBar2Label.setText('{}/{}'.format(current, total)) @pyqtSlot(tuple) def on_fncallback(self, tup): option, arg = tup self.fns[option](arg) @pyqtSlot(str) def on_textready(self, text): # Function to send text from client thread to GUI thread # Format of text: <Obj>:<Message> obj = text[0:4] msg = text[5:] if obj == 'Log1': if len(self.logs) > self.max_log_lines: self.logs.pop(0) self.logs.append(msg) log_text = '\n'.join(self.logs) self.log_window1.setText(log_text) if obj == 'Log2': if len(self.logs2) > self.max_log2_lines: self.logs2.pop(0) self.logs2.append(msg) log_text = '\n'.join(self.logs2) self.log_window2.setPlainText(log_text) self.log_window2.verticalScrollBar().setValue( self.log_window2.verticalScrollBar().maximum()) if obj == 'Sta2': self.statusbar.setText(msg) @pyqtSlot(int) def update_log_bar(self, val): self.progressBar.setValue(val) #self.progressBar.setTextVisible(val != 0) @pyqtSlot(int) def update_log_bar2(self, val): self.progressBar2.setValue(val) #self.progressBar2.setTextVisible(val != 0) @pyqtSlot(int) def on_elapsed(self, val): if self.tabWidget.currentIndex() == 0: self.update_log_window('Elapsed: ' + str(val) + 's', mode='overwrite') else: pass # No elapsed time for tab2 def on_finished(self): #print("THREAD COMPLETE!") pass def on_result(self, s): #print(s) pass def start(self): # Pass the function to execute global _running2, _running3 if not self.validate_se(): return if self.reload_model_flag: self.reload_model() self.reload_model_flag = False min_donation = self.get_min_donation() TOKEN = self.get_token() _mutex2.lock() _running2 = True _mutex2.unlock() _mutex3.lock() _running3 = True _mutex3.unlock() worker = Worker(self.execute_this_fn, TOKEN, min_donation, self.channel, self.se_opts, self.use_cuda, self.model, self.waveglow, self.offset, self.prev_time, self.startup_time) # Any other args, kwargs are passed to the run function worker.signals.result.connect(self.on_result) worker.signals.finished.connect(self.on_finished) worker.signals.progress.connect(self.update_log_bar2) worker.signals.textready.connect(self.on_textready) worker.signals.elapsed.connect(self.on_elapsed) worker.signals.fncallback.connect(self.on_fncallback) # Execute self.threadpool.start(worker) def stop(self): global _running2, _running3 _mutex2.lock() _running2 = False _mutex2.unlock() _mutex3.lock() _running3 = False _mutex3.unlock() self.skip_wav() def skip(self): global _running3 _mutex3.lock() _running3 = False _mutex3.unlock() self.skip_wav() # def progress_fn(self, n): # print("%d%% done" % n) def get_interruptflag2(self): _mutex3.lock() val = _running3 _mutex3.unlock() return val def execute_this_fn(self, TOKEN, min_donation, channel, se_opts, use_cuda, model, waveglow, offset, prev_time, startup_time, progress_callback, elapsed_callback, text_ready, fn_callback): # TODO: refactor this messy block fn_callback.emit(('GUI: start of polling loop', None)) text_ready.emit("Sta2:Connecting to StreamElements") url = "https://api.streamelements.com/kappa/v2/tips/" + self.channel_id headers = { 'accept': 'application/json', "Authorization": "Bearer " + TOKEN } text_ready.emit('Log2:Initializing') text_ready.emit('Log2:Minimum amount for TTS: ' + str(min_donation)) while True: _mutex2.lock() if _running2 == False: _mutex2.unlock() break else: _mutex2.unlock() if not channel.get_busy(): #print('Polling', datetime.datetime.utcnow().isoformat()) text_ready.emit("Sta2:Waiting for incoming donations . . .") current_time = datetime.datetime.utcnow().isoformat() # TODO: possible bug: missed donations once time pasts midnight querystring = { "offset": offset, "limit": "1", "sort": "createdAt", "after": startup_time, "before": current_time } response = requests.request("GET", url, headers=headers, params=querystring) data = json.loads(response.text) for dono in data['docs']: text_ready.emit("Sta2:Processing donations") dono_time = dono['createdAt'] offset += 1 if dono_time > prev_time: # Str comparison amount = dono['donation']['amount'] # Int if float(amount) >= min_donation and dono[ 'approved'] == 'allowed': name = dono['donation']['user']['username'] msg = dono['donation']['message'] if msg.isspace(): break # Check for empty line ## TODO Allow multiple speaker in msg currency = dono['donation']['currency'] dono_id = dono['_id'] text_ready.emit( "Log2:\n###########################") text_ready.emit("Log2:" + name + ' donated ' + currency + str(amount)) text_ready.emit("Log2:" + msg) lines = preprocess_text(msg) if se_opts[ 'read dono amount'] == 1: # reads dono name and amount msg = '{} donated {} {}.'.format( name, str(amount), cleaners.expand_currency(currency)) lines.insert(0, msg) # Add to head to list output = [] for count, line in enumerate(lines): fn_callback.emit( ('GUI: progress bar 2 text', (count, len(lines)))) sequence = np.array( text_to_sequence( line, ['english_cleaners']))[None, :] # Inference device = torch.device( 'cuda' if use_cuda else 'cpu') sequence = torch.autograd.Variable( torch.from_numpy(sequence)).to( device).long() # Decode text input mel_outputs, mel_outputs_postnet, _, alignments = model.inference( sequence) with torch.no_grad(): audio = waveglow.infer( mel_outputs_postnet, sigma=0.666, progress_callback=progress_callback, elapsed_callback=None, get_interruptflag=self. get_interruptflag2) if type(audio) != torch.Tensor: # Catches when waveglow is interrupted and returns none break fn_callback.emit( ('GUI: progress bar 2 text', (count + 1, len(lines)))) wav = audio[0].data.cpu().numpy() output.append(wav) _mutex3.lock() if _running3 == True: _mutex3.unlock() outwav = np.concatenate(output) # Playback fn_callback.emit(('Wav: playback', outwav)) else: _mutex3.unlock() prev_time = dono_time # Increment time time.sleep(0.5) fn_callback.emit(('GUI: end of polling loop', None)) text_ready.emit('Log2:\nDisconnected') text_ready.emit('Sta2:Ready') fn_callback.emit(('Var: offset', offset)) fn_callback.emit(('Var: prev_time', prev_time)) return #'Return value of execute_this_fn' def set_reload_model_flag(self): self.reload_model_flag = True def set_cuda(self): self.use_cuda = self.GpuSwitch.isChecked() self.reload_model_flag = True def startup_update(self): if not self.tab_2.isEnabled(): self.tab_2.setEnabled(True) if not self.TTSDialogButton.isEnabled(): self.TTSDialogButton.setEnabled(True) def add_TTmodel_path(self): fpath = str( QFileDialog.getOpenFileName(self, 'Select Tacotron2 model', filter='*.pt')[0]) if not fpath: # If no folder selected return if fpath not in self.TTmodel_dir: head, tail = os.path.split( fpath) # Split into parent and child dir self.TTmodel_dir.append(fpath) # Save full path self.populate_modelcombo(tail, self.TTModelCombo) self.update_log_window("Added Tacotron 2 model: " + tail) if self.WGModelCombo.count() > 0: self.startup_update() def add_WGmodel_path(self): fpath = str( QFileDialog.getOpenFileName(self, 'Select Waveglow model', filter='*.pt')[0]) if not fpath: # If no folder selected return if fpath not in self.WGmodel_dir: head, tail = os.path.split( fpath) # Split into parent and child dir self.WGmodel_dir.append(fpath) # Save full path self.populate_modelcombo(tail, self.WGModelCombo) self.update_log_window("Added Waveglow model: " + tail) if self.TTModelCombo.count() > 0: self.startup_update() def populate_modelcombo(self, item, combobox): combobox.addItem(item) combobox.setCurrentIndex(combobox.count() - 1) if not combobox.isEnabled(): combobox.setEnabled(True) def get_current_TTmodel_dir(self): return self.TTmodel_dir[self.TTModelCombo.currentIndex()] def get_current_WGmodel_dir(self): return self.WGmodel_dir[self.WGModelCombo.currentIndex()] def get_current_TTmodel_fname(self): return self.TTModelCombo.currentText() def get_current_WGmodel_fname(self): return self.WGModelCombo.currentText() def update_log_window(self, line, mode="newline"): if mode == "newline" or not self.logs: self.logs.append(line) if len(self.logs) > self.max_log_lines: del self.logs[0] elif mode == "append": self.logs[-1] += line elif mode == "overwrite": self.logs[-1] = line elif mode == "clear": self.logs = [line] log_text = '\n'.join(self.logs) self.log_window1.setText(log_text) #self.app.processEvents() def playback_wav(self, wav): #if self.tabWidget.currentIndex()==0: # self.TTSSkipButton.setEnabled(True) #else: # self.ClientSkipBtn.setEnabled(True) if self.tabWidget.currentIndex() == 1: self.ClientSkipBtn.setEnabled(True) if wav.dtype != np.int16: # Convert from float32 or float16 to signed int16 for pygame wav = (wav / np.amax(wav) * 32767).astype(np.int16) sound = pygame.mixer.Sound(wav) self.channel.queue(sound) # TODO Disable skip btn on playback end def skip_wav(self): if self.channel.get_busy(): self.channel.stop() self.ClientSkipBtn.setDisabled(True) def skip_infer_playback(self): global _running1 if self.channel.get_busy(): self.channel.stop() _mutex1.lock() # We could also use a signal/slot mechanism if _running1: self.progressBarLabel.setText('Interrupting...') _running1 = False # instead of mutex since inference is on QThread _mutex1.unlock() self.TTSSkipButton.setDisabled(True) def reload_model(self): TTmodel_fpath = self.get_current_TTmodel_dir() WGmodel_fpath = self.get_current_WGmodel_dir() # Setup hparams self.hparams = create_hparams() self.hparams.sampling_rate = 22050 # Load Tacotron 2 from checkpoint self.model = load_model(self.hparams, self.use_cuda) device = torch.device('cuda' if self.use_cuda else 'cpu') self.model.load_state_dict( torch.load(TTmodel_fpath, map_location=device)['state_dict']) if self.use_cuda: _ = self.model.cuda().eval().half() else: _ = self.model.eval() # Load WaveGlow for mel2audio synthesis and denoiser self.waveglow = torch.load(WGmodel_fpath, map_location=device)['model'] self.waveglow.use_cuda = self.use_cuda if self.use_cuda: self.waveglow.cuda().eval().half() else: self.waveglow.eval() for k in self.waveglow.convinv: k.float() #denoiser = Denoiser(waveglow,use_cuda=self.use_cuda) def start_synthesis(self): # Runs in main gui thread. Synthesize blocks gui. # Can update gui directly in this function. text = self.TTSTextEdit.toPlainText() if text.isspace(): return global _running1 self.t_1 = time.time() self.TTSDialogButton.setDisabled(True) self.TTModelCombo.setDisabled(True) self.WGModelCombo.setDisabled(True) self.TTSTextEdit.setDisabled(True) self.LoadTTButton.setDisabled(True) self.LoadWGButton.setDisabled(True) self.TTSSkipButton.setEnabled(True) self.tab_2.setDisabled(True) self.update_log_bar(0) self.update_log_window('Initializing', 'clear') self.update_status_bar("Creating voice") # We use a signal callback here to stick to the same params type in synthesize.py if self.reload_model_flag: self.reload_model() self.reload_model_flag = False # Prepare text input _mutex1.lock() _running1 = True _mutex1.unlock() self.current_thread = inferThread(text, self.use_cuda, self.model, self.waveglow, self.signals.progress, None, self.t_1, parent=self) self.current_thread.audioSignal.connect(self.on_inferThread_complete) self.current_thread.timeElapsed.connect(self.on_elapsed) self.current_thread.iterSignal.connect(self.on_itersignal) self.current_thread.interruptSignal.connect(self.on_interrupt) @pyqtSlot(np.ndarray) def on_inferThread_complete(self, wav): global _running1 _mutex1.lock() _running1 = False _mutex1.unlock() #audio_denoised = denoiser(audio, strength=0.01)[:, 0] #wav = audio_denoised.cpu().numpy() self.playback_wav(wav) self.TTSDialogButton.setEnabled(True) self.TTModelCombo.setEnabled(True) self.WGModelCombo.setEnabled(True) self.TTSTextEdit.setEnabled(True) self.LoadTTButton.setEnabled(True) self.LoadWGButton.setEnabled(True) self.tab_2.setEnabled(True) elapsed = (time.time() - self.t_1) wav_length = (len(wav) / self.hparams.sampling_rate) rtf = elapsed / wav_length line = 'Generated {:.1f}s of audio in {:.1f}s ({:.2f} real-time factor)'.format( wav_length, elapsed, rtf) self.update_log_window(line, 'overwrite') tps = elapsed / len(wav) print(" > Run-time: {}".format(elapsed)) print(" > Real-time factor: {}".format(rtf)) print(" > Time per step: {}".format(tps)) self.update_status_bar("Ready") # TODO get pygame mixer callback on end or use sounddevice @pyqtSlot(tuple) def on_itersignal(self, tup): current, total = tup self.progressBarLabel.setText('{}/{}'.format(current, total)) @pyqtSlot() def on_interrupt(self): # Reenable buttons self.TTSDialogButton.setEnabled(True) self.TTModelCombo.setEnabled(True) self.WGModelCombo.setEnabled(True) self.TTSTextEdit.setEnabled(True) self.LoadTTButton.setEnabled(True) self.LoadWGButton.setEnabled(True) self.tab_2.setEnabled(True) # Refresh progress bar self.update_log_bar(0) self.progressBarLabel.setText('') # Write to log window self.update_log_window('Interrupted', 'overwrite') # Write to status bar self.update_status_bar("Ready") def update_log_window_2(self, line, mode="newline"): if mode == "newline" or not self.logs2: self.logs2.append(line) elif mode == "append": self.logs2[-1] += line elif mode == "overwrite": self.logs2[-1] = line log_text = '\n'.join(self.logs2) self.log_window2.setPlainText(log_text) self.log_window2.verticalScrollBar().setValue( self.log_window2.verticalScrollBar().maximum()) self.app.processEvents() def update_status_bar(self, line): self.statusbar.setText(line) #self.app.processEvents() def get_token(self): TOKEN = ''.join(self.APIKeyLine.text().split()) return TOKEN #tokenobj = TOKEN() #return tokenobj.token def set_client_flag(self, val): self.client_flag = val def validate_se(self): # Connect to streamelement and saves channel id # return true if chn id and token returns valid # Test Channel ID self.update_status_bar("Validating StreamElements") CHANNEL_NAME = ''.join(self.ChannelName.text().split()) url = "https://api.streamelements.com/kappa/v2/channels/" + CHANNEL_NAME response = requests.request("GET", url, headers={'accept': 'application/json'}) if response.status_code == 200: # Test JWT Token self.channel_id = json.loads(response.text)['_id'] url = "https://api.streamelements.com/kappa/v2/tips/" + self.channel_id querystring = { "offset": "0", "limit": "10", "sort": "createdAt", "after": "0", "before": "0" } TOKEN = self.get_token() headers = { 'accept': 'application/json', "Authorization": "Bearer " + TOKEN } response2 = requests.request("GET", url, headers=headers, params=querystring) if response2.status_code == 200: self.update_log_window_2("\nConnected to " + CHANNEL_NAME) self.set_client_flag(True) return True else: self.update_log_window_2("\nError: Double check your token") self.update_status_bar("Invalid StreamElements") print(response2.text) else: self.update_log_window_2("\nError: Double check your channel name") self.update_status_bar("Invalid StreamElements") print(response.text) return False def get_min_donation(self): return float(self.ClientAmountLine.value())
class MS257_TEST_dialog(QDialog): def __init__(self, parent, inst_list): super().__init__(parent) # constants self.inst_list = inst_list # Initial read of the config file self.config = configparser.ConfigParser() try: self.config.read('config.ini') self.last_used_scan = self.config.get('LastScan','last_used_scan') self.grating = self.config.get(self.last_used_scan,'grating') self.unit_str = self.config.get(self.last_used_scan,'unit') self.shutter_str = self.config.get(self.last_used_scan,'shutter') self.ms257inport_str=self.config.get("Instruments",'ms257inport').strip().split(',')[0] self.ms257outport_str=self.config.get("Instruments",'ms257outport').strip().split(',')[0] except configparser.NoOptionError as e: QMessageBox.critical(self, 'Message',''.join(["Main FAULT while reading the config.ini file\n",str(e)])) raise self.setupUi() # close both MS257 serial ports if open if self.inst_list.get("MS257_in"): if self.inst_list.get("MS257_in").is_open(): self.inst_list.get("MS257_in").close() self.inst_list.pop('MS257_in', None) if self.inst_list.get("MS257_out"): if self.inst_list.get("MS257_out").is_open(): self.inst_list.get("MS257_out").close() self.inst_list.pop('MS257_out', None) def setupUi(self): self.serialport = QLabel("Serial port (input)",self) self.combo1 = QComboBox(self) mylist1=[self.ms257inport_str ,self.ms257outport_str] self.ms257port_str=self.ms257inport_str self.combo1.addItems(mylist1) self.combo1.setCurrentIndex(mylist1.index(self.ms257inport_str)) self.combo1.setFixedWidth(70) self.combo1.setEditable(True) grat_lbl = QLabel("Grating",self) grat_lbl.setFixedWidth(150) self.combo0 = QComboBox(self) mylist0=["0","1","2","3","4","home"] self.combo0.addItems(mylist0) self.combo0.setCurrentIndex(mylist0.index(self.grating)) self.combo0.setFixedWidth(70) setUnits_lbl = QLabel("Set unit",self) setUnits_lbl.setFixedWidth(150) self.combo2 = QComboBox(self) self.mylist2=["nm","um","wn"] self.combo2.addItems(self.mylist2) self.combo2.setCurrentIndex(self.mylist2.index(self.unit_str)) self.combo2.setFixedWidth(70) setShutter_lbl = QLabel("Set shutter",self) setShutter_lbl.setFixedWidth(150) self.combo3 = QComboBox(self) self.mylist3=["on","off", " "] self.combo3.addItems(self.mylist3) self.combo3.setCurrentIndex(self.mylist3.index(" ")) self.combo3.setFixedWidth(70) self.goWLButton = QPushButton("Go to wavelength",self) self.goWLButton.setFixedWidth(150) self.goWLButtonEdit = QLineEdit("",self) self.goWLButtonEdit.setFixedWidth(70) self.goPosButton = QPushButton("Go to position",self) self.goPosButton.setFixedWidth(150) self.goPosButtonEdit = QLineEdit("",self) self.goPosButtonEdit.setFixedWidth(70) self.clearButton = QPushButton("Clear",self) #self.clearButton.setFixedWidth(200) ############################################## g0_1 = QGridLayout() g0_1.addWidget(self.serialport,0,0) g0_1.addWidget(self.combo1,0,1) g0_1.addWidget(grat_lbl,1,0) g0_1.addWidget(self.combo0,1,1) g0_1.addWidget(setUnits_lbl,2,0) g0_1.addWidget(self.combo2,2,1) g0_1.addWidget(setShutter_lbl,3,0) g0_1.addWidget(self.combo3,3,1) g0_1.addWidget(self.goWLButton,4,0) g0_1.addWidget(self.goWLButtonEdit,4,1) g0_1.addWidget(self.goPosButton,5,0) g0_1.addWidget(self.goPosButtonEdit,5,1) g0_1.addWidget(self.clearButton,6,0,1,2) ############################################## # set graph and toolbar to a new vertical group vcan self.pw1 = pg.PlotWidget() ############################################## # create table self.tableWidget = self.createTable() ############################################## # SET ALL VERTICAL COLUMNS TOGETHER hbox = QHBoxLayout() hbox.addLayout(g0_1) hbox.addWidget(self.tableWidget) vbox = QVBoxLayout() vbox.addLayout(hbox) vbox.addWidget(self.pw1) self.threadpool = QThreadPool() print("Multithreading in the MS257 dialog with maximum %d threads" % self.threadpool.maxThreadCount()) self.isRunning=False self.setLayout(vbox) self.setWindowTitle("Test MS257 monochromator") # PLOT 2 settings # create plot and add it to the figure canvas self.p1 = self.pw1.plotItem self.curve1=self.p1.plot(pen='w') # create plot and add it to the figure self.p0_1 = pg.ViewBox() self.curve2=pg.PlotCurveItem(pen='r') self.p0_1.addItem(self.curve2) # connect respective axes to the plot #self.p1.showAxis('left') self.p1.getAxis('left').setLabel("Wavelength", units='m', color='yellow') self.p1.showAxis('right') self.p1.getAxis('right').setLabel("Position", units="", color='red') self.p1.scene().addItem(self.p0_1) self.p1.getAxis('right').linkToView(self.p0_1) self.p0_1.setXLink(self.p1) self.p1.getAxis('bottom').setLabel("Point no.", units="", color='yellow') # Use automatic downsampling and clipping to reduce the drawing load self.pw1.setDownsampling(mode='peak') self.pw1.setClipToView(True) # Initialize and set titles and axis names for both plots self.clear_vars_graphs() self.combo0.activated[str].connect(self.onActivated0) self.combo1.activated[str].connect(self.onActivated1) self.combo2.activated[str].connect(self.onActivated2) self.combo3.activated[str].connect(self.onActivated3) # run or cancel the main script self.clearButton.clicked.connect(self.clear_vars_graphs) self.clearButton.setEnabled(False) self.goPosButton.clicked.connect(self.set_runPosWl) self.goWLButton.clicked.connect(self.set_runPosWl) def is_number(self,s): try: float(s) return True except ValueError: pass try: import unicodedata unicodedata.numeric(s) return True except (TypeError, ValueError): pass return False def createTable(self): tableWidget = QTableWidget() # set row count #tableWidget.setRowCount(20) # set column count tableWidget.setColumnCount(5) # hide grid tableWidget.setShowGrid(False) # hide vertical header vh = tableWidget.verticalHeader() vh.setVisible(False) # set the font font = QFont("Courier New", 9) tableWidget.setFont(font) tableWidget.setStyleSheet("color: blue") # place content into individual table fields #tableWidget.setItem(0,0, QTableWidgetItem("abe")) tableWidget.setHorizontalHeaderLabels(["Port","Grat.","Point no.","Position","Wavelength"]) #tableWidget.setVerticalHeaderLabels(["aa","bb","cc","dd"]) # set horizontal header properties hh = tableWidget.horizontalHeader() hh.setStretchLastSection(True) # set column width to fit contents tableWidget.resizeColumnsToContents() # enable sorting #tableWidget.setSortingEnabled(True) return tableWidget def onActivated0(self, text): if not hasattr(self, 'MS257'): try: self.MS257 = MS257.MS257(self.ms257port_str, False) except Exception as e: reply = QMessageBox.critical(self, 'MS257 TEST MODE', ''.join(["MS257 could not return valid echo signal. Check the port name and check the connection.\n",str(e),"\n\nShould the program proceeds into the TEST MODE?"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.MS257 = MS257.MS257(self.ms257port_str, True) else: self.combo0.setCurrentIndex(self.mylist0.index(self.grating)) return self.MS257.set_timeout(60) else: try: if self.MS257.is_open(): self.MS257.close() self.MS257 = MS257.MS257(self.ms257port_str, False) except Exception as e: reply = QMessageBox.critical(self, 'MS257 TEST MODE', ''.join(["MS257 could not return valid echo signal. Check the port name and check the connection.\n",str(e),"\n\nShould the program proceeds into the TEST MODE?"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.MS257 = MS257.MS257(self.ms257port_str, True) else: self.combo0.setCurrentIndex(self.mylist0.index(self.grating)) return self.MS257.set_timeout(60) self.grating=str(text) self.clearButton.setEnabled(False) self.combo0.setEnabled(False) self.combo1.setEnabled(False) self.combo2.setEnabled(False) self.combo3.setEnabled(False) self.goWLButton.setEnabled(False) self.goWLButtonEdit.setEnabled(False) self.goPosButton.setEnabled(False) self.goPosButtonEdit.setEnabled(False) self.isRunning=True obj=type('obj',(object,),{ 'ms257':self.MS257, 'set_':"grating", 'val':str(text) }) self.worker=MS257_Worker(obj) self.worker.signals.finished.connect(self.finished) # Execute self.threadpool.start(self.worker) def onActivated1(self, text): self.ms257port_str=str(text) if self.ms257port_str=="COM3": self.serialport.setText("Serial port (output)") QMessageBox.warning(self, "Shutter warning", ''.join(["Shutter not available on port ", self.ms257port_str])) self.combo3.setEnabled(False) elif self.ms257port_str=="COM4": self.serialport.setText("Serial port (input)") self.combo3.setEnabled(True) print("Monochromator serial port changed to", str(text)) def onActivated2(self, text): if not hasattr(self, 'MS257'): try: self.MS257 = MS257.MS257(self.ms257port_str, False) except Exception as e: reply = QMessageBox.critical(self, 'MS257 TEST MODE', ''.join(["MS257 could not return valid echo signal. Check the port name and check the connection.\n",str(e),"\n\nShould the program proceeds into the TEST MODE?"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.MS257 = MS257.MS257(self.ms257port_str, True) else: self.combo2.setCurrentIndex(self.mylist2.index(self.unit_str)) return self.MS257.set_timeout(60) else: try: if self.MS257.is_open(): self.MS257.close() self.MS257 = MS257.MS257(self.ms257port_str, False) except Exception as e: reply = QMessageBox.critical(self, 'MS257 TEST MODE', ''.join(["MS257 could not return valid echo signal. Check the port name and check the connection.\n",str(e),"\n\nShould the program proceeds into the TEST MODE?"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.MS257 = MS257.MS257(self.ms257port_str, True) else: self.combo2.setCurrentIndex(self.mylist2.index(self.unit_str)) return self.MS257.set_timeout(60) self.unit_str=str(text) self.clearButton.setEnabled(False) self.combo0.setEnabled(False) self.combo1.setEnabled(False) self.combo2.setEnabled(False) self.combo3.setEnabled(False) self.goWLButton.setEnabled(False) self.goWLButtonEdit.setEnabled(False) self.goPosButton.setEnabled(False) self.goPosButtonEdit.setEnabled(False) self.isRunning=True obj=type('obj',(object,),{ 'ms257':self.MS257, 'set_':"unit", 'val':str(text) }) self.worker=MS257_Worker(obj) self.worker.signals.finished.connect(self.finished) # Execute self.threadpool.start(self.worker) def onActivated3(self, text): if not hasattr(self, 'MS257'): try: self.MS257 = MS257.MS257(self.ms257port_str, False) except Exception as e: reply = QMessageBox.critical(self, 'MS257 TEST MODE', ''.join(["MS257 could not return valid echo signal. Check the port name and check the connection.\n",str(e),"\n\nShould the program proceeds into the TEST MODE?"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.MS257 = MS257.MS257(self.ms257port_str, True) else: self.combo3.setCurrentIndex(self.mylist3.index(self.shutter_str)) return self.MS257.set_timeout(60) else: try: if self.MS257.is_open(): self.MS257.close() self.MS257 = MS257.MS257(self.ms257port_str, False) except Exception as e: reply = QMessageBox.critical(self, 'MS257 TEST MODE', ''.join(["MS257 could not return valid echo signal. Check the port name and check the connection.\n",str(e),"\n\nShould the program proceeds into the TEST MODE?"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.MS257 = MS257.MS257(self.ms257port_str, True) else: self.combo3.setCurrentIndex(self.mylist3.index(self.shutter_str)) return self.MS257.set_timeout(60) if str(text)==" ": QMessageBox.warning(self, 'Message',"Set the shutter to position on or position off.") self.combo3.setCurrentIndex(self.mylist3.index(self.shutter_str)) return self.shutter_str=str(text) self.clearButton.setEnabled(False) self.combo0.setEnabled(False) self.combo1.setEnabled(False) self.combo2.setEnabled(False) self.combo3.setEnabled(False) self.goWLButton.setEnabled(False) self.goWLButtonEdit.setEnabled(False) self.goPosButton.setEnabled(False) self.goPosButtonEdit.setEnabled(False) self.isRunning=True obj=type('obj',(object,),{ 'ms257':self.MS257, 'set_':"shutter", 'val':str(text) }) self.worker=MS257_Worker(obj) self.worker.signals.finished.connect(self.finished) # Execute self.threadpool.start(self.worker) def set_runPosWl(self): if not hasattr(self, 'MS257'): try: self.MS257 = MS257.MS257(self.ms257port_str, False) except Exception as e: reply = QMessageBox.critical(self, 'MS257 TEST MODE', ''.join(["MS257 could not return valid echo signal. Check the port name and check the connection.\n",str(e),"\n\nShould the program proceeds into the TEST MODE?"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.MS257 = MS257.MS257(self.ms257port_str, True) else: return self.MS257.set_timeout(60) else: try: if self.MS257.is_open(): self.MS257.close() self.MS257 = MS257.MS257(self.ms257port_str, False) except Exception as e: reply = QMessageBox.critical(self, 'MS257 TEST MODE', ''.join(["MS257 could not return valid echo signal. Check the port name and check the connection.\n",str(e),"\n\nShould the program proceeds into the TEST MODE?"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.MS257 = MS257.MS257(self.ms257port_str, True) else: return self.MS257.set_timeout(60) sender=self.sender() if sender.text()=="Go to wavelength": set_ = "wl" val = str(self.goWLButtonEdit.text()) if not val or not self.is_number(val): QMessageBox.warning(self, 'Message',"MS257 wavelength should be a numeric value") return if self.unit_str=='nm': if float(val)<0 or float(val)>2500: QMessageBox.warning(self, 'Message',"MS257 wavelength range is from 0 nm to 2500 nm") return if self.unit_str=='um': if float(val)<0.0 or float(val)>2.500: QMessageBox.warning(self, 'Message',"MS257 wavelength range is from 0 um to 2.5 um") return elif sender.text()=="Go to position": set_ = "pos" val = str(self.goPosButtonEdit.text()) if not val or not self.is_number(val): QMessageBox.warning(self, 'Message',"MS257 position should be a numeric value") return self.clearButton.setEnabled(False) self.combo0.setEnabled(False) self.combo1.setEnabled(False) self.combo2.setEnabled(False) self.combo3.setEnabled(False) self.goWLButton.setEnabled(False) self.goWLButtonEdit.setEnabled(False) self.goPosButton.setEnabled(False) self.goPosButtonEdit.setEnabled(False) self.isRunning=True obj=type('obj',(object,),{ 'ms257':self.MS257, 'set_':set_, 'val':val }) self.worker=MS257_Worker(obj) self.worker.signals.update0.connect(self.update0) self.worker.signals.finished.connect(self.finished) # Execute self.threadpool.start(self.worker) def update0(self,my_obj): pos, wl, grat = my_obj self.tal+=1 # set row height self.tableWidget.setRowCount(self.tal+1) self.tableWidget.setRowHeight(self.tal, 12) # add row elements self.tableWidget.setItem(self.tal, 0, QTableWidgetItem(self.ms257port_str)) self.tableWidget.setItem(self.tal, 1, QTableWidgetItem(grat)) self.tableWidget.setItem(self.tal, 2, QTableWidgetItem(str(self.tal))) self.tableWidget.setItem(self.tal, 3, QTableWidgetItem(str(pos))) self.tableWidget.setItem(self.tal, 4, QTableWidgetItem(str(wl))) self.tals.extend([ self.tal ]) self.plot_pos_tr.extend([ pos ]) self.plot_wl_tr.extend([ wl ]) #self.plot_time_tr.extend([ timelist ]) ## Handle view resizing def updateViews(): ## view has resized; update auxiliary views to match self.p0_1.setGeometry(self.p1.vb.sceneBoundingRect()) #p3.setGeometry(p1.vb.sceneBoundingRect()) ## need to re-update linked axes since this was called ## incorrectly while views had different shapes. ## (probably this should be handled in ViewBox.resizeEvent) self.p0_1.linkedViewChanged(self.p1.vb, self.p0_1.XAxis) #p3.linkedViewChanged(p1.vb, p3.XAxis) updateViews() self.p1.vb.sigResized.connect(updateViews) self.curve1.setData(self.tals, self.plot_wl_tr) self.curve2.setData(self.tals, self.plot_pos_tr) def finished(self): self.isRunning=False self.combo0.setEnabled(True) self.combo1.setEnabled(True) self.combo2.setEnabled(True) self.combo3.setEnabled(True) self.goWLButton.setEnabled(True) self.goWLButtonEdit.setEnabled(True) self.goPosButton.setEnabled(True) self.goPosButtonEdit.setEnabled(True) self.clearButton.setEnabled(True) self.clearButton.setText("Clear") def clear_vars_graphs(self): self.tal=-1 self.tals=[] self.all_time_tr=[] self.plot_pos_tr=[] self.plot_wl_tr=[] #self.plot_time_tr=[] self.curve1.clear() self.curve2.clear() self.tableWidget.clearContents() self.clearButton.setEnabled(False) self.clearButton.setText("Cleared") def closeEvent(self, event): reply = QMessageBox.question(self, 'Message', "Quit now? Changes will not be saved!", QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: if hasattr(self, 'MS257'): if not hasattr(self, 'worker'): if self.MS257.is_open(): self.MS257.close() else: if self.isRunning: QMessageBox.warning(self, 'Message', "Run in progress. Cancel the scan then quit!") event.ignore() return None else: if self.MS257.is_open(): self.MS257.close() event.accept() else: event.ignore()
class MainWindow(QWidget): def __init__(self, inList): super().__init__() self.inList = inList self.nameFrom = 'Folder' self.codec = 'utvideo' self.alpha = False self.frameRate = 24 self.defaulStyle = '' self.okIcon = QIcon(self.style().standardIcon(QStyle.SP_CustomBase)) self.okPix = QPixmap(self.okIcon.pixmap(QSize(13, 13))) self.goodIcon = QIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) self.goodPix = QPixmap(self.goodIcon.pixmap(QSize(13, 13))) self.badIcon = QIcon(self.style().standardIcon(QStyle.SP_MessageBoxCritical)) self.badPix = QPixmap(self.badIcon.pixmap(QSize(13, 13))) self.processingIcon = QIcon(self.style().standardIcon(QStyle.SP_ArrowRight)) self.processingPix = QPixmap(self.processingIcon.pixmap(QSize(13, 13))) self.removeIcon = QIcon(self.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) self.removePix = QPixmap(self.removeIcon.pixmap(QSize(19, 19))) self.folderIcon = QIcon(self.style().standardIcon(QStyle.SP_FileDialogNewFolder)) self.folderPix = QPixmap(self.folderIcon.pixmap(QSize(19, 19))) self.pbList = [] self.chList = [] self.lblList = [] self.rmbList = [] #self.newFolders = [] self.initUI() def initUI(self): self.resize(720, 300) self.setWindowTitle('FFMpeg Python Compressor') self.verticalLayout = QVBoxLayout(self) self.verticalLayout.setContentsMargins(11, 11, 11, 11) self.verticalLayout.setSpacing(11) #COMBOBOX LABELS self.gridLayoutControlls = QGridLayout() self.codecLabel = QLabel('Codec', self) self.codecLabel.setMinimumHeight(13) self.alphaLabel = QLabel('Alpha' , self) self.alphaLabel.setMinimumHeight(13) self.frameRateLabel = QLabel('Frame Rate' , self) self.frameRateLabel.setMinimumHeight(13) self.gridLayoutControlls.addWidget(self.codecLabel, 0, 0, 1, 1) self.gridLayoutControlls.addWidget(self.alphaLabel, 0, 1, 1, 1) self.gridLayoutControlls.addWidget(self.frameRateLabel, 0, 2, 1, 1) #COMBOBOXES AND COMPRESS BUTTON self.codecComboBox = QComboBox(self) self.codecComboBox.setMinimumSize(80,23) self.codecComboBox.addItem("UT Video") self.codecComboBox.activated[str].connect(self.chooseCodec) self.alphaComboBox = QComboBox(self) self.alphaComboBox.setMinimumSize(80,23) self.alphaComboBox.addItem("No Alpha") self.alphaComboBox.addItem("with Alpha") self.alphaComboBox.activated[str].connect(self.chooseAlpha) self.frameRateComboBox = QComboBox(self) self.frameRateComboBox.setMinimumSize(80,23) self.frameRateComboBox.addItem("23.976") self.frameRateComboBox.addItem("24.00") self.frameRateComboBox.addItem("29.97") self.frameRateComboBox.addItem("30.00") self.frameRateComboBox.setCurrentIndex(1) self.frameRateComboBox.activated[str].connect(self.chooseFrameRate) self.compressButton = QPushButton('Compress', self) self.compressButton.setMinimumSize(80,23) self.compressButton.clicked[bool].connect(self.compressPress) self.gridLayoutControlls.addWidget(self.codecComboBox, 1, 0, 1, 1) self.gridLayoutControlls.addWidget(self.alphaComboBox, 1, 1, 1, 1) self.gridLayoutControlls.addWidget(self.frameRateComboBox, 1, 2, 1, 1) self.gridLayoutControlls.addWidget(self.compressButton, 1, 3, 1, 1) #RADIO BUTTON GROUP self.groupBox = QButtonGroup(self) self.radio1 = QRadioButton('Output file name from Folder name', self) self.radio1.setMinimumSize(80,25) self.radio2 = QRadioButton('Output file name from File name', self) self.radio2.setMinimumSize(80,25) self.radio1.setChecked(True) self.groupBox.addButton(self.radio1,1) self.groupBox.addButton(self.radio2,2) self.groupBox.buttonClicked[int].connect(self.radioBtnState) self.gridLayoutControlls.addWidget(self.radio1, 2, 0, 1, 2) self.gridLayoutControlls.addWidget(self.radio2, 2, 2, 1, 2) #LINE self.line = QFrame(self) self.line.setLineWidth(2) self.line.setMinimumHeight(3) self.line.setFrameShape(QFrame.HLine) self.line.setFrameShadow(QFrame.Sunken) self.gridLayoutControlls.addWidget(self.line, 3, 0, 1, 4) #PROGRESS BAR self.gridLayoutProgress = QGridLayout() self.gridLayoutProgress.setVerticalSpacing(11) self.gridLayoutProgress.setHorizontalSpacing(6) self.gridLayoutProgress.setSizeConstraint(QLayout.SetNoConstraint) self.removeGroupBox = QButtonGroup(self) self.addProgressBarUI(self.inList) self.removeGroupBox.buttonClicked[int].connect(self.removeButtonClicked) #ADD MORE AREA self.gridLayoutAddMore = QGridLayout() self.gridLayoutAddMore.setContentsMargins(0, 0, 0, 0) self.dragAndDropLabel_1 = QLabel("Drag and Drop folders here", self) self.dragAndDropLabel_1.setMinimumSize(QSize(120, 40)) self.dragAndDropLabel_1.setAlignment(Qt.AlignCenter) self.dragAndDropLabel_2 = QLabel("", self) self.dragAndDropLabel_2.setFixedSize(QSize(20, 40)) self.dragAndDropLabel_2.setAlignment(Qt.AlignCenter) self.dragAndDropLabel_2.setPixmap(self.folderPix) sI = QSpacerItem(40, 40,QSizePolicy.Expanding, QSizePolicy.Minimum) sI2 = QSpacerItem(40, 40,QSizePolicy.Expanding, QSizePolicy.Minimum) self.gridLayoutAddMore.addItem(sI, 1, 0, 1, 1) self.gridLayoutAddMore.addWidget(self.dragAndDropLabel_2, 1, 1, 1, 1) self.gridLayoutAddMore.addWidget(self.dragAndDropLabel_1, 1, 2, 1, 1) self.gridLayoutAddMore.addItem(sI2, 1, 3, 1, 1) #DEBUG AREA self.gridLayoutDebug = QGridLayout() self.line_2 = QFrame(self) self.line_2.setLineWidth(2) self.line_2.setFrameShape(QFrame.HLine) self.line_2.setFrameShadow(QFrame.Sunken) self.exploreOutputBtn = QPushButton('Explore Output',self) self.exploreOutputBtn.clicked[bool].connect(self.exploreOutput) self.hideShowLog = QPushButton('Show Log',self) self.hideShowLog.setCheckable(True) self.hideShowLog.clicked[bool].connect(self.showDebugLog) #self.hideShowLog.setMinimumSize(QSize(0, 20)) self.logText = QPlainTextEdit('',self) self.logText.setReadOnly(True) self.logText.hide() self.spacerItem = QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Expanding) self.gridLayoutDebug.addWidget(self.line_2, 0, 0, 1, 1) self.gridLayoutDebug.addWidget(self.exploreOutputBtn, 1, 0, 1, 1) self.gridLayoutDebug.addWidget(self.hideShowLog, 2, 0, 1, 1) self.gridLayoutDebug.addWidget(self.logText, 3, 0, 1, 1) self.gridLayoutDebug.addItem(self.spacerItem, 4, 0, 1, 1) self.verticalLayout.addLayout(self.gridLayoutControlls) self.verticalLayout.addLayout(self.gridLayoutProgress) self.verticalLayout.addLayout(self.gridLayoutAddMore) self.verticalLayout.addLayout(self.gridLayoutDebug) # Enable dragging and dropping onto the GUI self.setAcceptDrops(True) #QtCore.QMetaObject.connectSlotsByName(self) self.show() self.setMinimumSize(self.size()) self.threadpool = QThreadPool() self.threadpool.setMaxThreadCount(1) print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) ''' Progress bar populate function ''' def addProgressBarUI(self, arr): pbCount = len(self.pbList) for i in range(len(arr)): tempCheckBox = QCheckBox(self) tempCheckBox.setChecked(True) tempCheckBox.setMinimumSize(14,21) tempRemoveButton = QPushButton(self) tempRemoveButton.setIcon(self.removeIcon) tempRemoveButton.setFlat(True) tempRemoveButton.setIconSize(QSize(19,19)) tempRemoveButton.setFixedSize(QSize(19,21)) tempPB = QProgressBar(self) tempPB.setMinimumSize(50,21) tempPB.setMinimum(0) tempPB.setMaximum(100) tempPB.setTextVisible(True) tempPB.setFormat(str(arr[i])+" %p%") tempPB.setAlignment(Qt.AlignCenter) tempPB.setValue(0) if i==0: self.defaulStyle = tempPB.styleSheet() tempStatusLabel = QLabel(self) tempStatusLabel.setPixmap(self.okPix) tempStatusLabel.setMinimumSize(13,21) self.gridLayoutProgress.addWidget(tempCheckBox, pbCount+i, 0, 1, 1) self.gridLayoutProgress.addWidget(tempPB, pbCount+i, 1, 1, 1) self.gridLayoutProgress.addWidget(tempStatusLabel, pbCount+i, 2, 1, 1) self.gridLayoutProgress.addWidget(tempRemoveButton, pbCount+i, 3, 1, 1) self.removeGroupBox.addButton(tempRemoveButton, pbCount+i) self.pbList.append(tempPB) self.chList.append(tempCheckBox) self.lblList.append(tempStatusLabel) self.rmbList.append(tempRemoveButton) ''' Drag+Drop Functions ''' # The following three methods set up dragging and dropping for the app def dragEnterEvent(self, e): if e.mimeData().hasUrls: e.accept() else: e.ignore() def dragMoveEvent(self, e): if e.mimeData().hasUrls: e.accept() else: e.ignore() def dropEvent(self, e): """ Drop files directly onto the widget File locations are stored in fname :param e: :return: """ newFolders = [] if e.mimeData().hasUrls: e.setDropAction(Qt.CopyAction) e.accept() # Workaround for OSx dragging and dropping for url in e.mimeData().urls(): if op_sys == 'Darwin': #check for dir here as well fname = str(NSURL.URLWithString_(str(url.toString())).filePathURL().path()) else: fname = str(url.toLocalFile()) if os.path.isdir(fname) == True: newFolders.append(fname) #print(fname) #self.fname = fname #print(self.fname) #self.load_image() self.addNewFolders(newFolders) self.inList = self.inList + newFolders else: e.ignore() def addNewFolders(self, newFolders): self.addProgressBarUI(newFolders) self.setMinimumHeight(self.height()+len(newFolders)*32) #self.resize(self.width(),self.height()+200) self.update() self.adjustSize() self.setMinimumSize(self.size()) ''' Button Functions ''' def chooseAlpha(self, text): switcher={ "No Alpha":False, "with Alpha":True } self.alpha = switcher.get(text,"Invalid day of week") #print (self.alpha) def chooseCodec(self, text): switcher={ "UT Video":"utvideo" } self.codec = switcher.get(text,"Invalid day of week") #print (self.codec) def chooseFrameRate(self, text): self.frameRate = float(text) #print (self.frameRate) def currentData(self, widget): return widget.currentText() def radioBtnState(self, text): switcher={ 1:'Folder', 2:'File' } self.nameFrom = switcher.get(text,"Invalid day of week") #print(self.nameFrom) def removeButtonClicked(self, i): #print('remove trigger on id '+str(i)) #self.pbList.pop(i) ''' self.pbList[i].hide() self.chList[i].setChecked(False) self.chList[i].hide() self.lblList[i].hide() self.rmbList[i].hide() ''' print(self.gridLayoutProgress.rowCount()) self.pbList[i].setParent(None) self.chList[i].setChecked(False) self.chList[i].setParent(None) self.lblList[i].setParent(None) self.rmbList[i].setParent(None) self.removeGroupBox.removeButton(self.rmbList[i]) self.gridLayoutProgress.removeWidget(self.pbList[i]) self.gridLayoutProgress.removeWidget(self.chList[i]) self.gridLayoutProgress.removeWidget(self.lblList[i]) self.gridLayoutProgress.removeWidget(self.rmbList[i]) self.gridLayoutProgress.invalidate() self.gridLayoutProgress = QGridLayout() self.gridLayoutProgress.setVerticalSpacing(11) self.gridLayoutProgress.setHorizontalSpacing(6) self.gridLayoutProgress.setSizeConstraint(QLayout.SetDefaultConstraint) self.verticalLayout.insertLayout(1,self.gridLayoutProgress) print(self.gridLayoutProgress.rowCount()) ''' print(self.pbList) print(self.chList) print(self.lblList) print(self.rmbList) ''' self.pbList.pop(i) self.chList.pop(i) self.lblList.pop(i) self.rmbList.pop(i) self.inList.pop(i) #clear the gridLayout for j in reversed(range(len(self.pbList))): self.pbList[j].setParent(None) self.chList[j].setParent(None) self.lblList[j].setParent(None) self.rmbList[j].setParent(None) #reorder the gridLayout for j in range(len(self.pbList)): self.gridLayoutProgress.addWidget(self.chList[j], j, 0, 1, 1) self.gridLayoutProgress.addWidget(self.pbList[j], j, 1, 1, 1) self.gridLayoutProgress.addWidget(self.lblList[j], j, 2, 1, 1) self.gridLayoutProgress.addWidget(self.rmbList[j], j, 3, 1, 1) self.removeGroupBox.setId(self.rmbList[j],j) self.setMinimumHeight(self.height()-30) #self.setMinimumHeight(100) self.adjustSize() self.setMinimumSize(self.size()) print(self.gridLayoutProgress.rowCount()) #self.correctSize() ''' for j in range(len(self.removeGroupBox.buttons())): button = self.removeGroupBox.buttons()[j] #print(button) #print('original id '+str(self.removeGroupBox.id(button))) #print('new id '+str(j)) self.removeGroupBox.setId(button,j) ''' def correctSize(self): self.logText.show() self.gridLayoutDebug.removeItem(self.spacerItem) self.adjustSize() self.setMinimumSize(self.size()) self.repaint() self.gridLayoutDebug.addItem(self.spacerItem) self.logText.hide() self.setMinimumHeight(100) self.adjustSize() self.setMinimumSize(self.size()) def showDebugLog(self, bol): if bol: self.logText.show() self.hideShowLog.setText('Hide Log') self.gridLayoutDebug.removeItem(self.spacerItem) self.adjustSize() #self.resize(self.width(), self.height()+80) self.setMinimumSize(self.size()) else: self.gridLayoutDebug.addItem(self.spacerItem) self.logText.hide() self.hideShowLog.setText('Show Log') self.setMinimumHeight(100) self.adjustSize() self.setMinimumSize(self.size()) #self.resize(self.width(), 100) #self.setGeometry(0,0,self.width(),100) ''' Execution Functions ''' def execute_this_fn(self, path, codec, alpha, frameRate, nameFrom, i, progress_callback, errorFFMPEG_callback): #print(path) pyCompression = pyFFMEGCompress(path, codec, alpha, frameRate, nameFrom) ffProcess = pyCompression.ffmpegCompress() self.lblList[i].setPixmap(self.processingPix) #with kwargs kwargs = {'progress_callback':progress_callback, 'errorFFMPEG_callback':errorFFMPEG_callback} pyCompression.printProcess(ffProcess, **kwargs) return (pyCompression.debugString,pyCompression.error,i) def printOutput(self, s): print("Printing output "+ str(s)) def threadComplete(self, r): #print("THREAD COMPLETE! WITH ERROR " + str(r[2]) ) if r[1] == False: self.lblList[r[2]].setPixmap(self.goodPix) self.pbList[r[2]].setStyleSheet("QProgressBar::chunk {background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #44dd14,stop: 0.4999 #39c10f,stop: 0.5 #39c10f,stop: 1 #39c10f );border-radius: 3px; border: 1px solid #29880b;}QProgressBar{color:white}") self.pbList[r[2]].setValue(100) def errorPB(self, err): for i in range(len(self.pbList)): if self.pbList[i].format() == err: self.pbList[i].setValue(100) self.pbList[i].setStyleSheet("QProgressBar::chunk {background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #FF0350,stop: 0.4999 #FF0020,stop: 0.5 #FF0019,stop: 1 #FF0000 );border-radius: 3px; border: 1px solid #a60233;}QProgressBar{color:white}") self.pbList[i].setFormat(self.pbList[i].format()+" - Error") self.chList[i].setChecked(False) self.lblList[i].setPixmap(self.badPix) def resetProgressBar(self, pb, text, lbl): pb.setValue(0) pb.setFormat(text + ' %p%') pb.setStyleSheet(self.defaulStyle) lbl.setPixmap(self.okPix) def compressPress(self): for i in range(len(self.pbList)): if self.chList[i].isChecked(): self.resetProgressBar(self.pbList[i],self.inList[i],self.lblList[i]) worker = Worker(self.execute_this_fn, self.inList[i], self.codec, self.alpha, self.frameRate, self.nameFrom, i) # Any other args, kwargs are passed to the run function #worker.signals.result.connect(self.printOutput) worker.signals.result.connect(self.logText.appendPlainText) worker.signals.progress.connect(self.pbList[i].setValue) worker.signals.errorFFMPEG.connect(self.errorPB) worker.signals.error.connect(self.errorPB) worker.signals.finished.connect(self.threadComplete) #worker.signals.finished.connect(self.logText.appendPlainText) # Execute self.threadpool.start(worker) def exploreOutput(self): FILEBROWSER_PATH = os.path.join(os.getenv('WINDIR'), 'explorer.exe') explorePath = os.path.normpath('C:\\ExportedMOVs') subprocess.run([FILEBROWSER_PATH, explorePath])
class WaveformGraphWidget(QWaveformGraph, Ui_waveformGraph): newBkgSignal = pyqtSignal() def __init__(self, parent=None): """ Notes: Needs a newDataSignal to be defined from the parent using self.connect_attr """ super(WaveformGraphWidget, self).__init__( parent=parent ) # passing parent here is important so that widget is properly displayed in parent widget. self.setupUi(self) try: self.threadpool = self.parent().threadpool except AttributeError: self.threadpool = QThreadPool(maxThreadCount=4) print('Threadpool with {} threads started.'.format( self.threadpool.maxThreadCount())) # epics PV setup self.pv = PV(config.DEFAULT_CHANNEL) self.channelEdit.setText(config.DEFAULT_CHANNEL) self.channelEdit.returnPressed.connect(self.change_pv) # graph (using remote lead to problems with QObjects (cant be pickled)) self.wfPlot = pg.PlotWidget() self.pgLayout.addWidget(self.wfPlot) self.wfCurve = utils.initialize_line_plot(self.wfPlot, color=config.COLORS[0]) self.wfFit = utils.initialize_line_plot(self.wfPlot, color=config.COLORS[1]) # self.view = pg.widgets.RemoteGraphicsView.RemoteGraphicsView() # self.wfPlot = self.view.pg.PlotItem() # self.pgLayout.addWidget(self.view) self.lr = pg.LinearRegionItem([0, 100]) self.lr.setZValue(-10) self.wfPlot.addItem(self.lr) self.lr.sigRegionChangeFinished.connect(self.get_roi) # background function self.new_bkg() self.bkgEdit.returnPressed.connect(self.new_bkg) return def connect_attr(self, name, attr): setattr(self, name, attr) return @pyqtSlot() def change_pv(self): self.pv = PV(self.channelEdit.text()) return @pyqtSlot() def get_roi(self): self.roi = np.round(self.lr.getRegion()).astype(int) return self.roi @pyqtSlot() def get_data(self): self.worker = GetWfWorker(self.pv, bkg_fun=self.bkg_fun, signal=self.newDataSignal) self.threadpool.tryStart( self.worker ) # skip (no queuing) if no thread available (if rate is too high) # self.worker.signals.signal.connect(self.display_data) return @pyqtSlot(dict) def display_data_fit(self, data_dict): data = data_dict['data'] fit = data_dict['fit'] # print(data.shape) self.wfCurve.setData(data[0], data[1]) # self.wfPlot.plot(data[0],data[1], clear=True) if fit is not None: self.wfFit.setData(fit[0], fit[1]) return @pyqtSlot() def new_bkg(self): bkg_idx = int(self.bkgEdit.text()) if (bkg_idx <= 0) or (bkg_idx is None): self.bkg_fun = lambda wf: 0 print('Background subtraction removed.') else: self.bkg_fun = lambda wf: utils.background(wf, bkg_idx=bkg_idx) print('Background subtraction updated.') self.newBkgSignal.emit() return
class ccbcGUI(QMainWindow, Ui_MainWindow): # TODO: Make generalized dictionary look up that includes exception handling def __init__(self, ard_dictionary, tsensor_names, psensor_names, heater_names, pump_names): super(self.__class__, self).__init__() self.setupUi(self) self.ard_dictionary = ard_dictionary self.tsensor_names = tsensor_names self.psensor_names = psensor_names self.heater_names = heater_names self.pump_names = pump_names self.thread = QThread() self.threadpool = QThreadPool() self.logger = Logger(self.ard_dictionary) self.logger_timer = QTimer() self.CBHeater1TSensor.addItems(tsensor_names) self.CBHeater2TSensor.addItems(tsensor_names) self.CBHeater3TSensor.addItems(tsensor_names) self.CBHeater1TSensor.currentIndexChanged.connect( self.update_heater1_tsensor) self.CBHeater2TSensor.currentIndexChanged.connect( self.update_heater2_tsensor) self.CBHeater3TSensor.currentIndexChanged.connect( self.update_heater3_tsensor) self.CBPump1PSensor.addItems(psensor_names) self.CBPump2PSensor.addItems(psensor_names) self.CBPump3PSensor.addItems(psensor_names) self.CBPump1PSensor.currentIndexChanged.connect( self.update_pump1_psensor) self.CBPump2PSensor.currentIndexChanged.connect( self.update_pump2_psensor) self.CBPump3PSensor.currentIndexChanged.connect( self.update_pump3_psensor) self.LabelHeater1TSensor.setText( 'Controlling Temperature Sensor: {}'.format( self.ard_dictionary['heaters'][ heater_names[0]]['tsensor_name'])) self.LabelHeater2TSensor.setText( 'Controlling Temperature Sensor: {}'.format( self.ard_dictionary['heaters'][ heater_names[1]]['tsensor_name'])) self.LabelHeater3TSensor.setText( 'Controlling Temperature Sensor: {}'.format( self.ard_dictionary['heaters'][ heater_names[2]]['tsensor_name'])) self.Button_startSerial.clicked.connect(self.start_everything) self.ButtonUpdateHeater1Setpoints.clicked.connect( self.update_heater1_setpoint) self.ButtonUpdateHeater2Setpoints.clicked.connect( self.update_heater2_setpoint) self.ButtonUpdateHeater3Setpoints.clicked.connect( self.update_heater3_setpoint) self.ButtonUpdateHeater1MaxTemp.clicked.connect( self.update_heater1_maxtemp) self.ButtonUpdateHeater2MaxTemp.clicked.connect( self.update_heater2_maxtemp) self.ButtonUpdateHeater3MaxTemp.clicked.connect( self.update_heater3_maxtemp) self.ButtonUpdatePump1Setpoints.clicked.connect( self.update_pump1_setpoint) self.ButtonUpdatePump2Setpoints.clicked.connect( self.update_pump2_setpoint) self.ButtonUpdatePump3Setpoints.clicked.connect( self.update_pump3_setpoint) self.ButtonUpdatePump1VolCalcInputs.clicked.connect( self.update_pump1_vol_calc_inputs) self.ButtonUpdatePump2VolCalcInputs.clicked.connect( self.update_pump2_vol_calc_inputs) self.ButtonUpdatePump3VolCalcInputs.clicked.connect( self.update_pump3_vol_calc_inputs) self.update_static_labels() self.update_labels() self.label_timer = QTimer() self.Clock = Clock(parent=self.centralwidget) self.Clock.setGeometry(QtCore.QRect(840, 10, 161, 61)) self.Clock.setObjectName("Clock") self.BrewingTime = BrewingTime(parent=self.centralwidget) self.BrewingTime.setGeometry(QtCore.QRect(840, 60, 161, 61)) self.BrewingTimeWidget.setObjectName("BrewingTime") self.start_time_edit.setDateTime(QDateTime.currentDateTime()) self.PausePushButton.clicked.connect(self.pause_or_resume_brew_time) self.StartNowButton.clicked.connect(self.start_brew_time) self.show() self.start_everything() print("Multithreading with maximum {} threads".format( self.threadpool.maxThreadCount())) def check_table_setpoints(self): """ This routine looks up the setpoint tables and updates the values accordingly """ pass def update_heater1_tsensor(self, i): """ Updates the heater 1 sensor to be the name given in the drop down menu""" self.ard_dictionary['heaters'][self.heater_names[0]][ 'tsensor_name'] = self.CBHeater1TSensor.currentText() def update_heater2_tsensor(self, i): """ Updates the heater 2 sensor to be the name given in the drop down menu""" self.ard_dictionary['heaters'][self.heater_names[1]][ 'tsensor_name'] = self.CBHeater2TSensor.currentText() def update_heater3_tsensor(self, i): """ Updates the heater 3 sensor to be the name given in the drop down menu""" self.ard_dictionary['heaters'][self.heater_names[2]][ 'tsensor_name'] = self.CBHeater3TSensor.currentText() def update_pump1_psensor(self, i): """ Updates the pump1 sensor to be the one in the drop down menu""" self.ard_dictionary['pumps'][self.pump_names[0]][ 'psensor_name'] = self.CBPump1PSensor.currentText() def update_pump2_psensor(self, i): """ Updates the pump1 sensor to be the one in the drop down menu""" self.ard_dictionary['pumps'][self.pump_names[1]][ 'psensor_name'] = self.CBPump2PSensor.currentText() def update_pump3_psensor(self, i): """ Updates the pump1 sensor to be the one in the drop down menu""" self.ard_dictionary['pumps'][self.pump_names[2]][ 'psensor_name'] = self.CBPump3PSensor.currentText() def update_pump_setpoint(self, upper_input_text, lower_input_text, pump_name): """ Changes the setpoints of a pump""" # Grab the values inside of the input boxes upper_limit = upper_input_text.text() lower_limit = lower_input_text.text() # Backfill the old setpoint values if none were entered if upper_limit == "": upper_limit = self.ard_dictionary['pumps'][pump_name][ 'upper limit'] else: upper_limit = float(upper_limit) if lower_limit == "": lower_limit = self.ard_dictionary['pumps'][pump_name][ 'lower limit'] else: lower_limit = float(lower_limit) upper_limit, lower_limit = self.check_component_setpoints( pump_name, upper_limit, lower_limit) # Set the values in the ard_dictionary self.ard_dictionary['pumps'][pump_name]['upper limit'] = upper_limit self.ard_dictionary['pumps'][pump_name]['lower limit'] = lower_limit # Clear the inputs for setpoint_input in [upper_input_text, lower_input_text]: setpoint_input.clear() def update_pump1_setpoint(self): worker = Worker(self.update_pump_setpoint, self.InputPump1VolUpper, self.InputPump1VolLower, self.pump_names[0]) worker.run() def update_pump2_setpoint(self): worker = Worker(self.update_pump_setpoint, self.InputPump2VolUpper, self.InputPump2VolLower, self.pump_names[1]) worker.run() def update_pump3_setpoint(self): worker = Worker(self.update_pump_setpoint, self.InputPump3VolUpper, self.InputPump3VolLower, self.pump_names[2]) worker.run() def update_pump_vol_calc_inputs(self, slope_obj, intercept_obj, pump_name): slope = slope_obj.text() intercept = intercept_obj.text() if slope == "": slope = self.ard_dictionary['pumps'][pump_name]['psi_to_gal_slope'] else: slope = float(slope) if intercept == "": intercept = self.ard_dictionary['pumps'][pump_name][ 'psi_to_gal_intercept'] else: intercept = float(intercept) self.ard_dictionary['pumps'][pump_name]['psi_to_gal_slope'] = round( slope, 2) self.ard_dictionary['pumps'][pump_name][ 'psi_to_gal_intercept'] = round(intercept, 2) # clear the inputs for setpoint_input in [slope_obj, intercept_obj]: setpoint_input.clear() def update_pump1_vol_calc_inputs(self): self.update_pump_vol_calc_inputs(self.InputPump1VolSlope, self.InputPump1VolIntercept, self.pump_names[0]) def update_pump2_vol_calc_inputs(self): self.update_pump_vol_calc_inputs(self.InputPump2VolSlope, self.InputPump2VolIntercept, self.pump_names[1]) def update_pump3_vol_calc_inputs(self): self.update_pump_vol_calc_inputs(self.InputPump3VolSlope, self.InputPump3VolIntercept, self.pump_names[2]) def update_heater_setpoint(self, upper_input_text, lower_input_text, heater_name): """ Changes the setpoints of a heater""" # Grab the values inside of the input boxes upper_limit = upper_input_text.toPlainText() lower_limit = lower_input_text.toPlainText() # Backfill the old setpoint values if none were entered if upper_limit == "": upper_limit = self.ard_dictionary['heaters'][heater_name][ 'upper limit'] else: upper_limit = float(upper_limit) if lower_limit == "": lower_limit = self.ard_dictionary['heaters'][heater_name][ 'lower limit'] else: lower_limit = float(lower_limit) # Check the inputs upper_limit, lower_limit = self.check_component_setpoints( heater_name, upper_limit, lower_limit) # Set the values in the ard_dictionary self.ard_dictionary['heaters'][heater_name]['upper limit'] = float( upper_limit) self.ard_dictionary['heaters'][heater_name]['lower limit'] = float( lower_limit) # Clear the inputs for setpoint_input in [upper_input_text, lower_input_text]: setpoint_input.clear() def check_component_setpoints(self, component_name, upper, lower): """ Does a simple check of the heater inputs""" # Check to make sure that the new upper value is above the lower setpoint if upper < lower: print( ("Upper limit {} for {} is lower than the lower setpoint {}.\n" "Changing upper to be 1 degree above setpoint").format( upper, component_name, lower)) upper = lower + 1.0 # Check to make sure that the new lower value is less than the upper setpoint if lower > upper: print(( "Lower limit {} for {} is higher than the higher setpoint {}.\n" "Changing lower limit to be 1 degree below upper setpoint" ).format(lower, component_name, upper)) lower = upper - 1.0 return upper, lower def update_heater1_setpoint(self): worker = Worker(self.update_heater_setpoint, self.InputHeater1Upper, self.InputHeater1Lower, self.heater_names[0]) worker.run() def update_heater2_setpoint(self): worker = Worker(self.update_heater_setpoint, self.InputHeater2Upper, self.InputHeater2Lower, self.heater_names[1]) worker.run() def update_heater3_setpoint(self): worker = Worker(self.update_heater_setpoint, self.InputHeater3Upper, self.InputHeater3Lower, self.heater_names[2]) worker.run() def update_heater1_maxtemp(self): setpoint = self.InputHeater1MaxTemp.toPlainText() self.ard_dictionary['heaters'][ self.heater_names[0]]['maxtemp'] = setpoint self.InputHeater1MaxTemp.clear() def update_heater2_maxtemp(self): setpoint = self.InputHeater2MaxTemp.toPlainText() self.ard_dictionary['heaters'][ self.heater_names[1]]['maxtemp'] = setpoint self.InputHeater2MaxTemp.clear() def update_heater3_maxtemp(self): setpoint = self.InputHeater3MaxTemp.toPlainText() self.ard_dictionary['heaters'][ self.heater_names[2]]['maxtemp'] = setpoint self.InputHeater3MaxTemp.clear() def update_static_labels(self): # Update the status page text variables self.LabelT1.setText( self.ard_dictionary['tempsensors'][self.tsensor_names[0]]['name']) self.LabelT2.setText( self.ard_dictionary['tempsensors'][self.tsensor_names[1]]['name']) self.LabelT3.setText( self.ard_dictionary['tempsensors'][self.tsensor_names[2]]['name']) self.LabelT4.setText( self.ard_dictionary['tempsensors'][self.tsensor_names[3]]['name']) self.LabelT5.setText( self.ard_dictionary['tempsensors'][self.tsensor_names[4]]['name']) self.LabelT6.setText( self.ard_dictionary['tempsensors'][self.tsensor_names[5]]['name']) self.LabelT7.setText( self.ard_dictionary['tempsensors'][self.tsensor_names[6]]['name']) self.LabelT8.setText( self.ard_dictionary['tempsensors'][self.tsensor_names[7]]['name']) self.LabelT9.setText( self.ard_dictionary['tempsensors'][self.tsensor_names[8]]['name']) self.LabelPress1.setText( self.ard_dictionary['presssensors'][self.psensor_names[0]]['name']) self.LabelPress2.setText( self.ard_dictionary['presssensors'][self.psensor_names[1]]['name']) self.LabelPress3.setText( self.ard_dictionary['presssensors'][self.psensor_names[2]]['name']) self.LabelPress4.setText( self.ard_dictionary['presssensors'][self.psensor_names[3]]['name']) self.VariablePress1.setText( str(self.ard_dictionary['presssensors'][self.psensor_names[0]] ['pressure'])) self.VariablePress2.setText( str(self.ard_dictionary['presssensors'][self.psensor_names[1]] ['pressure'])) self.VariablePress3.setText( str(self.ard_dictionary['presssensors'][self.psensor_names[2]] ['pressure'])) self.VariablePress4.setText( str(self.ard_dictionary['presssensors'][self.psensor_names[3]] ['pressure'])) self.LabelH1.setText( self.ard_dictionary['heaters'][self.heater_names[0]]['name']) self.LabelH2.setText( self.ard_dictionary['heaters'][self.heater_names[1]]['name']) self.LabelH3.setText( self.ard_dictionary['heaters'][self.heater_names[2]]['name']) self.VariableH1.setText( self.ard_dictionary['heaters'][self.heater_names[0]]['status']) self.VariableH2.setText( self.ard_dictionary['heaters'][self.heater_names[1]]['status']) self.VariableH3.setText( self.ard_dictionary['heaters'][self.heater_names[2]]['status']) self.LabelPump1.setText( self.ard_dictionary['pumps'][self.pump_names[0]]['name']) self.LabelPump2.setText( self.ard_dictionary['pumps'][self.pump_names[1]]['name']) self.LabelPump3.setText( self.ard_dictionary['pumps'][self.pump_names[2]]['name']) self.VariablePump1.setText( self.ard_dictionary['pumps'][self.pump_names[0]]['status']) self.VariablePump2.setText( self.ard_dictionary['pumps'][self.pump_names[1]]['status']) self.VariablePump3.setText( self.ard_dictionary['pumps'][self.pump_names[2]]['status']) self.VariableH1SetPoint.setText( str(self.ard_dictionary['heaters'][self.heater_names[0]] ['upper limit'])) self.VariableH2SetPoint.setText( str(self.ard_dictionary['heaters'][self.heater_names[1]] ['upper limit'])) self.VariableH3SetPoint.setText( str(self.ard_dictionary['heaters'][self.heater_names[2]] ['upper limit'])) # Heater 1 Page self.VariableHeater1Temp.setText( str(((self.ard_dictionary['tempsensors'][self.ard_dictionary[ 'heaters'][self.heater_names[0]]['tsensor_name']]['value'])))) self.VariableHeater1Status.setText( self.ard_dictionary['heaters'][self.heater_names[0]]['status']) self.VariableHeater1Upper.setText( str(self.ard_dictionary['heaters'][self.heater_names[0]] ['upper limit'])) self.VariableHeater1Lower.setText( str(self.ard_dictionary['heaters'][self.heater_names[0]] ['lower limit'])) self.VariableHeater1MaxTemp.setText( str(self.ard_dictionary['heaters'][self.heater_names[0]] ['maxtemp'])) # Heater 2 Page self.VariableHeater2Temp.setText( str(((self.ard_dictionary['tempsensors'][self.ard_dictionary[ 'heaters'][self.heater_names[1]]['tsensor_name']]['value'])))) self.VariableHeater2Status.setText( self.ard_dictionary['heaters'][self.heater_names[1]]['status']) self.VariableHeater2Upper.setText( str(self.ard_dictionary['heaters'][self.heater_names[1]] ['upper limit'])) self.VariableHeater2Lower.setText( str(self.ard_dictionary['heaters'][self.heater_names[1]] ['lower limit'])) self.VariableHeater2MaxTemp.setText( str(self.ard_dictionary['heaters'][self.heater_names[1]] ['maxtemp'])) # Heater 3 Page self.VariableHeater3Temp.setText( str(((self.ard_dictionary['tempsensors'][self.ard_dictionary[ 'heaters'][self.heater_names[2]]['tsensor_name']]['value'])))) self.VariableHeater3Status.setText( self.ard_dictionary['heaters'][self.heater_names[2]]['status']) self.VariableHeater3Upper.setText( str(self.ard_dictionary['heaters'][self.heater_names[2]] ['upper limit'])) self.VariableHeater3Lower.setText( str(self.ard_dictionary['heaters'][self.heater_names[2]] ['lower limit'])) self.VariableHeater3MaxTemp.setText( str(self.ard_dictionary['heaters'][self.heater_names[2]] ['maxtemp'])) def update_labels(self): # Utilize the shared ccbc.ard_dictionary to populate these numbers # ccbc.ard_dictionary['tempsensors'][t_sensor.name]['value'] self.VariableT1.setText( str(self.ard_dictionary['tempsensors'][self.tsensor_names[0]] ['value'])) self.VariableT2.setText( str(self.ard_dictionary['tempsensors'][self.tsensor_names[1]] ['value'])) self.VariableT3.setText( str(self.ard_dictionary['tempsensors'][self.tsensor_names[2]] ['value'])) self.VariableT4.setText( str(self.ard_dictionary['tempsensors'][self.tsensor_names[3]] ['value'])) self.VariableT5.setText( str(self.ard_dictionary['tempsensors'][self.tsensor_names[4]] ['value'])) self.VariableT6.setText( str(self.ard_dictionary['tempsensors'][self.tsensor_names[5]] ['value'])) self.VariableT7.setText( str(self.ard_dictionary['tempsensors'][self.tsensor_names[6]] ['value'])) self.VariableT8.setText( str(self.ard_dictionary['tempsensors'][self.tsensor_names[7]] ['value'])) self.VariableT9.setText( str(self.ard_dictionary['tempsensors'][self.tsensor_names[8]] ['value'])) self.VariablePress1.setText( str(self.ard_dictionary['presssensors'][self.psensor_names[0]] ['pressure'])) self.VariablePress2.setText( str(self.ard_dictionary['presssensors'][self.psensor_names[1]] ['pressure'])) self.VariablePress3.setText( str(self.ard_dictionary['presssensors'][self.psensor_names[2]] ['pressure'])) self.VariablePress4.setText( str(self.ard_dictionary['presssensors'][self.psensor_names[3]] ['pressure'])) self.VariableH1.setText( self.ard_dictionary['heaters'][self.heater_names[0]]['status']) self.VariableH2.setText( self.ard_dictionary['heaters'][self.heater_names[1]]['status']) self.VariableH3.setText( self.ard_dictionary['heaters'][self.heater_names[2]]['status']) self.VariablePump1.setText( self.ard_dictionary['pumps'][self.pump_names[0]]['status']) self.VariablePump2.setText( self.ard_dictionary['pumps'][self.pump_names[1]]['status']) self.VariablePump3.setText( self.ard_dictionary['pumps'][self.pump_names[2]]['status']) self.VariableH1SetPoint.setText( str(self.ard_dictionary['heaters'][self.heater_names[0]] ['upper limit'])) self.VariableH2SetPoint.setText( str(self.ard_dictionary['heaters'][self.heater_names[1]] ['upper limit'])) self.VariableH3SetPoint.setText( str(self.ard_dictionary['heaters'][self.heater_names[2]] ['upper limit'])) # Heater 1 Page self.VariableHeater1Temp.setText( str(((self.ard_dictionary['tempsensors'][self.ard_dictionary[ 'heaters'][self.heater_names[0]]['tsensor_name']]['value'])))) self.VariableHeater1Status.setText( self.ard_dictionary['heaters'][self.heater_names[0]]['status']) self.VariableHeater1Upper.setText( str(self.ard_dictionary['heaters'][self.heater_names[0]] ['upper limit'])) self.VariableHeater1Lower.setText( str(self.ard_dictionary['heaters'][self.heater_names[0]] ['lower limit'])) self.VariableHeater1MaxTemp.setText( str(self.ard_dictionary['heaters'][self.heater_names[0]] ['maxtemp'])) self.LabelHeater1TSensor.setText( 'Controlling Temperature Sensor: {}'.format( self.ard_dictionary['heaters'][ self.heater_names[0]]['tsensor_name'])) # Heater 2 Page self.VariableHeater2Temp.setText( str(((self.ard_dictionary['tempsensors'][self.ard_dictionary[ 'heaters'][self.heater_names[1]]['tsensor_name']]['value'])))) self.VariableHeater2Status.setText( self.ard_dictionary['heaters'][self.heater_names[1]]['status']) self.VariableHeater2Upper.setText( str(self.ard_dictionary['heaters'][self.heater_names[1]] ['upper limit'])) self.VariableHeater2Lower.setText( str(self.ard_dictionary['heaters'][self.heater_names[1]] ['lower limit'])) self.VariableHeater2MaxTemp.setText( str(self.ard_dictionary['heaters'][self.heater_names[1]] ['maxtemp'])) self.LabelHeater2TSensor.setText( 'Controlling Temperature Sensor: {}'.format( self.ard_dictionary['heaters'][ self.heater_names[1]]['tsensor_name'])) # Heater 3 Page self.VariableHeater3Temp.setText( str(((self.ard_dictionary['tempsensors'][self.ard_dictionary[ 'heaters'][self.heater_names[2]]['tsensor_name']]['value'])))) self.VariableHeater3Status.setText( self.ard_dictionary['heaters'][self.heater_names[2]]['status']) self.VariableHeater3Upper.setText( str(self.ard_dictionary['heaters'][self.heater_names[2]] ['upper limit'])) self.VariableHeater3Lower.setText( str(self.ard_dictionary['heaters'][self.heater_names[2]] ['lower limit'])) self.VariableHeater3MaxTemp.setText( str(self.ard_dictionary['heaters'][self.heater_names[2]] ['maxtemp'])) self.LabelHeater3TSensor.setText( 'Controlling Temperature Sensor: {}'.format( self.ard_dictionary['heaters'][ self.heater_names[2]]['tsensor_name'])) # Pump 1 Page self.VariablePump1Volume.setText( str(self.ard_dictionary['pumps'][self.pump_names[0]]['gallons'])) self.LabelPump1Status.setText( str(self.ard_dictionary['pumps'][self.pump_names[0]]['status'])) self.VariablePump1Upper.setText( str(self.ard_dictionary['pumps'][self.pump_names[0]] ['upper limit'])) self.VariablePump1Lower.setText( str(self.ard_dictionary['pumps'][self.pump_names[0]] ['lower limit'])) self.VariablePump1Pressure.setText((str( self.ard_dictionary['presssensors'][self.ard_dictionary['pumps'][ self.pump_names[0]]['psensor_name']]['pressure']))) self.VariablePump1VolSlope.setText( str(self.ard_dictionary['pumps'][self.pump_names[0]] ['psi_to_gal_slope'])) self.VariablePump1VolIntercept.setText( (str(self.ard_dictionary['pumps'][self.pump_names[0]] ['psi_to_gal_intercept']))) self.LabelPump1PSensor.setText( 'Controlling Pressure Sensor: {}'.format( self.ard_dictionary['pumps'][ self.pump_names[0]]['psensor_name'])) # Pump 2 Page self.VariablePump2Volume.setText( str(self.ard_dictionary['pumps'][self.pump_names[1]]['gallons'])) self.LabelPump2Status.setText( str(self.ard_dictionary['pumps'][self.pump_names[1]]['status'])) self.VariablePump2Upper.setText( str(self.ard_dictionary['pumps'][self.pump_names[1]] ['upper limit'])) self.VariablePump2Lower.setText( str(self.ard_dictionary['pumps'][self.pump_names[1]] ['lower limit'])) self.VariablePump2Pressure.setText((str( self.ard_dictionary['presssensors'][self.ard_dictionary['pumps'][ self.pump_names[1]]['psensor_name']]['pressure']))) self.VariablePump2VolSlope.setText( str(self.ard_dictionary['pumps'][self.pump_names[1]] ['psi_to_gal_slope'])) self.VariablePump2VolIntercept.setText( (str(self.ard_dictionary['pumps'][self.pump_names[1]] ['psi_to_gal_intercept']))) self.LabelPump2PSensor.setText( 'Controlling Pressure Sensor: {}'.format( self.ard_dictionary['pumps'][ self.pump_names[1]]['psensor_name'])) # Pump 3 Page self.VariablePump3Volume.setText( str(self.ard_dictionary['pumps'][self.pump_names[2]]['gallons'])) self.LabelPump3Status.setText( str(self.ard_dictionary['pumps'][self.pump_names[2]]['status'])) self.VariablePump3Upper.setText( str(self.ard_dictionary['pumps'][self.pump_names[2]] ['upper limit'])) self.VariablePump3Lower.setText( str(self.ard_dictionary['pumps'][self.pump_names[2]] ['lower limit'])) self.VariablePump3Pressure.setText((str( self.ard_dictionary['presssensors'][self.ard_dictionary['pumps'][ self.pump_names[2]]['psensor_name']]['pressure']))) self.VariablePump3VolSlope.setText( str(self.ard_dictionary['pumps'][self.pump_names[2]] ['psi_to_gal_slope'])) self.VariablePump3VolIntercept.setText( (str(self.ard_dictionary['pumps'][self.pump_names[2]] ['psi_to_gal_intercept']))) self.LabelPump3PSensor.setText( 'Controlling Pressure Sensor: {}'.format( self.ard_dictionary['pumps'][ self.pump_names[2]]['psensor_name'])) def start_brew_time(self): self.BrewingTime.brew_time = 0 self.BrewingTime.active = True self.logger.start() self.logger_timer.timeout.connect(self._update_logger) self.logger_timer.start(30000) def pause_or_resume_brew_time(self): # Brewing is currently going. After pressing button, brewing pauses and button should say resume if self.BrewingTime.active: self.BrewingTime.change_status() self.PausePushButton.setText("Resume") else: # Brewing is paused. After pressing the button, brewing continues and the button should say pause self.BrewingTime.change_status() self.PausePushButton.setText("Pause") def heater1_table_setpoint(self): pass def refresh_dynamic_labels(self): worker = Worker(self.update_labels) self.threadpool.start(worker) def _update_logger(self): self.logger.update() def start_everything(self): self.label_timer.timeout.connect(self.refresh_dynamic_labels) self.label_timer.start(250)
class SelectModelWidget(QWidget): """QTabWidget that holds all of the selectable models and the accompanying ModelDialog for each. """ update_progressbar = pyqtSignal(int, bool) def __init__(self, parent=None): super(SelectModelWidget, self).__init__(parent) self.logger = logging.getLogger(__name__) self.parent = parent self.threadpool = QThreadPool() self.logger.info( f"Multithreading enabled with a maximum of {self.threadpool.maxThreadCount()} threads." ) print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) self.training_data = pd.DataFrame() self.training_predictions = pd.DataFrame() self.selected_version = CONFIG.get('PATHS', 'DefaultModelDirectory') self.comms = Communicate() self.selected_models = {} self.selected_models['sklearn'] = {} self.selected_models['tensorflow'] = {} self.model_checkboxes = [] # * Initialize training parameter dict. # * Has entry for both model base types self.training_params = {} self.training_params['sklearn'] = {} self.training_params['sklearn']['type'] = None self.training_params['sklearn']['value'] = None self.training_params['tensorflow'] = {} # * Init tuning param dict # * Currently only using gridsearch self.tuning_params = {} self.tuning_params['gridsearch'] = { 'n_iter': 20, 'cv': 3, 'n_jobs': -1, 'scoring': ['accuracy'], 'tune_stacker': False } self.sklearn_model_dialogs = [] self.sklearn_model_dialog_btns = [] self.sklearn_training_inputs = [] self.tensorflow_training_inputs = [] self.tensorflow_model_dialogs = [] self.tensorflow_model_dialog_btns = [] self.main_layout = QVBoxLayout() self.upper_hbox = QHBoxLayout() self.version_form = QFormLayout() self.header_hbox = QHBoxLayout() self.header_hbox.addLayout(self.version_form) self.header_hbox.addStretch() self.tune_models_chkbox = QCheckBox("Tune Models") self.header_hbox.addWidget(self.tune_models_chkbox) self.tune_models_chkbox.stateChanged.connect( lambda state: self._enable_tuning_ui(state)) self.main_layout.addLayout(self.header_hbox) self.main_layout.addLayout(self.upper_hbox) self.model_vbox = QVBoxLayout() self.tuning_vbox = QVBoxLayout() self.upper_hbox.addLayout(self.model_vbox) self.upper_hbox.addSpacing(10) self.upper_hbox.addLayout(self.tuning_vbox) self.upper_hbox.addSpacing(200) # * Build sklearn ui components self.sklearn_hbox = QHBoxLayout() self.sklearn_groupbox = QGroupBox("Sklearn") self.sklearn_groupbox.setLayout(self.sklearn_hbox) self.skmodel_groupbox = QGroupBox("Model Selection") self.sklearn_hbox.addWidget(self.skmodel_groupbox) self.sklearn_model_form = QFormLayout() self.sklearn_model_form.setFormAlignment(Qt.AlignTop) self.skmodel_groupbox.setLayout(self.sklearn_model_form) # * Sklearn training and tuning ui components self.sklearn_training_groupbox = QGroupBox("Training") self.sklearn_training_form = QFormLayout() self.sklearn_training_groupbox.setLayout(self.sklearn_training_form) self.sklearn_hbox.addWidget(self.sklearn_training_groupbox) self.model_vbox.addWidget(self.sklearn_groupbox) # * Build Tensorflow ui components self.tensorflow_hbox = QHBoxLayout() self.tensorflow_groupbox = QGroupBox("Tensorflow") self.tensorflow_groupbox.setLayout(self.tensorflow_hbox) self.tensorflow_model_groupbox = QGroupBox("Model Selection") self.tensorflow_hbox.addWidget(self.tensorflow_model_groupbox) self.tensorflow_model_form = QFormLayout() self.tensorflow_model_groupbox.setLayout(self.tensorflow_model_form) self.tensorflow_training_groupbox = QGroupBox("Training") self.tensorflow_training_form = QFormLayout() self.tensorflow_training_groupbox.setLayout( self.tensorflow_training_form) self.tensorflow_hbox.addWidget(self.tensorflow_training_groupbox) # * This is the tensorflow groupbox for models and training params. # self.model_vbox.addWidget(self.tensorflow_groupbox) self.tuning_groupbox = QGroupBox("Tuning") self.tuning_form = QFormLayout() self.tuning_groupbox.setLayout(self.tuning_form) self.tuning_vbox.addWidget(self.tuning_groupbox) self.tuning_groupbox.setEnabled(False) self.model_form_grid = QGridLayout() self.setup_model_selection_ui() self.setup_training_ui() self.setup_tuning_ui() # * QTextEdit box for training/tuning status self.training_logger = QTextEdit() self.training_logger.setReadOnly(True) self.training_logger.setAcceptRichText(True) self.training_logger.insertHtml( "<i>Multithreading with maximum %d threads</i><br>" % self.threadpool.maxThreadCount()) self.training_logger.setMinimumHeight(400) self.main_layout.addWidget(self.training_logger) self.clear_btn_hbox = QHBoxLayout() self.clear_text_btn = QPushButton('Clear') self.clear_text_btn.setMaximumWidth(50) self.clear_text_btn.clicked.connect( lambda: self.training_logger.clear()) self.clear_btn_hbox.addStretch() self.clear_btn_hbox.addWidget(self.clear_text_btn) self.main_layout.addLayout(self.clear_btn_hbox) self.main_layout.addStretch() self.run_btn = QPushButton("&Train Models") self.run_btn.setMinimumWidth(200) self.run_btn.clicked.connect(lambda: self.train_models()) self.run_btn.setEnabled(False) self.stop_btn = QPushButton('Sto&p') self.stop_btn.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.comms.enable_training_btn.connect(self.set_training_btn_state) self.button_hbox = QHBoxLayout() icon = QIcon() icon.addPixmap(QPixmap('icons/Programming-Save-icon.png')) self.save_results_btn = QPushButton() self.save_results_btn.setIcon(icon) self.save_results_btn.setEnabled(False) self.save_results_btn.setToolTip( 'Save model evaluation predictions, agreement ratio, and bamboozled score' ) self.save_results_btn.clicked.connect(lambda: self.save_predictions()) self.button_hbox.addWidget(self.run_btn) self.button_hbox.addWidget(self.stop_btn) self.button_hbox.addStretch() self.button_hbox.addWidget(self.save_results_btn) self.main_layout.addLayout(self.button_hbox) self.setLayout(self.main_layout) # Trigger update to load model parameters self._update_version(self.version_selection.currentData()) def setup_model_selection_ui(self): """ Setup model selection ui. The order of the parameters in ModelDialog matters. model_data must come first! """ self.version_selection_label = QLabel("Select version: ") self.version_selection = QComboBox(objectName='version_select') self.version_selection.setMinimumWidth(100) # Changed default models to a unique directory. This # is where default models will be saved. # self.version_selection.addItem( # 'default', '.\\package\\data\\default_models\\default') available_versions = os.listdir(BASE_VERSION_DIR) for version in available_versions: v_path = os.path.join(BASE_VERSION_DIR, version) if os.path.isdir(v_path): self.version_selection.addItem(version, v_path) self.version_selection.currentIndexChanged.connect( lambda x, y=self.version_selection: self._update_version( y.currentData())) self.version_form.addRow(self.version_selection_label, self.version_selection) # Load base TF-IDF features # and feature selection data try: with open(CONFIG.get('PATHS', 'BaseTfidfDirectory'), 'r') as f: tfidf_data = json.load(f) except IOError as ioe: self.logger.error("Error loading base TFIDF params", exc_info=True) exceptionWarning( 'Error occurred while loading base TFIDF parameters.', repr(ioe)) try: with open(CONFIG.get('PATHS', 'BaseFeatureSeletionDirectory'), 'r') as f: self.fs_params = json.load(f) except IOError as ioe: self.logger.error("Error loading base feature selector params", exc_info=True) exceptionWarning( 'Error occurred while loading base feature selector parameters.', repr(ioe)) # Dynamically generate ModelDialogs for each model in the base model directory. # Only considers *.json file extension. try: row = 0 for filename in os.listdir( CONFIG.get('PATHS', 'BaseModelDirectory')): if filename.endswith('.json'): with open( os.path.join( CONFIG.get('PATHS', 'BaseModelDirectory'), filename), 'r') as f: model_data = json.load(f) model = model_data['model_class'] model_base = model_data['model_base'] model_module = model_data['model_module'] #! The order of the arguments matters! model_data must come first. if model_base == 'tensorflow': continue # model_dialog = SkModelDialog(self, model_data) if model_module == 'tpot': model_dialog = TPOTModelDialog( self, model_data, tfidf_data) else: model_dialog = SkModelDialog( self, model_data, tfidf_data, self.fs_params) self.comms.version_change.connect( model_dialog.update_version) # Initialize model as unselected self.selected_models[model_base][model] = False btn = QPushButton(model, objectName=model + '_btn') # Partial allows the connection of dynamically generated QObjects btn.clicked.connect( partial(self.open_dialog, model_dialog)) chkbox = QCheckBox(objectName=model) chkbox.stateChanged.connect( lambda state, x=model, y=model_base: self. _update_selected_models(x, y, state)) if model_base == 'tensorflow': self.tensorflow_model_form.addRow(chkbox, btn) self.tensorflow_model_dialogs.append(model_dialog) self.tensorflow_model_dialog_btns.append(btn) else: self.sklearn_model_form.addRow(chkbox, btn) self.sklearn_model_dialogs.append(model_dialog) self.sklearn_model_dialog_btns.append(btn) self.model_checkboxes.append(chkbox) row += 1 except OSError as ose: self.logger.error("OSError opening model config files", exc_info=True) exceptionWarning('OSError opening model config files!', ose) tb = traceback.format_exc() print(tb) except Exception as e: self.logger.error("Error opening model config files", exc_info=True) exceptionWarning('Error occured.', e) tb = traceback.format_exc() print(tb) def setup_training_ui(self): """ Build ui components for training parameters for both Sklearn and Tensorflow """ # Sklearn training first self.cv_n_fold_input = QSpinBox(objectName='n_folds') self.cv_n_fold_input.setRange(2, 10) self.cv_n_fold_input.setValue(5) self.cv_n_fold_input.setEnabled(False) self.cv_n_fold_input.valueChanged.connect( lambda state, x=self.cv_n_fold_input: self.update_training_params( 'sklearn', 'cv', x.value())) self.cv_radio_btn = QRadioButton("Cross-validation", objectName='cv') self.cv_radio_btn.toggled.connect( lambda state, x=self.cv_n_fold_input: self. _update_sklearn_training_type('cv', x.value())) self.sklearn_training_form.addRow(self.cv_radio_btn, self.cv_n_fold_input) self.sk_validation_radio_btn = QRadioButton("Validation set") self.sk_validation_percent_input = QDoubleSpinBox( objectName='test_split') self.sk_validation_percent_input.setRange(0.05, 1) self.sk_validation_percent_input.setSingleStep(0.1) self.sk_validation_percent_input.setEnabled(False) self.sk_validation_percent_input.valueChanged.connect( lambda state, x=self.sk_validation_percent_input: self. update_training_params('sklearn', 'validation', x.value())) self.sk_validation_radio_btn.toggled.connect( lambda state, x=self.sk_validation_percent_input: self. _update_sklearn_training_type('validation', x.value())) # NOTE: Removing validation split option from evaluation. It seems less than useful and # requires time that could be spent elsewhere as we near the end of our time together. # self.sklearn_training_form.addRow(self.sk_validation_radio_btn, self.sk_validation_percent_input) self.no_eval_btn = QRadioButton("No evaluation set", objectName='no_eval') self.no_eval_btn.toggled.connect( lambda: self._update_sklearn_training_type(None, None)) self.sklearn_training_form.addRow(self.no_eval_btn) # TENSORFLOW TRAINING UI. Removed as of 10/04/19 # Toggle to set params on load self.cv_radio_btn.toggle() # * Select stacker # self.stacker_groupbox = QGroupBox('Stacking algorithm') # self.stacker_vbox = QVBoxLayout() # self.train_stacker def setup_tuning_ui(self): self.tuning_n_iter_label = QLabel("Number of iterations:") self.tuning_n_iter_input = QSpinBox(objectName='n_iter') self.tuning_n_iter_input.setRange(2, 1000) self.tuning_n_iter_input.setSingleStep(1) self.tuning_n_iter_input.setValue(10) self.tuning_n_iter_input.valueChanged.connect( lambda state, x=self.tuning_n_iter_input: self. update_tuning_params('gridsearch', 'n_iter', x.value())) self.tuning_form.addRow(self.tuning_n_iter_label, self.tuning_n_iter_input) self.tuning_cv_label = QLabel("CV folds:") self.tuning_cv_input = QSpinBox(objectName='cv') self.tuning_cv_input.setRange(2, 10) self.tuning_cv_input.setValue(3) self.tuning_cv_input.valueChanged.connect( lambda state, x=self.tuning_cv_input: self.update_tuning_params( 'gridsearch', 'cv', x.value())) self.tuning_form.addRow(self.tuning_cv_label, self.tuning_cv_input) self.tuning_n_jobs_label = QLabel("Number of parallel jobs:") self.tuning_n_jobs_input = QSpinBox(objectName='n_jobs') self.tuning_n_jobs_input.setRange(-1, 4) self.tuning_n_jobs_input.setValue(-1) self.tuning_n_jobs_input.valueChanged.connect( lambda state, x=self.tuning_n_jobs_input: self. update_tuning_params('gridsearch', 'n_jobs', x.value())) self.tuning_form.addRow(self.tuning_n_jobs_label, self.tuning_n_jobs_input) self.scoring_metric_groupbox = QGroupBox('Scoring metrics') self.scoring_metric_vbox = QVBoxLayout() # * The following code is for metric radio buttons. Left in for posterity # self.acc_checkbox = QRadioButton('Accuracy') # self.acc_checkbox.setChecked(True) # self.acc_checkbox.toggled.connect( # lambda state, x=self.acc_checkbox: # self.update_tuning_params('gridsearch', 'scoring', 'accuracy') # ) # self.scoring_metric_vbox.addWidget(self.acc_checkbox) # self.f1_weighted_checkbox = QRadioButton('F1 weighted') # self.f1_weighted_checkbox.setChecked(False) # self.f1_weighted_checkbox.toggled.connect( # lambda state, x=self.f1_weighted_checkbox: # self.update_tuning_params('gridsearch', 'scoring', 'f1_weighted') # ) # self.scoring_metric_vbox.addWidget(self.f1_weighted_checkbox) # self.prec_weighted_checkbox = QRadioButton('Precision weighted') # self.prec_weighted_checkbox.setChecked(False) # self.prec_weighted_checkbox.toggled.connect( # lambda state, x=self.prec_weighted_checkbox: # self.update_tuning_params( # 'gridsearch', 'scoring', 'precision_weighted') # ) # self.scoring_metric_vbox.addWidget(self.prec_weighted_checkbox) self.acc_checkbox = QCheckBox('Accuracy') self.acc_checkbox.setChecked(True) self.acc_checkbox.stateChanged.connect( lambda state, x=self.acc_checkbox: self.update_tuning_params( 'gridsearch', 'accuracy', state, True)) self.scoring_metric_vbox.addWidget(self.acc_checkbox) self.f1_weighted_checkbox = QCheckBox('F1 weighted') self.f1_weighted_checkbox.setChecked(False) self.f1_weighted_checkbox.stateChanged.connect( lambda state, x=self.f1_weighted_checkbox: self. update_tuning_params('gridsearch', 'f1_weighted', state, True)) self.scoring_metric_vbox.addWidget(self.f1_weighted_checkbox) self.prec_weighted_checkbox = QCheckBox('Precision weighted') self.prec_weighted_checkbox.setChecked(False) self.prec_weighted_checkbox.stateChanged.connect( lambda state, x=self. prec_weighted_checkbox: self.update_tuning_params( 'gridsearch', 'precision_weighted', state, True)) self.scoring_metric_vbox.addWidget(self.prec_weighted_checkbox) self.scoring_metric_groupbox.setLayout(self.scoring_metric_vbox) self.tune_stacker_checkbox = QCheckBox('Tune Stacking Algorithm') self.tune_stacker_checkbox.setChecked(False) self.tune_stacker_checkbox.stateChanged.connect( lambda state, x=self.tune_stacker_checkbox: self. update_tuning_params('gridsearch', 'tune_stacker', state)) self.tuning_form.addRow(self.scoring_metric_groupbox) self.tuning_form.addRow(self.tune_stacker_checkbox) def open_dialog(self, dialog): """ Opens the passed ModelDialog via the save_params function, allowing the user to specify hyperparameters for each available version field. # Arguments dialog: ModelDialog, Specified model dialog. """ dialog.save_params() @pyqtSlot(str) def add_new_version(self, v_dir): """ pyqtSlot to receive new version created pyqtSignal. # Arguments v_dir: string, directory of newly created version. """ version = v_dir.split('\\')[-1] self.version_selection.addItem(version, v_dir) self.version_selection.model().sort(0) @pyqtSlot(pd.DataFrame) def load_data(self, data): """ pyqtSlot to receive pandas DataFrame after DataLoader has completed it's work # Arguments data: pandas.DataFrame, training data """ self.training_data = data self.comms.enable_training_btn.emit(True) @pyqtSlot(Qt.CheckState) def set_training_btn_state(self): """ Sets the run button enabled state. Checks that there are models selected in sklearn or tensorflow """ if (not self.training_data.empty and (1 in self.selected_models['sklearn'].values() or 1 in self.selected_models['tensorflow'].values())): self.run_btn.setEnabled(True) else: self.run_btn.setEnabled(False) @pyqtSlot(str, bool) def model_exists(self, model_name, truth): """ Adds styling to button if a trained model exists for the model in the selected version # Arguments model_name: string, name of the model designated by the button. truth: bool, true if there exists any trained model of type model_name in the current version. """ btn = self.findChild(QPushButton, model_name + '_btn') if btn: text = btn.text() if text.endswith("*"): text = text[:-2] if truth: btn.setText(text + " *") else: btn.setText(text) else: return def train_models(self): try: tune_models = self.tune_models_chkbox.isChecked() self.model_trainer = ModelTrainer( selected_models=self.selected_models, version_directory=self.selected_version, training_eval_params=self.training_params, training_data=self.training_data, tune_models=tune_models, tuning_params=self.tuning_params) self.model_trainer.signals.update_training_logger.connect( self.update_training_logger) self.update_progressbar.emit(1, True) self.model_trainer.signals.training_complete.connect( self.training_complete) self.comms.stop_training.connect(self.model_trainer.stop_thread) self.run_btn.setEnabled(False) self.stop_btn.clicked.connect(lambda: self._abort_training()) self.training_predictions = pd.DataFrame() self.threadpool.start(self.model_trainer) except Exception as e: self.logger.error("SelectModelWidget.train_models", exc_info=True) exceptionWarning('Exception occured when training models.', e) tb = traceback.format_exc() print(tb) @pyqtSlot(str, bool, bool) def update_training_logger(self, msg, include_time=True, use_html=True): ''' Slot that receives signals updating the terminal. # Arguments msg: string, Message to display in terminal include_time: bool, prepend time to message use_html: bool, insert text as html ''' if (include_time): current_time = time.localtime() outbound = f"{time.strftime('%Y-%m-%d %H:%M:%S', current_time)} - {msg}<br>" else: outbound = f"{msg}<br>" if (use_html): self.training_logger.insertHtml(outbound) self.training_logger.moveCursor(QTextCursor.End) else: self.training_logger.insertPlainText(msg) @pyqtSlot(pd.DataFrame) def training_complete(self, prediction_df=None): """ Resets progressbar, unchecks 'Train models', and emits signal to refresh the parameter values in each ModelDialog # Arguments val: int or float, value used to set progressbar pulse: bool, used to toggle progressbar pulse """ self.update_progressbar.emit(0, False) self.run_btn.setEnabled(True) self.run_btn.setText("Train models") self.tune_models_chkbox.setChecked(False) self.save_results_btn.setEnabled(True) if (prediction_df is not None and not prediction_df.empty): self.training_predictions = prediction_df # Emitting a version change here reloads all parameters. i.e. we update the # parameters displayed in the dialog. self.comms.version_change.emit(self.selected_version) def save_predictions(self): ''' Save predictions to user specified file. Opens a QFileDialog to choose save directory. ''' try: if self.training_predictions.empty: exceptionWarning('No predictions to save') return file_name, filter = QFileDialog.getSaveFileName( self, 'Save to CSV', os.getenv('HOME'), 'CSV(*.csv)') if file_name: self.training_predictions.to_csv(file_name, index_label='testnum', quoting=1, encoding='utf-8') self.comms.update_statusbar.emit( "Predictions saved successfully.") except PermissionError as pe: self.logger.warning("SelectModelWidget.save_predictions", exc_info=True) exceptionWarning( f'Permission denied while attempting to save {file_name}') except Exception as e: self.logger.error("SelectModelWidget.save_predictions", exc_info=True) exceptionWarning( "Exception occured. SelectModelWidget.save_predictions.", exception=e) tb = traceback.format_exc() print(tb) def _abort_training(self): ''' Notifies the ModelTrainer to abort training. ! Note: training ends only after the current training or tuning event ends. ''' self.comms.stop_training.emit() def _update_version(self, directory): """ Parses selected version directory and emits pyqtSignal to update each ModelDialog # Arguments directory: string, directory selected by user. """ self.selected_version = directory # Emit pyqtSignal self.comms.version_change.emit(directory) def _update_selected_models(self, model, model_base, state): """ Update the models selected by the user. This function is connected to the checkboxes associated with each model. # Arguments: model: string, name of the selected model state: bool, the truth of the selection. True->selected, False->unselected """ truth = False if state == Qt.Checked: truth = True self.selected_models[model_base][model] = truth self.comms.enable_training_btn.emit(truth) def _enable_tuning_ui(self, state): """ Helper function to enable/disable the tuning parameter UI if selected by the user. # Arguments: state: bool, the state of tuning. False->no tuning, True->tune models """ self.run_btn.setText("Tune Models" if state else "Train Models") self.tuning_groupbox.setEnabled(state) def update_tuning_params(self, model_base, param, value, scorer=False): ''' Updates the tuning parameters passed to ModelTrainer. # Arguments model_base: string, Signifies which type of tuning parameter to update. Currently, only used by RandomizedSearchCV (sklearn) param: string, parameter name value: [int, float, string], parameter value ''' if model_base is None or param is None: return try: if scorer: if value: self.tuning_params[model_base]['scoring'].append(param) else: if param in self.tuning_params[model_base]['scoring']: self.tuning_params[model_base]['scoring'].remove(param) else: self.tuning_params[model_base][param] = value except KeyError as ke: self.tuning_params[model_base][param] = {} self.tuning_params[model_base][param] = value except Exception as e: self.logger.error("SelectModelWidget.update_tuning_params", exc_info=True) exceptionWarning('Exception occured when training models.', e) tb = traceback.format_exc() print(tb) print(self.tuning_params) def update_training_params(self, model_base, param, value): """ Update the various training parameters with values supplied by the user. Needs work as the sklearn training parameters are mutually exclusive. # Arguments model_base: string, model base for specified training params param: string, parameter name value: string, int, or double, value of specified parameter """ if model_base is None or param is None: return try: # FIXME: This is super hackish and brittle. Can it be done more eloquently? if model_base == 'sklearn': self._update_sklearn_training_type(param, value) else: self.training_params[model_base][param] = value except KeyError as ke: print(ke) def _update_sklearn_training_type(self, eval_type, value): """ SKlearn model tuning is mutually exclusive. This helper function Enables/disables the appropriate field and updates the appropriate parameters of self.training_params Currently, for Sklearn models, only cross-validation (cv) or a holdout set (validation) or None are model evaluation options. # Arguments eval_type: string, The type of model evaluation specified by the user. value: int or double, value corresponding to selected type """ truth = False if eval_type == 'cv': self.cv_n_fold_input.setEnabled(not truth) self.sk_validation_percent_input.setEnabled(truth) elif eval_type == 'validation': self.cv_n_fold_input.setEnabled(truth) self.sk_validation_percent_input.setEnabled(not truth) elif eval_type == None: self.cv_n_fold_input.setEnabled(False) self.sk_validation_percent_input.setEnabled(False) else: raise ValueError("eval_type %s is invalid" % (eval_type)) self.training_params['sklearn']['type'] = eval_type self.training_params['sklearn']['value'] = value
class AppWindow(QMainWindow): def __init__(self): super(AppWindow,self).__init__() self.ui=Ui_MainWindow() self.ui.setupUi(self) yaml=os.path.join(os.getcwd(),'config.yml') print(yaml) self.cfg=util.import_yaml(yaml) self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) util.Qlogging(self.ui.textBrowser, 'The config File is loaded \n',"g") cfg_str=pprint.pformat(self.cfg) # print(cfg_str) util.Qlogging(self.ui.textBrowser,cfg_str,'b') self.Ui_config_() def Ui_config_(self): self.ui.btn_train.clicked.connect(self.btn_train) self.ui.btn_designNet.clicked.connect(self.btn_design_model) self.ui.btn_model_eval.clicked.connect(self.on_btn_eval) self.ui.cmbox_data_dir.addItems(self.cfg['data_dir']) self.ui.cmbox_model_select.addItems(self.cfg['model_names']) self.ui.in_num_classes.setText(str(self.cfg['num_classes'])) self.ui.in_num_classes.setEnabled(False) self.ui.in_batch_size.setText(str(self.cfg['batch_size'])) self.ui.in_epoches.setText(str(self.cfg['num_epochs'])) def btn_design_model(self): model_cfg=util.import_yaml('.\models\model_001.yml') net=TR.AliNet(model_cfg['model_layers']) util.Qlogging(str(net.modules()),'r') def update_cfg(self): self.new_cfg={} self.new_cfg.update({'model_name': self.ui.cmbox_model_select.currentText()}) self.new_cfg.update({'data_dir': self.ui.cmbox_data_dir.currentText()}) self.new_cfg.update({'num_classes': self.ui.in_num_classes.text()}) self.new_cfg.update({'batch_size': self.ui.in_batch_size.text()}) self.new_cfg.update({'num_epochs': self.ui.in_epoches.text()}) self.new_cfg.update({'feature_extract': self.ui.in_rdbtn_Feature.isChecked()}) self.new_cfg.update({'use_pretrained': self.ui.in_chbox_pretrained.isChecked()}) print(self.new_cfg) def btn_train(self): self.update_cfg() cfg=self.new_cfg model_ft, input_size = TR.initialize_model(cfg['model_name'], int(cfg['num_classes']), cfg['feature_extract'], use_pretrained=cfg['use_pretrained']) util.Qlogging(self.ui.textBrowser, 'The Model is loaded\n', "r") data_transforms = TR.Data_Augmrntation_Normalization(input_size) print("Initializing Datasets and Dataloaders...") # # # Create training and validation datasets image_datasets = {x: datasets.ImageFolder(os.path.join(cfg['data_dir'], x), data_transforms[x]) for x in ['train', 'val']} # # Create training and validation dataloaders self.dataloaders_dict = { x: torch.utils.data.DataLoader(image_datasets[x], batch_size=int(cfg['batch_size']), shuffle=True, num_workers=4) for x in ['train', 'val']} # # # Detect if we have a GPU available device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # KK = TR.AliNet(2) # model_ft= nn.Sequential(model_ft,nn.Linear(2,200), # nn.Linear(200,2),KK) print(model_ft) # Send the model to GPU model_ft = model_ft.to(device) params_to_update = model_ft.parameters() print("Params to learn:") if cfg['feature_extract']: params_to_update = [] for name, param in model_ft.named_parameters(): if param.requires_grad == True: params_to_update.append(param) print("\t", name) else: for name, param in model_ft.named_parameters(): if param.requires_grad == True: print("\t", name) # Observe that all parameters are being optimized optimizer_ft = optim.SGD(params_to_update, lr=0.001, momentum=0.9) # Setup the loss fxn criterion = nn.CrossEntropyLoss() # Train and evaluate # model_ft, hist = TR.train_model(model_ft, dataloaders_dict, criterion, optimizer_ft, num_epochs=int(cfg['num_epochs']), # is_inception=(cfg['model_name'] == "inception")) worker_train= Worker(TR.train_model,model_ft,self.dataloaders_dict,criterion,optimizer_ft, num_epochs=int(cfg['num_epochs']),is_inception=(cfg['model_name'] == "inception")) worker_train.signals.result.connect(self.on_Thread_resulat) self.threadpool.start(worker_train) def on_Thread_resulat(self,model): print("Thread has done.") self.model=model[0] print(model[0]) def on_btn_eval(self): device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") self.model.eval() inputs , labels= next(iter(self.dataloaders_dict['val'])) print('Actual labels:\n',labels) inputs = inputs.to(device) labels = labels.to(device) pred= self.model(inputs) # pred.data.cpu().numpy() print(pred.shape) print('Predicted labels: \n', pred.data.cpu().numpy())
class MADByTE_Main(QMainWindow): def __init__(self): __version__ = '1.3.0' super(MADByTE_Main, self).__init__() uic.loadUi(os.path.join(BASE, 'static','MADByTE_GUI.ui'),self) # setup threadpool for processing self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) ### setup window details self.Version_Label.setText(__version__) self.setWindowIcon(QIcon(LOGO_PATH)) self.Hppm_Input.setText('0.05') self.Cppm_Input.setText('0.40') self.Hppm_Input_2.setText('0.03') self.Cppm_Input_2.setText('0.40') self.Consensus_Error_Input.setText('0.03') self.Similarity_Ratio_Input.setText('0.50') self.Overlap_Score_lineEdit.setText('0.30') Banner_Pixmap = QPixmap(Banner_Path) Logo_Pixmap = QPixmap(LOGO_PATH) self.Logo_Space.setPixmap(Logo_Pixmap.scaled(121,101,Qt.KeepAspectRatio,Qt.SmoothTransformation)) self.Banner_Space.setPixmap(Banner_Pixmap.scaled(681,121,Qt.KeepAspectRatio,Qt.SmoothTransformation)) self.Extract_Node_Size_Box.setText('15') self.Feature_Node_Size_Box.setText('10') self.Spin_Max_Size.setText('20') self.Dereplicate_Button.setEnabled(False) self.SMART_Export_Button.setEnabled(False) self.Export_Derep_File_Button.setEnabled(False) self.Plot_Proton_Button.setEnabled(False) self.VIEWHSQC_2.setEnabled(False) self.VIEWTOCSY_2.setEnabled(False) self.MADByTE_Button_2.setEnabled(False) self.TOCSY_Net_Button_2.setEnabled(False) self.Multiplet_Merger_Checkbox.setChecked(True) for NMR_Datatype in ['Bruker','Mestrenova','CSV']:#,'JOEL','Agilent','NMRPipe','Peak Lists]: self.NMR_Data_Type_Combo_Box.addItem(NMR_Datatype) self.Network_Filename_Input.setText("MADByTE") ### Bioactivity Layering values ### self.High_Activity_Box.setText('1.0') self.Mild_Activity_Box.setText('0.66') self.Low_Activity_Box.setText('0.33') self.Generate_Bioactivity_Plot_Button.clicked.connect(self.Bioactivity_Plotting_Fx) self.Select_Bioactivity_File_Button.clicked.connect(self.Select_Bioactivity_File_Fx) self.Select_Network_To_Layer_Button.clicked.connect(self.Select_Network_To_Layer_Fx) self.Bioactivity_Network_Name_Box.setText("Bioactivity_Network") self.SMART_Export_Button.clicked.connect(self.SMART_Export_Fx) self.Export_Derep_File_Button.clicked.connect(self.Export_Derep_File) ###Connect Buttons to Fx ### self.NMR_Data_Directory_Select.clicked.connect(self.Select_NMR_Data_Directory) self.Select_Project_Directory.clicked.connect(self.Select_Project_Directory_Fx) self.RemoveSampleFromListButton.clicked.connect(self.Remove_From_Sample_List) self.MADByTE_Button_2.clicked.connect(self.prompt_MADByTE) self.ViewNetwork.clicked.connect(self.ViewNetwork_launch) self.TOCSY_Net_Button_2.clicked.connect(self.MADByTE_Networking_Launch) self.actionDocumentation.triggered.connect(self.Launch_Documentation) self.actionExamples.triggered.connect(self.Launch_Example) self.Plot_Proton_Button.clicked.connect(self.View_1D_Data) self.VIEWHSQC_2.clicked.connect(self.View_HSQC_Data) self.VIEWTOCSY_2.clicked.connect(self.View_TOCSY_Data) self.Update_Log.clicked.connect(self.Update_Log_Fx) self.Dereplicate_Button.clicked.connect(self.Dereplication_Report) self.Extract_Node_Color_Button.clicked.connect(self.Select_Extract_Color) self.Spin_Node_Color_Button.clicked.connect(self.Select_Spin_Color) self.Load_Parameters_Button.clicked.connect(self.Load_Parameters) self.Export_Derep_Button.clicked.connect(self.Export_Derep_Results) self.Load_Derep_Library_Button.clicked.connect(self.Select_Dereplication_Library) ###Create the Plotting Window for the NMR Data#### Plotted = self.plot global vLine global hLine vLine = InfiniteLine(angle=90, movable=False) hLine = InfiniteLine(angle=0, movable=False) Plotted.enableAutoRange(True) Plotted.addItem(vLine, ignoreBounds=True) Plotted.addItem(hLine, ignoreBounds=True) Plotted.setMouseTracking(True) Plotted.showGrid(x=True,y=True,alpha=0.75) Plotted.scene().sigMouseMoved.connect(self.mouseMoved) self.Solvent_comboBox.addItems(['DMSO-D6','MeOD','CDCl3','D2O']) ###Default Values for colors for networking### global Spin_color Spin_color = "#009999" global Extract_color Extract_color = "#ff3333" # Load sample networks if there... if not os.path.isdir(DEFAULT_NETWORKS): os.mkdir(DEFAULT_NETWORKS) for Network in os.listdir(DEFAULT_NETWORKS): if 'html' in Network: self.Drop_Down_List_Networks.addItem(Network) ###Functions#### def Launch_Documentation(self): try: subprocess.call(["open", os.path.join('Documentation','MADByTE_User_Manual.pdf')]) except: subprocess.Popen([os.path.join('Documentation','MADByTE_User_Manual.pdf')],shell=True) def Launch_Example(self): try: subprocess.call(["open", os.path.join('Documentation','MADByTE_Quick_Start_Tutorial.pdf')]) except: subprocess.Popen([os.path.join('Documentation','MADByTE_Quick_Start_Tutorial.pdf')],shell=True) def Load_Existing_Networks(self,MasterOutput): try: for Network in os.listdir(os.path.join(MasterOutput)): if 'html' in Network: self.Drop_Down_List_Networks.addItem(Network) except: pass for Network in os.listdir(DEFAULT_NETWORKS): if 'html' in Network: self.Drop_Down_List_Networks.addItem(Network) def Load_Parameters(self): ID = 'temp' global Entity Entity = "Extract" global Hppm_Error Hppm_Error = float(self.Hppm_Input.text()) global Cppm_Error Cppm_Error = float(self.Cppm_Input.text()) global Tocsy_Error Tocsy_Error = float(self.Consensus_Error_Input.text()) if self.Multiplet_Merger_Checkbox.isChecked()== True: Multiplet_Merger = True elif self.Multiplet_Merger_Checkbox.isChecked() == False: Multiplet_Merger = False Similarity_Cutoff = float(self.Similarity_Ratio_Input.text()) try: MasterOutput DataDirectory self.MADByTE_Button_2.setEnabled(True) except: try: DataDirectory except: PopUP('Please Select NMR Data Directory','Please select an NMR data directory before proceeding.','Error') try: MasterOutput except: PopUP('Please Select Project Directory','Please select a project directory before proceeding.','Error') if MasterOutput == DataDirectory: PopUP('Please Differentiate Directories','The NMR data directory is where the processed NMR datafiles are, and the project directory is the MADByTE output location. They must be different.','Error') else: PopUP("Parameters Loaded","MADByTE parameters Loaded.","Info") def Select_Extract_Color(self): global Extract_color Extract_color = QColorDialog.getColor() Extract_color = Extract_color.name() self.Extract_Node_Color_Button.setStyleSheet(str("background-color:"+Extract_color+';')) return Extract_color def Select_Spin_Color(self): global Spin_color Spin_color = QColorDialog.getColor() Spin_color = Spin_color.name() self.Spin_Node_Color_Button.setStyleSheet(str("background-color:"+Spin_color+';')) return Spin_color def Select_NMR_Data_Directory(self): Directory_Location = QFileDialog.getExistingDirectory() global DataDirectory DataDirectory = Directory_Location self.BatchSamplesList.clear() for item in os.listdir(DataDirectory): self.BatchSamplesList.addItem(item) for NMR_Dataset in os.listdir(DataDirectory): self.NMR_Data_View_Selector.addItem(NMR_Dataset) self.Plot_Proton_Button.setEnabled(True) self.Ready_Check() return DataDirectory #Raw Data Directory (analogous to input_dir) def Select_Project_Directory_Fx(self): Directory_Location = QFileDialog.getExistingDirectory(self) if Directory_Location == '': PopUP('Please Select A Project Directory', 'Please select a directory to store the project results in. It is recommended to create a new project directory for each experiment processing or batch of samples.','Error') return global MasterOutput MasterOutput = os.path.join(Directory_Location) for Processed_Dataset in os.listdir(MasterOutput): self.Dereplication_Report_Sample_Select.addItem(Processed_Dataset) if len(os.listdir(MasterOutput))>0: self.Dereplicate_Button.setEnabled(True) self.SMART_Export_Button.setEnabled(True) self.Export_Derep_File_Button.setEnabled(True) for Network in os.listdir(MasterOutput): if 'html' in Network: self.Drop_Down_List_Networks.addItem(Network) self.VIEWHSQC_2.setEnabled(True) self.VIEWTOCSY_2.setEnabled(True) self.Ready_Check() if 'correlation_matrix.json' in os.listdir(MasterOutput): self.TOCSY_Net_Button_2.setEnabled(True) return MasterOutput #Output Directory def Remove_From_Sample_List(self): Item_List = self.BatchSamplesList.selectedItems() if not Item_List: return for item in Item_List: self.BatchSamplesList.takeItem(self.BatchSamplesList.row(item)) #removes selected sample from list def openFileNameDialog(self): try: fileName,_ = QFileDialog.getOpenFileName(self) return fileName except: PopUP('Select Directory',"Please select a directory.",'Error') def MADByTE_Networking_Launch(self): self.MADByTE_Networking(Spin_color,Extract_color) def MADByTE_Networking(self,Spin_color,Extract_color): # Generates Network - allows for regen of network without reprocessing of files (updates size/colors) # Relevant Values: Colors and Sizes self.Drop_Down_List_Networks.clear() Extract_Node_Size = int(self.Extract_Node_Size_Box.text()) Feature_Node_Size = int(self.Feature_Node_Size_Box.text()) Filename = self.Network_Filename_Input.text() or "MADByTE" # Default if nothing entered Similarity_Cutoff = float(self.Similarity_Ratio_Input.text()) Max_Spin_Size = int(self.Spin_Max_Size.text()) colors = {'spin':Spin_color,'extract':Extract_color,'standard':"#0ffbff"} try: MADByTE.generate_network( MasterOutput, Similarity_Cutoff, Filename, Cppm_Error, Hppm_Error, colors, Extract_Node_Size, Feature_Node_Size, Max_Spin_Size ) self.Load_Existing_Networks(MasterOutput) PopUP("Networking Completed","MADByTE networking completed. Please select the network from the drop down list to view it.",'Info') self.Update_Log_Fx() except: try: Cppm_Error except: PopUP('Load Parameters Before Proceeding','Please load the MADByTE parameters before generating a network.','Error') PopUP('Networking Error','Network constructin could not be completed due to an error.','Error') ################################################################# ## The Dereplication Report was made to do dereplication through HSQC pattern matching ## ## The HSQC matching is only done when one of the spin systems has been found in the sample ## def Dereplication_Report(self): print('Comparing sample against the dereplication library... ') def point_comparison(observed_value, expectedVal, tolerance): observed_value = float(observed_value) expectedVal = float(expectedVal) if (expectedVal - tolerance < observed_value) & (expectedVal + tolerance > observed_value): return True return False def HSQC_Scoring(Database_Sample_ID,Sample_Analyzed,MasterOutput,ID,result_dict): if Database_Sample_ID not in Sample_Analyzed: Sample_Dataset = pd.read_json(os.path.join(MasterOutput,ID,ID+'_HSQC_Preprocessed.json')).drop(["Intensity"],axis=1) Database_Sample = pd.read_json(os.path.join(Dereplication_Database,Database_Sample_ID,"DDF_"+Database_Sample_ID+'_HSQC.json')) Number_Of_Resonances_Sample = len(Sample_Dataset) Number_Of_Resonances_Database_Item = len(Database_Sample) Match_Counter = 0 for i in range(Number_Of_Resonances_Database_Item): Database_Proton = Database_Sample.iloc[i-1].H_PPM Database_Carbon = Database_Sample.iloc[i-1].C_PPM for i in range(Number_Of_Resonances_Sample): Sample_Proton = Sample_Dataset.iloc[i-1].H_PPM Sample_Carbon = Sample_Dataset.iloc[i-1].C_PPM if point_comparison(Database_Proton,Sample_Proton,Hppm_Error)==True: if point_comparison(Database_Carbon,Sample_Carbon,Cppm_Error)==True: Match_Counter+=1 Compound_Match_Ratio = str(Match_Counter)+'/'+str(Number_Of_Resonances_Database_Item) if Match_Counter >= Number_Of_Resonances_Database_Item: Compound_Match_Ratio = '1' Sample_Analyzed.append(Database_Sample_ID) result_dict[Database_Sample_ID]=Compound_Match_Ratio return result_dict ID = self.Dereplication_Report_Sample_Select.currentText() Hppm_Error = float(self.Hppm_Input_2.text()) Cppm_Error = float(self.Cppm_Input_2.text()) list_of_Database_compounds = os.listdir(os.path.join(Dereplication_Database)) self.Dereplication_Report_Table.setColumnCount(2) self.Dereplication_Report_Table.setRowCount(len(list_of_Database_compounds)+1) self.Dereplication_Report_Table.setHorizontalHeaderLabels(["Compound","Matching Ratio"]) Spin_System_Confirmed = False Sample_Analyzed = list() Not_Detected = list() result_dict=dict() if self.Require_Spin_System_checkBox.isChecked()==True: with open(os.path.join(os.path.join(MasterOutput,ID,ID+'_spin_systems.json'))) as f: Sample_Spin_Systems = json.load(f) df_sample = pd.DataFrame([{"ID": k, "H_ppm": x[0], "C_ppm": x[1]} for k,v in Sample_Spin_Systems.items() for x in v]) df_DDFs = utils.load_spin_systems(os.path.join(Dereplication_Database)) df = pd.concat([df_sample,df_DDFs]) idxs = df_sample["ID"].unique() idys = df_DDFs["ID"].unique() for idx,idy in itertools.product(idxs,idys): ratio = utils.ratio_two_systems(idx, idy, df, Hppm_Error, Cppm_Error) if 'HND_' in idy: Database_Sample_ID = str("_".join(idy.split("_")[1:-1])) elif 'HND_' not in idy: Database_Sample_ID= str("_".join(idy.split("_")[:-1])) if ratio>float(self.Overlap_Score_lineEdit.text()): HSQC_Scoring(Database_Sample_ID,Sample_Analyzed,MasterOutput,ID,result_dict) if ratio <=float(self.Overlap_Score_lineEdit.text()): Not_Detected.append(Database_Sample_ID) if Database_Sample_ID not in Sample_Analyzed: result_dict[Database_Sample_ID]='Not Detected' elif self.Require_Spin_System_checkBox.isChecked()==False: for Database_Sample_ID in os.listdir(Dereplication_Database): result_dict = HSQC_Scoring(Database_Sample_ID,Sample_Analyzed,MasterOutput,ID,result_dict) compound_number=0 for Database_Value in result_dict: self.Dereplication_Report_Table.setItem(compound_number,0, QTableWidgetItem(str(Database_Value))) self.Dereplication_Report_Table.setItem(compound_number,1, QTableWidgetItem(result_dict[Database_Value])) compound_number+=1 print('Completed.') def SMART_Export_Fx(self): ID = self.Dereplication_Report_Sample_Select.currentText() Sample_Dataset = pd.read_json(os.path.join(MasterOutput,ID,ID+'_HSQC_Preprocessed.json')).drop(["Intensity"],axis=1) Sample_Dataset.columns = ['1H','13C'] Sample_Dataset = Sample_Dataset.sort_values(by=['1H'],ascending = True).round({'1H':2,'13C':1}) Sample_Dataset.to_csv(os.path.join(MasterOutput,ID,ID+'_SMART_Peak_List.csv')) PopUP('Dataset Exported',str('The HSQC Data for'+ID+' has been converted to a CSV formatted for direct import into SMART. Go to SMART.ucsd.edu to search this dataset against over 40k HSQC spectra'),'Info') def Export_Derep_File(self): from shutil import copyfile ID = self.Dereplication_Report_Sample_Select.currentText() Sample_Dataset = pd.read_json(os.path.join(MasterOutput,ID,ID+'_HSQC_Preprocessed.json')).drop(["Intensity"],axis=1) Sample_Dataset["Identity"] = ID if 'HND_' in ID: ID2 = ID ID = ID.replace("HND_","") os.mkdir(os.path.join('Dereplication_Database',ID)) try: ID2 copyfile(os.path.join(MasterOutput,ID2,ID2+'_spin_systems.json'),os.path.join('Dereplication_Database',ID,ID+'_spin_systems.json')) except: copyfile(os.path.join(MasterOutput,ID,ID+'_spin_systems.json'),os.path.join('Dereplication_Database',ID,ID+'_spin_systems.json')) Sample_Dataset.to_json(os.path.join('Dereplication_Database',ID,'DDF_'+ID+'_HSQC.json')) def Export_Derep_Results(self): import csv path = QFileDialog.getSaveFileName( self, 'Save File', '', 'CSV(*.csv)')[0] with open(path, 'w',newline='') as stream: writer = csv.writer(stream) headers = [] for column in range(self.Dereplication_Report_Table.columnCount()): header = self.Dereplication_Report_Table.horizontalHeaderItem(column) if header is not None: headers.append(header.text()) else: headers.append("Column " + str(column)) writer.writerow(headers) for row in range(self.Dereplication_Report_Table.rowCount()): rowdata = [] for column in range(self.Dereplication_Report_Table.columnCount()): item = self.Dereplication_Report_Table.item(row, column) if item is not None: rowdata.append(str(item.text())) print(str(item.text())) else: pass writer.writerow(rowdata) def Select_Dereplication_Library(self): global Dereplication_Database Dereplication_Database = QFileDialog.getExistingDirectory(self) print('Custom dereplication library loaded.') return Dereplication_Database def ViewNetwork_launch(self): self.window2=QMainWindow() self.ui = Network_Viewer() self.ui.show() def Update_Log_Fx(self): try: with open(os.path.join(MasterOutput, "MADByTE_Log.txt"), "r") as f: contents = f.read() self.Madbyte_Log_Viewer.setText(contents) except: PopUP('Log file not found.',"The MADByTE log file was not found. Please ensure you have selected a project directory to load the file.",'Error') def prompt_MADByTE(self): PopUP("Begining MADByTE","Now Begining MADByTE Analysis. \n Based on how many samples were submitted, this may take a while. Please hit 'ok'. ",'Info') ID = 'temp' global Entity Entity = "Extract" global Hppm_Error Hppm_Error = float(self.Hppm_Input.text()) global Cppm_Error Cppm_Error = float(self.Cppm_Input.text()) global Tocsy_Error Tocsy_Error = float(self.Consensus_Error_Input.text()) if self.Multiplet_Merger_Checkbox.isChecked()== True: Multiplet_Merger = True elif self.Multiplet_Merger_Checkbox.isChecked() == False: Multiplet_Merger = False Similarity_Cutoff = float(self.Similarity_Ratio_Input.text()) global nmr_data_type nmr_data_type = self.NMR_Data_Type_Combo_Box.currentText() self.run_MADByTE(DataDirectory, Entity, Hppm_Error, Cppm_Error, Tocsy_Error, MasterOutput, Multiplet_Merger, Similarity_Cutoff, nmr_data_type) def run_MADByTE( self, DataDirectory, Entity, Hppm_Error, Cppm_Error, Tocsy_Error, MasterOutput, Multiplet_Merger, Similarity_Cutoff, nmr_data_type ): Sample_List = [] for x in range(self.BatchSamplesList.count()): Sample_List.append(self.BatchSamplesList.item(x).text()) setup_logging("MADByTE_Log.txt", fpath=MasterOutput, level=logging.DEBUG) # Define workers to start processing Solvent = self.Solvent_comboBox.currentText() Restart_Flag = False if 'correlation_matrix.json' in os.listdir(MasterOutput): DF_Dialog = Data_Found_Dialog() if DF_Dialog.exec_(): print('Reprocessing Data') Restart_Flag = True else: print('Reprocessing Canceled') ss_worker = Worker( fn=MADByTE.spin_system_construction, sample_list=Sample_List, input_dir=DataDirectory, project_dir=MasterOutput, nmr_data_type=nmr_data_type, entity=Entity, hppm_error=Hppm_Error, tocsy_error=Tocsy_Error, merge_multiplets=Multiplet_Merger, restart = Restart_Flag, solvent=Solvent, ) corr_worker = Worker( fn=MADByTE.correlation_matrix_generation, project_dir=MasterOutput, hppm_error=Hppm_Error, cppm_error=Cppm_Error, ) def ss_complete(): for s in Sample_List: self.Dereplication_Report_Sample_Select.addItem(s) # Trigger correlation network self.threadpool.start(corr_worker) def corr_complete(): PopUP('MADByTE Analysis Completed',"MADByTE Analysis and Correlation Matrix Generation has completed on these datasets.","Info") self.Update_Log_Fx() self.TOCSY_Net_Button_2.setEnabled(True) self.Dereplicate_Button.setEnabled(True) self.SMART_Export_Button.setEnabled(True) self.Export_Derep_File_Button.setEnabled(True) # Tell workers to execute functions when complete ss_worker.signals.finished.connect(ss_complete) corr_worker.signals.finished.connect(corr_complete) # Execute self.threadpool.start(ss_worker) ###Plotting Functions### def mouseMoved(self,evt): pos = evt if self.plot.sceneBoundingRect().contains(pos): mousePoint = self.plot.plotItem.vb.mapSceneToView(pos) self.mousecoordinatesdisplay.setText("<span style='font-size: 15pt'>X=%0.01f, <span style='color: black'>Y=%0.01f</span>" % (mousePoint.x(),mousePoint.y())) vLine.setPos(mousePoint.x()) hLine.setPos(mousePoint.y()) ###How to view 1D NMR Data### def View_1D_Data(self): try: self.plot.clear() ID = self.NMR_Data_View_Selector.currentText() path_ = os.path.join(DataDirectory,ID) PROTON_DIR ="undefined" for directory in os.listdir(path_): with open(os.path.join(path_,directory,'pulseprogram')) as f: content = f.readlines(1) content = [x.strip() for x in content] content = [x.rsplit('pp/')[1] for x in content] if content == ['zg"']: PROTON_DIR = os.path.join(path_,directory,'pdata',"1") dic, data = ng.bruker.read_pdata(PROTON_DIR) udic = ng.bruker.guess_udic(dic, data) #needed to convert from points to PPM uc = ng.fileiobase.uc_from_udic(udic) ppm_scale = uc.ppm_scale() self.plot.plot(ppm_scale,data) self.plot.invertX(True) self.plot.invertY(False) except: PopUP('Data Not Found','1H data not found. \n This may be for a few reasons: \n \n * MADByTE can only display data processed by Topspin.\n * The FID is corrupted and cannot be read. \n * The pulse program file is missing or corrupted.','Error') def View_HSQC_Data(self): try: self.plot.clear() ID = self.NMR_Data_View_Selector.currentText() HSQC_DATA = pd.read_json(os.path.join(MasterOutput,ID,str(ID+"_HSQC_Preprocessed.json"))) self.plot.plot(HSQC_DATA.H_PPM,HSQC_DATA.C_PPM,pen=None,symbol = "o") self.plot.invertX(True) self.plot.invertY(True) except: PopUP('Data not found','Selected Dataset not found. Please process in Topspin Prior to running MADByTE. For 2D Datasets, the displayed data is derived from peak picking lists.','Error') def View_TOCSY_Data(self): try: self.plot.clear() ID = self.NMR_Data_View_Selector.currentText() TOCSY_DATA = pd.read_json(os.path.join(MasterOutput,ID,str(ID+"_TOCSY_Data.json"))) self.plot.plot(TOCSY_DATA.Ha,TOCSY_DATA.Hb,pen=None,symbol="o" ) self.plot.invertX(True) self.plot.invertY(True) vLine = InfiniteLine(angle=45, movable=False) self.plot.addItem(vLine) except: PopUP('Data not found','Selected Dataset not found. Please process in Topspin Prior to running MADByTE. For 2D Datasets, the displayed data is derived from peak picking lists.','Error') def Bioactivity_Plotting_Fx(self): try: Bioactivity_Low = float(self.Low_Activity_Box.text()) Bioactivity_Med = float(self.Mild_Activity_Box.text()) Bioactivity_High = float(self.High_Activity_Box.text()) fname = self.Bioactivity_Network_Name_Box.text() title = fname MADByTE.plotting.Bioactivity_plot(MasterOutput,Network_In_Path,Bioactivity_Data_In,title,fname,Bioactivity_Low,Bioactivity_Med,Bioactivity_High) PopUP('Network successfully created','The Bioactivity Network has been created successfully.','Info') except: PopUP('Something went wrong',"Please ensure you've correctly set the values for the bioactivity cutoffs and that the bioactivity data is in the correct format.",'Error') def Select_Network_To_Layer_Fx(self): global Network_In_Path Network_In_Path = self.openFileNameDialog() if '.graphml' not in Network_In_Path: PopUP('Incorrect data type','Please select the .graphml version of the network, the HTML file will not work.','Error') def Select_Bioactivity_File_Fx(self): global Bioactivity_Data_In Bioactivity_Data_In = self.openFileNameDialog() if Directory_Location == '': PopUP('Please Select A Bioactivity File', 'Please select a bioactivity file to continue.','Error') return if '.csv' not in Bioactivity_Data_In: PopUP('Incorrect data type','Please select a CSV file.','Error') def Ready_Check(self): try: MasterOutput except: self.MADByTE_Button_2.setToolTip('Select a project directory before proceeding.') try: DataDirectory except: self.MADByTE_Button_2.setToolTip('Select an NMR data directory.') try: MasterOutput DataDirectory self.MADByTE_Button_2.setToolTip('Ready to run MADByTE Analysis.') self.MADByTE_Button_2.setEnabled(True) except: return
class GridOperator(QObject): update_logger = pyqtSignal(name='update_logger') exec_pending = pyqtSignal(name='exec_pending') def __init__(self, grid): super().__init__() logging.debug('__init__() called on GridOperator') self.grid = grid self.stop_flag = False self.fastpath = False # fastpath is active when debug is diasbled self.retry_counter = 0 self.delay = 0 self.threadpool = QThreadPool() self.b_debug_window = False self.pending_return = [] self.pid_register = [] self.exec_pending.connect(self.checkPending) mp.set_start_method('spawn') logging.debug('__init__() GridOperator, threadCount: {}'.format( self.threadpool.maxThreadCount())) def startExec(self, start_pos, record=None): logging.debug('startExec() called, start_pos = {}'.format(start_pos)) try: element = self.grid.itemAtPosition(*start_pos).widget() except AttributeError as e: return if self.stop_flag: return self.update_logger.emit() executor = Executor(element, record, self.delay) executor.signals.finished.connect(self.execDone) executor.signals.pid_sig.connect(self.register_pid) element.highlightStart() self.threadpool.start(executor) def register_pid(self, pid): # register PID of spawned child process self.pid_register.append(pid) logging.debug('PID register: {}'.format(self.pid_register)) def execDone(self, prg_return): logging.debug('execDone() called GridOperator from {}'.format( prg_return.source)) element = self.grid.itemAtPosition(*prg_return.source).widget() logging.debug('PID returned: {}'.format(prg_return.pid)) # remove returned pid from register try: self.pid_register.remove(prg_return.pid) except Exception as e: logging.error('De-registration of PID failed: {}'.format(e)) # if an execption occured if (issubclass(prg_return.record_0.__class__, BaseException)): logging.error('Target {}|{} Exception found: {}'.format( prg_return.source[0], alphabet[prg_return.source[1]], prg_return.record_0)) element.highlightException() self.exceptwindow = ExceptWindow(str(prg_return.record_0), prg_return.source) self.exceptwindow.window_closed.connect(self.highlightStop) return ### proceed with regular execution ### # when the log checkbox is activated if prg_return.log: if prg_return.log_txt: logging.info('Message {}|{} : {}'.format( prg_return.source[0], alphabet[prg_return.source[1]], prg_return.log_txt)) if prg_return.log_output: log = prg_return.log_output else: log = prg_return.record_0 logging.info('Output {}|{} : {}'.format( prg_return.source[0], alphabet[prg_return.source[1]], log)) # when the debug button on the element is active if element.b_debug: if prg_return.log_output: log_message = prg_return.log_output else: log_message = str(prg_return.record_0) logging.debug( 'GridOperator::execDone() b_debug_window = {}'.format( self.b_debug_window)) if isinstance(element, ExecStack): # don't open the regular debug window logging.debug( 'GridOperator::execDone()Special window for Exec stack element' ) element.highlightStop() self.goNext(prg_return) # check if there is already an open debug window elif not self.b_debug_window: self.debugWindow = DebugWindow(log_message, prg_return.source) self.debugWindow.proceed_execution.connect( lambda: self.proceedExec(prg_return)) self.debugWindow.raiseWindow() #if not element.self_sync: self.b_debug_window = True else: self.pending_return.append(prg_return) else: # highlight stop =! element.highlightStop() self.goNext(prg_return) def checkPending(self): logging.debug('GridOperator::checkPending() called') if self.pending_return: prg_return = self.pending_return.pop(0) self.execDone(prg_return) def proceedExec(self, prg_return): element = self.grid.itemAtPosition(*prg_return.source).widget() element.highlightStop() self.b_debug_window = False self.exec_pending.emit() self.goNext(prg_return) def goNext(self, prg_return): if prg_return.target_0: logging.debug( 'GridOperator::goNext() called with next target_0: {}'.format( prg_return.target_0)) logging.debug( 'GridOperator::goNext() called with record_0: {}'.format( prg_return.record_0)) if self.fastpath: new_rec = self.fastPath(prg_return.target_0, prg_return.record_0) if new_rec: # check for ExecR or ExecRB self.goNext(new_rec) else: # if nothing found: proceed as usual self.startExec(prg_return.target_0, prg_return.record_0) else: self.startExec(prg_return.target_0, prg_return.record_0) if prg_return.target_1: logging.debug( 'GridOperator::goNext() called with additional target_1: {}'. format(prg_return.target_1)) logging.debug( 'GridOperator::goNext() called with record_1: {}'.format( prg_return.record_1)) # self_sync is true on basic_sched and binancesched self_sync = self.grid.itemAtPosition( *prg_return.target_1).widget().self_sync if self.fastpath and not self_sync: new_rec = self.fastPath(prg_return.target_1, prg_return.record_1) logging.debug('GridOperator::goNext() execption here') logging.debug( 'GridOperator::goNext() new_rec: {}'.format(new_rec)) self.goNext(new_rec) else: self.startExec(prg_return.target_1, prg_return.record_1) def fastPath(self, target, record): logging.debug( 'GridOperator::fastPath() check row: {} col: {}'.format(*target)) element = self.grid.itemAtPosition(*target).widget() if isinstance(element, ExecRB): # jump to the next target # record_1 -> record_0 when goNext() is called recursively # returning only target_0 and record_0 new_rec = Record(element.getPos(), (element.row + 1, element.column), record) return new_rec elif isinstance(element, ExecR): # jump to the next target #hier testen ob target fings # record_1 -> record_0 when goNext() is called recursively # returning only target_0 and record_0 new_rec = Record(element.getPos(), (element.row, element.column + 1), record) return new_rec else: return None def highlightStop(self, position): logging.debug( 'highlightStop() called for position {}'.format(position)) element = self.grid.itemAtPosition(*position).widget() element.highlightStop() def stop_execution(self): logging.debug('stop_execution() called') self.stop_flag = True def kill_proc(self): logging.debug('kill_proc() called') for proc in self.pid_register: os.kill(proc, signal.SIGTERM) logging.info('Process killed, PID {}'.format(proc)) self.pid_register.clear()