def preselect(self): progress = QProgressDialog('', None, 0, 100, self) try: self.project.preselect(progress) except BaseException as e: progress.close() raise e
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
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()
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()
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
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
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()
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
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()
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()
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))))
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)
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)
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()
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)