class OutputPreviewWidget(QWidget): """ A .py preview widget that appears as a drawer on the side of the main import widget. """ def __init__(self, parent=None): super(OutputPreviewWidget, self).__init__(parent=parent) self.setWindowFlags(Qt.Sheet) self.text_editor = QPlainTextEdit() self.close_button = QPushButton('Close') self.layout = QVBoxLayout() self.layout.addWidget(self.text_editor) self.layout.addWidget(self.close_button) self.setLayout(self.layout) self.close_button.clicked.connect(self.hide) self.resize(400, 500) font = QFont('Courier') self.text_editor.setFont(font) self.text_editor.setReadOnly(True) def set_text(self, text): """ Set the text in the loader preview widget. This will display the text that will be saved to the output loader python file. Parameters ---------- text: str Text that will be saved to the final loader file """ self.text_editor.setPlainText(text)
def setupUi(self, path): f = open(path, 'r', encoding='utf-8') jsonData = json.loads(f.read()) noticia = jsonData["noticia"] titulo = jsonData["title"] fecha = jsonData["fecha"] tags = "" for tag in jsonData["tags"]: tags += tag + " | " f.close() textArea = QPlainTextEdit(noticia, self) textArea.setReadOnly(True) textArea.move(10, 60) textArea.setFixedSize(1200, 600) labelTit = QLabel(titulo, self) labelTit.move(10, 30) labelFecha = QLabel(fecha, self) labelFecha.move(10, 670) labelTags = QLabel(tags[:-3], self) labelTags.move(300, 670) buttonSearchSim = QPushButton("Buscar noticias similares (text)", self) buttonSearchSim.move(1000, 30) buttonSearchSim.clicked.connect(self.make_searchSim(path)) buttonTagsSim = QPushButton("Buscar noticias similares (tags)", self) buttonTagsSim.move(1000, 5) buttonTagsSim.clicked.connect(self.make_tagsSim(path)) self.setWindowTitle("Texto de la noticia") self.setMinimumSize(1220, 705) self.show() self.exec_()
class OutputPreviewWidget(QWidget): """ A .py preview widget that appears as a drawer on the side of the main import widget. """ def __init__(self, parent=None): super(OutputPreviewWidget, self).__init__(parent=parent) self.setWindowFlags(Qt.Sheet) self.text_editor = QPlainTextEdit() self.close_button = QPushButton('Close') self.layout = QVBoxLayout() self.layout.addWidget(self.text_editor) self.layout.addWidget(self.close_button) self.setLayout(self.layout) self.close_button.clicked.connect(self.hide) self.resize(400, 500) font = QFont('Courier') self.text_editor.setFont(font) self.text_editor.setReadOnly(True) def set_text(self, text): """ Set the text in the loader preview widget. This will display the text that will be saved to the output loader python file. Parameters ---------- text: str Text that will be saved to the final loader file """ self.text_editor.setPlainText(text)
class ShellWidget(QWidget): run_shell = Signal(Bot, str) def __init__(self, bot, parent): super(ShellWidget, self).__init__(parent) self.bot = bot self.widget_layout = QVBoxLayout(self) self.setLayout(self.widget_layout) self.output_widget = QPlainTextEdit(self) self.output_widget.setReadOnly(True) self.highlighter = ShellHighlighter(self.output_widget.document()) self.widget_layout.addWidget(self.output_widget) self.input_widget = QLineEdit(self) self.widget_layout.addWidget(self.input_widget) self.send_button = QPushButton(self) self.send_button.setText("Send") self.send_button.clicked.connect(self.on_send_button_clicked) self.widget_layout.addWidget(self.send_button) @Slot() def on_send_button_clicked(self): text = self.input_widget.text() self.output_widget.appendPlainText("$: {}".format(text)) self.run_shell.emit(self.bot, text) self.input_widget.clear() @Slot(Bot, str) def append_shell(self, bot, message): if self.bot == bot: self.output_widget.appendPlainText(message)
class QtFontSizePreview(QFrame): """ Widget that displays a preview text. Parameters ---------- parent : QWidget, optional Parent widget. text : str, optional Preview text to display. Default is None. """ def __init__(self, parent: QWidget = None, text: str = None): super().__init__(parent) self._text = text or "" # Widget self._preview = QPlainTextEdit(self) # Widget setup self._preview.setReadOnly(True) self._preview.setPlainText(self._text) # Layout layout = QHBoxLayout() layout.addWidget(self._preview) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) def sizeHint(self): """Override Qt method.""" return QSize(100, 80) def text(self) -> str: """Return the current preview text. Returns ------- str The current preview text. """ return self._text def setText(self, text: str): """Set the current preview text. Parameters ---------- text : str The current preview text. """ self._text = text self._preview.setPlainText(text)
def __init__(self, parent, history): super().__init__(parent=parent) self.setWindowTitle("History") layout = QVBoxLayout() text = QPlainTextEdit() text.setReadOnly(True) text.setPlainText(history) layout.addWidget(text) buttonbox = QDialogButtonBox(QDialogButtonBox.Ok) layout.addWidget(buttonbox) self.setLayout(layout) buttonbox.accepted.connect(self.accept) self.resize(700, 500)
class DisplayDialog(QDialog): """ This is a simple dialog display which can be configured by users """ def __init__(self, parent=None, name='Test'): """ init :param parent: """ super(DisplayDialog, self).__init__(parent) layout = QVBoxLayout(self) # nice widget for editing the date self.message_edit = QPlainTextEdit(self) self.message_edit.setReadOnly(True) layout.addWidget(self.message_edit) self.setWindowTitle('Merged Scans Workspace Names') # OK and Cancel buttons buttons = QDialogButtonBox(QDialogButtonBox.Ok, QtCore.Qt.Horizontal, self) buttons.accepted.connect(self.accept) layout.addWidget(buttons) self.name = name return def set_name(self, new_name): self.name = new_name def show_message(self, message): """ show message :param message: :return: """ self.message_edit.setPlainText(message) return
class DisplayDialog(QDialog): """ This is a simple dialog display which can be configured by users """ def __init__(self, parent=None, name='Test'): """ init :param parent: """ super(DisplayDialog, self).__init__(parent) layout = QVBoxLayout(self) # nice widget for editing the date self.message_edit = QPlainTextEdit(self) self.message_edit.setReadOnly(True) layout.addWidget(self.message_edit) self.setWindowTitle('Merged Scans Workspace Names') # OK and Cancel buttons buttons = QDialogButtonBox(QDialogButtonBox.Ok, QtCore.Qt.Horizontal, self) buttons.accepted.connect(self.accept) layout.addWidget(buttons) self.name = name return def set_name(self, new_name): self.name = new_name def show_message(self, message): """ show message :param message: :return: """ self.message_edit.setPlainText(message) return
def preview_layout(self, show_hidden_areas=False): """ Show the layout with placeholder texts using a QWidget. """ from spyder.utils.qthelpers import qapplication app = qapplication() widget = QWidget() layout = QGridLayout() for area in self._areas: label = QPlainTextEdit() label.setReadOnly(True) label.setPlainText("\n".join(area["plugin_ids"])) if area["visible"] or show_hidden_areas: layout.addWidget( label, area["row"], area["column"], area["row_span"], area["col_span"], ) # label.setVisible(area["visible"]) if area["default"]: label.setStyleSheet( "QPlainTextEdit {background-color: #ff0000;}") if not area["visible"]: label.setStyleSheet( "QPlainTextEdit {background-color: #eeeeee;}") for row, stretch in self._row_stretchs.items(): layout.setRowStretch(row, stretch) for col, stretch in self._column_stretchs.items(): layout.setColumnStretch(col, stretch) widget.setLayout(layout) widget.showMaximized() app.exec_()
class YAMLPreviewWidget(QWidget): """ A YAML preview widget that appears as a drawer on the side of the main import widget. """ def __init__(self, parent=None): super(YAMLPreviewWidget, self).__init__(parent=parent) self.setWindowFlags(Qt.Sheet) self.text_editor = QPlainTextEdit() self.close_button = QPushButton('Close') self.layout = QVBoxLayout() self.layout.addWidget(self.text_editor) self.layout.addWidget(self.close_button) self.setLayout(self.layout) self.close_button.clicked.connect(self.hide) self.resize(400, 500) font = QFont('Courier') self.text_editor.setFont(font) self.text_editor.setReadOnly(True) def set_text(self, text): self.text_editor.setPlainText(text)
def __init__(self, parent, history): super().__init__(parent=parent) self.setWindowTitle("History") layout = QVBoxLayout() text = QPlainTextEdit() font = QFont() font.setFamily("monospace") font.setStyleHint(QFont.Monospace) text.setFont(font) highlighter = PythonHighlighter(text.document()) # noqa: F841 text.setReadOnly(True) text.setPlainText(history) layout.addWidget(text) buttonbox = QDialogButtonBox(QDialogButtonBox.Ok) clipboardbutton = QPushButton("Copy to clipboard") buttonbox.addButton(clipboardbutton, QDialogButtonBox.ActionRole) clipboard = QGuiApplication.clipboard() clipboardbutton.clicked.connect( lambda: clipboard.setText(history + "\n")) layout.addWidget(buttonbox) self.setLayout(layout) buttonbox.accepted.connect(self.accept) self.resize(700, 500)
class InfoDialog(QDialog): def __init__(self, content=None, description=None): super(InfoDialog, self).__init__() self.wikiLink = "https://github.com/eliranwong/UniqueBible/wiki" self.setMinimumWidth(350) self.setWindowTitle(config.thisTranslation["info"]) self.layout = QVBoxLayout() self.appName = QLabel("UniqueBible.app [{0} {1}]".format( config.thisTranslation["version"], config.version)) self.appName.mouseReleaseEvent = self.openWiki self.layout.addWidget(self.appName) if content is None: with open("latest_changes.txt", "r", encoding="utf-8") as fileObject: text = fileObject.read() else: text = content self.layout.addWidget( QLabel("{0}:".format(config.thisTranslation["latest_changes"] if description is None else description))) self.latestChanges = QPlainTextEdit() self.latestChanges.setPlainText(text) self.latestChanges.setReadOnly(True) self.layout.addWidget(self.latestChanges) buttons = QDialogButtonBox.Ok self.buttonBox = QDialogButtonBox(buttons) self.buttonBox.accepted.connect(self.accept) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) def openWiki(self, event): webbrowser.open(self.wikiLink)
class EventViewerPanel(QPlainTextEdit): def __init__(self, log_handler: GUILogHandler): self.log_handler = log_handler QPlainTextEdit.__init__(self) self.setMinimumWidth(500) self.setMinimumHeight(200) self._dynamic = False self.setWindowTitle("Event viewer") self.activateWindow() layout = QVBoxLayout() self.text_box = QPlainTextEdit() self.text_box.setReadOnly(True) self.text_box.setMaximumBlockCount(1000) layout.addWidget(self.text_box) self.setLayout(layout) log_handler.append_log_statement.connect(self.val_changed) @QtCore.Slot(str) def val_changed(self, value): self.text_box.appendPlainText(value)
class ZusatzFensterKerndaten(QWidget): def __init__(self, nummer, text): super().__init__() self.initMe(nummer, text) def initMe(self, nummer, text): self.l1 = QLabel(self) self.l1.setText('Inhalt der eingelesenen Zelle') self.l1.move(20, 5) self.nummer = nummer self.setGeometry(400, 300, 500, 700) self.zelle = QPlainTextEdit(self) self.zelle.setGeometry(0, 40, 500, 250) self.zelle.setPlainText(text) self.zelle.setReadOnly(True) self.l2 = QLabel(self) self.l2.setText( """Bitte geben Sie hier den Wert ein nach dem in der Zelle gesucht werden soll. Bsp. Wollen Sie einen Lastpunkt auslesen, welcher mit 5000 rpm angegeben ist, geben Sie 5000 ein. Achtung: keine Einheiten mit angeben. Nur Zahlen!""") self.l2.move(10, 330) self.eing = QLineEdit(self) self.eing.move(10, 410) p = QPushButton('Prüfen', self) p.clicked.connect(self.pruefen) p.move(180, 409) self.l3 = QLabel(self) self.l3.setText('vorangehende Zeichenkette') self.l3.move(10, 460) self.suchstring = QLineEdit(self) self.suchstring.move(180, 459) self.suchstring.setDisabled(True) self.l5 = QLabel(self) self.l5.setStyleSheet("background-color: yellow") self.l5.setText( "Prüfen Sie die vorrangehende Zeichenkette.\nSollte diese nicht stimmen, können Sie selbst eine angeben und erneut prüfen.\nAchtung: Leerzeichen nicht vergessen " ) self.l5.move(10, 490) self.l5.setVisible(False) self.l4 = QLabel(self) self.l4.setText('gefundener Eintrag') self.l4.move(10, 540) self.gefundener_string = QLineEdit(self) self.gefundener_string.move(180, 539) self.gefundener_string.setReadOnly(True) frage = QPushButton(self) frage.setIcon(QIcon("bilder_vorlagenersteller\\FrageIcon.png")) frage.move(450, 10) frage.clicked.connect(self.Hilfe) self.weiter = QPushButton('Weiter', self) self.weiter.move(420, 650) self.weiter.setDisabled(True) self.weiter.clicked.connect(self.weiter_gehts) def suchstring_finden(self): startindex = self.zelle.toPlainText().find(self.eing.text()) if startindex == 0: suchstring = '##Anfang###' elif startindex == -1: suchstring = 'ungültige Eingabe' else: suchstring = '' for i in range(0, 11): suchstring = self.zelle.toPlainText()[startindex - i] + suchstring if (startindex - i) == 0: break return suchstring[:-1] def pruefen(self): suchstring = self.suchstring.text() if suchstring == '': suchstring = self.suchstring_finden() print(suchstring) self.suchstring.setDisabled(False) self.l5.setVisible(True) self.weiter.setDisabled(False) self.suchstring.setText(suchstring) startindex = self.zelle.toPlainText().find(suchstring) + len( suchstring) ende = startindex + len(self.eing.text()) self.gefundener_string.setText( self.zelle.toPlainText()[startindex:ende]) def weiter_gehts(self): w.findChild(QLabel, self.nummer).setVisible(True) w.findChild(QLineEdit, 'suchstr' + self.nummer).setVisible(True) w.findChild(QLineEdit, 'suchstr' + self.nummer).setText(self.suchstring.text()) self.close() def Hilfe(self): self.h = HilfeFenster( "bilder_vorlagenersteller\\erweitertes_einlesen.png") self.h.show()
class ProfilePreviewDialog(QDialog): def __init__( self, profile_dict: typing.Dict[str, ROIExtractionProfile], algorithm_dict: Register, settings: BaseSettings, parent=None, ): super().__init__(parent=parent) self.profile_dict = profile_dict self.algorithm_dict = algorithm_dict self.settings = settings self.profile_list = SearchableListWidget() self.profile_list.addItems(self.profile_dict.keys()) self.profile_list.currentTextChanged.connect(self.profile_selected) self.profile_view = QPlainTextEdit() self.profile_view.setReadOnly(True) self.delete_btn = QPushButton("Delete") self.delete_btn.clicked.connect(self.delete_action) self.rename_btn = QPushButton("Rename") self.rename_btn.clicked.connect(self.rename_action) self.export_btn = QPushButton("Export") self.export_btn.clicked.connect(self.export_action) self.import_btn = QPushButton("Import") self.import_btn.clicked.connect(self.import_action) layout = QGridLayout() layout.addWidget(self.profile_list, 0, 0) layout.addWidget(self.profile_view, 0, 1) layout.addWidget(self.delete_btn, 1, 0) layout.addWidget(self.rename_btn, 1, 1) layout.addWidget(self.export_btn, 2, 0) layout.addWidget(self.import_btn, 2, 1) self.setLayout(layout) def profile_selected(self, text): if text not in self.profile_dict: return profile = self.profile_dict[text] self.profile_view.setPlainText(str(profile)) def delete_action(self): if self.profile_list.currentItem() is None: return # pragma: no cover if self.profile_list.currentItem().text() in self.profile_dict: del self.profile_dict[self.profile_list.currentItem().text()] self.profile_list.clear() self.profile_list.addItems(self.profile_dict.keys()) def rename_action(self): if self.profile_list.currentItem() is None: return # pragma: no cover old_name = self.profile_list.currentItem().text() if old_name not in self.profile_dict: return # pragma: no cover text, ok = QInputDialog.getText(self, "Profile Name", "Input profile name here") if not ok: return # pragma: no cover if text in self.profile_dict: # pragma: no cover QMessageBox.warning( self, "Already exists", "Profile with this name already exist.", QMessageBox.Ok, QMessageBox.Ok, ) self.rename_action() return profile = self.profile_dict[old_name] del self.profile_dict[old_name] profile.name = text self.profile_dict[text] = profile self.profile_list.clear() self.profile_list.addItems(self.profile_dict.keys()) def export_action(self): exp = ExportDialog(self.profile_dict, ProfileDictViewer, parent=self) if not exp.exec_(): return # pragma: no cover dial = PSaveDialog(SaveProfilesToJSON, settings=self.settings, parent=self, path=IO_SAVE_DIRECTORY) if dial.exec_(): save_location, _selected_filter, save_class, values = dial.get_result( ) data = {x: self.profile_dict[x] for x in exp.get_export_list()} save_class.save(save_location, data, values) def import_action(self): dial = PLoadDialog(LoadProfileFromJSON, settings=self.settings, parent=self, path=IO_SAVE_DIRECTORY) if not dial.exec_(): return # pragma: no cover file_list, _, load_class = dial.get_result() profs, err = load_class.load(file_list) if err: show_warning("Import error", "error during importing, part of data were filtered." ) # pragma: no cover imp = ImportDialog(profs, self.profile_dict, ProfileDictViewer, parent=self) if not imp.exec_(): return # pragma: no cover for original_name, final_name in imp.get_import_list(): self.profile_dict[final_name] = profs[original_name] self.settings.dump() self.profile_list.clear() self.profile_list.addItems(self.profile_dict.keys())
class FileDialog(QDialog): def __init__(self, file_name, job_name, job_number, realization, iteration, parent=None): super(FileDialog, self).__init__(parent) self.setWindowTitle("{} # {} Realization: {} Iteration: {}".format( job_name, job_number, realization, iteration)) try: self._file = open(file_name, "r") except OSError as error: self._mb = QMessageBox( QMessageBox.Critical, "Error opening file", error.strerror, QMessageBox.Ok, self, ) self._mb.finished.connect(self.accept) self._mb.show() return self._view = QPlainTextEdit() self._view.setReadOnly(True) self._view.setWordWrapMode(QTextOption.NoWrap) # for moving the actual slider self._view.verticalScrollBar().sliderMoved.connect(self._update_cursor) # for mouse wheel and keyboard arrows self._view.verticalScrollBar().valueChanged.connect( self._update_cursor) self._view.setFont(QFontDatabase.systemFont(QFontDatabase.FixedFont)) self._follow_mode = False self._init_layout() self._init_thread() self.show() @Slot() def _stop_thread(self): self._thread.quit() self._thread.wait() def _init_layout(self): self.setMinimumWidth(600) self.setMinimumHeight(400) dialog_buttons = QDialogButtonBox(QDialogButtonBox.Ok) dialog_buttons.accepted.connect(self.accept) self._copy_all_button = dialog_buttons.addButton( "Copy All", QDialogButtonBox.ActionRole) self._copy_all_button.clicked.connect(self._copy_all) self._follow_button = dialog_buttons.addButton( "Follow", QDialogButtonBox.ActionRole) self._follow_button.setCheckable(True) self._follow_button.toggled.connect(self._enable_follow_mode) self._enable_follow_mode(self._follow_mode) layout = QVBoxLayout(self) layout.addWidget(self._view) layout.addWidget(dialog_buttons) def _init_thread(self): self._thread = QThread() self._worker = FileUpdateWorker(self._file) self._worker.moveToThread(self._thread) self._worker.read.connect(self._append_text) self._thread.started.connect(self._worker.setup) self._thread.finished.connect(self._worker.stop) self._thread.finished.connect(self._worker.deleteLater) self.finished.connect(self._stop_thread) self._thread.start() def _copy_all(self) -> None: text = self._view.toPlainText() QApplication.clipboard().setText(text, QClipboard.Clipboard) pass def _update_cursor(self, value: int) -> None: if not self._view.textCursor().hasSelection(): block = self._view.document().findBlockByLineNumber(value) cursor = QTextCursor(block) self._view.setTextCursor(cursor) def _enable_follow_mode(self, enable: bool) -> None: if enable: self._view.moveCursor(QTextCursor.End) self._view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._view.verticalScrollBar().setDisabled(True) self._view.setTextInteractionFlags(Qt.NoTextInteraction) self._follow_mode = True else: self._view.verticalScrollBar().setDisabled(False) self._view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self._view.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard) self._follow_mode = False def _append_text(self, text: str) -> None: # Remove trailing newline as appendPlainText adds this if text[-1:] == "\n": text = text[:-1] if self._follow_mode: self._view.moveCursor(QTextCursor.End) self._view.appendPlainText(text)
class PyDMLogDisplay(QWidget, LogLevels): """ Standard display for Log Output This widget handles instantating a ``GuiHandler`` and displaying log messages to a ``QPlainTextEdit``. The level of the log can be changed from inside the widget itself, allowing users to select from any of the ``.levels`` specified by the widget. Parameters ---------- parent : QObject, optional logname : str Name of log to display in widget level : logging.Level Initial level of log display """ Q_ENUMS(LogLevels) LogLevels = LogLevels terminator = '\n' default_format = '%(asctime)s %(message)s' default_level = logging.INFO def __init__(self, parent=None, logname=None, level=logging.NOTSET): QWidget.__init__(self, parent=parent) # Create Widgets self.label = QLabel('Minimum displayed log level: ', parent=self) self.combo = QComboBox(parent=self) self.text = QPlainTextEdit(parent=self) self.text.setReadOnly(True) self.clear_btn = QPushButton("Clear", parent=self) # Create layout layout = QVBoxLayout() level_control = QHBoxLayout() level_control.addWidget(self.label) level_control.addWidget(self.combo) layout.addLayout(level_control) layout.addWidget(self.text) layout.addWidget(self.clear_btn) self.setLayout(layout) # Allow QCombobox to control log level for log_level, value in LogLevels.as_dict().items(): self.combo.addItem(log_level, value) self.combo.currentIndexChanged[str].connect(self.setLevel) # Allow QPushButton to clear log text self.clear_btn.clicked.connect(self.clear) # Create a handler with the default format self.handler = GuiHandler(level=level, parent=self) self.logFormat = self.default_format self.handler.message.connect(self.write) # Create logger. Either as a root or given logname self.log = None self.level = None self.logName = logname or '' self.logLevel = level self.destroyed.connect(functools.partial(logger_destroyed, self.log)) def sizeHint(self): return QSize(400, 300) @Property(LogLevels) def logLevel(self): return self.level @logLevel.setter def logLevel(self, level): if level != self.level: self.level = level idx = self.combo.findData(level) self.combo.setCurrentIndex(idx) @Property(str) def logName(self): """Name of associated log""" return self.log.name @logName.setter def logName(self, name): # Disconnect prior log from handler if self.log: self.log.removeHandler(self.handler) # Reattach handler to new handler self.log = logging.getLogger(name) # Ensure that the log matches level of handler # only if the handler level is less than the log. if self.log.level < self.handler.level: self.log.setLevel(self.handler.level) # Attach preconfigured handler self.log.addHandler(self.handler) @Property(str) def logFormat(self): """Format for log messages""" return self.handler.formatter._fmt @logFormat.setter def logFormat(self, fmt): self.handler.setFormatter(logging.Formatter(fmt)) @Slot(str) def write(self, message): """Write a message to the log display""" # We split the incoming message by new lines. In prior iterations of # this widget it was discovered that large blocks of text cause issues # at the Qt level. for msg in message.split(self.terminator): self.text.appendPlainText(msg) @Slot() def clear(self): """Clear the text area.""" self.text.clear() @Slot(str) def setLevel(self, level): """Set the level of the contained logger""" # Get the level from the incoming string specification try: level = getattr(logging, level.upper()) except AttributeError as exc: logger.exception("Invalid logging level specified %s", level.upper()) else: # Set the existing handler and logger to this level self.handler.setLevel(level) if self.log.level > self.handler.level or self.log.level == logging.NOTSET: self.log.setLevel(self.handler.level)
class AmiGui(QWidget): loadFile = Signal(str, name='loadFile') saveFile = Signal(str, name='saveFile') statusUpdate = Signal(str, name='statusUpdate') def __init__(self, queue, graphmgr_addr, ami_save, parent=None): super(__class__, self).__init__(parent) self.setWindowTitle("AMI Client") self.comm_handler = AsyncGraphCommHandler(graphmgr_addr.name, graphmgr_addr.comm) self.save_button = QPushButton('Save', self) self.save_button.clicked.connect(self.save) self.load_button = QPushButton('Load', self) self.load_button.clicked.connect(self.load) self.clear_button = QPushButton('Clear', self) self.clear_button.clicked.connect(self.clear) self.reset_button = QPushButton('Reset Plots', self) self.reset_button.clicked.connect(self.reset) self.status_box = QPlainTextEdit(self) self.status_box.setReadOnly(True) self.amilist = DetectorList(queue, self.comm_handler) if ami_save is not None: self.comm_handler.update(ami_save) self.setup = QGroupBox("Setup") self.setup_layout = QHBoxLayout(self.setup) self.setup_layout.addWidget(self.save_button) self.setup_layout.addWidget(self.load_button) self.setup_layout.addWidget(self.clear_button) self.setup.setLayout(self.setup_layout) self.data = QGroupBox("Data") self.data_layout = QVBoxLayout(self) self.data_layout.addWidget(self.reset_button) self.data_layout.addWidget(self.amilist) self.data.setLayout(self.data_layout) self.status = QGroupBox("Status") self.status_layout = QVBoxLayout(self) self.status_layout.addWidget(self.status_box) self.status.setLayout(self.status_layout) self.ami_layout = QVBoxLayout(self) self.ami_layout.addWidget(self.setup, 1) self.ami_layout.addWidget(self.data, 4) self.ami_layout.addWidget(self.status, 2) self.loadFile.connect(self.load_async) self.saveFile.connect(self.save_async) self.statusUpdate.connect(self.status_box.appendPlainText) # the status box to logging add_logging_handler(self.statusUpdate) # create a qthread that listens for info messages from the cluster to log them. self.info_thread = AmiInfo(graphmgr_addr.info, self.log_message) self.info_thread.start() @Slot(str, str) def log_message(self, topic, msg): # see if the topic name is a log level then use that otherwise use info log_func = getattr(logger, topic, 'info') log_func(msg) @Slot() def load(self): load_file = QFileDialog.getOpenFileName( self, "Open file", "", "AMI Autosave files (*.ami);;All Files (*)") if load_file[0]: logger.info("Loading graph configuration from file (%s)", load_file[0]) self.loadFile.emit(load_file[0]) @Slot() def save(self): save_file = QFileDialog.getSaveFileName( self, "Save file", "autosave.ami", "AMI Autosave files (*.ami);;All Files (*)") if save_file[0]: logger.info("Saving graph configuration to file (%s)", save_file[0]) self.saveFile.emit(save_file[0]) @asyncqt.asyncSlot() async def reset(self): if not (await self.comm_handler.reset()): logger.error("Unable to reset feature store of the manager!") @asyncqt.asyncSlot() async def clear(self): if not (await self.comm_handler.clear()): logger.error( "Unable to clear the graph configuration of the manager!") @asyncqt.asyncSlot(str) async def load_async(self, filename): await self.comm_handler.load(filename) @asyncqt.asyncSlot(str) async def save_async(self, filename): await self.comm_handler.save(filename)
class SegmentationInfoDialog(QWidget): def __init__(self, settings: StackSettings, set_parameters: Callable[[str, dict], None], additional_text=None): """ :param settings: :param set_parameters: Function which set parameters of chosen in dialog. :param additional_text: Additional text on top of Window. """ super().__init__() self.settings = settings self.parameters_dict = None self.set_parameters = set_parameters self.components = QListWidget() self.components.currentItemChanged.connect(self.change_component_info) self.description = QPlainTextEdit() self.description.setReadOnly(True) self.close_btn = QPushButton("Close") self.close_btn.clicked.connect(self.close) self.set_parameters_btn = QPushButton("Reuse parameters") self.set_parameters_btn.clicked.connect(self.set_parameter_action) self.additional_text_label = QLabel(additional_text) layout = QGridLayout() layout.addWidget(self.additional_text_label, 0, 0, 1, 2) if not additional_text: self.additional_text_label.setVisible(False) layout.addWidget(QLabel("Components:"), 1, 0) layout.addWidget(QLabel("segmentation parameters:"), 1, 1) layout.addWidget(self.components, 2, 0) layout.addWidget(self.description, 2, 1) layout.addWidget(self.close_btn, 3, 0) layout.addWidget(self.set_parameters_btn, 3, 1) self.setLayout(layout) self.setWindowTitle("Parameters preview") def set_parameters_dict(self, val: Optional[Dict[int, SegmentationProfile]]): self.parameters_dict = val def set_additional_text(self, text): self.additional_text_label.setText(text) self.additional_text_label.setVisible(bool(text)) @property def get_parameters(self): if self.parameters_dict: return self.parameters_dict return self.settings.components_parameters_dict def change_component_info(self): if self.components.currentItem() is None: return text = self.components.currentItem().text() parameters = self.get_parameters[int(text)] if parameters is None: self.description.setPlainText("None") else: self.description.setPlainText( f"Component {text}\n" + parameters.pretty_print(mask_algorithm_dict)) def set_parameter_action(self): if self.components.currentItem() is None: return text = self.components.currentItem().text() parameters = self.get_parameters[int(text)] self.set_parameters(parameters.algorithm, parameters.values) def event(self, event: QEvent): if event.type() == QEvent.WindowActivate: index = self.components.currentRow() self.components.clear() self.components.addItems(list(map(str, self.get_parameters.keys()))) self.components.setCurrentRow(index) return super().event(event)
class Properties(QWidget): def __init__(self, settings: PartSettings): super().__init__() self._settings = settings self.export_btn = QPushButton("Export profile") self.export_btn.clicked.connect(self.export_profile) self.import_btn = QPushButton("Import profile") self.import_btn.clicked.connect(self.import_profiles) self.export_pipeline_btn = QPushButton("Export pipeline") self.export_pipeline_btn.clicked.connect(self.export_pipeline) self.import_pipeline_btn = QPushButton("Import pipeline") self.import_pipeline_btn.clicked.connect(self.import_pipeline) self.delete_btn = QPushButton("Delete profile") self.delete_btn.setDisabled(True) self.delete_btn.clicked.connect(self.delete_profile) self.multiple_files_chk = QCheckBox("Show multiple files panel") self._update_measurement_chk() self.multiple_files_chk.stateChanged.connect( self.multiple_files_visibility) self.rename_btn = QPushButton("Rename profile") self.rename_btn.clicked.connect(self.rename_profile) self.rename_btn.setDisabled(True) self.voxel_size_label = QLabel() self.info_label = QPlainTextEdit() self.info_label.setReadOnly(True) self.profile_list = SearchableListWidget() self.profile_list.currentTextChanged.connect(self.profile_chosen) self.pipeline_list = SearchableListWidget() self.pipeline_list.currentTextChanged.connect(self.profile_chosen) self.spacing = [QDoubleSpinBox() for _ in range(3)] self.lock_spacing = LockCheckBox() self.lock_spacing.stateChanged.connect(self.spacing[1].setDisabled) self.lock_spacing.stateChanged.connect(self.synchronize_spacing) # noinspection PyUnresolvedReferences self.spacing[2].valueChanged.connect(self.synchronize_spacing) self._settings.roi_profiles_changed.connect(self.update_profile_list) self._settings.roi_pipelines_changed.connect(self.update_profile_list) self._settings.connect_("multiple_files_widget", self._update_measurement_chk) units_value = self._settings.get("units_value", Units.nm) for el in self.spacing: el.setAlignment(Qt.AlignRight) el.setButtonSymbols(QAbstractSpinBox.NoButtons) el.setRange(0, 1000000) # noinspection PyUnresolvedReferences el.valueChanged.connect(self.image_spacing_change) self.units = QEnumComboBox(enum_class=Units) self.units.setCurrentEnum(units_value) # noinspection PyUnresolvedReferences self.units.currentIndexChanged.connect(self.update_spacing) spacing_layout = QHBoxLayout() spacing_layout.addWidget(self.lock_spacing) for txt, el in zip(["x", "y", "z"], self.spacing[::-1]): spacing_layout.addWidget(QLabel(txt + ":")) spacing_layout.addWidget(el) spacing_layout.addWidget(self.units) spacing_layout.addStretch(1) voxel_size_layout = QHBoxLayout() voxel_size_layout.addWidget(self.voxel_size_label) voxel_size_layout.addSpacing(30) profile_layout = QGridLayout() profile_layout.setSpacing(0) profile_layout.addWidget(QLabel("Profiles:"), 0, 0) profile_layout.addWidget(self.profile_list, 1, 0) profile_layout.addWidget(QLabel("Pipelines:"), 2, 0) profile_layout.addWidget(self.pipeline_list, 3, 0, 4, 1) profile_layout.addWidget(self.info_label, 1, 1, 3, 2) profile_layout.addWidget(self.export_btn, 4, 1) profile_layout.addWidget(self.import_btn, 4, 2) profile_layout.addWidget(self.export_pipeline_btn, 5, 1) profile_layout.addWidget(self.import_pipeline_btn, 5, 2) profile_layout.addWidget(self.delete_btn, 6, 1) profile_layout.addWidget(self.rename_btn, 6, 2) layout = QVBoxLayout() layout.addLayout(spacing_layout) layout.addLayout(voxel_size_layout) layout.addWidget(self.multiple_files_chk) layout.addLayout(profile_layout, 1) self.setLayout(layout) self.update_profile_list() @Slot(int) def multiple_files_visibility(self, val: int): self._settings.set("multiple_files_widget", val) # @Slot(str) # PySide bug def profile_chosen(self, text): if text == "": self.delete_btn.setEnabled(False) self.rename_btn.setEnabled(False) self.info_label.setPlainText("") return try: if self.sender() == self.profile_list.list_widget: profile = self._settings.roi_profiles[text] self.pipeline_list.selectionModel().clear() self.delete_btn.setText("Delete profile") self.rename_btn.setText("Rename profile") elif self.sender() == self.pipeline_list.list_widget: profile = self._settings.roi_pipelines[text] self.profile_list.selectionModel().clear() self.delete_btn.setText("Delete pipeline") self.rename_btn.setText("Rename pipeline") else: return except KeyError: return # TODO update with knowledge from profile dict self.delete_btn.setEnabled(True) self.rename_btn.setEnabled(True) self.info_label.setPlainText( profile.pretty_print(analysis_algorithm_dict)) def synchronize_spacing(self): if self.lock_spacing.isChecked(): self.spacing[1].setValue(self.spacing[2].value()) def image_spacing_change(self): spacing = [ el.value() / UNIT_SCALE[self.units.currentIndex()] for i, el in enumerate(self.spacing) ] if not self.spacing[0].isEnabled(): spacing = spacing[1:] self._settings.image_spacing = spacing voxel_size = 1 for el in self._settings.image_spacing: voxel_size *= el * UNIT_SCALE[self.units.currentIndex()] self.voxel_size_label.setText( f"Voxel_size: {voxel_size} {self.units.currentEnum().name} <sup>{len(self._settings.image_spacing)}</sup>" ) def update_spacing(self, index=None): voxel_size = 1 value = self.units.currentEnum() if index is not None: self._settings.set("units_value", value) for el, sp in zip(self.spacing[::-1], self._settings.image_spacing[::-1]): el.blockSignals(True) current_size = sp * UNIT_SCALE[self.units.currentIndex()] voxel_size *= current_size el.setValue(current_size) el.blockSignals(False) self.spacing[0].setDisabled(len(self._settings.image_spacing) == 2) self.voxel_size_label.setText( f"Voxel_size: {voxel_size} {value.name} <sup>{len(self._settings.image_spacing)}</sup>" ) def update_profile_list(self): current_names = set(self._settings.roi_profiles.keys()) self.profile_list.clear() self.profile_list.addItems(sorted(current_names)) self.pipeline_list.clear() self.pipeline_list.addItems( sorted(set(self._settings.roi_pipelines.keys()))) self.delete_btn.setDisabled(True) self.rename_btn.setDisabled(True) self.info_label.setPlainText("") def showEvent(self, _event): self.update_spacing() def event(self, event: QEvent): if event.type() == QEvent.WindowActivate and self.isVisible(): self.update_spacing() return super().event(event) @ensure_main_thread def _update_measurement_chk(self): self.multiple_files_chk.setChecked( self._settings.get("multiple_files_widget", False)) def delete_profile(self): text, dkt = "", {} if self.profile_list.selectedItems(): text = self.profile_list.selectedItems()[0].text() dkt = self._settings.roi_profiles elif self.pipeline_list.selectedItems(): text = self.pipeline_list.selectedItems()[0].text() dkt = self._settings.roi_pipelines if text != "": self.delete_btn.setDisabled(True) del dkt[text] self.update_profile_list() def export_profile(self): exp = ExportDialog(self._settings.roi_profiles, ProfileDictViewer) if not exp.exec_(): return dial = PSaveDialog( "Segment profile (*.json)", settings=self._settings, path=IO_SAVE_DIRECTORY, caption="Export profile segment", ) dial.selectFile("segment_profile.json") if dial.exec_(): file_path = dial.selectedFiles()[0] data = { x: self._settings.roi_profiles[x] for x in exp.get_export_list() } with open(file_path, "w", encoding="utf-8") as ff: json.dump(data, ff, cls=self._settings.json_encoder_class, indent=2) def import_profiles(self): dial = PLoadDialog( "Segment profile (*.json)", settings=self._settings, path=IO_SAVE_DIRECTORY, caption="Import profile segment", ) if dial.exec_(): file_path = dial.selectedFiles()[0] profs, err = self._settings.load_part(file_path) if err: QMessageBox.warning( self, "Import error", "error during importing, part of data were filtered.") profiles_dict = self._settings.roi_profiles imp = ImportDialog(profs, profiles_dict, ProfileDictViewer) if not imp.exec_(): return for original_name, final_name in imp.get_import_list(): profiles_dict[final_name] = profs[original_name] self._settings.dump() self.update_profile_list() def export_pipeline(self): exp = ExportDialog(self._settings.roi_pipelines, ProfileDictViewer) if not exp.exec_(): return dial = PSaveDialog( "Segment pipeline (*.json)", settings=self._settings, path=IO_SAVE_DIRECTORY, caption="Export pipeline segment", ) dial.selectFile("segment_pipeline.json") if dial.exec_(): file_path = dial.selectedFiles()[0] data = { x: self._settings.roi_pipelines[x] for x in exp.get_export_list() } with open(file_path, "w", encoding="utf-8") as ff: json.dump(data, ff, cls=self._settings.json_encoder_class, indent=2) def import_pipeline(self): dial = PLoadDialog( "Segment pipeline (*.json)", settings=self._settings, path=IO_SAVE_DIRECTORY, caption="Import pipeline segment", ) if dial.exec_(): file_path = dial.selectedFiles()[0] profs, err = self._settings.load_part(file_path) if err: QMessageBox.warning( self, "Import error", "error during importing, part of data were filtered.") profiles_dict = self._settings.roi_pipelines imp = ImportDialog(profs, profiles_dict, ProfileDictViewer) if not imp.exec_(): return for original_name, final_name in imp.get_import_list(): profiles_dict[final_name] = profs[original_name] self._settings.dump() self.update_profile_list() def rename_profile(self): profile_name, profiles_dict = "", {} if self.profile_list.selectedItems(): profile_name = self.profile_list.selectedItems()[0].text() profiles_dict = self._settings.roi_profiles elif self.pipeline_list.selectedItems(): profile_name = self.pipeline_list.selectedItems()[0].text() profiles_dict = self._settings.roi_pipelines if profile_name == "": return text, ok = QInputDialog.getText(self, "New profile name", f"New name for {profile_name}", text=profile_name) if ok: text = text.strip() if text in profiles_dict.keys(): res = QMessageBox.warning( self, "Already exist", f"Profile with name {text} already exist. Would you like to overwrite?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No, ) if res == QMessageBox.No: self.rename_profile() return profiles_dict[text] = profiles_dict.pop(profile_name) self._settings.dump() self.update_profile_list()
class PyDMLogDisplay(QWidget, LogLevels): """ Standard display for Log Output This widget handles instantating a ``GuiHandler`` and displaying log messages to a ``QPlainTextEdit``. The level of the log can be changed from inside the widget itself, allowing users to select from any of the ``.levels`` specified by the widget. Parameters ---------- parent : QObject, optional logname : str Name of log to display in widget level : logging.Level Initial level of log display """ Q_ENUMS(LogLevels) LogLevels = LogLevels terminator = '\n' default_format = '%(asctime)s %(message)s' default_level = logging.INFO def __init__(self, parent=None, logname=None, level=logging.NOTSET): QWidget.__init__(self, parent=parent) # Create Widgets self.label = QLabel('Minimum displayed log level: ', parent=self) self.combo = QComboBox(parent=self) self.text = QPlainTextEdit(parent=self) self.text.setReadOnly(True) self.clear_btn = QPushButton("Clear", parent=self) # Create layout layout = QVBoxLayout() level_control = QHBoxLayout() level_control.addWidget(self.label) level_control.addWidget(self.combo) layout.addLayout(level_control) layout.addWidget(self.text) layout.addWidget(self.clear_btn) self.setLayout(layout) # Allow QCombobox to control log level for log_level, value in LogLevels.as_dict().items(): self.combo.addItem(log_level, value) self.combo.currentIndexChanged[str].connect(self.setLevel) # Allow QPushButton to clear log text self.clear_btn.clicked.connect(self.clear) # Create a handler with the default format self.handler = GuiHandler(level=level, parent=self) self.logFormat = self.default_format self.handler.message.connect(self.write) # Create logger. Either as a root or given logname self.log = None self.level = None self.logName = logname or '' self.logLevel = level self.destroyed.connect(functools.partial(logger_destroyed, self.log)) def sizeHint(self): return QSize(400, 300) @Property(LogLevels) def logLevel(self): return self.level @logLevel.setter def logLevel(self, level): if level != self.level: self.level = level idx = self.combo.findData(level) self.combo.setCurrentIndex(idx) @Property(str) def logName(self): """Name of associated log""" return self.log.name @logName.setter def logName(self, name): # Disconnect prior log from handler if self.log: self.log.removeHandler(self.handler) # Reattach handler to new handler self.log = logging.getLogger(name) # Ensure that the log matches level of handler # only if the handler level is less than the log. if self.log.level < self.handler.level: self.log.setLevel(self.handler.level) # Attach preconfigured handler self.log.addHandler(self.handler) @Property(str) def logFormat(self): """Format for log messages""" return self.handler.formatter._fmt @logFormat.setter def logFormat(self, fmt): self.handler.setFormatter(logging.Formatter(fmt)) @Slot(str) def write(self, message): """Write a message to the log display""" # We split the incoming message by new lines. In prior iterations of # this widget it was discovered that large blocks of text cause issues # at the Qt level. for msg in message.split(self.terminator): self.text.appendPlainText(msg) @Slot() def clear(self): """Clear the text area.""" self.text.clear() @Slot(str) def setLevel(self, level): """Set the level of the contained logger""" # Get the level from the incoming string specification try: level = getattr(logging, level.upper()) except AttributeError as exc: logger.exception("Invalid logging level specified %s", level.upper()) else: # Set the existing handler and logger to this level self.handler.setLevel(level) if self.log.level > self.handler.level or self.log.level == logging.NOTSET: self.log.setLevel(self.handler.level)