Ejemplo n.º 1
0
class ViolinGUI(QMainWindow):
    """Main Window Widget for ViolinGUI."""
    style = {
        'title': 'QLabel {font-size: 18pt; font-weight: 600}',
        'header': 'QLabel {font-size: 12pt; font-weight: 520}',
        'label': 'QLabel {font-size: 10pt}',
        'button': 'QPushButton {font-size: 10pt}',
        'run button': 'QPushButton {font-size: 18pt; font-weight: 600}',
        'line edit': 'QLineEdit {font-size: 10pt}',
        'checkbox': 'QCheckBox {font-size: 10pt}',
        'drop down': 'QComboBox {font-size: 10pt}'
    }

    def __init__(self) -> None:
        """ViolinGUI Constructor. Defines all aspects of the GUI."""
        # ## Setup section
        # Inherits from QMainWindow
        super().__init__()
        self.rootdir = get_project_root()
        # QMainWindow basic properties
        self.setWindowTitle("SCOUTS - Violins")
        self.setWindowIcon(
            QIcon(
                os.path.abspath(os.path.join(self.rootdir, 'src',
                                             'scouts.ico'))))
        # Creates QWidget as QMainWindow's central widget
        self.page = QWidget(self)
        self.setCentralWidget(self.page)
        # Miscellaneous initialization values
        self.threadpool = QThreadPool()  # Threadpool for workers
        self.population_df = None  # DataFrame of whole population (raw data)
        self.summary_df = None  # DataFrame indicating which SCOUTS output corresponds to which rule
        self.summary_path = None  # path to all DataFrames generated by SCOUTS

        self.main_layout = QVBoxLayout(self.page)

        # Title section
        # Title
        self.title = QLabel(self.page)
        self.title.setText('SCOUTS - Violins')
        self.title.setStyleSheet(self.style['title'])
        self.title.adjustSize()
        self.main_layout.addWidget(self.title)

        # ## Input section
        # Input header
        self.input_header = QLabel(self.page)
        self.input_header.setText('Load data')
        self.input_header.setStyleSheet(self.style['header'])
        self.input_header.adjustSize()
        self.main_layout.addWidget(self.input_header)
        # Input/Output frame
        self.input_frame = QFrame(self.page)
        self.input_frame.setFrameShape(QFrame.StyledPanel)
        self.input_frame.setLayout(QFormLayout())
        self.main_layout.addWidget(self.input_frame)
        # Raw data button
        self.input_button = QPushButton(self.page)
        self.input_button.setStyleSheet(self.style['button'])
        self.set_icon(self.input_button, 'x-office-spreadsheet')
        self.input_button.setObjectName('file')
        self.input_button.setText(' Load raw data file')
        self.input_button.setToolTip(
            'Load raw data file (the file given to SCOUTS as the input file)')
        self.input_button.clicked.connect(self.get_path)
        # SCOUTS results button
        self.output_button = QPushButton(self.page)
        self.output_button.setStyleSheet(self.style['button'])
        self.set_icon(self.output_button, 'folder')
        self.output_button.setObjectName('folder')
        self.output_button.setText(' Load SCOUTS results')
        self.output_button.setToolTip(
            'Load data from SCOUTS analysis '
            '(the folder given to SCOUTS as the output folder)')
        self.output_button.clicked.connect(self.get_path)
        # Add widgets above to input frame Layout
        self.input_frame.layout().addRow(self.input_button)
        self.input_frame.layout().addRow(self.output_button)

        # ## Samples section
        # Samples header
        self.samples_header = QLabel(self.page)
        self.samples_header.setText('Select sample names')
        self.samples_header.setStyleSheet(self.style['header'])
        self.samples_header.adjustSize()
        self.main_layout.addWidget(self.samples_header)
        # Samples frame
        self.samples_frame = QFrame(self.page)
        self.samples_frame.setFrameShape(QFrame.StyledPanel)
        self.samples_frame.setLayout(QFormLayout())
        self.main_layout.addWidget(self.samples_frame)
        # Samples label
        self.samples_label = QLabel(self.page)
        self.samples_label.setText(
            'Write sample names delimited by semicolons below.\nEx: Control;Treat_01;Pac-03'
        )
        self.samples_label.setStyleSheet(self.style['label'])
        # Sample names line edit
        self.sample_names = QLineEdit(self.page)
        self.sample_names.setStyleSheet(self.style['line edit'])
        # Add widgets above to samples frame Layout
        self.samples_frame.layout().addRow(self.samples_label)
        self.samples_frame.layout().addRow(self.sample_names)

        # ## Analysis section
        # Analysis header
        self.analysis_header = QLabel(self.page)
        self.analysis_header.setText('Plot parameters')
        self.analysis_header.setStyleSheet(self.style['header'])
        self.analysis_header.adjustSize()
        self.main_layout.addWidget(self.analysis_header)
        # Analysis frame
        self.analysis_frame = QFrame(self.page)
        self.analysis_frame.setFrameShape(QFrame.StyledPanel)
        self.analysis_frame.setLayout(QFormLayout())
        self.main_layout.addWidget(self.analysis_frame)
        # Analysis labels
        self.analysis_label_01 = QLabel(self.page)
        self.analysis_label_01.setText('Compare')
        self.analysis_label_01.setStyleSheet(self.style['label'])
        self.analysis_label_02 = QLabel(self.page)
        self.analysis_label_02.setText('with')
        self.analysis_label_02.setStyleSheet(self.style['label'])
        self.analysis_label_03 = QLabel(self.page)
        self.analysis_label_03.setText('for marker')
        self.analysis_label_03.setStyleSheet(self.style['label'])
        self.analysis_label_04 = QLabel(self.page)
        self.analysis_label_04.setText('Outlier type')
        self.analysis_label_04.setStyleSheet(self.style['label'])
        # Analysis drop-down boxes
        self.drop_down_01 = QComboBox(self.page)
        self.drop_down_01.addItems([
            'whole population', 'non-outliers', 'top outliers',
            'bottom outliers', 'none'
        ])
        self.drop_down_01.setStyleSheet(self.style['drop down'])
        self.drop_down_01.setCurrentIndex(2)
        self.drop_down_02 = QComboBox(self.page)
        self.drop_down_02.addItems([
            'whole population', 'non-outliers', 'top outliers',
            'bottom outliers', 'none'
        ])
        self.drop_down_02.setStyleSheet(self.style['drop down'])
        self.drop_down_02.setCurrentIndex(0)
        self.drop_down_03 = QComboBox(self.page)
        self.drop_down_03.setStyleSheet(self.style['drop down'])
        self.drop_down_04 = QComboBox(self.page)
        self.drop_down_04.addItems(['OutS', 'OutR'])
        self.drop_down_04.setStyleSheet(self.style['drop down'])
        # Add widgets above to samples frame Layout
        self.analysis_frame.layout().addRow(self.analysis_label_01,
                                            self.drop_down_01)
        self.analysis_frame.layout().addRow(self.analysis_label_02,
                                            self.drop_down_02)
        self.analysis_frame.layout().addRow(self.analysis_label_03,
                                            self.drop_down_03)
        self.analysis_frame.layout().addRow(self.analysis_label_04,
                                            self.drop_down_04)

        self.legend_checkbox = QCheckBox(self.page)
        self.legend_checkbox.setText('Add legend to the plot')
        self.legend_checkbox.setStyleSheet(self.style['checkbox'])
        self.main_layout.addWidget(self.legend_checkbox)

        # Plot button (stand-alone)
        self.plot_button = QPushButton(self.page)
        self.set_icon(self.plot_button, 'system-run')
        self.plot_button.setText(' Plot')
        self.plot_button.setToolTip(
            'Plot data after loading the input data and selecting parameters')
        self.plot_button.setStyleSheet(self.style['run button'])
        self.plot_button.setEnabled(False)
        self.plot_button.clicked.connect(self.run_plot)
        self.main_layout.addWidget(self.plot_button)

        # ## Secondary Window
        # This is used to plot the violins only
        self.secondary_window = QMainWindow(self)
        self.secondary_window.resize(720, 720)
        self.dynamic_canvas = DynamicCanvas(self.secondary_window,
                                            width=6,
                                            height=6,
                                            dpi=120)
        self.secondary_window.setCentralWidget(self.dynamic_canvas)
        self.secondary_window.addToolBar(
            NavBar(self.dynamic_canvas, self.secondary_window))

    def set_icon(self, widget: QWidget, icon: str) -> None:
        """Associates an icon to a widget."""
        i = QIcon()
        i.addPixmap(
            QPixmap(
                os.path.abspath(
                    os.path.join(self.rootdir, 'src', 'default_icons',
                                 f'{icon}.svg'))))
        widget.setIcon(QIcon.fromTheme(icon, i))

    def get_path(self) -> None:
        """Opens a dialog box and loads the corresponding data into memory, depending on the caller widget."""
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        query = None
        func = None
        if self.sender().objectName() == 'file':
            query, _ = QFileDialog.getOpenFileName(self,
                                                   "Select file",
                                                   "",
                                                   "All Files (*)",
                                                   options=options)
            func = self.load_scouts_input_data
        elif self.sender().objectName() == 'folder':
            query = QFileDialog.getExistingDirectory(self,
                                                     "Select Directory",
                                                     options=options)
            func = self.load_scouts_results
        if query:
            self.load_data(query, func)

    def load_data(self, query: str, func: Callable) -> None:
        """Loads input data into memory, while displaying a loading message as a separate worker."""
        worker = Worker(func=func, query=query)
        message = self.loading_message()
        worker.signals.started.connect(message.show)
        worker.signals.started.connect(self.page.setDisabled)
        worker.signals.error.connect(self.generic_error_message)
        worker.signals.error.connect(message.destroy)
        worker.signals.failed.connect(self.plot_button.setDisabled)
        worker.signals.success.connect(message.destroy)
        worker.signals.success.connect(self.enable_plot)
        worker.signals.finished.connect(self.page.setEnabled)
        self.threadpool.start(worker)

    def loading_message(self) -> QDialog:
        """Returns the message box to be displayed while the user waits for the input data to load."""
        message = QDialog(self)
        message.setWindowTitle('Loading')
        message.resize(300, 50)
        label = QLabel('loading DataFrame into memory...', message)
        label.setStyleSheet(self.style['label'])
        label.adjustSize()
        label.setAlignment(Qt.AlignCenter)
        label.move(int((message.width() - label.width()) / 2),
                   int((message.height() - label.height()) / 2))
        return message

    def load_scouts_input_data(self, query: str) -> None:
        """Loads data for whole population prior to SCOUTS into memory (used for plotting the whole population)."""
        try:
            self.population_df = pd.read_excel(query, index_col=0)
        except XLRDError:
            self.population_df = pd.read_csv(query, index_col=0)
        self.drop_down_03.clear()
        self.drop_down_03.addItems(list(self.population_df.columns))
        self.drop_down_03.setCurrentIndex(0)

    def load_scouts_results(self, query: str) -> None:
        """Loads the SCOUTS summary file into memory, in order to dynamically locate SCOUTS output files later when
        the user chooses which data to plot."""
        self.summary_df = pd.read_excel(os.path.join(query, 'summary.xlsx'),
                                        index_col=None)
        self.summary_path = query

    def enable_plot(self) -> None:
        """Enables plot button if all necessary files are placed in memory."""
        if isinstance(self.summary_df, pd.DataFrame) and isinstance(
                self.population_df, pd.DataFrame):
            self.plot_button.setEnabled(True)

    def run_plot(self) -> None:
        """Sets and starts the plot worker."""
        worker = Worker(func=self.plot)
        worker.signals.error.connect(self.generic_error_message)
        worker.signals.success.connect(self.secondary_window.show)
        self.threadpool.start(worker)

    def plot(self) -> None:
        """Logic for plotting data based on user selection of populations, markers, etc."""
        # Clear figure currently on plot
        self.dynamic_canvas.axes.cla()
        # Initialize values and get parameters from GUI
        columns = ['sample', 'marker', 'population', 'expression']
        samples = self.parse_sample_names()
        pop_01 = self.drop_down_01.currentText()
        pop_02 = self.drop_down_02.currentText()
        pops_to_analyse = [pop_01, pop_02]
        marker = self.drop_down_03.currentText()
        cutoff_from_reference = True if self.drop_down_04.currentText(
        ) == 'OutR' else False
        violin_df = pd.DataFrame(columns=columns)
        # Start fetching data from files
        # Whole population
        for pop in pops_to_analyse:
            if pop == 'whole population':
                for partial_df in self.yield_violin_values(
                        df=self.population_df,
                        population='whole population',
                        samples=samples,
                        marker=marker,
                        columns=columns):
                    violin_df = violin_df.append(partial_df)
        # Other comparisons
            elif pop != 'none':
                for file_number in self.yield_selected_file_numbers(
                        summary_df=self.summary_df,
                        population=pop,
                        cutoff_from_reference=cutoff_from_reference,
                        marker=marker):
                    df_path = os.path.join(self.summary_path, 'data',
                                           f'{"%04d" % file_number}.')
                    try:
                        sample_df = pd.read_excel(df_path + 'xlsx',
                                                  index_col=0)
                    except FileNotFoundError:
                        sample_df = pd.read_csv(df_path + 'csv', index_col=0)
                    if not sample_df.empty:
                        for partial_df in self.yield_violin_values(
                                df=sample_df,
                                population=pop,
                                samples=samples,
                                marker=marker,
                                columns=columns):
                            violin_df = violin_df.append(partial_df)
        # Plot data
        pops_to_analyse = [p for p in pops_to_analyse if p != 'none']
        violin_df = violin_df[violin_df['marker'] == marker]
        for pop in pops_to_analyse:
            pop_subset = violin_df.loc[violin_df['population'] == pop]
            for sample in samples:
                sample_subset = pop_subset.loc[pop_subset['sample'] == sample]
                sat = 1.0 - samples.index(sample) / (len(samples) + 1)
                self.dynamic_canvas.update_figure(
                    subset_by_sample=sample_subset,
                    pop=pop,
                    sat=sat,
                    samples=samples)
        # Draw plotted data on canvas
        if self.legend_checkbox.isChecked():
            self.dynamic_canvas.add_legend()
        self.dynamic_canvas.axes.set_title(
            f'{marker} expression - {self.drop_down_04.currentText()}')
        self.dynamic_canvas.fig.canvas.draw()

    def parse_sample_names(self) -> List[str]:
        """Parse sample names from the QLineEdit Widget."""
        return self.sample_names.text().split(';')

    def generic_error_message(self, error: Tuple[Exception, str]) -> None:
        """Error message box used to display any error message (including traceback) for any uncaught errors."""
        name, trace = error
        QMessageBox.critical(
            self, 'An error occurred!',
            f"Error: {str(name)}\n\nfull traceback:\n{trace}")

    def closeEvent(self, event: QEvent) -> None:
        """Defines the message box for when the user wants to quit ViolinGUI."""
        title = 'Quit Application'
        mes = "Are you sure you want to quit?"
        reply = QMessageBox.question(self, title, mes,
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.setEnabled(False)
            self.threadpool.waitForDone()
            event.accept()
        else:
            event.ignore()

    @staticmethod
    def yield_violin_values(df: pd.DataFrame, population: str,
                            samples: List[str], marker: str,
                            columns: List[str]) -> pd.DataFrame:
        """Returns a DataFrame from expression values, along with information of sample, marker and population. This
        DataFrame is appended to the violin plot DataFrame in order to simplify plotting the violins afterwards."""
        for sample in samples:
            series = df.loc[df.index.str.contains(sample)].loc[:, marker]
            yield pd.DataFrame(
                {
                    'sample': sample,
                    'marker': marker,
                    'population': population,
                    'expression': series
                },
                columns=columns)

    @staticmethod
    def yield_selected_file_numbers(
            summary_df: pd.DataFrame, population: str,
            cutoff_from_reference: bool,
            marker: str) -> Generator[pd.DataFrame, None, None]:
        """Yields file numbers from DataFrames resulting from SCOUTS analysis. DataFrames are yielded based on
        global values, i.e. the comparisons the user wants to perform."""
        cutoff = 'sample'
        if cutoff_from_reference is True:
            cutoff = 'reference'
        for index, (file_number, cutoff_from, reference, outliers_for,
                    category) in summary_df.iterrows():
            if cutoff_from == cutoff and outliers_for == marker and category == population:
                yield file_number
Ejemplo n.º 2
0
class ProjectListItem(QObject):
    """
    The core functionality class - the wrapper around the Stm32pio class suitable for the project GUI representation
    """

    logAdded = Signal(str, int,
                      arguments=['message', 'level'
                                 ])  # send the log message to the front-end
    initialized = Signal()
    destructed = Signal()

    def __init__(self,
                 project_args: List[Any] = None,
                 project_kwargs: Mapping[str, Any] = None,
                 from_startup: bool = False,
                 parent: QObject = None):
        """
        Instance construction is split into 2 phases: the wrapper setup and inner Stm32pio class initialization. The
        latter one is taken out to the separated thread as it is, potentially, a time-consuming operation. This thread
        starts right after the main constructor so the wrapper is already built at that moment and therefore can be used
        from GUI, be referenced and so on.

        Args:
            project_args: list of positional arguments that will be passed to the Stm32pio constructor
            project_kwargs: dictionary of keyword arguments that will be passed to the Stm32pio constructor
            from_startup: mark that this project comes from the beginning of the app life (e.g. from the NV-storage) so
                it can be treated differently on the GUI side
            parent: Qt parent
        """

        super().__init__(parent=parent)

        if project_args is None:
            project_args = []
        if project_kwargs is None:
            project_kwargs = {}

        self._from_startup = from_startup

        underlying_logger = logging.getLogger('stm32pio.gui.projects')
        self.logger = stm32pio.core.log.ProjectLogger(underlying_logger,
                                                      project_id=id(self))
        self.logging_worker = LoggingWorker(project_id=id(self))
        self.logging_worker.sendLog.connect(self.logAdded)

        # QThreadPool can automatically queue new incoming tasks if a number of them are larger than maxThreadCount
        self.workers_pool = QThreadPool(parent=self)
        self.workers_pool.setMaxThreadCount(1)
        self.workers_pool.setExpiryTimeout(
            -1)  # tasks wait forever for the available spot

        self._current_action: str = 'loading'
        self._last_action_succeed: bool = True

        # These values are valid only until the Stm32pio project initialize itself (or failed to) (see init_project)
        self.project: Optional[stm32pio.core.project.Stm32pio] = None
        # Use a project path string (as it should be a first argument) as a name
        self._name = str(project_args[0]) if len(project_args) else 'Undefined'
        self._state = {
            'LOADING': True
        }  # pseudo-stage (not present in the ProjectStage enum but is used from QML)
        self._current_stage = 'LOADING'

        self.qml_ready = threading.Event(
        )  # the front and the back both should know when each other is initialized
        self.should_be_destroyed = threading.Event(
        )  # currently, is used just to "cancel" the initialization thread

        # Register some kind of the deconstruction handler (later, after the project initialization, see init_project)
        self._finalizer = None

        if 'logger' not in project_kwargs:
            project_kwargs['logger'] = self.logger

        # Start the Stm32pio part initialization right after. It can take some time so we schedule it in a dedicated
        # thread
        init_thread = threading.Thread(target=self.init_project,
                                       args=project_args,
                                       kwargs=project_kwargs)
        init_thread.start()

    def init_project(self, *args, **kwargs) -> None:
        """
        Initialize the underlying Stm32pio project.

        Args:
            *args: positional arguments of the Stm32pio constructor
            **kwargs: keyword arguments of the Stm32pio constructor
        """
        try:
            self.project = stm32pio.core.project.Stm32pio(*args, **kwargs)
        except:
            stm32pio.core.log.log_current_exception(self.logger)
            self._state = {'INIT_ERROR': True}  # pseudo-stage
            self._current_stage = 'INIT_ERROR'
        else:
            if self.project.config.get('project', 'inspect_ioc').lower() in stm32pio.core.settings.yes_options and \
               self.project.state.current_stage > stm32pio.core.state.ProjectStage.EMPTY:
                self.project.inspect_ioc_config()
        finally:
            # Register some kind of the deconstruction handler
            self._finalizer = weakref.finalize(
                self, self.at_exit, self.workers_pool, self.logging_worker,
                self.name if self.project is None else str(self.project))
            self._current_action = ''

            # Wait for the GUI to initialize (which one is earlier, actually, back or front)
            while not self.qml_ready.wait(timeout=0.050):
                # When item is located out of visible scope then QML doesn't load its visual representation so this
                # initialization thread is kept hanging. This is OK except when the app shutting down, it prevents
                # Python GC of calling weakref for finalization process. So we introduce additional flag to be able
                # to quit the thread
                if self.should_be_destroyed.is_set():
                    break

            if not self.should_be_destroyed.is_set():
                self.updateState()
                self.initialized.emit()
                self.nameChanged.emit(
                )  # in any case we should notify the GUI part about the initialization ending

    @staticmethod
    def at_exit(workers_pool: QThreadPool, logging_worker: LoggingWorker,
                name: str):
        """
        The instance deconstruction handler is meant to be used with weakref.finalize() conforming with the requirement
        to have no reference to the target object (so it doesn't contain any instance reference and also is decorated as
        'staticmethod')
        """
        # Wait forever for all the jobs to complete. Currently, we cannot abort them gracefully
        workers_pool.waitForDone(msecs=-1)
        logging_worker.stopped.set(
        )  # post the event in the logging worker to inform it...
        logging_worker.thread.wait()  # ...and wait for it to exit, too
        module_logger.debug(f"destroyed {name}")

    def deleteLater(self) -> None:
        self.destructed.emit()
        return super().deleteLater()

    @Slot()
    def qmlLoaded(self):
        """Event signaling the complete loading of the needed frontend components"""
        self.qml_ready.set()
        self.logging_worker.can_flush_log.set()

    @Property(bool)
    def fromStartup(self) -> bool:
        """Is this project is here from the beginning of the app life?"""
        return self._from_startup

    @Property('QVariant')
    def config(self) -> dict:
        """Inner project's ConfigParser config converted to the dictionary (QML JS object)"""
        # TODO: cache this? (related to live-reloaded settings...)
        return {
            section:
            {key: value
             for key, value in self.project.config.items(section)}
            if self.project is not None else {}
            for section in ['app', 'project']
        }

    nameChanged = Signal()

    @Property(str, notify=nameChanged)
    def name(self) -> str:
        """Human-readable name of the project. Will evaluate to the absolute path if it cannot be instantiated"""
        if self.project is not None:
            return self.project.path.name
        else:
            return self._name

    stateChanged = Signal()

    @Property('QVariant', notify=stateChanged)
    def state(self) -> dict:
        """
        Get the current project state in the appropriate Qt form. Update the cached 'current stage' value as a side
        effect
        """
        if type(self._state) == stm32pio.core.state.ProjectState:
            return {stage.name: value for stage, value in self._state.items()}
        else:
            return self._state

    @Slot()
    def updateState(self):
        if self.project is not None:
            self._state = self.project.state
        self.stateChanged.emit()
        self.currentStageChanged.emit()

    currentStageChanged = Signal()

    @Property(str, notify=currentStageChanged)
    def currentStage(self) -> str:
        """
        Get the current stage the project resides in.
        Note: this returns a cached value. Cache updates every time the state property got requested
        """
        if type(self._state) == stm32pio.core.state.ProjectState:
            return self._state.current_stage.name
        else:
            return self._current_stage

    @Property(str)
    def currentAction(self) -> str:
        """
        Stm32pio action (i.e. function name) that is currently executing or an empty string if there is none. It is set
        on actionStarted signal and reset on actionFinished
        """
        return self._current_action

    @Property(bool)
    def lastActionSucceed(self) -> bool:
        """Have the last action ended with a success?"""
        return self._last_action_succeed

    actionStarted = Signal(str, arguments=['action'])

    @Slot(str)
    def actionStartedSlot(self, action: str):
        """Pass the corresponding signal from the worker, perform related tasks"""
        # Currently, this property should be set BEFORE emitting the 'actionStarted' signal (because QML will query it
        # when the signal will be handled in StateMachine) (probably, should be resolved later as it is bad to be bound
        # to such a specific logic)
        self._current_action = action
        self.actionStarted.emit(action)

    actionFinished = Signal(str, bool, arguments=['action', 'success'])

    @Slot(str, bool)
    def actionFinishedSlot(self, action: str, success: bool):
        """Pass the corresponding signal from the worker, perform related tasks"""
        self._last_action_succeed = success
        if not success:
            # Clear the queue - stop further execution (cancel planned tasks if an error had happened)
            self.workers_pool.clear()
        self.actionFinished.emit(action, success)
        # Currently, this property should be reset AFTER emitting the 'actionFinished' signal (because QML will query it
        # when the signal will be handled in StateMachine) (probably, should be resolved later as it is bad to be bound
        # to such a specific logic)
        self._current_action = ''

    @Slot(str, 'QVariantList')
    def run(self, action: str, args: List[Any]):
        """
        Asynchronously perform Stm32pio actions (generate, build, etc.) (dispatch all business logic).

        Args:
            action: method name of the corresponding Stm32pio action
            args: list of positional arguments for this action
        """

        worker = Worker(getattr(self.project, action),
                        args,
                        self.logger,
                        parent=self)
        worker.started.connect(self.actionStartedSlot)
        worker.finished.connect(self.actionFinishedSlot)
        worker.finished.connect(self.updateState)
        worker.finished.connect(self.currentStageChanged)

        self.workers_pool.start(
            worker)  # will automatically place to the queue
Ejemplo n.º 3
0
class myDirModel(QFileSystemModel, QObject):

    dirSelected = Signal(QModelIndex)
    scanProgressed = Signal(float)
    scanDirStudy = Signal(object)
    scanDirSeries = Signal(object)

    def __init__(self):
        QFileSystemModel.__init__(self)
        self.sizeRole = int(Qt.UserRole + 1)
        self.dirSelected.connect(self.selDirPath)
        self.Scanning = False
        self.threadpool = QThreadPool()
        self.progress = 0.0
        print("Multithreading with maximum %d threads" %
              self.threadpool.maxThreadCount())

    def sizeString(self, fInfo):
        if (not fInfo.isDir()):
            return ""
        dir = QDir(fInfo.filePath())
        fileFilters = QDir.Filters(QDir.Files | QDir.System | QDir.Hidden)
        size = 0
        for filePath in dir.entryList(fileFilters):
            fi = QFileInfo(dir, filePath)
            size += fi.size()

        if (size > 1024 * 1024 * 10):
            return str("%4.1f" % (size / (1024 * 1024))) + 'MB'
        if (size > 1024 * 10):
            return str("%4.1f" % (size / 1024)) + 'KB'
        return str(size)

    def data(self, index, role=Qt.DisplayRole):
        if (index.isValid() and role == self.sizeRole):
            return self.sizeString(self.fileInfo(index))
        else:
            return QFileSystemModel.data(self, index, role)

    def roleNames(self):
        roles = QFileSystemModel.roleNames(self)
        header = "size"
        roles[int(self.sizeRole)] = header.encode()
        return roles

    @Slot(object)
    def scanDir_output(self, res):
        qDebug("Return with result(study): " + res[0].__str__())
        qDebug("Return with result(series): " + res[1].__str__())

        self.scanProgressed.emit(1.0)
        self.scanDirStudy.emit(res[0])
        self.scanDirSeries.emit(res[1])
        self.Scanning = False

    @Slot()
    def thread_complete(self):
        qDebug("Thread Complete")
        self.Scanning = False

    @Slot()
    def scanProgress(self):
        self.progress = self.w.progress
        # qDebug("Progress callback " + str(self.w.progress))
        self.scanProgressed.emit(self.w.progress)

    @Slot(QModelIndex)
    def selDirPath(self, index):
        fInfo = self.fileInfo(index)
        qDebug("Scanning " + fInfo.filePath())

        # instantiate Scanner:
        self.sp = gdcm.Scanner.New()
        s = self.sp.__ref__()
        self.w = ProgressWatcher(s, 'Watcher')
        self.w.notifyProgress.connect(self.scanProgress)

        # Populate Study, Series, Image lists
        if (not self.Scanning):
            pass  # start scanning
            self.Scanning = True
            worker = Worker(
                self.scanDir, fInfo.filePath(),
                s)  # Any other args, kwargs are passed to the run function
            worker.signals.result.connect(self.scanDir_output)
            worker.signals.finished.connect(self.thread_complete)
            # Execute
            self.threadpool.start(worker)
            # Test how many thread starts?

    def scanDir(self, strPath, s):
        d = gdcm.Directory()
        nfiles = d.Load(strPath)
        if (nfiles == 0):
            sys.exit(-1)
            # No DICOM files in the directory

        filenames = d.GetFilenames()
        qDebug("The number of files to scan is " + str(len(filenames)))

        # Define the set of tags we are interested in, may need more
        t1 = gdcm.Tag(0x10, 0x20)
        # Patient ID
        t2 = gdcm.Tag(0x10, 0x10)
        # Patient Name
        t3 = gdcm.Tag(0x20, 0x10)
        # Study ID
        t4 = gdcm.Tag(0x20, 0x0d)
        # Study Instance UID
        t5 = gdcm.Tag(0x20, 0x0e)
        # Series Instance UID
        t6 = gdcm.Tag(0x20, 0x11)
        # Series Number
        t7 = gdcm.Tag(0x28, 0x08)
        # Number of Frames
        t8 = gdcm.Tag(0x20, 0x32)
        # Image Position
        t10 = gdcm.Tag(0x28, 0x30)
        # Pixel Spacing
        t11 = gdcm.Tag(0x20, 0x37)
        # Image Orientation Patient
        t12 = gdcm.Tag(0x28, 0x02)
        # Samples per pixel
        t13 = gdcm.Tag(0x28, 0x04)
        # Photometric Interpretation
        t14 = gdcm.Tag(0x28, 0x10)
        # Rows
        t15 = gdcm.Tag(0x28, 0x11)
        # Column
        t16 = gdcm.Tag(0x28, 0x101)
        # BitStored
        t17 = gdcm.Tag(0x02, 0x02)
        # Media Storage SOP Class UID
        t18 = gdcm.Tag(0x02, 0x03)
        # Media Storage SOP Instance UID
        t19 = gdcm.Tag(0x02, 0x10)
        # Transfer Syntax
        t20 = gdcm.Tag(0x08, 0x16)
        # SOP Class UID
        t21 = gdcm.Tag(0x08, 0x18)
        # SOP Instance UID
        t22 = gdcm.Tag(0x5200, 0x9229)
        # Shared functional group
        t23 = gdcm.Tag(0x5200, 0x9230)
        # Per frame functional group
        t24 = gdcm.Tag(0x0028, 0x1050)
        # WindowCenter
        t25 = gdcm.Tag(0x0028, 0x1051)
        # WindowWidth
        t26 = gdcm.Tag(0x0028, 0x1052)
        # Rescale Intercept
        t27 = gdcm.Tag(0x0028, 0x1053)
        # Rescale Slope
        t28 = gdcm.Tag(0x0028, 0x1054)
        # Rescale Type
        t29 = gdcm.Tag(0x0010, 0x0030)
        # PatientBirthDate
        t30 = gdcm.Tag(0x0010, 0x0040)
        # PatientSex
        t31 = gdcm.Tag(0x0008, 0x0020)
        # Study Date
        t32 = gdcm.Tag(0x0008, 0x1030)
        # Study Description

        s.AddTag(t1)
        s.AddTag(t2)
        s.AddTag(t3)
        s.AddTag(t4)
        s.AddTag(t5)
        s.AddTag(t6)
        s.AddTag(t7)
        s.AddTag(t8)
        s.AddTag(t10)
        s.AddTag(t11)
        s.AddTag(t12)
        s.AddTag(t13)
        s.AddTag(t14)
        s.AddTag(t15)
        s.AddTag(t16)
        s.AddTag(t17)
        s.AddTag(t18)
        s.AddTag(t19)
        s.AddTag(t20)
        s.AddTag(t21)
        s.AddTag(t22)
        s.AddTag(t23)
        s.AddTag(t29)
        s.AddTag(t30)
        s.AddTag(t31)
        s.AddTag(t32)

        b = s.Scan(filenames)

        # if no files in this directory
        dicomfiles = []
        if (not b):
            qDebug("Empty directory")
            return dicomfiles

        study_list = []
        series_list = []
        series_count = {}
        image_count = {}

        for aFile in filenames:
            if (s.IsKey(aFile)):  # existing DICOM file
                # qDebug("Scan "+aFile)
                is_multiframe = 0

                pttv = gdcm.PythonTagToValue(s.GetMapping(aFile))
                pttv.Start()
                patient_DOB = ""
                patient_sex = ""
                study_description = ""

                # iterate until the end:
                while (not pttv.IsAtEnd()):

                    # get current value for tag and associated value:
                    # if tag was not found, then it was simply not added to the internal std::map
                    # Warning value can be None
                    tag = pttv.GetCurrentTag()
                    value = pttv.GetCurrentValue()

                    if (tag == t1):
                        #print ("PatientID->",value)
                        patient_id = value
                    elif (tag == t2):
                        #print ("PatientName->",value)
                        patient_name = value
                    elif (tag == t29):
                        # print ("PatientBirthDate->",value)
                        patient_DOB = value
                    elif (tag == t30):
                        patient_sex = value
                    elif (tag == t3):
                        # print ("StudyID->",value)
                        study_id = value
                    elif (tag == t4):
                        studyinstance_uid = value
                    elif (tag == t31):
                        # print ("StudyDate->",value)
                        study_date = value
                    elif (tag == t32):
                        study_description = value
                    elif (tag == t6):
                        series_num = value
                        # print ("SeriesNum->",value)
                    elif (tag == t5):
                        # print ("SeriesInstanceUID->",value)
                        seriesinstance_uid = value
                    elif (tag == t7):
                        # print ("NumberOfFrame->",value)
                        if (int(value) > 1):
                            is_multiframe = int(value)
                        else:
                            is_multiframe = 0
                    elif (tag == t19):
                        # print("Transfer Syntax->",value)
                        pass
                    elif (tag == t20):
                        # print("SOP Class UID->",value)
                        sopclass_uid = value
                        #sop_ClassName = sopclass_uid.GetName()
                    elif (tag == t21):
                        # print("SOP Instance UID->",value)
                        sopinstance_uid = value
                    # increment iterator
                    pttv.Next()

                # new StudyInstanceUID
                if (studyinstance_uid not in series_count.keys()):
                    # Add to the study_list
                    study_list.append([
                        patient_id, patient_name, patient_DOB, patient_sex,
                        study_id, studyinstance_uid, study_date,
                        study_description, 0
                    ])
                    # Add count
                    series_count[studyinstance_uid] = 0

                # new SeriesInstanceUID
                if (seriesinstance_uid not in image_count.keys()):
                    # Add to the series_list
                    series_list.append([
                        study_id, studyinstance_uid, seriesinstance_uid,
                        series_num, sopclass_uid, sopinstance_uid, 0
                    ])
                    # Add count
                    image_count[seriesinstance_uid] = 0
                    series_count[studyinstance_uid] += 1

                if (is_multiframe == 0):
                    image_count[seriesinstance_uid] += 1
                else:
                    image_count[seriesinstance_uid] += is_multiframe

        # print(series_count)
        # print(image_count)

        # for each study_list items update series_count from series_count(studyinstance_uid)
        for study in study_list:
            study[8] = series_count[study[5]]

        # for each series_list items update images_count from image_count(seriesinstance_uid)
        for series in series_list:
            series[6] = image_count[series[2]]

        #print(study_list)
        #print(series_list)

        return study_list, series_list
Ejemplo n.º 4
0
class DataBase:
    def __init__(self,
                 username,
                 password,
                 host='localhost',
                 dbname='postgres',
                 port=5432,
                 pool_size=5):
        self.schema = 'soad'
        self.username = username
        self.host = host
        self.port = port
        self.dbname = dbname
        self.folder = 'Resources' + os.sep + 'database'
        os.environ["PGPASSWORD"] = password
        self.password = password
        self.dbinfo = 'postgres://' + username + ':' + password + '@' + host + ':' + str(
            port) + '/' + self.dbname
        self.db = DAL(self.dbinfo,
                      folder=self.folder,
                      pool_size=pool_size,
                      migrate=False,
                      attempts=1)
        self.connection = None
        self.threadpool = QThreadPool()

    def busca_registro(self,
                       nome_tabela,
                       coluna,
                       valor='',
                       operador='=',
                       filtro=None):

        if filtro == '' or filtro is None:
            filtro = '1=1'

        sql = "select * from " + self.schema + ".fnc_buscar_registro(" \
              + "p_tabela=>" + "'" + nome_tabela + "'" \
              + ", p_coluna=>" + "'" + coluna + "'" \
              + ", p_valor=>" + "'" + valor + "'" \
              + ", p_operador=>" + "'" + operador + "'" \
              + ", p_filtro=>" + "'" + filtro + "'" \
              + ");"

        return self.execute_sql(sql)

    def get_registro(self, fnc, campo, valor):

        sql = "select * from " + self.schema + "." + fnc + "(" \
              + "p_" + campo + "=>" + "'" + str(valor) + "'" \
              + ");"

        return self.execute_sql(sql)

    def call_procedure(self, schema='soad', params=None):
        if not params:
            return
        # Remove parametros vazios
        vazio = []
        for param in params["params"].items():
            if param[1] == '':
                vazio.append(param[0])

        logging.info('[DataBase] Parâmetros vazios: ' + str(vazio))

        for i in range(len(vazio)):
            del params["params"][vazio[i]]

        params = json.dumps(params, ensure_ascii=False)
        sql = "select * from " + schema + ".fnc_chamada_de_metodo(" \
              + "p_json_params=>" + "'" + params + "'" \
            + ");"

        return self.execute_sql(sql)

    def execute_sql(self, sql, as_dict=True):
        retorno = list()
        try:
            retorno = self.db.executesql(query=sql, as_dict=as_dict)
            self.db.commit()
            logging.debug('[DataBase] status=' + str(True))
            logging.debug('[DataBase] sql=' + str(sql))
            logging.debug('[DataBase] retorno=' + str(retorno))
            prc = True, retorno, str(self.db._lastsql)

        except Exception as e:
            self.db.rollback()
            logging.debug('[DataBase] status=' + str(False))
            logging.debug('[DataBase] sql=' + str(sql))
            logging.debug('[DataBase] exception=' + str(e))
            retorno.append(e)
            prc = False, retorno, str(sql)

        except:
            e = 'Exceção não tratada'
            logging.debug('[DataBase] status=' + str(False))
            logging.debug('[DataBase] sql=' + str(sql))
            logging.debug('[DataBase] exception2=' + str(e))
            retorno.append(e)
            prc = False, e, str(sql)

        return prc

    def __conectar_banco__(self, progress_callback):
        try:
            self.connection = self.db.__call__()
            #progress_callback.emit(100)
        except Exception as e:
            logging.debug('[DataBase] ' + str(e))
            os.environ["PGPASSWORD"] = ''
            pass
            #progress_callback.emit(0)
        return self

    def definir_schema(self, schema):
        self.schema = schema
        self.execute_sql("SET search_path TO " + self.schema, as_dict=False)

    def fechar_conexao(self):
        self.db.close()

    def progress_fn(self, n):
        print("%d%% done" % n)

    def retorno_conexao(self, s):
        self.connection = s

    def thread_complete(self):
        logging.debug('[DataBase] Thread Completed')

    def abrir_conexao(self):
        # Pass the function to execute
        worker = Worker(
            self.db.__call__
        )  # Any other args, kwargs are passed to the run function
        worker.signals.result.connect(self.retorno_conexao)
        worker.signals.finished.connect(self.thread_complete)
        #worker.signals.progress.connect(self.progress_fn)

        # Execute
        self.threadpool.start(worker)
Ejemplo n.º 5
0
        self.setCentralWidget(widget)

    @Slot()
    def exit_app(self):
        QApplication.quit()


def main():
    use_cuda = torch.cuda.is_available()
    device = torch.device("cuda:0" if use_cuda else "cpu")
    torch.backends.cudnn.benchmark = True
    input_dim = 7

    # Qt Application
    app = QApplication(sys.argv)
    # QWidget
    widget = Widget(device, input_dim)
    # QMainWindow using QWidget as central widget
    window = MainWindow(widget)
    window.resize(800, 600)
    window.show()

    # Execute application
    sys.exit(app.exec_())


if __name__ == "__main__":
    threadpool = QThreadPool()
    worker = Worker(main())
    threadpool.start(worker)
Ejemplo n.º 6
0
class KlineWidget(QWidget, Ui_Form):
    def __init__(self, mainwindow):
        super(self.__class__, self).__init__()
        self.setupUi(self)
        self.setStyleSheet(qss)
        self.mainwindow = mainwindow
        self.k_thread = QThreadPool()
        # calendar
        self.start.setCalendarPopup(True)
        self.end.setCalendarPopup(True)
        self.start.setDisplayFormat('yyyy-MM-dd HH:mm:ss')
        self.end.setDisplayFormat('yyyy-MM-dd HH:mm:ss')
        now = datetime.now() - timedelta(days=30)
        self.start.setDate(QDate(now.year, now.month, now.day))
        self.end.setDateTime(QDateTime.currentDateTime())
        #
        for local_symbol in sorted(G.all_contracts):
            self.symbol_list.addItem(local_symbol + contract_space + G.all_contracts[local_symbol])  # 添加下拉框
        self.symbol_list.currentIndexChanged.connect(self.symbol_change_slot)
        self.frq.addItems(['1min', '3min', '15min', '30min', '60min'])
        # table
        self.tick_table.setRowCount(0)
        self.tick_row = len(G.order_tick_row_map)
        self.tick_table.horizontalHeader().setStretchLastSection(True)  # 最后一列自适应表格宽度
        # self.tick_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)  # 所有列自适应表格宽度
        self.tick_table.setEditTriggers(QTableWidget.NoEditTriggers)  # 单元格不可编辑
        self.tick_table.horizontalHeader().setVisible(False)  # 水平表头不可见
        self.tick_table.verticalHeader().setVisible(False)  # 垂直表头不可见
        # btn
        self.source_btn.clicked.connect(self.source_btn_slot)
        self.hide_btn.clicked.connect(self.hide_btn_slot)
        self.reload_btn.clicked.connect(self.k_line_reload)
        self.hide_btn_slot()  # 默认隐藏
        self.mainwindow.job.kline_tick_signal.connect(self.set_tick_slot)
        self.ready_action()

    def ready_action(self):
        # k-line
        self.symbol_list.setFocus()
        self.k_line_init()
        if not G.config.LOCAL_SOURCE:
            info = G.config.DB_INFO.get(G.config.WHICH_DB)
            if info:
                G.temp_var = info
                self.k_thread.start(Worker(db_connect, **info, callback=self.db_callback))
            else:
                replay = QMessageBox.information(self, '提示', '你选择了外部数据源但未指定数据库.是否切换本地数据源',
                                                 QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
                if replay == QMessageBox.Yes:
                    G.config.LOCAL_SOURCE = True
                    G.config.to_file()

    def db_callback(self, res):
        if res:
            TipDialog("数据库连接出错,请在数据源中查看")
        else:
            from app.lib.helper import create_db_conn
            create_db_conn(**G.temp_var)

    def k_line_init(self):
        self.browser = QWebEngineView(self)
        # kline 信号
        self.kline_job = self.mainwindow.kline_job
        # 增加一个通信中需要用到的频道
        self.channel = QWebChannel()
        self.channel.registerObject("bee_signal", self.kline_job)
        # 在浏览器中设置该频道
        self.browser.page().setWebChannel(self.channel)
        self.t = 5  # 递归深度
        self.url = self.search_path(dir=os.path.split(__file__)[0])
        self.browser.page().load(QUrl.fromLocalFile(self.url))
        self.browser.show()
        self.kline_layout.addWidget(self.browser)

    def k_line_reload(self):
        G.choice_local_symbol = self.symbol_list.currentText().split(contract_space)[0]
        G.frq = self.frq.currentText()
        G.start = self.start.text()
        G.end = self.end.text()
        self.mainwindow.kline_job.qt_to_js_reload.emit()

    def symbol_change_slot(self):
        text = self.symbol_list.currentText()
        local_symbol = text.split(contract_space)[0]
        name = text.split(contract_space)[1]
        if local_symbol in G.all_contracts:
            G.choice_local_symbol = local_symbol
            self.k_line_reload()

    def search_path(self, dir):
        p = os.path.split(dir)[0] + G.kline_folder
        self.t -= 1
        if not os.path.exists(p):
            if self.t < 0:  # 防止超过递归深度
                return os.path.split(__file__)[0] + G.kline_folder
            return self.search_path(dir)
        return p

    def hide_btn_slot(self):
        if self.tick_table.isHidden():
            self.tick_table.show()
            self.hide_btn.setText("<<")
        else:
            self.tick_table.hide()
            self.hide_btn.setText(">>")

    def source_btn_slot(self):
        self.mainwindow.config_handle()
        self.mainwindow.cfg_dialog.tabWidget.setCurrentIndex(2)

    @Slot(dict)
    def set_tick_slot(self, tick: dict):
        local_symbol = tick['local_symbol']
        if local_symbol != G.choice_local_symbol:
            return
        for k, v in tick_zn.items():
            if k not in G.order_tick_row_map:  # 不在表中
                row = self.tick_row
                G.order_tick_row_map.append(k)
                self.tick_table.insertRow(row)
                self.tick_row += 1
            else:
                row = G.order_tick_row_map.index(k)
                if k not in ["local_symbol", "exchange", "datetime"]:
                    space_ = " " * 2
                    old = self.tick_table.item(row, 1).text()
                    different = float(tick[k]) - float(old)
                    if different > 0:  # 增
                        v = f"{v}{space_}↑≈{'%0.2f' % abs(different)}"
                    elif different < 0:  # 减
                        v = f"{v}{space_}↓≈{'%0.2f' % abs(different)}"
                    else:
                        continue
            self.tick_table.setItem(row, 0, QTableWidgetItem(v))
            self.tick_table.setItem(row, 1, QTableWidgetItem(str(tick[k])))

    def fill_tick_table(self):
        d = G.order_tick_row_map
        tick = G.market_tick[G.choice_local_symbol]
        for row, k in enumerate(d):
            self.tick_table.insertRow(row)
            self.tick_table.setItem(row, 0, QTableWidgetItem(str(tick_zn[k])))
            self.tick_table.setItem(row, 1, QTableWidgetItem(str(tick[k])))
Ejemplo n.º 7
0
class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setupUi(self)

        self.pushButton.pressed.connect(self.update_weather)

        self.threadpool = QThreadPool()
        self.update_weather()

        self.show()

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(330, 417)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout_3 = QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.lineEdit = QLineEdit(self.centralwidget)
        self.lineEdit.setObjectName("lineEdit")
        self.horizontalLayout_3.addWidget(self.lineEdit)
        self.pushButton = QPushButton(self.centralwidget)
        self.pushButton.setText("")
        icon = QIcon()
        icon.addPixmap(QPixmap("images/arrow-circle-225.png"), QIcon.Normal, QIcon.Off)
        self.pushButton.setIcon(icon)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout_3.addWidget(self.pushButton)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.horizontalLayout_4 = QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.weatherIcon = QLabel(self.centralwidget)
        sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.weatherIcon.sizePolicy().hasHeightForWidth())
        self.weatherIcon.setSizePolicy(sizePolicy)
        self.weatherIcon.setMinimumSize(QSize(64, 64))
        self.weatherIcon.setMaximumSize(QSize(64, 64))
        self.weatherIcon.setText("")
        self.weatherIcon.setObjectName("weatherIcon")
        self.horizontalLayout_4.addWidget(self.weatherIcon)
        self.weatherLabel = QLabel(self.centralwidget)
        self.weatherLabel.setText("")
        self.weatherLabel.setObjectName("weatherLabel")
        self.horizontalLayout_4.addWidget(self.weatherLabel)
        self.verticalLayout.addLayout(self.horizontalLayout_4)
        self.gridLayout_2 = QGridLayout()
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.forecastIcon4 = QLabel(self.centralwidget)
        sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.forecastIcon4.sizePolicy().hasHeightForWidth())
        self.forecastIcon4.setSizePolicy(sizePolicy)
        self.forecastIcon4.setMinimumSize(QSize(64, 64))
        self.forecastIcon4.setMaximumSize(QSize(200, 32))
        self.forecastIcon4.setBaseSize(QSize(0, 0))
        self.forecastIcon4.setText("")
        self.forecastIcon4.setAlignment(Qt.AlignCenter)
        self.forecastIcon4.setObjectName("forecastIcon4")
        self.gridLayout_2.addWidget(self.forecastIcon4, 1, 3, 1, 1)
        self.forecastTemp2 = QLabel(self.centralwidget)
        self.forecastTemp2.setText("")
        self.forecastTemp2.setObjectName("forecastTemp2")
        self.gridLayout_2.addWidget(self.forecastTemp2, 2, 1, 1, 1)
        self.forecastTemp5 = QLabel(self.centralwidget)
        self.forecastTemp5.setText("")
        self.forecastTemp5.setObjectName("forecastTemp5")
        self.gridLayout_2.addWidget(self.forecastTemp5, 2, 4, 1, 1)
        self.forecastTemp4 = QLabel(self.centralwidget)
        self.forecastTemp4.setText("")
        self.forecastTemp4.setObjectName("forecastTemp4")
        self.gridLayout_2.addWidget(self.forecastTemp4, 2, 3, 1, 1)
        self.forecastIcon2 = QLabel(self.centralwidget)
        sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.forecastIcon2.sizePolicy().hasHeightForWidth())
        self.forecastIcon2.setSizePolicy(sizePolicy)
        self.forecastIcon2.setMinimumSize(QSize(64, 64))
        self.forecastIcon2.setMaximumSize(QSize(200, 32))
        self.forecastIcon2.setBaseSize(QSize(0, 0))
        self.forecastIcon2.setText("")
        self.forecastIcon2.setAlignment(Qt.AlignCenter)
        self.forecastIcon2.setObjectName("forecastIcon2")
        self.gridLayout_2.addWidget(self.forecastIcon2, 1, 1, 1, 1)
        self.forecastIcon5 = QLabel(self.centralwidget)
        sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.forecastIcon5.sizePolicy().hasHeightForWidth())
        self.forecastIcon5.setSizePolicy(sizePolicy)
        self.forecastIcon5.setMinimumSize(QSize(64, 64))
        self.forecastIcon5.setMaximumSize(QSize(200, 32))
        self.forecastIcon5.setBaseSize(QSize(0, 0))
        self.forecastIcon5.setText("")
        self.forecastIcon5.setAlignment(Qt.AlignCenter)
        self.forecastIcon5.setObjectName("forecastIcon5")
        self.gridLayout_2.addWidget(self.forecastIcon5, 1, 4, 1, 1)
        self.forecastIcon1 = QLabel(self.centralwidget)
        sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.forecastIcon1.sizePolicy().hasHeightForWidth())
        self.forecastIcon1.setSizePolicy(sizePolicy)
        self.forecastIcon1.setMinimumSize(QSize(64, 64))
        self.forecastIcon1.setMaximumSize(QSize(200, 32))
        self.forecastIcon1.setBaseSize(QSize(0, 0))
        self.forecastIcon1.setText("")
        self.forecastIcon1.setAlignment(Qt.AlignCenter)
        self.forecastIcon1.setObjectName("forecastIcon1")
        self.gridLayout_2.addWidget(self.forecastIcon1, 1, 0, 1, 1)
        self.forecastIcon3 = QLabel(self.centralwidget)
        sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.forecastIcon3.sizePolicy().hasHeightForWidth())
        self.forecastIcon3.setSizePolicy(sizePolicy)
        self.forecastIcon3.setMinimumSize(QSize(64, 64))
        self.forecastIcon3.setMaximumSize(QSize(200, 32))
        self.forecastIcon3.setBaseSize(QSize(0, 0))
        self.forecastIcon3.setText("")
        self.forecastIcon3.setAlignment(Qt.AlignCenter)
        self.forecastIcon3.setObjectName("forecastIcon3")
        self.gridLayout_2.addWidget(self.forecastIcon3, 1, 2, 1, 1)
        self.forecastTemp3 = QLabel(self.centralwidget)
        self.forecastTemp3.setText("")
        self.forecastTemp3.setObjectName("forecastTemp3")
        self.gridLayout_2.addWidget(self.forecastTemp3, 2, 2, 1, 1)
        self.forecastTemp1 = QLabel(self.centralwidget)
        self.forecastTemp1.setText("")
        self.forecastTemp1.setObjectName("forecastTemp1")
        self.gridLayout_2.addWidget(self.forecastTemp1, 2, 0, 1, 1)
        self.forecastTime1 = QLabel(self.centralwidget)
        self.forecastTime1.setAlignment(Qt.AlignCenter)
        self.forecastTime1.setObjectName("forecastTime1")
        self.gridLayout_2.addWidget(self.forecastTime1, 0, 0, 1, 1)
        self.forecastTime2 = QLabel(self.centralwidget)
        self.forecastTime2.setAlignment(Qt.AlignCenter)
        self.forecastTime2.setObjectName("forecastTime2")
        self.gridLayout_2.addWidget(self.forecastTime2, 0, 1, 1, 1)
        self.forecastTime3 = QLabel(self.centralwidget)
        self.forecastTime3.setAlignment(Qt.AlignCenter)
        self.forecastTime3.setObjectName("forecastTime3")
        self.gridLayout_2.addWidget(self.forecastTime3, 0, 2, 1, 1)
        self.forecastTime4 = QLabel(self.centralwidget)
        self.forecastTime4.setAlignment(Qt.AlignCenter)
        self.forecastTime4.setObjectName("forecastTime4")
        self.gridLayout_2.addWidget(self.forecastTime4, 0, 3, 1, 1)
        self.forecastTime5 = QLabel(self.centralwidget)
        self.forecastTime5.setAlignment(Qt.AlignCenter)
        self.forecastTime5.setObjectName("forecastTime5")
        self.gridLayout_2.addWidget(self.forecastTime5, 0, 4, 1, 1)
        self.verticalLayout.addLayout(self.gridLayout_2)
        self.gridLayout = QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.formLayout = QFormLayout()
        self.formLayout.setObjectName("formLayout")
        self.label_5 = QLabel(self.centralwidget)
        font = QFont()
        font.setBold(True)
        font.setWeight(75)
        self.label_5.setFont(font)
        self.label_5.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label_5.setObjectName("label_5")
        self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_5)
        self.label_6 = QLabel(self.centralwidget)
        self.label_6.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label_6.setObjectName("label_6")
        self.formLayout.setWidget(3, QFormLayout.LabelRole, self.label_6)
        self.temperatureLabel = QLabel(self.centralwidget)
        self.temperatureLabel.setText("")
        self.temperatureLabel.setObjectName("temperatureLabel")
        self.formLayout.setWidget(3, QFormLayout.FieldRole, self.temperatureLabel)
        self.label_7 = QLabel(self.centralwidget)
        self.label_7.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label_7.setObjectName("label_7")
        self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_7)
        self.humidityLabel = QLabel(self.centralwidget)
        self.humidityLabel.setText("")
        self.humidityLabel.setObjectName("humidityLabel")
        self.formLayout.setWidget(4, QFormLayout.FieldRole, self.humidityLabel)
        self.label_8 = QLabel(self.centralwidget)
        self.label_8.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label_8.setObjectName("label_8")
        self.formLayout.setWidget(5, QFormLayout.LabelRole, self.label_8)
        self.pressureLabel = QLabel(self.centralwidget)
        self.pressureLabel.setText("")
        self.pressureLabel.setObjectName("pressureLabel")
        self.formLayout.setWidget(5, QFormLayout.FieldRole, self.pressureLabel)
        self.label = QLabel(self.centralwidget)
        font = QFont()
        font.setBold(True)
        font.setWeight(75)
        self.label.setFont(font)
        self.label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label.setObjectName("label")
        self.formLayout.setWidget(6, QFormLayout.LabelRole, self.label)
        self.label_2 = QLabel(self.centralwidget)
        self.label_2.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label_2.setObjectName("label_2")
        self.formLayout.setWidget(7, QFormLayout.LabelRole, self.label_2)
        self.longitudeLabel = QLabel(self.centralwidget)
        self.longitudeLabel.setText("")
        self.longitudeLabel.setObjectName("longitudeLabel")
        self.formLayout.setWidget(7, QFormLayout.FieldRole, self.longitudeLabel)
        self.label_3 = QLabel(self.centralwidget)
        self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label_3.setObjectName("label_3")
        self.formLayout.setWidget(8, QFormLayout.LabelRole, self.label_3)
        self.latitudeLabel = QLabel(self.centralwidget)
        self.latitudeLabel.setText("")
        self.latitudeLabel.setObjectName("latitudeLabel")
        self.formLayout.setWidget(8, QFormLayout.FieldRole, self.latitudeLabel)
        self.label_4 = QLabel(self.centralwidget)
        self.label_4.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label_4.setObjectName("label_4")
        self.formLayout.setWidget(9, QFormLayout.LabelRole, self.label_4)
        self.sunriseLabel = QLabel(self.centralwidget)
        self.sunriseLabel.setText("")
        self.sunriseLabel.setObjectName("sunriseLabel")
        self.formLayout.setWidget(9, QFormLayout.FieldRole, self.sunriseLabel)
        self.label_9 = QLabel(self.centralwidget)
        self.label_9.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label_9.setObjectName("label_9")
        self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label_9)
        self.label_10 = QLabel(self.centralwidget)
        font = QFont()
        font.setBold(True)
        font.setWeight(75)
        self.label_10.setFont(font)
        self.label_10.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
        self.label_10.setObjectName("label_10")
        self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label_10)
        self.windLabel = QLabel(self.centralwidget)
        self.windLabel.setText("")
        self.windLabel.setObjectName("windLabel")
        self.formLayout.setWidget(1, QFormLayout.FieldRole, self.windLabel)
        self.label_11 = QLabel(self.centralwidget)
        self.label_11.setText("")
        self.label_11.setObjectName("label_11")
        self.formLayout.setWidget(0, QFormLayout.FieldRole, self.label_11)
        self.label_13 = QLabel(self.centralwidget)
        self.label_13.setText("")
        self.label_13.setObjectName("label_13")
        self.formLayout.setWidget(6, QFormLayout.FieldRole, self.label_13)
        self.label_12 = QLabel(self.centralwidget)
        self.label_12.setText("")
        self.label_12.setObjectName("label_12")
        self.formLayout.setWidget(2, QFormLayout.FieldRole, self.label_12)
        self.gridLayout.addLayout(self.formLayout, 1, 0, 1, 1)
        self.verticalLayout.addLayout(self.gridLayout)
        self.horizontalLayout.addLayout(self.verticalLayout)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Raindar"))
        self.lineEdit.setText(_translate("MainWindow", "Utrecht,the Netherlands"))
        self.forecastTime1.setText(_translate("MainWindow", "+3h"))
        self.forecastTime2.setText(_translate("MainWindow", "+6h"))
        self.forecastTime3.setText(_translate("MainWindow", "+9h"))
        self.forecastTime4.setText(_translate("MainWindow", "+12h"))
        self.forecastTime5.setText(_translate("MainWindow", "+15h"))
        self.label_5.setText(_translate("MainWindow", "Barometer"))
        self.label_6.setText(_translate("MainWindow", "Temperature"))
        self.label_7.setText(_translate("MainWindow", "Humidity"))
        self.label_8.setText(_translate("MainWindow", "Pressure"))
        self.label.setText(_translate("MainWindow", "Location"))
        self.label_2.setText(_translate("MainWindow", "Longitude"))
        self.label_3.setText(_translate("MainWindow", "Latitude"))
        self.label_4.setText(_translate("MainWindow", "Sunrise"))
        self.label_9.setText(_translate("MainWindow", "Speed"))
        self.label_10.setText(_translate("MainWindow", "Wind"))


    def alert(self, message):
        alert = QMessageBox.warning(self, "Warning", message)

    def update_weather(self):
        worker = WeatherWorker(self.lineEdit.text())
        worker.signals.result.connect(self.weather_result)
        worker.signals.error.connect(self.alert)
        self.threadpool.start(worker)

    def weather_result(self, weather, forecasts):
        self.latitudeLabel.setText("%.2f °" % weather['coord']['lat'])
        self.longitudeLabel.setText("%.2f °" % weather['coord']['lon'])

        self.windLabel.setText("%.2f m/s" % weather['wind']['speed'])

        self.temperatureLabel.setText("%.1f °C" % weather['main']['temp'])
        self.pressureLabel.setText("%d" % weather['main']['pressure'])
        self.humidityLabel.setText("%d" % weather['main']['humidity'])

        self.sunriseLabel.setText(from_ts_to_time_of_day(weather['sys']['sunrise']))

        self.weatherLabel.setText("%s (%s)" % (
            weather['weather'][0]['main'],
            weather['weather'][0]['description']
        )
                                  )

        self.set_weather_icon(self.weatherIcon, weather['weather'])

        for n, forecast in enumerate(forecasts['list'][:5], 1):
            getattr(self, 'forecastTime%d' % n).setText(from_ts_to_time_of_day(forecast['dt']))
            self.set_weather_icon(getattr(self, 'forecastIcon%d' % n), forecast['weather'])
            getattr(self, 'forecastTemp%d' % n).setText("%.1f °C" % forecast['main']['temp'])

    def set_weather_icon(self, label, weather):
        label.setPixmap(QPixmap(os.path.join('images', "%s.png" % weather[0]['icon'])))
Ejemplo n.º 8
0
class Main_Window(QWidget):
    sig = Signal(list)

    def __init__(self):
        super().__init__()
        self.dataport = None
        self.cfgport = None
        self.stop = 0

        # set up qthreads
        self.threadpool = QThreadPool()

        self.layout = QGridLayout()
        self.setFixedSize(1280, 960)

        #declare widgets
        self.cfg_port = COM_Ports()
        self.data_port = COM_Ports()
        self.plot = Radar_Plot()
        self.log = Logging_Window()
        self.start_button = QPushButton("Start")
        self.stop_button = QPushButton("Stop")

        # declare and set up labels
        self.cfg_label = QLabel("Config")
        self.data_label = QLabel("Data")
        self.cfg_label.setMaximumSize(100, 30)
        self.data_label.setMaximumSize(100, 30)

        #set button callbacks
        self.start_button.clicked.connect(self.start_callback)
        self.stop_button.clicked.connect(self.stop_callback)

        #insert widgets to layout
        self.layout.addWidget(self.cfg_label, 0, 1)
        self.layout.addWidget(self.data_label, 0, 2)
        self.layout.addWidget(self.cfg_port, 1, 1)
        self.layout.addWidget(self.data_port, 1, 2)
        self.layout.addWidget(self.start_button, 2, 1)
        self.layout.addWidget(self.stop_button, 2, 2)
        self.layout.addWidget(self.plot, 0, 0, 6, 1)
        self.layout.addWidget(self.log, 3, 1, 4, 2)

        # add to widget
        self.setLayout(self.layout)

        self.sig.connect(self.plot.update_image)

    def start_callback(self):
        self.dataport = serial.Serial(port=self.data_port.currentText()[-5:-1],
                                      baudrate=921600)
        self.cfgport = serial.Serial(port=self.cfg_port.currentText()[-5:-1],
                                     baudrate=115200)

        self.log.append("Data port:" + self.data_port.currentText()[-5:-1] +
                        "baudrate: 921600")
        self.log.append("Cfg port:" + self.cfg_port.currentText()[-5:-1] +
                        "baudrate: 115200")

        f = open(cfg_file, "r")
        settings = f.readlines()
        for setting in settings:
            self.cfgport.write(setting.encode())
            self.log.append(setting)
            time.sleep(0.05)

        time.sleep(0.2)

        # start getting serial data and doing ml
        serial_worker = Worker(self.get_serial_data)
        self.threadpool.start(serial_worker)

    def stop_callback(self):
        try:
            self.stop = 1
            self.cfgport.write(("sensorStop\n").encode())
            self.log.append("sensorStop")
            self.cfgport.close()
            self.dataport.close()

        except:
            self.log.append("Invalid port")

    def get_serial_data(self):
        magic_word = b'\x02\x01\x04\x03\x06\x05\x08\x07'
        self.stop = 0
        while self.stop == 0:
            buffer = []
            byte_counter = 0

            while byte_counter < 2 * (
                    512 * 64 + 64
            ):  # (512 * 64 + 64) is 1 packet length, getting at least 2 packets guarantees at least one complete packet
                waiting = self.dataport.in_waiting
                buffer.append(self.dataport.read(waiting))
                byte_counter += waiting
                time.sleep(0.05)

            buffer = b''.join(buffer)  # combines into a single byte string
            buffer = buffer.split(
                magic_word
            )  # splits into packets based on magic word at start of header

            self.sig.emit(buffer)

    def do_ml(self):
        output = False
        self.log.append(str(output))  # log to output terminal
Ejemplo n.º 9
0
class AppView(QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Arrival")

        self.gscene = QGraphicsScene()
        self.setScene(self.gscene)
        CanvasSize = (WorldSize[0] * WorldScale, WorldSize[1] * WorldScale)
        self.gscene.setSceneRect(0, 0, *CanvasSize)
        global _Scene
        _Scene = self.gscene

        worker = Worker(execute_this_fn)
        worker.signals.result.connect(print_output)
        worker.signals.finished.connect(thread_complete)
        worker.signals.progress.connect(progress_fn)
        self.worker = worker

        self.threadpool = QThreadPool()
        self.threadpool.start(worker)

    def mousePressEvent(self, event):
        p = self.mapToScene(event.pos())
        WorldCenter = (WorldSize[0] // 2, WorldSize[1] // 2)
        mouse = (p.x() // WorldScale - WorldCenter[0],
                 p.y() // WorldScale - WorldCenter[1])
        self.worker.mouse_click = mouse

    def keyPressEvent(self, event):
        ctrl = (event.modifiers() == Qt.ControlModifier) or (
            event.modifiers() == (Qt.ControlModifier | Qt.ShiftModifier))
        if ctrl:
            if event.key() == 45:
                self.zoom_out()
            elif (event.key() == 43) or (event.key() == 61):
                self.zoom_in()
            elif event.key() == 71:
                self.input_state()
            elif event.key() == 73:
                self.ocr()
            else:
                print('keyboard:', event.key(), file=sys.stderr)

    def zoom_in(self):
        global WorldScale
        WorldScale += 1
        if WorldScale > 14:
            WorldScale = int(WorldScale * 1.1)
        self.worker.redraw = True

    def zoom_out(self):
        global WorldScale
        if WorldScale > 14:
            WorldScale = int(WorldScale / 1.1)
        WorldScale = max(1, WorldScale - 1)
        self.worker.redraw = True

    def input_state(self):
        text, ok = QInputDialog().getText(self, "Enter state", "State:",
                                          QLineEdit.Normal,
                                          '[2, [1, -1], 0, []]')
        if ok and text:
            self.worker.set_state = eval(text)
            self.worker.mouse_click = (-1000, -1000)

    def ocr(self):
        self.worker.ocr = True
Ejemplo n.º 10
0
class App(AppInterface):
    """App class

    Main object that contains everything else.
    """
    def __init__(self):
        super().__init__()
        self.player = Player(self, 96000)
        self.client = Client(self)
        self._threadpool = QThreadPool()
        self.main = None

    def run(self):
        """Runs the app"""
        # Qt GUI
        app = QApplication([])
        self.main = PlayerWindow(self)

        if not self.client.connect():
            dialog = LoginDialog(self, self.main)
            dialog.show()
        else:
            self.display_latest_albums()

        self.main.show()

        # Run the main Qt loop
        app.exec_()
        self.client.stop()

    def display_latest_albums(self):
        """Fetches then displays latests albums inside the GUI."""
        def display_latest_albums():
            self.main.display_albums(self.client.get_latest_albums())

        if self.client.logged_in:
            worker = Worker(display_latest_albums)
            self._threadpool.start(worker)

    def play_songs(self, songs: List[Song]):
        """Replaces the queue with the given list of songs and start playing.

        Parameters
        ----------
        songs : List[Song]
            The list of songs to start playing.
        """
        self.player.play_new_queue(songs)
        return songs

    def add_to_queue(self, songs: List[Song]):
        self.player.add_to_queue(songs)
        self.main.add_to_queue(songs)

    def download_stream(self, song: Song):
        """Fills the buffer of a given song."""
        if not song.read_from_cache():
            self.client.get_audio_stream(song)
            song.write_to_cache()

    def search(self, term):
        if (len(term) == 0):
            return
        worker = Worker(
            lambda: self.main.display_albums(self.client.search_albums(term)))
        self._threadpool.start(worker)
Ejemplo n.º 11
0
class AsyncFileAnalyzer(QObject):
    def __init__(self):
        QObject.__init__(self)
        self.threadpool = QThreadPool()
        self._waiting_for_info = {}
        self._cache = LRUCache(maxsize=10)

    def request_waveform(self, path, callback=None):
        if isinstance(path, FileKey):
            file_key = path
        else:
            file_key = FileKey(path)
        file_info = self._cache.get(file_key)
        if file_info is not None:
            waveform = file_info.get("waveform")
            callback(file_key, waveform)
            return

        def our_callback(path, file_info):
            if file_info:
                waveform = file_info.get("waveform")
                callback(path, waveform)

        self._request_info(path, callback=our_callback)

    def request_file_metadata(self, path, callback=None):
        if isinstance(path, FileKey):
            file_key = path
        else:
            file_key = FileKey(path)
        file_info = self._cache.get(file_key)
        if file_info is not None:
            metadata = Metadata.from_file_info(file_info)
            callback(file_key, metadata)
            return

        def our_callback(path, file_info):
            if file_info:
                metadata = Metadata.from_file_info(file_info)
            else:
                metadata = None
            callback(path, metadata)

        self._request_info(path, callback=our_callback)

    def _request_info(self, file_key, callback):
        logger.debug("File info for %r not known yet", str(file_key))
        waiting_list = self._waiting_for_info.get(file_key)
        if waiting_list:
            logger.debug("Already requested, adding to the waiting list")
            waiting_list.append(callback)
        else:
            worker = FileAnalyzerWorker(file_key)
            our_callback = partial(self._file_info_received, file_key)
            our_error_callback = partial(self._file_info_error, file_key)
            self._waiting_for_info[file_key] = [callback]
            worker.signals.finished.connect(our_callback)
            worker.signals.error.connect(our_error_callback)
            self.threadpool.start(worker)

    def _file_info_received(self, file_key, file_info):
        logger.debug("file_info_received for %r called with %r", file_key, file_info)
        self._cache.put(file_key, file_info)
        callbacks = self._waiting_for_info.pop(file_key)
        for callback in callbacks:
            callback(file_key, file_info)

    def _file_info_error(self, file_key, err):
        logger.debug("file_info_error for %r called with %r", file_key, err)
        callbacks = self._waiting_for_info.pop(file_key)
        for callback in callbacks:
            callback(file_key, None)

    def get_file_info(self, path):
        if not isinstance(path, FileKey):
            path = FileKey(path)
        return self._cache.get(path)

    def get_file_metadata(self, path):
        file_info = self.get_file_info(path)
        if file_info is not None:
            return Metadata.from_file_info(file_info)
        else:
            return None
Ejemplo n.º 12
0
class WindowManager:
    def __init__(self, windows):
        self.windows = windows
        # -Variables-
        self.threadpool = QThreadPool()
        self.eventHandler = EventHandler()
        self.gridOrder = [
            (0, 0),
            (0, 1),
            (1, 0),
            (1, 1),
            (0, 2),
            (1, 2),
            (2, 0),
            (2, 1),
            (2, 2),
            #   (0, 3),
            #   (1, 3),
            #   (2, 3),
            #   (3, 0),
            #   (3, 1),
            #   (3, 2),
            #   (3, 3), 16 Stations
        ]
        self.stationIDs = range(0, len(self.gridOrder))
        # -Setup-
        self.initialize_windows()
        self.initialize_threads()
        self.initialize_binds()
        self.initialize_widgets()
        # Key: stationID
        # Value: Station Class
        self.stations = {x: Station(self.windows, x) for x in self.stationIDs}
        self.deviceID_to_stationID = {}

        # -Other-
        self.load_page_stations()
        # Search devices is username and password is stored
        if (settingsManager.value('username')
                and settingsManager.value('password')):
            self.search_for_devices()
        else:
            self._update_stations()
        # Show window
        self.windows['main'].show()
        # Focus window
        self.windows['main'].activateWindow()
        self.windows['main'].raise_()

        self.windows['main'].stackedWidget_mainContents.setStyleSheet(
            """QWidget[page="true"]{background-image:url(\"%s\"); background-position: center; background-origin: padding;}"""
            % ResourcePaths.images.golf_icon_png.replace('\\', '/'))  # nopep8

    # -Initialize methods-
    def initialize_windows(self):
        """
        Set up all windows for this application
        """
        # -Event Filter-
        for window in self.windows.values():
            window.installEventFilter(self.eventHandler)
        self.eventHandler.addFilter(Qt.Key_Return,
                                    self.clicked_login_connect,
                                    parent=self.windows['login'])
        self.eventHandler.addFilter(Qt.Key_Delete,
                                    self.keyPress_edit_deleteSession,
                                    parent=self.windows['edit'])
        # -Images-
        # Refresh
        icon = QPixmap(ResourcePaths.images.refresh)
        self.windows['main'].pushButton_refresh.setIcon(icon)
        # Settings
        icon = QPixmap(ResourcePaths.images.settings)
        self.windows['main'].pushButton_settings.setIcon(icon)
        self.windows['main'].pushButton_settings.setIconSize(QSize(18, 18))

        # -Load saved data-
        if settingsManager.value('settings')['saveLogin']:
            # Load account data
            self.windows['login'].lineEdit_email.setText(
                settingsManager.value('username'))
            self.windows['login'].lineEdit_password.setText(
                settingsManager.value('password'))

    def initialize_widgets(self):
        """Load all widgets for the main window"""
        stationTemplate_path = ResourcePaths.ui_files.stationQWidget
        statisticsTemplate_path = ResourcePaths.ui_files.statisticsQWidget

        # Load stations
        with open(stationTemplate_path) as station_ui:
            station_ui_lines = station_ui.read()
        with open(statisticsTemplate_path) as statistic_ui:
            statistic_ui_lines = statistic_ui.read()

        for i, stationID in enumerate(self.stationIDs):
            # Load station
            new_station_path = os.path.join(settingsManager.save_folder,
                                            'generated_ui_files',
                                            f'station_{stationID}.ui')
            new_station_ui = open(new_station_path, 'w')
            new_station_ui_lines = station_ui_lines.replace(
                '_1">', f'_{stationID}">')
            new_station_ui_lines = new_station_ui_lines.replace(
                '<class>frame_station_1</class>',
                f'<class>frame_station_{stationID}</class>')  # nopep8
            new_station_ui.write(new_station_ui_lines)
            new_station_ui.close()
            station = loader.load(new_station_path)
            # Load statistic
            new_statistic_path = os.path.join(settingsManager.save_folder,
                                              'generated_ui_files',
                                              f'statistic_{stationID}.ui')
            new_statistic_ui = open(new_statistic_path, 'w')
            new_statistic_ui_lines = statistic_ui_lines.replace(
                '_1">', f'_{stationID}">')
            new_statistic_ui_lines = new_statistic_ui_lines.replace(
                '<class>frame_statistic_1</class>',
                f'<class>frame_statistic_{stationID}</class>')  # nopep8
            new_statistic_ui.write(new_statistic_ui_lines)
            new_statistic_ui.close()
            statistic = loader.load(new_statistic_path)

            self.windows['main'].gridLayout_page_stations.addWidget(
                station, self.gridOrder[i][0], self.gridOrder[i][1], 1,
                1)  # nopep8
            self.windows['main'].gridLayout_page_statistics.addWidget(
                statistic, self.gridOrder[i][0], self.gridOrder[i][1], 1,
                1)  # nopep8

    def initialize_threads(self):
        """
        Setup all threads used for this application
        """
        self.device_retriever = DeviceRetriever(
            parent=app,
            settingsManager=settingsManager,
            disable_widgets=[
                self.windows['main'].pushButton_refresh,
                self.windows['login'].pushButton_connect,
            ],
            label_widget=self.windows['main'].label_info)
        # Finished Signal
        reconnect(self.device_retriever.signals.finished,
                  self._update_stations)  # nopep8
        # Error Signal
        reconnect(self.device_retriever.signals.error,
                  self.show_error)  # nopep8

    def initialize_binds(self):
        """
        Bind the widgets to other methods
        """
        # -Main Window-
        reconnect(self.windows['main'].pushButton_login.clicked,
                  lambda *args: self.clicked_main_openLoginWindow())
        reconnect(self.windows['main'].pushButton_refresh.clicked,
                  lambda *args: self.search_for_devices())
        reconnect(self.windows['main'].pushButton_switch.clicked,
                  lambda *args: self.clicked_main_switchPage())
        reconnect(self.windows['main'].pushButton_settings.clicked,
                  lambda *args: self.clicked_main_openSettingsWindow())

        # -Login Window-
        reconnect(self.windows['login'].pushButton_connect.clicked,
                  lambda *args: self.clicked_login_connect())

        # -Session Window-
        time_buttons = [
            self.windows['session'].pushButton_30m,
            self.windows['session'].pushButton_1h,
            self.windows['session'].pushButton_2h,
            self.windows['session'].pushButton_3h,
        ]
        for button in time_buttons:
            reconnect(button.clicked,
                      lambda *args, wig=button: self.
                      clicked_session_createNewSession(
                          wig.property('duration').toPython()))
        # Custom Button
        timeEdit_duration = self.windows['session'].timeEdit_duration
        reconnect(self.windows['session'].pushButton_custom.clicked,
                  lambda *args, wig=timeEdit_duration: self.
                  clicked_session_createNewSession(
                      timeEdit_duration.property('time').toPython()))
        # Radio Buttons
        reconnect(
            self.windows['session'].radioButton_startAt.clicked,
            lambda: self.windows['session'].timeEdit_startAt.setEnabled(True))
        reconnect(
            self.windows['session'].radioButton_append.clicked,
            lambda: self.windows['session'].timeEdit_startAt.setEnabled(False))
        reconnect(
            self.windows['session'].radioButton_now.clicked,
            lambda: self.windows['session'].timeEdit_startAt.setEnabled(False))

        # -Settings Window-
        reconnect(self.windows['settings'].pushButton_apply.clicked,
                  lambda *args: self.clicked_settings_applySettings())
        reconnect(self.windows['settings'].pushButton_resetAll.clicked,
                  lambda *args: self.clicked_settings_resetAllSettings())
        reconnect(self.windows['settings'].pushButton_export.clicked,
                  lambda *args: self.clicked_settings_export())

    def refresh(self):
        """
        Refresh all stations and statistics
        """
        for station in self.stations.values():
            station.refresh()
        self.update_stackedWidget_minimumSize()

    # -Page loads-
    def update_stackedWidget_minimumSize(self):
        """
        Update the minimum size of the stacked widget
        """
        stackedWidget = self.windows['main'].stackedWidget_mainContents
        # Get minimum size of currently laoded page
        curPage_minSize = stackedWidget.currentWidget().minimumSizeHint()
        # Set minimum size for stacked widget
        stackedWidget.setMinimumSize(curPage_minSize)

    def load_page_stations(self):
        """
        Load the stations page
        """
        # -Initialize-
        # Load page
        stackedWidget = self.windows['main'].stackedWidget_mainContents
        stackedWidget.setCurrentIndex(0)
        # Change switch button text
        self.windows['main'].pushButton_switch.setText('Statistics')
        # Other
        self.refresh()

    def load_page_statistics(self):
        """
        Load the statistics page
        """
        # -Initialize-
        # Load page
        stackedWidget = self.windows['main'].stackedWidget_mainContents
        stackedWidget.setCurrentIndex(1)
        # Change switch button text
        self.windows['main'].pushButton_switch.setText('Stations')

        # Other
        self.refresh()

    # -Button clicks-
    def clicked_main_switchPage(self):
        """
        Switch page between stations and statistics
        """
        stackedWidget = self.windows['main'].stackedWidget_mainContents
        # 0 -> Station page
        # 1 -> Statistics page
        page_index = stackedWidget.currentIndex()

        if page_index == 0:
            self.load_page_statistics()
        elif page_index == 1:
            self.load_page_stations()

    def clicked_main_openLoginWindow(self):
        """
        Open the login window to enter the
        kasa app account data
        """
        if not settingsManager.value('settings')['saveLogin']:
            # Do not save login information, so
            # clear previously typed data
            self.windows['login'].lineEdit_email.setText('')
            self.windows['login'].lineEdit_password.setText('')
        # -Window Setup-
        # Reshow window
        self.windows['login'].show()
        # Focus window
        self.windows['login'].activateWindow()
        self.windows['login'].raise_()

    def clicked_login_connect(self):
        """
        Confirm the entered email and password
        and fill the main window
        """
        settingsManager.setValue('username',
                                 self.windows['login'].lineEdit_email.text())
        settingsManager.setValue(
            'password', self.windows['login'].lineEdit_password.text())

        if not self.search_for_devices():
            # User did not input any data
            return

        self.windows['login'].close()

    def clicked_main_openSettingsWindow(self):
        """
        Open the settings window
        """
        # -Load data-
        settings = settingsManager.value('settings')
        # Get states
        saveLoginCheckedState = Qt.CheckState.Checked if settings[
            'saveLogin'] else Qt.CheckState.Unchecked
        # Update window
        self.windows['settings'].checkBox_saveLogin.setCheckState(
            saveLoginCheckedState)

        # -Window Setup-
        # Reshow window
        self.windows['settings'].show()
        # Focus window
        self.windows['settings'].activateWindow()
        self.windows['settings'].raise_()

    def clicked_session_createNewSession(self, duration: dt.time):
        """
        Create a session which will either be appended to
        the currently running session or start immediately,
        based on the mode.

        Paramaters:
            duration(dt.time):
                Time length of the session
        """
        # -Get Variables-
        stationID = self.windows['session'].property('stationID')
        customerName = self.windows['session'].lineEdit_customerName.text()
        start_date: Union[dt.datetime, None]
        # Get Start Date
        if self.windows['session'].radioButton_startAt.isChecked():
            timeEdit = self.windows['session'].timeEdit_startAt
            time = timeEdit.property('time').toPython()
            start_date = dt.datetime.combine(dt.date.today(), time)
        elif self.windows['session'].radioButton_now.isChecked():
            start_date = dt.datetime.now()
        elif self.windows['session'].radioButton_append.isChecked():
            start_date = None
        # -Add session to station-
        station = self.stations[stationID]
        success = station.add_session(
            customerName=customerName,
            start_date=start_date,
            duration=duration,
        )
        # -Close Window-
        if success:
            self.windows['session'].setProperty('stationID', -1)
            self.windows['session'].close()

    def clicked_settings_applySettings(self):
        """
        Apply the settings entered by the user
        """
        saveLogin = self.windows['settings'].checkBox_saveLogin.isChecked()

        # Update data
        settingsManager.setValue('settings', {'saveLogin': saveLogin})
        # Close window
        self.windows['settings'].close()

    def clicked_settings_resetAllSettings(self):
        """
        Apply the settings entered by the user
        """
        msg = QMessageBox()
        msg.setWindowTitle('Confirmation')
        msg.setIcon(QMessageBox.Icon.Warning)
        messageBoxText = 'Resetting the application will:\n'
        messageBoxText += '  - Clear all data gathered on customer sessions.\n'
        messageBoxText += '  - Delete the saved login information and window positions.\n'
        messageBoxText += '  - Return to the default settings.\n'
        messageBoxText += 'This action is unrecoverable and will restart the application.'
        msg.setText(messageBoxText)  # nopep8
        msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        msg.setWindowFlag(Qt.WindowStaysOnTopHint)
        val = msg.exec_()
        if val == QMessageBox.Cancel:
            # Cancel operation
            return

        self.reset_application()

    def clicked_settings_export(self):
        """Export session history"""
        collected_histories: Dict[str, Station] = {}
        for station in self.stations.values():
            sessionTracker_data = station.sessionTracker.extract_data()
            if (sessionTracker_data is None
                    or not sessionTracker_data['tracked_sessions']):
                # No device registered for this station
                # OR no tracked sessions
                continue
            collected_histories[sessionTracker_data[
                'deviceID']] = sessionTracker_data['tracked_sessions']
        # -Get save path-
        defaultName = 'SessionHistoryExport_%s' % dt.datetime.now().strftime(
            r'%d-%m-%Y')
        filepath = QFileDialog.getSaveFileName(
            parent=self.windows['settings'],
            caption='Save File',
            dir=os.path.join(settingsManager.value('lastExportDir'),
                             defaultName),
            filter="Excel file (*.xlsx)",
        )[0]
        if not filepath:
            # No name specified
            return
        settingsManager.setValue('lastExportDir', os.path.dirname(filepath))
        # --Write to excel file--
        # Create a workbook and add a worksheet.
        workbook = xlsxwriter.Workbook(filepath)
        worksheet = workbook.add_worksheet()
        # -Create style-
        # Cell width and heights
        worksheet.set_column(0, 0, 30)
        worksheet.set_column(1, 1, 15)
        worksheet.set_column(2, 2, 20)
        worksheet.set_column(3, 5, 13.57)
        worksheet.set_column(6, 6, 50)
        worksheet.set_row(0, 30)
        worksheet.set_default_row(18.75)
        # Cell styles
        title_format = workbook.add_format()
        formats_1 = {
            'default': workbook.add_format(),
            'date': workbook.add_format(),
            'time': workbook.add_format(),
            'mergedCell': workbook.add_format(),
        }
        formats_2 = {
            'default': workbook.add_format(),
            'date': workbook.add_format(),
            'time': workbook.add_format(),
            'mergedCell': workbook.add_format(),
        }
        formatCycle = cycle([formats_1, formats_2])
        # ALignement
        title_format.set_align('center')
        title_format.set_align('vcenter')
        for format_1 in formats_1.values():
            format_1.set_align('center')
            format_1.set_align('vcenter')
            format_1.set_bg_color('#FFFFFF')
        for format_2 in formats_2.values():
            format_2.set_align('center')
            format_2.set_align('vcenter')
            format_2.set_bg_color('#DCE6F1')
        # Font
        title_format.set_font_size(12)
        # Borders
        title_format.set_bottom()
        # Cell backgrounds
        title_format.set_bg_color('#DCE6F1')
        # Format
        formats_1['date'].set_num_format('dd.mm.yyyy')
        formats_2['date'].set_num_format('dd.mm.yyyy')
        formats_1['time'].set_num_format('HH:MM')
        formats_2['time'].set_num_format('HH:MM')
        # -Cell Texts-
        # Titles
        titles = [
            'Device Name', 'Date', 'Customer Name', 'Session Start',
            'Session End', 'Duration', 'Device ID'
        ]
        for col, title in enumerate(titles):
            worksheet.write(0, col, title, title_format)
        # Content
        row = 1
        for deviceID, tracked_sessions in collected_histories.items():
            # -Device Name-
            formats = next(formatCycle)
            station = self.stations[self.deviceID_to_stationID[deviceID]]
            deviceName = station.device.deviceName
            if len(tracked_sessions) > 1:
                # Merge deviceID cells
                worksheet.merge_range(
                    'A%s:A%s' % ((row + 1),
                                 (row + 1) + len(tracked_sessions) - 1),
                    deviceName, formats['mergedCell'])
                worksheet.merge_range(
                    'G%s:G%s' % ((row + 1),
                                 (row + 1) + len(tracked_sessions) - 1),
                    deviceID, formats['mergedCell'])
            else:
                # Cant merge one cell
                worksheet.write(row, 0, deviceName, formats['mergedCell'])
                worksheet.write(row, 6, deviceID, formats['mergedCell'])
            # -Device ID-
            # -Session Data-
            for tracked_session in tracked_sessions:
                worksheet.write(row, 1, tracked_session.start_date,
                                formats['date'])
                worksheet.write(row, 2, tracked_session.customerName,
                                formats['default'])
                worksheet.write(row, 3, tracked_session.start_date,
                                formats['time'])
                worksheet.write(row, 4, tracked_session.end_date,
                                formats['time'])
                worksheet.write(row, 5, tracked_session.duration,
                                formats['time'])
                row += 1
        workbook.close()

        # TEMP
        subprocess.Popen(filepath, shell=True)

    # -Key presses-
    def keyPress_edit_deleteSession(self):
        """
        Delete selected queues
        """
        # Find currently open station in edit window
        station = self.stations[self.windows['edit'].property('stationID')]
        # Perform deletion on selection
        station.delete_selection()

    def reset_application(self):
        """Delete data file and restart the application"""
        del settingsManager.data
        os.execl(sys.executable, sys.executable, *sys.argv)

    def search_for_devices(self):
        """
        Refresh the main window by researching for
        devices and setting up the stations
        """
        if not (settingsManager.value('username')
                and settingsManager.value('password')):
            # No current device manager and username/password
            # was not changed
            msg = QMessageBox()
            msg.setWindowTitle('No Login Information')
            msg.setIcon(QMessageBox.Icon.Warning)
            msg.setText('Please enter your login data for the Kasa App.')
            msg.setStandardButtons(QMessageBox.Ok)
            msg.setWindowFlag(Qt.WindowStaysOnTopHint)
            msg.exec_()
            return False
        self.threadpool.start(self.device_retriever)
        return True

    def _update_stations(self, devices: list = []):
        """
        Checks if all plugs are still connected, and adds new ones
        if there have been detected new plugs.

        Paramaters:
            devices(list):
                List of devices found
        """
        if not devices:
            # No device registered on account (or no devices on remote control)
            pass
        # Sort devices by name
        devices = sorted(devices, key=lambda s: s.deviceName)
        tracked_sessions = settingsManager.value('tracked_sessions')
        for i, stationID in enumerate(self.stationIDs):
            station = self.stations[stationID]
            if i < len(devices):
                device = devices[i]
                try:
                    if self.deviceID_to_stationID[
                            device.deviceID] == stationID:
                        # Station at the place same place as before update -> no need for action
                        # Update, in case the plug name was changed
                        new_data = {'device': device}
                    else:
                        raise KeyError('Plug at the same position')
                except KeyError:
                    # See if the plug already existed
                    try:
                        old_stationID = self.deviceID_to_stationID[
                            device.deviceID]
                        # Plug has changed position
                        new_data = self.stations[old_stationID].extract_data()
                    except KeyError:
                        # Plug is completely new
                        new_data = {
                            'device': device,
                            'state': 'deactivated',
                        }
                        if device.deviceID in tracked_sessions.keys():
                            # Set tracked sessions to the saved ones
                            station.sessionTracker.update(
                                tracked_sessions=tracked_sessions[
                                    device.deviceID],
                                override=True)

                self.deviceID_to_stationID[device.deviceID] = stationID
                station.show(**new_data)
            else:
                station.reset(hide=True)

        self.refresh()

    def show_error(self, data):
        """
        Show the QMessagebox on this thread
        (Used for threads returning errors)
        """
        msg = QMessageBox()
        msg.setWindowFlag(Qt.WindowStaysOnTopHint)
        contact_creator = '\n\nPlease contact the creator and attach a screenshot of this error (+ expanded details)'

        if data['mode'] == 'untracked_connection_error':
            msg.setIcon(QMessageBox.Icon.Critical)
            msg.setWindowTitle('Untracked Connection Error')
            msg.setText(data['message'][0] + contact_creator)
            msg.setDetailedText(data['message'][1])
            msg.setStandardButtons(QMessageBox.Ok)
        elif data['mode'] == 'invalid_login_data':
            msg.setIcon(QMessageBox.Icon.Warning)
            msg.setWindowTitle('Invalid Login Data')
            msg.setText('Invalid username and/or password!')
            msg.setStandardButtons(QMessageBox.Ok)
        else:
            msg.setText(f'Invalid Error mode!' + contact_creator)
            msg.setDetailedText(repr(data))
        msg.exec_()
Ejemplo n.º 13
0
class Ui_MainWindow(object):
    def __init__(self):
        self.file_name = ""
        self.threadpool = QThreadPool()
        self.num_of_co = 1000
        self.alpha = 0.1
        self.watermarker = None

    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(1024, 768)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")
        self.verticalLayoutWidget_2 = QWidget(self.centralwidget)
        self.verticalLayoutWidget_2.setObjectName(u"verticalLayoutWidget_2")
        self.verticalLayoutWidget_2.setGeometry(QRect(0, 0, 1021, 511))
        self.pictureLayout = QVBoxLayout(self.verticalLayoutWidget_2)
        self.pictureLayout.setObjectName(u"pictureLayout")
        self.pictureLayout.setContentsMargins(0, 0, 0, 0)
        self.picture_label = QLabel(self.verticalLayoutWidget_2)
        self.picture_label.setObjectName(u"picture_label")

        self.pictureLayout.addWidget(self.picture_label)

        self.picture_button = QPushButton(self.verticalLayoutWidget_2)
        self.picture_button.setObjectName(u"picture_button")

        self.pictureLayout.addWidget(self.picture_button)

        self.horizontalLayoutWidget = QWidget(self.centralwidget)
        self.horizontalLayoutWidget.setObjectName(u"horizontalLayoutWidget")
        self.horizontalLayoutWidget.setGeometry(QRect(10, 520, 1011, 120))
        self.horizontalLayout_2 = QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
        self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout_3 = QVBoxLayout()
        self.verticalLayout_3.setObjectName(u"verticalLayout_3")
        self.label_2 = QLabel(self.horizontalLayoutWidget)
        self.label_2.setObjectName(u"label_2")

        self.verticalLayout_3.addWidget(self.label_2)

        self.DCT_encode = QPushButton(self.horizontalLayoutWidget)
        self.DCT_encode.setObjectName(u"DCT_encode")

        self.verticalLayout_3.addWidget(self.DCT_encode)

        self.DCT_decode = QPushButton(self.horizontalLayoutWidget)
        self.DCT_decode.setObjectName(u"DCT_decode")

        self.verticalLayout_3.addWidget(self.DCT_decode)

        self.label_4 = QLabel(self.horizontalLayoutWidget)
        self.label_4.setObjectName(u"label_4")

        self.verticalLayout_3.addWidget(self.label_4)

        self.DCT_masg = QLineEdit(self.horizontalLayoutWidget)
        self.DCT_masg.setObjectName(u"DCT_masg")

        self.verticalLayout_3.addWidget(self.DCT_masg)

        self.horizontalLayout_2.addLayout(self.verticalLayout_3)

        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setObjectName(u"verticalLayout")
        self.label_3 = QLabel(self.horizontalLayoutWidget)
        self.label_3.setObjectName(u"label_3")

        self.verticalLayout.addWidget(self.label_3)

        self.LSB_encode = QPushButton(self.horizontalLayoutWidget)
        self.LSB_encode.setObjectName(u"LSB_encode")

        self.verticalLayout.addWidget(self.LSB_encode)

        self.LSB_decode = QPushButton(self.horizontalLayoutWidget)
        self.LSB_decode.setObjectName(u"LSB_decode")

        self.verticalLayout.addWidget(self.LSB_decode)

        self.lsb_encode_msg = QLabel(self.horizontalLayoutWidget)
        self.lsb_encode_msg.setObjectName(u"lsb_encode_msg")

        self.verticalLayout.addWidget(self.lsb_encode_msg)

        self.LSB_msg = QLineEdit(self.horizontalLayoutWidget)
        self.LSB_msg.setObjectName(u"LSB_msg")

        self.verticalLayout.addWidget(self.LSB_msg)

        self.horizontalLayout_2.addLayout(self.verticalLayout)

        self.verticalLayoutWidget_3 = QWidget(self.centralwidget)
        self.verticalLayoutWidget_3.setObjectName(u"verticalLayoutWidget_3")
        self.verticalLayoutWidget_3.setGeometry(QRect(20, 650, 981, 51))
        self.verticalLayout_2 = QVBoxLayout(self.verticalLayoutWidget_3)
        self.verticalLayout_2.setObjectName(u"verticalLayout_2")
        self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.label_6 = QLabel(self.verticalLayoutWidget_3)
        self.label_6.setObjectName(u"label_6")

        self.verticalLayout_2.addWidget(self.label_6)

        self.progressBar = QProgressBar(self.verticalLayoutWidget_3)
        self.progressBar.setObjectName(u"progressBar")
        self.progressBar.setValue(0)

        self.verticalLayout_2.addWidget(self.progressBar)

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QMenuBar(MainWindow)
        self.menubar.setObjectName(u"menubar")
        self.menubar.setGeometry(QRect(0, 0, 1024, 21))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName(u"statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        # bind functions
        self.picture_button.clicked.connect(self.open_file)
        self.LSB_encode.clicked.connect(self.call_lsb_encode)
        self.LSB_decode.clicked.connect(self.call_lsb_decode)
        self.DCT_encode.clicked.connect(self.call_dct_encode)
        self.DCT_decode.clicked.connect(self.call_dct_decode)
        # end bind functions()
        self.hide_progress_bar()
        self.disable_Methods(True)

        QMetaObject.connectSlotsByName(MainWindow)

    # setupUi

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(
            QCoreApplication.translate("MainWindow", u"Watermarking tool",
                                       None))
        # self.picture_label.setText(QCoreApplication.translate("MainWindow", u"TextLabel", None))
        self.picture_button.setText(
            QCoreApplication.translate("MainWindow", u"Select picture", None))
        self.label_2.setText(
            QCoreApplication.translate("MainWindow", u"DCT", None))
        self.DCT_encode.setText(
            QCoreApplication.translate("MainWindow", u"encode", None))
        self.DCT_decode.setText(
            QCoreApplication.translate("MainWindow", u"decode", None))
        self.label_4.setText(
            QCoreApplication.translate("MainWindow", u"coeficient:", None))
        self.DCT_masg.setText(
            QCoreApplication.translate("MainWindow", u"1000, 0.1", None))
        self.label_3.setText(
            QCoreApplication.translate("MainWindow", u"LSB", None))
        self.LSB_encode.setText(
            QCoreApplication.translate("MainWindow", u"encode", None))
        self.LSB_decode.setText(
            QCoreApplication.translate("MainWindow", u"decode", None))
        self.lsb_encode_msg.setText(
            QCoreApplication.translate("MainWindow", u"LSB msg:", None))
        self.LSB_msg.setText(
            QCoreApplication.translate("MainWindow", u"secret", None))
        self.label_6.setText(
            QCoreApplication.translate("MainWindow", u"Work in progress:",
                                       None))

    # retranslateUi

    # def bindFunctions(self):

    def load_picture(self):
        pixmap = QPixmap(self.file_name[0])
        if pixmap.width() > pixmap.height():
            self.picture_label.setPixmap(
                pixmap.scaledToWidth(self.picture_label.width()))
        else:
            self.picture_label.setPixmap(
                pixmap.scaledToHeight(self.picture_label.height()))

    def open_file(self):
        self.hide_progress_bar()
        self.file_name = QFileDialog.getOpenFileName(
            None, "Open Image", "", "Image Files (*.png *.jpg *.bmp)")
        self.load_picture()
        self.disable_Methods(False)

    def method_LSB_encode(self, progress_callback):
        self.disable_Methods(True)
        progress_callback.emit(10)
        msg = self.LSB_msg.text()
        image = cv2.imread(self.file_name[0])
        watermarker = LSBWatermarker(image=image,
                                     mode='encode-message',
                                     message=msg,
                                     filename='result.png')
        progress_callback.emit(50)
        self.file_name = ["result.png", ""]
        watermarker.run()
        progress_callback.emit(90)
        self.load_picture()
        self.disable_Methods(False)
        progress_callback.emit(100)

    def method_LSB_decode(self, progress_callback):
        self.disable_Methods(True)
        self.LSB_msg.setText("")
        progress_callback.emit(10)
        image = cv2.imread(self.file_name[0])
        watermarker = LSBWatermarker(image=image, mode='decode-message')
        progress_callback.emit(50)
        watermarker.run()
        self.disable_Methods(False)
        progress_callback.emit(90)
        # self.show_decoded_msg(watermarker.decoded_msg)
        if watermarker.decoded_msg == "Provided file has no message encoded!":
            self.LSB_msg.setText(watermarker.decoded_msg)
        else:
            self.LSB_msg.setText("Decoded: " + watermarker.decoded_msg)
        progress_callback.emit(100)

    def method_DCT_encode(self, progress_callback):
        self.disable_Methods(True)
        progress_callback.emit(10)
        msg = self.DCT_masg.text().split(",")
        try:
            self.num_of_co = int(msg[0])
            self.alpha = float(msg[1])
            self.watermarker = WatermarkDCT(self.file_name[0],
                                            num_of_co=self.num_of_co,
                                            alpha=self.alpha)
            progress_callback.emit(50)
            self.watermarker.encode_watermark()
            self.file_name = ["result_DCT.png", ""]
            self.DCT_masg.setText("Picture is watermarked?:" +
                                  str(self.watermarker.detect_watermark()))

        except Exception as e:
            print(e)
            self.DCT_masg.setText("Error: wrong parameters: example 1000, 0.1")
        finally:
            progress_callback.emit(90)

            self.load_picture()
            self.disable_Methods(False)
            progress_callback.emit(100)

    def method_DCT_decode(self, progress_callback):
        self.disable_Methods(True)
        progress_callback.emit(10)
        try:
            if self.watermarker is None:
                self.watermarker = WatermarkDCT(self.file_name[0],
                                                num_of_co=self.num_of_co,
                                                alpha=self.alpha)
            progress_callback.emit(50)
            self.DCT_masg.setText(
                "Picture is watermarked?:" +
                str(self.watermarker.detect_watermark(self.file_name[0])))
        except Exception as e:
            print(e)
            self.DCT_masg.setText("Error: Unable to check the picture.")
        finally:
            progress_callback.emit(90)

            self.load_picture()
            self.disable_Methods(False)
            progress_callback.emit(100)

    def show_decoded_msg(self, msg):
        msgBox = QMessageBox()
        msgBox.setWindowTitle("Decoded message:")
        msgBox.setText(msg)
        msgBox.exec_()

    def thread_complete(self):
        print("THREAD COMPLETE!")

    def progress_fn(self, n):
        self.progressBar.setValue(n)

    def print_output(self, s):
        print(s)

    def call_lsb_encode(self):
        self.show_progress_bar()
        self.call_function(self.method_LSB_encode)

    def call_lsb_decode(self):
        self.show_progress_bar()
        self.call_function(self.method_LSB_decode)

    def call_dct_encode(self):
        self.show_progress_bar()
        self.call_function(self.method_DCT_encode)

    def call_dct_decode(self):
        self.show_progress_bar()
        self.call_function(self.method_DCT_decode)

    def call_function(self, fn):
        worker = Worker(
            fn)  # Any other args, kwargs are passed to the run function
        worker.signals.result.connect(self.print_output)
        worker.signals.finished.connect(self.thread_complete)
        worker.signals.progress.connect(self.progress_fn)

        # Execute
        self.threadpool.start(worker)

    def hide_progress_bar(self):
        self.progressBar.setValue(0)
        self.progressBar.setVisible(False)
        self.label_6.setVisible(False)

    def show_progress_bar(self):
        self.progressBar.setValue(0)
        self.progressBar.setVisible(True)
        self.label_6.setVisible(True)

    def disable_Methods(self, disable):
        self.LSB_encode.setDisabled(disable)
        self.LSB_decode.setDisabled(disable)
        self.DCT_decode.setDisabled(disable)
        self.DCT_encode.setDisabled(disable)
Ejemplo n.º 14
0
class LndOutputTab(OutputWidget):
    def __init__(self, lnd: Lnd, system_tray):
        super().__init__()
        self.system_tray = system_tray
        self.lnd = lnd

        self.process = lnd.process
        self.process.readyReadStandardOutput.connect(self.handle_output)
        self.setWindowTitle('LND Output')

        self.threadpool = QThreadPool()

        self.old_height = None
        self.old_timestamp = None

    def process_output_line(self, line: str):
        if 'Active chain: Bitcoin' in line:
            self.system_tray.menu.lnd_status_action.setText('LND starting')
        elif 'Waiting for wallet encryption password' in line:
            self.system_tray.menu.lnd_status_action.setText(
                'LND unlocking wallet')
            QTimer.singleShot(100, self.auto_unlock_wallet)
        elif 'Unable to synchronize wallet to chain' in line:
            self.process.terminate()
            self.restart_process()
        elif 'Unable to complete chain rescan' in line:
            self.process.terminate()
            self.restart_process()
        elif 'Starting HTLC Switch' in line:
            self.system_tray.set_green()
            self.system_tray.menu.lnd_status_action.setText('LND synced')
            self.system_tray.show_message(title='LND is ready')
        elif 'Caught up to height' in line:
            new_height = int(line.split(' ')[-1])
            timestamp = line.split('[INF]')[0].strip()
            new_timestamp = datetime.strptime(timestamp,
                                              '%Y-%m-%d %H:%M:%S.%f')
            if self.old_height is not None:
                change = new_height - self.old_height
                if change:
                    timestamp_change = new_timestamp - self.old_timestamp
                    total_left = 600000 - new_height
                    time_left = (total_left / change) * timestamp_change
                    humanized = humanize.naturaltime(-time_left)
                    self.system_tray.menu.lnd_status_action.setText(
                        f'ETA: {humanized}, caught up to height {new_height}')

            self.old_height = new_height
            self.old_timestamp = new_timestamp

    def restart_process(self):
        QTimer.singleShot(3000, self.process.start)

    @staticmethod
    def unlock_wallet(lnd, progress_callback, password: str):
        if password is None:
            return 'wallet not found'
        client = LndClient(lnd)
        try:
            client.unlock(password)
            return None
        except _Rendezvous as e:
            details = e.details()
            return details

    def generate_seed(self, new_seed_password: str):
        try:
            generate_seed_response = self.lnd.client.generate_seed(
                seed_password=new_seed_password)
        except _Rendezvous:
            log.error('generate_seed error', exc_info=True)
            raise

        seed = generate_seed_response.cipher_seed_mnemonic

        keyring_service_name = f'lnd_seed'
        keyring_user_name = ''.join(seed[0:2])
        log.info('generate_seed',
                 keyring_service_name=keyring_service_name,
                 keyring_user_name=keyring_user_name)

        keyring.set_password(service=keyring_service_name,
                             username=keyring_user_name,
                             password='******'.join(seed))

        keyring.set_password(service=f'{keyring_service_name}_seed_password',
                             username=keyring_user_name,
                             password=new_seed_password)
        return seed

    def handle_unlock_wallet(self, details: str):
        if details is None:
            return
        details = details.lower()
        # The Wallet Unlocker gRPC service disappears from LND's API
        # after the wallet is unlocked (or created/recovered)
        if 'unknown service lnrpc.walletunlocker' in details:
            pass
        # User needs to create a new wallet
        elif 'wallet not found' in details:
            new_wallet_password = get_random_password()
            keyring_service_name = keyring_user_name = f'lnd_wallet_password'
            log.info('create_wallet',
                     keyring_service_name=keyring_service_name,
                     keyring_user_name=keyring_user_name)
            keyring.set_password(service=keyring_service_name,
                                 username=keyring_user_name,
                                 password=new_wallet_password)
            seed = self.generate_seed(new_wallet_password)
            try:
                self.lnd.client.initialize_wallet(
                    wallet_password=new_wallet_password,
                    seed=seed,
                    seed_password=new_wallet_password)
            except _Rendezvous:
                log.error('initialize_wallet error', exc_info=True)
                raise
            keyring.set_password(service=f'lnd_mainnet_wallet_password',
                                 username=self.lnd.file['bitcoind.rpcuser'],
                                 password=new_wallet_password)
        else:
            log.warning('unlock_wallet failed', details=details, exc_info=True)

    def auto_unlock_wallet(self):
        keyring_service_name = f'lnd_mainnet_wallet_password'
        keyring_user_name = self.lnd.file['bitcoind.rpcuser']
        log.info('auto_unlock_wallet_get_password',
                 keyring_service_name=keyring_service_name,
                 keyring_user_name=keyring_user_name)
        password = keyring.get_password(
            service=keyring_service_name,
            username=keyring_user_name,
        )
        worker = Worker(fn=self.unlock_wallet, lnd=self.lnd, password=password)
        worker.signals.result.connect(self.handle_unlock_wallet)
        self.threadpool.start(worker)
Ejemplo n.º 15
0
class Logic(QObject):
    signal_0 = Signal()  # email column
    signal_1 = Signal()  # receiver file
    signal_2 = Signal()  # password
    signal_3 = Signal()  # my email
    signal_4 = Signal()  # subject

    sending_start = Signal()
    fail_email = Signal(str)
    success_email = Signal(str)
    sending_done = Signal(int, int)
    send_progress = Signal(int)
    estimate_time_finish = Signal(int)

    attachment_limit_reached = Signal()
    attachment_byte_limit = 20000000  #20 MB

    def __init__(self, model):
        super().__init__()
        self.logger = logging.getLogger(__name__)
        self.model = model
        self.is_valid_field_dict = dict()
        self.is_valid_field_dict['is_valid_email_column'] = False
        self.is_valid_field_dict['is_valid_receiver_file'] = False
        self.is_valid_field_dict['is_valid_password'] = False
        self.is_valid_field_dict['is_valid_my_email'] = False
        self.successful_email_counter = 0
        self.unsuccessful_list = []
        self.threadpool = QThreadPool()
        num_email_threads = 5
        self.threadpool.setMaxThreadCount(num_email_threads)
        self.total_email = 0
        self.thread_list = []

    def send_email(self, compulsory_text, optional_text, send_with_subject):
        is_valid_field = self.check_valid_field(compulsory_text)

        if not is_valid_field:
            return

        email_column, receiver_email_file, password, my_email = compulsory_text
        subject, body_msg, personalised_att = optional_text

        if send_with_subject and not subject:
            self.signal_4.emit()
            return

        receiver_df = pd.read_excel(receiver_email_file)
        if email_column not in receiver_df.columns:
            self.signal_0.emit()
            return

        # try:
        #     receiver_df = pd.read_excel(receiver_email_file)
        #     emails = receiver_df[email_column].to_numpy()
        # except KeyError:
        #     self.signal_0.emit()
        #     return

        self.logger.info('Start sending')

        try:
            context = ssl.create_default_context()
            server = smtplib.SMTP("smtp.gmail.com", 587)
            server.starttls(context=context)
            server.login(my_email, password)
            server.quit()
        except smtplib.SMTPAuthenticationError:
            self.signal_2.emit()
            self.signal_3.emit()
            return
        except:
            self.logger.exception("SMTP error")

        email_input = {
            'my_email': my_email,
            'password': password,
            'subject': subject,
            'body': body_msg,
            'email_column': email_column,
            'personalised_att': personalised_att
        }

        self.total_email = len(receiver_df)
        num_email_threads = min(1, len(receiver_df))
        receiver_df = np.array_split(receiver_df, num_email_threads)

        self.successful_email_counter = 0
        self.unsuccessful_list = []

        self.sending_start.emit()
        for sublist in receiver_df:
            email_thread = EmailThread(sublist, email_input)
            email_thread.signal.invalid_email_column.connect(
                self.show_invalid_email_error)
            email_thread.signal.fail_email.connect(self.update_fail_email)
            email_thread.signal.success_email.connect(
                self.update_successful_email)
            email_thread.signal.duration.connect(
                self.update_estimated_time_finish)
            self.thread_list.append(email_thread)
            self.threadpool.start(email_thread)

    def kill_thread(self):
        for thread in self.thread_list:
            thread.kill()

    def update_estimated_time_finish(self, duration):
        time_remaining = duration * (self.total_email - len(
            self.unsuccessful_list) - self.successful_email_counter)
        self.estimate_time_finish.emit(int(time_remaining))

    def is_sending_done(self):
        num_email_processed = len(
            self.unsuccessful_list) + self.successful_email_counter
        self.send_progress.emit(num_email_processed / self.total_email * 100)
        if num_email_processed == self.total_email:
            self.sending_done.emit(self.successful_email_counter,
                                   len(self.unsuccessful_list))
            self.thread_list = []

            unsuccessful_df = pd.DataFrame(self.unsuccessful_list)
            # output excel file to desktop
            unsuccessful_df.to_excel('{}/unsuccessful_emails.xlsx'.format(
                os.path.join(os.environ["HOMEPATH"], "Desktop")),
                                     index=False)

            self.logger.info('Send finished')

    def show_invalid_email_error(self):
        self.is_sending_done()
        self.signal_0.emit()

    def update_fail_email(self, email, data_row):
        self.unsuccessful_list.append(data_row)
        self.fail_email.emit(email)
        self.is_sending_done()

    def update_successful_email(self, email):
        self.successful_email_counter += 1
        self.success_email.emit(email)
        self.is_sending_done()

    def check_valid_field(self, compulsory_text):
        is_valid_field = True
        idx = 0

        for email_input in self.is_valid_field_dict:
            if not compulsory_text[idx] or compulsory_text[
                    idx] == 'Select email column':
                self.is_valid_field_dict[email_input] = False
                getattr(self, 'signal_' + str(idx)).emit()
                is_valid_field = False
            else:
                self.is_valid_field_dict[email_input] = True
            idx += 1

        if AttachmentLabel.attachment_list_byte_size > Logic.attachment_byte_limit:
            self.attachment_limit_reached.emit()
            is_valid_field = False

        return is_valid_field

    def show_preview(self, compulsory_text, optional_text):
        is_valid_field = self.check_valid_field(compulsory_text)

        if not is_valid_field:
            return [
                '',
                'Unable to preview message. Please fill in all compulsory fields'
            ]

        try:
            receiver_email_file = compulsory_text[1]
            receiver_df = pd.read_excel(receiver_email_file)
            first_row_data = receiver_df.iloc[0]

            subject_preview = self.process_placeholder(first_row_data,
                                                       optional_text[0])
            message_preview = self.process_placeholder(first_row_data,
                                                       optional_text[1])

            return [subject_preview, message_preview]
        except:
            return [
                '', 'Excel file is not in correct format. Please amend it.'
            ]

    def process_placeholder(self, row, msg):
        for placeholder, key in re.findall("({{(.+?)}})", msg):
            msg = msg.replace(placeholder, row.get(key, placeholder))
        return msg
Ejemplo n.º 16
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.threadpool = QThreadPool()

        self.x = {}  # Keep timepoints.
        self.y = {}  # Keep data.
        self.lines = {}  # Keep references to plotted lines, to update.

        layout = QVBoxLayout()
        self.graphWidget = pg.PlotWidget()
        self.graphWidget.setBackground("w")
        layout.addWidget(self.graphWidget)

        button = QPushButton("Create New Worker")
        button.pressed.connect(self.execute)

        # layout.addWidget(self.progress)
        layout.addWidget(button)

        w = QWidget()
        w.setLayout(layout)

        self.setCentralWidget(w)

        self.show()

    def execute(self):
        worker = Worker()
        worker.signals.data.connect(self.receive_data)

        # Execute
        self.threadpool.start(worker)

    def receive_data(self, data):
        worker_id, x, y = data  # <3>

        if worker_id not in self.lines:
            self.x[worker_id] = [x]
            self.y[worker_id] = [y]

            # Generate a random color.
            pen = pg.mkPen(
                width=2,
                color=(
                    random.randint(100, 255),
                    random.randint(100, 255),
                    random.randint(100, 255),
                ),
            )
            self.lines[worker_id] = self.graphWidget.plot(self.x[worker_id],
                                                          self.y[worker_id],
                                                          pen=pen)
            return

        # Update existing plot/data
        self.x[worker_id].append(x)
        self.y[worker_id].append(y)

        self.lines[worker_id].setData(self.x[worker_id], self.y[worker_id])
Ejemplo n.º 17
0
class ResultWidget(QWidget):
    def __init__(self, route=None):
        super().__init__()

        layout = QVBoxLayout()
        self.route = route

        self.thread_pool = QThreadPool()

        layout_send = QHBoxLayout()
        self.send_button = QPushButton('Send')
        self.send_button.clicked.connect(self.make_request)

        self.search_line = QLineEdit()
        self.search_line.setPlaceholderText('Search')
        self.search_line.textChanged.connect(self.search_result_reset)
        self.search_line.returnPressed.connect(self.search_result)

        self.response_status_label = QLabel()
        self.response_status_label.setFont(FONT_ROUTE)
        self.response_status_label.hide()

        self.elapsed_time_label = QLabel()
        self.elapsed_time_label.setFont(FONT_ROUTE)
        self.elapsed_time_label.hide()

        self.search_summary_label = QLabel()
        self.search_summary_label.setFont(FONT_ROUTE)
        self.search_summary_label.hide()

        if route is not None:
            layout_send.addWidget(self.send_button)

        layout_send.addWidget(self.response_status_label)
        layout_send.addWidget(self.elapsed_time_label)
        layout_send.addStretch(1)

        layout_send.addWidget(self.search_summary_label)
        layout_send.addWidget(self.search_line)

        layout.addLayout(layout_send)

        self.result_text_edit = ResultTextEdit()
        self.result_text_edit.setReadOnly(True)
        self.result_text_edit.setFont(TEXT_FONT)
        self.result_text_edit.setContextMenuPolicy(Qt.NoContextMenu)

        self.result_text_edit.search.connect(self.focus)

        self.shortcut = QShortcut(QKeySequence("Ctrl+Return"), self,
                                  self.make_request)

        self.result_text_edit.setUndoRedoEnabled(False)

        self.highlighter = TextHighlighter(self.result_text_edit.document())

        layout.addWidget(self.result_text_edit)

        if route is not None:
            saved_result = load_request_result(route)
            self.result_text_edit.setPlainText(saved_result)

        self.setLayout(layout)

    def goto(self, to):
        c = self.result_text_edit.textCursor()
        c.movePosition(to, QTextCursor.MoveAnchor, 1)
        self.result_text_edit.setTextCursor(c)

    def search_result_reset(self):
        self.goto(QTextCursor.Start)

        string_format = QTextCharFormat()
        string_format.setBackground(QColor('#668B8B'))

        extras = []
        self.search_positions = []
        while True:
            extra = QTextEdit.ExtraSelection()
            found = self.result_text_edit.find(self.search_line.text())

            if not found:
                break

            extra.cursor = self.result_text_edit.textCursor()
            extra.format = string_format

            self.search_positions.append(extra.cursor.position())
            extras.append(extra)

        self.result_text_edit.setExtraSelections(extras)
        self.goto(QTextCursor.Start)
        self.search_result()

    def search_result(self):
        p = self.result_text_edit.palette()
        p.setColor(QPalette.Highlight, QColor("#ee799f"))
        self.result_text_edit.setPalette(p)

        search_settings = QTextDocument.FindFlags()

        mod = QApplication.keyboardModifiers()
        if (mod & Qt.ShiftModifier) != 0:
            search_settings |= QTextDocument.FindBackward

        r = self.result_text_edit.find(self.search_line.text(),
                                       search_settings)
        if not r:
            if (mod & Qt.ShiftModifier) != 0:
                self.goto(QTextCursor.End)
            else:
                self.goto(QTextCursor.Start)
            self.result_text_edit.find(self.search_line.text(),
                                       search_settings)

        if self.search_line.text() == '':
            self.search_summary_label.hide()
            return

        current_position = self.result_text_edit.textCursor().position()
        try:
            current_index = self.search_positions.index(current_position)
        except ValueError:
            current_index = -1

        self.search_summary_label.show()
        self.search_summary_label.setText(
            '%s/%s' % (current_index + 1, len(self.search_positions)))

    def focus(self):
        self.search_line.setFocus()
        self.search_line.selectAll()

    def make_request(self):
        self.response_status_label.hide()
        self.elapsed_time_label.hide()
        self.result_text_edit.setPlainText('Loading..')
        try:
            group_values, route_values = get_parameter_values_for_route(
                self.route)
            request = self.route.get_request(group_values, route_values)
            worker = RequestWorker(
                request.method,
                request.url,
                params=request.params,
                headers=request.headers,
                json=request.json,
            )
            worker.signals.result.connect(self.set_result)
            self.thread_pool.start(worker)
        except CaribouException as e:
            self.result_text_edit.setPlainText(str(e))
        except Exception:
            self.result_text_edit.setPlainText(traceback.format_exc())

    def set_result(self, text, status_code, elapsed_time):
        if status_code == 0:
            self.response_status_label.hide()
            self.elapsed_time_label.hide()
        else:
            p = self.response_status_label.palette()
            if status_code == 200:
                self.response_status_label.setText(str(status_code) + ' OK')
                p.setColor(QPalette.WindowText, QColor('#1FDA9A'))
            else:
                self.response_status_label.setText(str(status_code) + ' ERROR')
                p.setColor(QPalette.WindowText, QColor('#DB3340'))

            self.response_status_label.setPalette(p)
            self.response_status_label.show()

            self.elapsed_time_label.setText('%s ms' % int(elapsed_time * 1000))
            self.elapsed_time_label.show()

        self.result_text_edit.setUpdatesEnabled(False)
        self.result_text_edit.setPlainText(text)
        self.result_text_edit.setUpdatesEnabled(True)

        save_request_result(self.route, text)
        persist_storage()
Ejemplo n.º 18
0
    controller = ControllerProc(child_pipe)

    browser = PickerBrowserView()

    browser.loadFinished.connect(controller.start)
    browser.show()

    def add_heroes(items):
        for item in items:
            browser.select_hero(item[0], not item[1])

    browser.reset_clicked.connect(
        lambda: mother_pipe.send(ControllerMessage("reset")))

    signals = ControllerSignals()

    signals.select_team.connect(browser.select_team)
    signals.add_heroes.connect(add_heroes)

    threadpool = QThreadPool()
    msgthread = MessageProcessorThread(mother_pipe, signals)
    threadpool.start(msgthread)

    def cleanup():
        mother_pipe.send(ControllerMessage("kill"))
        child_pipe.send(ControllerMessage("kill"))

    browser.closing.connect(cleanup)

    sys.exit(app.exec_())
Ejemplo n.º 19
0
class DBWidget(QDialog, Ui_DataBase):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.setStyleSheet(qss)
        self.config_widget = parent
        self.check_res = None
        self.btn_group = QButtonGroup(self)
        #
        self.res.setWordWrap(True)
        self.user.setFocus()
        self.host.textChanged.connect(self.host_slot)
        self.port.textChanged.connect(self.textChanged_slot)
        self.user.textChanged.connect(self.textChanged_slot)
        self.password.textChanged.connect(self.textChanged_slot)
        self.database.textChanged.connect(self.textChanged_slot)
        # 右键菜单
        self.ava_table.horizontalHeader().setVisible(False)
        self.ava_table.setContextMenuPolicy(
            Qt.CustomContextMenu)  ######允许右键产生子菜单
        self.ava_table.customContextMenuRequested.connect(
            self.generate_menu)  ####右键菜单
        # btn
        self.test_btn.clicked.connect(self.test_btn_slot)
        self.ok_btn.clicked.connect(self.ok_btn_slot)
        self.host.setText('localhost')
        self.port.setText('27017')
        self.url.setPlaceholderText(
            "mongodb://[user:password@]host:port/database")
        #
        self.thread_pool = QThreadPool()
        self.sig = DBObject()
        self.sig.dbsig.connect(self.db_connect_result)

    def load_available(self):
        self.ava_table.setRowCount(0)
        self.ava_table.clearContents()
        for name, info in G.config.DB_INFO.items():
            self.ava_table.insertRow(0)
            self.ava_table.setItem(0, 0, QTableWidgetItem(name))
            radio = QRadioButton(self)
            self.btn_group.addButton(radio)
            radio.clicked.connect(self.radio_slot)
            if name == G.config.WHICH_DB:
                radio.setChecked(True)
            self.ava_table.setCellWidget(0, 1, radio)

    def radio_slot(self):
        row = self.ava_table.currentRow()
        name = self.ava_table.item(row, 0).text()
        info = G.config.DB_INFO[name]
        self.thread_pool.start(
            Worker(db_connect, **info, callback=self.radio_slot_callback))
        G.temp_var = dict(WHICH_DB=name, info=info)

    def radio_slot_callback(self, res):
        if not res:
            G.config.update(G.temp_var)
            create_db_conn(**G.temp_var['info'])  # 创建数据库连接
            G.config.to_file()
            TipDialog("修改成功")
        else:
            TipDialog(f"修改失败{res}")

    def generate_menu(self, pos):
        row_num = -1
        for i in self.ava_table.selectionModel().selection().indexes():
            row_num = i.row()
        if row_num < 0:
            return
        menu = QMenu()
        modify_item = menu.addAction('修改')
        del_item = menu.addAction('删除')
        action = menu.exec_(self.ava_table.mapToGlobal(pos))
        name = self.ava_table.item(row_num, 0).text()
        if action == del_item:
            if G.config.WHICH_DB == name:
                G.config.WHICH_DB = ""
            G.config.DB_INFO.pop(name, None)
            self.ava_table.removeRow(row_num)
        elif action == modify_item:
            self.load_info(G.config.DB_INFO.get(name, {}))
            self.tabWidget.setCurrentIndex(1)

    def load_info(self, info: dict):
        for k, v in info.items():
            w = getattr(self, k)
            if k == 'password':
                w.setText(len(v) * "*")
            else:
                w.setText(v)

    def host_slot(self):
        self.name.setText(self.host.text())
        self.textChanged_slot()

    def textChanged_slot(self):
        host = self.host.text()
        port = self.port.text()
        user = self.user.text()
        password = self.password.text()
        database = self.database.text()
        userpwd = ''
        if user and password:
            userpwd = f"{user}:{'*' * len(password)}@"
        self.url.setText(f"mongodb://{userpwd}{host}:{port}/{database}")

    def test_btn_slot(self):
        self.test_btn.setText('connecting...')
        QApplication.processEvents()
        password = self.password.text()
        url = self.url.text().replace("*" * len(password), password)
        database = self.database.text()
        self.thread_pool.start(
            Worker(db_connect,
                   url=url,
                   database=database,
                   callback=self.db_connect_callback))

    def db_connect_callback(self, res):
        res = res if res else ""
        self.sig.dbsig.emit(res)

    @Slot(str)
    def db_connect_result(self, res):
        if res:
            self.check_res = False
            self.res.setText(res)
            self.res.setStyleSheet("""color:yellow""")
        else:
            self.check_res = True
            self.res.setText('connect success!')
            self.res.setStyleSheet("""color:green""")
        self.test_btn.setText('Test Connect')

    def ok_btn_slot(self):
        if self.check_res is None:
            self.test_btn_slot()
        if self.check_res is True:
            G.config.DB_INFO[self.name.text()] = dict(
                host=self.host.text(),
                user=self.user.text(),
                password=self.password.text()
                if self.savebox.currentText() == '总是' else "",
                database=self.database.text(),
                port=self.port.text(),
                url=self.url.text())
            G.config.to_file()
            TipDialog("成功")
            self.tabWidget.setCurrentIndex(0)

    def closeEvent(self, arg__1):
        self.config_widget.init_data_source()
        arg__1.accept()
Ejemplo n.º 20
0
class ImageCanvas(FigureCanvas):
    def __init__(self, parent=None, image_names=None):
        self.figure = Figure()
        super(ImageCanvas, self).__init__(self.figure)

        self.raw_axes = []  # only used for raw currently
        self.axes_images = []
        self.overlay_artists = {}
        self.cached_detector_borders = []
        self.saturation_texts = []
        self.cmap = hexrd.ui.constants.DEFAULT_CMAP
        self.norm = None
        self.iviewer = None
        self.azimuthal_integral_axis = None
        self.azimuthal_line_artist = None
        self.wppf_plot = None

        # Track the current mode so that we can more lazily clear on change.
        self.mode = None

        # Track the pixel size
        self.cartesian_res_config = (
            HexrdConfig().config['image']['cartesian'].copy())
        self.polar_res_config = HexrdConfig().config['image']['polar'].copy()

        # Set up our async stuff
        self.thread_pool = QThreadPool(parent)

        if image_names is not None:
            self.load_images(image_names)

        self.setup_connections()

    def setup_connections(self):
        HexrdConfig().overlay_config_changed.connect(self.update_overlays)
        HexrdConfig().show_saturation_level_changed.connect(
            self.show_saturation)
        HexrdConfig().detector_transform_modified.connect(
            self.on_detector_transform_modified)
        HexrdConfig().rerender_detector_borders.connect(
            self.draw_detector_borders)
        HexrdConfig().rerender_wppf.connect(self.draw_wppf)
        HexrdConfig().beam_vector_changed.connect(self.beam_vector_changed)
        HexrdConfig().polar_masks_changed.connect(self.update_polar)

    def __del__(self):
        # This is so that the figure can be cleaned up
        plt.close(self.figure)

    def clear(self):
        self.iviewer = None
        self.mode = None
        self.clear_figure()

    def clear_figure(self):
        self.figure.clear()
        self.raw_axes.clear()
        self.axes_images.clear()
        self.remove_all_overlay_artists()
        self.clear_azimuthal_integral_axis()
        self.mode = None

    def clear_azimuthal_integral_axis(self):
        self.clear_wppf_plot()
        self.azimuthal_integral_axis = None
        self.azimuthal_line_artist = None
        HexrdConfig().last_azimuthal_integral_data = None

    def clear_wppf_plot(self):
        if self.wppf_plot:
            self.wppf_plot.remove()
            self.wppf_plot = None

    def load_images(self, image_names):
        HexrdConfig().emit_update_status_bar('Loading image view...')
        if (self.mode != ViewType.raw
                or len(image_names) != len(self.axes_images)):
            # Either we weren't in image mode before, we have a different
            # number of images, or there are masks to apply. Clear and re-draw.
            self.clear()
            self.mode = ViewType.raw

            cols = 1
            if len(image_names) > 1:
                cols = 2

            rows = math.ceil(len(image_names) / cols)

            idx = HexrdConfig().current_imageseries_idx
            for i, name in enumerate(image_names):
                img = HexrdConfig().image(name, idx)

                # Apply any masks
                for mask_name, (det, mask) in HexrdConfig().raw_masks.items():
                    if (mask_name in HexrdConfig().visible_masks
                            and det == name):
                        img[~mask] = 0

                axis = self.figure.add_subplot(rows, cols, i + 1)
                axis.set_title(name)
                self.axes_images.append(
                    axis.imshow(img, cmap=self.cmap, norm=self.norm))
                axis.autoscale(False)
                self.raw_axes.append(axis)

            self.figure.tight_layout()
        else:
            idx = HexrdConfig().current_imageseries_idx
            for i, name in enumerate(image_names):
                img = HexrdConfig().image(name, idx)
                # Apply any masks
                for mask_name, (det, mask) in HexrdConfig().raw_masks.items():
                    if (mask_name in HexrdConfig().visible_masks
                            and det == name):
                        img[~mask] = 0
                self.axes_images[i].set_data(img)

        # This will call self.draw()
        self.show_saturation()

        # This will be used for drawing the rings
        self.iviewer = raw_iviewer()
        # Set the detectors to draw
        self.iviewer.detectors = [x.get_title() for x in self.raw_axes]
        self.update_overlays()

        msg = 'Image view loaded!'
        HexrdConfig().emit_update_status_bar(msg)

    def remove_all_overlay_artists(self):
        while self.overlay_artists:
            key = next(iter(self.overlay_artists))
            self.remove_overlay_artists(key)

    def remove_overlay_artists(self, key):
        artists = self.overlay_artists[key]
        while artists:
            artists.pop(0).remove()
        del self.overlay_artists[key]

    def overlay_axes_data(self, overlay):
        # Return the axes and data for drawing the overlay
        if not overlay['data']:
            return []

        if self.mode in [ViewType.cartesian, ViewType.polar]:
            # If it's cartesian or polar, there is only one axis
            # Use the same axis for all of the data
            return [(self.axis, x) for x in overlay['data'].values()]

        # If it's raw, there is data for each axis.
        # The title of each axis should match the data key.
        return [(x, overlay['data'][x.get_title()]) for x in self.raw_axes]

    def overlay_draw_func(self, type):
        overlay_funcs = {
            OverlayType.powder:
            self.draw_powder_overlay,
            OverlayType.laue:
            self.draw_laue_overlay,
            OverlayType.mono_rotation_series:
            (self.draw_mono_rotation_series_overlay)
        }

        if type not in overlay_funcs:
            raise Exception(f'Unknown overlay type: {type}')

        return overlay_funcs[type]

    def draw_overlay(self, overlay):
        if not overlay['visible']:
            return

        type = overlay['type']
        style = overlay['style']
        for axis, data in self.overlay_axes_data(overlay):
            if id(data) in self.overlay_artists:
                # It's already present. Skip it.
                continue

            self.overlay_draw_func(type)(axis, data, style)

    def draw_powder_overlay(self, axis, data, style):
        rings = data['rings']
        rbnds = data['rbnds']
        rbnd_indices = data['rbnd_indices']

        data_style = style['data']
        ranges_style = style['ranges']

        artists = []
        self.overlay_artists[id(data)] = artists
        for pr in rings:
            x, y = self.extract_ring_coords(pr)
            artist, = axis.plot(x, y, **data_style)
            artists.append(artist)

        # Add the rbnds too
        for ind, pr in zip(rbnd_indices, rbnds):
            x, y = self.extract_ring_coords(pr)
            current_style = copy.deepcopy(ranges_style)
            if len(ind) > 1:
                # If ranges are combined, override the color to red
                current_style['c'] = 'r'
            artist, = axis.plot(x, y, **current_style)
            artists.append(artist)

        if self.azimuthal_integral_axis is not None:
            az_axis = self.azimuthal_integral_axis
            for pr in rings:
                x, _ = self.extract_ring_coords(pr)
                # Average the points together for the vertical line
                x = np.nanmean(x)
                artist = az_axis.axvline(x, **data_style)
                artists.append(artist)

            # Add the rbnds too
            for ind, pr in zip(rbnd_indices, rbnds):
                x, _ = self.extract_ring_coords(pr)
                # Average the points together for the vertical line
                x = np.nanmean(x)

                current_style = copy.deepcopy(ranges_style)
                if len(ind) > 1:
                    # If rbnds are combined, override the color to red
                    current_style['c'] = 'r'

                artist = az_axis.axvline(x, **current_style)
                artists.append(artist)

    def draw_laue_overlay(self, axis, data, style):
        spots = data['spots']
        ranges = data['ranges']

        data_style = style['data']
        ranges_style = style['ranges']

        artists = []
        self.overlay_artists[id(data)] = artists
        for x, y in spots:
            artist = axis.scatter(x, y, **data_style)
            artists.append(artist)

        for range in ranges:
            x, y = zip(*range)
            artist, = axis.plot(x, y, **ranges_style)
            artists.append(artist)

    def draw_mono_rotation_series_overlay(self, id, axis, data, style):
        pass

    def update_overlays(self):
        # iviewer is required for drawing rings
        if not self.iviewer:
            return

        if not HexrdConfig().show_overlays:
            self.remove_all_overlay_artists()
            self.draw()
            return

        def overlay_with_data_id(data_id):
            for overlay in HexrdConfig().overlays:
                if any([data_id == id(x) for x in overlay['data'].values()]):
                    return overlay

            return None

        # Remove any artists that:
        # 1. Are no longer in the list of overlays
        # 2. Are not visible
        # 3. Need updating
        for key in list(self.overlay_artists.keys()):
            overlay = overlay_with_data_id(key)
            if overlay is None:
                # This artist is no longer a part of the overlays
                self.remove_overlay_artists(key)
                continue

            if overlay.get('update_needed', True) or not overlay['visible']:
                self.remove_overlay_artists(key)
                continue

        self.iviewer.update_overlay_data()

        for overlay in HexrdConfig().overlays:
            self.draw_overlay(overlay)

        self.draw()

    def clear_detector_borders(self):
        while self.cached_detector_borders:
            self.cached_detector_borders.pop(0).remove()

    def draw_detector_borders(self):
        self.clear_detector_borders()

        # If there is no iviewer, we are not currently viewing a
        # calibration. Just return.
        if not self.iviewer:
            self.draw()
            return

        # Make sure this is allowed by the configuration
        if not HexrdConfig().show_detector_borders:
            self.draw()
            return

        borders = self.iviewer.all_detector_borders
        for border in borders.values():
            # Draw each line in the border
            for line in border:
                plot, = self.axis.plot(*line, color='y', lw=2)
                self.cached_detector_borders.append(plot)

        self.draw()

    def draw_wppf(self):
        self.update_wppf_plot()
        self.draw()

    def extract_ring_coords(self, data):
        if self.mode == ViewType.cartesian:
            # These are in x, y coordinates. Do not swap them.
            return data[:, 0], data[:, 1]

        return data[:, 1], data[:, 0]

    def clear_saturation(self):
        for t in self.saturation_texts:
            t.remove()
        self.saturation_texts.clear()
        self.draw()

    def show_saturation(self):
        self.clear_saturation()

        # Do not proceed without config approval
        if not HexrdConfig().show_saturation_level:
            return

        if not self.axes_images:
            return

        # Do not show the saturation in calibration mode
        if self.mode != ViewType.raw:
            return

        for img in self.axes_images:
            # The titles of the images are currently the detector names
            # If we change this in the future, we will need to change
            # our method for getting the saturation level as well.
            ax = img.axes
            detector_name = ax.get_title()
            detector = HexrdConfig().detector(detector_name)
            saturation_level = detector['saturation_level']['value']

            array = img.get_array()

            num_sat = (array >= saturation_level).sum()
            percent = num_sat / array.size * 100.0
            str_sat = 'Saturation: ' + str(num_sat)
            str_sat += '\n%5.3f %%' % percent

            t = ax.text(0.05,
                        0.05,
                        str_sat,
                        fontdict={'color': 'w'},
                        transform=ax.transAxes)
            self.saturation_texts.append(t)

        self.draw()

    def beam_vector_changed(self):
        if not self.iviewer or not hasattr(self.iviewer, 'instr'):
            return

        # Re-draw all overlays from scratch
        HexrdConfig().clear_overlay_data()

        bvec = HexrdConfig().instrument_config['beam']['vector']
        self.iviewer.instr.beam_vector = (bvec['azimuth'], bvec['polar_angle'])
        self.update_overlays()

    def show_cartesian(self):
        HexrdConfig().emit_update_status_bar('Loading Cartesian view...')
        if self.mode != ViewType.cartesian:
            self.clear()
            self.mode = ViewType.cartesian

        # Force a redraw when the pixel size changes.
        if (self.cartesian_res_config !=
                HexrdConfig().config['image']['cartesian']):
            self.cartesian_res_config = (
                HexrdConfig().config['image']['cartesian'].copy())
            self.figure.clear()
            self.axes_images.clear()

        # Run the calibration in a background thread
        worker = AsyncWorker(cartesian_viewer)
        self.thread_pool.start(worker)

        # Get the results and close the progress dialog when finished
        worker.signals.result.connect(self.finish_show_cartesian)
        worker.signals.error.connect(self.async_worker_error)

    def finish_show_cartesian(self, iviewer):
        self.iviewer = iviewer
        img = self.iviewer.img

        # It is important to persist the plot so that we don't reset the scale.
        rescale_image = True
        if len(self.axes_images) == 0:
            self.axis = self.figure.add_subplot(111)
            self.axes_images.append(
                self.axis.imshow(img,
                                 cmap=self.cmap,
                                 norm=self.norm,
                                 vmin=None,
                                 vmax=None,
                                 interpolation="none"))
            self.axis.set_xlabel(r'x (mm)')
            self.axis.set_ylabel(r'y (mm)')
        else:
            rescale_image = False
            self.axes_images[0].set_data(img)

        # We must adjust the extent of the image
        if rescale_image:
            self.axes_images[0].set_extent(iviewer.extent)
            self.axis.relim()
            self.axis.autoscale_view()
            self.axis.autoscale(False)
            self.figure.tight_layout()

        self.update_overlays()
        self.draw_detector_borders()

        msg = 'Cartesian view loaded!'
        HexrdConfig().emit_update_status_bar(msg)

    def show_polar(self):
        HexrdConfig().emit_update_status_bar('Loading polar view...')
        if self.mode != ViewType.polar:
            self.clear()
            self.mode = ViewType.polar

        polar_res_config = HexrdConfig().config['image']['polar']
        if self._polar_reset_needed(polar_res_config):
            # Reset the whole image when certain config items change
            self.clear()
            self.mode = ViewType.polar

        self.polar_res_config = polar_res_config.copy()

        # Run the calibration in a background thread
        worker = AsyncWorker(polar_viewer)
        self.thread_pool.start(worker)

        # Get the results and close the progress dialog when finished
        worker.signals.result.connect(self.finish_show_polar)
        worker.signals.error.connect(self.async_worker_error)

    def finish_show_polar(self, iviewer):
        self.iviewer = iviewer
        img = self.iviewer.img
        extent = self.iviewer._extent

        rescale_image = True
        # TODO: maybe make this an option in the UI? Perhaps a checkbox
        # in the "View" menu?
        # if HexrdConfig().polar_show_azimuthal_integral
        if True:
            # The top image will have 2x the height of the bottom image
            grid = plt.GridSpec(3, 1)

            # It is important to persist the plot so that we don't reset the
            # scale.
            if len(self.axes_images) == 0:
                self.axis = self.figure.add_subplot(grid[:2, 0])
                self.axes_images.append(
                    self.axis.imshow(img,
                                     extent=extent,
                                     cmap=self.cmap,
                                     norm=self.norm,
                                     picker=True,
                                     interpolation='none'))
                self.axis.axis('auto')
                # Do not allow the axis to autoscale, which could happen if
                # overlays are drawn out-of-bounds
                self.axis.autoscale(False)
                self.axis.set_ylabel(r'$\eta$ (deg)')
                self.axis.label_outer()
            else:
                rescale_image = False
                self.axes_images[0].set_data(img)

            # Get the "tth" vector
            angular_grid = self.iviewer.angular_grid
            tth = np.degrees(angular_grid[1][0])

            if self.azimuthal_integral_axis is None:
                axis = self.figure.add_subplot(grid[2, 0], sharex=self.axis)
                data = (tth, np.sum(img, axis=0))
                self.azimuthal_line_artist, = axis.plot(*data)
                HexrdConfig().last_azimuthal_integral_data = data

                self.azimuthal_integral_axis = axis
                axis.set_xlabel(r'2$\theta$ (deg)')
                axis.set_ylabel(r'Azimuthal Integration')
                self.update_wppf_plot()
            else:
                self.update_azimuthal_integral_plot()
                axis = self.azimuthal_integral_axis

        else:
            if len(self.axes_images) == 0:
                self.axis = self.figure.add_subplot(111)
                self.axes_images.append(
                    self.axis.imshow(img,
                                     cmap=self.cmap,
                                     norm=self.norm,
                                     picker=True,
                                     interpolation='none'))
                self.axis.set_xlabel(r'2$\theta$ (deg)')
                self.axis.set_ylabel(r'$\eta$ (deg)')
            else:
                rescale_image = False
                self.axes_images[0].set_data(img)

        if rescale_image:
            self.axis.relim()
            self.axis.autoscale_view()
            self.figure.tight_layout()

        self.update_overlays()
        self.draw_detector_borders()

        msg = 'Polar view loaded!'
        HexrdConfig().emit_update_status_bar(msg)

    def update_polar(self):
        if not self.iviewer:
            return

        self.iviewer.update_image()
        self.axes_images[0].set_data(self.iviewer.img)
        self.update_azimuthal_integral_plot()
        self.update_overlays()

    def async_worker_error(self, error):
        QMessageBox.critical(self, 'HEXRD', str(error[1]))
        msg = f'{str(self.mode)} view error!'
        HexrdConfig().emit_update_status_bar(msg)

        self.clear_figure()
        self.draw()

    def set_cmap(self, cmap):
        self.cmap = cmap
        for axes_image in self.axes_images:
            axes_image.set_cmap(cmap)
        self.draw()

    def set_norm(self, norm):
        self.norm = norm
        for axes_image in self.axes_images:
            axes_image.set_norm(norm)
        self.draw()

    def update_azimuthal_integral_plot(self):
        if self.mode != ViewType.polar:
            # Nothing to do. Just return.
            return

        axis = self.azimuthal_integral_axis
        line = self.azimuthal_line_artist
        if any([x is None for x in [axis, line]]):
            # Nothing to do. Just return.
            return

        # Get the "tth" vector
        tth = np.degrees(self.iviewer.angular_grid[1][0])

        # Set the new data
        data = (tth, np.sum(self.iviewer.img, axis=0))
        line.set_data(*data)

        HexrdConfig().last_azimuthal_integral_data = data

        # Update the wppf data if applicable
        self.update_wppf_plot()

        # Rescale the axes for the new data
        axis.relim()
        axis.autoscale_view(scalex=False)

    def update_wppf_plot(self):
        self.clear_wppf_plot()

        wppf_data = HexrdConfig().wppf_data
        axis = self.azimuthal_integral_axis
        line = self.azimuthal_line_artist
        if any(x is None for x in (wppf_data, axis, line)):
            return

        style = {'s': 30, 'facecolors': 'none', 'edgecolors': 'r'}
        self.wppf_plot = axis.scatter(*wppf_data, **style)

    def on_detector_transform_modified(self, det):
        if self.mode not in [ViewType.cartesian, ViewType.polar]:
            return

        self.iviewer.update_detector(det)
        self.axes_images[0].set_data(self.iviewer.img)

        # This will only run if we are in polar mode
        self.update_azimuthal_integral_plot()

        # Update the detector borders if we are showing them
        # This will call self.draw()
        self.draw_detector_borders()

    def export_polar_plot(self, filename):
        if self.mode != ViewType.polar:
            raise Exception('Not in polar mode. Cannot export polar plot')

        if not self.iviewer:
            raise Exception('No iviewer. Cannot export polar plot')

        self.iviewer.write_image(filename)

    def _polar_reset_needed(self, new_polar_config):
        # If any of the entries on this list were changed, a reset is needed
        reset_needed_list = [
            'pixel_size_tth', 'pixel_size_eta', 'tth_min', 'tth_max',
            'eta_min', 'eta_max'
        ]

        for key in reset_needed_list:
            if self.polar_res_config[key] != new_polar_config[key]:
                return True

        return False

    def polar_show_snip1d(self):
        if self.mode != ViewType.polar:
            print('snip1d may only be shown in polar mode!')
            return

        if self.iviewer is None:
            print('No instrument viewer! Cannot generate snip1d!')
            return

        if self.iviewer.img is None:
            print('No image! Cannot generate snip1d!')

        extent = self.iviewer._extent

        if not hasattr(self, '_snip1d_figure_cache'):
            # Create the figure and axes to use
            fig, ax = plt.subplots()
            ax.set_xlabel(r'2$\theta$ (deg)')
            ax.set_ylabel(r'$\eta$ (deg)')
            fig.canvas.set_window_title('HEXRD')
            self._snip1d_figure_cache = (fig, ax)
        else:
            fig, ax = self._snip1d_figure_cache

        algorithm = HexrdConfig().polar_snip1d_algorithm
        titles = ['Fast SNIP 1D', 'SNIP 1D', 'SNIP 2D']
        if algorithm < len(titles):
            title = titles[algorithm]
        else:
            title = f'Algorithm {algorithm}'
        ax.set_title(title)

        if self.iviewer.snip1d_background is not None:
            background = self.iviewer.snip1d_background
        else:
            # We have to run it ourselves...
            # It should not have already been applied to the image
            background = utils.run_snip1d(self.iviewer.img)

        im = ax.imshow(background)

        im.set_extent(extent)
        ax.relim()
        ax.autoscale_view()
        ax.axis('auto')
        fig.tight_layout()

        fig.canvas.draw()
        fig.show()
Ejemplo n.º 21
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        Ui_MainWindow.__init__(self)
        self.setupUi(self)
        self.start_button.clicked.connect(self.start)
        self.stop_button.clicked.connect(self.stop)
        self.main_controller = None
        validator = QIntValidator(1, 200, self) # Validator for the operation time input. Only allow values 1 to 100 seconds
        self.operation_time_entry.setValidator(validator)
        self.current_elapsed_time = 0
        self.timer = QTimer(self)
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.update_elapsed_time)
        self.max_deburr_time = 0
        self.thread_pool = QThreadPool()

    def update_elapsed_time(self):
        time_left = self.main_controller.update_elapsed()
        self.elapsed_time_label.setText(f'{time_left}')

        if (self.main_controller.is_done()):
            self.timer.stop()
            self.start_button.setDisabled(False)
            parts_deburred = self.main_controller.stop()
            self.lcdNumber.display(parts_deburred)                    # Update displays of deburred pieces
              

    def reset_elapsed_time(self):
        self.current_elapsed_time = self.max_deburr_time
        

    def start(self):
        if self.operation_time_entry.text() == "":
            error = "Please Enter an Operation Time"
        else: error = None

       # if error is None:
        max_deburr_time = int(self.operation_time_entry.text()) # Set total operation time
        worker = Worker(self.main_controller.start, max_deburr_time)
        self.elapsed_time_label.setText(f'{max_deburr_time}') # Reset time label at start of operation
        self.thread_pool.start(worker)#self.deburr_controller.start_deburr(self.operation_time_entry.text())
        self.timer.start()                                           # Start timer
        #t.join()
        
        if error is not None:
            self.display_error(error)
        else:
            self.start_button.setDisabled(True)

    def display_error(self, msg):
        msg_box = QMessageBox()
        msg_box.setText(msg)
        msg_box.exec_()

    def stop(self):
        if self.main_controller.is_in_progress():
            self.timer.stop()
            time_left = self.main_controller.e_stop() # Tiny G is reset here
            # User must wait for Tiny G to reset.
            # We need a way to know when the system is ready to go
            # Start button should be disabled until the system is ready
            self.start_button.setDisabled(False) # for now we will enable start button right away
            self.elapsed_time_label.setText(f'{time_left}')
        else:
            self.display_error('System is not running')


    def initialize(self):
        """Initializes a Deburr Controller instance which establishes a
        connection with the motor controller"""
        self.main_controller = MainControl()
        error = self.main_controller.startup()
        if error is not None:
            msg_box = QMessageBox()
            msg_box.setText(error.__str__())
            msg_box.exec_()
Ejemplo n.º 22
0
class IndexingRunner(QObject):
    progress_text = Signal(str)

    def __init__(self, parent=None):
        super(IndexingRunner, self).__init__(parent)

        self.parent = parent
        self.ome_maps_select_dialog = None
        self.ome_maps_viewer_dialog = None
        self.fit_grains_dialog = None
        self.fit_grains_results = None
        self.thread_pool = QThreadPool(self.parent)
        self.progress_dialog = ProgressDialog(self.parent)

        self.ome_maps = None
        self.progress_text.connect(self.progress_dialog.setLabelText)

    def clear(self):
        self.ome_maps_select_dialog = None
        self.ome_maps_viewer_dialog = None
        self.fit_grains_dialog = None

        self.ome_maps = None

    def run(self):
        # We will go through these steps:
        # 1. Have the user select/generate eta omega maps
        # 2. Have the user view and threshold the eta omega maps
        # 3. Run the indexing
        self.select_ome_maps()

    def select_ome_maps(self):
        dialog = OmeMapsSelectDialog(self.parent)
        dialog.accepted.connect(self.ome_maps_selected)
        dialog.rejected.connect(self.clear)
        dialog.show()
        self.ome_maps_select_dialog = dialog

    def ome_maps_selected(self):
        dialog = self.ome_maps_select_dialog
        if dialog is None:
            return

        if dialog.method_name == 'load':
            self.ome_maps = EtaOmeMaps(dialog.file_name)
            self.ome_maps_select_dialog = None
            self.view_ome_maps()
        else:
            # Create a full indexing config
            config = create_indexing_config()

            # Setup to generate maps in background
            self.progress_dialog.setWindowTitle('Generating Eta Omega Maps')
            self.progress_dialog.setRange(0, 0)  # no numerical updates

            worker = AsyncWorker(self.run_eta_ome_maps, config)
            self.thread_pool.start(worker)

            worker.signals.result.connect(self.view_ome_maps)
            worker.signals.finished.connect(self.progress_dialog.accept)
            self.progress_dialog.exec_()

    def run_eta_ome_maps(self, config):
        self.ome_maps = generate_eta_ome_maps(config, save=False)

    def view_ome_maps(self):
        # Now, show the Ome Map viewer

        dialog = OmeMapsViewerDialog(self.ome_maps, self.parent)
        dialog.accepted.connect(self.ome_maps_viewed)
        dialog.rejected.connect(self.clear)
        dialog.show()

        self.ome_maps_viewer_dialog = dialog

    def ome_maps_viewed(self):
        # The dialog should have automatically updated our internal config
        # Let's go ahead and run the indexing!

        # For now, always use all hkls from eta omega maps
        hkls = list(range(len(self.ome_maps.iHKLList)))
        indexing_config = HexrdConfig().indexing_config
        indexing_config['find_orientations']['seed_search']['hkl_seeds'] = hkls

        # Create a full indexing config
        config = create_indexing_config()

        # Setup to run indexing in background
        self.progress_dialog.setWindowTitle('Find Orientations')
        self.progress_dialog.setRange(0, 0)  # no numerical updates

        worker = AsyncWorker(self.run_indexer, config)
        self.thread_pool.start(worker)

        worker.signals.result.connect(self.view_fit_grains_options)
        worker.signals.finished.connect(self.progress_dialog.accept)
        self.progress_dialog.exec_()

    def run_indexer(self, config):
        # Generate the orientation fibers
        self.update_progress_text('Generating orientation fibers')
        self.qfib = generate_orientation_fibers(config, self.ome_maps)

        # Find orientations
        self.update_progress_text('Running indexer (paintGrid)')
        ncpus = config.multiprocessing
        self.completeness = indexer.paintGrid(
            self.qfib,
            self.ome_maps,
            etaRange=np.radians(config.find_orientations.eta.range),
            omeTol=np.radians(config.find_orientations.omega.tolerance),
            etaTol=np.radians(config.find_orientations.eta.tolerance),
            omePeriod=np.radians(config.find_orientations.omega.period),
            threshold=config.find_orientations.threshold,
            doMultiProc=ncpus > 1,
            nCPUs=ncpus)
        print('Indexing complete')

    def view_fit_grains_options(self):
        # Run dialog for user options
        dialog = FitGrainsOptionsDialog(self.parent)
        dialog.accepted.connect(self.fit_grains_options_accepted)
        dialog.rejected.connect(self.clear)
        self.fit_grains_options_dialog = dialog
        dialog.show()

    def fit_grains_options_accepted(self):
        # Create a full indexing config
        config = create_indexing_config()

        # Setup to run in background
        self.progress_dialog.setWindowTitle('Fit Grains')
        self.progress_dialog.setRange(0, 0)  # no numerical updates

        worker = AsyncWorker(self.run_fit_grains, config)
        self.thread_pool.start(worker)

        worker.signals.result.connect(self.view_fit_grains_results)
        worker.signals.error.connect(self.on_async_error)
        worker.signals.finished.connect(self.progress_dialog.accept)
        self.progress_dialog.exec_()

    def run_fit_grains(self, config):
        self.fit_grains_results = None
        min_samples, mean_rpg = create_clustering_parameters(
            config, self.ome_maps)

        # Add fit_grains config
        dialog_config = HexrdConfig().indexing_config['fit_grains']
        config.set('fitgrains:npdiv', dialog_config['npdiv'])
        config.set('fitgrains:refit', dialog_config['refit'])
        config.set('fitgrains:threshold', dialog_config['threshold'])
        config.set('fitgrains:tth_max', dialog_config['tth_max'])
        config.set('fitgrains:tolerance:tth', dialog_config['tth_tolerances'])
        config.set('fitgrains:tolerance:eta', dialog_config['eta_tolerances'])
        config.set('fitgrains:tolerance:omega',
                   dialog_config['omega_tolerances'])

        kwargs = {
            'compl': self.completeness,
            'qfib': self.qfib,
            'qsym': config.material.plane_data.getQSym(),
            'cfg': config,
            'min_samples': min_samples,
            'compl_thresh': config.find_orientations.clustering.completeness,
            'radius': config.find_orientations.clustering.radius
        }
        self.update_progress_text('Running clustering')
        qbar, cl = run_cluster(**kwargs)

        # Generate grains table
        num_grains = qbar.shape[1]
        if num_grains == 0:
            print('Fit Grains Complete - no grains were found')
            return

        shape = (num_grains, 21)
        grains_table = np.empty(shape)
        gw = instrument.GrainDataWriter(array=grains_table)
        for gid, q in enumerate(qbar.T):
            phi = 2 * np.arccos(q[0])
            n = xfcapi.unitRowVector(q[1:])
            grain_params = np.hstack(
                [phi * n, const.zeros_3, const.identity_6x1])
            gw.dump_grain(gid, 1., 0., grain_params)
        gw.close()

        self.update_progress_text(
            f'Found {num_grains} grains. Running fit optimization.')

        self.fit_grains_results = fit_grains(config,
                                             grains_table,
                                             write_spots_files=False)
        print('Fit Grains Complete')

    def view_fit_grains_results(self):
        if self.fit_grains_results is None:
            QMessageBox.information(self.parent, "No Grains Fond",
                                    "No grains were found")
            return

        for result in self.fit_grains_results:
            print(result)

        # Build grains table
        num_grains = len(self.fit_grains_results)
        shape = (num_grains, 21)
        grains_table = np.empty(shape)
        gw = instrument.GrainDataWriter(array=grains_table)
        for result in self.fit_grains_results:
            gw.dump_grain(*result)
        gw.close()

        # Display results dialog
        dialog = FitGrainsResultsDialog(grains_table, self.parent)
        dialog.ui.resize(1200, 800)
        self.fit_grains_results_dialog = dialog
        dialog.show()

    def update_progress_text(self, text):
        self.progress_text.emit(text)

    def on_async_error(self, t):
        exctype, value, traceback = t
        msg = f'An ERROR occurred: {exctype}: {value}.'
        msg_box = QMessageBox(QMessageBox.Critical, 'Error', msg)
        msg_box.setDetailedText(traceback)
        msg_box.exec_()
Ejemplo n.º 23
0
class mainwindow_logic(QMainWindow, Ui_LRCSNet):
    def __init__(self, *args, **kwargs):
        QMainWindow.__init__(self, *args, **kwargs)
        self.setupUi(self)
        self.setWindowTitle(
            'Welcome to SegmentPy {}'.format(SEGMENTPY_VERSION))
        self.menubar.setNativeMenuBar(False)

        self.printLogo()
        # init the gpu queue and proc list
        self.gpu_queue = Queue()
        gpu_list = get_available_gpus_wrapper()

        for i in gpu_list:
            self.gpu_queue.put(i)
        if len(gpu_list) == 0:
            self.gpu_queue.put('cpu')
            # self.gpu_queue.put(1)  # todo: uncomment here for similation
            # self.gpu_queue.put(2)  # todo: uncomment here for similation
            # self.gpu_queue.put(3)  # todo: uncomment here for similation

        self.qManager = queueManager(gpu_queue=self.gpu_queue)
        self.refresh_gpu_list()
        self.proc_list = []  # tuple of (str: gpu, str: pid, subprocess)
        self.refresh_proc_list()
        self.actVs = []
        self.gradVs = []

        self.threadpool = QThreadPool()
        self.qManager.signals.available_gpu.connect(self.start)

        _translate = QtCore.QCoreApplication.translate

        # manually set absolute path for button icons
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "play-button.png")),
                       QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.start_button.setIcon(icon)
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "exchange_2.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        icon1.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "exchange.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.On)
        self.loop_button.setIcon(icon1)
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "stop.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        icon2.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "stop.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.On)
        self.stop_button.setIcon(icon2)
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "plus.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        icon3.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "plus.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.On)
        self.add_button.setIcon(icon3)
        icon4 = QtGui.QIcon()
        icon4.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "minus.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        icon4.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "minus.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.On)
        self.clean_button.setIcon(icon4)
        icon5 = QtGui.QIcon()
        icon5.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "reply.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        icon5.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "reply.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.On)
        self.forward_button.setIcon(icon5)
        icon6 = QtGui.QIcon()
        icon6.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "resume.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        icon6.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "resume.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.On)
        self.pushButton.setIcon(icon6)
        icon7 = QtGui.QIcon()
        icon7.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "rubik.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        icon7.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "rubik.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.On)
        self.predict_button.setIcon(icon7)
        icon8 = QtGui.QIcon()
        icon8.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "speedometer.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        icon8.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "speedometer.png")),
                        QtGui.QIcon.Normal, QtGui.QIcon.On)
        self.dashboard_button.setIcon(icon8)
        icon9 = QtGui.QIcon()
        icon9.addPixmap(QtGui.QPixmap(os.path.join(imgDir, "tuto.png")))
        self.tuto_button.setIcon(icon9)

        # init the hyper-params tablewidget
        item = self.tableWidget.item(0, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)  #note: make it read only
        item.setText(_translate("LRCSNet", "model"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(1, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "kernel size"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(2, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "conv nb"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(3, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "window size"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(4, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "batch size"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(5, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "nb epoch"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(6, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "batch norm"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(7, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "augmentation"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(8, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "dropout"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(9, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "lr type"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(10, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "lr init"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(11, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "k param"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(12, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "period"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(13, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "act fn"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(14, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "loss fn"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(15, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "cls/reg"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(16, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "sv step"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(17, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "tb step"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(18, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "comment"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(19, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "sampl. gap"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(20, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "stop. crit."))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(21, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "correction"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(22, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "trn repo. path"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(23, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "val repo. path"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(24, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "tst repo. path"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(25, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "mdl. saved path"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(26, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "ckpt path"))
        item.setBackground(QtGui.QColor(128, 128, 128))
        item = self.tableWidget.item(27, 0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setText(_translate("LRCSNet", "nodes"))
        item.setBackground(QtGui.QColor(128, 128, 128))

        self.header = ['Parameters', 'nextTrain']
        self.setHeader()

        # set the the buttons
        self.start_button.clicked.connect(self.start)
        self.stop_button.clicked.connect(self.stop)
        self.add_button.clicked.connect(self.addTrain)
        self.clean_button.clicked.connect(self.clean)
        self.loop_button.clicked.connect(self.loop_state)
        self.pushButton.clicked.connect(self.addResume)
        self.forward_button.clicked.connect(self.forward)
        self.dashboard_button.clicked.connect(self.openDashboard)
        self.predict_button.clicked.connect(self.predict)
        self.tuto_button.clicked.connect(self.open_website)

        # menu bar
        self.Activations.triggered.connect(self.activation_plugin)
        self.ActViewer.triggered.connect(self.actViewer_plugin)
        self.Loss_Landscape.triggered.connect(self.loss_landscape)
        self.Random_Forest.triggered.connect(self.random_forest)
        self.Volumes_Viewer.triggered.connect(self.volViewer_plugin)
        self.Metrics.triggered.connect(self.metric_plugin)
        self.AugViewer.triggered.connect(self.augViewer_plugin)
        self.GradViewer.triggered.connect(self.gradViewer_plugin)
        # self.actionGrid_Search.triggered.connect(self.gSearch)
        self.actionExtract_Result.triggered.connect(self.resultsExtractor)

    ################# menubar methods
    def resultsExtractor(self):
        self.rltE = rltExtractor_logic()
        try:
            self.rltE.show()
        except Exception as e:
            self.log_window('Unknown error', e.args[0])

    def gradViewer_plugin(self):
        i = self.gradVs.__len__()
        gradV = gradView_logic()
        self.gradVs.append(gradV)
        try:
            self.gradVs[i].show()
        except Exception as e:
            self.log_window('Unknown error', e.args[0])

    def actViewer_plugin(self):
        i = self.actVs.__len__()
        actV = actViewer_logic()
        self.actVs.append(
            actV)  #note: not using self.actV to create a new instance
        try:
            self.actVs[i].show()
        except Exception as e:
            self.log_window('Unknown error', e.args[0])

    def augViewer_plugin(self):
        self.augV = augViewer_logic()
        try:
            self.augV.show()
        except Exception as e:
            self.log_window('Unknown error', e.args[0])

    def metric_plugin(self):
        self.metric = metric_logic()
        try:
            self.metric.show()
        except Exception as e:
            self.log_window('Unknown error', e.args[0])

    def volViewer_plugin(self):
        self.volViewer = volViewer_logic()
        try:
            self.volViewer.exec_()
        except Exception as e:
            self.log_window('Unknown error', e.args[0])

    def activation_plugin(self):
        # os.environ["CUDA_VISIBLE_DEVICES"] = "-1"  #note: here might have conflict if there's an ongoing training with GPU
        import tensorflow as tf
        # get ckpt file
        dialog = file_dialog(
            title='select ckpts (*.meta) to retrieve activations',
            type='.meta')
        ckpt_paths = dialog.openFileNamesDialog()
        logger.debug('ckpt_paths: {}'.format(ckpt_paths))
        if ckpt_paths is not None and (not not ckpt_paths):
            # get input path
            dialog = file_dialog(
                title='select folder which contains datas for analyses')
            data_folder = dialog.openFolderDialog()
            logger.debug('data_folder: {}'.format(data_folder))
            if data_folder is not None and (not not data_folder):
                if len(ckpt_paths) != 0:
                    # restore from ckpt the nodes
                    tf.reset_default_graph()
                    logger.debug(ckpt_paths[0])
                    _ = tf.train.import_meta_graph(
                        ckpt_paths[0],
                        clear_devices=True,
                    )

                    # get arguments
                    graph = tf.get_default_graph().as_graph_def()
                    nodes = print_nodes_name(graph)
                    steps = [
                        re.search('step(\d+)', ck_pth).group(1)
                        for ck_pth in ckpt_paths
                    ]

                    # retrive nodes of activations
                    options = []
                    for node in nodes:
                        tmp = re.search(
                            '(^[a-zA-Z]+\d*\/).*(leaky|relu|sigmoid|tanh|logits\/identity|up\d+\/Reshape\_4|concat)$',
                            node)
                        if tmp is not None:
                            tmp = tmp.string
                            if 'identity' in tmp:
                                iden = tmp
                            else:
                                options.append(tmp)

                    # open nodes list dialog
                    nodes_list = node_list_logic(options=options)
                    nodes_list.exec()
                    if nodes_list.result() == 1:
                        acts = nodes_list.return_nodes()
                        if iden:
                            acts.append(iden)
                        types = nodes_list.return_analysis_types()
                        if len(types) == 0:
                            types = ['activation']

                        terminal = [
                            'python',
                            os.path.join(parentDir, 'main_analytic.py'),
                            '-ckpt',
                            *ckpt_paths,
                            '-step',
                            *steps,
                            '-type',
                            *types,
                            '-node',
                            *acts,
                            '-dir',
                            data_folder,
                        ]

                        logger.debug(terminal)

                        proc = subprocess.Popen(terminal)
                        proc.wait()

    def log_window(self, title: str, Msg: str):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Critical)
        msg.setText(Msg)
        msg.setWindowTitle(title)
        msg.exec_()

    def loss_landscape(self):
        self.log_window(
            title='Ooppsss',
            Msg=
            "Plug-in loss landscape of Goldstein et al. is coming in the next version. \nYou can try at terminal with main_loss_landscape.py"
        )

    def random_forest(self):
        self.log_window(
            title='Ooppsss',
            Msg=
            "Plug-in randomForest of Arganda-Carreras et al. is coming in the next version. \nYou can try at terminal with randomForest.py"
        )

    ############ main button methods
    def addTrain(self):
        default = {
            # 'mdl': 'model',
            # 'bat_size': 'batch size',
            # 'win_size': '512',
            # 'conv_size': 'kernel size',
            # 'nb_conv': 'conv nb',
            # 'act_fn': 'leaky',
            # 'lss_fn': 'DSC',
            # 'batch_norm': 'True',
            # 'aug': 'True',
            # 'dropout': '0.0',
            # 'lr_type': 'ramp',
            # 'lr_init': 'lr init',
            # 'lr_k': 'k param',
            # 'lr_p': '50',
            # 'cls_reg': 'classification',
            # 'comment': 'comment',
            # 'nb_epoch': '500',
            # 'sv_step': '160',
            # 'tb_step': '50',
            # 'gap': '50',
            # 'condition': '0.001',
            # 'correction': '1e-2',
            # 'train_dir': 'trn repo. path',
            # 'val_dir': 'val repo. path',
            # 'test_dir': 'tst repo. path',
            # 'log_dir': 'mdl. saved path',
        }
        self.dialog = dialog_logic(None)
        self.dialog.exec(
        )  #.show() won't return because of the Qdialog attribute
        if self.dialog.result() == 1:  #cancel: 0, ok: 1
            output = self.dialog.return_params()
            # convert strings to list then loop
            new = {}
            for k, v in output.items():
                new[k] = v.replace(';', ',').split(',')
            for ks, ws, nc, bs, ilr, decay, period, dp, af, lf in product(
                    new['conv_size'], new['win_size'], new['nb_conv'],
                    new['bat_size'], new['lr_init'], new['lr_k'], new['lr_p'],
                    new['dropout'], new['act_fn'], new['lss_fn']):
                # HPs
                default['mdl'] = new['mdl'][0]
                default['conv_size'] = ks
                default['nb_conv'] = nc
                default['win_size'] = ws
                default['bat_size'] = bs
                default['lr_init'] = ilr
                default['lr_k'] = decay
                default['lr_p'] = period
                default['dropout'] = dp
                default['act_fn'] = af
                default['lss_fn'] = lf
                default['correction'] = new['correction'][0]
                default['gap'] = new['gap'][0]
                default['condition'] = new['condition'][0]
                # others
                default['train_dir'] = new['train_dir'][0]
                default['val_dir'] = new['val_dir'][0]
                default['test_dir'] = new['test_dir'][0]
                default['log_dir'] = new['log_dir'][0]
                default['comment'] = new['comment'][0]
                default['batch_norm'] = new['batch_norm'][0]
                default['aug'] = new['aug'][0]
                default['nb_epoch'] = new['nb_epoch'][0]
                default['win_size'] = new['win_size'][0]
                default['lr_type'] = new['lr_type'][0]
                default['cls_reg'] = new['cls_reg'][0]
                default['sv_step'] = new['sv_step'][0]
                default['tb_step'] = new['tb_step'][0]
                logger.debug(default)
                self.write_train_column(contents=default)

    def write_train_column(self, contents: dict):
        pivot_table = {
            'mdl': 'model',
            'bat_size': 'batch size',
            'win_size': 'window size',
            'conv_size': 'kernel size',
            'nb_conv': 'conv nb',
            'act_fn': 'act fn',
            'lss_fn': 'loss fn',
            'batch_norm': 'batch norm',
            'aug': 'augmentation',
            'dropout': 'dropout',
            'lr_type': 'lr type',
            'lr_init': 'lr init',
            'lr_k': 'k param',
            'lr_p': 'period',
            'cls_reg': 'cls/reg',
            'comment': 'comment',
            'nb_epoch': 'nb epoch',
            'sv_step': 'sv step',
            'tb_step': 'tb step',
            'gap': 'sampl. gap',
            'condition': 'stop. crit.',
            'correction': 'correction',
            'train_dir': 'trn repo. path',
            'val_dir': 'val repo. path',
            'test_dir': 'tst repo. path',
            'log_dir': 'mdl. saved path',
        }

        nb_col = self.tableWidget.columnCount()
        self.tableWidget.setColumnCount(nb_col + 1)

        # write params
        for k, v in contents.items():
            i = self.tableWidget.findItems(pivot_table[k],
                                           Qt.MatchFlag.MatchExactly)
            self.tableWidget.setItem(i[0].row(), nb_col - 1,
                                     QTableWidgetItem(v))

        # fill somethine on the empty cell
        i = self.tableWidget.findItems('ckpt path', Qt.MatchFlag.MatchExactly)
        self.tableWidget.setItem(i[0].row(), nb_col - 1,
                                 QTableWidgetItem('None'))

        i = self.tableWidget.findItems('nodes', Qt.MatchFlag.MatchExactly)
        self.tableWidget.setItem(i[0].row(), nb_col - 1,
                                 QTableWidgetItem('None'))

        if nb_col > 2:
            self.header.append('train')
        else:
            self.header[1] = 'nextTrain'
        # bold first column
        self.lock_params()
        self.bold(column=1)
        self.setHeader()

    def addResume(self):
        pivot_table = {
            # output dict key --> SegmentPy column name
            'model': 'model',
            'batch_size': 'batch size',
            'window_size': 'window size',
            'kernel_size': 'kernel size',
            'nb_conv': 'conv nb',
            'act_fn': 'act fn',
            'BatchNorm': 'batch norm',
            'augmentation': 'augmentation',
            'dropout': 'dropout',
            'loss_fn': 'loss fn',
            'lr_decay_type': 'lr type',
            'lr_init': 'lr init',
            'lr_decay': 'k param',
            'lr_period': 'period',
            'comment': 'comment',
            'ckpt_path': 'ckpt path',
            'nb_epoch': 'nb epoch',
            'sv_step': 'sv step',
            'tb_step': 'tb step',
            'sampl. gap': 'sampl. gap',
            'condition': 'stop. crit.',
            'mode': 'cls/reg',
            'correction': 'correction',
            'trn repo.path': 'trn repo. path',
            'val repo.path': 'val repo. path',
            'tst repo.path': 'tst repo. path',
            'mdl. saved path': 'mdl. saved path',
        }
        self.Rdialog = resumeDialog_logic(None)
        self.Rdialog.exec()
        if self.Rdialog.result() == 1:
            output = self.Rdialog.return_params()

            nb_col = self.tableWidget.columnCount()
            self.tableWidget.setColumnCount(nb_col + 1)

            # overwrite old params
            old_hypers = string_to_hypers(output['ckpt_path']).parse()
            old_hypers['ckpt_path'] = output['ckpt_path']
            old_hypers['nb_epoch'] = int(output['extra_ep'])
            old_hypers['trn repo.path'] = output['trn repo. path']
            old_hypers['val repo.path'] = output['val repo. path']
            old_hypers['tst repo.path'] = output['tst repo. path']
            old_hypers['mdl. saved path'] = output['mdl. saved path']

            # new lr
            old_hypers['lr_init'] = output['lr_init']
            old_hypers['lr_decay'] = output['lr_decay']
            old_hypers['lr_period'] = output['lr_period']

            # new sampling/stop condition
            old_hypers['sampl. gap'] = output['gap']
            old_hypers['condition'] = output['condition']

            old_hypers['sv_step'] = output['sv step']
            old_hypers['tb_step'] = output['tb step']

            # todo: add restricted node to restore
            self.Rnodes = resumeNodes_logic(ckpt_path=old_hypers['ckpt_path'])
            self.Rnodes.exec()
            if self.Rnodes.result() == 1:
                Rnodes = self.Rnodes.get_restore_nodes()
                i = self.tableWidget.findItems('nodes',
                                               Qt.MatchFlag.MatchExactly)
                self.tableWidget.setItem(i[0].row(), nb_col - 1,
                                         QTableWidgetItem(str(Rnodes)))

            # write other params
            for k, v in old_hypers.items():
                i = self.tableWidget.findItems(pivot_table[k],
                                               Qt.MatchFlag.MatchExactly)
                self.tableWidget.setItem(i[0].row(), nb_col - 1,
                                         QTableWidgetItem(str(v)))

            # fill the empty or might throw NoneType error elsewhere
            # i = self.tableWidget.findItems('sv step', Qt.MatchFlag.MatchExactly)
            # self.tableWidget.setItem(i[0].row(), nb_col - 1, QTableWidgetItem('None'))
            # i = self.tableWidget.findItems('tb step', Qt.MatchFlag.MatchExactly)
            # self.tableWidget.setItem(i[0].row(), nb_col - 1, QTableWidgetItem('None'))

            # handle the headers
            if nb_col > 2:
                self.header.append('resume')
            else:
                self.header[1] = 'nextResume'
            # bold first column
            self.lock_params()
            self.bold(column=1)
            self.setHeader()

    def predict(self):
        self.predDialog = predictDialog_logic(None)
        self.predDialog.exec()
        if self.predDialog.result() == 1:
            self.printLogo()
            ckpt_path, raw_folder, pred_folder, correction, cores = self.predDialog.get_params(
            )
            logger.debug(
                'ckpt path:{}\nraw dir:{}\npred dir:{}\ncorr:{}\ncpu:{}\n'.
                format(ckpt_path, raw_folder, pred_folder, correction, cores))
            _Worker = predict_Worker(
                ckpt_path=ckpt_path,
                pred_dir=raw_folder,
                save_dir=pred_folder,
                correction=correction,
                cores=cores,
            )
            self.threadpool.start(_Worker)
            _Worker.signals.start_proc.connect(self.add_proc_surveillance)
            _Worker.signals.released_proc.connect(
                self.remove_process_from_list)

    def start(self):
        if self.header[1] == 'nextTrain':
            self.printLogo()
            if not self.gpu_queue.empty() and self.verify_column_not_None():
                gpu = self.gpu_queue.get()
                self.refresh_gpu_list()

                # start a thread
                _Worker = training_Worker(gpu, self.grab_params())
                self.threadpool.start(_Worker)
                _Worker.signals.start_proc.connect(self.add_proc_surveillance)

                # release gpu and process
                _Worker.signals.error.connect(self.print_in_log)
                _Worker.signals.released_gpu.connect(self.enqueue)
                _Worker.signals.released_proc.connect(
                    self.remove_process_from_list)

            elif not self.verify_column_not_None():
                print('Should fulfill the first column. \r')
            else:
                print('Waiting for available gpu \r')

        elif self.header[1] == 'nextResume':
            self.printLogo()
            gpu = self.gpu_queue.get()
            self.refresh_gpu_list()

            # start a thread
            _Worker = retraining_Worker(gpu, self.grab_params())
            self.threadpool.start(_Worker)
            _Worker.signals.start_proc.connect(self.add_proc_surveillance)

            # release gpu and process
            _Worker.signals.error.connect(self.print_in_log)
            _Worker.signals.released_gpu.connect(self.enqueue)
            _Worker.signals.released_proc.connect(
                self.remove_process_from_list)

    def print_in_log(self, content):
        # todo: in the log window
        print(content)

    def loop_state(self):
        if self.loop_button.isChecked():
            self.qManager.start()
        else:
            self.qManager.stop()

    def stop(self):
        # refers to: https://stackoverflow.com/questions/37601672/how-can-i-get-the-indices-of-qlistwidgetselecteditems
        selected = self.ongoing_process.selectionModel().selectedIndexes()
        for item in selected:
            # options
            # os.kill(self.proc_list[item.row()][2].pid, sig.SIGTERM)
            # self.proc_list[item.row()][2].terminate()
            self.proc_list[item.row()][2].kill()

    def clean(self):
        column = self.tableWidget.currentColumn()
        if column > 1:
            self.tableWidget.removeColumn(column)

        elif column == 1:
            self.tableWidget.removeColumn(column)
            nb_col = self.tableWidget.columnCount()
            if nb_col < 2:
                self.tableWidget.setColumnCount(nb_col + 1)

        self.bold(column=1)
        self.popHeader(column)
        self.tableWidget.repaint()

    def forward(self):
        column = self.tableWidget.currentColumn()
        if column > 1:
            self.tableWidget.insertColumn(column - 1)
            for i in range(self.tableWidget.rowCount()):
                self.tableWidget.setItem(
                    i, column - 1, self.tableWidget.takeItem(i, column + 1))
                self.tableWidget.setCurrentCell(i, column - 1)
            self.tableWidget.removeColumn(column + 1)

            if column == 2:
                self.bold(column=1)
                self.unbold(column=2)

                # change the internal header list
                if self.header[column - 1] == 'nextTrain':
                    self.header[column - 1] = 'train'
                elif self.header[column - 1] == 'nextResume':
                    self.header[column - 1] = 'resume'
                if self.header[column] == 'train':
                    self.header[column] = 'nextTrain'
                elif self.header[column] == 'resume':
                    self.header[column] = 'nextResume'

            # swap header
            self.header[column - 1], self.header[column] = self.header[
                column], self.header[column - 1]

        # reset headers
        self.setHeader()
        self.tableWidget.repaint()

    def openDashboard(self):
        self.Dashboard = dashboard_logic(None)
        self.Dashboard.show()

    ########### ongoing/available Qlist methods

    def enqueue(self, gpu):
        self.gpu_queue.put(gpu)
        self.refresh_gpu_list()

    def add_proc_surveillance(self, signal):
        self.proc_list.append(signal)
        self.refresh_proc_list()

    def remove_process_from_list(self, signal: tuple):
        # (str, subprocess.Popen)
        try:
            self.proc_list.remove(signal)
            self.refresh_proc_list()
        except Exception as e:
            logger.error(e)

    def refresh_gpu_list(self):
        self.AvailableGPUs.clear()
        self.AvailableGPUs.addItems([str(i) for i in self.gpu_queue.queue
                                     ]  # QListWidget only takes string not int
                                    )

    def refresh_proc_list(self):
        # this method only manipulate str in QlistWidget
        self.ongoing_process.clear()
        self.ongoing_process.addItems(
            ['{}'.format(t[0]) for t in self.proc_list])
        for i, sig in zip(range(self.ongoing_process.count()), self.proc_list):
            self.ongoing_process.item(i).setToolTip(
                str(sig[3]).replace(',', '\n'))

    ########### Qtable method

    def grab_params(self):
        nb_row = self.tableWidget.rowCount()
        out = {}

        # get training params or resume params
        if self.header[1] == 'nextTrain':
            for row in range(nb_row):
                out[self.tableWidget.item(row,
                                          0).text()] = self.tableWidget.item(
                                              row, 1).text()
            self.popHeader(1)

        elif self.header[1] == 'nextResume':
            for row in range(nb_row):
                if self.tableWidget.item(row, 1) is not None:
                    # use index name as dict's key
                    out[self.tableWidget.item(
                        row, 0).text()] = self.tableWidget.item(row, 1).text()
            self.popHeader(1)

        else:
            raise NotImplementedError

        # refresh the table
        self.tableWidget.removeColumn(1)
        self.setHeader()
        self.bold(column=1)
        return out

    def verify_column_not_None(self, column=1):
        nb_row = self.tableWidget.rowCount()
        for row in range(nb_row):
            item = self.tableWidget.item(row, column)
            if item is None:
                return False
        return True

    def lock_params(self):
        for column, head in enumerate(self.header):
            if 'train' in head.lower():
                for row in range(self.tableWidget.rowCount()):
                    # if self.tableWidget.item(row, column) is not None:
                    #     # self.tableWidget.item(row, column).setFlags(QtCore.Qt.ItemIsEnabled)  # note: make it editable
                    #     pass
                    if self.tableWidget.item(
                            row, 0).text() in ['ckpt path', 'nodes']:
                        try:
                            self.tableWidget.item(row, column).setBackground(
                                QtGui.QColor(230, 230, 250))
                            self.tableWidget.item(row, column).setFlags(
                                QtCore.Qt.ItemIsEditable)
                        except Exception as e:
                            logger.debug(e)

            elif 'resume' in head.lower():
                for row in range(self.tableWidget.rowCount()):
                    if self.tableWidget.item(row, 0).text() not in [
                            'lr type', 'lr init', 'k param', 'period',
                            'ckpt path', 'comment', 'nb epoch', 'nodes',
                            'mdl. saved path', 'sampl. gap', 'stop. crit.',
                            'correction'
                    ]:
                        if self.tableWidget.item(row, column) is not None:
                            self.tableWidget.item(row, column).setBackground(
                                QtGui.QColor(230, 230, 250))
                            self.tableWidget.item(row, column).setFlags(
                                QtCore.Qt.ItemIsEditable
                            )  # note: make it read only

    def setHeader(self):
        self.tableWidget.setHorizontalHeaderLabels(self.header)

    def bold(self, column):
        font = QtGui.QFont()
        font.setBold(True)
        for row_id in range(self.tableWidget.rowCount()):
            item = self.tableWidget.item(row_id, column)
            if item is not None:
                item.setFont(font)

    def unbold(self, column):
        font = QtGui.QFont()
        font.setBold(False)
        for row_id in range(self.tableWidget.rowCount()):
            item = self.tableWidget.item(row_id, column)
            if item is not None:
                item.setFont(font)

    def popHeader(self, column=1):
        if column == 1:
            try:
                if self.header[column + 1] == 'train':
                    self.header[column + 1] = 'nextTrain'
                elif self.header[column + 1] == 'resume':
                    self.header[column + 1] = 'nextResume'
                else:
                    raise ValueError('capture unknown header')
                self.header.pop(column)
            except IndexError as e:
                self.header.pop(column)
                self.header.append('nextTrain')

        self.setHeader()

    def printLogo(self):

        print('\n\n\n')
        print(
            '███████╗███████╗ ██████╗ ███╗   ███╗███████╗███╗   ██╗████████╗██████╗ ██╗   ██╗ '
        )
        print(
            '██╔════╝██╔════╝██╔════╝ ████╗ ████║██╔════╝████╗  ██║╚══██╔══╝██╔══██╗╚██╗ ██╔╝ '
        )
        print(
            '███████╗█████╗  ██║  ███╗██╔████╔██║█████╗  ██╔██╗ ██║   ██║   ██████╔╝ ╚████╔╝  '
        )
        print(
            '╚════██║██╔══╝  ██║   ██║██║╚██╔╝██║██╔══╝  ██║╚██╗██║   ██║   ██╔═══╝   ╚██╔╝   '
        )
        print(
            '███████║███████╗╚██████╔╝██║ ╚═╝ ██║███████╗██║ ╚████║   ██║   ██║        ██║    '
        )
        print(
            '╚══════╝╚══════╝ ╚═════╝ ╚═╝     ╚═╝╚══════╝╚═╝  ╚═══╝   ╚═╝   ╚═╝        ╚═╝    '
        )
        print('\n')
        print(
            '                                                          -version {}'
            .format(SEGMENTPY_VERSION))
        print('\n\n\n')

    def open_website(self):
        webbrowser.open_new_tab('https://segmentpy.readthedocs.io/en/latest/')
Ejemplo n.º 24
0
class SiteUpdater(QWidget):
    def __init__(self):
        super().__init__()
        self.start_button = QPushButton('Start')
        self.keywords_line = QLineEdit()
        self.url_line = QLineEdit()
        self.status_label = QLabel('nix')
        self.threadpool = QThreadPool()
        self.initUI()

    def initUI(self):
        self.start_button.clicked.connect(self.on_start_clicked)
        grid = QGridLayout()

        grid.addWidget(QLabel('Keywords:'), 0, 0)
        grid.addWidget(self.keywords_line, 0, 1)

        grid.addWidget(QLabel('URL:'), 1, 0)
        grid.addWidget(self.url_line, 1, 1)

        grid.addWidget(self.start_button, 2, 0)

        grid.addWidget(QLabel('Status:'), 3, 0)
        grid.addWidget(self.status_label, 3, 1)

        self.setLayout(grid)
        self.setMinimumWidth(500)
        self.setWindowTitle('Site Updater')

        self.show()

    def on_start_clicked(self):
        self.status_label.setText('Check Input')
        keywords_found = self.keywords_line.text()
        url = self.url_line.text()
        try:
            self.status_label.setText('Gehe auf Seite')
            request = requests.get(url=url)
        except Exception:
            self.status_label.setText('Kaputte URL du Hund')
            return
        source_code = request.text.lower()
        keywords_found = keywords_found.lower().strip()
        keywords_list = keywords_found.split(' ')
        if len(keywords_list) <= 0:
            self.status_label.setText('Keine Keywords du Bauer')
            return
        keywords_list = list(dict.fromkeys(keywords_list))
        keywords_dict = {}
        keyword_message = 'Aktuell gefunden:\n\n'
        for keyword in keywords_list:
            count = source_code.count(keyword)
            keywords_dict[keyword] = count
            keyword_message += f'{keyword}: \t{count} \n\n'
        keyword_message += 'Alert wenn sich die Anzahl ändert?'
        self.status_label.setText('Erfolgreich Seite abgerufen')

        message_box = QMessageBox.question(
            self, 'Solls los gehn?', keyword_message,
            QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel)
        if message_box == QMessageBox.Yes:
            self.status_label.setText('JETZT GEHTS LOS!!')
            worker = Worker(self.status_label, source_code, keywords_dict, url)
            self.threadpool.start(worker)
        else:
            self.status_label.setText('Heid ned')
Ejemplo n.º 25
0
class Widget(QWidget):
    def __init__(self, device, input_dim):
        QWidget.__init__(self)

        self.device = device
        self.input_dim = input_dim
        self.threadpool = QThreadPool()
        print("Multithreading with maximum %d threads" %
              self.threadpool.maxThreadCount())

        with res.open_binary('Titanicbc', 'config.yaml') as fp:
            model_parameters = yaml.load(fp, Loader=yaml.Loader)

        hidden_dim_current = model_parameters['Binary_Network'][
            'initialisations']['hidden_dim']
        learning_rate_current = model_parameters['Binary_Network'][
            'optimiser']['learning_rate']
        num_epochs_current = model_parameters['Binary_Network']['num_epochs']
        weight_init_current = model_parameters['Binary_Network'][
            'initialisations']['weight_init']
        weight_decay_current = model_parameters['Binary_Network']['optimiser'][
            'weight_decay']

        # layout

        ## Read in current values from config.yaml as the default values in QForm
        self.layout = QFormLayout()

        self.num_epochs = QLineEdit(str(num_epochs_current))
        self.num_epochs_label = QLabel("Number of Epochs")

        self.learning_rate = QLineEdit(str(learning_rate_current))
        self.learning_rate_label = QLabel("Learning Rate")

        self.weight_decay = QLineEdit(str(weight_decay_current))
        self.weight_decay_label = QLabel("Weight Decay")

        self.weight_init = QLineEdit(str(weight_init_current))
        self.weight_init_label = QLabel("Weight Initialisation")

        self.hidden_dim = QLineEdit(str(hidden_dim_current))
        self.hidden_dim_label = QLabel("Hidden Layers Dimension")

        self.confirm = QPushButton("Confirm network configuration and train")
        self.predict = QPushButton("Predict using last trained model")
        self.plot_loss = QPushButton("Plot loss of last trained model")
        self.output = QPushButton("Open output.csv")
        self.quit = QPushButton("Quit")

        self.layout.addRow(self.num_epochs_label, self.num_epochs)
        self.layout.addRow(self.learning_rate_label, self.learning_rate)
        self.layout.addRow(self.weight_decay_label, self.weight_decay)
        self.layout.addRow(self.hidden_dim_label, self.hidden_dim)
        self.layout.addRow(self.weight_init_label, self.weight_init)
        self.layout.addWidget(self.confirm)
        self.layout.addWidget(self.predict)
        self.layout.addWidget(self.plot_loss)
        self.layout.addWidget(self.output)
        self.layout.addWidget(self.quit)

        # Set the layout to the QWidget
        self.setLayout(self.layout)

        # Signals and Slots
        self.confirm.clicked.connect(self.confirm_thread)
        self.predict.clicked.connect(self.op_predict)
        self.plot_loss.clicked.connect(self.plot)
        self.output.clicked.connect(self.open_output)
        self.quit.clicked.connect(self.quit_application)

        ## Execution here

    def confirm_thread(self):
        worker = Worker(self.confirm_configuration)
        self.threadpool.start(worker)

    @Slot()
    def confirm_configuration(self):

        train_num_epochs = self.num_epochs.text()
        train_learning_rate = self.learning_rate.text()
        train_weight_decay = self.weight_decay.text()
        train_weight_init = self.weight_init.text()
        train_hidden_dim = self.hidden_dim.text()

        ## read about passing values out of here into
        print("Network Configuration")
        print("Number of Epochs: {}".format(train_num_epochs))
        print("Learning Rate: {}".format(train_learning_rate))
        print("Weight Decay: {}".format(train_weight_decay))
        print("Weight Initialisation: {}".format(train_weight_init))
        print("Hidden Layers Dimension: {}".format(train_hidden_dim))

        with res.open_binary('Titanicbc', 'config.yaml') as fp:
            model_parameters = yaml.load(fp, Loader=yaml.Loader)

        model_parameters['Binary_Network']['initialisations'][
            'hidden_dim'] = int(train_hidden_dim)
        model_parameters['Binary_Network']['optimiser'][
            'learning_rate'] = float(train_learning_rate)
        model_parameters['Binary_Network']['num_epochs'] = int(
            train_num_epochs)
        model_parameters['Binary_Network']['initialisations'][
            'weight_init'] = str(train_weight_init)  ## Read in Binary_Network
        model_parameters['Binary_Network']['optimiser'][
            'weight_decay'] = float(train_weight_decay)

        ## write out parameters

        with res.path('Titanicbc', 'config.yaml') as cf:
            path = cf

        with open(path, 'w') as outfile:
            yaml.dump(model_parameters, outfile, default_flow_style=False)

        ## Read in package resources

        with res.open_binary('Titanicbc', 'train.csv') as train:
            train = pd.read_csv(train)

        with res.open_binary('Titanicbc', 'test.csv') as test:
            test = pd.read_csv(test)

        with res.path('Titanicbc', 'trained_model.pth') as m:
            model_path = m

        # All params are coming through as a string
        self.running_loss, model = Binary_Network.train_new_model(
            train, self.input_dim, train_hidden_dim, model_path,
            train_learning_rate, train_num_epochs, train_weight_decay)
        model.to(self.device)
        Binary_Network.predict(model, test)

    @Slot()
    def op_predict(self):

        with res.open_binary('Titanicbc', 'config.yaml') as fp:
            model_parameters = yaml.load(fp, Loader=yaml.Loader)

        with res.open_binary('Titanicbc', 'test.csv') as test:
            test_predict = pd.read_csv(test)

        prev_hidden_dim = model_parameters['Binary_Network'][
            'initialisations']['hidden_dim']

        with res.path('Titanicbc', 'trained_model.pth') as m:
            model_path = m

        model = Binary_Network.Binary_Network(self.input_dim, prev_hidden_dim)
        model = Binary_Network.load_models(model_path, model).to(self.device)
        Binary_Network.predict(model, test_predict)

    @Slot()
    def plot(self):
        try:
            plt.plot(self.running_loss)
            plt.xlabel('epochs')
            plt.ylabel('loss')
            plt.show()
        except:
            print("Must train a new model before plotting loss")

    @Slot()
    def open_output(self):
        with res.path('Titanicbc', 'output.csv') as op:
            path = op
        output = pd.read_csv(path)
        print(output)
        try:
            os.startfile(path)
        except:
            opener = "open" if sys.platform == "darwin" else "xdg-open"
            subprocess.call([opener, path])

    @Slot()
    def quit_application(self):
        QApplication.quit()
Ejemplo n.º 26
0
class SaveImagesDialog:
    def __init__(
        self,
        parent=None,
    ):
        loader = UiLoader()
        self.ui = loader.load_file('save_images_dialog.ui', parent)

        self.parent_dir = HexrdConfig().working_dir
        self.thread_pool = QThreadPool()
        self.progress_dialog = ProgressDialog(self.ui)

        self.setup_gui()
        self.setup_connections()

    def setup_gui(self):
        self.ui.detectors.clear()
        self.ui.detectors.addItems(HexrdConfig().detector_names)
        self.ui.pwd.setText(self.parent_dir)
        self.ui.pwd.setToolTip(self.parent_dir)
        if HexrdConfig().unagg_images:
            self.ui.ignore_agg.setEnabled(True)

    def setup_connections(self):
        self.ui.single_detector.toggled.connect(self.ui.detectors.setEnabled)
        self.ui.change_directory.clicked.connect(self.change_directory)

    def change_directory(self):
        caption = HexrdConfig().images_dirtion = 'Select directory for images'
        new_dir = QFileDialog.getExistingDirectory(self.ui,
                                                   caption,
                                                   dir=self.parent_dir)

        if new_dir:
            HexrdConfig().working_dir = new_dir
            self.parent_dir = new_dir
            self.ui.pwd.setText(self.parent_dir)
            self.ui.pwd.setToolTip(self.parent_dir)

    def save_images(self):
        if self.ui.ignore_agg.isChecked():
            ims_dict = HexrdConfig().unagg_images
        else:
            ims_dict = HexrdConfig().imageseries_dict
        dets = HexrdConfig().detector_names
        if self.ui.single_detector.isChecked():
            dets = [self.ui.detectors.currentText()]
        for det in dets:
            selected_format = self.ui.format.currentText().lower()
            filename = f'{self.ui.file_stem.text()}_{det}.{selected_format}'
            path = f'{self.parent_dir}/{filename}'
            if selected_format.startswith('hdf5'):
                selected_format = 'hdf5'
            elif selected_format.startswith('npz'):
                selected_format = 'frame-cache'

            kwargs = {}
            if selected_format == 'hdf5':
                # A path must be specified. Set it ourselves for now.
                kwargs['path'] = 'imageseries'
            elif selected_format == 'frame-cache':
                # Get the user to pick a threshold
                result, ok = QInputDialog.getDouble(self.ui, 'HEXRD',
                                                    'Choose Threshold', 10, 0,
                                                    1e12, 3)
                if not ok:
                    # User canceled...
                    return

                kwargs['threshold'] = result

                # This needs to be specified, but I think it just needs
                # to be the same as the file name...
                kwargs['cache_file'] = path

            worker = AsyncWorker(HexrdConfig().save_imageseries,
                                 ims_dict.get(det), det, path, selected_format,
                                 **kwargs)
            self.thread_pool.start(worker)
            self.progress_dialog.setWindowTitle(f'Saving {filename}')
            self.progress_dialog.setRange(0, 0)
            worker.signals.finished.connect(self.progress_dialog.accept)
            self.progress_dialog.exec_()

    def exec_(self):
        if self.ui.exec_():
            self.save_images()
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        layout = QVBoxLayout()

        self.progress = QProgressBar()

        button = QPushButton("START IT UP")
        button.pressed.connect(self.execute)

        self.status = QLabel("0 workers")

        layout.addWidget(self.progress)
        layout.addWidget(button)
        layout.addWidget(self.status)

        w = QWidget()
        w.setLayout(layout)

        # Dictionary holds the progress of current workers.
        self.worker_progress = {}

        self.setCentralWidget(w)

        self.show()

        self.threadpool = QThreadPool()
        print(
            "Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()
        )

        self.timer = QTimer()
        self.timer.setInterval(100)
        self.timer.timeout.connect(self.refresh_progress)
        self.timer.start()

    def execute(self):
        worker = Worker()
        worker.signals.progress.connect(self.update_progress)
        worker.signals.finished.connect(self.cleanup)  # <3>

        # Execute
        self.threadpool.start(worker)

    def cleanup(self, job_id):
        if all(v == 100 for v in self.worker_progress.values()):
            self.worker_progress.clear()  # Empty the dict.

            # Update the progress bar if we've removed a value.
            self.refresh_progress()

    def update_progress(self, job_id, progress):
        self.worker_progress[job_id] = progress

    def calculate_progress(self):
        if not self.worker_progress:
            return 0

        return sum(v for v in self.worker_progress.values()) / len(self.worker_progress)

    def refresh_progress(self):
        # Calculate total progress.
        progress = self.calculate_progress()
        print(self.worker_progress)
        self.progress.setValue(progress)
        self.status.setText("%d workers" % len(self.worker_progress))
Ejemplo n.º 28
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.counter = 0

        layout = QVBoxLayout()

        self.l = QLabel("Start")
        b = QPushButton("DANGER!")
        b.pressed.connect(self.oh_no)

        layout.addWidget(self.l)
        layout.addWidget(b)

        w = QWidget()
        w.setLayout(layout)

        self.setCentralWidget(w)

        self.show()

        self.threadpool = QThreadPool()
        print("Multithreading with maximum %d threads" %
              self.threadpool.maxThreadCount())

        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.recurring_timer)
        self.timer.start()

    @staticmethod
    def progress_fn(n):
        print("%d%% done" % n)

    @staticmethod
    def execute_this_fn(progress_callback):
        for n in range(0, 5):
            time.sleep(1)
            progress_callback.emit(n * 100 / 4)

        return "Done."

    @staticmethod
    def print_output(s):
        print(s)

    @staticmethod
    def thread_complete():
        print("THREAD COMPLETE!")

    def oh_no(self):
        # Pass the function to execute
        worker = Worker(
            self.execute_this_fn
        )  # Any other args, kwargs are passed to the run function
        worker.signals.result.connect(self.print_output)
        worker.signals.finished.connect(self.thread_complete)
        worker.signals.progress.connect(self.progress_fn)

        # Execute
        self.threadpool.start(worker)

    def recurring_timer(self):
        self.counter += 1
        self.l.setText("Counter: %d" % self.counter)
Ejemplo n.º 29
0
class DLPMotorController(QObject):

    class MOVEMENT_MESSAGE(Enum):
        MANUAL_MOVEMENT = 0
        INFILTRATION_STEP = 1
        REPOSITIONING_MOVEMENT = 2
        ORIGIN_MANUAL_MOVEMENT = 3
        ORIGIN_SETUP_MOVEMENT = 4
        PROJECTOR_MOVEMENT = 5
        NONE = 6

    print_text_signal = Signal(str)
    repositioning_completed_signal = Signal()
    ready_for_printing_signal = Signal()

    def __init__(self, printer_setup='BOTTOM-UP', motor_setup='ClearpathSDSK', spindle_pitch_micron=None, steps_per_revolution=None):
        QObject.__init__(self)
        if printer_setup == 'BOTTOM-UP':
            self.platform_direction = -1
        else:
            self.platform_direction = 1

        if motor_setup == 'ClearpathSDSK':
            self.motor_instance = clearpathSDSK.ClearpathSDSK()
        elif motor_setup == 'ClearpathSCSK':
            self.motor_instance = clearpathSCSK.ClearpathSCSK(axis_orientation=-1)
            if self.motor_instance is None:
                self.print_text_signal.emit("Clearpath SCSK not supported: selected Clearpath SDSK")
                self.motor_instance = clearpathSDSK.ClearpathSDSK()
        elif motor_setup == 'Nanostage':
            self.motor_instance = nanostage.NanoStage()
        else:
            print("Error: an invalid motor was selected!")
            sys.exit(1)
        self.motor_instance.print_text_signal.connect(self.print_to_console)
        self.motor_instance.connected_signal.connect(self.set_connection_status)
        self.motor_instance.homed_signal.connect(self.homing_completed)
        self.current_movement_message = self.MOVEMENT_MESSAGE.NONE
        self.motor_movement_timer = QTimer()
        self.motor_movement_timer.setSingleShot(True)
        self.motor_movement_timer.timeout.connect(self.__handle_movement_signals__)
        self.delay_timer = QTimer()
        self.delay_timer.setSingleShot(True)
        self.delay_timer.timeout.connect(self.__emit_repositioned_signal__)
        self.repositioning_delay = 500  # s
        self.feed_rate = 300  # mm/min
        self.projector_feed_rate = 300
        self.manual_plate_distance = 0  # mm
        self.manual_projector_distance = 0  # mm
        self.building_plate_origin = 0
        self.current_plate_position = 0
        self.projector_origin = 0
        self.projector_current_position = 0
        self.repositioning_offset = 5  # mm
        self.layer_thickness = 0
        self.available_ports = list_ports.comports()
        self.serial_port = ''
        ports = ()
        for p in self.available_ports:
            ports = ports + (str(p.device),)
        self.available_ports = ports
        if len(self.available_ports) > 0:
            self.serial_port = self.available_ports[0]
        self.building_plate_is_moving = False
        self.projector_is_moving = False
        self.is_connected = False
        self.threadpool = QThreadPool()

    def __wait_for_movement__(self, movement_length, feed_rate, message=0):
        delay = abs(movement_length)/feed_rate * 60 * 1000  # in ms
        self.current_movement_message = message
        print('delay', delay)
        self.motor_movement_timer = QTimer()
        self.motor_movement_timer.setSingleShot(True)
        self.motor_movement_timer.timeout.connect(self.__handle_movement_signals__)
        self.motor_movement_timer.setInterval(delay)
        self.motor_movement_timer.start()

    @Slot()
    def connect_printer(self):
        worker = Worker(self.motor_instance.connect_motor, self.serial_port)
        self.threadpool.start(worker)

    @Slot()
    def disconnect_printer(self):
        if self.motor_instance.disconnect_motor():
            self.building_plate_is_moving = False
            self.projector_is_moving = False
            self.is_connected = False

    @Slot()
    def reset_printer(self):
        if self.is_connected:
            self.motor_instance.reset_motor()
        else:
            self.print_text_signal.emit("The printer is NOT connected!")

    @Slot()
    def home_building_plate(self):
        if self.building_plate_is_moving:
            self.print_text_signal.emit("Building plate already moving!")
            return False
        if self.is_connected:
            self.building_plate_is_moving = True
            # self.motor_instance.home_building_plate()
            worker = Worker(self.motor_instance.home_motor)
            self.threadpool.start(worker)
            return True
        else:
            self.print_text_signal.emit("The printer is NOT connected!")
            return False

    @Slot()
    def reposition_next_layer(self, thickness):
        self.motor_instance.reposition_next_layer(thickness)

    @Slot()
    def begin_printing_process(self):
        self.motor_instance.begin_printing_process()

    @Slot()
    def set_origin(self):
        if self.is_connected:
            self.building_plate_origin = self.current_plate_position
            self.print_text_signal.emit("Setting NEW ORIGIN!")
            # self.motor_instance.set_origin()
        else:
            self.print_text_signal.emit("The printer is NOT connected!")
            return False

    @Slot()
    def move_building_plate(self, target_mm=None, print_on=True, message=None, relative_move=True):
        if message is None:
            message = self.MOVEMENT_MESSAGE.ORIGIN_MANUAL_MOVEMENT
        if target_mm is None:
            target_mm = self.manual_plate_distance
        if self.building_plate_is_moving:
            self.print_text_signal.emit("Building plate already moving!")
            return
        if self.is_connected:
            if self.motor_instance.move_motor(target_mm, self.feed_rate, relative_move):
                self.building_plate_is_moving = True
                if print_on:
                    self.print_text_signal.emit("...moving the building plate by " + str(target_mm) + "mm...")
                if relative_move:
                    self.current_plate_position += target_mm
                    distance_to_target = target_mm
                else:
                    distance_to_target = abs(self.current_plate_position - target_mm)
                    self.current_plate_position = target_mm
                self.__wait_for_movement__(distance_to_target, self.feed_rate, message)
                # if print_on:
                #     self.print_text_signal.emit("...building plate in position!")
        else:
            self.print_text_signal.emit("The printer is NOT connected!")

    @Slot()
    def move_plate_to_origin(self, message=None):
        if message is None:
            message = self.MOVEMENT_MESSAGE.ORIGIN_MANUAL_MOVEMENT
        if self.building_plate_is_moving:
            self.print_text_signal.emit("Building plate already moving!")
            return
        if self.is_connected:
            if self.motor_instance.move_motor(self.building_plate_origin, self.feed_rate, is_relative=False):
                self.building_plate_is_moving = True
                distance_to_target = abs(self.current_plate_position - self.building_plate_origin)
                self.__wait_for_movement__(distance_to_target, self.feed_rate, message)
                self.current_plate_position = self.building_plate_origin
        else:
            self.print_text_signal.emit("The printer is NOT connected!")

    @Slot()
    def move_projector(self, target=None, relative_move=True):
        if target is None:
            target = self.manual_projector_distance
        if self.projector_is_moving:
            self.print_text_signal.emit("Projector already moving!")
            return
        if self.is_connected:
            if self.motor_instance.move_projector(target, self.projector_feed_rate, relative_move):
                self.projector_is_moving = True
                if relative_move:
                    self.projector_current_position += target
                    distance_to_target = target
                else:
                    distance_to_target = abs(self.projector_current_position - target)
                    self.projector_current_position = target
                self.__wait_for_movement__(distance_to_target, self.projector_feed_rate, self.MOVEMENT_MESSAGE.PROJECTOR_MOVEMENT)
        else:
            self.print_text_signal.emit("The printer is NOT connected!")

    @Slot()
    def home_projector(self):
        if self.projector_is_moving:
            self.print_text_signal.emit("Projector already moving!")
            return
        if self.is_connected:
            self.motor_instance.home_projector()
            self.projector_current_position = 0
        else:
            self.print_text_signal.emit("The printer is NOT connected!")

    @Slot()
    def lock_projector(self):
        if self.is_connected:
            self.motor_instance.lock_projector()
        else:
            self.print_text_signal.emit("The printer is NOT connected!")

    @Slot(int)
    def select_port(self, idx):
        if idx >= 0:
            self.serial_port = self.available_ports[idx]
        else:
            self.serial_port = ''

    @Slot()
    def update_port_list(self):
        self.available_ports = list_ports.comports()
        ports = ()
        for p in self.available_ports:
            ports = ports + (str(p.device),)
        self.available_ports = ports

    @Slot()
    def get_port_list(self):
        return self.available_ports

    @Slot(float)
    def update_manual_plate_distance(self, distance):
        self.manual_plate_distance = distance

    @Slot(float)
    def update_manual_projector_distance(self, distance):
        self.manual_projector_distance = distance

    @Slot()
    def print_motor_position(self):
        self.motor_instance.print_motor_position()

    def stop_motor_movements(self):
        self.motor_instance.stop_motor_movements()
        self.building_plate_is_moving = False
        self.projector_is_moving = False

    @Slot(str)
    def print_to_console(self, text):
        self.print_text_signal.emit(text)

    def __infiltration_step__(self):
        movement_offset = self.platform_direction * self.repositioning_offset
        self.move_building_plate(target_mm=movement_offset, print_on=False,
                                 message=self.MOVEMENT_MESSAGE.INFILTRATION_STEP)

    def __lowering_step__(self):
        movement_offset = self.platform_direction * (-self.repositioning_offset + self.layer_thickness)
        self.move_building_plate(target_mm=movement_offset, print_on=False,
                                 message=self.MOVEMENT_MESSAGE.REPOSITIONING_MOVEMENT)

    def __finish_repositioning__(self):
        self.delay_timer = QTimer()
        self.delay_timer.setSingleShot(True)
        self.delay_timer.timeout.connect(self.__emit_repositioned_signal__)
        self.delay_timer.setInterval(self.repositioning_delay)
        self.delay_timer.start()

    def begin_printing_process(self):
        self.move_plate_to_origin(message=self.MOVEMENT_MESSAGE.ORIGIN_SETUP_MOVEMENT)

    def reposition_next_layer(self, thickness):
        self.layer_thickness = thickness
        if self.layer_thickness > 0.0:
            self.__infiltration_step__()
        else:
            self.delay_timer = QTimer()
            self.delay_timer.setSingleShot(True)
            self.delay_timer.timeout.connect(self.__emit_repositioned_signal__)
            self.delay_timer.setInterval(self.repositioning_delay)
            self.delay_timer.start()

    def __handle_movement_signals__(self):
        if self.current_movement_message == self.MOVEMENT_MESSAGE.MANUAL_MOVEMENT \
                or self.current_movement_message == self.MOVEMENT_MESSAGE.ORIGIN_MANUAL_MOVEMENT:
            self.building_plate_is_moving = False
            self.print_text_signal.emit("...building plate in position!")
        elif self.current_movement_message == self.MOVEMENT_MESSAGE.ORIGIN_SETUP_MOVEMENT:
            self.building_plate_is_moving = False
            self.ready_for_printing_signal.emit()
        elif self.current_movement_message == self.MOVEMENT_MESSAGE.INFILTRATION_STEP:
            self.building_plate_is_moving = False
            self.__lowering_step__()
        elif self.current_movement_message == self.MOVEMENT_MESSAGE.REPOSITIONING_MOVEMENT:
            self.building_plate_is_moving = False
            self.__finish_repositioning__()
        elif self.current_movement_message == self.MOVEMENT_MESSAGE.PROJECTOR_MOVEMENT:
            self.projector_is_moving = False

    def __emit_repositioned_signal__(self):
        self.repositioning_completed_signal.emit()

    @Slot(bool)
    def set_connection_status(self, status):
        self.is_connected = status

    @Slot(bool)
    def homing_completed(self, is_completed):
        self.building_plate_is_moving = False
        if is_completed:
            self.current_plate_position = 0

    def get_motor_step_length_microns(self):
        return self.motor_instance.get_step_length_microns()
Ejemplo n.º 30
0
class MainWindow(QMainWindow):
    def __init__(self, app):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setWindowTitle("PortaPoll")
        
        self.run_poll = True
        app.aboutToQuit.connect(self.thread_stop)

        self.ui.pushButton_poll.setStyleSheet("""
            QPushButton {
                background-color: red;
                color:white;    
            }   
            QPushButton:checked{
                background-color: rgb(35, 199, 35);
                border: none; 
            }
            QPushButton:hover{  
                background-color: grey; 
                border-style: outset;  
            }  
        """)

        with open(resource_path("config/settings.json")) as f:
            self.settings = json.load(f)

        self.ui.tableWidget.setColumnCount(len(self.settings["tags"]) + 1)
        self.ui.tableWidget.setHorizontalHeaderItem(0, QTableWidgetItem("Date"))
        for i in range(0, len(self.settings["tags"])):
            self.ui.tableWidget.setHorizontalHeaderItem(i+1, QTableWidgetItem(self.settings["tags"][i]))
            self.ui.comboBox_chart_tag.addItem(self.settings["tags"][i])
        
        self.ui.lineEdit_ip.setText(self.settings['default_ip'])
        self.ui.lineEdit_ip.editingFinished.connect(self.ip_change)
        self.ui.label_log_file.setText(self.settings["log_file"])
        self.ui.pushButton_log_file.clicked.connect(self.log_file)

        self.threadpool = QThreadPool().globalInstance()
        print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())

        self.poller_thread()

        self.chart_table()

    def chart_table(self):
        #base = datetime.today()
        #self.x = [base - timedelta(seconds=x) for x in range(20)]  # 100 time points
        self.x=[]
        self.y=[]
        #self.y = [0 for _ in range(20)]  # 100 data points

        date_axis = TimeAxisItem(orientation='bottom')
        self.graph = pg.PlotWidget(axisItems = {'bottom': date_axis})
        #self.graph.plot(x=[x.timestamp() for x in self.x], y=self.y, clear=True)

        layout = QVBoxLayout(self)
        layout.addWidget(self.graph)
        self.ui.frame_chart.setLayout(layout)
        
    def refresh_chart(self):
        pen = pg.mkPen(color=(255, 0, 0))
        self.graph.plot(x=[x.timestamp() for x in self.x], y=self.y, pen=pen, clear=True)

    def log_file(self):
        file_name = QFileDialog.getSaveFileName(self, "Save", "C:/Test Trailer Log.csv", "CSV (Comma delimited) (*.csv)")
        if file_name:
            self.ui.label_log_file.setText(file_name[0])
            self.settings["log_file"] = file_name[0]
            with open(resource_path("config/settings.json"), 'w') as f:
                json.dump(self.settings, f, indent=4)

    def ip_change(self):
        self.settings['default_ip'] = self.ui.lineEdit_ip.text()
        with open(resource_path("config/settings.json"), 'w') as f:
            json.dump(self.settings, f, indent=4)
    
    def thread_stop(self):
        self.run_poll = False

    def poller_thread(self):
        worker = Worker(self.poll_plc)
        self.threadpool.start(worker)

    def poll_plc(self):
        while self.run_poll:
            while self.ui.pushButton_poll.isChecked():
                if int(self.ui.label_countdown.text()) > self.ui.spinBox_poll.value():
                    self.ui.label_countdown.setText(str(self.ui.spinBox_poll.value()))
                if int(self.ui.label_countdown.text()) < 1:
                    self.ui.label_countdown.setText(str(self.ui.spinBox_poll.value()))
                    try:
                        
                        with LogixDriver(self.ui.lineEdit_ip.text()) as plc:
                            curr_datetime = datetime.now()
                            date_stamp = curr_datetime.strftime("%Y-%m-%d %H:%M:%S")
                            
                            
                            row = self.ui.tableWidget.rowCount()
                            if row >= self.ui.spinBox_chart_points.value():
                                self.ui.tableWidget.removeRow(row-1)
                            self.ui.tableWidget.insertRow(0)
                            self.ui.tableWidget.setItem(0, 0, QTableWidgetItem(date_stamp))

                            with open(self.ui.label_log_file.text(), "a", newline="") as outfile:
                                writer = csv.writer(outfile)
                                for i in self.settings["tags"]:
                                    fields = []
                                    fields.append(date_stamp)
                                    val = round(plc.read(i).value, 2)
                                    fields.append(i)
                                    fields.append(val)
                                    writer.writerow(fields)

                                    if i == self.ui.comboBox_chart_tag.currentText():
                                        self.x.append(curr_datetime)
                                        self.y.append(val)
                                        if len(self.x) > self.ui.spinBox_chart_points.value():
                                            self.x.pop(0)
                                        if len(self.y) > self.ui.spinBox_chart_points.value():
                                            self.y.pop(0)
                                        self.refresh_chart()

                                    self.ui.tableWidget.setItem(0, self.settings["tags"].index(i)+1, QTableWidgetItem("%.2f" % val))
                    except Exception as error:
                        self.ui.label_error.setStyleSheet("background-color: rgba(255, 0, 0, 0.5);")
                        self.ui.label_error.setText(str(error))
                        time.sleep(10)
                    else:
                        self.ui.label_error.setStyleSheet("background-color: rgba(35, 199, 35, 1);")
                        self.ui.label_error.setText(" ")
                
                time.sleep(1)
                self.ui.label_countdown.setText(str(int(self.ui.label_countdown.text()) - 1))