def main(): app = QApplication(sys.argv) chart = DynamicSpline() chart.setTitle("Dynamic spline chart") chart.legend().hide() chart.setAnimationOptions(QChart.AllAnimations) view = QChartView(chart) view.setRenderHint(QPainter.Antialiasing) view.resize(1000, 500) view.show() sys.exit(app.exec_())
self.timer = QTimer(self) self.timer.setInterval(1000) self.timer.timeout.connect(self.handleTimeout) self.timer.start() def handleTimeout(self): x = self.plotArea().width() / self.axisX.tickCount() y = (self.axisX.max() - self.axisX.min()) / self.axisX.tickCount() self.m_x += y # 在PyQt5.11.3及以上版本中,QRandomGenerator.global()被重命名为global_() self.m_y = QRandomGenerator.global_().bounded(5) - 2.5 self.series.append(self.m_x, self.m_y) self.scroll(x, 0) if self.m_x >= 100: self.timer.stop() if __name__ == "__main__": app = QApplication(sys.argv) chart = DynamicSpline() chart.setTitle("Dynamic spline chart") chart.legend().hide() chart.setAnimationOptions(QChart.AllAnimations) view = QChartView(chart) view.setRenderHint(QPainter.Antialiasing) # 抗锯齿 view.resize(400, 300) view.show() sys.exit(app.exec_())
@site: http://alyl.vip, http://orzorz.vip, http://coding.net/u/892768447, http://github.com/892768447 @email: [email protected] @file: LineChart @description: ''' import sys from PyQt5.QtChart import QChartView, QLineSeries, QChart from PyQt5.QtGui import QPainter from PyQt5.QtWidgets import QApplication __version__ = "0.0.1" if __name__ == "__main__": app = QApplication(sys.argv) chart = QChart() chart.setTitle("Line Chart 1") series = QLineSeries(chart) series.append(0, 6) series.append(2, 4) chart.addSeries(series) chart.createDefaultAxes() # 创建默认轴 view = QChartView(chart) view.setRenderHint(QPainter.Antialiasing) # 抗锯齿 view.resize(800, 600) view.show() sys.exit(app.exec_())
class initial(QDialog): def __init__(self): super(initial, self).__init__() loadUi("main.ui", self) # definir elementos de la UI y acciones/funciones asociadas # creamos sets para mostrar resultados en el barchart self.setRecall = QBarSet("Recalls") self.setRecall.append([0, 0, 0]) self.setAccurracy = QBarSet("Accurracy") self.setAccurracy.append([0, 0, 0]) self.series = QBarSeries() # Elementos Tab Entrenamiento: # =========================== # Btn Insertar primeros datos entrenamiento self.trainingAddFilesBtn.clicked.connect( self.insertarNoticiasEntrenamientoDespoblacion) # Btn Insertar segundos datos entrenamiento self.trainingAddFilesBtn2.clicked.connect( self.insertarNoticiasEntrenamientoNoDespoblacion) # Btn Preprocesamiento de texto self.procesarTextoBtn.clicked.connect(self.procesarTextoEntrenamiento) # ComboBox selector de Modelo a entrenar # self.chooseModelComboBox.activated.connect(self.elegirModeloEntrenamiento) # añadir elementos al comboBox y sus valores asociados self.chooseModelComboBox.addItem("KNN", 1) self.chooseModelComboBox.addItem("Naive Bayes", 2) self.chooseModelComboBox.addItem("Decision Tree", 3) # Btn para entrenar el modelo seleccionado self.trainModelBtn.clicked.connect(self.entrenarModelo) # Elementos Tab Testeo: # ==================== # Btn Insertar Datos Testeo self.testingFilesBtn.clicked.connect(self.insertarNoticiasTesteo) # Btn Seleccionar Modelo self.selectTestModelBtn.clicked.connect(self.elegirModeloTesteo) # Btn Mostrar Resultados self.testBtn.clicked.connect(self.mostrarResultados) # Tab Testeo #self.tabTest.clicked.connect(self.abrirTabTesteo) # nombre excel self.nombreresultadoexcel = ':)' # funciones # ========= # abrir tab testeo def abrirTabTesteo(self): self.stepTipsField.setPlainText( "En esta pestaña puede realizar el testeo sobre un nuevo set de datos para un modelo ya existente." ) # abrir dialog window para seleccionar los datos de entrenamiento def insertarNoticiasEntrenamientoDespoblacion(self): del desp[:] print(desp) # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Seleccionamos los directorios donde tenemos los archivos de texto que utilizaremos para entrenar nuestro modelo." ) # abrir ventana seleccion archivos desp.append(self.openDialogBox()) print(desp) #cambiar self.procesarTextoBtn a habilitado self.trainingAddFilesBtn2.setEnabled(True) # abrir dialog window para seleccionar los segundos datos de entrenamiento def insertarNoticiasEntrenamientoNoDespoblacion(self): del no_despoblacion[:] # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Seleccionamos los directorios donde tenemos los archivos de texto que utilizaremos para entrenar nuestro modelo." ) # abrir ventana seleccion archivos no_despoblacion.append(self.openDialogBox()) #cambiar self.procesarTextoBtn a habilitado self.procesarTextoBtn.setEnabled(True) # aplicar preprocesamiento de texto def procesarTextoEntrenamiento(self): # cambiar texto campo descripcion self.stepTipsField.setPlainText( "El preprocesamiento a realizar consta de 4 etapas:\n1. Tokenizar: separar las palabras que componen un texto, obteniendo como resultado una secuencia de tokens.\n2. Normalización: se pasa a minúsculas tdoos los tokens.\n3.Filtrado de stopwords: en esta etapa eliminamos aquellas palabras con poco valor semántico, denominadas stopwords.\n4.Stemming: extraemos el lexema de los tokens restantes (un ejemplo sería ‘cas-’ para la palabra ‘casero’)" ) del noticias[:] del clases[:] # bucle inserción de noticias mediante open ingresar_noticias(desp[0], noticias) ingresar_noticias(no_despoblacion[0], noticias) ingresar_noticias(desp[0], despoblacion) # Procesamiento de texto texto_procesado(processed_text_entrenamiento, noticias) # Creación de arreglo de clases crea_clases(clases, processed_text_entrenamiento, despoblacion) # cambiar self.trainModelBtn a habilitado self.trainModelBtn.setEnabled(True) # cambiar texto campo descripcion self.stepTipsField.setPlainText( "El preprocesamiento a realizar consta de 4 etapas:\n1. Tokenizar: separar las palabras que componen un texto, obteniendo como resultado una secuencia de tokens.\n2. Normalización: se pasa a minúsculas tdoos los tokens.\n3.Filtrado de stopwords: en esta etapa eliminamos aquellas palabras con poco valor semántico, denominadas stopwords.\n4.Stemming: extraemos el lexema de los tokens restantes (un ejemplo sería ‘cas-’ para la palabra ‘casero’).\n====================\nEl preprocesamiento ha acabado" ) # cambiar self.procesarTextoBtn a deshabilitado self.procesarTextoBtn.setEnabled(False) # abrir ventana seleccion archivos def openDialogBox(self): filenames = QFileDialog.getOpenFileNames() return filenames[0] # mostrar resultados testeo en nueva tabla def mostrarResultados(self): # cambiar texto campo descripcion self.stepTipsField.setPlainText( "A continuación se muestra una tabla con los resultados de la clasificación realizada por el modelo seleccionado." ) # para ocupar toda la tabla self.tableWidgetshowTest.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.Stretch) # resetear tabla self.tableWidgetshowTest.setRowCount(0) nombre = self.nombreresultadoexcel # mostrar contenido xlsx documento = xlrd.open_workbook(nombre + '.xlsx') df = documento.sheet_by_index(0) self.tableWidgetshowTest.setRowCount((df.nrows) - 1) self.tableWidgetshowTest.setColumnCount(2) for x in range(1, df.nrows): for y in range(2): print('x: ' + df.cell_value(x, y - 1)) item = QTableWidgetItem() nombreArchivo = df.cell_value(x, y - 1).split("/") item.setText(nombreArchivo[len(nombreArchivo) - 1]) self.tableWidgetshowTest.setItem(x - 1, y - 1, item) # insertar archivos fase testeo def insertarNoticiasTesteo(self): # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Seleccione los archivos que utilizará durante la fase de testeo.") # abrir ventana seleccion archivos filepaths = self.openDialogBox() #ingresar noticias ingresar_noticias(filepaths, nuevas) #Procesamiento de texto texto_procesado(processed_text_testeo, nuevas) # cambiar self.selectTestModelBtn a deshabilitado self.selectTestModelBtn.setEnabled(True) # cambiar self.testingFilesBtn a habilitado # self.testingFilesBtn.setEnabled(False) # seleccionar modelo fase testeo def elegirModeloTesteo(self): # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Seleccione el diccionario .pk y modelo correspondiente .pk1.") # abrir ventana seleccion archivos modelopath = self.openDialogBox() if (len(modelopath) == 2): # cargar diccionario cv1 = cargar_modelo(modelopath[0]) # cargar modelo modelo = cargar_modelo(modelopath[1]) # aplicar tfidf X_testcv = tfid_fit(processed_text_testeo, cv1) # insertar predicciones predicciones = [] for i in X_testcv: predicciones.append(prediccion(modelo, i)) # crear dataframe df = pd.DataFrame(data=predicciones, index=nuevas) # nombrar archivo y exportar a excel archivo = modelopath[0] new_archivo = archivo.replace('modelos', 'resultados') nombre = new_archivo[:len(new_archivo) - 3] self.nombreresultadoexcel = nombre df.to_excel(nombre + ".xlsx", "Sheet1") # cambiar self.testBtn a habilitado self.testBtn.setEnabled(True) # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Resultados exportados a la carpeta resultados en formato Excel." ) # aplicar modelo NaiveBayes entrenamiento def entrenamientoNaiveBayes(self): # Proceso TFIDF X_traincv = cv.fit_transform(processed_text_entrenamiento) # Partición de datos X_train, X_test, Y_train, Y_test = train_test_split(X_traincv, clases, test_size=0.15, random_state=324) #Modelos naive = naive_bayes(X_train, Y_train) print(naive) print( "####################### Test Score ##############################\n" ) test_score(naive, X_train, Y_train) # Creamos los datos a testear Y_true_naive, Y_pred_naive = datos_test(Y_test, naive, X_test) # Datos de los modelos print( "###################### Accuracy ###############################\n" ) accuracy(Y_true_naive, Y_pred_naive) self.setAccurracy.replace( 1, accuracy_score(Y_true_naive, Y_pred_naive) * 100) print( "####################### Recall ##############################\n") print(recall_score(Y_true_naive, Y_pred_naive, average='macro')) self.setRecall.replace( 1, recall_score(Y_true_naive, Y_pred_naive, average='macro') * 100) a = "Modelo Naive-Bayes\n==================\nRecall:" + str( recall_score(Y_true_naive, Y_pred_naive, average='macro')) + "\nAccuracy: " + str( accuracy_score(Y_true_naive, Y_pred_naive)) self.stepTipsField.setPlainText(a) #llamar a funcion para actualizar los valores del Barchart self.appendResults() print( "\n###################### Matriz de confusion ###############################\n" ) matrizconf(Y_true_naive, Y_pred_naive) #Guardamos modelo now = datetime.now() # dd/mm/YY H:M:S dt_string = now.strftime("dia_%d-%m-%Y,hora_%H-%M-%S") guardar_modelo('modelos/naive_' + dt_string, naive) with open('modelos/naive_' + dt_string + '.pk', 'wb') as f: pickle.dump(cv, f) # aplicar modelo Decision Tree entrenamiento def entrenamientoArbolDecision(self): # Proceso TFIDF X_traincv = cv.fit_transform(processed_text_entrenamiento) #Partición de datos X_train, X_test, Y_train, Y_test = train_test_split(X_traincv, clases, test_size=0.15, random_state=324) #Modelos tree = decision_tree(X_train, Y_train) print( "####################### Test Score ##############################\n" ) test_score(tree, X_train, Y_train) #Creamos los datos a testear Y_true_tree, Y_pred_tree = datos_test(Y_test, tree, X_test) #Datos de los modelos print( "###################### Accuracy ###############################\n" ) accuracy(Y_true_tree, Y_pred_tree) #incluir nueva accurracy al set de resultados de NaiveBayes #self.setDTrees.append(accuracy_score(Y_true_tree, Y_pred_tree)*100) self.setAccurracy.replace( 2, accuracy_score(Y_true_tree, Y_pred_tree) * 100) #llamar a funcion para actualizar los valores del Barchart print( "####################### Recall ##############################\n") print(recall_score(Y_true_tree, Y_pred_tree, average='macro')) #self.setDTrees.append(recall_score(Y_true_tree, Y_pred_tree, average='macro')*100) self.setRecall.replace( 2, recall_score(Y_true_tree, Y_pred_tree, average='macro') * 100) a = "Modelo Arbol Decision\n=====================\nRecall:" + str( recall_score(Y_true_tree, Y_pred_tree, average='macro')) + "\nAccuracy: " + str( accuracy_score(Y_true_tree, Y_pred_tree)) self.stepTipsField.setPlainText(a) self.appendResults() #Matriz confusion print( "\n###################### Matriz de confusion ###############################\n" ) matrizconf(Y_true_tree, Y_pred_tree) now = datetime.now() # dd/mm/YY H:M:S dt_string = now.strftime("dia_%d-%m-%Y,hora_%H-%M-%S") guardar_modelo('modelos/tree_' + dt_string, tree) with open('modelos/tree_' + dt_string + '.pk', 'wb') as f: pickle.dump(cv, f) # aplicar modelo KNN def entrenamientoKnn(self): # Proceso TFIDF X_traincv = cv.fit_transform(processed_text_entrenamiento) #Partición de datos X_train, X_test, Y_train, Y_test = train_test_split(X_traincv, clases, test_size=0.15, random_state=324) #Modelos modknn = knn(X_train, Y_train) print( "####################### Test Score ##############################\n" ) test_score(modknn, X_train, Y_train) #Creamos los datos a testear Y_true_knn, Y_pred_knn = datos_test(Y_test, modknn, X_test) #Datos de los modelos print( "###################### Accuracy ###############################\n" ) accuracy(Y_true_knn, Y_pred_knn) #llamar a funcion para actualizar los valores del Barchart self.setAccurracy.replace(0, accuracy_score(Y_true_knn, Y_pred_knn) * 100) print( "####################### Recall ##############################\n") print(recall_score(Y_true_knn, Y_pred_knn, average='macro')) #self.setDTrees.append(recall_score(Y_true_knn, Y_pred_knn, average='macro')*100) self.setRecall.replace( 0, recall_score(Y_true_knn, Y_pred_knn, average='macro') * 100) a = "Modelo KNN\n===============\nRecall:" + str( recall_score(Y_true_knn, Y_pred_knn, average='macro')) + "\nAccuracy: " + str( accuracy_score(Y_true_knn, Y_pred_knn)) self.stepTipsField.setPlainText(a) self.appendResults() #Matriz confusion print( "\n###################### Matriz de confusion ###############################\n" ) matrizconf(Y_true_knn, Y_pred_knn) #Guardamos modelo now = datetime.now() # dd/mm/YY H:M:S dt_string = now.strftime("dia_%d-%m-%Y,hora_%H-%M-%S") guardar_modelo('modelos/knn_' + dt_string, modknn) with open('modelos/knn_' + dt_string + '.pk', 'wb') as f: pickle.dump(cv, f) # comprobar modelo seleccionado en comboBox def entrenarModelo(self): #cambiar texto en self.stepTipsField self.stepTipsField.setPlainText("Entrenando el modelo seleccionado...") #tomar valor actual del comboBox modelSelect = self.chooseModelComboBox.currentData() #no existe switch en python (o.o) if modelSelect == 1: self.entrenamientoKnn() if modelSelect == 2: self.entrenamientoNaiveBayes() if modelSelect == 3: self.entrenamientoArbolDecision() # add resultados entrenamiento y actualizar barchart def appendResults(self): #clear de series self.series = QBarSeries() #add sets de Accurracy y Recall de todos los modelos procesados a series self.series.append(self.setAccurracy) self.series.append(self.setRecall) # crear nuevo Chart chart = QChart() # add series al nuevo Chart chart.addSeries(self.series) chart.setTitle("Precisiones de Modelos") chart.setAnimationOptions(QChart.SeriesAnimations) # parametro QChart modelosEjeX = ('KNN', 'Naive Bayes', 'Decision Trees') # parametros ejeX ejeX = QBarCategoryAxis() ejeX.append(modelosEjeX) # parametros ejeY ejeY = QValueAxis() chart.addAxis(ejeX, Qt.AlignBottom) chart.addAxis(ejeY, Qt.AlignLeft) # leyenda Barchart chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) # Mostrar ventana Barchart self.QChartView = QChartView(chart) self.QChartView.resize(600, 600) self.QChartView.show()
class SeeingMonitor(QMainWindow, Ui_MainWindow): def __init__(self): super(SeeingMonitor, self).__init__() self.setupUi(self) self.Camera = None self.THRESH = None self.threshold_auto = False self.frame = None self.draw_only_frame = None self.video_source = VideoSource.NONE self.export_video = False self.select_noiseArea = False self.coordinates_noiseArea = [] self.lineedit_path.setText(QDir.currentPath()) self.lineedit_filename.setText("seeing.csv") self.save_filename = None self._updateFileSave() self.pause_pressed = False self.datetimeedit_start.setMinimumDateTime(QDateTime.currentDateTime()) self.datetimeedit_end.setMinimumDateTime(QDateTime.currentDateTime()) if platform.system() == 'Linux': self.button_start.setEnabled(False) self.button_settings.setEnabled(False) self.button_start.clicked.connect(self.startLiveCamera) self.button_settings.clicked.connect(self.showSettings) self.button_simulation.clicked.connect(self.startSimulation) self.button_import.clicked.connect(self.importVideo) self.button_export.clicked.connect(self.exportVideo) self.button_noise.clicked.connect(self.selectNoiseArea) self.lineedit_path.textEdited.connect(self._updateFileSave) self.lineedit_filename.textEdited.connect(self._updateFileSave) self.slider_threshold.valueChanged.connect(self._updateThreshold) self.checkbox_thresh.stateChanged.connect(self._updateThresholdState) # Update the Tilt value self.spinbox_b.valueChanged.connect(self._updateFormulaZTilt) self.spinbox_d.valueChanged.connect(self._updateFormulaZTilt) # Update the constants in the FWHM seeing formula self.spinbox_d.valueChanged.connect(self._updateFormulaConstants) self.spinbox_lambda.valueChanged.connect(self._updateFormulaConstants) # Timer for acquiring images at regular intervals self.acquisition_timer = QTimer(parent=self.centralwidget) self.timer_interval = None self._updateThreshold() self._updateFormulaZTilt() self._updateFormulaConstants() # Storing the Delta X and Y in an array to calculate the Standard Deviation self.arr_delta_x = deque(maxlen=100) self.arr_delta_y = deque(maxlen=100) self.plot_length = 1000 self.fwhm_lat = 0 self.fwhm_tra = 0 self.max_lat = 1 self.min_lat = 0 self.max_tra = 1 self.min_tra = 0 self.series_lat = QLineSeries() self.series_lat.setName("Lateral") self.series_tra = QLineSeries() self.series_tra.setName("Transversal") self.chart = QChart() self.chart.addSeries(self.series_lat) self.chart.addSeries(self.series_tra) # self.chart.createDefaultAxes() self.axis_horizontal = QDateTimeAxis() self.axis_horizontal.setMin(QDateTime.currentDateTime().addSecs(-60 * 1)) self.axis_horizontal.setMax(QDateTime.currentDateTime().addSecs(0)) self.axis_horizontal.setFormat("HH:mm:ss.zzz") self.axis_horizontal.setLabelsFont( QFont(QFont.defaultFamily(self.font()), pointSize=5)) self.axis_horizontal.setLabelsAngle(-20) self.chart.addAxis(self.axis_horizontal, Qt.AlignBottom) self.axis_vertical_lat = QValueAxis() self.axis_vertical_lat.setRange(self.max_lat, self.min_lat) self.chart.addAxis(self.axis_vertical_lat, Qt.AlignLeft) self.axis_vertical_tra = QValueAxis() self.axis_vertical_tra.setRange(self.max_tra, self.min_tra) self.chart.addAxis(self.axis_vertical_tra, Qt.AlignRight) self.series_lat.attachAxis(self.axis_horizontal) self.series_lat.attachAxis(self.axis_vertical_lat) self.series_tra.attachAxis(self.axis_horizontal) self.series_tra.attachAxis(self.axis_vertical_tra) self.chart.setTitle("Full Width at Half Maximum") self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignBottom) self.chartView = QChartView(self.chart, parent=self.graphicsView) self.chartView.resize(640, 250) self.chartView.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.chartView.setRenderHint(QPainter.Antialiasing) def closeEvent(self, event): try: self.Camera.StopLive() except AttributeError: pass try: self.cap.release() except AttributeError: pass try: self.video_writer.release() except AttributeError: pass event.accept() def _callbackFunction(self, hGrabber, pBuffer, framenumber, pData): """ This is an example callback function for image processig with opencv. The image data in pBuffer is converted into a cv Matrix and with cv.mean() the average brightness of the image is measuered. :param: hGrabber: This is the real pointer to the grabber object. :param: pBuffer : Pointer to the first pixel's first byte :param: framenumber : Number of the frame since the stream started :param: pData : Pointer to additional user data structure """ if pData.buffer_size > 0: image = C.cast(pBuffer, C.POINTER(C.c_ubyte * pData.buffer_size)) cvMat = np.ndarray(buffer=image.contents, dtype=np.uint8, shape=(pData.height, pData.width, pData.iBitsPerPixel)) frame = np.uint8(cvMat) self.frame = cv2.resize(frame, (640, 480)) self.draw_only_frame = self.frame.copy() self._monitor() def _startLiveCamera(self): # Create a function pointer Callbackfunc = IC.TIS_GrabberDLL.FRAMEREADYCALLBACK( self._callbackFunction) ImageDescription = CallbackUserData() # Create the camera object self.Camera = IC.TIS_CAM() self.Camera.ShowDeviceSelectionDialog() if self.Camera.IsDevValid() != 1: print("[Error Camera Selection] Couldn't open camera device !") # QMessageBox.warning(self, "Error Camera Selection", "Couldn't open camera device !") # raise Exception("Unable to open camera device !") return # Now pass the function pointer and our user data to the library self.Camera.SetFrameReadyCallback(Callbackfunc, ImageDescription) # Handle each incoming frame automatically self.Camera.SetContinuousMode(0) print('Starting live stream ...') self.Camera.StartLive( 0 ) ####### PAUSE LIVE STREAM WHEN PAUSE CLICKED ??? ############################################## # self.Camera.StartLive(1) Imageformat = self.Camera.GetImageDescription()[:3] ImageDescription.width = Imageformat[0] ImageDescription.height = Imageformat[1] ImageDescription.iBitsPerPixel = Imageformat[2] // 8 ImageDescription.buffer_size = ImageDescription.width * ImageDescription.height * ImageDescription.iBitsPerPixel while self.video_source == VideoSource.CAMERA: pass # self.timer_interval = 20 # try: # self.acquisition_timer.disconnect() # except TypeError: # pass # self.acquisition_timer.timeout.connect(self._updateLiveCamera) # self.acquisition_timer.start(self.timer_interval) def startLiveCamera(self): try: self.acquisition_timer.disconnect() except TypeError: pass self.video_source = VideoSource.CAMERA self.button_export.setEnabled(True) self._setPauseButton() # Disable other functionalities # self.button_simulation.setEnabled(False) t = threading.Thread(target=self._startLiveCamera, args=(), daemon=True) t.start() def showSettings(self): if not self.Camera.IsDevValid(): QMessageBox.warning( self, "Camera Selection Error", "Please select a camera first by clicking on the button <strong>Start</strong>" ) return try: self.Camera.ShowPropertyDialog() except Exception as e: logging.error(traceback.format_exc()) QMessageBox.warning(self, "Property Dialog Error", traceback.format_exc()) # def _updateLiveCamera(self): # # Capturing a frame # self.Camera.SnapImage() # frame = self.Camera.GetImage() # frame = np.uint8(frame) # self.frame = cv2.resize(frame, (640, 480)) # self.draw_only_frame = self.frame.copy() # self._monitor() # self.displayParameters() def displayParameters(self): parameters_text = "" ExposureTime = [0] self.Camera.GetPropertyAbsoluteValue("Exposure", "Value", ExposureTime) parameters_text = parameters_text + str(ExposureTime[0]) + "\n" GainValue = [0] self.Camera.GetPropertyAbsoluteValue("Gain", "Value", GainValue) parameters_text = parameters_text + str(GainValue[0]) + "\n" self.parameters_label.setText(parameters_text) self.parameters_label.adjustSize() def startSimulation(self): if self.Camera != None and self.Camera.IsDevValid() == 1: self.Camera.StopLive() self.video_source = VideoSource.SIMULATION self.button_export.setEnabled(True) self._setPauseButton() # Disable other functionalities # self.button_start.setEnabled(False) self.button_settings.setEnabled(False) # Generating fake images of DIMM star (One single star that is split by the DIMM) self.starsGenerator = FakeStars() self.timer_interval = 100 try: self.acquisition_timer.disconnect() except TypeError: pass self.acquisition_timer.timeout.connect(self._updateSimulation) self.acquisition_timer.start(self.timer_interval) def _updateSimulation(self): frame = self.starsGenerator.generate() self.frame = cv2.resize(frame, (640, 480)) self.draw_only_frame = self.frame.copy() self._monitor() ################################################################################################################################################################ def _writeCSV(self, headerOnly=False): if headerOnly: with open(self.save_filename, "w") as csvFile: fieldnames = ["timestamp", "lateral", "transversal", "star"] writer = csv.DictWriter(csvFile, fieldnames=fieldnames) writer.writeheader() csvFile.close() else: with open(self.save_filename, "a") as csvFile: writer = csv.writer(csvFile) writer.writerow([ self.current.toTime_t(), self.fwhm_lat, self.fwhm_tra, self.lineedit_star.text() ]) # csvFile.write(",".join([str(self.current) , str(self.fwhm_lat), str(self.fwhm_tra), self.lineedit_star.text()])) # csvFile.write("\n") # csvFile.close() def selectNoiseArea(self): self.label_info.setText("Please select on the video a noise area.") self.button_noise.setText("Selecting ...") self.coordinates_noiseArea = [] self.select_noiseArea = True def _set_noiseArea(self, x1, y1, x2, y2): if len(self.coordinates_noiseArea) == 0: self.coordinates_noiseArea.append([x1, y1]) self.coordinates_noiseArea.append([x2, y2]) elif len(self.coordinates_noiseArea) == 2: self.coordinates_noiseArea[0] = [x1, y1] self.coordinates_noiseArea[1] = [x2, y2] def _draw_noiseArea(self): if len(self.coordinates_noiseArea) >= 2: cv2.rectangle(self.draw_only_frame, (self.coordinates_noiseArea[0][0], self.coordinates_noiseArea[0][1]), (self.coordinates_noiseArea[1][0], self.coordinates_noiseArea[1][1]), (0, 255, 0), 1) def _updateFileSave(self): self.save_filename = join(self.lineedit_path.text(), self.lineedit_filename.text()) self._writeCSV(headerOnly=True) def _updateThreshold(self): if self.threshold_auto: if self.coordinates_noiseArea.__len__() >= 2: noise_area = self.frame[self.coordinates_noiseArea[0][1]:self. coordinates_noiseArea[1][1], self.coordinates_noiseArea[0][0]:self. coordinates_noiseArea[1][0]] try: self.THRESH = noise_area.max() + 20 # self.THRESH = int(round(noise_area.mean())) except ValueError: return self.slider_threshold.setValue(self.THRESH) self.checkbox_thresh.setText("Threshold ({}, auto)".format( self.THRESH)) else: self.THRESH = self.slider_threshold.value() self.checkbox_thresh.setText("Threshold ({})".format(self.THRESH)) def _updateThresholdState(self, state): if state == 0: self.threshold_auto = False self.slider_threshold.setEnabled(True) else: if self.coordinates_noiseArea.__len__() < 2: QMessageBox.information(self, "Select Noise Area", "Please select the noise area") self.selectNoiseArea() self.threshold_auto = True self.slider_threshold.setEnabled(False) def _updateFormulaZTilt(self): self.spinbox_d.setStyleSheet("QSpinBox { background-color: blue; }") try: b = float(self.spinbox_b.value()) / float(self.spinbox_d.value()) except ZeroDivisionError: QMessageBox.warning(self, "Zero Division Error", "D (Apertures Diameter cannot be Zero") return self.K_lat = 0.364 * (1 - 0.532 * np.power(b, -1 / 3) - 0.024 * np.power(b, -7 / 3)) self.K_tra = 0.364 * (1 - 0.798 * np.power(b, -1 / 3) - 0.018 * np.power(b, -7 / 3)) def _updateFormulaConstants(self): # Calculate value to make process faster self.A = 0.98 * np.power( float(self.spinbox_d.value()) / float(self.spinbox_lambda.value()), 0.2) def _calcSeeing(self): std_x = np.std(self.arr_delta_x) std_y = np.std(self.arr_delta_y) # Seeing self.current = QDateTime.currentDateTime() self.fwhm_lat = self.A * np.power(std_x / self.K_lat, 0.6) self.fwhm_tra = self.A * np.power(std_y / self.K_tra, 0.6) threading.Thread(target=self._plotSeeing, args=(), daemon=True).start() threading.Thread(target=self._writeCSV, args=(), daemon=True).start() self.label_info.setText("lat: " + str(self.fwhm_lat) + " | lon: " + str(self.fwhm_tra)) def _calcSeeing_arcsec(self): std_x = np.std(self.arr_delta_x) std_y = np.std(self.arr_delta_y) # Seeing self.current = QDateTime.currentDateTime() self.fwhm_lat = self.A * np.power( std_x / self.K_lat, 0.6) * 205.0 * self.spinbox_pwidth.value( ) / self.spinbox_focal.value() self.fwhm_tra = self.A * np.power( std_y / self.K_tra, 0.6) * 205.0 * self.spinbox_pheight.value( ) / self.spinbox_focal.value() threading.Thread(target=self._plotSeeing, args=(), daemon=True).start() threading.Thread(target=self._writeCSV, args=(), daemon=True).start() self.label_info.setText("lat: " + str(self.fwhm_lat) + " | lon: " + str(self.fwhm_tra)) def _monitor(self): tic = time.time() gray = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY) self._updateThreshold() _, thresholded = cv2.threshold(gray, self.THRESH, 255, cv2.THRESH_TOZERO) # _, contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) contours = contours[:2] # if contours.__len__() > 2: # QMessageBox.warning(self, "Thresholding error", "More than 2 projections were found. " + \ # "Please increase threshold manually or select a better noise area.") cv2.drawContours(self.draw_only_frame, contours, -1, (0, 255, 0), 2) self._draw_noiseArea() try: moments_star_1 = cv2.moments(contours[0]) moments_star_2 = cv2.moments(contours[1]) except IndexError: print("Only {} were found ! (Must be at least 2)".format( len(contours))) else: try: cX_star1 = int(moments_star_1["m10"] / moments_star_1["m00"]) cY_star1 = int(moments_star_1["m01"] / moments_star_1["m00"]) cX_star2 = int(moments_star_2["m10"] / moments_star_2["m00"]) cY_star2 = int(moments_star_2["m01"] / moments_star_2["m00"]) except ZeroDivisionError: return if self.enable_seeing.isChecked(): delta_x = abs(cX_star2 - cX_star1) delta_y = abs(cY_star2 - cY_star1) self.arr_delta_x.append(delta_x) self.arr_delta_y.append(delta_y) # self._calcSeeing() self._calcSeeing_arcsec() cv2.drawMarker(self.draw_only_frame, (cX_star1, cY_star1), color=(0, 0, 255), markerSize=30, thickness=1) cv2.drawMarker(self.draw_only_frame, (cX_star2, cY_star2), color=(0, 0, 255), markerSize=30, thickness=1) finally: self._displayImage() threading.Thread(target=self._writeVideoFile, args=(), daemon=True).start() toc = time.time() elapsed = toc - tic try: print("FPS max = {}".format(int(1.0 / elapsed))) except ZeroDivisionError: pass def _displayImage(self): qImage = array2qimage(self.draw_only_frame) self.stars_capture.setPixmap(QPixmap(qImage)) def _plotSeeing(self): self.axis_horizontal.setMin(QDateTime.currentDateTime().addSecs(-60 * 1)) self.axis_horizontal.setMax(QDateTime.currentDateTime().addSecs(0)) if self.series_lat.count() > self.plot_length - 1: self.series_lat.removePoints( 0, self.series_lat.count() - self.plot_length - 1) if self.series_tra.count() > self.plot_length - 1: self.series_tra.removePoints( 0, self.series_tra.count() - self.plot_length - 1) if self.fwhm_lat > self.max_lat: self.max_lat = self.fwhm_lat self.axis_vertical_lat.setMax(self.max_lat + 10) if self.fwhm_lat < self.min_lat: self.min_lat = self.fwhm_lat self.axis_vertical_lat.setMax(self.min_lat - 10) if self.fwhm_tra > self.max_tra: self.max_tra = self.fwhm_tra self.axis_vertical_tra.setMax(self.max_tra + 10) if self.fwhm_tra < self.min_tra: self.min_tra = self.fwhm_tra self.axis_vertical_tra.setMax(self.min_tra - 10) # print(self.fwhm_lat, self.fwhm_tra) self.series_lat.append(self.current.toMSecsSinceEpoch(), self.fwhm_lat) self.series_tra.append(self.current.toMSecsSinceEpoch(), self.fwhm_tra) def importVideo(self): self.video_source = VideoSource.VIDEO self.button_export.setEnabled(True) self._setPauseButton() options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog filename, _ = QFileDialog.getOpenFileName( self, "Import from Video File", QDir.currentPath(), "Video Files (*.avi *.mp4 *.mpeg *.flv *.3gp *.mov);;All Files (*)", options=options) if filename: if self.Camera != None and self.Camera.IsDevValid() == 1: self.Camera.StopLive() self.cap = cv2.VideoCapture(filename) # print("CAP_PROP_POS_MSEC :", self.cap.get(cv2.CAP_PROP_POS_MSEC)) # print("CAP_PROP_POS_FRAMES :", self.cap.get(cv2.CAP_PROP_POS_FRAMES)) # print("CAP_PROP_POS_AVI_RATIO :", self.cap.get(cv2.CAP_PROP_POS_AVI_RATIO)) # print("CAP_PROP_FRAME_WIDTH :", self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # print("CAP_PROP_FRAME_HEIGHT :", self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # print("CAP_PROP_FPS :", self.cap.get(cv2.CAP_PROP_FPS)) # print("CAP_PROP_FOURCC :", self.cap.get(cv2.CAP_PROP_FOURCC)) # print("CAP_PROP_FRAME_COUNT :", self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) # print("CAP_PROP_FORMAT :", self.cap.get(cv2.CAP_PROP_FORMAT)) # print("CAP_PROP_MODE :", self.cap.get(cv2.CAP_PROP_MODE)) # print("CAP_PROP_BRIGHTNESS :", self.cap.get(cv2.CAP_PROP_BRIGHTNESS)) # print("CAP_PROP_CONTRAST :", self.cap.get(cv2.CAP_PROP_CONTRAST)) # print("CAP_PROP_SATURATION :", self.cap.get(cv2.CAP_PROP_SATURATION)) # print("CAP_PROP_HUE :", self.cap.get(cv2.CAP_PROP_HUE)) # print("CAP_PROP_GAIN :", self.cap.get(cv2.CAP_PROP_GAIN)) # print("CAP_PROP_EXPOSURE :", self.cap.get(cv2.CAP_PROP_EXPOSURE)) # print("CAP_PROP_CONVERT_RGB :", self.cap.get(cv2.CAP_PROP_CONVERT_RGB)) # print("CAP_PROP_WHITE_APERTURE :", self.cap.get(cv2.CAP_PROP_APERTURE)) # print("CAP_PROP_RECTIFICATION :", self.cap.get(cv2.CAP_PROP_RECTIFICATION)) # print("CAP_PROP_ISO_SPEED :", self.cap.get(cv2.CAP_PROP_ISO_SPEED)) # print("CAP_PROP_BUFFERSIZE :", self.cap.get(cv2.CAP_PROP_BUFFERSIZE)) if self.cap.isOpened() == False: QMessageBox.warning(self, "Import from Video", "Cannot load file '{}'.".format(filename)) return self.timer_interval = round(1000.0 / self.cap.get(cv2.CAP_PROP_FPS)) try: self.acquisition_timer.disconnect() except TypeError: pass self.acquisition_timer.timeout.connect(self._grabVideoFrame) self.acquisition_timer.start(self.timer_interval) def _grabVideoFrame(self): ret, frame = self.cap.read() if ret == True: self.frame = cv2.resize(frame, (640, 480)) self.draw_only_frame = self.frame.copy() self._monitor() else: try: self.acquisition_timer.disconnect() except TypeError: pass QMessageBox.information(self, "Import from Video", "Video complete !") self.cap.release() def exportVideo(self): # if not self.enable_seeing.isChecked(): # answer = QMessageBox.question(self, # "Export to Video File", # "Seeing Monitoring is not activated. Continue ?", # QMessageBox.Yes|QMessageBox.No, # QMessageBox.No) # if answer == QMessageBox.No: # return options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog filename, _ = QFileDialog.getSaveFileName( self, "Export to Video File", QDir.currentPath(), "All Files (*);;Video Files (*.avi *.mp4 *.mpeg *.flv *.3gp *.mov)", options=options) if filename: if splitext(filename)[1] != ".avi": filename = splitext(filename)[0] + ".avi" QMessageBox.information( self, "Export to Video File", "Only '.avi' extension is supported. Video will be saved as '{}'" .format(filename)) print(round(1000.0 / float(self.timer_interval))) self.video_writer = cv2.VideoWriter( filename, cv2.VideoWriter_fourcc(*'MJPG'), round(1000.0 / float(self.timer_interval)), ( 640, 480 ) ################################################################################# ) self.export_video = True def _writeVideoFile(self): current = QDateTime.currentDateTime() if self.export_video and current >= self.datetimeedit_start.dateTime() and \ current < self.datetimeedit_end.dateTime(): # self.video_writer.write(self.frame) self.video_writer.write(self.draw_only_frame) def _setPauseButton(self): self.button_pause.setEnabled(True) self.button_pause.setText("⏸ Pause") self.button_pause.clicked.connect(self._pause) def _pause(self): self.pause_pressed = True # IC_SuspendLive IC_StopLive ################################################################################## self.button_pause.setText("▶ Resume") self.button_pause.clicked.connect(self._resume) if self.video_source == VideoSource.CAMERA: self.Camera.StopLive() else: self.acquisition_timer.stop() def _resume(self): self.pause_pressed = False self._setPauseButton() if self.video_source == VideoSource.CAMERA: self.Camera.StartLive(0) else: try: self.acquisition_timer.disconnect() except TypeError: pass self.acquisition_timer.start(self.timer_interval) if self.video_source == VideoSource.CAMERA: pass elif self.video_source == VideoSource.SIMULATION: self.acquisition_timer.timeout.connect(self._updateSimulation) elif self.video_source == VideoSource.VIDEO: self.acquisition_timer.timeout.connect(self._grabVideoFrame)
class SproutUI(QtWidgets.QMainWindow): def __init__(self): super(SproutUI, self).__init__() uic.loadUi('Sprout.ui', self) self.setWindowTitle("Sprout") self.setWindowIcon(QIcon('./Images/SproutIcon.ico')) self.get_default_path() self.save_window_ui = SaveWindow(self) self.chartView = None self.myThread = None # columns: wedges, rows: rings (r1(w1,w2,...,w7,wAvg),r2(...),r3(...),rAvg(...)) self.densities = [] self.measurement_data = [] self.error_message = "" self.loading_image = QPixmap("./Images/LoadingImage") def ui(self): """ Sets Sprout user interface and displays the user interface :return: None """ self.tabWidget_1.setCurrentIndex(0) self.tabWidget_2.setCurrentIndex(0) self.lineEdit_numMeasurements.setFocus(0) # Main Screen and save button self.browse_button_1.clicked.connect(self.browse_file) self.browse_button_2.clicked.connect(self.browse_folder) self.lineEdit_intermediateStepPath.setText(in_data['intermediate_path']) self.lineEdit_numMeasurements.editingFinished.connect(self.update_num_diameter_measurements) self.lineEdit_numWedges.editingFinished.connect(self.update_num_wedges) self.lineEdit_numRings.editingFinished.connect(self.update_num_rings) self.lineEdit_imageDPI.editingFinished.connect(self.update_image_dpi) self.start_button.clicked.connect(self.start_button_func) self.stop_button.clicked.connect(self.stop_button_func) self.save_button.clicked.connect(self.show_save_files) # Graphs View self.comboBox_rings.currentIndexChanged.connect(self.filter_rings_graph) self.comboBox_wedges.currentIndexChanged.connect(self.filter_wedges_graph) self.progressBar.setValue(0) self.progressBar.hide() self.label_progressBar.hide() self.disable_dashboard() self.progressBar.valueChanged.connect(self.progress_change) self.tabWidget_1.tabBar().setCursor(QtCore.Qt.PointingHandCursor) self.tabWidget_2.tabBar().setCursor(QtCore.Qt.PointingHandCursor) self.show() def get_default_path(self): """ Gets the "Documents" directory of the user of the local computer. :return: None """ temp = str(os.path.expanduser("~")) split = temp.split(os.path.sep) temp = str(split[0] + "/" + split[1] + "/" + split[2] + "/" + "Documents/Sprout/Run") in_data['intermediate_path'] = temp def browse_file(self): """ Function mapped to the browse button used to select the file path of the image that will be used to calculate the fiber density of a bamboo cross-section. :return: None """ url = QFileDialog.getOpenFileName(self, "Open a file", "", "*jpg; *jpeg;; *bmp;; *tif") if url[0] is not '': try: self.label_bamboo.setPixmap(self.loading_image) self.label_bamboo.repaint() except Exception: self.warning_message_box("Missing loading image. ") try: assert (url[0])[-4:] in ('.bmp', '.jpg', 'jpeg', '.tif'), "Image format is not supported." except Exception as e: self.label_bamboo.clear() self.warning_message_box(str(e)) return try: cross_section = QPixmap(url[0]) cross_section = cross_section.scaled(500, 500) if cross_section.isNull(): self.label_bamboo.clear() self.warning_message_box("Unable to open input file. \n\n") else: self.label_bamboo.clear() self.label_bamboo.setPixmap(cross_section) self.lineEdit_imagePath.setText(url[0]) in_data['img_path'] = url[0] except Exception as e: self.label_bamboo.clear() self.warning_message_box("Unable to open input file, verify \n file path or image file type.\n\n") def browse_folder(self): """ Function mapped to the browse button used to select the folder path that will be used to save the intermediate step for the fiber density calculation. :return: None """ url = QFileDialog.getExistingDirectory(self, "Open a directory", "", QFileDialog.ShowDirsOnly) if url is not '': self.lineEdit_intermediateStepPath.setText(url) in_data['intermediate_path'] = url def disable_dashboard(self): """ Disables the dashboard and hides the graphs. :return: None """ self.dashboard_tab.setEnabled(False) self.tabWidget_2.setEnabled(False) self.graphs_tab.setEnabled(False) self.region_density_tab.setEnabled(False) self.measurement_data_tab.setEnabled(False) self.tabWidget_2.setCurrentIndex(0) self.widget_rings.hide() self.widget_wedges.hide() self.comboBox_rings.hide() self.comboBox_wedges.hide() def update_num_diameter_measurements(self): """ Update the value for the total number of measurements in the user interface main screen that serves as feedback. :return: None """ global in_data if self.is_int_inbound(self.lineEdit_numMeasurements.text(), 3, 100): temp = int(self.lineEdit_numMeasurements.text()) * 4 self.label_numMeasurementsFeedback.setText("Total Diameter Measurements: " + str(temp)) in_data['num_measurement'] = temp else: self.lineEdit_numMeasurements.clear() def update_num_wedges(self): """ Update the value for number of wedges in the user interface main screen that serves as feedback. :return: None """ global in_data, wedge_degree if self.is_int_inbound(self.lineEdit_numWedges.text(), 3, 100): temp = int(self.lineEdit_numWedges.text()) * 4 wedge_degree = 360 / temp self.label_numWedgesFeedback.setText("Num. Wedges: " + str(temp) + " @ {:.1f}º".format(wedge_degree)) self.label_numRegionsFeedback.setText("Num. Regions: " + str(temp * int(in_data['num_rings']))) in_data['num_wedges'] = temp else: self.lineEdit_numWedges.clear() def update_num_rings(self): """ Update the value for number of rings in the user interface main screen that serves as feedback. :return: None """ global in_data temp = self.lineEdit_numRings.text() if self.is_int_inbound(temp, 1, 25): self.label_numRingsFeedback.setText("Num. Rings: " + str(temp)) self.label_numRegionsFeedback.setText("Num. Regions: " + str(int(temp) * int(in_data['num_wedges']))) in_data['num_rings'] = int(temp) else: self.lineEdit_numRings.clear() def update_image_dpi(self): """ Update the value for the image dpi (dot per inch) in the user interface main screen that serves as feedback. :return: None """ global in_data if self.is_int_inbound(self.lineEdit_imageDPI.text(), 1200, 4800): temp = int(self.lineEdit_imageDPI.text()) self.label_imageDPIFeedback.setText("Image DPI: " + str(temp)) in_data['img_dpi'] = temp else: self.lineEdit_imageDPI.clear() def start_button_func(self): """ Sets what the start button will do when it is pressed. It starts the fiber density calculation and disables user input. The button is replaced by the stop button. :return: None """ global in_data, debounce # if program has not started if debounce is not 0: if (time.time() - debounce) < .30: return debounce = 0 # Test input data for being empty if(self.lineEdit_imagePath.text() is "" or self.lineEdit_intermediateStepPath.text() is "" or self.lineEdit_numWedges.text() is "" or self.lineEdit_numRings.text() is "" or self.lineEdit_numMeasurements.text() is "" or self.lineEdit_imageDPI.text() is ""): self.warning_message_box("Make sure all inputs are filled in.") return # Test numeric input if not self.is_int_inbound(self.lineEdit_numMeasurements.text(), 3, 100, self.label_numMeasurements.text()): return if not self.is_int_inbound(self.lineEdit_numWedges.text(), 3, 100, self.label_numWedges.text()): return if not self.is_int_inbound(self.lineEdit_numRings.text(), 1, 25, self.label_numRings.text()): return if not self.is_int_inbound(self.lineEdit_imageDPI.text(), 1200, 4800, self.label_imageDPI.text()): return # After all inputs have been validated self.disable_dashboard() # Save input data in in_data dictionary in_data['units'] = self.comboBox_units.currentText() in_data['num_measurement'] = int(self.lineEdit_numMeasurements.text())*4 in_data['img_dpi'] = int(self.lineEdit_imageDPI.text()) in_data['enhance'] = bool(self.checkBox_imageEnhancement.isChecked()) in_data['pixelMap'] = bool(self.checkBox_pixelMap.isChecked()) self.inputs_set_enabled(False) self.progressBar.show() self.label_progressBar.show() self.progressBar.setValue(1) self.stop_button.setEnabled(True) self.start_button.hide() # Start Sprout Controller for fiber density calculation self.myThread = Sprout.SproutController(self, in_data) try: self.myThread.start() self.myThread.progress.connect(self.progressBar.setValue) except: self.warning_message_box("Error while starting process.") def stop_button_func(self): """ Sets what the stop button will do when it is pressed or is called when the fiber density calculation is completed. It cancels the running session and enables user input. If running session is completed it enables the user input, and proceeds to display the dashboard (graphs, region density table, and measurement data). The button is replaced by the start button. :return: None """ global debounce # if program is currently in progress self.stop_button.setEnabled(False) self.stop_button.repaint() if not self.myThread.isFinished(): self.myThread.requestInterruption() self.myThread.wait() if self.progressBar.value() == 100: # if finished successfully # self.progressBar.setValue(100) self.dashboard_tab.setEnabled(True) self.tabWidget_2.setEnabled(True) self.graphs_tab.setEnabled(True) self.region_density_tab.setEnabled(True) self.measurement_data_tab.setEnabled(True) # create graphs self.create_graphs() # create table self.create_table() # set measurement data self.display_measurement_data() self.tabWidget_1.setCurrentIndex(1) self.tabWidget_2.setCurrentIndex(0) self.inputs_set_enabled(True) self.progressBar.hide() self.label_progressBar.hide() self.progressBar.setValue(0) self.start_button.show() debounce = time.time() def inputs_set_enabled(self, val: bool): """ Enable or disable the options presented in the home screen depending on input parameter: val. :param val: True to anabel all the options in the home screen and False to disable. :return: None """ self.browse_button_1.setEnabled(val) self.browse_button_2.setEnabled(val) self.comboBox_units.setEnabled(val) self.lineEdit_numMeasurements.setEnabled(val) self.lineEdit_numWedges.setEnabled(val) self.lineEdit_numRings.setEnabled(val) self.lineEdit_imageDPI.setEnabled(val) self.checkBox_imageEnhancement.setEnabled(val) self.checkBox_pixelMap.setEnabled(val) self.label_imagePath.setEnabled(val) self.label_intermediateStepPath.setEnabled(val) self.label_numMeasurements.setEnabled(val) self.label_numWedges.setEnabled(val) self.label_numRings.setEnabled(val) self.label_imageDPI.setEnabled(val) self.label_units.setEnabled(val) def create_graphs(self): """ Creates the graphs that will be displayed int the dashboard's Graphs tab. :return: None """ global default_comboBox_graph_item_count # Set Graphs ComboBox for x in range(self.comboBox_rings.count()): self.comboBox_rings.removeItem(default_comboBox_graph_item_count) for x in range(self.comboBox_wedges.count()): self.comboBox_wedges.removeItem(default_comboBox_graph_item_count) self.comboBox_rings.setCurrentIndex(0) self.comboBox_wedges.setCurrentIndex(0) # Ring Graph self.ring_chart = QChart() for x in range(len(self.densities)): ring_series = QLineSeries() for y in range(len(self.densities[x])-1): ring_series.append(y+1, self.densities[x][y]) self.ring_chart.addSeries(ring_series) if x < len(self.densities)-1: self.comboBox_rings.addItem("Ring " + str(x+1)) self.ring_chart.setTitle('Fiber Density VS Wedges') self.ring_chart.legend().hide() self.ring_chart.createDefaultAxes() self.ring_chart.axes(Qt.Horizontal)[0].setRange(1, len(self.densities[0])-1) self.ring_chart.axes(Qt.Vertical)[0].setRange(0, 1) self.ring_chart.axes(Qt.Horizontal)[0].setTitleText("Wedge Number") self.ring_chart.axes(Qt.Vertical)[0].setTitleText("Fiber Density") self.chartView = QChartView(self.ring_chart, self.widget_rings) self.chartView.resize(self.widget_rings.size()) # Wedges Graph self.wedge_chart = QChart() for y in range(len(self.densities[0])): if in_data['num_rings'] == 1: ring_series = QScatterSeries() else: ring_series = QLineSeries() for x in range(len(self.densities)-1): ring_series.append(x+1, self.densities[x][y]) self.wedge_chart.addSeries(ring_series) if y < len(self.densities[0])-1: self.comboBox_wedges.addItem("Wedge " + str(y+1)) self.wedge_chart.setTitle('Fiber Density VS Rings') self.wedge_chart.legend().hide() self.wedge_chart.createDefaultAxes() if (len(self.densities)) == 2: self.wedge_chart.axes(Qt.Horizontal)[0].setRange(0, 2) else: self.wedge_chart.axes(Qt.Horizontal)[0].setRange(1, len(self.densities)-1) self.wedge_chart.axes(Qt.Vertical)[0].setRange(0, 1) self.wedge_chart.axes(Qt.Horizontal)[0].setTitleText("Ring Number") self.wedge_chart.axes(Qt.Vertical)[0].setTitleText("Fiber Density") self.chartView = QChartView(self.wedge_chart, self.widget_wedges) self.chartView.resize(self.widget_wedges.size()) self.widget_rings.show() self.widget_wedges.show() if in_data['num_rings'] == 1: self.comboBox_rings.hide() else: self.comboBox_rings.show() self.comboBox_wedges.show() def filter_rings_graph(self): """ Filters the rings graph by: All(includes average), All Wedges(only rings are shown), Average(only average is shown), and individual rings(varies depending on number of rings). :return: None """ global default_comboBox_graph_item_count for x in range(len(self.ring_chart.series())): self.ring_chart.series()[x].show() if self.comboBox_rings.currentText() == "All Rings": for x in range(len(self.ring_chart.series()) - 1): self.ring_chart.series()[x].show() self.ring_chart.series()[len(self.ring_chart.series()) - 1].hide() elif self.comboBox_rings.currentText() == "Average": for x in range(len(self.ring_chart.series()) - 1): self.ring_chart.series()[x].hide() self.ring_chart.series()[len(self.ring_chart.series()) - 1].show() elif "Ring" in self.comboBox_rings.currentText(): for x in range(len(self.ring_chart.series())): self.ring_chart.series()[x].hide() self.ring_chart.series()[self.comboBox_rings.currentIndex() - default_comboBox_graph_item_count].show() def filter_wedges_graph(self): """ Filters the wedges graph by: All(includes average), All Wedges(only wedges are shown), Average(only average is shown), and individual wedges(varies depending on number of wedges). :return: None """ global default_comboBox_graph_item_count for x in range(len(self.wedge_chart.series())): self.wedge_chart.series()[x].show() if self.comboBox_wedges.currentText() == "All Wedges": for x in range(len(self.wedge_chart.series()) - 1): self.wedge_chart.series()[x].show() self.wedge_chart.series()[len(self.wedge_chart.series()) - 1].hide() elif self.comboBox_wedges.currentText() == "Average": for x in range(len(self.wedge_chart.series()) - 1): self.wedge_chart.series()[x].hide() self.wedge_chart.series()[len(self.wedge_chart.series()) - 1].show() elif "Wedge" in self.comboBox_wedges.currentText(): for x in range(len(self.wedge_chart.series())): self.wedge_chart.series()[x].hide() self.wedge_chart.series()[self.comboBox_wedges.currentIndex() - default_comboBox_graph_item_count].show() def create_table(self): """ Creates the table that will be presented in the dashboard's Region Density tab based on fiber density calculations. :return: None """ i = 0 j = 0 column_name = [] row_name = [] self.tableWidget.setRowCount(len(self.densities)) self.tableWidget.setColumnCount(len(self.densities[0])) for ring in self.densities: for wedge in ring: item = QTableWidgetItem("{:.4f}".format(wedge)) item.setTextAlignment(Qt.AlignCenter) # if average of average if j == (len(self.densities[0])-1) and i == (len(self.densities)-1): font = QFont() font.setBold(True) item.setFont(font) self.tableWidget.setItem(i, j, item) j += 1 j = 0 i += 1 for x in range(len(self.densities[0])): if x == len(self.densities[0]) - 1: column_name.append("Average") else: column_name.append("Wedge " + str(x + 1)) for y in range(len(self.densities)): if y == len(self.densities) - 1: row_name.append("Average") else: row_name.append("Ring " + str(y + 1)) self.tableWidget.setHorizontalHeaderLabels(column_name) self.tableWidget.setVerticalHeaderLabels(row_name) self.tableWidget.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers) def display_measurement_data(self): """ Manages all output data related to the measurement data that is presented in the dashboard Measurement Data. :return: None """ self.lineEdit_area.setText(str("{:.4f}".format(self.measurement_data[0])) + " " + in_data['units'] + "^2") self.lineEdit_avgOuterDiameter.setText(str("{:.4f}".format(self.measurement_data[1])) + " " + in_data['units']) self.lineEdit_avgInnerDiameter.setText(str("{:.4f}".format(self.measurement_data[2])) + " " + in_data['units']) self.lineEdit_AverageT.setText(str("{:.4f}".format(self.measurement_data[3])) + " " + in_data['units']) self.lineEdit_centroid_x.setText(str("{:.4f}".format(self.measurement_data[4])) + " " + in_data['units']) self.lineEdit_centroid_y.setText(str("{:.4f}".format(self.measurement_data[5])) + " " + in_data['units']) self.lineEdit_momentOfInertia_x.setText(str("{:.4f}".format(self.measurement_data[6])) + " " + in_data['units'] + "^4") self.lineEdit_momentOfInertia_y.setText(str("{:.4f}".format(self.measurement_data[7])) + " " + in_data['units'] + "^4") self.lineEdit_productOfInertia.setText(str("{:.4f}".format(self.measurement_data[8])) + " " + (in_data['units']) + "^4") def is_int_inbound(self, ui_in: str, lower: int, upper: int, ui_in_name: str = None): """ Test if user input is in specified upper and lower bound, including upper and lower bound value. :param ui_in: user input for value that will be tested :param lower: lowest value that ui_in can have to return True :param upper: highest value that ui_in can hava to return True :param ui_in_name: user input label name that is used in popup messages for users to relate error :return: True if ui_in is in upper and lower bound (lower <= ui_in <= upper) otherwise False """ if not (str.isdigit(ui_in)) or int(ui_in) > upper or int(ui_in) < lower: if ui_in_name is not None: self.warning_message_box(str(ui_in_name) + "\nPlease input a number from " + str(lower) + " to " + str(upper)) return False else: return True def warning_message_box(self, message): """ Display a popup message box to inform users of error. :param message: Message to be displayed in the popup message :return: """ mbox = QMessageBox.critical(self, "Warning!!", message) if mbox == QMessageBox.Ok: self.lineEdit_numMeasurements.setFocus(0) def show_save_files(self): """ Call to displays the popup for the Save Popup Window. :return: None """ self.windowModality() self.save_window_ui.lineEdit_filePath.setText(os.getcwd()) self.save_window_ui.show() self.save_window_ui.raise_() def progress_change(self): """ Signals the user interface when process is completed or wen error occurs. :return: None """ if self.progressBar.value() == 2: self.stop_button_func() self.warning_message_box(str(self.error_message)) elif self.progressBar.value() == 100: self.densities = DM.get_fiber_density_average() self.measurement_data = DM.get_dimensional_measurements() self.stop_button_func()
class MyWidget(QWidget): def __init__(self): super().__init__() uic.loadUi('ws_ui.ui', self) self.con = sqlite3.connect("ws_database.db") self.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.create_linechart() self.tmp, self.hmd, self.prs = 0, 0, 0 self.make_measure() self.measure_timer = QTimer(self) self.measure_timer.setInterval(MEASURE_FREQUENCIES * 1000) self.measure_timer.timeout.connect(self.make_measure) self.measure_timer.start() self.update_timer = QTimer(self) self.update_timer.setInterval(1000) self.update_timer.timeout.connect(self.update_labels) self.update_timer.start() self.update_labels() def quit(self): self.destroy() quit() def make_measure(self): sns = sensor_measure() if sns[0] != ERROR_CODE: self.tmp = sns[0] else: print('tmp error') if sns[1] != ERROR_CODE: self.hmd = sns[1] else: print('hmd error') if sns[2] != ERROR_CODE: self.prs = sns[2] else: print('prs error') time = int(dt.datetime.now().timestamp()) req = """ INSERT INTO short_term_data(tmp, hmd, prs, time_from_epoch) VALUES(?,?,?,?) """ self.con.execute(req, (self.tmp, self.hmd, self.prs, time)) self.con.commit() self.update_linechart() def update_labels(self): deg = u'\N{DEGREE SIGN}' hpa = 'ʰᴾᵃ' self.time_label.setText(dt.datetime.now().strftime('%H:%M')) self.tmp_label.setText('{} {}C'.format(self.tmp, deg)) self.hmd_label.setText('{} %'.format(self.hmd)) self.prs_label.setText('{} {}'.format(self.prs, hpa)) def create_linechart(self): self.chart = QChart() self.chart.legend().hide() self.series = QLineSeries() self.axisValue = QValueAxis() self.axisCurrentTime = QValueAxis() self.axisTime = QDateTimeAxis() self.axisTime.setFormat("hh:mm") self.chartview = QChartView(self.chart, self.groupBox) self.chartview.resize(540, 460) self.chartview.move(0, 0) self.chartview.setRenderHint(QPainter.Antialiasing) def update_linechart(self): if self.axisTime in self.chart.axes(): self.chart.removeAxis(self.axisTime) if self.axisCurrentTime in self.chart.axes(): self.chart.removeAxis(self.axisCurrentTime) if self.axisValue in self.chart.axes(): self.chart.removeAxis(self.axisValue) if self.series in self.chart.series(): self.chart.removeSeries(self.series) self.series.clear() self.axisValue.setMax(50) self.axisValue.setMin(-50) req = """ SELECT tmp, time_from_epoch FROM short_term_data WHERE (time_from_epoch - ?) < 86400 AND NOT tmp = ? """ cur = self.con.cursor() result = list(cur.execute(req, (int(dt.datetime.now().timestamp()), ERROR_CODE))) for measure in result: self.series.append(measure[1] * 1000, measure[0]) self.axisTime.setMin(QDateTime.fromMSecsSinceEpoch(int(dt.datetime.now().timestamp()) * 1000 - 86390000)) self.axisTime.setMax(QDateTime.fromMSecsSinceEpoch(int(dt.datetime.now().timestamp()) * 1000)) self.chart.addSeries(self.series) self.chart.addAxis(self.axisTime, Qt.AlignBottom) self.series.attachAxis(self.axisTime) self.chart.addAxis(self.axisValue, Qt.AlignLeft) self.series.attachAxis(self.axisValue) self.chart.setTitle('Температура')