Beispiel #1
0
 def preselect(self):
     progress = QProgressDialog('', None, 0, 100, self)
     try:
         self.project.preselect(progress)
     except BaseException as e:
         progress.close()
         raise e
Beispiel #2
0
 def openProject(self, filename=None):
     if not filename:
         (filename, ftype) = QFileDialog.getOpenFileName(
             self,
             "Choose project file",
             dir=self.lastProjectDirectory,
             filter="Projects (*.prj)",
             options=QFileDialog.DontResolveSymlinks
             | QFileDialog.HideNameFilterDetails)
     if filename:
         self.lastProjectDirectory = str(Path(filename).parent)
         self.createProject()
         progress = QProgressDialog('', None, 0, 100, self)
         try:
             self.project.load(filename, progress)
             self.lastProjectFile = filename
             if filename in self.recentProjectFiles:
                 self.recentProjectFiles.remove(filename)
             self.recentProjectFiles.insert(0, filename)
             self.updateRecentMenu()
             self.updateShaderMenu()
             self.editor.setProject(self.project)
             self.saveSettings()
         except BaseException as e:
             progress.close()
             raise e
Beispiel #3
0
 def action_compare_files(self):
     before_file_path, _ = QFileDialog.getOpenFileName(
         self,
         'Open Before Apk',
         filter='Apk(*.apk)',
         options=QFileDialog.DontUseNativeDialog)
     if not before_file_path:
         return
     after_file_path, _ = QFileDialog.getOpenFileName(
         self,
         'Open After Apk',
         filter='Apk(*.apk)',
         options=QFileDialog.DontUseNativeDialog)
     if not after_file_path:
         return
     progress_dialog = QProgressDialog('Compare Apks...', '', 0, 100, self)
     progress_dialog.setCancelButton(None)
     progress_dialog.setWindowModality(Qt.WindowModal)
     progress_dialog.setWindowFlags(Qt.Window | Qt.WindowTitleHint
                                    | Qt.CustomizeWindowHint)
     progress_dialog.show()
     try:
         self.action_presenter.compare_files(
             before_file_path, after_file_path,
             lambda value: progress_dialog.setValue(value * 100))
     except:  # noqa: E722
         progress_dialog.close()
         self.open_error_dialog()
Beispiel #4
0
    def prepare(self):
        modelfile = 'models/median_b{}.mdl'.format(self.block)
        try:
            model = load(modelfile)
        except FileNotFoundError:
            QMessageBox.critical(
                self, self.tr('Error'),
                self.tr('Model not found ("{}")!'.format(modelfile)))
            return
        limit = model.best_ntree_limit if hasattr(model,
                                                  'best_ntree_limit') else None
        columns = model._features_count
        if columns == 8:
            levels = 1
            windows = 1
        elif columns == 24:
            levels = 3
            windows = 1
        elif columns == 96:
            levels = 3
            windows = 4
        elif columns == 128:
            levels = 4
            windows = 4
        else:
            QMessageBox.critical(self, self.tr('Error'),
                                 self.tr('Unknown model format!'))
            return

        padded = pad_image(self.gray, self.block)
        rows, cols = padded.shape
        self.prob = np.zeros(
            ((rows // self.block) + 1, (cols // self.block) + 1))
        self.var = np.zeros_like(self.prob)
        progress = QProgressDialog(self.tr('Detecting median filter...'),
                                   self.tr('Cancel'), 0, self.prob.size, self)
        progress.canceled.connect(self.cancel)
        progress.setWindowModality(Qt.WindowModal)
        k = 0
        self.canceled = False
        for i in range(0, rows, self.block):
            for j in range(0, cols, self.block):
                roi = padded[i:i + self.block, j:j + self.block]
                x = np.reshape(get_features(roi, levels, windows),
                               (1, columns))
                y = model.predict_proba(x, ntree_limit=limit)[0, 1]
                ib = i // self.block
                jb = j // self.block
                self.var[ib, jb] = np.var(roi)
                self.prob[ib, jb] = y
                if self.canceled:
                    self.prob = self.var = None
                    progress.close()
                    return
                progress.setValue(k)
                k += 1
        progress.close()
        self.process()
Beispiel #5
0
 def exportViews(self):
     filename, ok = QInputDialog.getText(self, "View Image Filename",
                                         "Png filename")
     if ok and len(filename) > 0:
         progress = QProgressDialog('', None, 0, 100, self)
         try:
             self.project.exportviews(self.view.context(), filename,
                                      progress)
         except BaseException as e:
             progress.close()
             raise e
Beispiel #6
0
 def exportSelection(self):
     (filename, ftype) = QFileDialog.getSaveFileName(
         self,
         "Choose ply file",
         dir=self.lastProjectDirectory,
         filter="Meshes (*.ply)",
         options=QFileDialog.DontResolveSymlinks
         | QFileDialog.HideNameFilterDetails)
     if filename:
         if not filename.endswith('.ply'):
             filename += '.ply'
         progress = QProgressDialog('', None, 0, 100, self)
         try:
             self.project.exportply(filename, progress)
         except BaseException as e:
             progress.close()
             raise e
Beispiel #7
0
 def importMVEScene(self):
     (filename, ftype) = QFileDialog.getOpenFileName(
         self,
         "Choose MVE scene",
         dir=self.lastImportDirectory,
         filter="MVE Scene (synth_0.out)",
         options=QFileDialog.DontResolveSymlinks)
     if filename:
         directory = str(Path(filename).parent)
         self.lastImportDirectory = directory
         progress = QProgressDialog('', None, 0, 100, self)
         try:
             self.project.importmve(directory, progress)
             self.view.update()
             self.saveSettings()
         except BaseException as e:
             progress.close()
             raise e
class DialogProgressMonitor(Monitor):
    def __init__(self, label_text: str, parent: QWidget):
        self._label_text = label_text
        self._parent = parent
        self._dialog: Optional[QProgressDialog] = None

    def start(self, total: int) -> None:
        self._dialog = QProgressDialog(self._label_text, "Cancel", 0, total,
                                       self._parent)
        self._dialog.show()

    def progress(self, value: int) -> None:
        self._dialog.setValue(value)
        QApplication.instance().processEvents()
        if self._dialog.wasCanceled():
            raise CancelException

    def done(self) -> None:
        self._dialog.close()
Beispiel #9
0
    def __init__(self, soundfile, parent):
        self.soundfile = soundfile
        self.isplaying = False
        self.time = 0  # current audio position in frames
        self.audio = QMediaPlayer()
        self.decoder = QAudioDecoder()
        self.is_loaded = False
        self.volume = 100
        self.isplaying = False
        self.decoded_audio = {}
        self.only_samples = []
        self.decoding_is_finished = False
        self.max_bits = 32768
        self.signed = False
        # File Loading is Asynchronous, so we need to be creative here, doesn't need to be duration but it works
        self.audio.durationChanged.connect(self.on_durationChanged)
        self.decoder.finished.connect(self.decode_finished_signal)
        self.audio.setMedia(QUrl.fromLocalFile(soundfile))
        self.decoder.setSourceFilename(
            soundfile)  # strangely inconsistent file-handling
        progress = QProgressDialog("Loading Audio", "Cancel", 0, 0)
        progress.setWindowTitle("Progress")
        progress.setModal(True)
        progress.forceShow()
        # It will hang here forever if we don't process the events.
        while not self.is_loaded:
            QCoreApplication.processEvents()
            time.sleep(0.1)

        self.decode_audio()

        self.np_data = np.array(self.only_samples)
        if not self.signed:  # don't ask me why this fixes 8 bit samples...
            self.np_data = self.np_data - self.max_bits / 2
        print(len(self.only_samples))
        print(self.max_bits)
        progress.close()
        self.isvalid = True
Beispiel #10
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__(None)
        self.setWindowTitle("Feel the streets")
        self.setCentralWidget(QWidget(self))
        self._download_progress_dialog = None
        self._downloader = None
        self._progress = None
        self._pending_count = 0
        self._applier = None
        self._maybe_show_message()
        self._do_select_db()

    def _maybe_show_message(self):
        motd = get_motd()
        if motd and motd.is_newer_than_last_local:
            message_dialog = MessageDialog(self, motd)
            res = message_dialog.exec_()
            if res == MessageDialog.DialogCode.Rejected:
                sys.exit(0)
            elif res == MessageDialog.DialogCode.Accepted:
                motd.mark_as_seen()

    def _do_select_db(self):
        dlg = AreaSelectionDialog(self)
        res = dlg.exec_()
        if res == QDialog.DialogCode.Rejected:
            sys.exit(0)
        elif res == QDialog.DialogCode.Accepted:
            self._selected_map = dlg.selected_map
            self._selected_map_name = dlg.selected_map_name
            if not os.path.exists(
                    AreaDatabase.path_for(dlg.selected_map,
                                          server_side=False)):
                self._download_database(dlg.selected_map)
            else:
                self._update_database(dlg.selected_map)

    def _on_map_ready(self):
        self.setWindowTitle(f"{self._selected_map_name} - Feel the streets")
        if is_frozen_area(self._selected_map):
            original_id = self._selected_map - FROZEN_AREA_OSM_ID_OFFSET
            area_name = get_area_names_cache()[original_id]
        else:
            area_name = self._selected_map_name
        map.set_call_args(self._selected_map, area_name)
        menu_service.set_call_args(self)
        self._app_controller = ApplicationController(self)
        person = Person(map=map(), position=LatLon(0, 0))
        self._person_controller = InteractivePersonController(person, self)
        self._interesting_entities_controller = InterestingEntitiesController(
            person)
        self._sound_controller = SoundController(person)
        self._announcements_controller = AnnouncementsController(person)
        self._last_location_controller = LastLocationController(person)
        self._restriction_controller = MovementRestrictionController(person)
        self._speech_controller = SpeechController()
        self._adjustment_controller = PositionAdjustmentController(person)
        if not self._last_location_controller.restored_position:
            person.move_to(map().default_start_location, force=True)
        self.raise_()
        menu_service().ensure_key_capturer_focus()

    def _download_progress_callback(self, total, so_far):
        if not self._download_progress_dialog:
            self._download_progress_dialog = QProgressDialog(
                _("Downloading the selected database."), None, 0, 100, self)
            self._download_progress_dialog.setWindowTitle(
                _("Download in progress"))
        percentage = int((so_far / total) * 100)
        self._download_progress_dialog.setLabelText(
            _("Downloading the selected database. Downloaded {so_far} of {total}."
              ).format(so_far=format_size(so_far), total=format_size(total)))
        self._download_progress_dialog.setValue(percentage)

    def _download_database(self, area):
        self._downloader = AreaDatabaseDownloader(area, None)
        self._area = area
        self._downloader.moveToThread(QApplication.instance().thread())
        self._downloader.download_progressed.connect(
            self._download_progress_callback)
        self._downloader.download_finished.connect(self._on_download_finished)
        self._downloader.start()

    def _on_download_finished(self, res):
        if not res:
            QMessageBox.warning(self, _("Download failure"),
                                _("Download of the selected area had failed."))
            self.close()
            os.remove(AreaDatabase.path_for(self._area, server_side=False))
        else:
            self._on_map_ready()

    def _update_database(self, area):
        if area > FROZEN_AREA_OSM_ID_OFFSET or not has_api_connectivity():
            self._on_map_ready()
            return
        try:
            retriever = SemanticChangeRetriever()
            self._pending_count = retriever.new_change_count_in(area)
        except ConnectionError:
            QMessageBox.warning(
                self, _("Warning"),
                _("Could not retrieve changes in the selected area, using the potentially stale local copy."
                  ))
            self._on_map_ready()
            return
        except UnknownQueueError:
            QMessageBox.warning(
                self, _("Warning"),
                _("The changes queue for the selected database no longer exists on the server, downloading it as if it was a new area."
                  ))
            self._download_database(area)
            return
        if not self._pending_count:
            self._on_map_ready()
            return
        generate_changelog = config().changelogs.enabled
        if self._pending_count > 10000 and generate_changelog:
            resp = QMessageBox.question(
                self, _("Question"),
                _("The server reports %s pending changes. Do you really want to generate the changelog from all of them? It might take a while."
                  ) % self._pending_count)
            if resp == QMessageBox.StandardButton.Yes:
                generate_changelog = False
        self._progress = QProgressDialog(
            _("Applying changes for the selected database."), None, 0,
            self._pending_count, self)
        self._progress.setWindowTitle(_("Change application"))
        self._applier = ChangesApplier(area, retriever, generate_changelog)
        self._applier.will_process_change.connect(self._on_will_process_change)
        self._applier.changes_applied.connect(self._on_changes_applied)
        self._applier.redownload_requested.connect(
            self._on_redownload_requested)
        self._applier.start()

    def _on_redownload_requested(self):
        self._progress.close()
        self._progress = None
        QMessageBox.information(
            self, _("Redownload requested"),
            _("The server has requested a redownload of the selected database, proceeding with the operation."
              ))
        self._download_database(self._selected_map)

    def _on_will_process_change(self, nth):
        self._progress.setLabelText(
            _("Applying changes for the selected database, change {nth} of {total}"
              ).format(nth=nth, total=self._pending_count))
        self._progress.setValue(nth)

    def _on_changes_applied(self, changelog_path):
        self._applier.deleteLater()
        if changelog_path:
            changelog_size = os.path.getsize(changelog_path)
            resp = QMessageBox.question(
                self,
                _("Success"),
                _("Successfully applied {total} changes. A changelog of size {size} was generated, do you want to view it now?"
                  ).format(total=self._pending_count,
                           size=format_size(changelog_size)),
                defaultButton=QMessageBox.No)
            if resp == QMessageBox.StandardButton.Yes:
                # Somewhat hacky, but os.startfile is not cross platform and the webbrowser way appears to be.
                webbrowser.open(changelog_path)
        else:
            QMessageBox.information(
                self, _("Success"),
                _("Successfully applied {total} changes.").format(
                    total=self._pending_count))
        self._on_map_ready()
Beispiel #11
0
    def load_projectindex(self, project_idx_file):
        index_loaded = False
        stats_loaded = False
        if project_idx_file != "":
            if os.path.exists(project_idx_file):
                try:
                    self.parent().status.showMessage(
                        f"Loading {project_idx_file}")
                    progress = QProgressDialog("Loading index file...",
                                               "Cancel", 0, 3, self)
                    progress.setWindowTitle("Loading study index")
                    progress.setWindowModality(Qt.WindowModal)
                    progress.setMinimumWidth(300)
                    progress.setMinimumDuration(0)
                    progress.forceShow()
                    progress.setValue(0)
                    wb = load_workbook(project_idx_file, read_only=True)
                    progress.setValue(1)
                    ws = wb['Index']
                    ws.reset_dimensions()
                    data = ws.values
                    cols = next(data)
                    data = list(data)
                    progress.setValue(2)
                    self.data_index = pd.DataFrame(data,
                                                   columns=cols).fillna("")
                    progress.setValue(3)
                    progress.close()
                    # Parse index to the tree
                    num_ecgs = 0
                    if "EGREFID" in self.data_index.columns:
                        num_ecgs = self.data_index[[
                            "ZIPFILE", "AECGXML", "EGREFID", "WFTYPE"
                        ]].drop_duplicates().shape[0]
                        progress = QProgressDialog("Parsing index ...",
                                                   "Cancel", 0, num_ecgs, self)
                        progress.setWindowTitle("Loading study index")
                        progress.setLabelText("Parsing index...")
                        progress.setWindowModality(Qt.WindowModal)
                        progress.setMinimumWidth(300)
                        progress.setMinimumDuration(0)
                        progress.forceShow()
                        progress.setValue(0)
                        self.projectmodel = ProjectTreeModel(
                            self.data_index, progress_dialog=progress)
                        self.project_treeview.setModel(self.projectmodel)
                        self.project_treeview.selectionModel().\
                            selectionChanged.connect(self.load_data)
                        progress.close()
                    else:
                        QMessageBox.warning(
                            self, "EGREFID missing",
                            f"EGREFID column missing Index sheet of "
                            f"{project_idx_file}")
                    self.project_loaded = project_idx_file

                    # Reset aECG display
                    self.tabdisplays.aecg_display.aecg_data = None
                    self.tabdisplays.aecg_display.plot_aecg()

                    # Populate study information/validator tab
                    self.tabdisplays.load_study_info(project_idx_file)
                    self.data_index_info = self.tabdisplays.studyindex_info

                    index_loaded = True
                    try:
                        progress = QProgressDialog(
                            "Loading study index stats...", "Cancel", 0, 3,
                            self)
                        progress.setWindowTitle("Loading study index stats")
                        progress.setWindowModality(Qt.WindowModal)
                        progress.setMinimumWidth(300)
                        progress.setMinimumDuration(0)
                        progress.forceShow()
                        progress.setValue(0)
                        ws = wb['Stats']
                        ws.reset_dimensions()
                        data = ws.values
                        cols = next(data)
                        data = list(data)
                        progress.setValue(1)
                        progress.forceShow()
                        statsdf = pd.DataFrame(data, columns=cols).fillna("")
                        progress.setValue(2)
                        progress.forceShow()
                        self.data_index_stats = aecg.tools.indexer.StudyStats()
                        self.data_index_stats.__dict__.update(
                            statsdf.set_index("Property").transpose().
                            reset_index(drop=True).to_dict('index')[0])
                        progress.setValue(3)
                        progress.forceShow()
                        progress.close()
                        stats_loaded = True
                    except Exception as ex:
                        QMessageBox.warning(
                            self, f"Error loading study index stats",
                            f"Error loading study index stats from"
                            f"{project_idx_file}\n{str(ex)}")
                except Exception as ex:
                    QMessageBox.warning(
                        self, f"Error loading study index",
                        f"Error loading study index from {project_idx_file}"
                        f"\n{str(ex)}")
            else:
                QMessageBox.warning(self, f"Study index file not found",
                                    f"{project_idx_file} not found")

            if index_loaded:
                self.projectindex_loaded.emit()
                if stats_loaded:
                    self.projectindexstats_loaded.emit()
            self.parentWidget().status.clearMessage()
Beispiel #12
0
    def process(self):
        start = time()
        self.canceled = False
        self.status_label.setText(self.tr('Processing, please wait...'))
        algorithm = self.detector_combo.currentIndex()
        response = 100 - self.response_spin.value()
        matching = self.matching_spin.value() / 100 * 255
        distance = self.distance_spin.value() / 100
        cluster = self.cluster_spin.value()
        modify_font(self.status_label, bold=False, italic=True)
        QCoreApplication.processEvents()

        if self.kpts is None:
            if algorithm == 0:
                detector = cv.BRISK_create()
            elif algorithm == 1:
                detector = cv.ORB_create()
            elif algorithm == 2:
                detector = cv.AKAZE_create()
            else:
                return
            mask = self.mask if self.onoff_button.isChecked() else None
            self.kpts, self.desc = detector.detectAndCompute(self.gray, mask)
            self.total = len(self.kpts)
            responses = np.array([k.response for k in self.kpts])
            strongest = (cv.normalize(responses, None, 0, 100, cv.NORM_MINMAX)
                         >= response).flatten()
            self.kpts = list(compress(self.kpts, strongest))
            if len(self.kpts) > 30000:
                QMessageBox.warning(
                    self, self.tr('Warning'),
                    self.
                    tr('Too many keypoints found ({}), please reduce response value'
                       .format(self.total)))
                self.kpts = self.desc = None
                self.total = 0
                self.status_label.setText('')
                return
            self.desc = self.desc[strongest]

        if self.matches is None:
            matcher = cv.BFMatcher_create(cv.NORM_HAMMING, True)
            self.matches = matcher.radiusMatch(self.desc, self.desc, matching)
            if self.matches is None:
                self.status_label.setText(
                    self.tr('No keypoint match found with current settings'))
                modify_font(self.status_label, italic=False, bold=True)
                return
            self.matches = [
                item for sublist in self.matches for item in sublist
            ]
            self.matches = [
                m for m in self.matches if m.queryIdx != m.trainIdx
            ]

        if not self.matches:
            self.clusters = []
        elif self.clusters is None:
            self.clusters = []
            min_dist = distance * np.min(self.gray.shape) / 2
            kpts_a = np.array([p.pt for p in self.kpts])
            ds = np.linalg.norm([
                kpts_a[m.queryIdx] - kpts_a[m.trainIdx] for m in self.matches
            ],
                                axis=1)
            self.matches = [
                m for i, m in enumerate(self.matches) if ds[i] > min_dist
            ]

            total = len(self.matches)
            progress = QProgressDialog(self.tr('Clustering matches...'),
                                       self.tr('Cancel'), 0, total, self)
            progress.canceled.connect(self.cancel)
            progress.setWindowModality(Qt.WindowModal)
            for i in range(total):
                match0 = self.matches[i]
                d0 = ds[i]
                query0 = match0.queryIdx
                train0 = match0.trainIdx
                group = [match0]

                for j in range(i + 1, total):
                    match1 = self.matches[j]
                    query1 = match1.queryIdx
                    train1 = match1.trainIdx
                    if query1 == train0 and train1 == query0:
                        continue
                    d1 = ds[j]
                    if np.abs(d0 - d1) > min_dist:
                        continue

                    a0 = np.array(self.kpts[query0].pt)
                    b0 = np.array(self.kpts[train0].pt)
                    a1 = np.array(self.kpts[query1].pt)
                    b1 = np.array(self.kpts[train1].pt)

                    aa = np.linalg.norm(a0 - a1)
                    bb = np.linalg.norm(b0 - b1)
                    ab = np.linalg.norm(a0 - b1)
                    ba = np.linalg.norm(b0 - a1)

                    if not (0 < aa < min_dist and 0 < bb < min_dist
                            or 0 < ab < min_dist and 0 < ba < min_dist):
                        continue
                    for g in group:
                        if g.queryIdx == train1 and g.trainIdx == query1:
                            break
                    else:
                        group.append(match1)

                if len(group) >= cluster:
                    self.clusters.append(group)
                progress.setValue(i)
                if self.canceled:
                    self.update_detector()
                    return
            progress.close()

        output = np.copy(self.image)
        hsv = np.zeros((1, 1, 3))
        nolines = self.nolines_check.isChecked()
        show_kpts = self.kpts_check.isChecked()

        if show_kpts:
            for kpt in self.kpts:
                cv.circle(output, (int(kpt.pt[0]), int(kpt.pt[1])), 2,
                          (250, 227, 72))

        angles = []
        for c in self.clusters:
            for m in c:
                ka = self.kpts[m.queryIdx]
                pa = tuple(map(int, ka.pt))
                sa = int(np.round(ka.size))
                kb = self.kpts[m.trainIdx]
                pb = tuple(map(int, kb.pt))
                sb = int(np.round(kb.size))
                angle = np.arctan2(pb[1] - pa[1], pb[0] - pa[0])
                if angle < 0:
                    angle += np.pi
                angles.append(angle)
                hsv[0, 0, 0] = angle / np.pi * 180
                hsv[0, 0, 1] = 255
                hsv[0, 0, 2] = m.distance / matching * 255
                rgb = cv.cvtColor(hsv.astype(np.uint8), cv.COLOR_HSV2BGR)
                rgb = tuple([int(x) for x in rgb[0, 0]])
                cv.circle(output, pa, sa, rgb, 1, cv.LINE_AA)
                cv.circle(output, pb, sb, rgb, 1, cv.LINE_AA)
                if not nolines:
                    cv.line(output, pa, pb, rgb, 1, cv.LINE_AA)

        regions = 0
        if angles:
            angles = np.reshape(np.array(angles, dtype=np.float32),
                                (len(angles), 1))
            if np.std(angles) < 0.1:
                regions = 1
            else:
                criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER,
                            10, 1.0)
                attempts = 10
                flags = cv.KMEANS_PP_CENTERS
                compact = [
                    cv.kmeans(angles, k, None, criteria, attempts, flags)[0]
                    for k in range(1, 11)
                ]
                compact = cv.normalize(np.array(compact), None, 0, 1,
                                       cv.NORM_MINMAX)
                regions = np.argmax(compact < 0.005) + 1
        self.viewer.update_processed(output)
        self.process_button.setEnabled(False)
        modify_font(self.status_label, italic=False, bold=True)
        self.status_label.setText(
            self.
            tr('Keypoints: {} --> Filtered: {} --> Matches: {} --> Clusters: {} --> Regions: {}'
               .format(self.total, len(self.kpts), len(self.matches),
                       len(self.clusters), regions)))
        self.info_message.emit(
            self.tr('Copy-Move Forgery = {}'.format(elapsed_time(start))))
Beispiel #13
0
class MainWindow(QMainWindow):
    """
    The main window of angr management.
    """
    def __init__(self,
                 app: Optional['QApplication'] = None,
                 parent=None,
                 show=True,
                 use_daemon=False):
        super().__init__(parent)

        icon_location = os.path.join(IMG_LOCATION, 'angr.png')
        self.setWindowIcon(QIcon(icon_location))

        GlobalInfo.main_window = self

        # initialization
        self.setMinimumSize(QSize(400, 400))
        self.setDockNestingEnabled(True)

        self.app: Optional['QApplication'] = app
        self.workspace: Workspace = None
        self.central_widget: QMainWindow = None

        self.toolbar_manager: ToolbarManager = ToolbarManager(self)
        self._progressbar = None  # type: QProgressBar
        self._progress_dialog = None  # type: QProgressDialog
        self._load_binary_dialog = None

        self.defaultWindowFlags = None

        # menus
        self._file_menu = None  # FileMenu
        self._analyze_menu = None
        self._view_menu = None
        self._help_menu = None
        self._plugin_menu = None

        self._init_statusbar()
        self._init_workspace()
        self._init_toolbars()
        self._init_menus()
        self._init_plugins()
        self._init_library_docs()
        self._init_url_scheme_handler()

        self.workspace.plugins.on_workspace_initialized(self)

        self._init_shortcuts()
        self._init_flirt_signatures()

        self._run_daemon(use_daemon=use_daemon)

        # I'm ready to show off!
        if show:
            self.showMaximized()
            self.windowHandle().screenChanged.connect(self.on_screen_changed)
            self.show()

        self.status = "Ready."

    def sizeHint(self, *args, **kwargs):  # pylint: disable=unused-argument,no-self-use
        return QSize(1200, 800)

    #
    # Properties
    #

    @property
    def caption(self):
        return self.getWindowTitle()

    @caption.setter
    def caption(self, v):
        self.setWindowTitle(v)

    #
    # Dialogs
    #

    def open_mainfile_dialog(self):
        # pylint: disable=assigning-non-slot
        # https://github.com/PyCQA/pylint/issues/3793
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "Open a binary",
            Conf.last_used_directory,
            "All executables (*);;"
            "Windows PE files (*.exe);;"
            "Core Dumps (*.core);;"
            "angr database (*.adb)",
        )
        Conf.last_used_directory = os.path.dirname(file_path)
        return file_path

    def _pick_image_dialog(self):
        try:
            prompt = LoadDockerPrompt(parent=self)
        except LoadDockerPromptError:
            return None
        if prompt.exec_() == 0:
            return None  # User canceled
        return prompt.textValue()

    def open_load_plugins_dialog(self):
        dlg = LoadPlugins(self.workspace.plugins)
        dlg.setModal(True)
        dlg.exec_()

    def open_newstate_dialog(self):
        if self.workspace.instance.project.am_none:
            QMessageBox.critical(self, "Cannot create new states",
                                 "Please open a binary to analyze first.")
            return
        new_state_dialog = NewState(self.workspace.instance,
                                    parent=self,
                                    create_simgr=True)
        new_state_dialog.exec_()

    def open_doc_link(self):
        QDesktopServices.openUrl(
            QUrl("https://docs.angr.io/", QUrl.TolerantMode))

    def open_about_dialog(self):
        dlg = LoadAboutDialog()
        dlg.exec_()

    #
    # Widgets
    #

    def _init_statusbar(self):
        self._progressbar = QProgressBar()

        self._progressbar.setMinimum(0)
        self._progressbar.setMaximum(100)
        self._progressbar.hide()

        self.statusBar().addPermanentWidget(self._progressbar)

        self._progress_dialog = QProgressDialog("Waiting...", "Cancel", 0, 100,
                                                self)
        self._progress_dialog.setAutoClose(False)
        self._progress_dialog.setWindowFlags(
            self._progress_dialog.windowFlags()
            & ~Qt.WindowContextHelpButtonHint)
        self._progress_dialog.setModal(True)
        self._progress_dialog.setMinimumDuration(2**31 - 1)

        def on_cancel():
            if self.workspace is None:
                return
            for job in self.workspace.instance.jobs:
                if job.blocking:
                    job.keyboard_interrupt()
                    break

        self._progress_dialog.canceled.connect(on_cancel)
        self._progress_dialog.close()

    def _init_toolbars(self):
        for cls in (FileToolbar, DebugToolbar):
            self.toolbar_manager.show_toolbar_by_class(cls)

    #
    # Menus
    #

    def _init_menus(self):
        self._file_menu = FileMenu(self)
        self._analyze_menu = AnalyzeMenu(self)
        self._view_menu = ViewMenu(self)
        self._help_menu = HelpMenu(self)
        self._plugin_menu = PluginMenu(self)

        for path in Conf.recent_files:
            self._file_menu.add_recent(path)

        # TODO: Eventually fix menu bars to have native support on MacOS
        # if on a Mac, don't use the native menu bar (bug mitigation from QT)
        if sys.platform == 'darwin':
            self.menuBar().setNativeMenuBar(False)

        self.menuBar().addMenu(self._file_menu.qmenu())
        self.menuBar().addMenu(self._view_menu.qmenu())
        self.menuBar().addMenu(self._analyze_menu.qmenu())
        self.menuBar().addMenu(self._plugin_menu.qmenu())
        self.menuBar().addMenu(self._help_menu.qmenu())

    #
    # Workspace
    #

    def _init_workspace(self):
        """
        Initialize workspace

        :return:    None
        """
        self.central_widget = QMainWindow()
        self.setCentralWidget(self.central_widget)
        wk = Workspace(self, Instance())
        self.workspace = wk
        self.workspace.view_manager.tabify_center_views()
        self.central_widget.setTabPosition(Qt.RightDockWidgetArea,
                                           QTabWidget.North)
        self.central_widget.setDockNestingEnabled(True)

        def set_caption(**kwargs):  # pylint: disable=unused-argument
            if self.workspace.instance.project.am_none:
                self.caption = ''
            elif self.workspace.instance.project.filename is None:
                self.caption = "Loaded from stream"
            else:
                self.caption = os.path.basename(
                    self.workspace.instance.project.filename)

        self.workspace.instance.project.am_subscribe(set_caption)

        self.tab = self.central_widget.findChild(QTabBar)
        self.tab.tabBarClicked.connect(self.on_center_tab_clicked)

    #
    # Shortcuts
    #

    def interrupt_current_job(self):
        self.workspace.instance.interrupt_current_job()

    def _init_shortcuts(self):
        """
        Initialize shortcuts

        :return:    None
        """

        center_dockable_views = self.workspace.view_manager.get_center_views()
        for i in range(1, len(center_dockable_views) + 1):
            QShortcut(QKeySequence('Ctrl+' + str(i)), self,
                      center_dockable_views[i - 1].raise_)

        QShortcut(QKeySequence("Ctrl+I"), self, self.interrupt_current_job)

        # Raise the DisassemblyView after everything has initialized
        center_dockable_views[0].raise_()

        # Toggle exec breakpoint
        QShortcut(QKeySequence(Qt.Key_F2), self,
                  self.workspace.toggle_exec_breakpoint)

        # Single step
        QShortcut(QKeySequence(Qt.Key_F7), self, self.workspace.step_forward)

        # Run
        QShortcut(QKeySequence(Qt.Key_F9), self,
                  self.workspace.continue_forward)

    #
    # Plugins
    #

    def _init_plugins(self):
        self.workspace.plugins.discover_and_initialize_plugins()

    #
    # FLIRT Signatures
    #

    def _init_flirt_signatures(self):
        if Conf.flirt_signatures_root:
            # if it's a relative path, it's relative to the angr-management package
            if os.path.isabs(Conf.flirt_signatures_root):
                flirt_signatures_root = Conf.flirt_signatures_root
            else:
                if is_pyinstaller():
                    flirt_signatures_root = os.path.join(
                        app_root(), Conf.flirt_signatures_root)
                else:
                    # when running as a Python package, we should use the git submodule, which is on the same level
                    # with (instead of inside) the angrmanagement module directory.
                    flirt_signatures_root = os.path.join(
                        app_root(), "..", Conf.flirt_signatures_root)
            flirt_signatures_root = os.path.normpath(flirt_signatures_root)
            _l.info("Loading FLIRT signatures from %s.", flirt_signatures_root)
            angr.flirt.load_signatures(flirt_signatures_root)

    #
    # Library docs
    #

    def _init_library_docs(self):
        GlobalInfo.library_docs = LibraryDocs()
        if Conf.library_docs_root:
            GlobalInfo.library_docs.load_func_docs(Conf.library_docs_root)

    #
    # Daemon
    #

    def _run_daemon(self, use_daemon=None):

        if use_daemon is None:
            # Load it from the configuration file
            use_daemon = Conf.use_daemon

        if not use_daemon:
            return

        # connect to daemon (if there is one)
        if not daemon_exists():
            print("[+] Starting a new daemon.")
            run_daemon_process()
            time.sleep(0.2)
        else:
            print("[+] Connecting to an existing angr management daemon.")

        while True:
            try:
                GlobalInfo.daemon_conn = daemon_conn(service=ClientService)
            except ConnectionRefusedError:
                print("[-] Connection failed... try again.")
                time.sleep(0.4)
                continue
            print("[+] Connected to daemon.")
            break

        from rpyc import BgServingThread  # pylint:disable=import-outside-toplevel
        _ = BgServingThread(GlobalInfo.daemon_conn)

    #
    # URL scheme handler setup
    #

    def _init_url_scheme_handler(self):
        # URL scheme
        from ..logic.url_scheme import AngrUrlScheme  # pylint:disable=import-outside-toplevel

        scheme = AngrUrlScheme()
        registered, _ = scheme.is_url_scheme_registered()
        supported = scheme.is_url_scheme_supported()
        checrs_plugin = self.workspace.plugins.get_plugin_instance_by_name(
            "ChessConnector")
        if checrs_plugin is None:
            return

        if not registered and supported:
            btn = QMessageBox.question(
                None,
                "Setting up angr URL scheme",
                "angr URL scheme allows \"deep linking\" from browsers and other applications "
                "by registering the angr:// protocol to the current user. Do you want to "
                "register it? You may unregister at any "
                "time in Preferences.",
                defaultButton=QMessageBox.Yes)
            if btn == QMessageBox.Yes:
                try:
                    AngrUrlScheme().register_url_scheme()
                except (ValueError, FileNotFoundError) as ex:
                    QMessageBox.warning(
                        None, "Error in registering angr URL scheme",
                        "Failed to register the angr URL scheme.\n"
                        "The following exception occurred:\n" + str(ex))

    #
    # Event
    #

    def resizeEvent(self, event: QResizeEvent):
        """

        :param event:
        :return:
        """

        self._recalculate_view_sizes(event.oldSize())

    def closeEvent(self, event):

        # Ask if the user wants to save things
        if self.workspace.instance is not None and not self.workspace.instance.project.am_none:
            msgbox = QMessageBox()
            msgbox.setWindowTitle("Save database")
            msgbox.setText(
                "angr management is about to exit. Do you want to save the database?"
            )
            msgbox.setIcon(QMessageBox.Question)
            msgbox.setWindowIcon(self.windowIcon())
            msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No
                                      | QMessageBox.Cancel)
            msgbox.setDefaultButton(QMessageBox.Yes)
            r = msgbox.exec_()

            if r == QMessageBox.Cancel:
                event.ignore()
                return
            elif r == QMessageBox.Yes:
                save_r = self.save_database()
                if not save_r:
                    # failed to save the database
                    event.ignore()
                    return

        for plugin in list(self.workspace.plugins.active_plugins):
            self.workspace.plugins.deactivate_plugin(plugin)
        event.accept()

    def event(self, event):

        if event.type() == QEvent.User:
            # our event callback

            try:
                event.result = event.execute()
            except Exception as ex:  # pylint:disable=broad-except
                event.exception = ex
            event.event.set()

            return True

        return super().event(event)

    def on_screen_changed(self, screen):
        """
        When changing from one screen to another, ask disassembly views to refresh in case the DPI is changed.
        """
        self.workspace.current_screen.am_obj = screen
        self.workspace.current_screen.am_event()

    def on_center_tab_clicked(self, index):
        self.workspace.view_manager.handle_center_tab_click(index)

    #
    # Actions
    #

    def reload(self):
        self.workspace.reload()

    def open_file_button(self):
        file_path = self.open_mainfile_dialog()
        if not file_path:
            return
        self.load_file(file_path)

    def open_trace_file_button(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "Open a trace file",
            Conf.last_used_directory,
            "All files (*);;"
            "Trace files (*.trace);;",
        )
        Conf.last_used_directory = os.path.dirname(file_path)
        if not file_path:
            return
        self.load_trace_file(file_path)

    def open_docker_button(self):
        required = {
            'archr: git clone https://github.com/angr/archr && cd archr && pip install -e .':
            archr,
            'keystone: pip install --no-binary keystone-engine keystone-engine':
            keystone
        }
        is_missing = [key for key, value in required.items() if value is None]
        if len(is_missing) > 0:
            req_msg = 'You need to install the following:\n\n\t' + '\n\t'.join(
                is_missing)
            req_msg += '\n\nInstall them to enable this functionality.'
            req_msg += '\nRelaunch angr-management after install.'
            QMessageBox(self).critical(None, 'Dependency error', req_msg)
            return

        img_name = self._pick_image_dialog()
        if img_name is None:
            return
        target = archr.targets.DockerImageTarget(img_name, target_path=None)
        self.workspace.instance.add_job(LoadTargetJob(target))
        self.workspace.instance.img_name = img_name

    def load_trace_file(self, file_path):
        if isurl(file_path):
            QMessageBox.critical(
                self, "Unsupported Action",
                "Downloading trace files is not yet supported."
                "Please specify a path to a file on disk.")
        else:
            # File
            if os.path.isfile(file_path):
                try:
                    with open(file_path, 'rb') as f:
                        loaded_sim_state = pickle.load(f)
                        analysis_params = {
                            'end_state': loaded_sim_state,
                            'start_addr': None,
                            'end_addr': None,
                            'block_addrs': None,
                        }
                        self.workspace.view_data_dependency_graph(
                            analysis_params)
                    self._recent_file(file_path)
                except pickle.PickleError:
                    QMessageBox.critical(
                        self, "Unable to load trace file",
                        "Trace file must contain a serialized SimState.")
            else:
                QMessageBox.critical(
                    self, "File not found",
                    f"angr management cannot open file {file_path}. "
                    "Please make sure that the file exists.")

    def load_file(self, file_path):

        if not isurl(file_path):
            # file
            if os.path.isfile(file_path):
                if file_path.endswith(".trace"):
                    self.workspace.load_trace_from_path(file_path)
                    return

                self.workspace.instance.binary_path = file_path
                self.workspace.instance.original_binary_path = file_path
                if file_path.endswith(".adb"):
                    self._load_database(file_path)
                else:
                    self._recent_file(file_path)
                    self.workspace.instance.add_job(LoadBinaryJob(file_path))
            else:
                QMessageBox.critical(
                    self, "File not found",
                    f"angr management cannot open file {file_path}. "
                    "Please make sure that the file exists.")
        else:
            # url
            r = QMessageBox.question(
                self,
                "Downloading a file",
                f"Do you want to download a file from {file_path} and open it in angr management?",
                defaultButton=QMessageBox.Yes)
            if r == QMessageBox.Yes:
                try:
                    target_path = download_url(file_path,
                                               parent=self,
                                               to_file=True,
                                               file_path=None)
                except InvalidURLError:
                    QMessageBox.critical(
                        self, "Downloading failed",
                        "angr management failed to download the file. The URL is invalid."
                    )
                    return
                except UnexpectedStatusCodeError as ex:
                    QMessageBox.critical(
                        self, "Downloading failed",
                        "angr management failed to retrieve the header of the file. "
                        f"The HTTP request returned an unexpected status code {ex.status_code}."
                    )
                    return

                if target_path:
                    # open the file - now it's a local file
                    self.workspace.instance.binary_path = target_path
                    self.workspace.instance.original_binary_path = file_path
                    self.load_file(target_path)

    def load_database(self):
        # Open File window
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "Load angr database",
            ".",
            "angr databases (*.adb)",
        )

        if not file_path:
            return

        self._load_database(file_path)

    def save_database(self):
        if self.workspace.instance is None or self.workspace.instance.project.am_none:
            return True

        if self.workspace.instance.database_path is None:
            return self.save_database_as()
        else:
            return self._save_database(self.workspace.instance.database_path)

    def save_database_as(self):

        if self.workspace.instance is None or self.workspace.instance.project.am_none:
            return False

        default_database_path = self.workspace.instance.database_path
        if default_database_path is None:
            default_database_path = os.path.normpath(
                self.workspace.instance.project.filename) + ".adb"

        # Open File window
        file_path, _ = QFileDialog.getSaveFileName(
            self,
            "Save angr database",
            default_database_path,
            "angr databases (*.adb)",
        )

        if not file_path:
            return False

        if not file_path.endswith(".adb"):
            file_path = file_path + ".adb"

        return self._save_database(file_path)

    def load_trace(self):
        file_path, _ = QFileDialog.getOpenFileName(self, "Load trace", ".",
                                                   "bintrace (*.trace)")
        if not file_path:
            return
        self.workspace.load_trace_from_path(file_path)

    def preferences(self):

        # Open Preferences dialog
        pref = Preferences(self.workspace, parent=self)
        pref.exec_()

    def quit(self):
        self.close()

    def run_variable_recovery(self):
        self.workspace._get_or_create_disassembly_view(
        ).variable_recovery_flavor = 'accurate'

    def run_induction_variable_analysis(self):
        self.workspace._get_or_create_disassembly_view(
        ).run_induction_variable_analysis()

    def run_dependency_analysis(self,
                                func_addr: Optional[int] = None,
                                func_arg_idx: Optional[int] = None):
        if self.workspace is None or self.workspace.instance is None:
            return
        dep_analysis_job = DependencyAnalysisJob(func_addr=func_addr,
                                                 func_arg_idx=func_arg_idx)
        self.workspace.instance.add_job(dep_analysis_job)

    def decompile_current_function(self):
        if self.workspace is not None:
            self.workspace.decompile_current_function()

    def view_proximity_for_current_function(self):
        if self.workspace is not None:
            self.workspace.view_proximity_for_current_function()

    def interact(self):
        self.workspace.interact_program(self.workspace.instance.img_name)

    #
    # Other public methods
    #

    def progress(self, status, progress):
        self.statusBar().showMessage(f'Working... {status}')
        self._progress_dialog.setLabelText(status)
        self._progressbar.show()
        self._progressbar.setValue(progress)
        self._progress_dialog.setValue(progress)

    def progress_done(self):
        self._progressbar.hide()
        self.statusBar().showMessage("Ready.")
        self._progress_dialog.hide()

    def bring_to_front(self):
        self.setWindowState((self.windowState() & ~Qt.WindowMinimized)
                            | Qt.WindowActive)
        self.activateWindow()
        self.raise_()

    #
    # Private methods
    #

    def _recent_file(self, file_path):
        file_path = os.path.abspath(file_path)
        self._file_menu.add_recent(file_path)
        Conf.recent_file(file_path)
        save_config()

    def _load_database(self, file_path):

        if AngrDB is None:
            QMessageBox.critical(
                None, 'Error',
                'AngrDB is not enabled. Maybe you do not have SQLAlchemy installed?'
            )
            return

        angrdb = AngrDB()
        other_kbs = {}
        extra_info = {}
        try:
            proj = angrdb.load(file_path,
                               kb_names=["global", "pseudocode_variable_kb"],
                               other_kbs=other_kbs,
                               extra_info=extra_info)
        except angr.errors.AngrIncompatibleDBError as ex:
            QMessageBox.critical(
                None, 'Error',
                "Failed to load the angr database because of compatibility issues.\n"
                f"Details: {ex}")
            return
        except angr.errors.AngrDBError as ex:
            QMessageBox.critical(
                None, 'Error', 'Failed to load the angr database.\n'
                f'Details: {ex}')
            _l.critical("Failed to load the angr database.", exc_info=True)
            return

        self._recent_file(file_path)

        cfg = proj.kb.cfgs['CFGFast']
        cfb = proj.analyses.CFB()  # it will load functions from kb

        self.workspace.instance.database_path = file_path

        self.workspace.instance._reset_containers()
        self.workspace.instance.project = proj
        self.workspace.instance.cfg = cfg
        self.workspace.instance.cfb = cfb
        if "pseudocode_variable_kb" in other_kbs:
            self.workspace.instance.pseudocode_variable_kb = other_kbs[
                "pseudocode_variable_kb"]
        else:
            self.workspace.instance.initialize_pseudocode_variable_kb()
        self.workspace.instance.project.am_event(initialized=True)

        # trigger callbacks
        self.workspace.reload()
        self.workspace.on_cfg_generated()
        self.workspace.plugins.angrdb_load_entries(extra_info)

    def _save_database(self, file_path):
        if self.workspace.instance is None or self.workspace.instance.project.am_none:
            return False

        if AngrDB is None:
            QMessageBox.critical(
                None, 'Error',
                'AngrDB is not enabled. Maybe you do not have SQLAlchemy installed?'
            )
            return False

        self.workspace.plugins.handle_project_save(file_path)

        angrdb = AngrDB(project=self.workspace.instance.project)
        extra_info = self.workspace.plugins.angrdb_store_entries()
        angrdb.dump(
            file_path,
            kbs=[
                self.workspace.instance.kb,
                self.workspace.instance.pseudocode_variable_kb,
            ],
            extra_info=extra_info,
        )

        self.workspace.instance.database_path = file_path
        return True

    def _recalculate_view_sizes(self, old_size):
        adjustable_dockable_views = [
            dock for dock in self.workspace.view_manager.docks
            if dock.widget().default_docking_position in (
                'left',
                'bottom',
            )
        ]

        if not adjustable_dockable_views:
            return

        for dock in adjustable_dockable_views:
            widget = dock.widget()

            if old_size.width() < 0:
                dock.old_size = widget.sizeHint()
                continue

            if old_size != self.size():
                # calculate the width ratio

                if widget.default_docking_position == 'left':
                    # we want to adjust the width
                    ratio = widget.old_width * 1.0 / old_size.width()
                    new_width = int(self.width() * ratio)
                    widget.width_hint = new_width
                    widget.updateGeometry()
                elif widget.default_docking_position == 'bottom':
                    # we want to adjust the height
                    ratio = widget.old_height * 1.0 / old_size.height()
                    new_height = int(self.height() * ratio)
                    widget.height_hint = new_height
                    widget.updateGeometry()

                dock.old_size = widget.size()

    def _resize_dock_widget(self, dock_widget, new_width, new_height):

        original_size = dock_widget.size()
        original_min = dock_widget.minimumSize()
        original_max = dock_widget.maximumSize()

        dock_widget.resize(new_width, new_height)

        if new_width != original_size.width():
            if original_size.width() > new_width:
                dock_widget.setMaximumWidth(new_width)
            else:
                dock_widget.setMinimumWidth(new_width)

        if new_height != original_size.height():
            if original_size.height() > new_height:
                dock_widget.setMaximumHeight(new_height)
            else:
                dock_widget.setMinimumHeight(new_height)

        dock_widget.original_min = original_min
        dock_widget.original_max = original_max

        QTimer.singleShot(1, dock_widget.restore_original_size)
Beispiel #14
0
class PredictionForm(QDialog):
    base_esn_method = "ESN"
    deep_esn_method = "DeepESN"
    sub_reservoir_method = "SubReservoirESN"
    abc_smc_method = "ABC-SMC"
    name_to_impl = {
        base_esn_method: generate_prediction_esn_base,
        deep_esn_method: generate_prediction_deep_esn,
        sub_reservoir_method: generate_prediction_subreservoir,
        abc_smc_method: generate_prediction_abc_smc,
    }

    def __init__(self, parent, patient):
        QDialog.__init__(self)
        self.parent = parent
        self.patient = patient
        self.resize(480, 100)
        self.setWindowTitle("Stwórz predykcję")

        self.choose_method = QComboBox()
        self.choose_method.setItemDelegate(QStyledItemDelegate())
        self.choose_method.addItem(self.base_esn_method)
        self.choose_method.addItem(self.deep_esn_method)
        self.choose_method.addItem(self.sub_reservoir_method)
        self.choose_method.addItem(self.abc_smc_method)

        spin_box = QDoubleSpinBox()
        spin_box.setRange(0.0, 1000.0)
        spin_box.setSingleStep(0.1)
        self.choose_prediction_length = spin_box

        self.predict_button = QPushButton("Stwórz predykcję")
        self.predict_button.clicked.connect(self.generatePrediction)

        self.add_future_treatment_button = QPushButton(
            "Dodaj podanie lekarstwa")
        self.add_future_treatment_button.clicked.connect(
            self.addFutureTreatment)

        self.form_layout = QFormLayout()
        self.form_layout.addRow("Wybierz metodę", self.choose_method)
        self.form_layout.addRow("Długość predykcji (dni)",
                                self.choose_prediction_length)

        self.treatments = []

        buttons_layout = QHBoxLayout()
        buttons_layout.addWidget(self.add_future_treatment_button)
        buttons_layout.addWidget(self.predict_button)

        layout = QVBoxLayout(self)
        layout.addLayout(self.form_layout)
        layout.addLayout(buttons_layout)

    def addFutureTreatment(self):
        row = QHBoxLayout()
        row.addWidget(QLabel("Wybierz punkt podania lekarstwa"))
        treatment_date = QDateEdit()

        treatment_date.setMinimumDate(self.patient.measurements[-1].date)
        row.addWidget(treatment_date)

        remove_treatment_button = QToolButton()
        remove_treatment_button.setIcon(QIcon("resources/icon/minus.png"))
        remove_treatment_button.setStatusTip("Usuń")

        row.addWidget(remove_treatment_button)

        self.treatments.append(treatment_date)

        self.form_layout.addRow(row)

        def remove_treatment():
            self.form_layout.removeRow(row)
            self.treatments.remove(treatment_date)

        remove_treatment_button.clicked.connect(remove_treatment)

    def generatePrediction(self):
        self.method_name = self.choose_method.currentText()
        self.prediction_length = self.choose_prediction_length.value()
        form_treatments = sorted(
            [
                Treatment(0,
                          t_date.date().toPython(), 1)
                for t_date in self.treatments
            ],
            key=attrgetter("date"),
        )
        treatments = self.patient.treatments + form_treatments
        self.accept()

        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 0)
        self.progress_dialog = QProgressDialog(
            f"Generowanie predykcji za pomocą {self.method_name}", None, 0, 1)
        self.progress_dialog.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
        self.progress_dialog.setWindowModality(Qt.WindowModal)
        self.progress_dialog.setBar(self.progress_bar)

        prediction_fun = self.name_to_impl[self.method_name]

        self.progress_dialog.show()
        self.runnable = PredictionRunnable(
            prediction_fun,
            self.patient.measurements,
            self.prediction_length,
            treatments,
        )
        self.runnable.result.connect(self.handlePrediction)
        self.runnable.start()

    def handlePrediction(self, predictions):
        self.progress_dialog.close()
        for date_values in predictions:
            prediction_obj: Prediction = create_prediction_for_patient(
                self.patient,
                self.method_name,
                datetime.now().replace(microsecond=0),
                date_values,
            )
            logging.debug("Generated prediction %s", prediction_obj)
            self.parent.addPrediction(prediction_obj)
Beispiel #15
0
    def new_project_indexstats_loaded(self):
        tabs = self.centralWidget().tabdisplays
        progress = QProgressDialog("Parsing statistics from index file...",
                                   "Cancel", 0, 12, self)
        progress.setWindowTitle("Loading study index")
        progress.setWindowModality(Qt.WindowModal)
        progress.setMinimumWidth(300)
        progress.setMinimumDuration(0)
        progress.forceShow()
        progress.setValue(0)
        studyStats = self.centralWidget().data_index_stats

        # number of unique subjects in the index
        tabs.aecg_numsubjects.setText(str(studyStats.num_subjects))
        progress.setValue(1)
        # Num of unique aECG waveforms
        tabs.aecg_numaecg.setText(str(studyStats.num_aecgs))
        progress.setValue(2)
        # aECGs per subject
        tabs.aecg_aecgpersubject.setText(
            str(round(studyStats.avg_aecgs_subject, 2)))
        progress.setValue(3)

        # Other stats
        # Subjects with fewer ECGs
        if studyStats.num_subjects > 0:
            tabs.subjects_less_aecgs.setText(
                str(
                    round(
                        studyStats.subjects_less_aecgs /
                        float(studyStats.num_subjects) * 100, 2)) + " %")
        else:
            tabs.subjects_less_aecgs.setText(" %")
        progress.setValue(4)

        # Subjects with more ECGs
        if studyStats.num_subjects > 0:
            tabs.subjects_more_aecgs.setText(
                str(
                    round(
                        studyStats.subjects_more_aecgs /
                        float(studyStats.num_subjects) * 100, 2)) + " %")
        else:
            tabs.subjects_more_aecgs.setText(" %")
        progress.setValue(5)

        tabs.aecgs_no_annotations.setText(" %")
        tabs.aecgs_less_qt_in_primary_lead.setText(" %")
        tabs.aecgs_less_qt_in_primary_lead.setText(" %")
        tabs.aecgs_less_qts.setText(" %")
        tabs.aecgs_annotations_multiple_leads.setText(" %")
        tabs.aecgs_annotations_no_primary_lead.setText(" %")
        tabs.aecgs_potentially_digitized.setText(" %")
        if studyStats.num_aecgs > 0:
            tabs.aecgs_no_annotations.setText(
                str(
                    round(
                        studyStats.aecgs_no_annotations /
                        studyStats.num_aecgs * 100, 2)) + " %")
            progress.setValue(6)

            # aECGs without expected number of QTs in primary lead
            tabs.aecgs_less_qt_in_primary_lead.setText(
                str(
                    round(
                        studyStats.aecgs_less_qt_in_primary_lead /
                        studyStats.num_aecgs * 100, 2)) + " %")
            progress.setValue(7)

            # num aecgs with at least num_qts in primary lead
            tabs.aecgs_less_qts.setText(
                str(
                    round(
                        studyStats.aecgs_less_qts / studyStats.num_aecgs *
                        100, 2)) + " %")
            progress.setValue(8)

            # aECGs annotated in multiple leads
            tabs.aecgs_annotations_multiple_leads.setText(
                str(
                    round(
                        studyStats.aecgs_annotations_multiple_leads /
                        studyStats.num_aecgs * 100, 2)) + " %")
            progress.setValue(9)

            # aECGs with annotations not in primary lead
            tabs.aecgs_annotations_no_primary_lead.setText(
                str(
                    round(
                        studyStats.aecgs_annotations_no_primary_lead /
                        studyStats.num_aecgs * 100, 2)) + " %")
            progress.setValue(10)

            # aECGS with errors
            tabs.aecgs_with_errors.setText(str(studyStats.aecgs_with_errors))
            progress.setValue(11)

            # Potentially digitized aECGs
            tabs.aecgs_potentially_digitized.setText(
                str(studyStats.aecgs_potentially_digitized))
        progress.setValue(11)
        progress.close()
Beispiel #16
0
    def process(self):
        rows0, cols0 = self.gray.shape
        block = int(self.block_combo.currentText())
        padded = cv.copyMakeBorder(self.gray, 0, block - rows0 % block, 0,
                                   block - cols0 % block, cv.BORDER_CONSTANT)
        rows, cols = padded.shape
        prob = np.zeros(((rows // block) + 1, (cols // block) + 1))

        if self.multi_check.isChecked():
            model = load('models/median_multi.mdl')
        else:
            model = load('models/median_single.mdl')
        columns = model._features_count
        if columns == 8:
            levels = 1
            windows = 1
        elif columns == 24:
            levels = 3
            windows = 1
        elif columns == 96:
            levels = 3
            windows = 4
        elif columns == 128:
            levels = 4
            windows = 4
        else:
            return
        limit = model.best_ntree_limit if hasattr(model,
                                                  'best_ntree_limit') else None

        features = np.reshape(get_features(self.gray, levels, windows),
                              (1, columns))
        p = model.predict_proba(features, ntree_limit=limit)[0, 1]
        self.info_message.emit('Filtered probability = {:.2f}%'.format(p *
                                                                       100))

        progress = QProgressDialog(self.tr('Detecting median filter...'),
                                   self.tr('Cancel'), 0, prob.size, self)
        progress.canceled.connect(self.cancel)
        progress.setWindowModality(Qt.WindowModal)

        p = 0
        for i in range(0, rows, block):
            for j in range(0, cols, block):
                roi = padded[i:i + block, j:j + block]
                x = np.reshape(get_features(roi, levels, windows),
                               (1, columns))
                prob[i // block,
                     j // block] = model.predict_proba(x, ntree_limit=limit)[0,
                                                                             1]
                if self.canceled:
                    self.canceled = False
                    progress.close()
                    return
                progress.setValue(p)
                p += 1
        progress.setValue(prob.size)

        prob = cv.convertScaleAbs(prob, None, 255)
        prob = cv.resize(prob, None, None, block, block, cv.INTER_NEAREST)
        prob = gray_to_bgr(prob[:rows0, :cols0])
        self.viewer.update_processed(prob)
        self.process_button.setEnabled(False)