class Console(FramelessWindow): message = Signal(dict) def __init__(self, parent=None): super(Console, self).__init__(parent) self.json_decode_warning = None self.widget = QWidget(self) self.console_layout = QVBoxLayout(self.widget) self.widget.setLayout(self.console_layout) self.output_label = QLabel(self.widget) self.output_label.setText("Output") self.output_edit = QTextEdit(self.widget) self.output_edit.setReadOnly(True) self.highlighter = Highlighter(self.output_edit.document()) self.output_edit.setStyleSheet("background-color: rgb(0, 0, 0);") self.output_edit.setTextColor(QColor(0, 255, 0)) self.output_edit.setFont(QFont(self.output_edit.currentFont().family(), 10)) self.input_label = QLabel(self.widget) self.input_label.setText("Command") self.input_edit = QLineEdit(self.widget) self.input_edit.setStyleSheet("background-color: rgb(0, 0, 0); color: rgb(0, 255, 0)") self.input_edit.setFont(QFont(self.output_edit.currentFont().family(), 10)) self.send_button = QPushButton(self.widget) self.send_button.setObjectName("send_button") self.send_button.setText("Send command") self.console_layout.addWidget(self.output_label) self.console_layout.addWidget(self.output_edit) self.console_layout.addWidget(self.input_label) self.console_layout.addWidget(self.input_edit) self.console_layout.addWidget(self.send_button) self.addContentWidget(self.widget) QMetaObject.connectSlotsByName(self) def on_send_button_clicked(self): mess = self.input_edit.text() try: mess = json.loads(mess) self.message.emit(mess) self.input_edit.clear() self.output_edit.append("$: {}".format(mess)) except json.JSONDecodeError as e: self.json_decode_warning = FramelessCriticalMessageBox(self) self.json_decode_warning.setText("Failed to convert string to JSON: {}".format(e)) self.json_decode_warning.setStandardButtons(QDialogButtonBox.Ok) self.json_decode_warning.button(QDialogButtonBox.Ok).clicked.connect(self.json_decode_warning.close) self.json_decode_warning.show() def write(self, resp: dict): if resp.get("event_type"): resp = "[{}]: {}".format(str(resp.get("event_type")).upper(), resp) else: resp = str(resp) self.output_edit.append(resp)
class SLogWidget(QWidget): """Widget to log the STracking plugins messages in the graphical interface""" def __init__(self): super().__init__() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.progress_bar = QProgressBar() self.log_area = QTextEdit() layout.addWidget(self.progress_bar) layout.addWidget(self.log_area) self.setLayout(layout) def set_advanced(self, mode: bool): """Show hide the log area depending on the plugin mode""" if mode: self.log_area.setVisible(True) else: self.log_area.setVisible(False) def set_progress(self, value: int): """Callback to update the progress bar""" self.progress_bar.setValue(value) def add_log(self, value: str): """Callback to add a new message in the log area""" self.log_area.append(value) def clear_log(self): """Callback to clear all the log area""" self.log_area.clear()
class QtPipDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setup_ui() self.setAttribute(Qt.WA_DeleteOnClose) # create install process self.process = QProcess(self) self.process.setProgram(sys.executable) self.process.setProcessChannelMode(QProcess.MergedChannels) # setup process path env = QProcessEnvironment() combined_paths = os.pathsep.join( [user_site_packages(), env.systemEnvironment().value("PYTHONPATH")] ) env.insert("PYTHONPATH", combined_paths) self.process.setProcessEnvironment(env) # connections self.install_button.clicked.connect(self._install) self.uninstall_button.clicked.connect(self._uninstall) self.process.readyReadStandardOutput.connect(self._on_stdout_ready) from ..plugins import plugin_manager self.process.finished.connect(plugin_manager.discover) self.process.finished.connect(plugin_manager.prune) def setup_ui(self): layout = QVBoxLayout() self.setLayout(layout) title = QLabel("Install/Uninstall Packages:") title.setObjectName("h2") self.line_edit = QLineEdit() self.install_button = QPushButton("install", self) self.uninstall_button = QPushButton("uninstall", self) self.text_area = QTextEdit(self, readOnly=True) hlay = QHBoxLayout() hlay.addWidget(self.line_edit) hlay.addWidget(self.install_button) hlay.addWidget(self.uninstall_button) layout.addWidget(title) layout.addLayout(hlay) layout.addWidget(self.text_area) self.setFixedSize(700, 400) self.setMaximumHeight(800) self.setMaximumWidth(1280) def _install(self): cmd = ['-m', 'pip', 'install'] if running_as_bundled_app() and sys.platform.startswith('linux'): cmd += [ '--no-warn-script-location', '--prefix', user_plugin_dir(), ] self.process.setArguments(cmd + self.line_edit.text().split()) self.text_area.clear() self.process.start() def _uninstall(self): args = ['-m', 'pip', 'uninstall', '-y'] self.process.setArguments(args + self.line_edit.text().split()) self.text_area.clear() self.process.start() def _on_stdout_ready(self): text = self.process.readAllStandardOutput().data().decode() self.text_area.append(text)
class ErrorDialog(QDialog): """ Dialog to present user the exception information. User can send error report (possible to add custom information) """ def __init__(self, exception: Exception, description: str, additional_notes: str = "", additional_info=None): super().__init__() self.exception = exception self.additional_notes = additional_notes self.send_report_btn = QPushButton("Send information") self.send_report_btn.setDisabled(not state_store.report_errors) self.cancel_btn = QPushButton("Cancel") self.error_description = QTextEdit() self.traceback_summary = additional_info if additional_info is None: self.error_description.setText("".join( traceback.format_exception(type(exception), exception, exception.__traceback__))) elif isinstance(additional_info, traceback.StackSummary): self.error_description.setText("".join(additional_info.format())) elif isinstance(additional_info[1], traceback.StackSummary): self.error_description.setText("".join( additional_info[1].format())) self.error_description.append(str(exception)) self.error_description.setReadOnly(True) self.additional_info = QTextEdit() self.contact_info = QLineEdit() self.user_name = QLineEdit() self.cancel_btn.clicked.connect(self.reject) self.send_report_btn.clicked.connect(self.send_information) layout = QVBoxLayout() self.desc = QLabel(description) self.desc.setWordWrap(True) info_text = QLabel( "If you see these dialog it not means that you do something wrong. " "In such case you should see some message box not error report dialog." ) info_text.setWordWrap(True) layout.addWidget(info_text) layout.addWidget(self.desc) layout.addWidget(self.error_description) layout.addWidget(QLabel("Contact information")) layout.addWidget(self.contact_info) layout.addWidget(QLabel("User name")) layout.addWidget(self.user_name) layout.addWidget(QLabel("Additional information from user:"******"Sending reports was disabled by runtime flag. " "You can report it manually by creating report on " "https://github.com/4DNucleome/PartSeg/issues")) btn_layout = QHBoxLayout() btn_layout.addWidget(self.cancel_btn) btn_layout.addWidget(self.send_report_btn) layout.addLayout(btn_layout) self.setLayout(layout) if isinstance(additional_info, tuple): self.exception_tuple = additional_info[0], None else: exec_info = exc_info_from_error(exception) self.exception_tuple = event_from_exception(exec_info) def exec(self): self.exec_() def exec_(self): """ Check if dialog should be shown base on :py:data:`state_store.show_error_dialog`. If yes then show dialog. Otherwise print exception traceback on stderr. """ # TODO check if this check is needed if not state_store.show_error_dialog: sys.__excepthook__(type(self.exception), self.exception, self.exception.__traceback__) return False super().exec_() def send_information(self): """ Function with construct final error message and send it using sentry. """ with sentry_sdk.push_scope() as scope: text = self.desc.text() + "\n\nVersion: " + __version__ + "\n" if len(self.additional_notes) > 0: scope.set_extra("additional_notes", self.additional_notes) if len(self.additional_info.toPlainText()) > 0: scope.set_extra("user_information", self.additional_info.toPlainText()) if len(self.contact_info.text()) > 0: scope.set_extra("contact", self.contact_info.text()) event, hint = self.exception_tuple event["message"] = text if self.traceback_summary is not None: scope.set_extra("traceback", self.error_description.toPlainText()) event_id = sentry_sdk.capture_event(event, hint=hint) if event_id is None: event_id = sentry_sdk.hub.Hub.current.last_event_id() if len(self.additional_info.toPlainText()) > 0: contact_text = self.contact_info.text() user_name = self.user_name.text() data = { "comments": self.additional_info.toPlainText(), "event_id": event_id, "email": contact_text if _email_regexp.match(contact_text) else "*****@*****.**", "name": user_name or getpass.getuser(), } r = requests.post( url=_feedback_url, data=data, headers={ "Authorization": "DSN https://[email protected]/1309302" }, ) if r.status_code != 200: data["email"] = "*****@*****.**" data["name"] = getpass.getuser() requests.post( url=_feedback_url, data=data, headers={ "Authorization": "DSN https://[email protected]/1309302" }, ) # sentry_sdk.capture_event({"message": text, "level": "error", "exception": self.exception}) self.accept()