def __init__(self, parent=QWidget.find(rt.windows.getMAXHWND())): super(PyMaxDialog, self).__init__(parent) self.setWindowTitle('Progress') main_layout = QVBoxLayout() label = QLabel("Progress so far") main_layout.addWidget(label) # progress bar progb = QProgressBar() progb.minimum = MINRANGE progb.maximum = MAXRANGE main_layout.addWidget(progb) # abort button btn = QPushButton("abort") main_layout.addWidget(btn) self.setLayout(main_layout) self.resize(250, 100) # start the worker self.worker = Worker() self.worker.progress.connect(progb.setValue) self.worker.start() # connect abort button btn.clicked.connect(self.worker.abort)
class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setWindowTitle("A Company Login Windows") self.setGeometry(300,250,400,300) #self.CreateStatusBar() self.CreateProgressBar() #self.showProgress() def CreateStatusBar(self): self.myStatusBar = QStatusBar() self.myStatusBar.showMessage('Ready',0) self.setStatusBar(self.myStatusBar) def CreateProgressBar(self): self.myStatusBar = QStatusBar() self.progressBar = QProgressBar() self.statusLabel = QLabel("Showing Progress") #self.finishLabel = QLabel("Finish Progress") self.progressBar.setMinimum(0) self.progressBar.setMaximum(100) self.progressBar.setValue(0) self.myStatusBar.addWidget(self.statusLabel,1) self.myStatusBar.addWidget(self.progressBar,2) #self.myStatusBar.addWidget(self.finishLabel,3) self.setStatusBar(self.myStatusBar) def showProgress(self): while(self.progressBar.value() < self.progressBar.maximum()): self.progressBar.setValue(self.progressBar.value() + 10) time.sleep(1) self.statusLabel.setText('Ready')
class TrainingConsoleWidget(QWidget): """The TrainingConsoleWidget provides a widget for controlling the training status of a model, in a simple way. (Analog of the console output in Keras, but with a few more options). It also can save and show an history of the last trained models. """ training_started = Signal() training_stopped = Signal() class TrainingStatus(Enum): Running = 1 Stopped = 2 Not_Compiled = 3 def __init__(self, parent: "QWidget" = None): super().__init__(parent) # Components self._pretrained_model: Optional["keras.models.Model"] = None self._ttv: Optional["TTVSets"] = None self._hyperparameters: Optional[Dict] = None self._callbacks: List[Callback] = [] self._trained_model: Optional["keras.models.Model"] = None # Widgets self._start_training_button = QPushButton("Start training") self._stop_training_button = QPushButton("Stop training") self._buttons_layout = QHBoxLayout() self._buttons_layout.addWidget(self._start_training_button) self._buttons_layout.addWidget(self._stop_training_button) self._status_label = QLabel() self._batch_progress_bar = QProgressBar() self._epoch_progress_bar = QProgressBar() self.training_output_textbox = QPlainTextEdit() self.training_output_textbox.setReadOnly(True) console_output_group = QGroupBox("Console output") console_output_layout = QVBoxLayout() console_output_layout.setContentsMargins(0, 0, 0, 0) console_output_layout.addWidget(self.training_output_textbox) console_output_group.setLayout(console_output_layout) self._main_layout = QVBoxLayout() self._main_layout.addLayout(self._buttons_layout) self._main_layout.addWidget(self._status_label, Qt.AlignRight) self._main_layout.addWidget(console_output_group) self._main_layout.addWidget(self._batch_progress_bar) self._main_layout.addWidget(self._epoch_progress_bar) self.setLayout(self._main_layout) # Connections self._start_training_button.clicked.connect(self.start_training) self._stop_training_button.clicked.connect(self.stop_training) # Inner workings self.training_status = self.TrainingStatus.Not_Compiled self._training_thread = None @property def training_status(self): """Returns the current status of the training (Running, Stopped...)""" return self._training_status @training_status.setter def training_status(self, new_status): """Changes the training status. Doing so will update the interface accordingly. """ self._training_status = new_status if self._training_status == self.TrainingStatus.Running: self._start_training_button.setEnabled(False) self._stop_training_button.setEnabled(True) self._status_label.setText("Running") self.start_training elif self._training_status == self.TrainingStatus.Stopped: self._start_training_button.setEnabled(True) self._stop_training_button.setEnabled(False) self._status_label.setText("Stopped") elif self._training_status == self.TrainingStatus.Not_Compiled: self._start_training_button.setEnabled(True) self._stop_training_button.setEnabled(False) self._status_label.setText("Not Compiled") def set_ttv(self, ttv: "TTVSets"): """Sets the Train/Test/Validation models used for training.""" self._ttv = ttv def set_pretrained_model(self, pretrained_model: "Model"): """Sets a new pretrained model for training.""" self._pretrained_model = pretrained_model self.training_status = self.TrainingStatus.Not_Compiled def set_hyperparameters(self, hyperparameters: Dict): """Sets new hyperparameters for training.""" self._hyperparameters = hyperparameters self.training_status = self.TrainingStatus.Not_Compiled def set_callbacks(self, callbacks: List[Callback]): self._callbacks = callbacks def get_trained_model(self): """Returns the model after it has been trained.""" return self._trained_model def compile_model(self): """Compile the model with the passed hyperparameters. The dataset is needed for the input shape.""" LOGGER.info("Starting to compile the model...") if not self._is_input_ready(): return False # Create a new model based on the pretrained one, but with a new InputLayer # compatible with the dataset if self._pretrained_model.layers[0].__class__.__name__ != "InputLayer": input_layer = Input(self._ttv.train.input_shape) output = self._pretrained_model(input_layer) self._trained_model = Model(input_layer, output) else: self._trained_model = self._pretrained_model try: self._trained_model.compile( optimizer=self._hyperparameters["optimizer"], loss=self._hyperparameters["loss_function"], metrics=["accuracy"], ) self._trained_model.summary() LOGGER.info("Model compiled successfully!!") self.training_status = self.TrainingStatus.Stopped return True except Exception as err: LOGGER.exception("Model Compiling error: ", err) self.training_output_textbox.setPlainText( "> Error while compiling the model:\n", str(err)) return False def start_training(self): """Starts the training on a new thread.""" if self.training_status == self.TrainingStatus.Not_Compiled: successfully_compiled = self.compile_model() if not successfully_compiled: LOGGER.info("Couldn't compile model. Training not started.") return total_train_batches = len(self._ttv.train) total_train_epochs = self._hyperparameters["epochs"] self._batch_progress_bar.setMaximum(total_train_batches) self._epoch_progress_bar.setMaximum(total_train_epochs) self._epoch_progress_bar.setValue(0) self.training_output_textbox.clear() def epoch_begin_update(epoch: int, logs): message = f"==== Epoch {epoch + 1}/{total_train_epochs} ====" LOGGER.info(message) self.training_output_textbox.appendPlainText(message) self._epoch_progress_bar.setValue(epoch) def batch_end_update(batch: int, logs): # Update progress self._batch_progress_bar.setValue(batch) # Log metrics on console message = f"{batch}/{total_train_batches}" for (k, v) in list(logs.items()): message += f" - {k}: {v:.4f}" LOGGER.info(message) self.training_output_textbox.appendPlainText(message) def train_end_update(logs): # Put the progress bar at 100% when the training ends self._batch_progress_bar.setValue( self._batch_progress_bar.maximum()) self._epoch_progress_bar.setValue( self._epoch_progress_bar.maximum()) # Stop the training self.stop_training() # Connect callbacks signals_callback = SignalsCallback() signals_callback.epoch_begin.connect(epoch_begin_update) signals_callback.train_batch_end.connect(batch_end_update) signals_callback.train_end.connect(train_end_update) self.training_stopped.connect(signals_callback.stop_model) print(self._callbacks) # log_dir = "logs/fit/" + datetime.datetime.now().strftime("%Y_%m_%d-%H_%M_%S") # tb = program.TensorBoard() # tb.configure(argv=[None, "--logdir", log_dir]) # url = tb.launch() # print("Launched Tensorboard instance in:", url) # tf_callback=tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1) # tf_callback.set_model(self._trained_model) # Start training self._fit_worker = FitWorker( self._trained_model, self._ttv.train, self._hyperparameters, callbacks=[signals_callback] + self._callbacks, ) self.training_status = self.TrainingStatus.Running self._fit_worker.start() self.training_started.emit() def stop_training(self): """Stops the training.""" self.training_status = self.TrainingStatus.Stopped self.training_stopped.emit() def _is_input_ready(self) -> bool: """Checks if the input values used for training (model, dataset, hyperparameters...) are valid.""" message = "" if not self._ttv.train: message += "> Training dataset not specified\n" if not self._pretrained_model: message += "> Model not specified.\n" if not self._hyperparameters: message += "> Hyperparameters not specified.\n" if message: self.training_output_textbox.setPlainText(message) LOGGER.info(message) return False return True def sizeHint(self) -> "QSize": """Returns the expected size of the widget.""" return QSize(500, 300) def __reduce__(self): return (TrainingConsoleWidget, ())
class TabDisplays(QTabWidget): def __init__(self, parent=None): super(TabDisplays, self).__init__(parent) # Initialize logging logging_conf_file = os.path.join(os.path.dirname(__file__), 'cfg/aecgviewer_aecg_logging.conf') logging.config.fileConfig(logging_conf_file) self.logger = logging.getLogger(__name__) self.studyindex_info = aecg.tools.indexer.StudyInfo() self.validator = QWidget() self.studyinfo = QWidget() self.statistics = QWidget() self.waveforms = QWidget() self.waveforms.setAccessibleName("Waveforms") self.scatterplot = QWidget() self.histogram = QWidget() self.trends = QWidget() self.xmlviewer = QWidget() self.xml_display = QTextEdit(self.xmlviewer) self.options = QWidget() self.aecg_display_area = QScrollArea() self.cbECGLayout = QComboBox() self.aecg_display = EcgDisplayWidget(self.aecg_display_area) self.aecg_display_area.setWidget(self.aecg_display) self.addTab(self.validator, "Study information") self.addTab(self.waveforms, "Waveforms") self.addTab(self.xmlviewer, "XML") self.addTab(self.options, "Options") self.setup_validator() self.setup_waveforms() self.setup_xmlviewer() self.setup_options() size = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) size.setHeightForWidth(False) self.setSizePolicy(size) # Initialized a threpool with 2 threads 1 for the GUI, 1 for long # tasks, so GUI remains responsive self.threadpool = QThreadPool() self.threadpool.setMaxThreadCount(2) self.validator_worker = None self.indexing_timer = QElapsedTimer() def setup_validator(self): self.directory_indexer = None # aecg.indexing.DirectoryIndexer() self.validator_layout_container = QWidget() self.validator_layout = QFormLayout() self.validator_form_layout = QFormLayout( self.validator_layout_container) self.validator_grid_layout = QGridLayout() self.study_info_file = QLineEdit() self.study_info_file.setToolTip("Study index file") self.study_info_description = QLineEdit() self.study_info_description.setToolTip("Description") self.app_type = QLineEdit() self.app_type.setToolTip("Application type (e.g., NDA, IND, BLA, IDE)") self.app_num = QLineEdit() self.app_num.setToolTip("Six-digit application number") self.app_num.setValidator(QIntValidator(self.app_num)) self.study_id = QLineEdit() self.study_id.setToolTip("Study identifier") self.study_sponsor = QLineEdit() self.study_sponsor.setToolTip("Sponsor of the study") self.study_annotation_aecg_cb = QComboBox() self.study_annotation_aecg_cb.addItems( ["Rhythm", "Derived beat", "Holter-rhythm", "Holter-derived"]) self.study_annotation_aecg_cb.setToolTip( "Waveforms used to perform the ECG measurements (i.e., " "annotations)\n" "\tRhythm: annotations in a rhythm strip or discrete ECG " "extraction (e.g., 10-s strips)\n" "\tDerived beat: annotations in a representative beat derived " "from a rhythm strip\n" "\tHolter-rhythm: annotations in a the analysis window of a " "continuous recording\n" "\tHolter-derived: annotations in a representative beat derived " "from analysis window of a continuous recording\n") self.study_annotation_lead_cb = QComboBox() self.ui_leads = ["GLOBAL"] + aecg.STD_LEADS[0:12] +\ [aecg.KNOWN_NON_STD_LEADS[1]] + aecg.STD_LEADS[12:15] + ["Other"] self.study_annotation_lead_cb.addItems(self.ui_leads) self.study_annotation_lead_cb.setToolTip( "Primary analysis lead annotated per protocol. There could be " "annotations in other leads also, but only the primary lead should" " be selected here.\n" "Select global if all leads were used at the " "same time (e.g., superimposed on screen).\n" "Select other if the primary lead used is not in the list.") self.study_numsubjects = QLineEdit() self.study_numsubjects.setToolTip( "Number of subjects with ECGs in the study") self.study_numsubjects.setValidator( QIntValidator(self.study_numsubjects)) self.study_aecgpersubject = QLineEdit() self.study_aecgpersubject.setToolTip( "Number of scheduled ECGs (or analysis windows) per subject as " "specified in the study protocol.\n" "Enter average number of ECGs " "per subject if the protocol does not specify a fixed number of " "ECGs per subject.") self.study_aecgpersubject.setValidator( QIntValidator(self.study_aecgpersubject)) self.study_numaecg = QLineEdit() self.study_numaecg.setToolTip( "Total number of aECG XML files in the study") self.study_numaecg.setValidator(QIntValidator(self.study_numaecg)) self.study_annotation_numbeats = QLineEdit() self.study_annotation_numbeats.setToolTip( "Minimum number of beats annotated in each ECG or analysis window" ".\nEnter 1 if annotations were done in the derived beat.") self.study_annotation_numbeats.setValidator( QIntValidator(self.study_annotation_numbeats)) self.aecg_numsubjects = QLineEdit() self.aecg_numsubjects.setToolTip( "Number of subjects found across the provided aECG XML files") self.aecg_numsubjects.setReadOnly(True) self.aecg_aecgpersubject = QLineEdit() self.aecg_aecgpersubject.setToolTip( "Average number of ECGs per subject found across the provided " "aECG XML files") self.aecg_aecgpersubject.setReadOnly(True) self.aecg_numaecg = QLineEdit() self.aecg_numaecg.setToolTip( "Number of aECG XML files found in the study aECG directory") self.aecg_numaecg.setReadOnly(True) self.subjects_less_aecgs = QLineEdit() self.subjects_less_aecgs.setToolTip( "Percentage of subjects with less aECGs than specified per " "protocol") self.subjects_less_aecgs.setReadOnly(True) self.subjects_more_aecgs = QLineEdit() self.subjects_more_aecgs.setToolTip( "Percentage of subjects with more aECGs than specified per " "protocol") self.subjects_more_aecgs.setReadOnly(True) self.aecgs_no_annotations = QLineEdit() self.aecgs_no_annotations.setToolTip( "Percentage of aECGs with no annotations") self.aecgs_no_annotations.setReadOnly(True) self.aecgs_less_qt_in_primary_lead = QLineEdit() self.aecgs_less_qt_in_primary_lead.setToolTip( "Percentage of aECGs with less QT intervals in the primary lead " "than specified per protocol") self.aecgs_less_qt_in_primary_lead.setReadOnly(True) self.aecgs_less_qts = QLineEdit() self.aecgs_less_qts.setToolTip( "Percentage of aECGs with less QT intervals than specified per " "protocol") self.aecgs_less_qts.setReadOnly(True) self.aecgs_annotations_multiple_leads = QLineEdit() self.aecgs_annotations_multiple_leads.setToolTip( "Percentage of aECGs with QT annotations in multiple leads") self.aecgs_annotations_multiple_leads.setReadOnly(True) self.aecgs_annotations_no_primary_lead = QLineEdit() self.aecgs_annotations_no_primary_lead.setToolTip( "Percentage of aECGs with QT annotations not in the primary lead") self.aecgs_annotations_no_primary_lead.setReadOnly(True) self.aecgs_with_errors = QLineEdit() self.aecgs_with_errors.setToolTip("Number of aECG files with errors") self.aecgs_with_errors.setReadOnly(True) self.aecgs_potentially_digitized = QLineEdit() self.aecgs_potentially_digitized.setToolTip( "Number of aECG files potentially digitized (i.e., with more than " "5% of samples missing)") self.aecgs_potentially_digitized.setReadOnly(True) self.study_dir = QLineEdit() self.study_dir.setToolTip("Directory containing the aECG files") self.study_dir_button = QPushButton("...") self.study_dir_button.clicked.connect(self.select_study_dir) self.study_dir_button.setToolTip("Open select directory dialog") self.validator_form_layout.addRow("Application Type", self.app_type) self.validator_form_layout.addRow("Application Number", self.app_num) self.validator_form_layout.addRow("Study name/ID", self.study_id) self.validator_form_layout.addRow("Sponsor", self.study_sponsor) self.validator_form_layout.addRow("Study description", self.study_info_description) self.validator_form_layout.addRow("Annotations in", self.study_annotation_aecg_cb) self.validator_form_layout.addRow("Annotations primary lead", self.study_annotation_lead_cb) self.validator_grid_layout.addWidget(QLabel(""), 0, 0) self.validator_grid_layout.addWidget( QLabel("Per study protocol or report"), 0, 1) self.validator_grid_layout.addWidget(QLabel("Found in aECG files"), 0, 2) self.validator_grid_layout.addWidget(QLabel("Number of subjects"), 1, 0) self.validator_grid_layout.addWidget(self.study_numsubjects, 1, 1) self.validator_grid_layout.addWidget(self.aecg_numsubjects, 1, 2) self.validator_grid_layout.addWidget( QLabel("Number of aECG per subject"), 2, 0) self.validator_grid_layout.addWidget(self.study_aecgpersubject, 2, 1) self.validator_grid_layout.addWidget(self.aecg_aecgpersubject, 2, 2) self.validator_grid_layout.addWidget(QLabel("Total number of aECG"), 3, 0) self.validator_grid_layout.addWidget(self.study_numaecg, 3, 1) self.validator_grid_layout.addWidget(self.aecg_numaecg, 3, 2) self.validator_grid_layout.addWidget( QLabel("Number of beats per aECG"), 4, 0) self.validator_grid_layout.addWidget(self.study_annotation_numbeats, 4, 1) self.validator_grid_layout.addWidget( QLabel("Subjects with fewer ECGs"), 5, 1) self.validator_grid_layout.addWidget(self.subjects_less_aecgs, 5, 2) self.validator_grid_layout.addWidget(QLabel("Subjects with more ECGs"), 6, 1) self.validator_grid_layout.addWidget(self.subjects_more_aecgs, 6, 2) self.validator_grid_layout.addWidget( QLabel("aECGs without annotations"), 7, 1) self.validator_grid_layout.addWidget(self.aecgs_no_annotations, 7, 2) self.validator_grid_layout.addWidget( QLabel("aECGs without expected number of QTs in primary lead"), 8, 1) self.validator_grid_layout.addWidget( self.aecgs_less_qt_in_primary_lead, 8, 2) self.validator_grid_layout.addWidget( QLabel("aECGs without expected number of QTs"), 9, 1) self.validator_grid_layout.addWidget(self.aecgs_less_qts, 9, 2) self.validator_grid_layout.addWidget( QLabel("aECGs annotated in multiple leads"), 10, 1) self.validator_grid_layout.addWidget( self.aecgs_annotations_multiple_leads, 10, 2) self.validator_grid_layout.addWidget( QLabel("aECGs with annotations not in primary lead"), 11, 1) self.validator_grid_layout.addWidget( self.aecgs_annotations_no_primary_lead, 11, 2) self.validator_grid_layout.addWidget(QLabel("aECGs with errors"), 12, 1) self.validator_grid_layout.addWidget(self.aecgs_with_errors, 12, 2) self.validator_grid_layout.addWidget( QLabel("Potentially digitized aECGs"), 13, 1) self.validator_grid_layout.addWidget(self.aecgs_potentially_digitized, 13, 2) self.validator_form_layout.addRow(self.validator_grid_layout) tmp = QHBoxLayout() tmp.addWidget(self.study_dir) tmp.addWidget(self.study_dir_button) self.validator_form_layout.addRow("Study aECGs directory", tmp) self.validator_form_layout.addRow("Study index file", self.study_info_file) self.validator_layout.addWidget(self.validator_layout_container) self.validator_effective_dirs = QLabel("") self.validator_effective_dirs.setWordWrap(True) self.validator_layout.addWidget(self.validator_effective_dirs) self.val_button = QPushButton("Generate/update study index") self.val_button.clicked.connect(self.importstudy_dialog) self.validator_layout.addWidget(self.val_button) self.cancel_val_button = QPushButton("Cancel study index generation") self.cancel_val_button.clicked.connect(self.cancel_validator) self.cancel_val_button.setEnabled(False) self.validator_layout.addWidget(self.cancel_val_button) self.validator_pl = QLabel("") self.validator_layout.addWidget(self.validator_pl) self.validator_pb = QProgressBar() self.validator_layout.addWidget(self.validator_pb) self.validator.setLayout(self.validator_layout) self.stop_indexing = False self.lastindexing_starttime = None self.update_validator_effective_dirs() def effective_aecgs_dir(self, navwidget, silent=False): aecgs_effective_dir = self.study_dir.text() if navwidget.project_loaded != '': # Path specified in the GUI potential_aecgs_dirs = [self.study_dir.text()] # StudyDir path from current working directory potential_aecgs_dirs += [self.studyindex_info.StudyDir] # StudyDir path from directory where the index is located potential_aecgs_dirs += [ os.path.join(os.path.dirname(navwidget.project_loaded), self.studyindex_info.StudyDir) ] # StudyDir replaced with the directory where the index is located potential_aecgs_dirs += [os.path.dirname(navwidget.project_loaded)] dir_found = False # Get xml and zip filenames from first element in the index aecg_xml_file = navwidget.data_index["AECGXML"][0] zipfile = "" if aecg_xml_file != "": zipfile = navwidget.data_index["ZIPFILE"][0] for p in potential_aecgs_dirs: testfn = os.path.join(p, aecg_xml_file) if zipfile != "": testfn = os.path.join(p, zipfile) if os.path.isfile(testfn): dir_found = True aecgs_effective_dir = p break if not silent: if not dir_found: QMessageBox.warning( self, f"Study aECGs directory not found", f"The following paths were checked:" f"{[','.join(p) for p in potential_aecgs_dirs]} and " f"none of them is valid") elif p != self.study_dir.text(): QMessageBox.warning( self, f"Study aECGs directory not found", f"The path specified in the study aECGs directory is " f"not valid and {p} is being used instead. Check and " f"update the path in Study aECGs directory textbox if " f"the suggested path is not the adequate path") return aecgs_effective_dir def update_validator_effective_dirs(self): msg = f"Working directory: {os.getcwd()}" if self.parent() is not None: if isinstance(self.parent(), QSplitter): navwidget = self.parent().parent() else: # Tabs widget has not been allocated the QSplitter yet navwidget = self.parent() project_loaded = navwidget.project_loaded if project_loaded != '': msg = f"{msg}\nLoaded project index: "\ f"{navwidget.project_loaded}" effective_aecgs_path = self.effective_aecgs_dir(navwidget) msg = f"{msg}\nEffective study aECGs directory: "\ f"{effective_aecgs_path}" else: msg = f"{msg}\nLoaded project index: None" else: msg = f"{msg}\nLoaded project index: None" self.validator_effective_dirs.setText(msg) def load_study_info(self, fileName): self.study_info_file.setText(fileName) try: study_info = pd.read_excel(fileName, sheet_name="Info") self.studyindex_info = aecg.tools.indexer.StudyInfo() self.studyindex_info.__dict__.update( study_info.set_index("Property").transpose().reset_index( drop=True).to_dict('index')[0]) sponsor = "" description = "" if self.studyindex_info.Sponsor is not None and\ isinstance(self.studyindex_info.Sponsor, str): sponsor = self.studyindex_info.Sponsor if self.studyindex_info.Description is not None and\ isinstance(self.studyindex_info.Description, str): description = self.studyindex_info.Description self.study_sponsor.setText(sponsor) self.study_info_description.setText(description) self.app_type.setText(self.studyindex_info.AppType) self.app_num.setText(f"{int(self.studyindex_info.AppNum):06d}") self.study_id.setText(self.studyindex_info.StudyID) self.study_numsubjects.setText(str(self.studyindex_info.NumSubj)) self.study_aecgpersubject.setText( str(self.studyindex_info.NECGSubj)) self.study_numaecg.setText(str(self.studyindex_info.TotalECGs)) anns_in = self.studyindex_info.AnMethod.upper() idx = 0 if anns_in == "RHYTHM": idx = 0 elif anns_in == "DERIVED": idx = 1 elif anns_in == "HOLTER_RHYTHM": idx = 2 elif anns_in == "HOLTER_MEDIAN_BEAT": idx = 3 else: idx = int(anns_in) - 1 self.study_annotation_aecg_cb.setCurrentIndex(idx) the_lead = self.studyindex_info.AnLead idx = self.study_annotation_lead_cb.findText(str(the_lead)) if idx == -1: idx = self.study_annotation_lead_cb.findText("MDC_ECG_LEAD_" + str(the_lead)) if idx == -1: idx = int(the_lead) self.study_annotation_lead_cb.setCurrentIndex(idx) self.study_annotation_numbeats.setText( str(self.studyindex_info.AnNbeats)) if self.studyindex_info.StudyDir == "": self.studyindex_info.StudyDir = os.path.dirname(fileName) self.study_dir.setText(self.studyindex_info.StudyDir) self.update_validator_effective_dirs() self.setCurrentWidget(self.validator) except Exception as ex: QMessageBox.critical( self, "Import study error", "Error reading the study information file: '" + fileName + "'") def setup_waveforms(self): wflayout = QVBoxLayout() # ECG plot layout selection box self.cbECGLayout.addItems( ['12-lead stacked', '3x4 + lead II rhythm', 'Superimposed']) self.cbECGLayout.currentIndexChanged.connect( self.ecgplotlayout_changed) # Zoom buttons blayout = QHBoxLayout() pb_zoomin = QPushButton() pb_zoomin.setText("Zoom +") pb_zoomin.clicked.connect(self.zoom_in) pb_zoomreset = QPushButton() pb_zoomreset.setText("Zoom 1:1") pb_zoomreset.clicked.connect(self.zoom_reset) pb_zoomout = QPushButton() pb_zoomout.setText("Zoom -") pb_zoomout.clicked.connect(self.zoom_out) blayout.addWidget(self.cbECGLayout) blayout.addWidget(pb_zoomout) blayout.addWidget(pb_zoomreset) blayout.addWidget(pb_zoomin) wflayout.addLayout(blayout) # Add QScrollArea to main layout of waveforms tab self.aecg_display_area.setWidgetResizable(False) wflayout.addWidget(self.aecg_display_area) self.waveforms.setLayout(wflayout) size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) size.setHeightForWidth(False) self.aecg_display_area.setSizePolicy(size) self.waveforms.setSizePolicy(size) def setup_xmlviewer(self): wf_layout = QHBoxLayout() wf_layout.addWidget(self.xml_display) self.xmlviewer.setLayout(wf_layout) def setup_options(self): self.options_layout = QFormLayout() self.aecg_schema_filename = QLineEdit(aecg.get_aecg_schema_location()) self.options_layout.addRow("aECG XML schema path", self.aecg_schema_filename) self.save_index_every_n_aecgs = QSpinBox() self.save_index_every_n_aecgs.setMinimum(0) self.save_index_every_n_aecgs.setMaximum(50000) self.save_index_every_n_aecgs.setValue(0) self.save_index_every_n_aecgs.setSingleStep(100) self.save_index_every_n_aecgs.setSuffix(" aECGs") self.save_index_every_n_aecgs.setToolTip( "Set o 0 to save the study index file only after its generation " "is completed.\nOtherwise, the file is saved everytime the " " specified number of ECGs have been appended to the index.") self.options_layout.addRow("Save index every ", self.save_index_every_n_aecgs) self.save_all_intervals_cb = QCheckBox("") self.save_all_intervals_cb.setChecked(False) self.options_layout.addRow("Save individual beat intervals", self.save_all_intervals_cb) self.parallel_processing_cb = QCheckBox("") self.parallel_processing_cb.setChecked(True) self.options_layout.addRow("Parallel processing of files", self.parallel_processing_cb) self.options.setLayout(self.options_layout) def zoom_in(self): self.aecg_display.apply_zoom(self.aecg_display.zoom_factor + 0.1) def zoom_out(self): self.aecg_display.apply_zoom(self.aecg_display.zoom_factor - 0.1) def zoom_reset(self): self.aecg_display.apply_zoom(1.0) def ecgplotlayout_changed(self, i): self.aecg_display.update_aecg_plot( ecg_layout=aecg.utils.ECG_plot_layout(i + 1)) def update_search_progress(self, i, n): self.validator_pl.setText( f"Searching aECGs in directory ({n} XML files found)") def update_progress(self, i, n): j = i m = n if i <= 1: j = 1 if self.validator_pb.value() > 0: j = self.validator_pb.value() + 1 m = self.validator_pb.maximum() running_time = self.indexing_timer.elapsed() * 1e-3 # in seconds time_per_item = running_time / j # reamining = seconds per item so far * total pending items to process remaining_time = time_per_item * (m - j) eta = datetime.datetime.now() +\ datetime.timedelta(seconds=round(remaining_time, 0)) self.validator_pl.setText( f"Validating aECG {j}/{m} | " f"Execution time: " f"{str(datetime.timedelta(0,seconds=round(running_time)))} | " f"{round(1/time_per_item,2)} aECGs per second | " f"ETA: {eta.isoformat(timespec='seconds')}") self.validator_pb.setValue(j) if self.save_index_every_n_aecgs.value() > 0 and\ len(self.directory_indexer.studyindex) % \ self.save_index_every_n_aecgs.value() == 0: self.save_validator_results( pd.concat(self.directory_indexer.studyindex, ignore_index=True)) def save_validator_results(self, res): if res.shape[0] > 0: self.studyindex_info = aecg.tools.indexer.StudyInfo() self.studyindex_info.StudyDir = self.study_dir.text() self.studyindex_info.IndexFile = self.study_info_file.text() self.studyindex_info.Sponsor = self.study_sponsor.text() self.studyindex_info.Description =\ self.study_info_description.text() self.studyindex_info.Date = self.lastindexing_starttime.isoformat() self.studyindex_info.End_date = datetime.datetime.now().isoformat() self.studyindex_info.Version = aecg.__version__ self.studyindex_info.AppType = self.app_type.text() self.studyindex_info.AppNum = f"{int(self.app_num.text()):06d}" self.studyindex_info.StudyID = self.study_id.text() self.studyindex_info.NumSubj = int(self.study_numsubjects.text()) self.studyindex_info.NECGSubj = int( self.study_aecgpersubject.text()) self.studyindex_info.TotalECGs = int(self.study_numaecg.text()) anmethod = aecg.tools.indexer.AnnotationMethod( self.study_annotation_aecg_cb.currentIndex()) self.studyindex_info.AnMethod = anmethod.name self.studyindex_info.AnLead =\ self.study_annotation_lead_cb.currentText() self.studyindex_info.AnNbeats = int( self.study_annotation_numbeats.text()) # Calculate stats study_stats = aecg.tools.indexer.StudyStats( self.studyindex_info, res) # Save to file aecg.tools.indexer.save_study_index(self.studyindex_info, res, study_stats) validator_data_ready = Signal() def save_validator_results_and_load_index(self, res): self.save_validator_results(res) self.validator_data_ready.emit() def indexer_validator_results(self, res): self.studyindex_df = pd.concat([self.studyindex_df, res], ignore_index=True) def subindex_thread_complete(self): return def index_directory_thread_complete(self): tmp = self.validator_pl.text().replace("ETA:", "Completed: ").replace( "Validating", "Validated") self.validator_pl.setText(tmp) self.val_button.setEnabled(True) self.cancel_val_button.setEnabled(False) self.validator_layout_container.setEnabled(True) def index_directory(self, progress_callback): self.lastindexing_starttime = datetime.datetime.now() self.indexing_timer.start() studyindex_df = [] n_cores = os.cpu_count() aecg_schema = None if self.aecg_schema_filename.text() != "": aecg_schema = self.aecg_schema_filename.text() if self.parallel_processing_cb.isChecked(): studyindex_df = self.directory_indexer.index_directory( self.save_all_intervals_cb.isChecked(), aecg_schema, n_cores, progress_callback) else: studyindex_df = self.directory_indexer.index_directory( self.save_all_intervals_cb.isChecked(), aecg_schema, 1, progress_callback) return studyindex_df def importstudy_dialog(self): dirName = os.path.normpath(self.study_dir.text()) if dirName != "": if os.path.exists(dirName): self.directory_indexer = aecg.indexing.DirectoryIndexer() self.directory_indexer.set_aecg_dir( dirName, self.update_search_progress) self.validator_pb.setMaximum(self.directory_indexer.num_files) self.validator_pb.reset() self.stop_indexing = False self.validator_layout_container.setEnabled(False) self.val_button.setEnabled(False) self.cancel_val_button.setEnabled(True) self.validator_worker = Worker(self.index_directory) self.validator_worker.signals.result.connect( self.save_validator_results_and_load_index) self.validator_worker.signals.finished.connect( self.index_directory_thread_complete) self.validator_worker.signals.progress.connect( self.update_progress) # Execute self.threadpool.start(self.validator_worker) else: QMessageBox.critical( self, "Directory not found", f"Specified study directory not found:\n{dirName}") else: QMessageBox.critical(self, "Import study error", "Study directory cannot be empty") def cancel_validator(self): self.cancel_val_button.setEnabled(False) self.stop_indexing = True self.directory_indexer.cancel_indexing = True self.threadpool.waitForDone(3000) self.val_button.setEnabled(True) def select_study_dir(self): cd = self.study_dir.text() if cd == "": cd = "." dir = QFileDialog.getExistingDirectory( self, "Open Directory", cd, QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if dir != "": self.study_dir.setText(dir)
class Window(QWidget): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.resize(400, 200) self._value = 0 self.setWindowTitle('Adobe Version Changer') self.setWindowIcon(QIcon('favicon.ico')) self.progressbar = QProgressBar(self) self.progressbar.setRange(0, 99) self.widget = QWidget(self) self.layout = QGridLayout(self.widget) self.label = QLabel('| Drop your file here |') self.layout.addWidget(self.label, 1, 23, 30, 10) self.layout.addWidget(self.progressbar, 2, 0, 1, 58) QToolTip.setFont(QFont('Helvetica', 20)) self.setToolTip( '''Drag and Drop into this window a <b>Premiere Project File or Motion Graphics File</b> to be converted. You will find a file named as the Project or Motion Graphic file with the extension <b>"_changed"</b> in that same location.''') self.setAcceptDrops(True) def progressStart(self): animation = QPropertyAnimation(self.progressbar, b'value', self) x = int(os.stat(self.userInput).st_size) / 20000 # print(x) animation.setDuration(x) animation.setLoopCount(1) animation.setKeyValueAt(0, self.progressbar.minimum()) animation.setKeyValueAt(0.1, 10) animation.setKeyValueAt(0.2, 30) animation.setKeyValueAt(0.5, 60) animation.setKeyValueAt(0.7, 80) animation.setKeyValueAt(1, self.progressbar.maximum()) animation.start(animation.DeleteWhenStopped) def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def dragEnterEvent(self, e): if e.mimeData().hasUrls(): e.acceptProposedAction() def dropEvent(self, e): for url in e.mimeData().urls(): self.userInput = url.toLocalFile() self.progressStart() func(self.userInput) self.label.setText("| Another file? |") # repeat return self.userInput def startprogressBar(self): self.progressBar.setVisible(True) if self.timer.isActive(): self.timer.stop() else: self.timer.start(100, self) def resetBar(self): self.step = 0 self.progressBar.setValue(0) def timerEvent(self, event): if self.step >= 100: self.timer.stop() return self.step += 1 self.progressBar.setValue(self.step)