コード例 #1
0
ファイル: cluster.py プロジェクト: raj347/cecog
    def _connect(self):

        success = False
        try:
            self._check_host_url()
            client = RemotingService(self._host_url)
            self.dlg = ProgressDialog("Connecting to Cluster...", None, 0, 0,
                                      self)
            func = lambda: client.getService('clustercontrol')
            self.dlg.exec_(func)
            self._service = self.dlg.getTargetResult()
            self._check_api_version()
        except ConnectionError as e:
            msg = ("%s\nDo you want to turn off the cluster support?") % str(e)
            ret = QMessageBox.question(self, "Error", msg)
            if ret == QMessageBox.Yes:
                self._turn_off_cluster_support()
        except Exception as e:
            QMessageBox.critical(self, "Error", str(e))
        else:
            try:
                self.dlg.exec_(self._service.get_cecog_versions)
                cluster_versions = self.dlg.getTargetResult()
            except Exception as e:
                QMessageBox.critical(self, "Error", str(e))
            else:
                if not version in set(cluster_versions):
                    QMessageBox.warning(
                        self, "Warning",
                        ("Cluster does not support %s %s"
                         "Available versions: %s" %
                         (appname, version, ', '.join(cluster_versions))))
                else:
                    success = True
        return success
コード例 #2
0
ファイル: cluster.py プロジェクト: bobi5rova/cecog
    def _connect(self):
        self._check_host_url()

        success = False
        msg = 'Error on connecting to cluster control service on %s' % self._host_url
        try:
            client = RemotingService(self._host_url)
            self.dlg = ProgressDialog("connecting to cluster...", None, 0, 0, self)
            func = lambda: client.getService('clustercontrol')
            self.dlg.exec_(func)
            self._service = self.dlg.getTargetResult()
        except:
            exception(self, msg)
        else:
            try:
                self.dlg.exec_(self._service.get_cecog_versions)
                cluster_versions = self.dlg.getTargetResult()
            except Exception as e:
                exception(self, msg + '(%s)' %str(e))
            else:
                if not version in set(cluster_versions):
                    warning(self, 'Cecog version %s not supported by the cluster' %
                            version, 'Valid versions are: %s' \
                                % ', '.join(cluster_versions))
                else:
                    success = True
        return success
コード例 #3
0
ファイル: cluster.py プロジェクト: waterponey/cecog
    def _on_submit_job(self):

        self._submit_settings.set_section(SECTION_NAME_GENERAL)
        if not self._submit_settings.get2('constrain_positions'):
            positions = []

            for plate_id in self.imagecontainer.plates:
                self.imagecontainer.set_plate(plate_id)
                meta_data = self.imagecontainer.get_meta_data()
                positions += [
                    '%s___%s' % (plate_id, p) for p in meta_data.positions
                ]
            self._submit_settings.set2('positions', ','.join(positions))
            nr_items = len(positions)
        else:
            positions = self._submit_settings.get2('positions')
            nr_items = len(positions.split(','))

        settings_dummy = self._clusterframe.get_special_settings(
            self._settings)

        apc = AppPreferences()
        batch_size = apc.batch_size
        pathout = self._submit_settings.get2('pathout')

        if not self._submit_settings('General', 'skip_finished'):
            self.clear_output_directory(self._settings("General", "pathout"))

        try:
            self.dlg = ProgressDialog("Submitting Jobs...", None, 0, 0, self)
            settings_str = self._submit_settings.to_string()

            func = lambda: self._service.submit_job(
                'cecog_batch', settings_str, pathout, nr_items, batch_size,
                version)
            self.dlg.exec_(func)
            jobid = self.dlg.getTargetResult()
        except Exception as e:
            QMessageBox.critical(self, "Error",
                                 'Job submission failed (%s)' % str(e))
        else:
            # FIXME: no idea how DRMAA 1.0 compatible this is
            if type(jobid) == types.ListType:
                self._jobid = ','.join(jobid)
                main_jobid = jobid[0].split('.')[0]
            else:
                self._jobid = str(jobid)
                main_jobid = jobid
            self._txt_jobid.setText(self._jobid)
            self._update_job_status()
            QMessageBox.information(self, "Information",
                                    ("Job(s) successfully submitted\n"
                                     "Job ID: %s, #jobs: %d" %
                                     (main_jobid, nr_items)))
コード例 #4
0
ファイル: cluster.py プロジェクト: bobi5rova/cecog
 def _on_terminate_job(self):
     try:
         self.dlg = ProgressDialog("terminating jobs...", None, 0, 0, self)
         func = lambda: self._service.control_job(self._jobid, JOB_CONTROL_TERMINATE)
         self.dlg.exec_(func)
     except Exception as e:
         exception(self, 'Error on job termination (%s)' %str(e))
     else:
         self._btn_toogle.setChecked(False)
         self._toggle_state = JOB_CONTROL_SUSPEND
         self._btn_toogle.setText(self._toggle_state)
         self._update_job_status()
コード例 #5
0
ファイル: cluster.py プロジェクト: bobi5rova/cecog
    def _update_job_status(self):

        try:
            self.dlg = ProgressDialog("updating job status...", None, 0, 0, self)

            func = lambda: self._service.get_job_status(self._jobid)
            self.dlg.exec_(func)
            txt = self.dlg.getTargetResult()
        except Exception as e:
            exception(self, 'Error on retrieve job status (%s)' %str(e))
        else:
            self._label_jobstatus.setText(txt)
        return txt
コード例 #6
0
ファイル: cluster.py プロジェクト: bobi5rova/cecog
 def _on_toggle_job(self):
     try:
         self.dlg = ProgressDialog("suspending jobs...", None, 0, 0, self)
         func = lambda: self._service.control_job(self._jobid, self._toggle_state)
         self.dlg.exec_(func)
     except Exception as e:
         self._toggle_state = JOB_CONTROL_SUSPEND
         self._btn_toogle.setChecked(False)
         exception(self, 'Error on toggle job status (%s)' %str(e))
     else:
         if self._toggle_state == JOB_CONTROL_SUSPEND:
             self._toggle_state = JOB_CONTROL_RESUME
         else:
             self._toggle_state = JOB_CONTROL_SUSPEND
         self._update_job_status()
     self._btn_toogle.setText(self._toggle_state)
コード例 #7
0
ファイル: cluster.py プロジェクト: manerotoni/cecog
    def _connect(self):
        self._check_host_url()

        success = False
        msg = 'Error on connecting to cluster control service on %s' % self._host_url
        try:
            client = RemotingService(self._host_url)
            self.dlg = ProgressDialog("connecting to cluster...", None, 0, 0, self)
            func = lambda: client.getService('clustercontrol')
            self.dlg.exec_(func)
            self._service = self.dlg.getTargetResult()
        except:
            exception(self, msg)
        else:
            try:
                self.dlg.exec_(self._service.get_cecog_versions)
                cluster_versions = self.dlg.getTargetResult()
            except Exception as e:
                exception(self, msg + '(%s)' %str(e))
            else:
                if not VERSION in set(cluster_versions):
                    warning(self, 'Cecog version %s not supported by the cluster' %
                            VERSION, 'Valid versions are: %s' \
                                % ', '.join(cluster_versions))
                else:
                    success = True
        return success
コード例 #8
0
ファイル: cluster.py プロジェクト: CellCognition/cecog
    def _connect(self):

        success = False
        try:
            self._check_host_url()
            client = RemotingService(self._host_url)
            self.dlg = ProgressDialog("Connecting to Cluster...", None, 0, 0, self)
            func = lambda: client.getService('clustercontrol')
            self.dlg.exec_(func)
            self._service = self.dlg.getTargetResult()
            self._check_api_version()
        except ConnectionError as e:
            msg = ("%s\nDo you want to turn off the cluster support?") %str(e)
            ret = QMessageBox.question(
                self, "Error", msg)
            if ret == QMessageBox.Yes:
                self._turn_off_cluster_support()
        except Exception as e:
            QMessageBox.critical(self, "Error", str(e))
        else:
            try:
                self.dlg.exec_(self._service.get_cecog_versions)
                cluster_versions = self.dlg.getTargetResult()
            except Exception as e:
                QMessageBox.critical(self, "Error", str(e))
            else:
                if not version in set(cluster_versions):
                    QMessageBox.warning(
                        self, "Warning",
                        ("Cluster does not support %s %s"
                         "Available versions: %s"
                         %(appname, version, ', '.join(cluster_versions))))
                else:
                    success = True
        return success
コード例 #9
0
ファイル: cluster.py プロジェクト: raj347/cecog
    def _update_job_status(self):
        if self.jobIds is None:
            return

        try:
            self.dlg = ProgressDialog("Updating Job Status...", None, 0, 0,
                                      self)

            func = lambda: self._service.get_job_status(self._jobid)
            self.dlg.exec_(func)
            txt = self.dlg.getTargetResult()
        except Exception as e:
            QMessageBox.critical(self, "Error",
                                 'Could not retrieve job status (%s)' % str(e))
        else:
            self._label_jobstatus.setText(txt)
            return txt
コード例 #10
0
ファイル: cluster.py プロジェクト: raj347/cecog
    def _on_terminate_job(self):
        if self.jobIds is None:
            return

        try:
            self.dlg = ProgressDialog("Terminating Jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid,
                                                     JOB_CONTROL_TERMINATE)
            self.dlg.exec_(func)
        except Exception as e:
            QMessageBox.critical(self, "Error",
                                 "Job termination failed (%s)" % str(e))
        else:
            self._btn_toogle.setChecked(False)
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setText(self._toggle_state)
            self._update_job_status()
コード例 #11
0
ファイル: cluster.py プロジェクト: bobi5rova/cecog
    def _on_submit_job(self):
        self._submit_settings.set_section(SECTION_NAME_GENERAL)
        if not self._submit_settings.get2('constrain_positions'):
            positions = []

            for plate_id in self.imagecontainer.plates:
                self.imagecontainer.set_plate(plate_id)
                meta_data = self.imagecontainer.get_meta_data()
                positions += ['%s___%s' % (plate_id, p) for p in meta_data.positions]
            self._submit_settings.set2('positions', ','.join(positions))
            nr_items = len(positions)
        else:
            positions = self._submit_settings.get2('positions')
            nr_items = len(positions.split(','))

        # FIXME: we need to get the current value for 'position_granularity'
        settings_dummy = self._clusterframe.get_special_settings(self._settings)
        position_granularity = settings_dummy.get('Cluster', 'position_granularity')

        path_out = self._submit_settings.get2('pathout')
        emails = str(self._txt_mail.text()).split(',')
        try:
            self.dlg = ProgressDialog("submitting jobs...", None, 0, 0, self)
            settings_str = self._submit_settings.to_string()
            func = lambda: self._service.submit_job('cecog_batch', settings_str,
                                                    path_out, emails, nr_items,
                                                    position_granularity, version)
            self.dlg.exec_(func)
            jobid = self.dlg.getTargetResult()
        except Exception as e:
            exception(self, 'Error on job submission (%s)' %str(e))
        else:
            # FIXME: no idea how DRMAA 1.0 compatible this is
            if type(jobid) == types.ListType:
                self._jobid = ','.join(jobid)
                main_jobid = jobid[0].split('.')[0]
            else:
                self._jobid = str(jobid)
                main_jobid = jobid
            self._txt_jobid.setText(self._jobid)
            self._update_job_status()
            information(self, 'Job submitted successfully',
                        "Job successfully submitted to the cluster.\nJob ID: %s, items: %d" % (main_jobid, nr_items))
コード例 #12
0
ファイル: navigation.py プロジェクト: bobi5rova/cecog
    def _set_plate(self, coordinate_new, set_current=False):
        coordinate_old = self.browser.get_coordinate()
        plate = coordinate_new.plate
        func = lambda: self._imagecontainer.set_plate(plate)
        self.dlg = ProgressDialog("Loading plate...", None, 0, 0, self)
        self.dlg.exec_(func)

        meta_data = self._imagecontainer.get_meta_data()
        if set_current:
            self._set_current_plate(plate)
        self._update_position_table(meta_data)
        self._get_closeby_position(coordinate_old, coordinate_new)
        self._set_current_position(coordinate_new.position)
        if self._imagecontainer.has_timelapse:
            self._update_time_table(meta_data, coordinate_new)
            self._get_closeby_time(coordinate_old, coordinate_new)
            self._set_current_time(coordinate_new.time)
        self._update_info_frame(meta_data)
        self.coordinate_changed.emit(coordinate_new)
コード例 #13
0
ファイル: cluster.py プロジェクト: raj347/cecog
 def _on_toggle_job(self):
     if self.jobIds is None:
         return
     try:
         self.dlg = ProgressDialog("Suspending Jobs...", None, 0, 0, self)
         func = lambda: self._service.control_job(self._jobid, self.
                                                  _toggle_state)
         self.dlg.exec_(func)
     except Exception as e:
         self._toggle_state = JOB_CONTROL_SUSPEND
         self._btn_toogle.setChecked(False)
         QMessageBox.critical(self, "Error",
                              "Could not toggle job status (%s)" % str(e))
     else:
         if self._toggle_state == JOB_CONTROL_SUSPEND:
             self._toggle_state = JOB_CONTROL_RESUME
         else:
             self._toggle_state = JOB_CONTROL_SUSPEND
         self._update_job_status()
     self._btn_toogle.setText(self._toggle_state)
コード例 #14
0
ファイル: cluster.py プロジェクト: manerotoni/cecog
 def _update_job_status(self):
     try:
         self.dlg = ProgressDialog("updating job status...", None, 0, 0, self)
         func = lambda: self._service.get_job_status(self._jobid)
         self.dlg.exec_(func)
         txt = self.dlg.getTargetResult()
     except Exception as e:
         exception(self, 'Error on retrieve job status (%s)' %str(e))
     else:
         self._label_jobstatus.setText(txt)
     return txt
コード例 #15
0
ファイル: cluster.py プロジェクト: manerotoni/cecog
 def _on_terminate_job(self):
     try:
         self.dlg = ProgressDialog("terminating jobs...", None, 0, 0, self)
         func = lambda: self._service.control_job(self._jobid, JOB_CONTROL_TERMINATE)
         self.dlg.exec_(func)
     except Exception as e:
         exception(self, 'Error on job termination (%s)' %str(e))
     else:
         self._btn_toogle.setChecked(False)
         self._toggle_state = JOB_CONTROL_SUSPEND
         self._btn_toogle.setText(self._toggle_state)
         self._update_job_status()
コード例 #16
0
ファイル: cluster.py プロジェクト: CellCognition/cecog
    def _on_submit_job(self):

        self._submit_settings.set_section(SECTION_NAME_GENERAL)
        if not self._submit_settings.get2('constrain_positions'):
            positions = []

            for plate_id in self.imagecontainer.plates:
                self.imagecontainer.set_plate(plate_id)
                meta_data = self.imagecontainer.get_meta_data()
                positions += ['%s___%s' % (plate_id, p) for p in meta_data.positions]
            self._submit_settings.set2('positions', ','.join(positions))
            nr_items = len(positions)
        else:
            positions = self._submit_settings.get2('positions')
            nr_items = len(positions.split(','))

        settings_dummy = self._clusterframe.get_special_settings(self._settings)

        apc = AppPreferences()
        batch_size = apc.batch_size
        pathout = self._submit_settings.get2('pathout')

        if not self._submit_settings('General', 'skip_finished'):
            self.clear_output_directory(self._settings("General", "pathout"))

        try:
            self.dlg = ProgressDialog("Submitting Jobs...", None, 0, 0, self)
            settings_str = self._submit_settings.to_string()

            func = lambda: self._service.submit_job('cecog_batch', settings_str,
                                                    pathout, nr_items,
                                                    batch_size, version)
            self.dlg.exec_(func)
            jobid = self.dlg.getTargetResult()
        except Exception as e:
            QMessageBox.critical(
                self, "Error", 'Job submission failed (%s)' %str(e))
        else:
            # FIXME: no idea how DRMAA 1.0 compatible this is
            if type(jobid) == types.ListType:
                self._jobid = ','.join(jobid)
                main_jobid = jobid[0].split('.')[0]
            else:
                self._jobid = str(jobid)
                main_jobid = jobid
            self._txt_jobid.setText(self._jobid)
            self._update_job_status()
            QMessageBox.information(
                self, "Information", ("Job(s) successfully submitted\n"
                                      "Job ID: %s, #jobs: %d" % (main_jobid, nr_items)))
コード例 #17
0
ファイル: cluster.py プロジェクト: CellCognition/cecog
    def _on_terminate_job(self):
        if self.jobIds is None:
            return

        try:
            self.dlg = ProgressDialog("Terminating Jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid, JOB_CONTROL_TERMINATE)
            self.dlg.exec_(func)
        except Exception as e:
            QMessageBox.critical(
                self, "Error", "Job termination failed (%s)" %str(e))
        else:
            self._btn_toogle.setChecked(False)
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setText(self._toggle_state)
            self._update_job_status()
コード例 #18
0
ファイル: cluster.py プロジェクト: manerotoni/cecog
 def _on_toggle_job(self):
     try:
         self.dlg = ProgressDialog("suspending jobs...", None, 0, 0, self)
         func = lambda: self._service.control_job(self._jobid, self._toggle_state)
         self.dlg.exec_(func)
     except Exception as e:
         self._toggle_state = JOB_CONTROL_SUSPEND
         self._btn_toogle.setChecked(False)
         exception(self, 'Error on toggle job status (%s)' %str(e))
     else:
         if self._toggle_state == JOB_CONTROL_SUSPEND:
             self._toggle_state = JOB_CONTROL_RESUME
         else:
             self._toggle_state = JOB_CONTROL_SUSPEND
         self._update_job_status()
     self._btn_toogle.setText(self._toggle_state)
コード例 #19
0
ファイル: cluster.py プロジェクト: CellCognition/cecog
    def _update_job_status(self):
        if self.jobIds is None:
            return

        try:
            self.dlg = ProgressDialog("Updating Job Status...", None, 0, 0, self)

            func = lambda: self._service.get_job_status(self._jobid)
            self.dlg.exec_(func)
            txt = self.dlg.getTargetResult()
        except Exception as e:
            QMessageBox.critical(
                self, "Error", 'Could not retrieve job status (%s)' %str(e))
        else:
            self._label_jobstatus.setText(txt)
            return txt
コード例 #20
0
ファイル: cluster.py プロジェクト: manerotoni/cecog
    def _on_submit_job(self):
        self._submit_settings.set_section(SECTION_NAME_GENERAL)
        if not self._submit_settings.get2('constrain_positions'):
            positions = []

            for plate_id in self.imagecontainer.plates:
                self.imagecontainer.set_plate(plate_id)
                meta_data = self.imagecontainer.get_meta_data()
                positions += ['%s___%s' % (plate_id, p) for p in meta_data.positions]
            self._submit_settings.set2('positions', ','.join(positions))
            nr_items = len(positions)
        else:
            positions = self._submit_settings.get2('positions')
            nr_items = len(positions.split(','))

        # FIXME: we need to get the current value for 'position_granularity'
        settings_dummy = self._clusterframe.get_special_settings(self._settings)
        position_granularity = settings_dummy.get('Cluster', 'position_granularity')

        path_out = self._submit_settings.get2('pathout')
        emails = str(self._txt_mail.text()).split(',')
        try:
            self.dlg = ProgressDialog("submitting jobs...", None, 0, 0, self)
            settings_str = self._submit_settings.to_string()
            func = lambda: self._service.submit_job('cecog_batch', settings_str,
                                                    path_out, emails, nr_items,
                                                    position_granularity, VERSION)
            self.dlg.exec_(func)
            jobid = self.dlg.getTargetResult()
        except Exception as e:
            exception(self, 'Error on job submission (%s)' %str(e))
        else:
            # FIXME: no idea how DRMAA 1.0 compatible this is
            if type(jobid) == types.ListType:
                self._jobid = ','.join(jobid)
                main_jobid = jobid[0].split('.')[0]
            else:
                self._jobid = str(jobid)
                main_jobid = jobid
            self._txt_jobid.setText(self._jobid)
            self._update_job_status()
            information(self, 'Job submitted successfully',
                        "Job successfully submitted to the cluster.\nJob ID: %s, items: %d" % (main_jobid, nr_items))
コード例 #21
0
ファイル: cluster.py プロジェクト: CellCognition/cecog
 def _on_toggle_job(self):
     if self.jobIds is None:
         return
     try:
         self.dlg = ProgressDialog("Suspending Jobs...", None, 0, 0, self)
         func = lambda: self._service.control_job(self._jobid, self._toggle_state)
         self.dlg.exec_(func)
     except Exception as e:
         self._toggle_state = JOB_CONTROL_SUSPEND
         self._btn_toogle.setChecked(False)
         QMessageBox.critical(
             self, "Error", "Could not toggle job status (%s)" %str(e))
     else:
         if self._toggle_state == JOB_CONTROL_SUSPEND:
             self._toggle_state = JOB_CONTROL_RESUME
         else:
             self._toggle_state = JOB_CONTROL_SUSPEND
         self._update_job_status()
     self._btn_toogle.setText(self._toggle_state)
コード例 #22
0
ファイル: navigation.py プロジェクト: CellCognition/cecog
    def _set_plate(self, coordinate_new, set_current=False):
        coordinate_old = self.browser.get_coordinate()
        plate = coordinate_new.plate
        func = lambda: self._imagecontainer.set_plate(plate)
        self.dlg = ProgressDialog("Loading plate...", None, 0, 0, self)
        self.dlg.exec_(func)

        meta_data = self._imagecontainer.get_meta_data()
        if set_current:
            self._set_current_plate(plate)
        self._update_position_table(meta_data)
        self._get_closeby_position(coordinate_old, coordinate_new)
        self._set_current_position(coordinate_new.position)
        if self._imagecontainer.has_timelapse:
            self._update_time_table(meta_data, coordinate_new)
            self._get_closeby_time(coordinate_old, coordinate_new)
            self._set_current_time(coordinate_new.time)
        self._update_info_frame(meta_data)
        self.coordinate_changed.emit(coordinate_new)
コード例 #23
0
 def _abort_processing(self):
     self.setCursor(Qt.BusyCursor)
     self._is_abort = True
     self.dlg = ProgressDialog('terminating...', None, 0, 0, self)
     self.dlg.exec_(lambda: self._analyzer.abort(wait=True))
     self.setCursor(Qt.ArrowCursor)
コード例 #24
0
class BaseProcessorFrame(BaseFrame):

    def __init__(self, settings, parent, name):
        super(BaseProcessorFrame, self).__init__(settings, parent, name)

        self.idialog = parent.idialog

        self._is_running = False
        self._is_abort = False
        self._has_error = True
        self._current_process = None
        self._image_combo = None
        self._stage_infos = {}
        self._process_items = None

        self._control_buttons = OrderedDict()

        shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self)
        shortcut.activated.connect(self._on_esc_pressed)


    def set_active(self, state):
        # set intern state and enable/disable control buttons
        super(BaseProcessorFrame, self).set_active(state)
        self.process_control.setButtonsEnabled(state)

    def _on_update_image(self, images, message):
        if self.process_control.showImages():
            self.idialog.updateImages(images, message)
            if not self.idialog.isVisible():
                self.idialog.raise_()

    def register_process(self, name):
        pass

    def register_control_button(self, name, cls, labels):

        self._control_buttons[name] = {'labels': labels,
                                       'cls' : cls}

    def _init_control(self, has_images=True):

        if not has_images:
            self.process_control.hideImageCheckBox()

        for name in self._control_buttons:
            slot = lambda x: lambda : self._on_process_start(x)
            self.process_control.addControlButton(name, slot(name))

        if not self.TABS is None:
            self._tab.currentChanged.connect(self._on_tab_changed)
            self._on_tab_changed(0)
        else:
            for name in self._control_buttons:
                self._set_control_button_text(name=name)


    def _set_control_button_text(self, name=None, idx=0):

        if name is None:
            name = self._current_process
        try:
            text = self._control_buttons[name]['labels'][idx] % self._tab_name
        except:
            text = self._control_buttons[name]['labels'][idx]

        self.process_control.buttonByName(name).setText(text)


    def _toggle_control_buttons(self, name=None):
        if name is None:
            name = self._current_process

        for name2 in self._control_buttons:
            if name != name2:
                btn = self.process_control.buttonByName(name)
                btn.setEnabled(not btn.isEnabled())

    @classmethod
    def get_special_settings(cls, settings, has_timelapse=True):
        settings = settings.copy()
        return settings

    def _get_modified_settings(self, name, has_timelapse):
        return self.get_special_settings(self._settings, has_timelapse)

    def _on_tab_changed(self, idx):
        self._tab_name = CHANNEL_PREFIX[idx]
        for name in self._control_buttons:
            self._set_control_button_text(name=name)

    def _clear_image(self):
        """Pop up and clear the image display"""
        self.idialog.clearImage()

    def _on_process_start(self, name, start_again=False):
        if not self._is_running or start_again:
            is_valid = True
            self._is_abort = False
            self._has_error = False

            if self._process_items is None:
                cls = self._control_buttons[name]['cls']
                if type(cls) == types.ListType:
                    self._process_items = cls
                    self._current_process_item = 0
                    cls = cls[self._current_process_item]
                else:
                    self._process_items = None
                    self._current_process_item = 0
            else:
                cls = self._process_items[self._current_process_item]


            if self.name == SECTION_NAME_CLASSIFICATION:

                result_frame = self._get_result_frame(self._tab_name)
                result_frame.load_classifier(check=False)
                learner = result_frame._learner

                if name == self.PICKING:
                    if not result_frame.classifier.is_annotated:
                        is_valid = False
                        result_frame.msg_pick_samples(self)
                    elif result_frame.classifier.is_trained:
                        if not question(self, 'Samples already picked',
                                    'Do you want to pick samples again and '
                                    'overwrite previous '
                                    'results?'):
                            is_valid = False

                elif name == self.TRAINING:
                    if not result_frame.classifier.is_trained:
                        is_valid = False
                        result_frame.msg_train_classifier(self)
                    elif result_frame.classifier.is_valid:
                        if not question(self, 'Classifier already trained',
                                    'Do you want to train the classifier '
                                    'again?'):
                            is_valid = False

                elif name == self.TESTING and not result_frame.classifier.is_valid:
                    is_valid = False
                    result_frame.msg_apply_classifier(self)

            if cls is MultiAnalyzerThread:
                ncpu = cpu_count()
                (ncpu, ok) = QInputDialog.getInt(None, "On your machine are %d processers available." % ncpu, \
                                             "Select the number of processors", \
                                              ncpu, 1, ncpu*2)
                if not ok:
                    self._process_items = None
                    is_valid = False

            if is_valid:
                self._current_process = name

                if not start_again:
                    self.parent().log_window.clear()

                    self._is_running = True
                    self._stage_infos = {}

                    self._toggle_tabs(False)
                    # disable all section button of the main widget
                    self.toggle_tabs.emit(self.get_name())

                    self._set_control_button_text(idx=1)
                    self.process_control.toggleButtons(self._current_process)

                imagecontainer = self.parent().main_window._imagecontainer

                if cls is PickerThread:
                    self._current_settings = self._get_modified_settings(name, imagecontainer.has_timelapse)
                    self._analyzer = cls(self, self._current_settings, imagecontainer)
                    self._clear_image()

                elif cls is AnalyzerThread:
                    self._current_settings = self._get_modified_settings(name, imagecontainer.has_timelapse)
                    self._analyzer = cls(self, self._current_settings, imagecontainer)
                    self._clear_image()

                elif cls is TrainingThread:
                    self._current_settings = self._settings.copy()
                    self._analyzer = cls(self, self._current_settings, result_frame._learner)
                    self._analyzer.setTerminationEnabled(True)

                    self._analyzer.conf_result.connect(result_frame.on_conf_result,
                                                       Qt.QueuedConnection)
                    result_frame.reset()

                elif cls is MultiAnalyzerThread:
                    self._current_settings = self._get_modified_settings(name, imagecontainer.has_timelapse)
                    self._analyzer = cls(self, self._current_settings, imagecontainer, ncpu)


                elif cls is ErrorCorrectionThread:
                    self._current_settings = self._get_modified_settings(name, imagecontainer.has_timelapse)
                    self._analyzer = cls(self, self._current_settings,
                                         self.parent().main_window._imagecontainer)

                self._analyzer.finished.connect(self._on_process_finished)
                self._analyzer.stage_info.connect(self._on_update_stage_info, Qt.QueuedConnection)
                self._analyzer.analyzer_error.connect(self._on_error, Qt.QueuedConnection)
                self._analyzer.image_ready.connect(self._on_update_image)

                self._analyzer.start(QThread.LowestPriority)
                if self._current_process_item == 0:
                    self.status_message.emit('Process started...')

        else:
            self._abort_processing()

    def _toggle_tabs(self, state):
        if not self.TABS is None:
            self._tab.enable_non_active(state)

    def _abort_processing(self):
        self.setCursor(Qt.BusyCursor)
        self._is_abort = True
        self.dlg = ProgressDialog('terminating...', None, 0, 0, self)
        self.dlg.exec_(lambda: self._analyzer.abort(wait=True))
        self.setCursor(Qt.ArrowCursor)

    def _on_error(self, msg, short='An error occurred during processing!'):
        self._has_error = True
        critical(self, short, detail=msg)

    def _on_process_finished(self):
        self._analyzer.image_ready.disconnect(self._on_update_image)

        if (not self._process_items is None and
            self._current_process_item+1 < len(self._process_items) and
            not self._is_abort and
            not self._has_error):
            self._current_process_item += 1
            self._on_process_start(self._current_process, start_again=True)
        else:
            self._is_running = False
            self._set_control_button_text(idx=0)
            self.process_control.toggleButtons(self._current_process)
            self._toggle_tabs(True)
            # enable all section button of the main widget
            self.toggle_tabs.emit(self.get_name())
            if not self._is_abort and not self._has_error:
                if self.name == SECTION_NAME_OBJECTDETECTION:
                    msg = 'Object detection successfully finished.'
                elif self.name == SECTION_NAME_CLASSIFICATION:
                    if self._current_process == self.PICKING:
                        msg = 'Samples successfully picked.\n\n'\
                              'Please train the classifier now based on the '\
                              'newly picked samples.'
                        result_frame = self._get_result_frame(self._tab_name)
                        result_frame.load_classifier(check=False)
                        nr_removed = len(result_frame._learner.nan_features)
                        if nr_removed > 0:
                            msg += '\n\n%d features contained NA values and will be removed from training.' % nr_removed
                    elif self._current_process == self.TRAINING:
                        msg = 'Classifier successfully trained.\n\n'\
                              'You can test the classifier performance here'\
                              'visually or apply the classifier in the '\
                              'processing workflow.'
                    elif self._current_process == self.TESTING:
                        msg = 'Classifier testing successfully finished.'
                elif self.name == SECTION_NAME_TRACKING:
                    msg = 'Tracking successfully finished.'
                elif self.name == SECTION_NAME_EVENT_SELECTION:
                    msg = 'Event selection successfully finished.'
                elif self.name == SECTION_NAME_ERRORCORRECTION:
                    msg = 'Error correction successfully finished.'
                elif self.name == SECTION_NAME_PROCESSING:
                    msg = 'Processing successfully finished.'
                self.status_message.emit(msg)
            else:
                if self._is_abort:
                    self.status_message.emit('Process aborted by user.')
                elif self._has_error:
                    self.status_message.emit('Process aborted by error.')

            self._current_process = None
            self._process_items = None

    def _on_esc_pressed(self):
        if self._is_running:
            self._abort_processing()
            self._analyzer.image_ready.disconnect(self._on_update_image)

    def _on_update_stage_info(self, info):
        sep = '   |   '
        info = dict([(str(k), v) for k,v in info.iteritems()])
        #print info
        if self.CONTROL == CONTROL_1:
            if info['stage'] == 0:
                self.process_control.setRange(info['min'], info['max'])
                if not info['progress'] is None:
                    self.process_control.setProgress(info['progress'])

                    msg = ''
                    if 'meta' in info:
                        msg += '%s' % info['meta']
                    if 'text' in info:
                        msg += '   %s' % info['text']
                    if info['progress'] > info['min'] and 'interval' in info:
                        interval = info['interval']
                        self._intervals.append(interval)
                        avg = numpy.average(self._intervals)
                        estimate = seconds2datetime(avg*float(info['max']-info['progress']))
                        msg += '%s~ %.1fs / %s%s%s remaining' % (sep,
                                                                 avg,
                                                                 info['item_name'],
                                                                 sep,
                                                                 estimate.strftime("%H:%M:%S"))
                    else:
                        self._intervals = []
                    self.status_message.emit(msg)
                else:
                    self.process_control.clearText()
            else:
                self._stage_infos[info['stage']] = info
                if len(self._stage_infos) > 1:
                    total = self._stage_infos[1]['max']*self._stage_infos[2]['max']
                    current = (self._stage_infos[1]['progress']-1)*self._stage_infos[2]['max']+self._stage_infos[2]['progress']
                    self.process_control.setRange(0, total)
                    self.process_control.setProgress(current)
                    sep = '   |   '
                    msg = '%s   %s%s%s' % (self._stage_infos[2]['meta'],
                                           self._stage_infos[1]['text'],
                                           sep,
                                           self._stage_infos[2]['text'])
                    if current > 1 and ('interval' in info.keys()):
                        interval = info['interval']
                        self._intervals.append(interval)
                        estimate = seconds2datetime(
                            numpy.average(self._intervals)*float(total-current))
                        msg += '%s%.1fs / %s%s%s remaining' % (sep,
                                                               interval,
                                                               self._stage_infos[2]['item_name'],
                                                               sep,
                                                               estimate.strftime("%H:%M:%S"))
                    else:
                        self._intervals = []
                    self.status_message.emit(msg)

        elif self.CONTROL == CONTROL_2:

            if info['stage'] == 1:
                if 'progress' in info:
                    self._analyzer_progress1.setRange(info['min'], info['max'])
                    self._analyzer_progress1.setValue(info['progress'])
                    self._analyzer_label1.setText('%s (%d / %d)' % (info['text'],
                                                                    info['progress'],
                                                                    info['max']))
                else:
                    self._analyzer_label1.setText(info['text'])
            else:
                if 'progress' in info:
                    self._analyzer_progress2.setRange(info['min'], info['max'])
                    self._analyzer_progress2.setValue(info['progress'])
                    self._analyzer_label2.setText('%s: %s (%d / %d)' % (info['text'],
                                                                        info['meta'],
                                                                        info['progress'],
                                                                        info['max']))
                else:
                    self._analyzer_label2.setText(info['text'])
コード例 #25
0
ファイル: cluster.py プロジェクト: CellCognition/cecog
class ClusterDisplay(QGroupBox):

    MIN_API_VERSION = 2

    def __init__(self, parent, clusterframe,  settings):
        super(ClusterDisplay, self).__init__(parent)
        self._settings = settings
        self._clusterframe = clusterframe
        self._imagecontainer = None
        self._jobid = None
        self._toggle_state = JOB_CONTROL_SUSPEND
        self._service = None
        self._host_url = None

        self.setTitle('ClusterControl')
        label1 = QLabel('Cluster URL:', self)

        fixed = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        expanding = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        label1.setSizePolicy(fixed)

        self._label_hosturl = QLabel('', self)
        self._label_hosturl.setSizePolicy(expanding)

        label3 = QLabel('Cluster status:', self)
        label3.setSizePolicy(fixed)

        self._label_status = QLabel('', self)
        self._label_status.setSizePolicy(expanding)

        label4 = QLabel('Path mappings:', self)
        label4.setSizePolicy(fixed)

        self._table_info = QTableWidget(self)
        self._table_info.setSelectionMode(QTableWidget.NoSelection)
        labels = ['Local', 'Remote']
        self._table_info.setColumnCount(len(labels))
        self._table_info.setHorizontalHeaderLabels(labels)
        self._table_info.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding|QSizePolicy.Maximum,
                        QSizePolicy.MinimumExpanding))

        layout = QGridLayout(self)
        layout.addWidget(label1, 0, 0, Qt.AlignRight)
        layout.addWidget(self._label_hosturl, 0, 1, 1, 4)
        layout.addWidget(label3, 1, 0, Qt.AlignRight)
        layout.addWidget(self._label_status, 1, 1, 1, 4)
        layout.addWidget(label4, 2, 0, Qt.AlignRight)
        layout.addWidget(self._table_info, 3, 0, 1, 5)

        self._btn_submit = QPushButton('Submit job', self)
        self._btn_submit.setEnabled(False)
        self._btn_submit.clicked.connect(self._on_submit_job)
        layout.addWidget(self._btn_submit, 5, 0, 1, 5)

        line = QFrame(self)
        line.setFrameShape(QFrame.HLine)
        layout.addWidget(line, 6, 0, 1, 5)

        label5 = QLabel('Current job ID:', self)
        layout.addWidget(label5, 7, 0, Qt.AlignRight)
        self._txt_jobid = QLineEdit(self._jobid or '', self)

        regexp = QRegExp('\d+')
        regexp.setPatternSyntax(QRegExp.RegExp2)
        self._txt_jobid.textEdited.connect(self._on_jobid_entered)
        self._txt_jobid.returnPressed.connect(self._on_update_job_status)

        layout.addWidget(self._txt_jobid, 7, 1)
        self._btn_update = QPushButton('Update', self)
        self._btn_update.clicked.connect(self._on_update_job_status)
        layout.addWidget(self._btn_update, 7, 2)
        self._btn_toogle = QPushButton(self._toggle_state, self)
        self._btn_toogle.clicked.connect(self._on_toggle_job)
        self._btn_toogle.setCheckable(True)
        layout.addWidget(self._btn_toogle, 7, 3)
        self._btn_terminate = QPushButton('Terminate', self)
        self._btn_terminate.clicked.connect(self._on_terminate_job)
        layout.addWidget(self._btn_terminate, 7, 4)

        label = QLabel('Job status:', self)
        layout.addWidget(label, 8, 0, Qt.AlignRight)
        self._label_jobstatus = QLabel('', self)
        layout.addWidget(self._label_jobstatus, 8, 1, 1, 4)

        layout.addItem(QSpacerItem(1, 1,
                                   QSizePolicy.MinimumExpanding,
                                   QSizePolicy.Expanding|QSizePolicy.Maximum),
                       10, 0, 1, 5)

    @property
    def jobIds(self):
        jids = self._txt_jobid.text()
        if not jids:
            return None
        else:
            return jids

    @jobIds.setter
    def jobIds(self, jobids):
        self._txt_jobid.setText(jobids)
        self._jobid = str(jobids)

    @property
    def imagecontainer(self):
        if self._imagecontainer is None:
            raise RuntimeError("Image container is not loaded yet")
        return self._imagecontainer

    @imagecontainer.deleter
    def imagecontainer(self):
        del self._imagecontainer

    @imagecontainer.setter
    def imagecontainer(self, imagecontainer):
        self._imagecontainer = imagecontainer

    def _on_jobid_entered(self, txt):
        self._jobid = str(txt)

    def clear_output_directory(self, directory):
        """Remove the content of the output directory except the structure file."""
        for root, dirs, files in os.walk(directory, topdown=False):
            for name in files:
                if not name.endswith(".xml"):
                    try:
                        os.remove(os.path.join(root, name))
                    except OSError:
                        pass
            for name in dirs:
                try:
                    os.rmdir(os.path.join(root, name))
                except OSError:
                    pass

    @pyqtSlot()
    def _on_submit_job(self):

        self._submit_settings.set_section(SECTION_NAME_GENERAL)
        if not self._submit_settings.get2('constrain_positions'):
            positions = []

            for plate_id in self.imagecontainer.plates:
                self.imagecontainer.set_plate(plate_id)
                meta_data = self.imagecontainer.get_meta_data()
                positions += ['%s___%s' % (plate_id, p) for p in meta_data.positions]
            self._submit_settings.set2('positions', ','.join(positions))
            nr_items = len(positions)
        else:
            positions = self._submit_settings.get2('positions')
            nr_items = len(positions.split(','))

        settings_dummy = self._clusterframe.get_special_settings(self._settings)

        apc = AppPreferences()
        batch_size = apc.batch_size
        pathout = self._submit_settings.get2('pathout')

        if not self._submit_settings('General', 'skip_finished'):
            self.clear_output_directory(self._settings("General", "pathout"))

        try:
            self.dlg = ProgressDialog("Submitting Jobs...", None, 0, 0, self)
            settings_str = self._submit_settings.to_string()

            func = lambda: self._service.submit_job('cecog_batch', settings_str,
                                                    pathout, nr_items,
                                                    batch_size, version)
            self.dlg.exec_(func)
            jobid = self.dlg.getTargetResult()
        except Exception as e:
            QMessageBox.critical(
                self, "Error", 'Job submission failed (%s)' %str(e))
        else:
            # FIXME: no idea how DRMAA 1.0 compatible this is
            if type(jobid) == types.ListType:
                self._jobid = ','.join(jobid)
                main_jobid = jobid[0].split('.')[0]
            else:
                self._jobid = str(jobid)
                main_jobid = jobid
            self._txt_jobid.setText(self._jobid)
            self._update_job_status()
            QMessageBox.information(
                self, "Information", ("Job(s) successfully submitted\n"
                                      "Job ID: %s, #jobs: %d" % (main_jobid, nr_items)))

    @pyqtSlot()
    def _on_terminate_job(self):
        if self.jobIds is None:
            return

        try:
            self.dlg = ProgressDialog("Terminating Jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid, JOB_CONTROL_TERMINATE)
            self.dlg.exec_(func)
        except Exception as e:
            QMessageBox.critical(
                self, "Error", "Job termination failed (%s)" %str(e))
        else:
            self._btn_toogle.setChecked(False)
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setText(self._toggle_state)
            self._update_job_status()

    @pyqtSlot()
    def _on_toggle_job(self):
        if self.jobIds is None:
            return
        try:
            self.dlg = ProgressDialog("Suspending Jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid, self._toggle_state)
            self.dlg.exec_(func)
        except Exception as e:
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setChecked(False)
            QMessageBox.critical(
                self, "Error", "Could not toggle job status (%s)" %str(e))
        else:
            if self._toggle_state == JOB_CONTROL_SUSPEND:
                self._toggle_state = JOB_CONTROL_RESUME
            else:
                self._toggle_state = JOB_CONTROL_SUSPEND
            self._update_job_status()
        self._btn_toogle.setText(self._toggle_state)

    @pyqtSlot()
    def _on_update_job_status(self):
        txt = self._update_job_status()
        if txt is not None:
            QMessageBox.information(self, "Cluster update", "Message: '%s'" % txt)

    def _update_job_status(self):
        if self.jobIds is None:
            return

        try:
            self.dlg = ProgressDialog("Updating Job Status...", None, 0, 0, self)

            func = lambda: self._service.get_job_status(self._jobid)
            self.dlg.exec_(func)
            txt = self.dlg.getTargetResult()
        except Exception as e:
            QMessageBox.critical(
                self, "Error", 'Could not retrieve job status (%s)' %str(e))
        else:
            self._label_jobstatus.setText(txt)
            return txt

    def _check_host_url(self):
        url = urlparse.urlparse(self._host_url)
        try:
            test_sock = socket.create_connection((url.hostname, url.port), timeout=1)
            test_sock.shutdown(2)
            test_sock.close()
        except:
            msg = "Connection failed (%s)" %self._host_url
            raise ConnectionError(msg)

    def _check_api_version(self):
        try:
            api_version = self._service.api_version()
        except RemotingError:
            # this call is not supported by older version of the
            # cluster service
            api_version = 1

        if api_version < self.MIN_API_VERSION:
            msg = ("Api version of the cluster services is %d "
                   "but version %d is required.") \
                   %(self.MIN_API_VERSION, api_version)
            raise RuntimeError(msg)

    def _turn_off_cluster_support(self):
        pref = AppPreferences()
        pref.cluster_support = False
        pref.saveSettings()

    def _connect(self):

        success = False
        try:
            self._check_host_url()
            client = RemotingService(self._host_url)
            self.dlg = ProgressDialog("Connecting to Cluster...", None, 0, 0, self)
            func = lambda: client.getService('clustercontrol')
            self.dlg.exec_(func)
            self._service = self.dlg.getTargetResult()
            self._check_api_version()
        except ConnectionError as e:
            msg = ("%s\nDo you want to turn off the cluster support?") %str(e)
            ret = QMessageBox.question(
                self, "Error", msg)
            if ret == QMessageBox.Yes:
                self._turn_off_cluster_support()
        except Exception as e:
            QMessageBox.critical(self, "Error", str(e))
        else:
            try:
                self.dlg.exec_(self._service.get_cecog_versions)
                cluster_versions = self.dlg.getTargetResult()
            except Exception as e:
                QMessageBox.critical(self, "Error", str(e))
            else:
                if not version in set(cluster_versions):
                    QMessageBox.warning(
                        self, "Warning",
                        ("Cluster does not support %s %s"
                         "Available versions: %s"
                         %(appname, version, ', '.join(cluster_versions))))
                else:
                    success = True
        return success

    def _get_mappable_paths(self):
        """Get the paths/filenames which have to be mapped to run on a remote
        cluster. Whether an option is considered or not might depend on other
        values/switches, e.g. if classification is not needed there is no need
        to map the paths.
        """
        # FIXME: should be done in a better way.
        results = []
        targets = [(('General', 'pathin'), []),
                   (('General', 'pathout'),[]),
                   (('General', 'plate_layout'),[]),
                   (('Classification', 'primary_classification_envpath'),
                    [('Processing', 'primary_classification')]),
                   (('Classification', 'secondary_classification_envpath'),
                    [('General', 'process_secondary'),
                     ('Processing', 'secondary_classification')]),
                   (('Classification', 'tertiary_classification_envpath'),
                    [('General', 'process_tertiary'),
                     ('Processing', 'tertiary_classification')]),
                   (('Classification', 'merged_classification_envpath'),
                    [('General', 'process_merged'),
                     ('Processing', 'merged_classification')]),
                   ]
        targets.extend(
            [(('ObjectDetection', '%s_flat_field_correction_image_dir' % prefix),
              [('ObjectDetection', '%s_flat_field_correction' % prefix)])
             for prefix in ['primary', 'secondary', 'tertiary']])

        for info, const in targets:
            passed = reduce(lambda x,y: x and y,
                            map(lambda z: self._settings.get(*z), const),
                            True)
            if passed:
                results.append(info)

        return results

    def check_directories(self):
        """Check local and remote directories defined in the path mapping table
        for existence and setup the table view accordingly.
        """

        ndirs = self._table_info.rowCount()
        remote_dirs = [self._table_info.item(i, 1).text() for i in xrange(ndirs)]
        remote_state = self._service.check_directory(remote_dirs)
        local_dirs = [self._table_info.item(i, 0).text() for i in xrange(ndirs)]
        local_state = [isdir(d) for d in local_dirs]
        states = [local_state, remote_state]

        for i in xrange(ndirs):
            for j in xrange(2):
                item = self._table_info.item(i, j)
                item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
                if states[j][i]:
                    item.setBackground(QBrush(QColor("green")))
                else:
                    if not item.text():
                        item.setText("Not available")
                    item.setBackground(QBrush(QColor("red")))

    def update_display(self, is_active):
        apc = AppPreferences()
        self._host_url = apc.url
        if self._connect():
            can_submit = True

            try:
                self._submit_settings = self._clusterframe.get_special_settings( \
                    self._settings, self.imagecontainer.has_timelapse)
            except:
                self._submit_settings = self._clusterframe.get_special_settings(self._settings)

            self._label_hosturl.setText(self._host_url)
            self._label_status.setText(self._service.get_service_info())

            mappable_paths = self._get_mappable_paths()
            self._table_info.clearContents()
            self._table_info.setRowCount(len(mappable_paths))

            for i, info in enumerate(mappable_paths):
                value = self._settings.get(*info)
                mapped = apc.map2platform(value)
                self._submit_settings.set(info[0], info[1], mapped)
                if mapped is None:
                    can_submit = False
                self._table_info.setItem(i, 0, QTableWidgetItem(value))
                self._table_info.setItem(i, 1, QTableWidgetItem(mapped))

            self.check_directories()
            self._table_info.resizeColumnsToContents()
            self._table_info.resizeRowsToContents()
            self._btn_submit.setEnabled(can_submit and is_active)
        else:
            self._btn_submit.setEnabled(False)
コード例 #26
0
ファイル: cluster.py プロジェクト: raj347/cecog
class ClusterDisplay(QGroupBox):

    MIN_API_VERSION = 2

    def __init__(self, parent, clusterframe, settings):
        super(ClusterDisplay, self).__init__(parent)
        self._settings = settings
        self._clusterframe = clusterframe
        self._imagecontainer = None
        self._jobid = None
        self._toggle_state = JOB_CONTROL_SUSPEND
        self._service = None
        self._host_url = None

        self.setTitle('ClusterControl')
        label1 = QLabel('Cluster URL:', self)

        fixed = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        expanding = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        label1.setSizePolicy(fixed)

        self._label_hosturl = QLabel('', self)
        self._label_hosturl.setSizePolicy(expanding)

        label3 = QLabel('Cluster status:', self)
        label3.setSizePolicy(fixed)

        self._label_status = QLabel('', self)
        self._label_status.setSizePolicy(expanding)

        label4 = QLabel('Path mappings:', self)
        label4.setSizePolicy(fixed)

        self._table_info = QTableWidget(self)
        self._table_info.setSelectionMode(QTableWidget.NoSelection)
        labels = ['Local', 'Remote']
        self._table_info.setColumnCount(len(labels))
        self._table_info.setHorizontalHeaderLabels(labels)
        self._table_info.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding | QSizePolicy.Maximum,
                        QSizePolicy.MinimumExpanding))

        layout = QGridLayout(self)
        layout.addWidget(label1, 0, 0, Qt.AlignRight)
        layout.addWidget(self._label_hosturl, 0, 1, 1, 4)
        layout.addWidget(label3, 1, 0, Qt.AlignRight)
        layout.addWidget(self._label_status, 1, 1, 1, 4)
        layout.addWidget(label4, 2, 0, Qt.AlignRight)
        layout.addWidget(self._table_info, 3, 0, 1, 5)

        self._btn_submit = QPushButton('Submit job', self)
        self._btn_submit.setEnabled(False)
        self._btn_submit.clicked.connect(self._on_submit_job)
        layout.addWidget(self._btn_submit, 5, 0, 1, 5)

        line = QFrame(self)
        line.setFrameShape(QFrame.HLine)
        layout.addWidget(line, 6, 0, 1, 5)

        label5 = QLabel('Current job ID:', self)
        layout.addWidget(label5, 7, 0, Qt.AlignRight)
        self._txt_jobid = QLineEdit(self._jobid or '', self)

        regexp = QRegExp('\d+')
        regexp.setPatternSyntax(QRegExp.RegExp2)
        self._txt_jobid.textEdited.connect(self._on_jobid_entered)
        self._txt_jobid.returnPressed.connect(self._on_update_job_status)

        layout.addWidget(self._txt_jobid, 7, 1)
        self._btn_update = QPushButton('Update', self)
        self._btn_update.clicked.connect(self._on_update_job_status)
        layout.addWidget(self._btn_update, 7, 2)
        self._btn_toogle = QPushButton(self._toggle_state, self)
        self._btn_toogle.clicked.connect(self._on_toggle_job)
        self._btn_toogle.setCheckable(True)
        layout.addWidget(self._btn_toogle, 7, 3)
        self._btn_terminate = QPushButton('Terminate', self)
        self._btn_terminate.clicked.connect(self._on_terminate_job)
        layout.addWidget(self._btn_terminate, 7, 4)

        label = QLabel('Job status:', self)
        layout.addWidget(label, 8, 0, Qt.AlignRight)
        self._label_jobstatus = QLabel('', self)
        layout.addWidget(self._label_jobstatus, 8, 1, 1, 4)

        layout.addItem(
            QSpacerItem(1, 1, QSizePolicy.MinimumExpanding,
                        QSizePolicy.Expanding | QSizePolicy.Maximum), 10, 0, 1,
            5)

    @property
    def jobIds(self):
        jids = self._txt_jobid.text()
        if not jids:
            return None
        else:
            return jids

    @jobIds.setter
    def jobIds(self, jobids):
        self._txt_jobid.setText(jobids)
        self._jobid = str(jobids)

    @property
    def imagecontainer(self):
        if self._imagecontainer is None:
            raise RuntimeError("Image container is not loaded yet")
        return self._imagecontainer

    @imagecontainer.deleter
    def imagecontainer(self):
        del self._imagecontainer

    @imagecontainer.setter
    def imagecontainer(self, imagecontainer):
        self._imagecontainer = imagecontainer

    def _on_jobid_entered(self, txt):
        self._jobid = str(txt)

    @pyqtSlot()
    def _on_submit_job(self):

        self._submit_settings.set_section(SECTION_NAME_GENERAL)
        if not self._submit_settings.get2('constrain_positions'):
            positions = []

            for plate_id in self.imagecontainer.plates:
                self.imagecontainer.set_plate(plate_id)
                meta_data = self.imagecontainer.get_meta_data()
                positions += [
                    '%s___%s' % (plate_id, p) for p in meta_data.positions
                ]
            self._submit_settings.set2('positions', ','.join(positions))
            nr_items = len(positions)
        else:
            positions = self._submit_settings.get2('positions')
            nr_items = len(positions.split(','))

        settings_dummy = self._clusterframe.get_special_settings(
            self._settings)

        apc = AppPreferences()
        batch_size = apc.batch_size
        pathout = self._submit_settings.get2('pathout')

        try:
            self.dlg = ProgressDialog("Submitting Jobs...", None, 0, 0, self)
            settings_str = self._submit_settings.to_string()

            func = lambda: self._service.submit_job(
                'cecog_batch', settings_str, pathout, nr_items, batch_size,
                version)
            self.dlg.exec_(func)
            jobid = self.dlg.getTargetResult()
        except Exception as e:
            QMessageBox.critical(self, "Error",
                                 'Job submission failed (%s)' % str(e))
        else:
            # FIXME: no idea how DRMAA 1.0 compatible this is
            if type(jobid) == types.ListType:
                self._jobid = ','.join(jobid)
                main_jobid = jobid[0].split('.')[0]
            else:
                self._jobid = str(jobid)
                main_jobid = jobid
            self._txt_jobid.setText(self._jobid)
            self._update_job_status()
            QMessageBox.information(self, "Information",
                                    ("Job(s) successfully submitted\n"
                                     "Job ID: %s, #jobs: %d" %
                                     (main_jobid, nr_items)))

    @pyqtSlot()
    def _on_terminate_job(self):
        if self.jobIds is None:
            return

        try:
            self.dlg = ProgressDialog("Terminating Jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid,
                                                     JOB_CONTROL_TERMINATE)
            self.dlg.exec_(func)
        except Exception as e:
            QMessageBox.critical(self, "Error",
                                 "Job termination failed (%s)" % str(e))
        else:
            self._btn_toogle.setChecked(False)
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setText(self._toggle_state)
            self._update_job_status()

    @pyqtSlot()
    def _on_toggle_job(self):
        if self.jobIds is None:
            return
        try:
            self.dlg = ProgressDialog("Suspending Jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid, self.
                                                     _toggle_state)
            self.dlg.exec_(func)
        except Exception as e:
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setChecked(False)
            QMessageBox.critical(self, "Error",
                                 "Could not toggle job status (%s)" % str(e))
        else:
            if self._toggle_state == JOB_CONTROL_SUSPEND:
                self._toggle_state = JOB_CONTROL_RESUME
            else:
                self._toggle_state = JOB_CONTROL_SUSPEND
            self._update_job_status()
        self._btn_toogle.setText(self._toggle_state)

    @pyqtSlot()
    def _on_update_job_status(self):
        txt = self._update_job_status()
        if txt is not None:
            QMessageBox.information(self, "Cluster update",
                                    "Message: '%s'" % txt)

    def _update_job_status(self):
        if self.jobIds is None:
            return

        try:
            self.dlg = ProgressDialog("Updating Job Status...", None, 0, 0,
                                      self)

            func = lambda: self._service.get_job_status(self._jobid)
            self.dlg.exec_(func)
            txt = self.dlg.getTargetResult()
        except Exception as e:
            QMessageBox.critical(self, "Error",
                                 'Could not retrieve job status (%s)' % str(e))
        else:
            self._label_jobstatus.setText(txt)
            return txt

    def _check_host_url(self):
        url = urlparse.urlparse(self._host_url)
        try:
            test_sock = socket.create_connection((url.hostname, url.port),
                                                 timeout=1)
            test_sock.shutdown(2)
            test_sock.close()
        except:
            msg = "Connection failed (%s)" % self._host_url
            raise ConnectionError(msg)

    def _check_api_version(self):
        try:
            api_version = self._service.api_version()
        except RemotingError:
            # this call is not supported by older version of the
            # cluster service
            api_version = 1

        if api_version < self.MIN_API_VERSION:
            msg = ("Api version of the cluster services is %d "
                   "but version %d is required.") \
                   %(self.MIN_API_VERSION, api_version)
            raise RuntimeError(msg)

    def _turn_off_cluster_support(self):
        pref = AppPreferences()
        pref.cluster_support = False
        pref.saveSettings()

    def _connect(self):

        success = False
        try:
            self._check_host_url()
            client = RemotingService(self._host_url)
            self.dlg = ProgressDialog("Connecting to Cluster...", None, 0, 0,
                                      self)
            func = lambda: client.getService('clustercontrol')
            self.dlg.exec_(func)
            self._service = self.dlg.getTargetResult()
            self._check_api_version()
        except ConnectionError as e:
            msg = ("%s\nDo you want to turn off the cluster support?") % str(e)
            ret = QMessageBox.question(self, "Error", msg)
            if ret == QMessageBox.Yes:
                self._turn_off_cluster_support()
        except Exception as e:
            QMessageBox.critical(self, "Error", str(e))
        else:
            try:
                self.dlg.exec_(self._service.get_cecog_versions)
                cluster_versions = self.dlg.getTargetResult()
            except Exception as e:
                QMessageBox.critical(self, "Error", str(e))
            else:
                if not version in set(cluster_versions):
                    QMessageBox.warning(
                        self, "Warning",
                        ("Cluster does not support %s %s"
                         "Available versions: %s" %
                         (appname, version, ', '.join(cluster_versions))))
                else:
                    success = True
        return success

    def _get_mappable_paths(self):
        """Get the paths/filenames which have to be mapped to run on a remote
        cluster. Whether an option is considered or not might depend on other
        values/switches, e.g. if classification is not needed there is no need
        to map the paths.
        """
        #FIXME: should be done in a better way.
        results = []
        targets = [
            (('General', 'pathin'), []),
            (('General', 'pathout'), []),
            (('General', 'structure_file_extra_path_name'),
             [('General', 'structure_file_extra_path')]),
            (('Classification', 'primary_classification_envpath'),
             [('Processing', 'primary_classification')]),
            (('Classification', 'secondary_classification_envpath'),
             [('General', 'process_secondary'),
              ('Processing', 'secondary_classification')]),
            (('Classification', 'tertiary_classification_envpath'),
             [('General', 'process_tertiary'),
              ('Processing', 'tertiary_classification')]),
            (('Classification', 'merged_classification_envpath'),
             [('General', 'process_merged'),
              ('Processing', 'merged_classification')]),
        ]
        targets.extend([(('ObjectDetection',
                          '%s_flat_field_correction_image_dir' % prefix),
                         [('ObjectDetection',
                           '%s_flat_field_correction' % prefix)])
                        for prefix in ['primary', 'secondary', 'tertiary']])
        for info, const in targets:
            passed = reduce(lambda x, y: x and y,
                            map(lambda z: self._settings.get(*z), const), True)
            if passed:
                results.append(info)
        return results

    def check_directories(self):
        """Check local and remote directories defined in the path mapping table
        for existence and setup the table view accordingly.
        """

        ndirs = self._table_info.rowCount()
        remote_dirs = [
            self._table_info.item(i, 1).text() for i in xrange(ndirs)
        ]
        remote_state = self._service.check_directory(remote_dirs)
        local_dirs = [
            self._table_info.item(i, 0).text() for i in xrange(ndirs)
        ]
        local_state = [isdir(d) for d in local_dirs]
        states = [local_state, remote_state]

        for i in xrange(ndirs):
            for j in xrange(2):
                item = self._table_info.item(i, j)
                item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                if states[j][i]:
                    item.setBackground(QBrush(QColor("green")))
                else:
                    if not item.text():
                        item.setText("Not available")
                    item.setBackground(QBrush(QColor("red")))

    def update_display(self, is_active):
        apc = AppPreferences()
        self._host_url = apc.url
        if self._connect():
            can_submit = True

            try:
                self._submit_settings = self._clusterframe.get_special_settings( \
                    self._settings, self.imagecontainer.has_timelapse)
            except:
                self._submit_settings = self._clusterframe.get_special_settings(
                    self._settings)

            self._label_hosturl.setText(self._host_url)
            self._label_status.setText(self._service.get_service_info())

            mappable_paths = self._get_mappable_paths()
            self._table_info.clearContents()
            self._table_info.setRowCount(len(mappable_paths))

            for i, info in enumerate(mappable_paths):
                value = self._settings.get(*info)
                mapped = apc.map2platform(value)
                self._submit_settings.set(info[0], info[1], mapped)
                if mapped is None:
                    can_submit = False
                self._table_info.setItem(i, 0, QTableWidgetItem(value))
                self._table_info.setItem(i, 1, QTableWidgetItem(mapped))

            self.check_directories()
            self._table_info.resizeColumnsToContents()
            self._table_info.resizeRowsToContents()
            self._btn_submit.setEnabled(can_submit and is_active)
        else:
            self._btn_submit.setEnabled(False)
コード例 #27
0
ファイル: main.py プロジェクト: PeterJackNaylor/cecog
class CecogAnalyzer(QtWidgets.QMainWindow):

    NAME_FILTERS = ['Settings files (*.conf)', 'All files (*.*)']
    modified = QtCore.pyqtSignal('bool')

    def __init__(self,
                 appname,
                 version,
                 redirect,
                 settings=None,
                 debug=False,
                 *args,
                 **kw):
        super(CecogAnalyzer, self).__init__(*args, **kw)
        self.setWindowTitle("%s-%s" % (appname, version) + '[*]')
        self.setAcceptDrops(True)
        self.setCentralWidget(QtWidgets.QFrame(self))
        self.setObjectName(appname)

        self.version = version
        self.appname = appname
        self.debug = debug

        self.environ = CecogEnvironment(version=version,
                                        redirect=redirect,
                                        debug=debug)

        if debug:
            self.environ.pprint()

        self._is_initialized = False
        self._imagecontainer = None
        self._meta_data = None
        self._browser = None

        action_quit = self.create_action('&Quit', slot=self.close)
        action_pref = self.create_action('&Preferences',
                                         slot=self.open_preferences)

        action_load = self.create_action('&Load Settings...',
                                         shortcut=QtGui.QKeySequence.Open,
                                         slot=self._on_file_open)
        action_save = self.create_action('&Save Settings',
                                         shortcut=QtGui.QKeySequence.Save,
                                         slot=self._on_file_save)
        self.action_save = action_save
        action_save_as = self.create_action('&Save Settings As...',
                                            shortcut=QtGui.QKeySequence.SaveAs,
                                            slot=self._on_file_save_as)

        menu_file = self.menuBar().addMenu('&File')
        self.add_actions(menu_file,
                         (action_pref, None, action_load, None, action_save,
                          action_save_as, None, action_quit))

        action_log = self.create_action('&Log window',
                                        shortcut=QtGui.QKeySequence(Qt.CTRL +
                                                                    Qt.Key_L),
                                        slot=self._on_show_log_window)

        action_open = self.create_action('&Browser',
                                         shortcut=QtGui.QKeySequence('CTRL+B'),
                                         slot=self._on_browser_open)

        menu_view = self.menuBar().addMenu('&View')
        self.add_actions(menu_view, (action_log, ))
        self.add_actions(menu_view, (action_open, ))

        action_assistant = self.create_action(
            '&Help',
            shortcut=QtGui.QKeySequence.HelpContents,
            slot=self.show_assistant)
        action_about = self.create_action('&About', slot=self.on_about)
        action_aboutQt = self.create_action('&About Qt', slot=self.about_qt)

        menu_help = self.menuBar().addMenu('&Help')
        self.add_actions(menu_help,
                         (action_assistant, action_about, action_aboutQt))

        self.setStatusBar(QtWidgets.QStatusBar(self))

        self._selection = QtWidgets.QListWidget(self.centralWidget())
        self._selection.setViewMode(QtWidgets.QListView.IconMode)
        self._selection.setIconSize(QtCore.QSize(35, 35))
        self._selection.setGridSize(QtCore.QSize(140, 60))
        self._selection.setMovement(QtWidgets.QListView.Static)
        self._selection.setMaximumWidth(self._selection.gridSize().width() + 5)
        self._selection.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._selection.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                  QtWidgets.QSizePolicy.Expanding))

        self._pages = FrameStack(self)

        self._settings_filename = None
        self._settings = GuiConfigSettings(self)

        self._tab_lookup = OrderedDict()
        self._tabs = [
            GeneralFrame(self._settings, self._pages, SECTION_NAME_GENERAL),
            ObjectDetectionFrame(self._settings, self._pages,
                                 SECTION_NAME_OBJECTDETECTION),
            FeatureExtractionFrame(self._settings, self._pages,
                                   SECTION_NAME_FEATURE_EXTRACTION),
            ClassificationFrame(self._settings, self._pages,
                                SECTION_NAME_CLASSIFICATION),
            TrackingFrame(self._settings, self._pages, SECTION_NAME_TRACKING),
            EventSelectionFrame(self._settings, self._pages,
                                SECTION_NAME_EVENT_SELECTION),
            ErrorCorrectionFrame(self._settings, self._pages,
                                 SECTION_NAME_ERRORCORRECTION),
            OutputFrame(self._settings, self._pages, SECTION_NAME_OUTPUT),
            ProcessingFrame(self._settings, self._pages,
                            SECTION_NAME_PROCESSING)
        ]

        # connections for the section frames
        self._tabs[3].connect_browser_btn(self._on_browser_open)
        for frame in self._tabs:
            frame.status_message.connect(self.statusBar().showMessage)

        app = AppPreferences()
        if app.cluster_support:
            clusterframe = ClusterFrame(self._settings, self._pages,
                                        SECTION_NAME_CLUSTER)
            clusterframe.set_imagecontainer(self._imagecontainer)
            self._tabs.append(clusterframe)

        try:
            self.updateStyleSheet(loadStyle(app.stylesheet))
        except Exception as e:
            # proceed with no stylesheet
            traceback.print_exc()

        widths = []
        for tab in self._tabs:
            size = self._add_page(tab)
            widths.append(size.width())
        self.set_modules_active(state=False)
        self._pages.setMinimumWidth(max(widths) + 45)

        self._selection.currentItemChanged.connect(self._on_change_page)
        self._selection.setCurrentRow(0)

        w_logo = QtWidgets.QLabel(self.centralWidget())
        w_logo.setPixmap(QtGui.QPixmap(':cecog_logo_w145'))

        layout = QtWidgets.QGridLayout(self.centralWidget())
        layout.addWidget(self._selection, 0, 0)
        layout.addWidget(w_logo, 1, 0, Qt.AlignBottom | Qt.AlignHCenter)
        layout.addWidget(self._pages, 0, 1, 2, 1)
        layout.setContentsMargins(1, 1, 1, 1)

        self.setGeometry(0, 0, 1250, 800)
        self.setMinimumSize(QtCore.QSize(700, 600))
        self._is_initialized = True

        self._restore_geometry()
        self.show()

        # finally load (demo) - settings
        if settings is None:
            self.load_settings(self.environ.demo_settings)
        elif os.path.isfile(settings):
            self.load_settings(settings)
        else:
            QMessageBox.warning(self, "Warning",
                                "File (%s) does not exist" % settings)

    def dragEnterEvent(self, event):
        event.acceptProposedAction()

    def dragMoveEvent(self, event):
        event.acceptProposedAction()

    def dropEvent(self, event):
        mimeData = event.mimeData()
        if mimeData.hasUrls():
            if len(mimeData.urls()) == 1:
                self.load_settings(fix_path(mimeData.urls()[0].path()))
                # self._on_load_input()
        event.acceptProposedAction()

    def dragLeaveEvent(self, event):
        event.accept()

    def _save_geometry(self):
        settings = QtCore.QSettings(version.organisation, version.appname)
        settings.beginGroup('Gui')
        settings.setValue('state', self.saveState())
        settings.setValue('geometry', self.saveGeometry())

        try:
            settings.setValue(
                'clusterjobs',
                self._pages.widgetByType(ClusterFrame).get_jobids())
        except KeyError:
            pass

        settings.endGroup()

    def _restore_geometry(self):
        settings = QtCore.QSettings(version.organisation, version.appname)
        settings.beginGroup('Gui')

        if settings.contains('geometry'):
            self.restoreGeometry(settings.value('geometry'))

        if settings.contains('state'):
            self.restoreState(settings.value('state'))

        if settings.contains('clusterjobs'):
            jobids = settings.value('clusterjobs')
            if AppPreferences().cluster_support and jobids:
                self._pages.widgetByType(ClusterFrame).restore_jobids(jobids)

        settings.endGroup()

    def closeEvent(self, event):
        self._pages.close()
        # Quit dialog only if not debuging flag is not set
        self._save_geometry()
        if self.debug:
            QtWidgets.QApplication.exit()
        ret = QMessageBox.question(self, "Quit %s" % self.appname,
                                   "Do you really want to quit?",
                                   QMessageBox.Yes | QMessageBox.No)

        if ret == QMessageBox.No:
            event.ignore()
        else:
            self._check_settings_saved(QMessageBox.Yes | QMessageBox.No)
            QtWidgets.QApplication.exit()

    def settings_changed(self, changed):
        if self._is_initialized:
            self.setWindowModified(changed)
            self.action_save.setEnabled(changed)
            self.modified.emit(changed)

    def _add_page(self, widget):
        button = QtWidgets.QListWidgetItem(self._selection)
        button.setIcon(QtGui.QIcon(widget.ICON))
        button.setText(widget.get_name())
        button.setTextAlignment(Qt.AlignHCenter)
        self._pages.addWidget(widget)

        widget.toggle_tabs.connect(self._on_toggle_tabs)
        self._tab_lookup[widget.get_name()] = (button, widget)
        return widget.size()

    def _on_toggle_tabs(self, name):
        """Toggle ItemIsEnabled flag for all list items but name."""
        for name2 in self._tab_lookup:
            if name2 != name:
                item, _ = self._tab_lookup[name2]
                flags = item.flags()
                # check flag (and)
                if flags & Qt.ItemIsEnabled:
                    # remove flag (nand)
                    item.setFlags(flags & ~Qt.ItemIsEnabled)
                else:
                    # set flag (or)
                    item.setFlags(flags | Qt.ItemIsEnabled)

    def _on_change_page(self, current, previous):
        if not current:
            current = previous
        index = self._selection.row(current)
        self._pages.setCurrentIndex(index)
        widget = self._pages.widget(index)
        widget.page_changed()

    def _check_settings_saved(self,
                              buttons=QMessageBox.Yes | QMessageBox.No
                              | QMessageBox.Cancel):
        if self.isWindowModified():
            result = QMessageBox.question(self, "Settings have been modified",
                                          "Do you want to save the settings?",
                                          buttons)

            if result == QMessageBox.Yes:
                self.save_settings()
        else:
            result = QMessageBox.No
        return result

    def add_actions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)

    def create_action(self,
                      text,
                      slot=None,
                      shortcut=None,
                      icon=None,
                      tooltip=None,
                      checkable=None,
                      signal='triggered',
                      checked=False):
        action = QtWidgets.QAction(text, self)
        if icon is not None:
            action.setIcon(QtGui.QIcon(':/%s.png' % icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tooltip is not None:
            action.setToolTip(tooltip)
            action.setStatusTip(tooltip)
        if slot is not None:
            getattr(action, signal).connect(slot)
        if checkable is not None:
            action.setCheckable(True)
        action.setChecked(checked)
        return action

    def save_settings(self, save_as=False):
        filename = self._settings_filename
        if filename is None or save_as:
            filename = self._get_save_as_filename()
        if not filename is None:
            self._write_settings(filename)

    def load_settings(self, filename):
        try:
            self._settings.read(filename)
        except Exception as e:
            QMessageBox.critical(self, "Error",
                                 ("Error loading settings file\n"
                                  "Could not load settings file '%s'.\n%s" %
                                  (filename, str(e))))
            self.statusBar().showMessage('Error loading settings files.')
        else:
            self._settings_filename = filename
            title = self.windowTitle().split(' - ')[0]
            self.setWindowTitle('%s - %s[*]' % (title, filename))
            try:
                # reset naming scheme to load config file completely
                nst = self._settings.get_trait("General", "namingscheme")
                namingscheme_file = self._settings("General", "namingscheme")
                if not namingscheme_file in nst.list_data:
                    self._settings.set("General", "namingscheme",
                                       nst.default_value)
                    QMessageBox.warning(
                        self, "Unkown naming scheme",
                        ("%s-%s can not use the naming scheme '%s'."
                         " Resetting to default '%s'" %
                         (version.appname, version.version, namingscheme_file,
                          nst.default_value)))

                for widget in self._tabs:
                    widget.update_input()
            except Exception as e:
                msg = "Could not load settings file (%s)\n.%s" \
                      %(filename, traceback.format_exc())
                QMessageBox.critical(self, "Error", msg)

            else:
                # set settings to not-changed (assume no changed since loaded from file)
                self.settings_changed(False)
                # notify tabs about new settings loaded
                for tab in self._tabs:
                    tab.settings_loaded()
                self.statusBar().showMessage('Settings successfully loaded.')

    def _write_settings(self, filename):
        try:
            f = file(filename, 'w')
            # create a new version (copy) of the current
            # settings which add the needed rendering information
            pframe = self._tab_lookup[SECTION_NAME_PROCESSING][1]
            settings_dummy = pframe.get_export_settings(self._settings)
            settings_dummy.write(f)
            f.close()
        except Exception as e:
            msg = "Could not save settings\n%s" % str(e)
            QMessageBox.critical(self, "Error", msg)
            self.statusBar().showMessage('Settings not successfully saved.')
        else:
            self._settings_filename = filename
            self.setWindowTitle('%s - %s[*]' % (self.appname, filename))
            self.settings_changed(False)
            self.statusBar().showMessage('Settings successfully saved.')

    def on_about(self):
        dialog = CecogAboutDialog(self)
        dialog.show()

    def about_qt(self):
        QMessageBox.aboutQt(self, "about Qt")

    def open_preferences(self):
        pref = PreferencesDialog(self)
        pref.exec_()

    def updateStyleSheet(self, stylesheet):
        self.setStyleSheet("")
        self.setStyleSheet(stylesheet)

        self._pages.assistant.setStyleSheet("")
        self._pages.assistant.setStyleSheet(stylesheet)

        if self._browser is not None:
            self._browser.setStyleSheet("")
            self._browser.setStyleSheet(stylesheet)

    def _on_browser_open(self):
        if self._imagecontainer is None:
            QMessageBox.warning(
                self, 'Data structure not loaded',
                'The input directory structure file was not loaded.\n'
                'Click "Scan input directory" in section "General" to proceed.'
            )
        elif self._browser is None:
            try:
                browser = Browser(self._settings, self._imagecontainer, None)
                browser.show()
                browser.raise_()
                browser.setFocus()
                app = AppPreferences()
                browser.setStyleSheet(loadStyle(app.stylesheet))
                self._browser = browser
            except Exception as e:
                traceback.print_exc()
                QMessageBox.critical(self, "Error", str(e))
        else:
            self._browser.show()
            self._browser.raise_()

    def _on_load_input(self):
        txt = "Error scanning image structure"
        path_in = self._settings.get(SECTION_NAME_GENERAL, 'pathin')
        if path_in == '':
            QMessageBox.critical(self, "Error",
                                 "%s\nImage path must be defined." % txt)
        elif not os.path.isdir(path_in) and \
             not os.path.isdir(os.path.join(self.environ.package_dir, path_in)):
            QMessageBox.critical(
                self, "Error",
                "%s\nImage path '%s' not found." % (txt, path_in))
        else:
            try:
                infos = list(ImageContainer.iter_check_plates(self._settings))
            except Exception as e:
                QMessageBox.critical(self, "Error", "%s\n%s" % (txt, str(e)))
            else:
                found_any = numpy.any([not info[3] is None for info in infos])
                cancel = False
                if found_any:
                    found_plates = [
                        info[0] for info in infos if not info[3] is None
                    ]
                    missing_plates = [
                        info[0] for info in infos if info[3] is None
                    ]
                    has_missing = len(missing_plates) > 0
                    txt = '%s plates were already scanned.\nDo you want ' \
                          'to rescan the file structure(s)? ' \
                          'This can take several minutes.' % \
                          ('Some' if has_missing else 'All')
                    title = 'Rescan input structure?'

                    box = QMessageBox(QMessageBox.Question, title, title,
                                      QMessageBox.Cancel, self, Qt.Sheet)
                    box.setWindowModality(Qt.WindowModal)
                    box.setInformativeText(txt)
                    box.setDetailedText(
                        'Plates with scanned structure: \n%s\n'
                        '\nPlates without scanned structure: '
                        '\n%s' %
                        ('\n'.join(found_plates), '\n'.join(missing_plates)))
                    if not has_missing:
                        btn1 = QtWidgets.QPushButton('No', box)
                        box.addButton(btn1, QMessageBox.NoRole)
                        box.setDefaultButton(btn1)
                    elif len(found_plates) > 0:
                        btn1 = QtWidgets.QPushButton('Rescan missing', box)
                        box.addButton(btn1, QMessageBox.YesRole)
                        box.setDefaultButton(btn1)
                    else:
                        btn1 = None

                    btn2 = QtWidgets.QPushButton('Rescan all', box)
                    box.addButton(btn2, QMessageBox.YesRole)

                    if box.exec_() == QMessageBox.Cancel:
                        cancel = True
                    else:
                        btn = box.clickedButton()
                        if btn == btn1:
                            if has_missing:
                                scan_plates = dict([(info[0], info[0]
                                                     in missing_plates)
                                                    for info in infos])
                            else:
                                scan_plates = dict(
                                    (info[0], False) for info in infos)
                        else:
                            scan_plates = dict(
                                (info[0], True) for info in infos)
                else:
                    has_multiple = self._settings.get(SECTION_NAME_GENERAL,
                                                      "has_multiple_plates")
                    ret = QMessageBox.question(
                        self, "No structure data found",
                        ("Scanning the input directory can be time "
                         "consuming.\n\nDo you want to proceed?"),
                        QMessageBox.Yes | QMessageBox.No)
                    if ret == QMessageBox.No:
                        cancel = True
                    scan_plates = dict((info[0], True) for info in infos)

                if not cancel:
                    self._load_image_container(infos, scan_plates)

    def _load_image_container(self,
                              plate_infos=None,
                              scan_plates=None,
                              show_dialog=True):
        self._clear_browser()

        if plate_infos is None:
            plate_infos = list(ImageContainer.iter_check_plates(
                self._settings))

        imagecontainer = ImageContainer()
        self._imagecontainer = imagecontainer

        if scan_plates is None:
            scan_plates = dict((info[0], False) for info in plate_infos)

        def load(emitter, icontainer, settings, splates):
            iter_ = icontainer.iter_import_from_settings(settings,
                                                         scan_plates=splates)
            for idx, info in enumerate(iter_):
                emitter.setValue.emit(idx)

            emitter.setLabelText.emit("checking dimensions...")
            emitter.setRange.emit(0, 0)
            QtCore.QCoreApplication.processEvents()

            if len(icontainer.plates) > 0:
                icontainer.set_plate(icontainer.plates[0])
                icontainer.check_dimensions()

        label = ('Please wait until the input structure is scanned\n'
                 'or the structure data loaded...')
        self._dlg = ProgressDialog(label, None, 0, len(scan_plates), self)
        emitter = ProgressObject()
        emitter.setRange.connect(self._dlg.setRange)
        emitter.setValue.connect(self._dlg.setValue)
        emitter.setLabelText.connect(self._dlg.setLabelText)

        try:
            func = lambda: load(emitter, imagecontainer, self._settings,
                                scan_plates)
            self._dlg.exec_(func, (emitter, ))
        except ImportError as e:
            # structure file from versions older than 1.3 contain pdk which is
            # removed
            if 'pdk' in str(e):
                QMessageBox.critical(
                    self, "Error", ("Your structure file format is outdated.\n"
                                    "You have to rescan the plate(s)"))
            else:
                QMessageBox.critical(self, "Error", traceback.format_exc())
            return
        except Exception as e:
            QMessageBox.critical(self, "Error", str(e))

        try:  # I hate lookup tables!
            self._tab_lookup['Cluster'][1].set_imagecontainer(imagecontainer)
        except KeyError:
            pass

        if len(imagecontainer.plates) > 0:
            channels = imagecontainer.channels

            # do not report value changes to the main window
            self._settings.set_notify_change(False)

            self.set_image_crop_size()

            problems = []
            for prefix in ['primary', 'secondary', 'tertiary']:
                trait = self._settings.get_trait(SECTION_NAME_OBJECTDETECTION,
                                                 '%s_channelid' % prefix)
                if trait.set_list_data(channels) is None:
                    problems.append(prefix)
                self._tabs[1].get_widget('%s_channelid' % prefix).update()

            # report problems about a mismatch between channel IDs found in the data
            # and specified by the user
            if len(problems) > 0:
                # a mismatch between settings and data will cause changed settings
                self.settings_changed(True)

            trait = self._settings.get_trait(SECTION_NAME_EVENT_SELECTION,
                                             'duration_unit')

            # allow time-base tracking durations only if time-stamp
            # information is present
            meta_data = imagecontainer.get_meta_data()
            if meta_data.has_timestamp_info:
                result = trait.set_list_data(TimeConverter.units)
            else:
                result = trait.set_list_data([TimeConverter.FRAMES])
            if result is None:
                QMessageBox.critical(
                    self, "Could not set tracking duration units",
                    ("The tracking duration units selected to match the "
                     "load data. Please check your settings."))
                # a mismatch between settings and data will cause changed settings
                self.settings_changed(True)

            # activate change notification again
            self._settings.set_notify_change(True)

            self.set_modules_active(state=True)
            if show_dialog:
                QMessageBox.information(
                    self, "Information", "%d plate(s) successfully loaded." %
                    len(imagecontainer.plates))
        else:
            QMessageBox.critical(
                self, "Error",
                ("No images found\n"
                 "Verifiy your nameing scheme and rescan the data."))

    def set_image_crop_size(self):
        x0, y0, x1, y1 = self._settings.get('General', 'crop_image_x0'), \
                         self._settings.get('General', 'crop_image_y0'), \
                         self._settings.get('General', 'crop_image_x1'), \
                         self._settings.get('General', 'crop_image_y1')

        x0_, y0_, x1_, y1_ = 0, \
                             0, \
                             self._imagecontainer.get_meta_data().dim_x, \
                             self._imagecontainer.get_meta_data().dim_y

        tr_x0 = self._settings.get_trait(SECTION_NAME_GENERAL, 'crop_image_x0')
        tr_y0 = self._settings.get_trait(SECTION_NAME_GENERAL, 'crop_image_y0')
        tr_x1 = self._settings.get_trait(SECTION_NAME_GENERAL, 'crop_image_x1')
        tr_y1 = self._settings.get_trait(SECTION_NAME_GENERAL, 'crop_image_y1')

        # Check if the crop values are valid
        if x0 > 0 and y0 > 0 and x1 <= x1_ and y1 <= y1_ and \
                x0 != x1 and y0 != y1:
            # Set to default values
            tr_x0.set_value(tr_x0.get_widget(), x0)
            tr_y0.set_value(tr_y0.get_widget(), y0)
            tr_x1.set_value(tr_x1.get_widget(), x1)
            tr_y0.set_value(tr_y1.get_widget(), y1)
        else:
            tr_x0.set_value(tr_x0.get_widget(), x0_)
            tr_y0.set_value(tr_y0.get_widget(), y0_)
            tr_x1.set_value(tr_x1.get_widget(), x1_)
            tr_y0.set_value(tr_y1.get_widget(), y1_)

        # Set GUI widget valid ranges
        tr_x0.set_min_value(x0_)
        tr_x0.set_max_value(x1_)
        tr_y0.set_min_value(y0_)
        tr_y0.set_max_value(y1_)
        tr_x1.set_min_value(x0_)
        tr_x1.set_max_value(x1_)
        tr_y1.set_min_value(y0_)
        tr_y1.set_max_value(y1_)

    def set_modules_active(self, state=True):
        for name, (button, widget) in self._tab_lookup.iteritems():
            widget.set_active(state)

    @QtCore.pyqtSlot()
    def _on_file_open(self):

        dir_ = os.path.dirname(self._settings_filename)
        if self._check_settings_saved() != QMessageBox.Cancel:
            if self._settings_filename is not None:
                settings_filename = self.environ.demo_settings
                if os.path.isfile(settings_filename):
                    home = settings_filename
            filename = QtWidgets.QFileDialog.getOpenFileName( \
               self, 'Open config file', dir_, ';;'.join(self.NAME_FILTERS))[0]
            if not bool(filename):
                return

            try:
                self.load_settings(filename)
                if self._settings.was_old_file_format():
                    QMessageBox.information(
                        self, 'Information',
                        'Config file was updated to version %s' % self.version)
            except Exception as e:
                msg = "File could not be loaded\n%s" % str(e)
                QMessageBox.critical(self, "Error", msg)
            finally:
                self._clear_browser()
                self.set_modules_active(state=False)

    def _on_file_save(self):
        self.save_settings(False)

    def _on_file_save_as(self):
        self.save_settings(True)

    def _clear_browser(self):
        if not self._browser is None:
            self._browser.close()
            self._browser = None

    def _on_show_log_window(self):
        self._pages.showLogWindow()

    def _get_save_as_filename(self):
        dir = ""
        if self._settings_filename is not None:
            settings_filename = self.environ.demo_settings
            if os.path.isfile(settings_filename):
                dir = settings_filename
        filename = QtWidgets.QFileDialog.getSaveFileName(
            self, 'Save config file as', dir, ';;'.join(self.NAME_FILTERS))[0]
        return filename or None

    def show_assistant(self):
        self._pages.assistant.show()
        self._pages.assistant.openKeyword('index')
コード例 #28
0
ファイル: __init__.py プロジェクト: waterponey/cecog
class BaseProcessorFrame(BaseFrame):
    def __init__(self, settings, parent, name):
        super(BaseProcessorFrame, self).__init__(settings, parent, name)

        self.idialog = parent.idialog

        self._is_running = False
        self._is_abort = False
        self._has_error = True
        self._current_process = None
        self._image_combo = None
        self._stage_infos = {}
        self._process_items = None

        self._control_buttons = OrderedDict()

        shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self)
        shortcut.activated.connect(self._on_esc_pressed)

    def set_active(self, state):
        # set intern state and enable/disable control buttons
        super(BaseProcessorFrame, self).set_active(state)
        self.process_control.setButtonsEnabled(state)

    def _on_update_image(self, images, message):
        if self.process_control.showImages():
            self.idialog.updateImages(images, message)
            if not self.idialog.isVisible():
                self.idialog.raise_()

    def register_process(self, name):
        pass

    def register_control_button(self, name, cls, labels):

        self._control_buttons[name] = {'labels': labels, 'cls': cls}

    def _init_control(self, has_images=True):

        if not has_images:
            self.process_control.hideImageCheckBox()

        for name in self._control_buttons:
            slot = lambda x: lambda: self._on_process_start(x)
            self.process_control.addControlButton(name, slot(name))

        if not self.TABS is None:
            self._tab.currentChanged.connect(self._on_tab_changed)
            self._on_tab_changed(0)
        else:
            for name in self._control_buttons:
                self._set_control_button_text(name=name)

    def _set_control_button_text(self, name=None, idx=0):

        if name is None:
            name = self._current_process
        try:
            text = self._control_buttons[name]['labels'][idx] % self._tab_name
        except:
            text = self._control_buttons[name]['labels'][idx]

        self.process_control.buttonByName(name).setText(text)

    def _toggle_control_buttons(self, name=None):
        if name is None:
            name = self._current_process

        for name2 in self._control_buttons:
            if name != name2:
                btn = self.process_control.buttonByName(name)
                btn.setEnabled(not btn.isEnabled())

    @classmethod
    def get_special_settings(cls, settings, has_timelapse=True):
        settings = settings.copy()
        return settings

    def _get_modified_settings(self, name, has_timelapse):
        return self.get_special_settings(self._settings, has_timelapse)

    def _on_tab_changed(self, idx):
        self._tab_name = CHANNEL_PREFIX[idx]
        for name in self._control_buttons:
            self._set_control_button_text(name=name)

    def _clear_image(self):
        """Pop up and clear the image display"""
        self.idialog.clearImage()

    def _on_process_start(self, name, start_again=False):
        if not self._is_running or start_again:
            is_valid = True
            self._is_abort = False
            self._has_error = False

            if self._process_items is None:
                cls = self._control_buttons[name]['cls']
                if type(cls) == types.ListType:
                    self._process_items = cls
                    self._current_process_item = 0
                    cls = cls[self._current_process_item]
                else:
                    self._process_items = None
                    self._current_process_item = 0
            else:
                cls = self._process_items[self._current_process_item]

            if self.name == SECTION_NAME_CLASSIFICATION:

                result_frame = self._get_result_frame(self._tab_name)
                result_frame.load_classifier()

                if name == self.Training:
                    is_valid = True
                    if result_frame.classifier_exists():
                        ret = QMessageBox.question(
                            self, 'Trained Classifier found',
                            'Do you want to owerwrite the already '
                            'trained classifier?')
                        if ret == QMessageBox.No:
                            is_valid = False

                elif name == self.Testing and not result_frame.classifier_exists(
                ):
                    is_valid = False
                    QMessageBox.critical(self, "Error",
                                         "Please train the classifier first")

            if is_valid:
                self._current_process = name

                if not start_again:
                    self.parent().log_window.clear()

                    self._is_running = True
                    self._stage_infos = {}

                    self._toggle_tabs(False)
                    # disable all section button of the main widget
                    self.toggle_tabs.emit(self.get_name())

                    self._set_control_button_text(idx=1)
                    self.process_control.toggleButtons(self._current_process)

                imagecontainer = self.parent().main_window._imagecontainer

                if cls is TrainerThread:
                    self._current_settings = self._get_modified_settings(
                        name, imagecontainer.has_timelapse)
                    self._analyzer = cls(self, self._current_settings,
                                         imagecontainer)
                    self._clear_image()

                elif cls is AnalyzerThread:
                    self._current_settings = self._get_modified_settings(
                        name, imagecontainer.has_timelapse)
                    self._analyzer = cls(self, self._current_settings,
                                         imagecontainer)
                    self._clear_image()

                elif cls is MultiAnalyzerThread:
                    self._current_settings = self._get_modified_settings(
                        name, imagecontainer.has_timelapse)
                    self._analyzer = cls(self, self._current_settings,
                                         imagecontainer)

                elif cls is ErrorCorrectionThread:
                    self._current_settings = self._get_modified_settings(
                        name, imagecontainer.has_timelapse)
                    self._analyzer = cls(
                        self, self._current_settings,
                        self.parent().main_window._imagecontainer)

                self._analyzer.finished.connect(self._on_process_finished)
                self._analyzer.status.connect(self._on_update_stage_info,
                                              Qt.QueuedConnection)
                self._analyzer.error.connect(self._on_error,
                                             Qt.QueuedConnection)
                self._analyzer.increment.connect(
                    self.process_control.increment)
                self._analyzer.image_ready.connect(self._on_update_image)

                self._analyzer.start(QThread.LowestPriority)
                if self._current_process_item == 0:
                    self.status_message.emit('Process started...')

        else:
            self._abort_processing()

    def _toggle_tabs(self, state):
        if not self.TABS is None:
            self._tab.enable_non_active(state)

    def _abort_processing(self):
        self.setCursor(Qt.BusyCursor)
        self._is_abort = True
        self.dlg = ProgressDialog('terminating...', None, 0, 0, self)
        self.dlg.exec_(lambda: self._analyzer.abort(wait=True))
        self.setCursor(Qt.ArrowCursor)

    def _on_error(self, msg, short='Error'):
        self._has_error = True
        QMessageBox.critical(self, short, msg)

    def _on_process_finished(self):
        self._analyzer.image_ready.disconnect(self._on_update_image)
        self.process_control.reset()

        if (not self._process_items is None
                and self._current_process_item + 1 < len(self._process_items)
                and not self._is_abort and not self._has_error):
            self._current_process_item += 1
            self._on_process_start(self._current_process, start_again=True)
        else:
            self._is_running = False
            self._set_control_button_text(idx=0)
            self.process_control.toggleButtons(self._current_process)
            self._toggle_tabs(True)
            # enable all section button of the main widget
            self.toggle_tabs.emit(self.get_name())
            msg = 'Processing successfully finished'

            if not self._is_abort and not self._has_error:
                if self.name == SECTION_NAME_OBJECTDETECTION:
                    msg = 'Object detection successfully finished.'
                elif self.name == SECTION_NAME_TRACKING:
                    msg = 'Tracking successfully finished.'
                elif self.name == SECTION_NAME_EVENT_SELECTION:
                    msg = 'Event selection successfully finished.'
                elif self.name == SECTION_NAME_PROCESSING:
                    msg = 'Processing successfully finished.'
                self.status_message.emit(msg)
                QMessageBox.information(self, "Finished", msg)
            else:
                if self._is_abort:
                    self.status_message.emit('Process aborted by user.')
                elif self._has_error:
                    self.status_message.emit('Process aborted by error.')

            self._current_process = None
            self._process_items = None

    def _on_esc_pressed(self):

        if self._is_running:
            self._abort_processing()
            self._analyzer.image_ready.disconnect(self._on_update_image)

    def _on_update_stage_info(self, info):

        sep = ' | '
        info = dict([(str(k), v) for k, v in info.iteritems()])

        self.process_control.setRange(info['min'], info['max'])

        if info['progress'] is not None:
            self.process_control.setProgress(info['progress'])

        msg = ''
        if 'meta' in info:
            msg += '%s' % info['meta']
        if 'text' in info:
            msg += '   %s' % info['text']

        if info['interval'] is not None:
            prg = self.process_control.progress()
            max_ = self.process_control.maximum()
            self._intervals.append(info["interval"])
            avg = numpy.average(self._intervals)
            estimate = seconds2datetime(avg * float(max_ - prg))
            msg += '%s~ %.1fs %s%s remaining' \
                   % (sep, avg, sep,
                      estimate.strftime("%H:%M:%S"))
        else:
            self._intervals = []
        self.status_message.emit(msg)
コード例 #29
0
ファイル: __init__.py プロジェクト: CellCognition/cecog
class BaseProcessorFrame(BaseFrame):

    def __init__(self, settings, parent, name):
        super(BaseProcessorFrame, self).__init__(settings, parent, name)

        self.idialog = parent.idialog

        self._is_running = False
        self._is_abort = False
        self._has_error = True
        self._current_process = None
        self._image_combo = None
        self._stage_infos = {}
        self._process_items = None

        self._control_buttons = OrderedDict()

        shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self)
        shortcut.activated.connect(self._on_esc_pressed)


    def set_active(self, state):
        # set intern state and enable/disable control buttons
        super(BaseProcessorFrame, self).set_active(state)
        self.process_control.setButtonsEnabled(state)

    def _on_update_image(self, images, message):
        if self.process_control.showImages():
            self.idialog.updateImages(images, message)
            if not self.idialog.isVisible():
                self.idialog.raise_()

    def register_process(self, name):
        pass

    def register_control_button(self, name, cls, labels):

        self._control_buttons[name] = {'labels': labels,
                                       'cls' : cls}

    def _init_control(self, has_images=True):

        if not has_images:
            self.process_control.hideImageCheckBox()

        for name in self._control_buttons:
            slot = lambda x: lambda : self._on_process_start(x)
            self.process_control.addControlButton(name, slot(name))

        if not self.TABS is None:
            self._tab.currentChanged.connect(self._on_tab_changed)
            self._on_tab_changed(0)
        else:
            for name in self._control_buttons:
                self._set_control_button_text(name=name)


    def _set_control_button_text(self, name=None, idx=0):

        if name is None:
            name = self._current_process
        try:
            text = self._control_buttons[name]['labels'][idx] % self._tab_name
        except:
            text = self._control_buttons[name]['labels'][idx]

        self.process_control.buttonByName(name).setText(text)


    def _toggle_control_buttons(self, name=None):
        if name is None:
            name = self._current_process

        for name2 in self._control_buttons:
            if name != name2:
                btn = self.process_control.buttonByName(name)
                btn.setEnabled(not btn.isEnabled())

    @classmethod
    def get_special_settings(cls, settings, has_timelapse=True):
        settings = settings.copy()
        return settings

    def _get_modified_settings(self, name, has_timelapse):
        return self.get_special_settings(self._settings, has_timelapse)

    def _on_tab_changed(self, idx):
        self._tab_name = CHANNEL_PREFIX[idx]
        for name in self._control_buttons:
            self._set_control_button_text(name=name)

    def _clear_image(self):
        """Pop up and clear the image display"""
        self.idialog.clearImage()

    def _on_process_start(self, name, start_again=False):
        if not self._is_running or start_again:
            is_valid = True
            self._is_abort = False
            self._has_error = False

            if self._process_items is None:
                cls = self._control_buttons[name]['cls']
                if type(cls) == types.ListType:
                    self._process_items = cls
                    self._current_process_item = 0
                    cls = cls[self._current_process_item]
                else:
                    self._process_items = None
                    self._current_process_item = 0
            else:
                cls = self._process_items[self._current_process_item]


            if self.name == SECTION_NAME_CLASSIFICATION:

                result_frame = self._get_result_frame(self._tab_name)
                result_frame.load_classifier()

                if name == self.Training:
                    is_valid = True
                    if result_frame.classifier_exists():
                        ret = QMessageBox.question(self,'Trained Classifier found',
                                                   'Do you want to owerwrite the already '
                                                   'trained classifier?')
                        if ret == QMessageBox.No:
                            is_valid = False

                elif name == self.Testing and not result_frame.classifier_exists():
                    is_valid = False
                    QMessageBox.critical(self, "Error", "Please train the classifier first")

            if is_valid:
                self._current_process = name

                if not start_again:
                    self.parent().log_window.clear()

                    self._is_running = True
                    self._stage_infos = {}

                    self._toggle_tabs(False)
                    # disable all section button of the main widget
                    self.toggle_tabs.emit(self.get_name())

                    self._set_control_button_text(idx=1)
                    self.process_control.toggleButtons(self._current_process)

                imagecontainer = self.parent().main_window._imagecontainer

                if cls is TrainerThread:
                    self._current_settings = self._get_modified_settings(
                        name, imagecontainer.has_timelapse)
                    self._analyzer = cls(
                        self, self._current_settings, imagecontainer)
                    self._clear_image()

                elif cls is AnalyzerThread:
                    self._current_settings = self._get_modified_settings(
                        name, imagecontainer.has_timelapse)
                    self._analyzer = cls(
                        self, self._current_settings, imagecontainer)
                    self._clear_image()

                elif cls is MultiAnalyzerThread:
                    self._current_settings = self._get_modified_settings(
                        name, imagecontainer.has_timelapse)
                    self._analyzer = cls(
                        self, self._current_settings, imagecontainer)


                elif cls is ErrorCorrectionThread:
                    self._current_settings = self._get_modified_settings(
                        name, imagecontainer.has_timelapse)
                    self._analyzer = cls(
                        self, self._current_settings,
                        self.parent().main_window._imagecontainer)

                self._analyzer.finished.connect(self._on_process_finished)
                self._analyzer.status.connect(
                    self._on_update_stage_info, Qt.QueuedConnection)
                self._analyzer.error.connect(self._on_error, Qt.QueuedConnection)
                self._analyzer.increment.connect(self.process_control.increment)
                self._analyzer.image_ready.connect(self._on_update_image)

                self._analyzer.start(QThread.LowestPriority)
                if self._current_process_item == 0:
                    self.status_message.emit('Process started...')

        else:
            self._abort_processing()

    def _toggle_tabs(self, state):
        if not self.TABS is None:
            self._tab.enable_non_active(state)

    def _abort_processing(self):
        self.setCursor(Qt.BusyCursor)
        self._is_abort = True
        self.dlg = ProgressDialog('terminating...', None, 0, 0, self)
        self.dlg.exec_(lambda: self._analyzer.abort(wait=True))
        self.setCursor(Qt.ArrowCursor)

    def _on_error(self, msg, short='Error'):
        self._has_error = True
        QMessageBox.critical(self, short, msg)

    def _on_process_finished(self):
        self._analyzer.image_ready.disconnect(self._on_update_image)
        self.process_control.reset()

        if (not self._process_items is None and
            self._current_process_item+1 < len(self._process_items) and
            not self._is_abort and
            not self._has_error):
            self._current_process_item += 1
            self._on_process_start(self._current_process, start_again=True)
        else:
            self._is_running = False
            self._set_control_button_text(idx=0)
            self.process_control.toggleButtons(self._current_process)
            self._toggle_tabs(True)
            # enable all section button of the main widget
            self.toggle_tabs.emit(self.get_name())
            msg = 'Processing successfully finished'

            if not self._is_abort and not self._has_error:
                if self.name == SECTION_NAME_OBJECTDETECTION:
                    msg = 'Object detection successfully finished.'
                elif self.name == SECTION_NAME_TRACKING:
                    msg = 'Tracking successfully finished.'
                elif self.name == SECTION_NAME_EVENT_SELECTION:
                    msg = 'Event selection successfully finished.'
                elif self.name == SECTION_NAME_PROCESSING:
                    msg = 'Processing successfully finished.'
                self.status_message.emit(msg)
                QMessageBox.information(self, "Finished", msg)
            else:
                if self._is_abort:
                    self.status_message.emit('Process aborted by user.')
                elif self._has_error:
                    self.status_message.emit('Process aborted by error.')

            self._current_process = None
            self._process_items = None

    def _on_esc_pressed(self):

        if self._is_running:
            self._abort_processing()
            self._analyzer.image_ready.disconnect(self._on_update_image)

    def _on_update_stage_info(self, info):

        sep = ' | '
        info = dict([(str(k), v) for k, v in info.iteritems()])

        self.process_control.setRange(info['min'], info['max'])

        if info['progress'] is not None:
            self.process_control.setProgress(info['progress'])

        msg = ''
        if 'meta' in info:
            msg += '%s' % info['meta']
        if 'text' in info:
            msg += '   %s' % info['text']

        if info['interval'] is not None:
            prg = self.process_control.progress()
            max_ = self.process_control.maximum()
            self._intervals.append(info["interval"])
            avg = numpy.average(self._intervals)
            estimate = seconds2datetime(avg*float(max_-prg))
            msg += '%s~ %.1fs %s%s remaining' \
                   % (sep, avg, sep,
                      estimate.strftime("%H:%M:%S"))
        else:
            self._intervals = []
        self.status_message.emit(msg)
コード例 #30
0
ファイル: navigation.py プロジェクト: bobi5rova/cecog
class NavigationModule(Module):

    NAME = 'Navigation'

    coordinate_changed = pyqtSignal(Coordinate)

    def __init__(self, parent, browser, imagecontainer):
        Module.__init__(self, parent, browser)

        self._imagecontainer = imagecontainer

        frame_info = QGroupBox('Plate Information', self)
        layout = QGridLayout(frame_info)
        frame_info.setStyleSheet('QLabel { font-size: 10px }')
        self._label_info = QLabel(frame_info)
        layout.addWidget(self._label_info, 0, 0, 0, 0,
                         Qt.AlignCenter | Qt.AlignHCenter)

        splitter = QSplitter(Qt.Vertical, self)
        splitter.setMinimumWidth(40)

        layout = QBoxLayout(QBoxLayout.TopToBottom, self)
        layout.setContentsMargins(5, 5, 5, 5)
        layout.addWidget(splitter)

        grp1 = QGroupBox('Plates', splitter)
        grp2 = QGroupBox('Positions', splitter)
        splitter.addWidget(grp1)
        splitter.addWidget(grp2)

        layout = QGridLayout(grp1)
        layout.setContentsMargins(5, 10, 5, 5)

        table = QTableWidget(grp1)
        table.setEditTriggers(QTableWidget.NoEditTriggers)
        table.setSelectionMode(QTableWidget.SingleSelection)
        table.setSelectionBehavior(QTableWidget.SelectRows)
        table.setAlternatingRowColors(True)
        table.setStyleSheet('font-size: 10px;')
        table.currentItemChanged.connect(self._on_plate_changed)
        self._table_plate = table
        layout.addWidget(table, 0, 0)

        self._update_plate_table()

        layout = QGridLayout(grp2)
        layout.setContentsMargins(5, 10, 5, 5)

        table = QTableWidget(grp2)
        table.setEditTriggers(QTableWidget.NoEditTriggers)
        table.setSelectionMode(QTableWidget.SingleSelection)
        table.setSelectionBehavior(QTableWidget.SelectRows)
        table.setAlternatingRowColors(True)
        table.setStyleSheet('font-size: 10px;')
        table.currentItemChanged.connect(self._on_position_changed)
        self._table_position = table
        layout.addWidget(table, 0, 0)

        if self._imagecontainer.has_timelapse:
            grp3 = QGroupBox('Time', splitter)
            splitter.addWidget(grp3)

            layout = QGridLayout(grp3)
            layout.setContentsMargins(5, 10, 5, 5)

            table = QTableWidget(grp3)
            table.setEditTriggers(QTableWidget.NoEditTriggers)
            table.setSelectionMode(QTableWidget.SingleSelection)
            table.setSelectionBehavior(QTableWidget.SelectRows)
            table.setAlternatingRowColors(True)
            table.setStyleSheet('font-size: 10px;')
            table.currentItemChanged.connect(self._on_time_changed)
            self._table_time = table
            layout.addWidget(table, 0, 0)

        splitter.addWidget(frame_info)

    def _update_plate_table(self):
        table = self._table_plate
        table.blockSignals(True)
        table.clearContents()

        column_names = ['Plate ID']
        table.setColumnCount(len(column_names))
        table.setHorizontalHeaderLabels(column_names)
        plates = self._imagecontainer.plates
        table.setRowCount(len(plates))

        for idx, plate in enumerate(plates):
            item = QTableWidgetItem(plate)
            item.setData(0, plate)
            table.setItem(idx, 0, item)

        table.resizeColumnsToContents()
        table.resizeRowsToContents()
        table.blockSignals(False)

    def _update_time_table(self, meta_data, coordinate):
        coordinate = coordinate.copy()
        table = self._table_time
        table.blockSignals(True)
        table.clearContents()

        column_names = ['Frame']

        if meta_data.has_timestamp_info:
            column_names += ['rel. t (min)', 'abs. t (GMT)']
        table.setColumnCount(len(column_names))
        table.setHorizontalHeaderLabels(column_names)
        table.setRowCount(len(meta_data.times))

        for idx, time in enumerate(meta_data.times):
            item = QTableWidgetItem(str(time))
            item.setData(0, time)
            table.setItem(idx, 0, item)

            if meta_data.has_timestamp_info:
                coordinate.time = time
                ts_rel = meta_data.get_timestamp_relative(coordinate)
                ts_abs = meta_data.get_timestamp_absolute(coordinate)
                if not numpy.isnan(ts_rel):
                    info = '%.1f' % (ts_rel / 60)
                    table.setItem(idx, 1, QTableWidgetItem(info))
                if not numpy.isnan(ts_abs):
                    info = time_lib.strftime("%Y-%m-%d %H:%M:%S",
                                             time_lib.gmtime(ts_abs))
                    table.setItem(idx, 2, QTableWidgetItem(info))
        table.resizeColumnsToContents()
        table.resizeRowsToContents()
        table.blockSignals(False)

    def _update_position_table(self, meta_data):
        table = self._table_position
        table.blockSignals(True)
        table.clearContents()

        column_names = ['Position']
        if meta_data.has_well_info:
            column_names += ['Well', 'Subwell']
        if meta_data.has_condition_info:
            column_names.append('Condition')
        if meta_data.has_timestamp_info and meta_data.has_timelapse:
            column_names.append('Time-lapse')

        table.setColumnCount(len(column_names))
        table.setHorizontalHeaderLabels(column_names)
        table.setRowCount(len(meta_data.positions))

        for idx, pos in enumerate(meta_data.positions):
            item = QTableWidgetItem(pos)
            item.setData(0, pos)
            table.setItem(idx, 0, item)

            if 'Time-lapse' in column_names:
                column = column_names.index('Time-lapse')
                info = meta_data.get_timestamp_info(pos)
                info_str = '%.1fmin (%.1fs)' % (info[0] / 60, info[1])
                table.setItem(idx, column, QTableWidgetItem(info_str))

            if 'Well' in column_names:
                column = column_names.index('Well')
                well, subwell = meta_data.get_well_and_subwell(pos)
                if not well is None:
                    table.setItem(idx, column, QTableWidgetItem(well))
                if not subwell is None:
                    table.setItem(idx, column + 1,
                                  QTableWidgetItem("%02d" % subwell))

        table.resizeColumnsToContents()
        table.resizeRowsToContents()
        table.blockSignals(False)

    def _update_info_frame(self, meta):

        txt  = '<table>' \
               '<tr><td align="right">Positions: </td><td>%s</td></tr>' \
               '<tr><td align="right">Frames: </td><td>%d</td></tr>' % \
               (meta.dim_p, meta.dim_t)
        txt += '<tr><td align="right">Channels: </td><td>%d (%s)</td></tr>' \
               '<tr><td align="right">Z-slices: </td><td>%d</td></tr>' \
               '<tr><td align="right">Width / Height: </td><td>%d x %d</td></tr>' \
               '<tr><td colspan="2"></td></tr>' \
               '<tr><td align="right">Image Files: </td><td>%d</td></tr>' % \
               (meta.dim_c, meta.pixel_type, meta.dim_z, meta.dim_x,
                meta.dim_y, meta.image_files)
        txt += '<tr><td></td></tr>'
        if meta.has_timestamp_info and meta.has_timelapse:
            info = meta.plate_timestamp_info
            txt += \
               '<tr><td align="right">Time-lapse info: </td><td>%.1f min (+/- %.1f s)</td></tr>' % \
               (info[0]/60, info[1])
        else:
            txt += '<tr><td align="right">Time-lapse info: </td><td>no</td></tr>'

        txt += '<tr><td align="right">Well info: </td><td>%s</td></tr>' % \
               yesno(meta.has_well_info)
        txt += '<tr><td align="right">Condition info: </td><td>%s</td></tr>' % \
               yesno(meta.has_condition_info)
        txt += '</table>'
        self._label_info.setText(txt)

    def initialize(self):
        self.coordinate_changed.connect(self.browser.on_coordinate_changed)
        coordinate = self.browser.get_coordinate()

        meta_data = self._imagecontainer.get_meta_data()
        self._update_position_table(meta_data)
        self._update_info_frame(meta_data)

        self._set_current_plate(coordinate.plate)
        self._set_current_position(coordinate.position)

        if self._imagecontainer.has_timelapse:
            self._update_time_table(meta_data, coordinate)
            self._set_current_time(coordinate.time)

    def nav_to_coordinate(self, coordinate):
        """
        Set the browser coordinate to a coordinate and emit signal
        """
        self._imagecontainer.set_plate(coordinate.plate)
        meta_data = self._imagecontainer.get_meta_data()
        self._set_current_plate(coordinate.plate)
        self._update_position_table(meta_data)
        self._set_current_position(coordinate.position)
        if self._imagecontainer.has_timelapse:
            self._update_time_table(meta_data, coordinate)
            self._set_current_time(coordinate.time)
        self._update_info_frame(meta_data)
        self.coordinate_changed.emit(coordinate)

    def nav_to_time(self, time):
        """
        Set the browser coordinate to a coordinate and emit signal
        """
        coordinate = self.browser.get_coordinate()
        coordinate.time = time
        self._set_time(coordinate, True)

    def nav_to_prev_position(self):
        coordinate = self.browser.get_coordinate()
        meta_data = self._imagecontainer.get_meta_data()
        pos = meta_data.positions
        idx = pos.index(coordinate.position)
        if idx > 0:
            coordinate.position = pos[idx - 1]
            self._set_position(coordinate, True)

    def nav_to_next_position(self):
        coordinate = self.browser.get_coordinate()
        meta_data = self._imagecontainer.get_meta_data()
        pos = meta_data.positions
        idx = pos.index(coordinate.position)
        if idx < len(pos) - 1:
            coordinate.position = pos[idx + 1]
            self._set_position(coordinate, True)

    def nav_to_prev_plate(self):
        coordinate = self.browser.get_coordinate()
        plates = self._imagecontainer.plates
        idx = plates.index(coordinate.plate)
        if idx > 0:
            coordinate.plate = plates[idx - 1]
            self._set_plate(coordinate, True)

    def nav_to_next_plate(self):
        coordinate = self.browser.get_coordinate()
        plates = self._imagecontainer.plates
        idx = plates.index(coordinate.plate)
        if idx < len(plates) - 1:
            coordinate.plate = plates[idx + 1]
            self._set_plate(coordinate, True)

    def _get_closeby_position(self, coordinate_old, coordinate_new):
        md_new = self._imagecontainer.get_meta_data()
        if coordinate_old.position in md_new.positions:
            coordinate_new.position = coordinate_old.position
        else:
            coordinate_new.position = md_new.positions[0]

    def _get_closeby_time(self, coordinate_old, coordinate_new):
        md_new = self._imagecontainer.get_meta_data()
        if coordinate_old.time in md_new.times:
            coordinate_new.time = coordinate_old.time
        else:
            coordinate_new.time = md_new.times[0]

    def _on_plate_changed(self, current, previous):
        coordinate_new = self.browser.get_coordinate()
        item = self._table_plate.item(current.row(), 0)
        plate = item.data(0)
        coordinate_new.plate = plate
        self._set_plate(coordinate_new)

    def _set_plate(self, coordinate_new, set_current=False):
        coordinate_old = self.browser.get_coordinate()
        plate = coordinate_new.plate
        func = lambda: self._imagecontainer.set_plate(plate)
        self.dlg = ProgressDialog("Loading plate...", None, 0, 0, self)
        self.dlg.exec_(func)

        meta_data = self._imagecontainer.get_meta_data()
        if set_current:
            self._set_current_plate(plate)
        self._update_position_table(meta_data)
        self._get_closeby_position(coordinate_old, coordinate_new)
        self._set_current_position(coordinate_new.position)
        if self._imagecontainer.has_timelapse:
            self._update_time_table(meta_data, coordinate_new)
            self._get_closeby_time(coordinate_old, coordinate_new)
            self._set_current_time(coordinate_new.time)
        self._update_info_frame(meta_data)
        self.coordinate_changed.emit(coordinate_new)

    def _on_position_changed(self, current, previous):
        coordinate = self.browser.get_coordinate()
        item = self._table_position.item(current.row(), 0)
        position = item.data(0)
        coordinate.position = position
        self._set_position(coordinate)

    def _set_position(self, coordinate, set_current=False):
        if set_current:
            self._set_current_position(coordinate.position)
        if self._imagecontainer.has_timelapse:
            meta_data = self._imagecontainer.get_meta_data()
            self._update_time_table(meta_data, coordinate)
            self._set_current_time(coordinate.time)
        self.coordinate_changed.emit(coordinate)

    def _on_time_changed(self, current, previous):
        coordinate = self.browser.get_coordinate()
        item = self._table_time.item(current.row(), 0)
        time = int(item.data(0))
        coordinate.time = time
        self._set_time(coordinate)

    def _set_time(self, coordinate, set_current=False):
        if set_current:
            self._set_current_time(coordinate.time)
        self.coordinate_changed.emit(coordinate)

    def _set_current_plate(self, plate):
        self._table_plate.blockSignals(True)
        item = self._table_plate.findItems(plate, Qt.MatchExactly)[0]
        self._table_plate.setCurrentItem(item)
        self._table_plate.blockSignals(False)
        self._table_plate.scrollToItem(item)
        self._table_plate.update()

    def _set_current_position(self, position):
        self._table_position.blockSignals(True)
        item = self._table_position.findItems(position, Qt.MatchExactly)[0]
        self._table_position.setCurrentItem(item)
        self._table_position.blockSignals(False)
        self._table_position.scrollToItem(item)
        self._table_position.update()

    def _set_current_time(self, time):
        if self._imagecontainer.has_timelapse:
            self._table_time.blockSignals(True)
            item = self._table_time.findItems(str(time), Qt.MatchExactly)[0]
            self._table_time.setCurrentItem(item)
            self._table_time.blockSignals(False)
            self._table_time.scrollToItem(item)
            self._table_time.update()
コード例 #31
0
ファイル: main.py プロジェクト: PeterJackNaylor/cecog
    def _load_image_container(self,
                              plate_infos=None,
                              scan_plates=None,
                              show_dialog=True):
        self._clear_browser()

        if plate_infos is None:
            plate_infos = list(ImageContainer.iter_check_plates(
                self._settings))

        imagecontainer = ImageContainer()
        self._imagecontainer = imagecontainer

        if scan_plates is None:
            scan_plates = dict((info[0], False) for info in plate_infos)

        def load(emitter, icontainer, settings, splates):
            iter_ = icontainer.iter_import_from_settings(settings,
                                                         scan_plates=splates)
            for idx, info in enumerate(iter_):
                emitter.setValue.emit(idx)

            emitter.setLabelText.emit("checking dimensions...")
            emitter.setRange.emit(0, 0)
            QtCore.QCoreApplication.processEvents()

            if len(icontainer.plates) > 0:
                icontainer.set_plate(icontainer.plates[0])
                icontainer.check_dimensions()

        label = ('Please wait until the input structure is scanned\n'
                 'or the structure data loaded...')
        self._dlg = ProgressDialog(label, None, 0, len(scan_plates), self)
        emitter = ProgressObject()
        emitter.setRange.connect(self._dlg.setRange)
        emitter.setValue.connect(self._dlg.setValue)
        emitter.setLabelText.connect(self._dlg.setLabelText)

        try:
            func = lambda: load(emitter, imagecontainer, self._settings,
                                scan_plates)
            self._dlg.exec_(func, (emitter, ))
        except ImportError as e:
            # structure file from versions older than 1.3 contain pdk which is
            # removed
            if 'pdk' in str(e):
                QMessageBox.critical(
                    self, "Error", ("Your structure file format is outdated.\n"
                                    "You have to rescan the plate(s)"))
            else:
                QMessageBox.critical(self, "Error", traceback.format_exc())
            return
        except Exception as e:
            QMessageBox.critical(self, "Error", str(e))

        try:  # I hate lookup tables!
            self._tab_lookup['Cluster'][1].set_imagecontainer(imagecontainer)
        except KeyError:
            pass

        if len(imagecontainer.plates) > 0:
            channels = imagecontainer.channels

            # do not report value changes to the main window
            self._settings.set_notify_change(False)

            self.set_image_crop_size()

            problems = []
            for prefix in ['primary', 'secondary', 'tertiary']:
                trait = self._settings.get_trait(SECTION_NAME_OBJECTDETECTION,
                                                 '%s_channelid' % prefix)
                if trait.set_list_data(channels) is None:
                    problems.append(prefix)
                self._tabs[1].get_widget('%s_channelid' % prefix).update()

            # report problems about a mismatch between channel IDs found in the data
            # and specified by the user
            if len(problems) > 0:
                # a mismatch between settings and data will cause changed settings
                self.settings_changed(True)

            trait = self._settings.get_trait(SECTION_NAME_EVENT_SELECTION,
                                             'duration_unit')

            # allow time-base tracking durations only if time-stamp
            # information is present
            meta_data = imagecontainer.get_meta_data()
            if meta_data.has_timestamp_info:
                result = trait.set_list_data(TimeConverter.units)
            else:
                result = trait.set_list_data([TimeConverter.FRAMES])
            if result is None:
                QMessageBox.critical(
                    self, "Could not set tracking duration units",
                    ("The tracking duration units selected to match the "
                     "load data. Please check your settings."))
                # a mismatch between settings and data will cause changed settings
                self.settings_changed(True)

            # activate change notification again
            self._settings.set_notify_change(True)

            self.set_modules_active(state=True)
            if show_dialog:
                QMessageBox.information(
                    self, "Information", "%d plate(s) successfully loaded." %
                    len(imagecontainer.plates))
        else:
            QMessageBox.critical(
                self, "Error",
                ("No images found\n"
                 "Verifiy your nameing scheme and rescan the data."))
コード例 #32
0
ファイル: cluster.py プロジェクト: bobi5rova/cecog
class ClusterDisplay(QGroupBox):

    def __init__(self, parent, clusterframe,  settings):
        QGroupBox.__init__(self, parent)
        self._settings = settings
        self._clusterframe = clusterframe
        self._imagecontainer = None
        self._jobid = None
        self._toggle_state = JOB_CONTROL_SUSPEND
        self._service = None

        self._host_url = CecogEnvironment.analyzer_config.get(
            'Cluster', 'host_url')
        try:
            self._host_url_fallback = CecogEnvironment.analyzer_config.get(\
                'Cluster', 'host_url_fallback')
        except:
            # old config file
            self._host_url_fallback = self._host_url

        self.setTitle('ClusterControl')
        label1 = QLabel('Cluster URL:', self)

        fixed = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        expanding = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        label1.setSizePolicy(fixed)

        self._label_hosturl = QLabel('', self)
        self._label_hosturl.setSizePolicy(expanding)

        label3 = QLabel('Cluster status:', self)
        label3.setSizePolicy(fixed)

        self._label_status = QLabel('', self)
        self._label_status.setSizePolicy(expanding)

        label4 = QLabel('Path mappings:', self)
        label4.setSizePolicy(fixed)

        self._table_info = QTableWidget(self)
        self._table_info.setSelectionMode(QTableWidget.NoSelection)
        labels = ['status', 'your machine', 'on the cluster']
        self._table_info.setColumnCount(len(labels))
        self._table_info.setHorizontalHeaderLabels(labels)
        self._table_info.setSizePolicy(QSizePolicy(QSizePolicy.Expanding|QSizePolicy.Maximum,
                                                   QSizePolicy.MinimumExpanding))


        layout = QGridLayout(self)
        layout.addWidget(label1, 0, 0, Qt.AlignRight)
        layout.addWidget(self._label_hosturl, 0, 1, 1, 4)
        layout.addWidget(label3, 1, 0, Qt.AlignRight)
        layout.addWidget(self._label_status, 1, 1, 1, 4)
        layout.addWidget(label4, 2, 0, Qt.AlignRight)
        layout.addWidget(self._table_info, 3, 0, 1, 5)

        label = QLabel('Mail addresses:', self)
        layout.addWidget(label, 4, 0, Qt.AlignRight)
        mails = CecogEnvironment.analyzer_config.get('Cluster', 'mail_adresses')
        self._txt_mail = QLineEdit(mails, self)
        layout.addWidget(self._txt_mail, 4, 1, 1, 4)

        self._btn_submit = QPushButton('Submit job', self)
        self._btn_submit.setEnabled(False)
        self._btn_submit.clicked.connect(self._on_submit_job)
        layout.addWidget(self._btn_submit, 5, 0, 1, 5)

        line = QFrame(self)
        line.setFrameShape(QFrame.HLine)
        layout.addWidget(line, 6, 0, 1, 5)

        label5 = QLabel('Current job ID:', self)
        layout.addWidget(label5, 7, 0, Qt.AlignRight)
        self._txt_jobid = QLineEdit(self._jobid or '', self)

        regexp = QRegExp('\d+')
        regexp.setPatternSyntax(QRegExp.RegExp2)
        self._txt_jobid.textEdited.connect(self._on_jobid_entered)
        self._txt_jobid.returnPressed.connect(self._on_update_job_status)

        layout.addWidget(self._txt_jobid, 7, 1)
        self._btn_update = QPushButton('Update', self)
        self._btn_update.clicked.connect(self._on_update_job_status)
        layout.addWidget(self._btn_update, 7, 2)
        self._btn_toogle = QPushButton(self._toggle_state, self)
        self._btn_toogle.clicked.connect(self._on_toggle_job)
        self._btn_toogle.setCheckable(True)
        layout.addWidget(self._btn_toogle, 7, 3)
        self._btn_terminate = QPushButton('Terminate', self)
        self._btn_terminate.clicked.connect(self._on_terminate_job)
        layout.addWidget(self._btn_terminate, 7, 4)

        label = QLabel('Job status:', self)
        layout.addWidget(label, 8, 0, Qt.AlignRight)
        self._label_jobstatus = QLabel('', self)
        layout.addWidget(self._label_jobstatus, 8, 1, 1, 4)

        layout.addItem(QSpacerItem(1, 1,
                                   QSizePolicy.MinimumExpanding,
                                   QSizePolicy.Expanding|QSizePolicy.Maximum),
                       10, 0, 1, 5)

    @property
    def jobIds(self):
        return self._txt_jobid.text()

    @jobIds.setter
    def jobIds(self, jobids):
        self._txt_jobid.setText(jobids)
        self._jobid = str(jobids)

    @property
    def imagecontainer(self):
        if self._imagecontainer is None:
            raise RuntimeError("Image container is not loaded yet")
        return self._imagecontainer

    @imagecontainer.deleter
    def imagecontainer(self):
        del self._imagecontainer

    @imagecontainer.setter
    def imagecontainer(self, imagecontainer):
        self._imagecontainer = imagecontainer

    def _on_jobid_entered(self, txt):
        self._jobid = str(txt)

    @pyqtSlot()
    def _on_submit_job(self):
        self._submit_settings.set_section(SECTION_NAME_GENERAL)
        if not self._submit_settings.get2('constrain_positions'):
            positions = []

            for plate_id in self.imagecontainer.plates:
                self.imagecontainer.set_plate(plate_id)
                meta_data = self.imagecontainer.get_meta_data()
                positions += ['%s___%s' % (plate_id, p) for p in meta_data.positions]
            self._submit_settings.set2('positions', ','.join(positions))
            nr_items = len(positions)
        else:
            positions = self._submit_settings.get2('positions')
            nr_items = len(positions.split(','))

        # FIXME: we need to get the current value for 'position_granularity'
        settings_dummy = self._clusterframe.get_special_settings(self._settings)
        position_granularity = settings_dummy.get('Cluster', 'position_granularity')

        path_out = self._submit_settings.get2('pathout')
        emails = str(self._txt_mail.text()).split(',')
        try:
            self.dlg = ProgressDialog("submitting jobs...", None, 0, 0, self)
            settings_str = self._submit_settings.to_string()
            func = lambda: self._service.submit_job('cecog_batch', settings_str,
                                                    path_out, emails, nr_items,
                                                    position_granularity, version)
            self.dlg.exec_(func)
            jobid = self.dlg.getTargetResult()
        except Exception as e:
            exception(self, 'Error on job submission (%s)' %str(e))
        else:
            # FIXME: no idea how DRMAA 1.0 compatible this is
            if type(jobid) == types.ListType:
                self._jobid = ','.join(jobid)
                main_jobid = jobid[0].split('.')[0]
            else:
                self._jobid = str(jobid)
                main_jobid = jobid
            self._txt_jobid.setText(self._jobid)
            self._update_job_status()
            information(self, 'Job submitted successfully',
                        "Job successfully submitted to the cluster.\nJob ID: %s, items: %d" % (main_jobid, nr_items))

    @pyqtSlot()
    def _on_terminate_job(self):
        try:
            self.dlg = ProgressDialog("terminating jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid, JOB_CONTROL_TERMINATE)
            self.dlg.exec_(func)
        except Exception as e:
            exception(self, 'Error on job termination (%s)' %str(e))
        else:
            self._btn_toogle.setChecked(False)
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setText(self._toggle_state)
            self._update_job_status()

    @pyqtSlot()
    def _on_toggle_job(self):
        try:
            self.dlg = ProgressDialog("suspending jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid, self._toggle_state)
            self.dlg.exec_(func)
        except Exception as e:
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setChecked(False)
            exception(self, 'Error on toggle job status (%s)' %str(e))
        else:
            if self._toggle_state == JOB_CONTROL_SUSPEND:
                self._toggle_state = JOB_CONTROL_RESUME
            else:
                self._toggle_state = JOB_CONTROL_SUSPEND
            self._update_job_status()
        self._btn_toogle.setText(self._toggle_state)

    @pyqtSlot()
    def _on_update_job_status(self):
        txt = self._update_job_status()
        information(self, 'Cluster update', "Message: '%s'" % txt)

    def _update_job_status(self):

        try:
            self.dlg = ProgressDialog("updating job status...", None, 0, 0, self)

            func = lambda: self._service.get_job_status(self._jobid)
            self.dlg.exec_(func)
            txt = self.dlg.getTargetResult()
        except Exception as e:
            exception(self, 'Error on retrieve job status (%s)' %str(e))
        else:
            self._label_jobstatus.setText(txt)
        return txt

    def _check_host_url(self):
        url = urlparse.urlparse(self._host_url)
        try:
            test_sock = socket.create_connection((url.hostname, url.port), timeout=1)
            test_sock.shutdown(2)
            test_sock.close()
        except:
            try:
                url = urlparse.urlparse(self._host_url_fallback)
                test_sock = socket.create_connection((url.hostname, url.port), timeout=1)
                test_sock.shutdown(2)
                test_sock.close()
                self._host_url = self._host_url_fallback
            except:
                exception(self, 'Error on connecting to cluster control service. Please check your config.ini')

    def _connect(self):
        self._check_host_url()

        success = False
        msg = 'Error on connecting to cluster control service on %s' % self._host_url
        try:
            client = RemotingService(self._host_url)
            self.dlg = ProgressDialog("connecting to cluster...", None, 0, 0, self)
            func = lambda: client.getService('clustercontrol')
            self.dlg.exec_(func)
            self._service = self.dlg.getTargetResult()
        except:
            exception(self, msg)
        else:
            try:
                self.dlg.exec_(self._service.get_cecog_versions)
                cluster_versions = self.dlg.getTargetResult()
            except Exception as e:
                exception(self, msg + '(%s)' %str(e))
            else:
                if not version in set(cluster_versions):
                    warning(self, 'Cecog version %s not supported by the cluster' %
                            version, 'Valid versions are: %s' \
                                % ', '.join(cluster_versions))
                else:
                    success = True
        return success

    def _get_mappable_paths(self):
        '''
        Get the paths/filenames which have to be mapped to run on a remote
        cluster. Whether an option is considered or not might depend on other
        values/switches, e.g. if classification is not needed there is no need
        to map the paths.
        '''
        #FIXME: should be done in a better way.
        results = []
        targets = [(('General', 'pathin'), []),
                   (('General', 'pathout'),[]),
                   (('General', 'structure_file_extra_path_name'),
                    [('General', 'structure_file_extra_path')]),
                   (('Classification', 'primary_classification_envpath'),
                    [('Processing', 'primary_classification')]),
                   (('Classification', 'secondary_classification_envpath'),
                    [('General', 'process_secondary'),
                     ('Processing', 'secondary_classification')]),
                   (('Classification', 'tertiary_classification_envpath'),
                    [('General', 'process_tertiary'),
                     ('Processing', 'tertiary_classification')]),
                   (('Classification', 'merged_classification_envpath'),
                    [('General', 'process_merged'),
                     ('Processing', 'merged_classification')]),
                   ]
        targets.extend([(('ObjectDetection', '%s_flat_field_correction_image_dir' % prefix),
                          [('ObjectDetection', '%s_flat_field_correction' % prefix)]) for prefix in ['primary',
                                                                                        'secondary',
                                                                                        'tertiary']]

                       )
        for info, const in targets:
            passed = reduce(lambda x,y: x and y,
                            map(lambda z: self._settings.get(*z), const),
                            True)
            if passed:
                results.append(info)
        return results

    def update_display(self, is_active):
        if self._connect():
            self._can_submit = True

            try:
                self._submit_settings = self._clusterframe.get_special_settings( \
                    self._settings, self.imagecontainer.has_timelapse)
            except:
                self._submit_settings = self._clusterframe.get_special_settings(self._settings)

            self._label_hosturl.setText(self._host_url)
            self._label_status.setText(self._service.get_service_info())

            mappable_paths = self._get_mappable_paths()
            self._table_info.clearContents()
            self._table_info.setRowCount(len(mappable_paths))
            for idx, info in enumerate(mappable_paths):
                value = self._settings.get(*info)
                mapped = CecogEnvironment.map_path_to_os(
                    value, target_os='linux', force=False)
                self._submit_settings.set(info[0], info[1], mapped)
                status = not mapped is None
                item = QTableWidgetItem()
                item.setBackground(QBrush(QColor('green' if status else 'red')))
                txt_mapped = str(mapped) if status else \
                            'Warning: path can not be mapped on the cluster'
                self._table_info.setItem(idx, 0, item)
                self._table_info.setItem(idx, 1, QTableWidgetItem(value))
                self._table_info.setItem(idx, 2, QTableWidgetItem(txt_mapped))
                self._can_submit &= status
            self._table_info.resizeColumnsToContents()
            self._table_info.resizeRowsToContents()
            self._btn_submit.setEnabled(self._can_submit and is_active)
            self._btn_terminate.setEnabled(is_active)
            self._btn_toogle.setEnabled(is_active)
            self._btn_update.setEnabled(is_active)
        else:
            self._btn_submit.setEnabled(False)
            self._btn_terminate.setEnabled(False)
            self._btn_toogle.setEnabled(False)
            self._btn_update.setEnabled(False)
コード例 #33
0
ファイル: cluster.py プロジェクト: manerotoni/cecog
class ClusterDisplay(QGroupBox):

    def __init__(self, parent, clusterframe,  settings):
        QGroupBox.__init__(self, parent)
        self._settings = settings
        self._clusterframe = clusterframe
        self._imagecontainer = None
        self._jobid = None
        self._toggle_state = JOB_CONTROL_SUSPEND
        self._service = None

        self._host_url = CecogEnvironment.analyzer_config.get('Cluster', 'host_url')
        try:
            self._host_url_fallback = CecogEnvironment.analyzer_config.get(\
                'Cluster', 'host_url_fallback')
        except:
            # old config file
            self._host_url_fallback = self._host_url

        self.setTitle('ClusterControl')
        label1 = QLabel('Cluster URL:', self)

        fixed = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        expanding = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        label1.setSizePolicy(fixed)

        self._label_hosturl = QLabel('', self)
        self._label_hosturl.setSizePolicy(expanding)

        label3 = QLabel('Cluster status:', self)
        label3.setSizePolicy(fixed)

        self._label_status = QLabel('', self)
        self._label_status.setSizePolicy(expanding)

        label4 = QLabel('Path mappings:', self)
        label4.setSizePolicy(fixed)

        self._table_info = QTableWidget(self)
        self._table_info.setSelectionMode(QTableWidget.NoSelection)
        labels = ['status', 'your machine', 'on the cluster']
        self._table_info.setColumnCount(len(labels))
        self._table_info.setHorizontalHeaderLabels(labels)
        self._table_info.setSizePolicy(QSizePolicy(QSizePolicy.Expanding|QSizePolicy.Maximum,
                                                   QSizePolicy.MinimumExpanding))


        layout = QGridLayout(self)
        layout.addWidget(label1, 0, 0, Qt.AlignRight)
        layout.addWidget(self._label_hosturl, 0, 1, 1, 4)
        layout.addWidget(label3, 1, 0, Qt.AlignRight)
        layout.addWidget(self._label_status, 1, 1, 1, 4)
        layout.addWidget(label4, 2, 0, Qt.AlignRight)
        layout.addWidget(self._table_info, 3, 0, 1, 5)

        label = QLabel('Mail addresses:', self)
        layout.addWidget(label, 4, 0, Qt.AlignRight)
        mails = CecogEnvironment.analyzer_config.get('Cluster', 'mail_adresses')
        self._txt_mail = QLineEdit(mails, self)
        layout.addWidget(self._txt_mail, 4, 1, 1, 4)

        self._btn_submit = QPushButton('Submit job', self)
        self._btn_submit.setEnabled(False)
        self._btn_submit.clicked.connect(self._on_submit_job)
        layout.addWidget(self._btn_submit, 5, 0, 1, 5)

        line = QFrame(self)
        line.setFrameShape(QFrame.HLine)
        layout.addWidget(line, 6, 0, 1, 5)

        label5 = QLabel('Current job ID:', self)
        layout.addWidget(label5, 7, 0, Qt.AlignRight)
        self._txt_jobid = QLineEdit(self._jobid or '', self)

        regexp = QRegExp('\d+')
        regexp.setPatternSyntax(QRegExp.RegExp2)
        self._txt_jobid.textEdited.connect(self._on_jobid_entered)
        self._txt_jobid.returnPressed.connect(self._on_update_job_status)

        layout.addWidget(self._txt_jobid, 7, 1)
        self._btn_update = QPushButton('Update', self)
        self._btn_update.clicked.connect(self._on_update_job_status)
        layout.addWidget(self._btn_update, 7, 2)
        self._btn_toogle = QPushButton(self._toggle_state, self)
        self._btn_toogle.clicked.connect(self._on_toggle_job)
        self._btn_toogle.setCheckable(True)
        layout.addWidget(self._btn_toogle, 7, 3)
        self._btn_terminate = QPushButton('Terminate', self)
        self._btn_terminate.clicked.connect(self._on_terminate_job)
        layout.addWidget(self._btn_terminate, 7, 4)

        label = QLabel('Job status:', self)
        layout.addWidget(label, 8, 0, Qt.AlignRight)
        self._label_jobstatus = QLabel('', self)
        layout.addWidget(self._label_jobstatus, 8, 1, 1, 4)

        layout.addItem(QSpacerItem(1, 1,
                                   QSizePolicy.MinimumExpanding,
                                   QSizePolicy.Expanding|QSizePolicy.Maximum),
                       10, 0, 1, 5)

    @property
    def imagecontainer(self):
        if self._imagecontainer is None:
            raise RuntimeError("Image container is not loaded yet")
        return self._imagecontainer

    @imagecontainer.deleter
    def imagecontainer(self):
        del self._imagecontainer

    @imagecontainer.setter
    def imagecontainer(self, imagecontainer):
        self._imagecontainer = imagecontainer

    def _on_jobid_entered(self, txt):
        self._jobid = str(txt)

    @pyqtSlot()
    def _on_submit_job(self):
        self._submit_settings.set_section(SECTION_NAME_GENERAL)
        if not self._submit_settings.get2('constrain_positions'):
            positions = []

            for plate_id in self.imagecontainer.plates:
                self.imagecontainer.set_plate(plate_id)
                meta_data = self.imagecontainer.get_meta_data()
                positions += ['%s___%s' % (plate_id, p) for p in meta_data.positions]
            self._submit_settings.set2('positions', ','.join(positions))
            nr_items = len(positions)
        else:
            positions = self._submit_settings.get2('positions')
            nr_items = len(positions.split(','))

        # FIXME: we need to get the current value for 'position_granularity'
        settings_dummy = self._clusterframe.get_special_settings(self._settings)
        position_granularity = settings_dummy.get('Cluster', 'position_granularity')

        path_out = self._submit_settings.get2('pathout')
        emails = str(self._txt_mail.text()).split(',')
        try:
            self.dlg = ProgressDialog("submitting jobs...", None, 0, 0, self)
            settings_str = self._submit_settings.to_string()
            func = lambda: self._service.submit_job('cecog_batch', settings_str,
                                                    path_out, emails, nr_items,
                                                    position_granularity, VERSION)
            self.dlg.exec_(func)
            jobid = self.dlg.getTargetResult()
        except Exception as e:
            exception(self, 'Error on job submission (%s)' %str(e))
        else:
            # FIXME: no idea how DRMAA 1.0 compatible this is
            if type(jobid) == types.ListType:
                self._jobid = ','.join(jobid)
                main_jobid = jobid[0].split('.')[0]
            else:
                self._jobid = str(jobid)
                main_jobid = jobid
            self._txt_jobid.setText(self._jobid)
            self._update_job_status()
            information(self, 'Job submitted successfully',
                        "Job successfully submitted to the cluster.\nJob ID: %s, items: %d" % (main_jobid, nr_items))

    @pyqtSlot()
    def _on_terminate_job(self):
        try:
            self.dlg = ProgressDialog("terminating jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid, JOB_CONTROL_TERMINATE)
            self.dlg.exec_(func)
        except Exception as e:
            exception(self, 'Error on job termination (%s)' %str(e))
        else:
            self._btn_toogle.setChecked(False)
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setText(self._toggle_state)
            self._update_job_status()

    @pyqtSlot()
    def _on_toggle_job(self):
        try:
            self.dlg = ProgressDialog("suspending jobs...", None, 0, 0, self)
            func = lambda: self._service.control_job(self._jobid, self._toggle_state)
            self.dlg.exec_(func)
        except Exception as e:
            self._toggle_state = JOB_CONTROL_SUSPEND
            self._btn_toogle.setChecked(False)
            exception(self, 'Error on toggle job status (%s)' %str(e))
        else:
            if self._toggle_state == JOB_CONTROL_SUSPEND:
                self._toggle_state = JOB_CONTROL_RESUME
            else:
                self._toggle_state = JOB_CONTROL_SUSPEND
            self._update_job_status()
        self._btn_toogle.setText(self._toggle_state)

    @pyqtSlot()
    def _on_update_job_status(self):
        txt = self._update_job_status()
        information(self, 'Cluster update', "Message: '%s'" % txt)

    def _update_job_status(self):
        try:
            self.dlg = ProgressDialog("updating job status...", None, 0, 0, self)
            func = lambda: self._service.get_job_status(self._jobid)
            self.dlg.exec_(func)
            txt = self.dlg.getTargetResult()
        except Exception as e:
            exception(self, 'Error on retrieve job status (%s)' %str(e))
        else:
            self._label_jobstatus.setText(txt)
        return txt

    def _check_host_url(self):
        url = urlparse.urlparse(self._host_url)
        try:
            test_sock = socket.create_connection((url.hostname, url.port), timeout=1)
            test_sock.shutdown(2)
            test_sock.close()
        except:
            try:
                url = urlparse.urlparse(self._host_url_fallback)
                test_sock = socket.create_connection((url.hostname, url.port), timeout=1)
                test_sock.shutdown(2)
                test_sock.close()
                self._host_url = self._host_url_fallback
            except:
                exception(self, 'Error on connecting to cluster control service. Please check your config.ini')

    def _connect(self):
        self._check_host_url()

        success = False
        msg = 'Error on connecting to cluster control service on %s' % self._host_url
        try:
            client = RemotingService(self._host_url)
            self.dlg = ProgressDialog("connecting to cluster...", None, 0, 0, self)
            func = lambda: client.getService('clustercontrol')
            self.dlg.exec_(func)
            self._service = self.dlg.getTargetResult()
        except:
            exception(self, msg)
        else:
            try:
                self.dlg.exec_(self._service.get_cecog_versions)
                cluster_versions = self.dlg.getTargetResult()
            except Exception as e:
                exception(self, msg + '(%s)' %str(e))
            else:
                if not VERSION in set(cluster_versions):
                    warning(self, 'Cecog version %s not supported by the cluster' %
                            VERSION, 'Valid versions are: %s' \
                                % ', '.join(cluster_versions))
                else:
                    success = True
        return success

    def _get_mappable_paths(self):
        '''
        Get the paths/filenames which have to be mapped to run on a remote
        cluster. Whether an option is considered or not might depend on other
        values/switches, e.g. if classification is not needed there is no need
        to map the paths.
        '''
        #FIXME: should be done in a better way.
        results = []
        targets = [(('General', 'pathin'), []),
                   (('General', 'pathout'),[]),
                   (('General', 'structure_file_extra_path_name'),
                    [('General', 'structure_file_extra_path')]),
                   (('Classification', 'primary_classification_envpath'),
                    [('Processing', 'primary_classification')]),
                   (('Classification', 'secondary_classification_envpath'),
                    [('General', 'process_secondary'),
                     ('Processing', 'secondary_classification')]),
                   (('Classification', 'tertiary_classification_envpath'),
                    [('General', 'process_tertiary'),
                     ('Processing', 'tertiary_classification')]),
                   (('Classification', 'merged_classification_envpath'),
                    [('General', 'process_merged'),
                     ('Processing', 'merged_classification')]),
                   ]
        targets.extend([(('ObjectDetection', '%s_flat_field_correction_image_dir' % prefix),
                          [('ObjectDetection', '%s_flat_field_correction' % prefix)]) for prefix in ['primary',
                                                                                        'secondary',
                                                                                        'tertiary']]

                       )
        for info, const in targets:
            passed = reduce(lambda x,y: x and y,
                            map(lambda z: self._settings.get(*z), const),
                            True)
            if passed:
                results.append(info)
        return results

    def update_display(self, is_active):
        if self._connect():
            self._can_submit = True

            try:
                self._submit_settings = self._clusterframe.get_special_settings( \
                    self._settings, self.imagecontainer.has_timelapse)
            except:
                self._submit_settings = self._clusterframe.get_special_settings(self._settings)

            self._label_hosturl.setText(self._host_url)
            self._label_status.setText(self._service.get_service_info())

            mappable_paths = self._get_mappable_paths()
            self._table_info.clearContents()
            self._table_info.setRowCount(len(mappable_paths))
            for idx, info in enumerate(mappable_paths):
                value = self._settings.get(*info)
                mapped = CecogEnvironment.map_path_to_os(
                    value, target_os='linux', force=False)
                self._submit_settings.set(info[0], info[1], mapped)
                status = not mapped is None
                item = QTableWidgetItem()
                item.setBackground(QBrush(QColor('green' if status else 'red')))
                txt_mapped = str(mapped) if status else \
                            'Warning: path can not be mapped on the cluster'
                self._table_info.setItem(idx, 0, item)
                self._table_info.setItem(idx, 1, QTableWidgetItem(value))
                self._table_info.setItem(idx, 2, QTableWidgetItem(txt_mapped))
                self._can_submit &= status
            self._table_info.resizeColumnsToContents()
            self._table_info.resizeRowsToContents()
            self._btn_submit.setEnabled(self._can_submit and is_active)
            self._btn_terminate.setEnabled(is_active)
            self._btn_toogle.setEnabled(is_active)
            self._btn_update.setEnabled(is_active)
        else:
            self._btn_submit.setEnabled(False)
            self._btn_terminate.setEnabled(False)
            self._btn_toogle.setEnabled(False)
            self._btn_update.setEnabled(False)
コード例 #34
0
ファイル: main.py プロジェクト: xiwei-zhang/cecog
class CecogAnalyzer(QtWidgets.QMainWindow):

    NAME_FILTERS = ['Settings files (*.conf)', 'All files (*.*)']
    modified = QtCore.pyqtSignal('bool')

    def __init__(self, appname, version, redirect, settings=None,
                 debug=False, *args, **kw):
        super(CecogAnalyzer, self).__init__(*args, **kw)
        self.setWindowTitle("%s-%s" %(appname, version) + '[*]')
        self.setAcceptDrops(True)
        self.setCentralWidget(QtWidgets.QFrame(self))
        self.setObjectName(appname)

        self.version = version
        self.appname = appname
        self.debug = debug

        self.environ = CecogEnvironment(version=version, redirect=redirect,
                                        debug=debug)

        if debug:
            self.environ.pprint()

        self._is_initialized = False
        self._imagecontainer = None
        self._meta_data = None
        self._browser = None

        action_quit = self.create_action('&Quit', slot=self.close)
        action_pref = self.create_action('&Preferences',
                                         slot=self.open_preferences)

        action_load = self.create_action('&Load Settings...',
                                         shortcut=QtGui.QKeySequence.Open,
                                         slot=self._on_file_open)
        action_save = self.create_action('&Save Settings',
                                         shortcut=QtGui.QKeySequence.Save,
                                         slot=self._on_file_save)
        self.action_save = action_save
        action_save_as = self.create_action('&Save Settings As...',
                                            shortcut=QtGui.QKeySequence.SaveAs,
                                            slot=self._on_file_save_as)

        menu_file = self.menuBar().addMenu('&File')
        self.add_actions(menu_file, (action_pref,
                                     None, action_load,
                                     None, action_save, action_save_as,
                                     None, action_quit))

        action_log = self.create_action('&Log window',
                                        shortcut=QtGui.QKeySequence(Qt.CTRL+Qt.Key_L),
                                        slot=self._on_show_log_window)

        action_open = self.create_action('&Browser',
                                         shortcut=QtGui.QKeySequence('CTRL+B'),
                                         slot=self._on_browser_open)

        menu_view = self.menuBar().addMenu('&View')
        self.add_actions(menu_view, (action_log,))
        self.add_actions(menu_view, (action_open,))

        action_assistant = self.create_action('&Help',
                                              shortcut=QtGui.QKeySequence.HelpContents,
                                              slot=self.show_assistant)
        action_about = self.create_action('&About', slot=self.on_about)
        action_aboutQt = self.create_action('&About Qt', slot=self.about_qt)


        menu_help = self.menuBar().addMenu('&Help')
        self.add_actions(menu_help, (action_assistant, action_about,
                                     action_aboutQt))

        self.setStatusBar(QtWidgets.QStatusBar(self))

        self._selection = QtWidgets.QListWidget(self.centralWidget())
        self._selection.setViewMode(QtWidgets.QListView.IconMode)
        self._selection.setIconSize(QtCore.QSize(35, 35))
        self._selection.setGridSize(QtCore.QSize(140, 60))
        self._selection.setMovement(QtWidgets.QListView.Static)
        self._selection.setMaximumWidth(self._selection.gridSize().width() + 5)
        self._selection.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._selection.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                  QtWidgets.QSizePolicy.Expanding))

        self._pages = FrameStack(self)

        self._settings_filename = None
        self._settings = GuiConfigSettings(self)

        self._tab_lookup = OrderedDict()
        self._tabs = [GeneralFrame(self._settings, self._pages, SECTION_NAME_GENERAL),
                      ObjectDetectionFrame(self._settings, self._pages, SECTION_NAME_OBJECTDETECTION),
                      FeatureExtractionFrame(self._settings, self._pages, SECTION_NAME_FEATURE_EXTRACTION),
                      ClassificationFrame(self._settings, self._pages, SECTION_NAME_CLASSIFICATION),
                      TrackingFrame(self._settings, self._pages, SECTION_NAME_TRACKING),
                      EventSelectionFrame(self._settings, self._pages, SECTION_NAME_EVENT_SELECTION),
                      ErrorCorrectionFrame(self._settings, self._pages, SECTION_NAME_ERRORCORRECTION),
                      OutputFrame(self._settings, self._pages, SECTION_NAME_OUTPUT),
                      ProcessingFrame(self._settings, self._pages, SECTION_NAME_PROCESSING)]

        # connections for the section frames
        self._tabs[3].connect_browser_btn(self._on_browser_open)
        for frame in self._tabs:
            frame.status_message.connect(self.statusBar().showMessage)

        app = AppPreferences()
        if app.cluster_support:
            clusterframe = ClusterFrame(self._settings, self._pages, SECTION_NAME_CLUSTER)
            clusterframe.set_imagecontainer(self._imagecontainer)
            self._tabs.append(clusterframe)

        try:
            self.updateStyleSheet(loadStyle(app.stylesheet))
        except Exception as e:
            # proceed with no stylesheet
            traceback.print_exc()


        widths = []
        for tab in self._tabs:
            size = self._add_page(tab)
            widths.append(size.width())
        self.set_modules_active(state=False)
        self._pages.setMinimumWidth(max(widths) + 45)

        self._selection.currentItemChanged.connect(self._on_change_page)
        self._selection.setCurrentRow(0)

        w_logo = QtWidgets.QLabel(self.centralWidget())
        w_logo.setPixmap(QtGui.QPixmap(':cecog_logo_w145'))

        layout = QtWidgets.QGridLayout(self.centralWidget())
        layout.addWidget(self._selection, 0, 0)
        layout.addWidget(w_logo, 1, 0, Qt.AlignBottom | Qt.AlignHCenter)
        layout.addWidget(self._pages, 0, 1, 2, 1)
        layout.setContentsMargins(1, 1, 1, 1)

        self.setGeometry(0, 0, 1250, 800)
        self.setMinimumSize(QtCore.QSize(700, 600))
        self._is_initialized = True

        self._restore_geometry()
        self.show()

        # finally load (demo) - settings
        if settings is None:
            self.load_settings(self.environ.demo_settings)
        elif os.path.isfile(settings):
            self.load_settings(settings)
        else:
            QMessageBox.warning(
                self, "Warning", "File (%s) does not exist" %settings)

    def dragEnterEvent(self, event):
        event.acceptProposedAction()

    def dragMoveEvent(self, event):
        event.acceptProposedAction()

    def dropEvent(self, event):
        mimeData = event.mimeData()
        if mimeData.hasUrls():
            if len(mimeData.urls()) == 1:
                self.load_settings(fix_path(mimeData.urls()[0].path()))
                # self._on_load_input()
        event.acceptProposedAction()

    def dragLeaveEvent(self, event):
        event.accept()

    def _save_geometry(self):
        settings = QtCore.QSettings(version.organisation, version.appname)
        settings.beginGroup('Gui')
        settings.setValue('state', self.saveState())
        settings.setValue('geometry', self.saveGeometry())

        try:
            settings.setValue(
                'clusterjobs', self._pages.widgetByType(
                    ClusterFrame).get_jobids())
        except KeyError:
            pass

        settings.endGroup()

    def _restore_geometry(self):
        settings = QtCore.QSettings(version.organisation, version.appname)
        settings.beginGroup('Gui')

        if settings.contains('geometry'):
            self.restoreGeometry(settings.value('geometry'))

        if settings.contains('state'):
            self.restoreState(settings.value('state'))

        if settings.contains('clusterjobs'):
            jobids = settings.value('clusterjobs')
            if AppPreferences().cluster_support and jobids:
                self._pages.widgetByType(ClusterFrame).restore_jobids(jobids)

        settings.endGroup()

    def closeEvent(self, event):
        self._pages.close()
        # Quit dialog only if not debuging flag is not set
        self._save_geometry()
        if self.debug:
            QtWidgets.QApplication.exit()
        ret = QMessageBox.question(self, "Quit %s" %self.appname,
                                   "Do you really want to quit?",
                                   QMessageBox.Yes|QMessageBox.No)

        if ret == QMessageBox.No:
            event.ignore()
        else:
            self._check_settings_saved(QMessageBox.Yes|QMessageBox.No)
            QtWidgets.QApplication.exit()

    def settings_changed(self, changed):
        if self._is_initialized:
            self.setWindowModified(changed)
            self.action_save.setEnabled(changed)
            self.modified.emit(changed)

    def _add_page(self, widget):
        button = QtWidgets.QListWidgetItem(self._selection)
        button.setIcon(QtGui.QIcon(widget.ICON))
        button.setText(widget.get_name())
        button.setTextAlignment(Qt.AlignHCenter)
        self._pages.addWidget(widget)

        widget.toggle_tabs.connect(self._on_toggle_tabs)
        self._tab_lookup[widget.get_name()] = (button, widget)
        return widget.size()

    def _on_toggle_tabs(self, name):
        """Toggle ItemIsEnabled flag for all list items but name."""
        for name2 in self._tab_lookup:
            if name2 != name:
                item, _ = self._tab_lookup[name2]
                flags = item.flags()
                # check flag (and)
                if flags & Qt.ItemIsEnabled:
                    # remove flag (nand)
                    item.setFlags(flags & ~Qt.ItemIsEnabled)
                else:
                    # set flag (or)
                    item.setFlags(flags | Qt.ItemIsEnabled)

    def _on_change_page(self, current, previous):
        if not current:
            current = previous
        index = self._selection.row(current)
        self._pages.setCurrentIndex(index)
        widget = self._pages.widget(index)
        widget.page_changed()

    def _check_settings_saved(
            self, buttons=QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel):
        if self.isWindowModified():
            result = QMessageBox.question(
                self, "Settings have been modified",
                "Do you want to save the settings?",
                buttons)

            if result == QMessageBox.Yes:
                self.save_settings()
        else:
            result = QMessageBox.No
        return result

    def add_actions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)

    def create_action(self, text, slot=None, shortcut=None, icon=None,
                      tooltip=None, checkable=None, signal='triggered',
                      checked=False):
        action = QtWidgets.QAction(text, self)
        if icon is not None:
            action.setIcon(QtGui.QIcon(':/%s.png' % icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tooltip is not None:
            action.setToolTip(tooltip)
            action.setStatusTip(tooltip)
        if slot is not None:
            getattr(action, signal).connect(slot)
        if checkable is not None:
            action.setCheckable(True)
        action.setChecked(checked)
        return action

    def save_settings(self, save_as=False):
        filename = self._settings_filename
        if filename is None or save_as:
            filename = self._get_save_as_filename()
        if not filename is None:
            self._write_settings(filename)

    def load_settings(self, filename):
        try:
            self._settings.read(filename)
        except Exception as e:
            QMessageBox.critical(self, "Error",
                                 ("Error loading settings file\n"
                                  "Could not load settings file '%s'.\n%s"
                                  %(filename, str(e))))
            self.statusBar().showMessage('Error loading settings files.')
        else:
            self._settings_filename = filename
            title = self.windowTitle().split(' - ')[0]
            self.setWindowTitle('%s - %s[*]' % (title, filename))
            try:
                # reset naming scheme to load config file completely
                nst = self._settings.get_trait("General",  "namingscheme")
                namingscheme_file = self._settings("General", "namingscheme")
                if not namingscheme_file in nst.list_data:
                    self._settings.set("General", "namingscheme", nst.default_value)
                    QMessageBox.warning(self, "Unkown naming scheme",
                                        ("%s-%s can not use the naming scheme '%s'."
                                         " Resetting to default '%s'"
                                         %(version.appname, version.version,
                                           namingscheme_file, nst.default_value)))

                for widget in self._tabs:
                    widget.update_input()
            except Exception as e:
                msg = "Could not load settings file (%s)\n.%s" \
                      %(filename, traceback.format_exc())
                QMessageBox.critical(self, "Error", msg)

            else:
                # set settings to not-changed (assume no changed since loaded from file)
                self.settings_changed(False)
                # notify tabs about new settings loaded
                for tab in self._tabs:
                    tab.settings_loaded()
                self.statusBar().showMessage('Settings successfully loaded.')

    def _write_settings(self, filename):
        try:
            f = file(filename, 'w')
            # create a new version (copy) of the current
            # settings which add the needed rendering information
            pframe = self._tab_lookup[SECTION_NAME_PROCESSING][1]
            settings_dummy = pframe.get_export_settings(self._settings)
            settings_dummy.write(f)
            f.close()
        except Exception as e:
            msg = "Could not save settings\n%s" %str(e)
            QMessageBox.critical(self, "Error", msg)
            self.statusBar().showMessage('Settings not successfully saved.')
        else:
            self._settings_filename = filename
            self.setWindowTitle('%s - %s[*]' % (self.appname, filename))
            self.settings_changed(False)
            self.statusBar().showMessage('Settings successfully saved.')

    def on_about(self):
        dialog = CecogAboutDialog(self)
        dialog.show()

    def about_qt(self):
        QMessageBox.aboutQt(self, "about Qt")

    def open_preferences(self):
        pref = PreferencesDialog(self)
        pref.exec_()

    def updateStyleSheet(self, stylesheet):
        self.setStyleSheet("")
        self.setStyleSheet(stylesheet)

        self._pages.assistant.setStyleSheet("")
        self._pages.assistant.setStyleSheet(stylesheet)

        if self._browser is not None:
            self._browser.setStyleSheet("")
            self._browser.setStyleSheet(stylesheet)

    def _on_browser_open(self):
        if self._imagecontainer is None:
            QMessageBox.warning(self, 'Data structure not loaded',
                                'The input directory structure file was not loaded.\n'
                                'Click "Scan input directory" in section "General" to proceed.')
        elif self._browser is None:
            try:
                browser = Browser(self._settings, self._imagecontainer, None)
                browser.show()
                browser.raise_()
                browser.setFocus()
                app = AppPreferences()
                browser.setStyleSheet(loadStyle(app.stylesheet))
                self._browser = browser
            except Exception as e:
                traceback.print_exc()
                QMessageBox.critical(self, "Error", str(e))
        else:
            self._browser.show()
            self._browser.raise_()

    def _on_load_input(self):
        txt = "Error scanning image structure"
        path_in = self._settings.get(SECTION_NAME_GENERAL, 'pathin')
        if path_in == '':
            QMessageBox.critical(self, "Error", "%s\nImage path must be defined." %txt)
        elif not os.path.isdir(path_in) and \
             not os.path.isdir(os.path.join(self.environ.package_dir, path_in)):
            QMessageBox.critical(self, "Error", "%s\nImage path '%s' not found."
                                 %(txt, path_in))
        else:
            try:
                infos = list(ImageContainer.iter_check_plates(self._settings))
            except Exception as e:
                QMessageBox.critical(self, "Error", "%s\n%s" %(txt, str(e)))
            else:
                found_any = numpy.any([not info[3] is None for info in infos])
                cancel = False
                if found_any:
                    found_plates = [info[0] for info in infos
                                    if not info[3] is None]
                    missing_plates = [info[0] for info in infos
                                      if info[3] is None]
                    has_missing = len(missing_plates) > 0
                    txt = '%s plates were already scanned.\nDo you want ' \
                          'to rescan the file structure(s)? ' \
                          'This can take several minutes.' % \
                          ('Some' if has_missing else 'All')
                    title = 'Rescan input structure?'

                    box = QMessageBox(QMessageBox.Question, title, title,
                                      QMessageBox.Cancel, self, Qt.Sheet)
                    box.setWindowModality(Qt.WindowModal)
                    box.setInformativeText(txt)
                    box.setDetailedText('Plates with scanned structure: \n%s\n'
                                        '\nPlates without scanned structure: '
                                        '\n%s' %
                                        ('\n'.join(found_plates),
                                         '\n'.join(missing_plates)))
                    if not has_missing:
                        btn1 = QtWidgets.QPushButton('No', box)
                        box.addButton(btn1, QMessageBox.NoRole)
                        box.setDefaultButton(btn1)
                    elif len(found_plates) > 0:
                        btn1 = QtWidgets.QPushButton('Rescan missing', box)
                        box.addButton(btn1, QMessageBox.YesRole)
                        box.setDefaultButton(btn1)
                    else:
                        btn1 = None

                    btn2 = QtWidgets.QPushButton('Rescan all', box)
                    box.addButton(btn2, QMessageBox.YesRole)

                    if box.exec_() == QMessageBox.Cancel:
                        cancel = True
                    else:
                        btn = box.clickedButton()
                        if btn == btn1:
                            if has_missing:
                                scan_plates = dict([(info[0], info[0] in missing_plates) for info in infos])
                            else:
                                scan_plates = dict((info[0], False) for info in infos)
                        else:
                            scan_plates = dict((info[0], True) for info in infos)
                else:
                    has_multiple = self._settings.get(SECTION_NAME_GENERAL,
                                                      "has_multiple_plates")
                    ret = QMessageBox.question(self, "No structure data found",
                                               ("Scanning the input directory can be time "
                                                "consuming.\n\nDo you want to proceed?"),
                                               QMessageBox.Yes|QMessageBox.No)
                    if ret == QMessageBox.No:
                        cancel = True
                    scan_plates = dict((info[0], True) for info in infos)

                if not cancel:
                    self._load_image_container(infos, scan_plates)

    def _load_image_container(self, plate_infos=None, scan_plates=None,
                              show_dialog=True):
        self._clear_browser()

        if plate_infos is None:
            plate_infos = list(ImageContainer.iter_check_plates(self._settings))

        imagecontainer = ImageContainer()
        self._imagecontainer = imagecontainer

        if scan_plates is None:
            scan_plates = dict((info[0], False) for info in plate_infos)

        def load(emitter, icontainer, settings, splates):
            iter_ = icontainer.iter_import_from_settings(settings, scan_plates=splates)
            for idx, info in enumerate(iter_):
                emitter.setValue.emit(idx)

            emitter.setLabelText.emit("checking dimensions...")
            emitter.setRange.emit(0, 0)
            QtCore.QCoreApplication.processEvents()

            if len(icontainer.plates) > 0:
                icontainer.set_plate(icontainer.plates[0])
                icontainer.check_dimensions()


        label = ('Please wait until the input structure is scanned\n'
                 'or the structure data loaded...')
        self._dlg = ProgressDialog(label, None, 0, len(scan_plates), self)
        emitter = ProgressObject()
        emitter.setRange.connect(self._dlg.setRange)
        emitter.setValue.connect(self._dlg.setValue)
        emitter.setLabelText.connect(self._dlg.setLabelText)

        try:
            func = lambda: load(emitter, imagecontainer,
                                self._settings, scan_plates)
            self._dlg.exec_(func, (emitter, ))
        except ImportError as e:
            # structure file from versions older than 1.3 contain pdk which is
            # removed
            if 'pdk' in str(e):
                QMessageBox.critical(self, "Error",
                                     ("Your structure file format is outdated.\n"
                                      "You have to rescan the plate(s)"))
            else:
                QMessageBox.critical(self, "Error", traceback.format_exc())
            return
        except Exception as e:
            QMessageBox.critical(self, "Error", str(e))


        try: # I hate lookup tables!
            self._tab_lookup['Cluster'][1].set_imagecontainer(imagecontainer)
        except KeyError:
            pass

        if len(imagecontainer.plates) > 0:
            channels = imagecontainer.channels

            # do not report value changes to the main window
            self._settings.set_notify_change(False)

            self.set_image_crop_size()

            problems = []
            for prefix in ['primary', 'secondary', 'tertiary']:
                trait = self._settings.get_trait(SECTION_NAME_OBJECTDETECTION,
                                                 '%s_channelid' % prefix)
                if trait.set_list_data(channels) is None:
                    problems.append(prefix)
                self._tabs[1].get_widget('%s_channelid' % prefix).update()

            # report problems about a mismatch between channel IDs found in the data
            # and specified by the user
            if len(problems) > 0:
                # a mismatch between settings and data will cause changed settings
                self.settings_changed(True)

            trait = self._settings.get_trait(SECTION_NAME_EVENT_SELECTION,
                                             'duration_unit')

            # allow time-base tracking durations only if time-stamp
            # information is present
            meta_data = imagecontainer.get_meta_data()
            if meta_data.has_timestamp_info:
                result = trait.set_list_data(TimeConverter.units)
            else:
                result = trait.set_list_data([TimeConverter.FRAMES])
            if result is None:
                QMessageBox.critical(self, "Could not set tracking duration units",
                                     ("The tracking duration units selected to match the "
                                      "load data. Please check your settings."))
                # a mismatch between settings and data will cause changed settings
                self.settings_changed(True)

            # activate change notification again
            self._settings.set_notify_change(True)


            self.set_modules_active(state=True)
            if show_dialog:
                QMessageBox.information(
                    self, "Information",
                    "%d plate(s) successfully loaded." % len(imagecontainer.plates))
        else:
            QMessageBox.critical(self, "Error",
                                 ("No images found\n"
                                  "Verifiy your nameing scheme and rescan the data."))

    def set_image_crop_size(self):
        x0, y0, x1, y1 = self._settings.get('General', 'crop_image_x0'), \
                         self._settings.get('General', 'crop_image_y0'), \
                         self._settings.get('General', 'crop_image_x1'), \
                         self._settings.get('General', 'crop_image_y1')

        x0_, y0_, x1_, y1_ = 0, \
                             0, \
                             self._imagecontainer.get_meta_data().dim_x, \
                             self._imagecontainer.get_meta_data().dim_y

        tr_x0 = self._settings.get_trait(SECTION_NAME_GENERAL, 'crop_image_x0')
        tr_y0 = self._settings.get_trait(SECTION_NAME_GENERAL, 'crop_image_y0')
        tr_x1 = self._settings.get_trait(SECTION_NAME_GENERAL, 'crop_image_x1')
        tr_y1 = self._settings.get_trait(SECTION_NAME_GENERAL, 'crop_image_y1')

        # Check if the crop values are valid
        if x0 > 0 and y0 > 0 and x1 <= x1_ and y1 <= y1_ and \
                x0 != x1 and y0 != y1:
            # Set to default values
            tr_x0.set_value(tr_x0.get_widget(), x0)
            tr_y0.set_value(tr_y0.get_widget(), y0)
            tr_x1.set_value(tr_x1.get_widget(), x1)
            tr_y0.set_value(tr_y1.get_widget(), y1)
        else:
            tr_x0.set_value(tr_x0.get_widget(), x0_)
            tr_y0.set_value(tr_y0.get_widget(), y0_)
            tr_x1.set_value(tr_x1.get_widget(), x1_)
            tr_y0.set_value(tr_y1.get_widget(), y1_)

        # Set GUI widget valid ranges
        tr_x0.set_min_value(x0_)
        tr_x0.set_max_value(x1_)
        tr_y0.set_min_value(y0_)
        tr_y0.set_max_value(y1_)
        tr_x1.set_min_value(x0_)
        tr_x1.set_max_value(x1_)
        tr_y1.set_min_value(y0_)
        tr_y1.set_max_value(y1_)

    def set_modules_active(self, state=True):
        for name, (button, widget) in self._tab_lookup.iteritems():
            widget.set_active(state)

    @QtCore.pyqtSlot()
    def _on_file_open(self):

        if self._check_settings_saved() != QMessageBox.Cancel:
            home = ""
            if self._settings_filename is not None:
                settings_filename = self.environ.demo_settings
                if os.path.isfile(settings_filename):
                    home = settings_filename
            filename = QtWidgets.QFileDialog.getOpenFileName( \
               self, 'Open config file', home, ';;'.join(self.NAME_FILTERS))[0]
            if not bool(filename):
                return

            try:
                self.load_settings(filename)
                if self._settings.was_old_file_format():
                    QMessageBox.information(
                        self, 'Information',
                        'Config file was updated to version %s' %self.version)
            except Exception as e:
                msg = "File could not be loaded\n%s" %str(e)
                QMessageBox.critical(self, "Error", msg)
            finally:
                self._clear_browser()
                self.set_modules_active(state=False)


    @QtCore.pyqtSlot()
    def _on_file_save(self):
        self.save_settings(False)

    @QtCore.pyqtSlot()
    def _on_file_save_as(self):
        self.save_settings(True)

    def _clear_browser(self):
        if not self._browser is None:
            self._browser.close()
            self._browser = None

    def _on_show_log_window(self):
        self._pages.showLogWindow()

    def _get_save_as_filename(self):
        dir = ""
        if self._settings_filename is not None:
            settings_filename = self.environ.demo_settings
            if os.path.isfile(settings_filename):
                dir = settings_filename
        filename = QtWidgets.QFileDialog.getSaveFileName(
            self, 'Save config file as', dir, ';;'.join(self.NAME_FILTERS))[0]
        return filename or None

    def show_assistant(self):
        self._pages.assistant.show()
        self._pages.assistant.openKeyword('index')
コード例 #35
0
ファイル: navigation.py プロジェクト: CellCognition/cecog
class NavigationModule(Module):

    NAME = 'Navigation'

    coordinate_changed = pyqtSignal(Coordinate)

    def __init__(self, parent, browser, imagecontainer):
        Module.__init__(self, parent, browser)

        self._imagecontainer = imagecontainer

        frame_info = QGroupBox('Plate Information', self)
        layout = QGridLayout(frame_info)
#         frame_info.setStyleSheet('QLabel { font-size: 10px }')
        self._label_info = QLabel(frame_info)
        layout.addWidget(self._label_info, 0, 0, 0, 0,
                         Qt.AlignCenter | Qt.AlignHCenter)

        splitter = QSplitter(Qt.Vertical, self)
        splitter.setMinimumWidth(40)

        layout = QBoxLayout(QBoxLayout.TopToBottom, self)
        layout.setContentsMargins(5, 5, 5, 5)
        layout.addWidget(splitter)

        grp1 = QGroupBox('Plates', splitter)
        grp2 = QGroupBox('Positions', splitter)
        splitter.addWidget(grp1)
        splitter.addWidget(grp2)

        layout = QGridLayout(grp1)
        layout.setContentsMargins(5, 10, 5, 5)

        table = QTableWidget(grp1)
        table.setEditTriggers(QTableWidget.NoEditTriggers)
        table.setSelectionMode(QTableWidget.SingleSelection)
        table.setSelectionBehavior(QTableWidget.SelectRows)
        table.setAlternatingRowColors(True)
#         table.setStyleSheet('font-size: 10px;')
        table.currentItemChanged.connect(self._on_plate_changed)
        self._table_plate = table
        layout.addWidget(table, 0, 0)

        self._update_plate_table()

        layout = QGridLayout(grp2)
        layout.setContentsMargins(5, 10, 5, 5)

        table = QTableWidget(grp2)
        table.setEditTriggers(QTableWidget.NoEditTriggers)
        table.setSelectionMode(QTableWidget.SingleSelection)
        table.setSelectionBehavior(QTableWidget.SelectRows)
        table.setAlternatingRowColors(True)
#         table.setStyleSheet('font-size: 10px;')
        table.currentItemChanged.connect(self._on_position_changed)
        self._table_position = table
        layout.addWidget(table, 0, 0)

        if self._imagecontainer.has_timelapse:
            grp3 = QGroupBox('Time', splitter)
            splitter.addWidget(grp3)

            layout = QGridLayout(grp3)
            layout.setContentsMargins(5, 10, 5, 5)

            table = QTableWidget(grp3)
            table.setEditTriggers(QTableWidget.NoEditTriggers)
            table.setSelectionMode(QTableWidget.SingleSelection)
            table.setSelectionBehavior(QTableWidget.SelectRows)
            table.setAlternatingRowColors(True)
#             table.setStyleSheet('font-size: 10px;')
            table.currentItemChanged.connect(self._on_time_changed)
            self._table_time = table
            layout.addWidget(table, 0, 0)

        splitter.addWidget(frame_info)

    def _update_plate_table(self):
        table = self._table_plate
        table.blockSignals(True)
        table.clearContents()

        column_names = ['Plate ID']
        table.setColumnCount(len(column_names))
        table.setHorizontalHeaderLabels(column_names)
        plates = self._imagecontainer.plates
        table.setRowCount(len(plates))

        for idx, plate in enumerate(plates):
            item = QTableWidgetItem(plate)
            item.setData(0, plate)
            table.setItem(idx, 0, item)

        table.resizeColumnsToContents()
        table.resizeRowsToContents()
        table.blockSignals(False)

    def _update_time_table(self, meta_data, coordinate):
        coordinate = coordinate.copy()
        table = self._table_time
        table.blockSignals(True)
        table.clearContents()

        column_names = ['Frame']

        if meta_data.has_timestamp_info:
            column_names += ['rel. t (min)', 'abs. t (GMT)']
        table.setColumnCount(len(column_names))
        table.setHorizontalHeaderLabels(column_names)
        table.setRowCount(len(meta_data.times))

        for idx, time in enumerate(meta_data.times):
            item = QTableWidgetItem(str(time))
            item.setData(0, time)
            table.setItem(idx, 0, item)

            if meta_data.has_timestamp_info:
                coordinate.time = time
                ts_rel = meta_data.get_timestamp_relative(coordinate)
                ts_abs = meta_data.get_timestamp_absolute(coordinate)
                if not numpy.isnan(ts_rel):
                    info = '%.1f' % (ts_rel / 60)
                    table.setItem(idx, 1, QTableWidgetItem(info))
                if not numpy.isnan(ts_abs):
                    info = time_lib.strftime("%Y-%m-%d %H:%M:%S",
                                             time_lib.gmtime(ts_abs))
                    table.setItem(idx, 2, QTableWidgetItem(info))
        table.resizeColumnsToContents()
        table.resizeRowsToContents()
        table.blockSignals(False)

    def _update_position_table(self, meta_data):
        table = self._table_position
        table.blockSignals(True)
        table.clearContents()

        column_names = ['Position']
        if meta_data.has_well_info:
            column_names += ['Well', 'Subwell']
        if meta_data.has_condition_info:
            column_names.append('Condition')
        if meta_data.has_timestamp_info and meta_data.has_timelapse:
            column_names.append('Time-lapse')

        table.setColumnCount(len(column_names))
        table.setHorizontalHeaderLabels(column_names)
        table.setRowCount(len(meta_data.positions))

        for idx, pos in enumerate(meta_data.positions):
            item = QTableWidgetItem(pos)
            item.setData(0, pos)
            table.setItem(idx, 0, item)

            if 'Time-lapse' in column_names:
                column = column_names.index('Time-lapse')
                info = meta_data.get_timestamp_info(pos)
                info_str = '%.1fmin (%.1fs)' % (info[0] / 60, info[1])
                table.setItem(idx, column, QTableWidgetItem(info_str))

            if 'Well' in column_names:
                column = column_names.index('Well')
                well, subwell = meta_data.get_well_and_subwell(pos)
                if not well is None:
                    table.setItem(idx, column, QTableWidgetItem(well))
                if not subwell is None:
                    table.setItem(idx, column+1,
                                  QTableWidgetItem("%02d" % subwell))

        table.resizeColumnsToContents()
        table.resizeRowsToContents()
        table.blockSignals(False)

    def _update_info_frame(self, meta):

        txt  = '<table>' \
               '<tr><td align="right">Positions: </td><td>%s</td></tr>' \
               '<tr><td align="right">Frames: </td><td>%d</td></tr>' % \
               (meta.dim_p, meta.dim_t)
        txt += '<tr><td align="right">Channels: </td><td>%d (%s)</td></tr>' \
               '<tr><td align="right">Z-slices: </td><td>%d</td></tr>' \
               '<tr><td align="right">Width / Height: </td><td>%d x %d</td></tr>' \
               '<tr><td colspan="2"></td></tr>' \
               '<tr><td align="right">Image Files: </td><td>%d</td></tr>' % \
               (meta.dim_c, meta.pixel_type, meta.dim_z, meta.dim_x,
                meta.dim_y, meta.image_files)
        txt += '<tr><td></td></tr>'
        if meta.has_timestamp_info and meta.has_timelapse:
            info = meta.plate_timestamp_info
            txt += \
               '<tr><td align="right">Time-lapse info: </td><td>%.1f min (+/- %.1f s)</td></tr>' % \
               (info[0]/60, info[1])
        else:
            txt += '<tr><td align="right">Time-lapse info: </td><td>no</td></tr>'

        txt += '<tr><td align="right">Well info: </td><td>%s</td></tr>' % \
               yesno(meta.has_well_info)
        txt += '<tr><td align="right">Condition info: </td><td>%s</td></tr>' % \
               yesno(meta.has_condition_info)
        txt += '</table>'
        self._label_info.setText(txt)

    def initialize(self):
        self.coordinate_changed.connect(self.browser.on_coordinate_changed)
        coordinate = self.browser.get_coordinate()

        meta_data = self._imagecontainer.get_meta_data()
        self._update_position_table(meta_data)
        self._update_info_frame(meta_data)

        self._set_current_plate(coordinate.plate)
        self._set_current_position(coordinate.position)

        if self._imagecontainer.has_timelapse:
            self._update_time_table(meta_data, coordinate)
            self._set_current_time(coordinate.time)

    def nav_to_coordinate(self, coordinate):
        """
        Set the browser coordinate to a coordinate and emit signal
        """
        self._imagecontainer.set_plate(coordinate.plate)
        meta_data = self._imagecontainer.get_meta_data()
        self._set_current_plate(coordinate.plate)
        self._update_position_table(meta_data)
        self._set_current_position(coordinate.position)
        if self._imagecontainer.has_timelapse:
            self._update_time_table(meta_data, coordinate)
            self._set_current_time(coordinate.time)
        self._update_info_frame(meta_data)
        self.coordinate_changed.emit(coordinate)

    def nav_to_time(self, time):
        """
        Set the browser coordinate to a coordinate and emit signal
        """
        coordinate = self.browser.get_coordinate()
        coordinate.time = time
        self._set_time(coordinate, True)

    def nav_to_prev_position(self):
        coordinate = self.browser.get_coordinate()
        meta_data = self._imagecontainer.get_meta_data()
        pos = meta_data.positions
        idx = pos.index(coordinate.position)
        if idx > 0:
            coordinate.position = pos[idx-1]
            self._set_position(coordinate, True)

    def nav_to_next_position(self):
        coordinate = self.browser.get_coordinate()
        meta_data = self._imagecontainer.get_meta_data()
        pos = meta_data.positions
        idx = pos.index(coordinate.position)
        if idx < len(pos)-1:
            coordinate.position = pos[idx+1]
            self._set_position(coordinate, True)

    def nav_to_prev_plate(self):
        coordinate = self.browser.get_coordinate()
        plates = self._imagecontainer.plates
        idx = plates.index(coordinate.plate)
        if idx > 0:
            coordinate.plate = plates[idx-1]
            self._set_plate(coordinate, True)

    def nav_to_next_plate(self):
        coordinate = self.browser.get_coordinate()
        plates = self._imagecontainer.plates
        idx = plates.index(coordinate.plate)
        if idx < len(plates)-1:
            coordinate.plate = plates[idx+1]
            self._set_plate(coordinate, True)

    def _get_closeby_position(self, coordinate_old, coordinate_new):
        md_new = self._imagecontainer.get_meta_data()
        if coordinate_old.position in md_new.positions:
            coordinate_new.position = coordinate_old.position
        else:
            coordinate_new.position = md_new.positions[0]

    def _get_closeby_time(self, coordinate_old, coordinate_new):
        md_new = self._imagecontainer.get_meta_data()
        if coordinate_old.time in md_new.times:
            coordinate_new.time = coordinate_old.time
        else:
            coordinate_new.time = md_new.times[0]

    def _on_plate_changed(self, current, previous):
        coordinate_new = self.browser.get_coordinate()
        item = self._table_plate.item(current.row(), 0)
        plate = item.data(0)
        coordinate_new.plate = plate
        self._set_plate(coordinate_new)

    def _set_plate(self, coordinate_new, set_current=False):
        coordinate_old = self.browser.get_coordinate()
        plate = coordinate_new.plate
        func = lambda: self._imagecontainer.set_plate(plate)
        self.dlg = ProgressDialog("Loading plate...", None, 0, 0, self)
        self.dlg.exec_(func)

        meta_data = self._imagecontainer.get_meta_data()
        if set_current:
            self._set_current_plate(plate)
        self._update_position_table(meta_data)
        self._get_closeby_position(coordinate_old, coordinate_new)
        self._set_current_position(coordinate_new.position)
        if self._imagecontainer.has_timelapse:
            self._update_time_table(meta_data, coordinate_new)
            self._get_closeby_time(coordinate_old, coordinate_new)
            self._set_current_time(coordinate_new.time)
        self._update_info_frame(meta_data)
        self.coordinate_changed.emit(coordinate_new)

    def _on_position_changed(self, current, previous):
        coordinate = self.browser.get_coordinate()
        item = self._table_position.item(current.row(), 0)
        position = item.data(0)
        coordinate.position = position
        self._set_position(coordinate)

    def _set_position(self, coordinate, set_current=False):
        if set_current:
            self._set_current_position(coordinate.position)
        if self._imagecontainer.has_timelapse:
            meta_data = self._imagecontainer.get_meta_data()
            self._update_time_table(meta_data, coordinate)
            self._set_current_time(coordinate.time)
        self.coordinate_changed.emit(coordinate)

    def _on_time_changed(self, current, previous):
        coordinate = self.browser.get_coordinate()
        item = self._table_time.item(current.row(), 0)
        time = int(item.data(0))
        coordinate.time = time
        self._set_time(coordinate)

    def _set_time(self, coordinate, set_current=False):
        if set_current:
            self._set_current_time(coordinate.time)
        self.coordinate_changed.emit(coordinate)

    def _set_current_plate(self, plate):
        self._table_plate.blockSignals(True)
        item = self._table_plate.findItems(plate, Qt.MatchExactly)[0]
        self._table_plate.setCurrentItem(item)
        self._table_plate.blockSignals(False)
        self._table_plate.scrollToItem(item)
        self._table_plate.update()

    def _set_current_position(self, position):
        self._table_position.blockSignals(True)
        item = self._table_position.findItems(position, Qt.MatchExactly)[0]
        self._table_position.setCurrentItem(item)
        self._table_position.blockSignals(False)
        self._table_position.scrollToItem(item)
        self._table_position.update()

    def _set_current_time(self, time):
        if self._imagecontainer.has_timelapse:
            self._table_time.blockSignals(True)
            item = self._table_time.findItems(str(time), Qt.MatchExactly)[0]
            self._table_time.setCurrentItem(item)
            self._table_time.blockSignals(False)
            self._table_time.scrollToItem(item)
            self._table_time.update()
コード例 #36
0
ファイル: main.py プロジェクト: xiwei-zhang/cecog
    def _load_image_container(self, plate_infos=None, scan_plates=None,
                              show_dialog=True):
        self._clear_browser()

        if plate_infos is None:
            plate_infos = list(ImageContainer.iter_check_plates(self._settings))

        imagecontainer = ImageContainer()
        self._imagecontainer = imagecontainer

        if scan_plates is None:
            scan_plates = dict((info[0], False) for info in plate_infos)

        def load(emitter, icontainer, settings, splates):
            iter_ = icontainer.iter_import_from_settings(settings, scan_plates=splates)
            for idx, info in enumerate(iter_):
                emitter.setValue.emit(idx)

            emitter.setLabelText.emit("checking dimensions...")
            emitter.setRange.emit(0, 0)
            QtCore.QCoreApplication.processEvents()

            if len(icontainer.plates) > 0:
                icontainer.set_plate(icontainer.plates[0])
                icontainer.check_dimensions()


        label = ('Please wait until the input structure is scanned\n'
                 'or the structure data loaded...')
        self._dlg = ProgressDialog(label, None, 0, len(scan_plates), self)
        emitter = ProgressObject()
        emitter.setRange.connect(self._dlg.setRange)
        emitter.setValue.connect(self._dlg.setValue)
        emitter.setLabelText.connect(self._dlg.setLabelText)

        try:
            func = lambda: load(emitter, imagecontainer,
                                self._settings, scan_plates)
            self._dlg.exec_(func, (emitter, ))
        except ImportError as e:
            # structure file from versions older than 1.3 contain pdk which is
            # removed
            if 'pdk' in str(e):
                QMessageBox.critical(self, "Error",
                                     ("Your structure file format is outdated.\n"
                                      "You have to rescan the plate(s)"))
            else:
                QMessageBox.critical(self, "Error", traceback.format_exc())
            return
        except Exception as e:
            QMessageBox.critical(self, "Error", str(e))


        try: # I hate lookup tables!
            self._tab_lookup['Cluster'][1].set_imagecontainer(imagecontainer)
        except KeyError:
            pass

        if len(imagecontainer.plates) > 0:
            channels = imagecontainer.channels

            # do not report value changes to the main window
            self._settings.set_notify_change(False)

            self.set_image_crop_size()

            problems = []
            for prefix in ['primary', 'secondary', 'tertiary']:
                trait = self._settings.get_trait(SECTION_NAME_OBJECTDETECTION,
                                                 '%s_channelid' % prefix)
                if trait.set_list_data(channels) is None:
                    problems.append(prefix)
                self._tabs[1].get_widget('%s_channelid' % prefix).update()

            # report problems about a mismatch between channel IDs found in the data
            # and specified by the user
            if len(problems) > 0:
                # a mismatch between settings and data will cause changed settings
                self.settings_changed(True)

            trait = self._settings.get_trait(SECTION_NAME_EVENT_SELECTION,
                                             'duration_unit')

            # allow time-base tracking durations only if time-stamp
            # information is present
            meta_data = imagecontainer.get_meta_data()
            if meta_data.has_timestamp_info:
                result = trait.set_list_data(TimeConverter.units)
            else:
                result = trait.set_list_data([TimeConverter.FRAMES])
            if result is None:
                QMessageBox.critical(self, "Could not set tracking duration units",
                                     ("The tracking duration units selected to match the "
                                      "load data. Please check your settings."))
                # a mismatch between settings and data will cause changed settings
                self.settings_changed(True)

            # activate change notification again
            self._settings.set_notify_change(True)


            self.set_modules_active(state=True)
            if show_dialog:
                QMessageBox.information(
                    self, "Information",
                    "%d plate(s) successfully loaded." % len(imagecontainer.plates))
        else:
            QMessageBox.critical(self, "Error",
                                 ("No images found\n"
                                  "Verifiy your nameing scheme and rescan the data."))