コード例 #1
0
    def __init__(self, parent=None, *, file_path="", calib_preview=""):

        super().__init__(parent)

        self.setWindowTitle("View Calibration Standard")

        self.setMinimumSize(300, 400)
        self.resize(700, 700)

        # Displayed data (must be set before the dialog is shown
        self.file_path = file_path
        self.calib_preview = calib_preview

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel("<b>Source file:</b> "), 0, Qt.AlignTop)
        file_path = textwrap.fill(self.file_path, width=80)
        hbox.addWidget(QLabel(file_path), 0, Qt.AlignTop)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        te = QTextEdit()
        te.setReadOnly(True)
        te.setText(self.calib_preview)
        vbox.addWidget(te)

        button_box = QDialogButtonBox(QDialogButtonBox.Close)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        vbox.addWidget(button_box)

        self.setLayout(vbox)
コード例 #2
0
def _credits_tab():
    #scroll = QScrollArea()
    #scroll.setWidget(self)
    #scroll.setWidgetResizable(True)
    ##scroll.setFixedHeight(400)
    #layout.addWidget(scroll)

    #vbox = QVBoxLayout()
    #vbox.addLayout(layout)
    #vbox.addStretch()

    scrollArea = QScrollArea()
    scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
    scrollArea.setWidgetResizable(True)
    #scrollArea->setGeometry( 10, 10, 200, 200 );

    package_tab = QWidget()
    scrollArea.setWidget(package_tab)

    widget = QWidget(scrollArea)

    vbox = QVBoxLayout(widget)
    text = QTextEdit(CREDITS)
    text.setReadOnly(True)
    vbox.addWidget(text)
    #vbox.addLayout(scrollArea)

    package_tab = QWidget()
    package_tab.setLayout(vbox)
    return package_tab
コード例 #3
0
ファイル: Window.py プロジェクト: desty2k/QtPyBotnet
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)
コード例 #4
0
            def show_tb(parent):
                tbdialog = QDialog(parent=parent.parent())
                tbdialog.setModal(True)
                # this is about the minimum width to not get rewrap
                # and the minimum height to not have scrollbar
                tbdialog.resize(650, 270)
                tbdialog.setLayout(QVBoxLayout())

                text = QTextEdit()
                text.setHtml(notification.as_html())
                text.setReadOnly(True)
                btn = QPushButton('Enter Debugger')

                def _enter_debug_mode():
                    btn.setText(
                        'Now Debugging. Please quit debugger in console '
                        'to continue'
                    )
                    _debug_tb(notification.exception.__traceback__)
                    btn.setText('Enter Debugger')

                btn.clicked.connect(_enter_debug_mode)
                tbdialog.layout().addWidget(text)
                tbdialog.layout().addWidget(btn, 0, Qt.AlignRight)
                tbdialog.show()
コード例 #5
0
ファイル: TermsFrame.py プロジェクト: desty2k/QtPyBotnet
class TermsFrame(ConfigBaseFrame):
    def __init__(self, parent=None):
        super(TermsFrame, self).__init__(parent)
        self.setObjectName("botnet_termsframe_start")
        self.disable_next_on_enter = True

        self.widget_layout = QVBoxLayout(self)
        self.setLayout(self.widget_layout)

        self.terms = QTextEdit(self)
        self.terms.setReadOnly(True)
        self.terms.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.terms.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.terms.setLineWrapMode(QTextEdit.WidgetWidth)
        self.terms.setWordWrapMode(QTextOption.WrapAnywhere)
        with open(os.path.join(sys.path[0], "../LICENSE")) as f:
            self.terms.setText(f.read())
        self.widget_layout.addWidget(self.terms)

        self.accept = QCheckBox(self)
        self.accept.setChecked(False)
        self.accept.setText("Accept license")
        self.accept.setObjectName("license_checkbox")
        self.widget_layout.addWidget(self.accept)

        QMetaObject.connectSlotsByName(self)

    @Slot(bool)
    def on_license_checkbox_clicked(self, checked):
        self.set_next_enabled.emit(checked)
        self.disable_next_on_enter = not checked

    @Slot()
    def collect_info(self):
        return {"terms_accepted": self.accept.isChecked()}
コード例 #6
0
class FileViewer(QDialog):
    def __init__(self, parent, file_name, job_name, job_number, realization,
                 iteration):
        super(FileViewer, self).__init__(parent)

        self.setWindowTitle("{} # {} Realization: {} Iteration: {}" \
                            .format(job_name, job_number, realization, iteration))

        self.text_cont = QTextEdit()
        self.text_cont.setReadOnly(True)
        close_button = QPushButton("Close")
        close_button.clicked.connect(self.accept)

        layout = QGridLayout(self)
        layout.addWidget(self.text_cont)
        layout.addWidget(close_button)

        self.setMinimumWidth(400)
        self.setMinimumHeight(200)

        self.reload(file_name)

    def reload(self, file_name):
        with open(file_name) as f:
            text = f.read()
        self.text_cont.setText(text)
        self.show()
コード例 #7
0
ファイル: qt_notification.py プロジェクト: jojoelfe/napari
            def show_tb(parent):
                tbdialog = QDialog(parent=parent.parent())
                tbdialog.setModal(True)
                # this is about the minimum width to not get rewrap
                # and the minimum height to not have scrollbar
                tbdialog.resize(650, 270)
                tbdialog.setLayout(QVBoxLayout())

                text = QTextEdit()
                theme = get_theme(
                    get_settings().appearance.theme, as_dict=False
                )
                _highlight = Pylighter(  # noqa: F841
                    text.document(), "python", theme.syntax_style
                )
                text.setText(notification.as_text())
                text.setReadOnly(True)
                btn = QPushButton(trans._('Enter Debugger'))

                def _enter_debug_mode():
                    btn.setText(
                        trans._(
                            'Now Debugging. Please quit debugger in console to continue'
                        )
                    )
                    _debug_tb(notification.exception.__traceback__)
                    btn.setText(trans._('Enter Debugger'))

                btn.clicked.connect(_enter_debug_mode)
                tbdialog.layout().addWidget(text)
                tbdialog.layout().addWidget(btn, 0, Qt.AlignRight)
                tbdialog.show()
コード例 #8
0
class ServerInfoDialog(BaseDialog):
    """Dialog window showing information about notebook servers."""
    def __init__(self, server_info, parent=None):
        """
        Construct a RecoveryDialog.

        Parameters
        ----------
        servers : list of ServerProcess
            Information to be displayed. This parameter is read only.
        parent : QWidget, optional
            Parent of the dialog window. The default is None.
        """
        super().__init__(parent)
        self.servers = server_info

        self.setWindowTitle(_('Notebook server info'))

        self.layout = QVBoxLayout(self)
        self.formlayout = QFormLayout()
        self.layout.addLayout(self.formlayout)

        self.process_combo = QComboBox(self)
        self.process_combo.currentIndexChanged.connect(self.select_process)
        self.formlayout.addRow(_('Process ID:'), self.process_combo)

        self.dir_lineedit = QLineEdit(self)
        self.dir_lineedit.setReadOnly(True)
        self.formlayout.addRow(_('Notebook dir:'), self.dir_lineedit)

        self.state_lineedit = QLineEdit(self)
        self.state_lineedit.setReadOnly(True)
        self.formlayout.addRow(_('State:'), self.state_lineedit)

        self.log_textedit = QTextEdit(self)
        self.log_textedit.setReadOnly(True)
        self.layout.addWidget(self.log_textedit)

        self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok, self)
        self.buttonbox.accepted.connect(self.accept)
        self.refresh_button = QPushButton(_('Refresh'), self)
        self.refresh_button.clicked.connect(self.refresh_data)
        self.buttonbox.addButton(self.refresh_button,
                                 QDialogButtonBox.ActionRole)
        self.layout.addWidget(self.buttonbox)

        self.refresh_data()

    def refresh_data(self):
        self.process_combo.clear()
        for server in self.servers:
            self.process_combo.addItem(str(server.process.processId()))
        self.select_process(0)

    def select_process(self, index):
        self.dir_lineedit.setText(self.servers[index].notebook_dir)
        self.state_lineedit.setText(
            SERVER_STATE_DESCRIPTIONS[self.servers[index].state])
        self.log_textedit.setPlainText(self.servers[index].output)
コード例 #9
0
            def show_tb(parent):
                tbdialog = QDialog(parent=parent.parent())
                tbdialog.setModal(True)
                # this is about the minimum width to not get rewrap
                # and the minimum height to not have scrollbar
                tbdialog.resize(650, 270)
                tbdialog.setLayout(QVBoxLayout())

                text = QTextEdit()
                text.setHtml(notification.as_html())
                text.setReadOnly(True)
                tbdialog.layout().addWidget(text)
                tbdialog.show()
コード例 #10
0
ファイル: text_view.py プロジェクト: janpipek/boadata
class TextView(View):
    title = "Text"

    supported_types = ("text",)

    def __init__(self, data_object):
        super(TextView, self).__init__(data_object)

    def create_widget(self, parent=None):
        self.text_widget = QTextEdit(parent)
        self.text_widget.setReadOnly(True)
        do = self.data_object.convert("text")
        self.text_widget.setText(do.inner_data)
        return self.text_widget
コード例 #11
0
class ModuleDetailsWindow(FramelessWindow):
    def __init__(self, parent):
        super(ModuleDetailsWindow, self).__init__(parent)
        self.content_widget = QWidget(self)
        self.widget_layout = QFormLayout(self.content_widget)
        self.content_widget.setLayout(self.widget_layout)
        self.addContentWidget(self.content_widget)

        self.module_code = QTextEdit(self.content_widget)
        self.module_code.setReadOnly(True)
        self.widget_layout.addWidget(self.module_code)

    def set_module(self, module: Module):
        try:
            self.module_code.setText(base64.b64decode(module.code).decode())
        except Exception as e:
            self.module_code.setText(
                "Exception while loading module's code: {}".format(e))
コード例 #12
0
ファイル: about.py プロジェクト: YuDe95/pyNastran
def _credits_tab():
    """creates the credits tab"""
    scroll_area = QScrollArea()
    scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
    scroll_area.setWidgetResizable(True)
    scroll_widget = QWidget(scroll_area)

    widget = QWidget()
    scroll_area.setWidget(widget)

    vbox = QVBoxLayout(scroll_widget)
    text = QTextEdit(CREDITS)
    text.setReadOnly(True)
    vbox.addWidget(text)

    package_tab = QWidget()
    package_tab.setLayout(vbox)
    return package_tab
コード例 #13
0
    def __init__(self,
                 run_button=None,
                 qwidgets_to_disable=None,
                 text_edit=None,
                 busy=None,
                 timer_label=None,
                 parent=None,
                 setup_layout=True,
                 **kwargs):
        super(JobRunner, self).__init__(parent, **kwargs)

        self.is_passed = False
        self._iteration_edit = QLineEdit(self, placeholderText="Iteration (n)")
        self._iteration_edit.setText('5')
        if run_button is None:
            run_button = QPushButton("Calculate Prime", self)
            run_button.clicked.connect(self.prime_requested)

        if text_edit is None:
            text_edit = QTextEdit("Result:<br>", self)
            text_edit.setReadOnly(True)

        if busy is None:
            busy = QProgressBar(self)
        self._busy = busy

        if timer_label is None:
            timer_label = QLabel('0:00')
        self._timer_label = timer_label

        self._busy.setVisible(False)
        self._timer_label.setVisible(False)
        self._run_button = run_button
        self._text_edit = text_edit
        self._time0 = None
        self.process = None

        if qwidgets_to_disable is None:
            qwidgets_to_disable = [self._run_button, self._iteration_edit]
        self.qwidgets_to_disable = qwidgets_to_disable

        if setup_layout:
            self.setup_layout()
コード例 #14
0
ファイル: EditSrcCodeInfoDialog.py プロジェクト: xxoolm/Ryven
    def __init__(self, parent):
        super(EditSrcCodeInfoDialog, self).__init__(parent)

        self.setLayout(QGridLayout())

        # info text edit
        info_text_edit = QTextEdit()
        info_text_edit.setHtml('''
            <h2 style="font-family: Poppins; font-size: xx-large; color: #a9d5ef;">Some info before you delete the
            universe</h2>
            <div style="font-family: Corbel; font-size: x-large;">
                <p>
                    Yes, you can change method implementations of objects.
                    This can be quite useful but since changing an instance's implementation at runtime is kinda sketchy, 
                    you should be a bit careful, it's not exactly bulletproof, and doesnt <i>always</i> work.
                    When you override a method implementation, a new function object will be created using python's ast 
                    module, which then gets bound to the object as method, which essentially shadows the old implementation. 
                    Therefore, you might need to add imports etc. you node uses in the original nodes package. 
                    All changes are temporary and only apply on a single 
                    object.
                </p>
                <p>
                    Have fun.
                </p>
            </div>
        ''')
        info_text_edit.setReadOnly(True)
        self.layout().addWidget(info_text_edit, 0, 0, 1, 2)

        dont_show_again_button = QPushButton('Stop being annoying')
        dont_show_again_button.clicked.connect(self.close_and_dont_show_again)
        self.layout().addWidget(dont_show_again_button, 1, 0)

        ok_button = QPushButton('Got it')
        ok_button.clicked.connect(self.accept)
        self.layout().addWidget(ok_button, 1, 1)

        ok_button.setFocus()

        self.setWindowTitle('Editing Source Code Info')
        self.resize(560, 366)
コード例 #15
0
ファイル: dlg_view_metadata.py プロジェクト: mrakitin/PyXRF
class DialogViewMetadata(QDialog):
    def __init__(self):

        super().__init__()

        self.resize(500, 500)
        self.setWindowTitle("Run Metadata")

        self.te_meta = QTextEdit()
        self.te_meta.setReadOnly(True)

        # 'Close' button box
        button_box = QDialogButtonBox(QDialogButtonBox.Close)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)

        vbox = QVBoxLayout()
        vbox.addWidget(self.te_meta)
        vbox.addWidget(button_box)
        self.setLayout(vbox)

    def setText(self, text):
        self.te_meta.setText(text)
コード例 #16
0
ファイル: NodeDetailsWidget.py プロジェクト: xxoolm/Ryven
class NodeDetailsWidget(QGroupBox):
    def __init__(self, main_window, nodes_tree_widget):
        super().__init__('details')

        self.main_window = main_window
        self.nodes_tree_widget = nodes_tree_widget
        self.nodes_tree_widget.details_widget = self

        self.setLayout(QVBoxLayout())

        self.doc_text_edit = QTextEdit()
        self.doc_text_edit.setFont(QFont('Consolas', 9))
        self.doc_text_edit.setReadOnly(True)

        self.layout().addWidget(self.doc_text_edit)

        self.node = None

    def set_node(self, node):
        self.node = node
        self.update_details()

    def update_details(self):
        self.doc_text_edit.setText(self.node.doc)
コード例 #17
0
ファイル: __init__.py プロジェクト: QuQBaZhaHei/search-engine
class MessageBox(DialogBase):
    """Base message box dialog."""

    QUESTION_BOX = 100
    INFORMATION_BOX = 101
    ERROR_BOX = 102
    REMOVE_BOX = 103

    sig_url_clicked = Signal(object)

    def __init__(self, type_, error='', title='', text='', learn_more=None):
        """Base message box dialog."""
        super(MessageBox, self).__init__()
        #        self.tracker = GATracker()
        self.label_text = QLabel(to_text_string(text))
        self.textbox_error = QTextEdit()
        self.button_ok = ButtonPrimary('Ok')
        self.button_yes = ButtonPrimary('Yes')
        self.button_no = ButtonNormal('No')
        self.button_copy = ButtonNormal('Copy text')
        self.button_learn = ButtonNormal('Learn more')
        self.button_remove = ButtonDanger('Remove')
        self.button_cancel = ButtonNormal('Cancel')
        self.bbox = QDialogButtonBox(Qt.Horizontal)

        self.label_text.setOpenExternalLinks(False)
        self.label_text.linkActivated.connect(self.url_clicked)
        self.textbox_error.setReadOnly(True)
        self.textbox_error.setFrameStyle(QTextEdit.Plain)
        self.textbox_error.setFrameShape(QTextEdit.NoFrame)
        self.setMinimumWidth(260)
        self.textbox_error.verticalScrollBar().show()
        self.setWindowTitle(to_text_string(title))

        error = to_text_string(error).split('\n')
        error = '<br>'.join(error)
        self.textbox_error.setText(error)

        # Layouts
        layout = QVBoxLayout()
        layout.addWidget(self.label_text)
        layout.addWidget(SpacerVertical())
        if error:
            layout.addWidget(self.textbox_error)
            layout.addWidget(SpacerVertical())
            layout.addWidget(self.button_copy)
            layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()

        layout.addLayout(layout_buttons)

        self.layout = layout
        self.setLayout(layout)

        # Signals
        self.button_copy.clicked.connect(self.copy_text)
        self.button_ok.clicked.connect(self.accept)
        self.button_yes.clicked.connect(self.accept)
        self.button_no.clicked.connect(self.reject)
        self.button_remove.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)

        # Setup
        self.button_learn.setVisible(bool(learn_more))
        if bool(learn_more):
            layout_buttons.addWidget(self.button_learn)
            layout_buttons.addWidget(SpacerHorizontal())
            self.button_learn.clicked.connect(
                lambda: self.show_url(learn_more)
            )

        if type_ == self.ERROR_BOX:
            layout_buttons.addWidget(self.button_ok)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.INFORMATION_BOX:
            layout_buttons.addWidget(self.button_ok)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
            self.textbox_error.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.QUESTION_BOX:
            layout_buttons.addStretch()
            layout_buttons.addWidget(self.button_no)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_yes)
            layout_buttons.addWidget(SpacerHorizontal())
            self.textbox_error.setVisible(False)
            self.button_ok.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.REMOVE_BOX:
            layout_buttons.addStretch()
            layout_buttons.addWidget(self.button_cancel)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_remove)
            layout_buttons.addWidget(SpacerHorizontal())
            self.textbox_error.setVisible(False)
            self.button_ok.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)

    def url_clicked(self, url):
        """Emit url interaction."""
        self.sig_url_clicked.emit(url)

    def copy_text(self):
        """Copy all the content of the displayed error message."""
        self.textbox_error.selectAll()
        self.textbox_error.copy()

    def show_url(self, url=None):
        """Open url in default browser."""
        if url:
            qurl = QUrl(url)
            QDesktopServices.openUrl(qurl)
            self.tracker.track_event('help', 'documentation', url)
コード例 #18
0
class CreatePlan(QWidget):

    plan_created = Signal()
    plan_node_changed = Signal()

    def __init__(self, settings: PartSettings):
        super().__init__()
        self.settings = settings
        self.save_translate_dict: typing.Dict[str, SaveBase] = {x.get_short_name(): x for x in save_dict.values()}
        self.plan = PlanPreview(self)
        self.save_plan_btn = QPushButton("Save")
        self.clean_plan_btn = QPushButton("Remove all")
        self.remove_btn = QPushButton("Remove")
        self.update_element_chk = QCheckBox("Update element")
        self.change_root = EnumComboBox(RootType)
        self.save_choose = QComboBox()
        self.save_choose.addItem("<none>")
        self.save_choose.addItems(list(self.save_translate_dict.keys()))
        self.save_btn = QPushButton("Save")
        self.segment_profile = SearchableListWidget()
        self.pipeline_profile = SearchableListWidget()
        self.segment_stack = QTabWidget()
        self.segment_stack.addTab(self.segment_profile, "Profile")
        self.segment_stack.addTab(self.pipeline_profile, "Pipeline")
        self.generate_mask_btn = QPushButton("Add mask")
        self.generate_mask_btn.setToolTip("Mask need to have unique name")
        self.mask_name = QLineEdit()
        self.mask_operation = EnumComboBox(MaskOperation)

        self.chanel_num = QSpinBox()
        self.choose_channel_for_measurements = QComboBox()
        self.choose_channel_for_measurements.addItems(
            ["Same as segmentation"] + [str(x + 1) for x in range(MAX_CHANNEL_NUM)]
        )
        self.units_choose = EnumComboBox(Units)
        self.units_choose.set_value(self.settings.get("units_value", Units.nm))
        self.chanel_num.setRange(0, 10)
        self.expected_node_type = None
        self.save_constructor = None

        self.chose_profile_btn = QPushButton("Add Profile")
        self.get_big_btn = QPushButton("Leave the biggest")
        self.get_big_btn.hide()
        self.get_big_btn.setDisabled(True)
        self.measurements_list = SearchableListWidget(self)
        self.measurement_name_prefix = QLineEdit(self)
        self.add_calculation_btn = QPushButton("Add measurement calculation")
        self.information = QTextEdit()
        self.information.setReadOnly(True)

        self.protect = False
        self.mask_set = set()
        self.calculation_plan = CalculationPlan()
        self.plan.set_plan(self.calculation_plan)
        self.segmentation_mask = MaskWidget(settings)
        self.file_mask = FileMask()

        self.change_root.currentIndexChanged.connect(self.change_root_type)
        self.save_choose.currentTextChanged.connect(self.save_changed)
        self.measurements_list.currentTextChanged.connect(self.show_measurement)
        self.segment_profile.currentTextChanged.connect(self.show_segment)
        self.measurements_list.currentTextChanged.connect(self.show_measurement_info)
        self.segment_profile.currentTextChanged.connect(self.show_segment_info)
        self.pipeline_profile.currentTextChanged.connect(self.show_segment_info)
        self.pipeline_profile.currentTextChanged.connect(self.show_segment)
        self.mask_name.textChanged.connect(self.mask_name_changed)
        self.generate_mask_btn.clicked.connect(self.create_mask)
        self.clean_plan_btn.clicked.connect(self.clean_plan)
        self.remove_btn.clicked.connect(self.remove_element)
        self.mask_name.textChanged.connect(self.mask_text_changed)
        self.chose_profile_btn.clicked.connect(self.add_segmentation)
        self.get_big_btn.clicked.connect(self.add_leave_biggest)
        self.add_calculation_btn.clicked.connect(self.add_measurement)
        self.save_plan_btn.clicked.connect(self.add_calculation_plan)
        # self.forgot_mask_btn.clicked.connect(self.forgot_mask)
        # self.cmap_save_btn.clicked.connect(self.save_to_cmap)
        self.save_btn.clicked.connect(self.add_save_to_project)
        self.update_element_chk.stateChanged.connect(self.mask_text_changed)
        self.update_element_chk.stateChanged.connect(self.show_measurement)
        self.update_element_chk.stateChanged.connect(self.show_segment)
        self.update_element_chk.stateChanged.connect(self.update_names)
        self.segment_stack.currentChanged.connect(self.change_segmentation_table)

        plan_box = QGroupBox("Prepare workflow:")
        lay = QVBoxLayout()
        lay.addWidget(self.plan)
        bt_lay = QGridLayout()
        bt_lay.setSpacing(1)
        bt_lay.addWidget(self.save_plan_btn, 0, 0)
        bt_lay.addWidget(self.clean_plan_btn, 0, 1)
        bt_lay.addWidget(self.remove_btn, 1, 0)
        bt_lay.addWidget(self.update_element_chk, 1, 1)
        lay.addLayout(bt_lay)
        plan_box.setLayout(lay)
        plan_box.setStyleSheet(group_sheet)

        other_box = QGroupBox("Other operations:")
        other_box.setContentsMargins(0, 0, 0, 0)
        bt_lay = QVBoxLayout()
        bt_lay.setSpacing(0)
        bt_lay.addWidget(QLabel("Root type:"))
        bt_lay.addWidget(self.change_root)
        bt_lay.addStretch(1)
        bt_lay.addWidget(QLabel("Saving:"))
        bt_lay.addWidget(self.save_choose)
        bt_lay.addWidget(self.save_btn)
        other_box.setLayout(bt_lay)
        other_box.setStyleSheet(group_sheet)

        mask_box = QGroupBox("Use mask from:")
        mask_box.setStyleSheet(group_sheet)
        self.mask_stack = QTabWidget()

        self.mask_stack.addTab(stretch_widget(self.file_mask), "File")
        self.mask_stack.addTab(stretch_widget(self.segmentation_mask), "Current ROI")
        self.mask_stack.addTab(stretch_widget(self.mask_operation), "Operations on masks")
        self.mask_stack.setTabToolTip(2, "Allows to create mask which is based on masks previously added to plan.")

        lay = QGridLayout()
        lay.setSpacing(0)
        lay.addWidget(self.mask_stack, 0, 0, 1, 2)
        label = QLabel("Mask name:")
        label.setToolTip("Needed if you would like to reuse this mask in tab 'Operations on masks'")
        self.mask_name.setToolTip("Needed if you would like to reuse this mask in tab 'Operations on masks'")
        lay.addWidget(label, 1, 0)
        lay.addWidget(self.mask_name, 1, 1)
        lay.addWidget(self.generate_mask_btn, 2, 0, 1, 2)
        mask_box.setLayout(lay)

        segment_box = QGroupBox("ROI extraction:")
        segment_box.setStyleSheet(group_sheet)
        lay = QVBoxLayout()
        lay.setSpacing(0)
        lay.addWidget(self.segment_stack)
        lay.addWidget(self.chose_profile_btn)
        lay.addWidget(self.get_big_btn)
        segment_box.setLayout(lay)

        measurement_box = QGroupBox("Set of measurements:")
        measurement_box.setStyleSheet(group_sheet)
        lay = QGridLayout()
        lay.setSpacing(0)
        lay.addWidget(self.measurements_list, 0, 0, 1, 2)
        lab = QLabel("Name prefix:")
        lab.setToolTip("Prefix added before each column name")
        lay.addWidget(lab, 1, 0)
        lay.addWidget(self.measurement_name_prefix, 1, 1)
        lay.addWidget(QLabel("Channel:"), 2, 0)
        lay.addWidget(self.choose_channel_for_measurements, 2, 1)
        lay.addWidget(QLabel("Units:"), 3, 0)
        lay.addWidget(self.units_choose, 3, 1)
        lay.addWidget(self.add_calculation_btn, 4, 0, 1, 2)
        measurement_box.setLayout(lay)

        info_box = QGroupBox("Information")
        info_box.setStyleSheet(group_sheet)
        lay = QVBoxLayout()
        lay.addWidget(self.information)
        info_box.setLayout(lay)

        layout = QGridLayout()
        fst_col = QVBoxLayout()
        fst_col.addWidget(plan_box, 1)
        fst_col.addWidget(mask_box)
        layout.addWidget(plan_box, 0, 0, 5, 1)
        # layout.addWidget(plan_box, 0, 0, 3, 1)
        # layout.addWidget(mask_box, 3, 0, 2, 1)
        # layout.addWidget(segmentation_mask_box, 1, 1)
        layout.addWidget(mask_box, 0, 2, 1, 2)
        layout.addWidget(other_box, 0, 1)
        layout.addWidget(segment_box, 1, 1, 1, 2)
        layout.addWidget(measurement_box, 1, 3)
        layout.addWidget(info_box, 3, 1, 1, 3)
        self.setLayout(layout)

        self.generate_mask_btn.setDisabled(True)
        self.chose_profile_btn.setDisabled(True)
        self.add_calculation_btn.setDisabled(True)

        self.mask_allow = False
        self.segment_allow = False
        self.file_mask_allow = False
        self.node_type = NodeType.root
        self.node_name = ""
        self.plan_node_changed.connect(self.mask_text_changed)
        self.plan.changed_node.connect(self.node_type_changed)
        self.plan_node_changed.connect(self.show_segment)
        self.plan_node_changed.connect(self.show_measurement)
        self.plan_node_changed.connect(self.mask_stack_change)
        self.mask_stack.currentChanged.connect(self.mask_stack_change)
        self.file_mask.value_changed.connect(self.mask_stack_change)
        self.mask_name.textChanged.connect(self.mask_stack_change)
        self.node_type_changed()

    def change_root_type(self):
        value: RootType = self.change_root.get_value()
        self.calculation_plan.set_root_type(value)
        self.plan.update_view()

    def change_segmentation_table(self):
        index = self.segment_stack.currentIndex()
        text = self.segment_stack.tabText(index)
        if self.update_element_chk.isChecked():
            self.chose_profile_btn.setText("Replace " + text)
        else:
            self.chose_profile_btn.setText("Add " + text)
        self.segment_profile.setCurrentItem(None)
        self.pipeline_profile.setCurrentItem(None)

    def save_changed(self, text):
        text = str(text)
        if text == "<none>":
            self.save_btn.setText("Save")
            self.save_btn.setToolTip("Choose file type")
            self.expected_node_type = None
            self.save_constructor = None
        else:
            save_class = self.save_translate_dict.get(text, None)
            if save_class is None:
                self.save_choose.setCurrentText("<none>")
                return
            self.save_btn.setText(f"Save to {save_class.get_short_name()}")
            self.save_btn.setToolTip("Choose mask create in plan view")
            if save_class.need_mask():
                self.expected_node_type = NodeType.mask
            elif save_class.need_segmentation():
                self.expected_node_type = NodeType.segment
            else:
                self.expected_node_type = NodeType.root
            self.save_constructor = Save
        self.save_activate()

    def save_activate(self):
        self.save_btn.setDisabled(True)
        if self.node_type == self.expected_node_type:
            self.save_btn.setEnabled(True)
            return

    def segmentation_from_project(self):
        self.calculation_plan.add_step(Operations.reset_to_base)
        self.plan.update_view()

    def update_names(self):
        if self.update_element_chk.isChecked():
            self.chose_profile_btn.setText("Replace Profile")
            self.add_calculation_btn.setText("Replace set of measurements")
            self.generate_mask_btn.setText("Replace mask")
        else:
            self.chose_profile_btn.setText("Add Profile")
            self.add_calculation_btn.setText("Add set of measurements")
            self.generate_mask_btn.setText("Generate mask")

    def node_type_changed(self):
        # self.cmap_save_btn.setDisabled(True)
        self.save_btn.setDisabled(True)
        self.node_name = ""
        if self.plan.currentItem() is None:
            self.mask_allow = False
            self.file_mask_allow = False
            self.segment_allow = False
            self.remove_btn.setDisabled(True)
            self.plan_node_changed.emit()
            logging.debug("[node_type_changed] return")
            return
        node_type = self.calculation_plan.get_node_type()
        self.node_type = node_type
        if node_type in [NodeType.file_mask, NodeType.mask, NodeType.segment, NodeType.measurement, NodeType.save]:
            self.remove_btn.setEnabled(True)
        else:
            self.remove_btn.setEnabled(False)
        if node_type in (NodeType.mask, NodeType.file_mask):
            self.mask_allow = False
            self.segment_allow = True
            self.file_mask_allow = False
            self.node_name = self.calculation_plan.get_node().operation.name
        elif node_type == NodeType.segment:
            self.mask_allow = True
            self.segment_allow = False
            self.file_mask_allow = False
            self.save_btn.setEnabled(True)
            # self.cmap_save_btn.setEnabled(True)
        elif node_type == NodeType.root:
            self.mask_allow = False
            self.segment_allow = True
            self.file_mask_allow = True
        elif node_type in (NodeType.none, NodeType.measurement, NodeType.save):
            self.mask_allow = False
            self.segment_allow = False
            self.file_mask_allow = False
        self.save_activate()
        self.plan_node_changed.emit()

    def add_save_to_project(self):
        save_class = self.save_translate_dict.get(self.save_choose.currentText(), None)
        if save_class is None:
            QMessageBox.warning(self, "Save problem", "Not found save class")
        dial = FormDialog(
            [AlgorithmProperty("suffix", "File suffix", ""), AlgorithmProperty("directory", "Sub directory", "")]
            + save_class.get_fields()
        )
        if dial.exec():
            values = dial.get_values()
            suffix = values["suffix"]
            directory = values["directory"]
            del values["suffix"]
            del values["directory"]
            save_elem = Save(suffix, directory, save_class.get_name(), save_class.get_short_name(), values)
            if self.update_element_chk.isChecked():
                self.calculation_plan.replace_step(save_elem)
            else:
                self.calculation_plan.add_step(save_elem)
            self.plan.update_view()

    def create_mask(self):
        text = str(self.mask_name.text()).strip()

        if text != "" and text in self.mask_set:
            QMessageBox.warning(self, "Already exists", "Mask with this name already exists", QMessageBox.Ok)
            return
        if _check_widget(self.mask_stack, EnumComboBox):  # existing mask
            mask_dialog = TwoMaskDialog
            if self.mask_operation.get_value() == MaskOperation.mask_intersection:  # Mask intersection
                MaskConstruct = MaskIntersection
            else:
                MaskConstruct = MaskSum
            dial = mask_dialog(self.mask_set)
            if not dial.exec():
                return
            names = dial.get_result()

            mask_ob = MaskConstruct(text, *names)
        elif _check_widget(self.mask_stack, MaskWidget):
            mask_ob = MaskCreate(text, self.segmentation_mask.get_mask_property())
        elif _check_widget(self.mask_stack, FileMask):
            mask_ob = self.file_mask.get_value(text)
        else:
            raise ValueError("Unknowsn widget")

        if self.update_element_chk.isChecked():
            node = self.calculation_plan.get_node()
            name = node.operation.name
            if name in self.calculation_plan.get_reused_mask() and name != text:
                QMessageBox.warning(
                    self, "Cannot remove", f"Cannot remove mask '{name}' from plan because it is used in other elements"
                )
                return

            self.mask_set.remove(name)
            self.mask_set.add(mask_ob.name)
            self.calculation_plan.replace_step(mask_ob)
        else:
            self.mask_set.add(mask_ob.name)
            self.calculation_plan.add_step(mask_ob)
        self.plan.update_view()
        self.mask_text_changed()

    def mask_stack_change(self):
        node_type = self.calculation_plan.get_node_type()
        if self.update_element_chk.isChecked() and node_type not in [NodeType.mask, NodeType.file_mask]:
            self.generate_mask_btn.setDisabled(True)
        text = self.mask_name.text()
        update = self.update_element_chk.isChecked()
        if self.node_type == NodeType.none:
            self.generate_mask_btn.setDisabled(True)
            return
        operation = self.calculation_plan.get_node().operation
        if (
            not update
            and isinstance(operation, (MaskMapper, MaskBase))
            and self.calculation_plan.get_node().operation.name == text
        ):
            self.generate_mask_btn.setDisabled(True)
            return
        if _check_widget(self.mask_stack, EnumComboBox):  # reuse mask
            if len(self.mask_set) > 1 and (
                (not update and node_type == NodeType.root) or (update and node_type == NodeType.file_mask)
            ):
                self.generate_mask_btn.setEnabled(True)
            else:
                self.generate_mask_btn.setEnabled(False)
            self.generate_mask_btn.setToolTip("Need at least two named mask and root selected")
        elif _check_widget(self.mask_stack, MaskWidget):  # mask from segmentation
            if (not update and node_type == NodeType.segment) or (update and node_type == NodeType.mask):
                self.generate_mask_btn.setEnabled(True)
            else:
                self.generate_mask_btn.setEnabled(False)
            self.generate_mask_btn.setToolTip("Select segmentation")
        else:
            if (not update and node_type == NodeType.root) or (update and node_type == NodeType.file_mask):
                self.generate_mask_btn.setEnabled(self.file_mask.is_valid())
            else:
                self.generate_mask_btn.setEnabled(False)
            self.generate_mask_btn.setToolTip("Need root selected")

    def mask_name_changed(self, text):
        if str(text) in self.mask_set:
            self.generate_mask_btn.setDisabled(True)
        else:
            self.generate_mask_btn.setDisabled(False)

    def add_leave_biggest(self):
        profile = self.calculation_plan.get_node().operation
        profile.leave_biggest_swap()
        self.calculation_plan.replace_step(profile)
        self.plan.update_view()

    def add_segmentation(self):
        if self.segment_stack.currentIndex() == 0:
            text = str(self.segment_profile.currentItem().text())
            if text not in self.settings.segmentation_profiles:
                self.refresh_all_profiles()
                return
            profile = self.settings.segmentation_profiles[text]
            if self.update_element_chk.isChecked():
                self.calculation_plan.replace_step(profile)
            else:
                self.calculation_plan.add_step(profile)
            self.plan.update_view()

        else:  # self.segment_stack.currentIndex() == 1
            text = self.pipeline_profile.currentItem().text()
            segmentation_pipeline = self.settings.segmentation_pipelines[text]
            pos = self.calculation_plan.current_pos[:]
            old_pos = self.calculation_plan.current_pos[:]
            for el in segmentation_pipeline.mask_history:
                self.calculation_plan.add_step(el.segmentation)
                self.plan.update_view()
                node = self.calculation_plan.get_node(pos)
                pos.append(len(node.children) - 1)
                self.calculation_plan.set_position(pos)
                self.calculation_plan.add_step(MaskCreate("", el.mask_property))
                self.plan.update_view()
                pos.append(0)
                self.calculation_plan.set_position(pos)
            self.calculation_plan.add_step(segmentation_pipeline.segmentation)
            self.calculation_plan.set_position(old_pos)
            self.plan.update_view()

    def add_measurement(self):
        text = str(self.measurements_list.currentItem().text())
        measurement_copy = deepcopy(self.settings.measurement_profiles[text])
        prefix = str(self.measurement_name_prefix.text()).strip()
        channel = self.choose_channel_for_measurements.currentIndex() - 1
        measurement_copy.name_prefix = prefix
        # noinspection PyTypeChecker
        measurement_calculate = MeasurementCalculate(
            channel=channel,
            measurement_profile=measurement_copy,
            name_prefix=prefix,
            units=self.units_choose.get_value(),
        )
        if self.update_element_chk.isChecked():
            self.calculation_plan.replace_step(measurement_calculate)
        else:
            self.calculation_plan.add_step(measurement_calculate)
        self.plan.update_view()

    def remove_element(self):
        conflict_mask, used_mask = self.calculation_plan.get_file_mask_names()
        if len(conflict_mask) > 0:
            logging.info("Mask in use")
            QMessageBox.warning(self, "In use", "Masks {} are used in other places".format(", ".join(conflict_mask)))
            return
        self.mask_set -= used_mask
        self.calculation_plan.remove_step()
        self.plan.update_view()

    def clean_plan(self):
        self.calculation_plan = CalculationPlan()
        self.plan.set_plan(self.calculation_plan)
        self.node_type_changed()
        self.mask_set = set()

    def mask_text_changed(self):
        name = str(self.mask_name.text()).strip()
        self.generate_mask_btn.setDisabled(True)
        # load mask from file
        if not self.update_element_chk.isChecked():
            # generate mask from segmentation
            if self.mask_allow and (name == "" or name not in self.mask_set):
                self.generate_mask_btn.setEnabled(True)
        else:
            if self.node_type != NodeType.file_mask and self.node_type != NodeType.mask:
                return
            # generate mask from segmentation
            if self.node_type == NodeType.mask and (name == "" or name == self.node_name or name not in self.mask_set):
                self.generate_mask_btn.setEnabled(True)

    def add_calculation_plan(self, text=None):
        if text is None or isinstance(text, bool):
            text, ok = QInputDialog.getText(self, "Plan title", "Set plan title")
        else:
            text, ok = QInputDialog.getText(
                self, "Plan title", f"Set plan title. Previous ({text}) is already in use", text=text
            )
        text = text.strip()
        if ok:
            if text == "":
                QMessageBox.information(
                    self, "Name cannot be empty", "Name cannot be empty, Please set correct name", QMessageBox.Ok
                )
                self.add_calculation_plan()
                return
            if text in self.settings.batch_plans:
                res = QMessageBox.information(
                    self,
                    "Name already in use",
                    "Name already in use. Would like to overwrite?",
                    QMessageBox.Yes | QMessageBox.No,
                )
                if res == QMessageBox.No:
                    self.add_calculation_plan(text)
                    return
            plan = copy(self.calculation_plan)
            plan.set_name(text)
            self.settings.batch_plans[text] = plan
            self.settings.dump()
            self.plan_created.emit()

    @staticmethod
    def get_index(item: QListWidgetItem, new_values: typing.List[str]) -> int:
        if item is None:
            return -1
        text = item.text()
        try:
            return new_values.index(text)
        except ValueError:
            return -1

    @staticmethod
    def refresh_profiles(list_widget: QListWidget, new_values: typing.List[str], index: int):
        list_widget.clear()
        list_widget.addItems(new_values)
        if index != -1:
            list_widget.setCurrentRow(index)

    def showEvent(self, _event):
        self.refresh_all_profiles()

    def refresh_all_profiles(self):
        new_measurements = list(sorted(self.settings.measurement_profiles.keys()))
        new_segment = list(sorted(self.settings.segmentation_profiles.keys()))
        new_pipelines = list(sorted(self.settings.segmentation_pipelines.keys()))
        measurement_index = self.get_index(self.measurements_list.currentItem(), new_measurements)
        segment_index = self.get_index(self.segment_profile.currentItem(), new_segment)
        pipeline_index = self.get_index(self.pipeline_profile.currentItem(), new_pipelines)
        self.protect = True
        self.refresh_profiles(self.measurements_list, new_measurements, measurement_index)
        self.refresh_profiles(self.segment_profile, new_segment, segment_index)
        self.refresh_profiles(self.pipeline_profile, new_pipelines, pipeline_index)
        self.protect = False

    def show_measurement_info(self, text=None):
        if self.protect:
            return
        if text is None:
            if self.measurements_list.currentItem() is not None:
                text = str(self.measurements_list.currentItem().text())
            else:
                return
        profile = self.settings.measurement_profiles[text]
        self.information.setText(str(profile))

    def show_measurement(self):
        if self.update_element_chk.isChecked():
            if self.node_type == NodeType.measurement:
                self.add_calculation_btn.setEnabled(True)
            else:
                self.add_calculation_btn.setDisabled(True)
        else:
            if self.measurements_list.currentItem() is not None:
                self.add_calculation_btn.setEnabled(self.mask_allow)
            else:
                self.add_calculation_btn.setDisabled(True)

    def show_segment_info(self, text=None):
        if self.protect:
            return
        if text == "":
            return
        if self.segment_stack.currentIndex() == 0:
            if text is None:
                if self.segment_profile.currentItem() is not None:
                    text = str(self.segment_profile.currentItem().text())
                else:
                    return
            profile = self.settings.segmentation_profiles[text]
        else:
            if text is None:
                if self.pipeline_profile.currentItem() is not None:
                    text = str(self.pipeline_profile.currentItem().text())
                else:
                    return
            profile = self.settings.segmentation_pipelines[text]
        self.information.setText(profile.pretty_print(analysis_algorithm_dict))

    def show_segment(self):
        if self.update_element_chk.isChecked() and self.segment_stack.currentIndex() == 0:
            self.get_big_btn.setDisabled(True)
            if self.node_type == NodeType.segment:
                self.chose_profile_btn.setEnabled(True)
            else:
                self.chose_profile_btn.setDisabled(True)
        else:
            if self.node_type == NodeType.segment:
                self.get_big_btn.setEnabled(True)
            else:
                self.get_big_btn.setDisabled(True)
            if (
                self.segment_stack.currentIndex() == 0 and self.segment_profile.currentItem()
            ) or self.pipeline_profile.currentItem() is not None:
                self.chose_profile_btn.setEnabled(self.segment_allow)
            else:
                self.chose_profile_btn.setDisabled(True)

    def edit_plan(self):
        plan = self.sender().plan_to_edit  # type: CalculationPlan
        self.calculation_plan = copy(plan)
        self.plan.set_plan(self.calculation_plan)
        self.mask_set.clear()
        self.calculation_plan.set_position([])
        self.mask_set.update(self.calculation_plan.get_mask_names())
コード例 #19
0
class QtPluginDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.installer = Installer()
        self.setup_ui()
        self.installer.set_output_widget(self.stdout_text)
        self.installer.process.started.connect(self._on_installer_start)
        self.installer.process.finished.connect(self._on_installer_done)
        self.refresh()

    def _on_installer_start(self):
        self.show_status_btn.setChecked(True)
        self.working_indicator.show()
        self.process_error_indicator.hide()

    def _on_installer_done(self, exit_code, exit_status):
        self.working_indicator.hide()
        if exit_code:
            self.process_error_indicator.show()
        else:
            self.show_status_btn.setChecked(False)
        self.refresh()
        self.plugin_sorter.refresh()

    def refresh(self):
        self.installed_list.clear()
        self.available_list.clear()

        # fetch installed
        from ...plugins import plugin_manager

        plugin_manager.discover()  # since they might not be loaded yet

        already_installed = set()

        for plugin_name, mod_name, distname in plugin_manager.iter_available():
            # not showing these in the plugin dialog
            if plugin_name in ('napari_plugin_engine', ):
                continue
            if distname:
                already_installed.add(distname)
                meta = standard_metadata(distname)
            else:
                meta = {}
            self.installed_list.addItem(
                ProjectInfo(
                    normalized_name(distname or ''),
                    meta.get('version', ''),
                    meta.get('url', ''),
                    meta.get('summary', ''),
                    meta.get('author', ''),
                    meta.get('license', ''),
                ),
                plugin_name=plugin_name,
                enabled=plugin_name in plugin_manager.plugins,
            )
        # self.v_splitter.setSizes([70 * self.installed_list.count(), 10, 10])

        # fetch available plugins
        self.worker = create_worker(iter_napari_plugin_info)

        def _handle_yield(project_info):
            if project_info.name in already_installed:
                self.installed_list.tag_outdated(project_info)
            else:
                self.available_list.addItem(project_info)

        self.worker.yielded.connect(_handle_yield)
        self.worker.finished.connect(self.working_indicator.hide)
        self.worker.start()

    def setup_ui(self):
        self.resize(1080, 640)
        vlay_1 = QVBoxLayout(self)
        self.h_splitter = QSplitter(self)
        vlay_1.addWidget(self.h_splitter)
        self.h_splitter.setOrientation(Qt.Horizontal)
        self.v_splitter = QSplitter(self.h_splitter)
        self.v_splitter.setOrientation(Qt.Vertical)
        self.v_splitter.setMinimumWidth(500)
        self.plugin_sorter = QtPluginSorter(parent=self.h_splitter)
        self.plugin_sorter.layout().setContentsMargins(2, 0, 0, 0)
        self.plugin_sorter.hide()

        installed = QWidget(self.v_splitter)
        lay = QVBoxLayout(installed)
        lay.setContentsMargins(0, 2, 0, 2)
        lay.addWidget(QLabel("Installed Plugins"))
        self.installed_list = QPluginList(installed, self.installer)
        lay.addWidget(self.installed_list)

        uninstalled = QWidget(self.v_splitter)
        lay = QVBoxLayout(uninstalled)
        lay.setContentsMargins(0, 2, 0, 2)
        lay.addWidget(QLabel("Available Plugin Packages"))
        self.available_list = QPluginList(uninstalled, self.installer)
        lay.addWidget(self.available_list)

        self.stdout_text = QTextEdit(self.v_splitter)
        self.stdout_text.setReadOnly(True)
        self.stdout_text.setObjectName("pip_install_status")
        self.stdout_text.hide()

        buttonBox = QHBoxLayout()
        self.working_indicator = QLabel("loading ...", self)
        self.process_error_indicator = QLabel(self)
        self.process_error_indicator.setObjectName("error_label")
        self.process_error_indicator.hide()
        load_gif = str(Path(napari.resources.__file__).parent / "loading.gif")
        mov = QMovie(load_gif)
        mov.setScaledSize(QSize(18, 18))
        self.working_indicator.setMovie(mov)
        mov.start()
        self.show_status_btn = QPushButton("Show Status", self)
        self.show_status_btn.setFixedWidth(100)
        self.show_sorter_btn = QPushButton("<< Show Sorter", self)
        self.close_btn = QPushButton("Close", self)
        self.close_btn.clicked.connect(self.reject)
        buttonBox.addWidget(self.show_status_btn)
        buttonBox.addWidget(self.working_indicator)
        buttonBox.addWidget(self.process_error_indicator)
        buttonBox.addStretch()
        buttonBox.addWidget(self.show_sorter_btn)
        buttonBox.addWidget(self.close_btn)
        buttonBox.setContentsMargins(0, 0, 4, 0)
        vlay_1.addLayout(buttonBox)

        self.show_status_btn.setCheckable(True)
        self.show_status_btn.setChecked(False)
        self.show_status_btn.toggled.connect(self._toggle_status)

        self.show_sorter_btn.setCheckable(True)
        self.show_sorter_btn.setChecked(False)
        self.show_sorter_btn.toggled.connect(self._toggle_sorter)

        self.v_splitter.setStretchFactor(1, 2)
        self.h_splitter.setStretchFactor(0, 2)

    def _toggle_sorter(self, show):
        if show:
            self.show_sorter_btn.setText(">> Hide Sorter")
            self.plugin_sorter.show()
        else:
            self.show_sorter_btn.setText("<< Show Sorter")
            self.plugin_sorter.hide()

    def _toggle_status(self, show):
        if show:
            self.show_status_btn.setText("Hide Status")
            self.stdout_text.show()
        else:
            self.show_status_btn.setText("Show Status")
            self.stdout_text.hide()
コード例 #20
0
ファイル: PyChopGui.py プロジェクト: samueljackson92/mantid
class PyChopGui(QMainWindow):
    """
    GUI Class using PyQT for PyChop to help users plan inelastic neutron experiments
    at spallation sources by calculating the resolution and flux at a given neutron energies.
    """

    instruments = {}
    choppers = {}
    minE = {}
    maxE = {}

    def __init__(self):
        super(PyChopGui, self).__init__()
        self.folder = os.path.dirname(sys.modules[self.__module__].__file__)
        for fname in os.listdir(self.folder):
            if fname.endswith('.yaml'):
                instobj = Instrument(os.path.join(self.folder, fname))
                self.instruments[instobj.name] = instobj
                self.choppers[instobj.name] = instobj.getChopperNames()
                self.minE[instobj.name] = max([instobj.emin, 0.01])
                self.maxE[instobj.name] = instobj.emax
        self.drawLayout()
        self.setInstrument(list(self.instruments.keys())[0])
        self.resaxes_xlim = 0
        self.qeaxes_xlim = 0
        self.isFramePlotted = 0

    def setInstrument(self, instname):
        """
        Defines the instrument parameters by the name of the instrument.
        """
        self.engine = self.instruments[str(instname)]
        self.tabs.setTabEnabled(self.tdtabID, False)
        self.widgets['ChopperCombo']['Combo'].clear()
        self.widgets['FrequencyCombo']['Combo'].clear()
        self.widgets['FrequencyCombo']['Label'].setText('Frequency')
        self.widgets['PulseRemoverCombo']['Combo'].clear()
        for item in self.choppers[str(instname)]:
            self.widgets['ChopperCombo']['Combo'].addItem(item)
        rep = self.engine.moderator.source_rep
        maxfreq = self.engine.chopper_system.max_frequencies
        # At the moment, the GUI only supports up to two independent frequencies
        if not hasattr(maxfreq, '__len__') or len(maxfreq) == 1:
            self.widgets['PulseRemoverCombo']['Combo'].hide()
            self.widgets['PulseRemoverCombo']['Label'].hide()
            for fq in range(
                    rep,
                (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1,
                    rep):
                self.widgets['FrequencyCombo']['Combo'].addItem(str(fq))
            if hasattr(self.engine.chopper_system, 'frequency_names'):
                self.widgets['FrequencyCombo']['Label'].setText(
                    self.engine.chopper_system.frequency_names[0])
        else:
            self.widgets['PulseRemoverCombo']['Combo'].show()
            self.widgets['PulseRemoverCombo']['Label'].show()
            if hasattr(self.engine.chopper_system, 'frequency_names'):
                for idx, chp in enumerate([
                        self.widgets['FrequencyCombo']['Label'],
                        self.widgets['PulseRemoverCombo']['Label']
                ]):
                    chp.setText(
                        self.engine.chopper_system.frequency_names[idx])
            for fq in range(rep, maxfreq[0] + 1, rep):
                self.widgets['FrequencyCombo']['Combo'].addItem(str(fq))
            for fq in range(rep, maxfreq[1] + 1, rep):
                self.widgets['PulseRemoverCombo']['Combo'].addItem(str(fq))
        if len(self.engine.chopper_system.choppers) > 1:
            self.widgets['MultiRepCheck'].setEnabled(True)
            self.tabs.setTabEnabled(self.tdtabID, True)
        else:
            self.widgets['MultiRepCheck'].setEnabled(False)
            self.widgets['MultiRepCheck'].setChecked(False)
        self.widgets['Chopper2Phase']['Edit'].hide()
        self.widgets['Chopper2Phase']['Label'].hide()
        if self.engine.chopper_system.isPhaseIndependent:
            self.widgets['Chopper2Phase']['Edit'].show()
            self.widgets['Chopper2Phase']['Label'].show()
            self.widgets['Chopper2Phase']['Edit'].setText(
                str(self.engine.chopper_system.defaultPhase[0]))
            self.widgets['Chopper2Phase']['Label'].setText(
                self.engine.chopper_system.phaseNames[0])
            # Special case for MERLIN - hide phase control from normal users
            if 'MERLIN' in str(instname) and not self.instSciAct.isChecked():
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()
        self.engine.setChopper(
            str(self.widgets['ChopperCombo']['Combo'].currentText()))
        self.engine.setFrequency(
            float(self.widgets['FrequencyCombo']['Combo'].currentText()))
        val = self.flxslder.val * self.maxE[self.engine.instname] / 100
        self.flxedt.setText('%3.2f' % (val))
        nframe = self.engine.moderator.n_frame if hasattr(
            self.engine.moderator, 'n_frame') else 1
        self.repfig_nframe_edit.setText(str(nframe))
        if hasattr(self.engine.chopper_system, 'default_frequencies'):
            cb = [
                self.widgets['FrequencyCombo']['Combo'],
                self.widgets['PulseRemoverCombo']['Combo']
            ]
            for idx, freq in enumerate(
                    self.engine.chopper_system.default_frequencies):
                cb[idx].setCurrentIndex([
                    i for i in range(cb[idx].count())
                    if str(freq) in cb[idx].itemText(i)
                ][0])
                if idx > 1:
                    break
        self.tabs.setTabEnabled(self.qetabID, False)
        if self.engine.has_detector and hasattr(self.engine.detector,
                                                'tthlims'):
            self.tabs.setTabEnabled(self.qetabID, True)

    def setChopper(self, choppername):
        """
        Defines the Fermi chopper slit package type by name, or the disk chopper arrangement variant.
        """
        self.engine.setChopper(str(choppername))
        self.engine.setFrequency(
            float(self.widgets['FrequencyCombo']['Combo'].currentText()))
        # Special case for MERLIN - only enable multirep for 'G' chopper
        if 'MERLIN' in self.engine.instname:
            if 'G' in str(choppername):
                self.widgets['MultiRepCheck'].setEnabled(True)
                self.tabs.setTabEnabled(self.tdtabID, True)
                self.widgets['Chopper2Phase']['Edit'].setText('1500')
                self.widgets['Chopper2Phase']['Label'].setText(
                    'Disk chopper phase delay time')
                if self.instSciAct.isChecked():
                    self.widgets['Chopper2Phase']['Edit'].show()
                    self.widgets['Chopper2Phase']['Label'].show()
            else:
                self.widgets['MultiRepCheck'].setEnabled(False)
                self.widgets['MultiRepCheck'].setChecked(False)
                self.tabs.setTabEnabled(self.tdtabID, False)
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()

    def setFreq(self, freqtext=None, **kwargs):
        """
        Sets the chopper frequency(ies), in Hz.
        """
        freq_gui = float(self.widgets['FrequencyCombo']['Combo'].currentText())
        freq_in = kwargs['manual_freq'] if ('manual_freq'
                                            in kwargs.keys()) else freq_gui
        if len(self.engine.getFrequency()) > 1 and (
                not hasattr(freq_in, '__len__') or len(freq_in) == 1):
            freqpr = float(
                self.widgets['PulseRemoverCombo']['Combo'].currentText())
            freq_in = [freq_in, freqpr]
        if not self.widgets['Chopper2Phase']['Label'].isHidden():
            chop2phase = self.widgets['Chopper2Phase']['Edit'].text()
            if isinstance(self.engine.chopper_system.defaultPhase[0],
                          string_types):
                chop2phase = str(chop2phase)
            else:
                chop2phase = float(chop2phase) % (
                    1e6 / self.engine.moderator.source_rep)
            self.engine.setFrequency(freq_in, phase=chop2phase)
        else:
            self.engine.setFrequency(freq_in)

    def setEi(self):
        """
        Sets the incident energy (or focused incident energy for multi-rep case).
        """
        try:
            eitxt = float(self.widgets['EiEdit']['Edit'].text())
            self.engine.setEi(eitxt)
            if self.eiPlots.isChecked():
                self.calc_callback()
        except ValueError:
            raise ValueError('No Ei specified, or Ei string not understood')

    def calc_callback(self):
        """
        Calls routines to calculate the resolution / flux and to update the Matplotlib graphs.
        """
        try:
            if self.engine.getChopper() is None:
                self.setChopper(
                    self.widgets['ChopperCombo']['Combo'].currentText())
            self.setEi()
            self.setFreq()
            self.calculate()
            if self.errormess:
                idx = [
                    i for i, ei in enumerate(self.eis)
                    if np.abs(ei - self.engine.getEi()) < 1.e-4
                ]
                if idx and self.flux[idx[0]] == 0:
                    raise ValueError(self.errormess)
                self.errormessage(self.errormess)
            self.plot_res()
            self.plot_frame()
            if self.instSciAct.isChecked():
                self.update_script()
        except ValueError as err:
            self.errormessage(err)
        self.plot_flux_ei()
        self.plot_flux_hz()

    def calculate(self):
        """
        Performs the resolution and flux calculations.
        """
        self.errormess = None
        if self.engine.getEi() is None:
            self.setEi()
        if self.widgets['MultiRepCheck'].isChecked():
            en = np.linspace(0, 0.95, 200)
            self.eis = self.engine.getAllowedEi()
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always', UserWarning)
                self.res = self.engine.getMultiRepResolution(en)
                self.flux = self.engine.getMultiRepFlux()
                if len(w) > 0:
                    mess = [str(w[i].message) for i in range(len(w))]
                    self.errormess = '\n'.join(
                        [m for m in mess if 'tchop' in m])
        else:
            en = np.linspace(0, 0.95 * self.engine.getEi(), 200)
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always', UserWarning)
                self.res = self.engine.getResolution(en)
                self.flux = self.engine.getFlux()
                if len(w) > 0:
                    raise ValueError(w[0].message)

    def _set_overplot(self, overplot, axisname):
        axis = getattr(self, axisname)
        if overplot:
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                axis.hold(True)
        else:
            setattr(self, axisname + '_xlim', 0)
            axis.clear()
            axis.axhline(color='k')

    def plot_res(self):
        """
        Plots the resolution in the resolution tab
        """
        overplot = self.widgets['HoldCheck'].isChecked()
        multiplot = self.widgets['MultiRepCheck'].isChecked()
        self._set_overplot(overplot, 'resaxes')
        self._set_overplot(overplot, 'qeaxes')
        inst = self.engine.instname
        freq = self.engine.getFrequency()
        if hasattr(freq, '__len__'):
            freq = freq[0]
        if multiplot:
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                self.resaxes.hold(True)
            for ie, Ei in enumerate(self.eis):
                en = np.linspace(0, 0.95 * Ei, 200)
                if any(self.res[ie]):
                    if not self.flux[ie]:
                        continue
                    line, = self.resaxes.plot(en, self.res[ie])
                    label_text = '%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (
                        inst, Ei, freq, self.flux[ie])
                    line.set_label(label_text)
                    if self.tabs.isTabEnabled(self.qetabID):
                        self.plot_qe(Ei, label_text, hold=True)
                    self.resaxes_xlim = max(Ei, self.resaxes_xlim)
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                self.resaxes.hold(False)
        else:
            ei = self.engine.getEi()
            en = np.linspace(0, 0.95 * ei, 200)
            line, = self.resaxes.plot(en, self.res)
            chopper = self.engine.getChopper()
            label_text = '%s_%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (
                inst, chopper, ei, freq, self.flux)
            line.set_label(label_text)
            if self.tabs.isTabEnabled(self.qetabID):
                self.plot_qe(ei, label_text, overplot)
            self.resaxes_xlim = max(ei, self.resaxes_xlim)
        self.resaxes.set_xlim([0, self.resaxes_xlim])
        self.resaxes.legend().draggable()
        self.resaxes.set_xlabel('Energy Transfer (meV)')
        self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)')
        self.rescanvas.draw()

    def plot_qe(self, Ei, label_text, hold=False):
        """ Plots the Q-E diagram """
        from scipy import constants
        E2q, meV2J = (2. * constants.m_n / (constants.hbar**2),
                      constants.e / 1000.)
        en = np.linspace(-Ei / 5., Ei, 100)
        q2 = []
        for tth in self.engine.detector.tthlims:
            q = np.sqrt(E2q * (2 * Ei - en - 2 * np.sqrt(Ei * (Ei - en)) *
                               np.cos(np.deg2rad(tth))) * meV2J) / 1e10
            q2.append(np.concatenate((np.flipud(q), q)))
        self._set_overplot(hold, 'qeaxes')
        self.qeaxes_xlim = max(np.max(q2), self.qeaxes_xlim)
        line, = self.qeaxes.plot(
            np.hstack(q2),
            np.concatenate((np.flipud(en), en)).tolist() *
            len(self.engine.detector.tthlims))
        line.set_label(label_text)
        self.qeaxes.set_xlim([0, self.qeaxes_xlim])
        self.qeaxes.legend().draggable()
        self.qecanvas.draw()

    def plot_flux_ei(self, **kwargs):
        """
        Plots the flux vs Ei in the middle tab
        """
        inst = self.engine.instname
        chop = self.engine.getChopper()
        freq = self.engine.getFrequency()
        overplot = self.widgets['HoldCheck'].isChecked()
        if hasattr(freq, '__len__'):
            freq = freq[0]
        update = kwargs['update'] if 'update' in kwargs.keys() else False
        # Do not recalculate if all relevant parameters still the same.
        _, labels = self.flxaxes2.get_legend_handles_labels()
        searchStr = '([A-Z]+) "(.+)" ([0-9]+) Hz'
        tmpinst = []
        if (labels and (overplot or len(labels) == 1)) or update:
            for prevtitle in labels:
                prevInst, prevChop, prevFreq = re.search(searchStr,
                                                         prevtitle).groups()
                if update:
                    tmpinst.append(
                        copy.deepcopy(
                            Instrument(self.instruments[prevInst], prevChop,
                                       float(prevFreq))))
                else:
                    if inst == prevInst and chop == prevChop and freq == float(
                            prevFreq):
                        return
        ne = 25
        mn = self.minE[inst]
        mx = (self.flxslder.val / 100) * self.maxE[inst]
        eis = np.linspace(mn, mx, ne)
        flux = eis * 0
        elres = eis * 0
        if update:
            self.flxaxes1.clear()
            self.flxaxes2.clear()
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                self.flxaxes1.hold(True)
                self.flxaxes2.hold(True)
            for ii, instrument in enumerate(tmpinst):
                for ie, ei in enumerate(eis):
                    with warnings.catch_warnings(record=True):
                        warnings.simplefilter('always', UserWarning)
                        flux[ie] = instrument.getFlux(ei)
                        elres[ie] = instrument.getResolution(0., ei)[0]
                self.flxaxes1.plot(eis, flux)
                line, = self.flxaxes2.plot(eis, elres)
                line.set_label(labels[ii])
        else:
            for ie, ei in enumerate(eis):
                with warnings.catch_warnings(record=True):
                    warnings.simplefilter('always', UserWarning)
                    flux[ie] = self.engine.getFlux(ei)
                    elres[ie] = self.engine.getResolution(0., ei)[0]
            if overplot:
                if matplotlib.compare_versions('2.1.0',
                                               matplotlib.__version__):
                    self.flxaxes1.hold(True)
                    self.flxaxes2.hold(True)
            else:
                self.flxaxes1.clear()
                self.flxaxes2.clear()
            self.flxaxes1.plot(eis, flux)
            line, = self.flxaxes2.plot(eis, elres)
            line.set_label('%s "%s" %d Hz' % (inst, chop, freq))
        self.flxaxes1.set_xlim([mn, mx])
        self.flxaxes2.set_xlim([mn, mx])
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        lg = self.flxaxes2.legend()
        lg.draggable()
        self.flxcanvas.draw()

    def update_slider(self, val=None):
        """
        Callback function for the x-axis slider of the flux tab
        """
        if val is None:
            val = float(
                self.flxedt.text()) / self.maxE[self.engine.instname] * 100
            if val < self.minE[self.engine.instname]:
                self.errormessage("Max Ei must be greater than %2.1f" %
                                  (self.minE[self.engine.instname]))
                val = (self.minE[self.engine.instname] +
                       0.1) / self.maxE[self.engine.instname] * 100
            self.flxslder.set_val(val)
        else:
            val = self.flxslder.val * self.maxE[self.engine.instname] / 100
            self.flxedt.setText('%3.2f' % (val))
        self.plot_flux_ei(update=True)
        self.flxcanvas.draw()

    def plot_flux_hz(self):
        """
        Plots the flux vs freq in the middle tab
        """
        inst = self.engine.instname
        chop = self.engine.getChopper()
        ei = float(self.widgets['EiEdit']['Edit'].text())
        overplot = self.widgets['HoldCheck'].isChecked()
        # Do not recalculate if one of the plots has the same parametersc
        _, labels = self.frqaxes2.get_legend_handles_labels()
        searchStr = '([A-Z]+) "(.+)" Ei = ([0-9.-]+) meV'
        if labels and (overplot or len(labels) == 1):
            for prevtitle in labels:
                prevInst, prevChop, prevEi = re.search(searchStr,
                                                       prevtitle).groups()
                if inst == prevInst and chop == prevChop and abs(
                        ei - float(prevEi)) < 0.01:
                    return
        freq0 = self.engine.getFrequency()
        rep = self.engine.moderator.source_rep
        maxfreq = self.engine.chopper_system.max_frequencies
        freqs = range(
            rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1,
            rep)
        flux = np.zeros(len(freqs))
        elres = np.zeros(len(freqs))
        for ie, freq in enumerate(freqs):
            if hasattr(freq0, '__len__'):
                self.setFreq(manual_freq=[freq] + freq0[1:])
            else:
                self.setFreq(manual_freq=freq)
            with warnings.catch_warnings(record=True):
                warnings.simplefilter('always', UserWarning)
                flux[ie] = self.engine.getFlux(ei)
                elres[ie] = self.engine.getResolution(0., ei)[0]
        if overplot:
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                self.frqaxes1.hold(True)
                self.frqaxes2.hold(True)
        else:
            self.frqaxes1.clear()
            self.frqaxes2.clear()
        self.setFreq(manual_freq=freq0)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        line, = self.frqaxes1.plot(freqs, flux, 'o-')
        self.frqaxes1.set_xlim([0, np.max(freqs)])
        self.frqaxes2.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        line, = self.frqaxes2.plot(freqs, elres, 'o-')
        line.set_label('%s "%s" Ei = %5.3f meV' % (inst, chop, ei))
        lg = self.frqaxes2.legend()
        lg.draggable()
        self.frqaxes2.set_xlim([0, np.max(freqs)])
        self.frqcanvas.draw()

    def instSciCB(self):
        """
        Callback function for the "Instrument Scientist Mode" menu option
        """
        # MERLIN is a special case - want to hide ability to change phase from users
        if 'MERLIN' in self.engine.instname and 'G' in self.engine.getChopper(
        ):
            if self.instSciAct.isChecked():
                self.widgets['Chopper2Phase']['Edit'].show()
                self.widgets['Chopper2Phase']['Label'].show()
                self.widgets['Chopper2Phase']['Edit'].setText('1500')
                self.widgets['Chopper2Phase']['Label'].setText(
                    'Disk chopper phase delay time')
            else:
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()
        if self.instSciAct.isChecked():
            self.tabs.insertTab(self.scrtabID, self.scrtab, 'ScriptOutput')
            self.scrtab.show()
        else:
            self.tabs.removeTab(self.scrtabID)
            self.scrtab.hide()

    def errormessage(self, message):
        msg = QMessageBox()
        msg.setText(str(message))
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()

    def loadYaml(self):
        yaml_file = QFileDialog().getOpenFileName(self.mainWidget,
                                                  'Open Instrument YAML File',
                                                  self.folder,
                                                  'Files (*.yaml)')
        if isinstance(yaml_file, tuple):
            yaml_file = yaml_file[0]
        yaml_file = str(yaml_file)
        new_folder = os.path.dirname(yaml_file)
        if new_folder != self.folder:
            self.folder = new_folder
        try:
            new_inst = Instrument(yaml_file)
        except (RuntimeError, AttributeError, ValueError) as err:
            self.errormessage(err)
        newname = new_inst.name
        if newname in self.instruments.keys(
        ) and not self.overwriteload.isChecked():
            overwrite, newname = self._ask_overwrite()
            if overwrite == 1:
                return
            elif overwrite == 0:
                newname = new_inst.name
        self.instruments[newname] = new_inst
        self.choppers[newname] = new_inst.getChopperNames()
        self.minE[newname] = max([new_inst.emin, 0.01])
        self.maxE[newname] = new_inst.emax
        self.updateInstrumentList()
        combo = self.widgets['InstrumentCombo']['Combo']
        idx = [
            i for i in range(combo.count())
            if str(combo.itemText(i)) == newname
        ]
        combo.setCurrentIndex(idx[0])
        self.setInstrument(newname)

    def _ask_overwrite(self):
        msg = QDialog()
        msg.setWindowTitle('Load overwrite')
        layout = QGridLayout()
        layout.addWidget(
            QLabel('Instrument %s already exists in memory. Overwrite this?'),
            0, 0, 1, -1)
        buttons = [
            QPushButton(label) for label in
            ['Load and overwrite', 'Cancel Load', 'Load and rename to']
        ]
        locations = [[1, 0], [1, 1], [2, 0]]
        self.overwrite_flag = 1

        def overwriteCB(idx):
            self.overwrite_flag = idx
            msg.accept()

        for idx, button in enumerate(buttons):
            button.clicked.connect(lambda _, idx=idx: overwriteCB(idx))
            layout.addWidget(button, locations[idx][0], locations[idx][1])
        newname = QLineEdit()
        newname.editingFinished.connect(lambda: overwriteCB(2))
        layout.addWidget(newname, 2, 1)
        msg.setLayout(layout)
        msg.exec_()
        newname = str(newname.text())
        if not newname or newname in self.instruments:
            self.errormessage('Invalid instrument name. Cancelling load.')
            self.overwrite_flag = 1
        return self.overwrite_flag, newname

    def updateInstrumentList(self):
        combo = self.widgets['InstrumentCombo']['Combo']
        old_instruments = [
            str(combo.itemText(i)) for i in range(combo.count())
        ]
        new_instruments = [
            inst for inst in self.instruments if inst not in old_instruments
        ]
        for inst in new_instruments:
            combo.addItem(inst)

    def plot_frame(self):
        """
        Plots the distance-time diagram in the right tab
        """
        if len(self.engine.chopper_system.choppers) > 1:
            self.engine.n_frame = int(self.repfig_nframe_edit.text())
            self.repaxes.clear()
            self.engine.plotMultiRepFrame(self.repaxes)
            self.repcanvas.draw()

    def genText(self):
        """
        Generates text output of the resolution function versus energy transfer and other information.
        """
        en = np.linspace(0, 0.95 * self.engine.getEi(), 10)
        try:
            flux = self.engine.getFlux()
            res = self.engine.getResolution(en)
        except ValueError as err:
            self.errormessage(err)
            raise ValueError(err)
        obj = self.engine
        instname, chtyp, freqs, ei_in = tuple(
            [obj.instname,
             obj.getChopper(),
             obj.getFrequency(),
             obj.getEi()])
        ei = ei_in
        tsqvan, tsqdic, tsqmodchop = obj.getVanVar()
        v_mod, v_chop = tuple(np.sqrt(tsqmodchop[:2]) * 1e6)
        x0, _, x1, x2, _ = obj.chopper_system.getDistances()
        first_component = 'moderator'
        if x0 != tsqmodchop[2]:
            x0 = tsqmodchop[2]
            first_component = 'chopper 1'
        txt = '# ------------------------------------------------------------- #\n'
        txt += '# Chop calculation for instrument %s\n' % (instname)
        if obj.isFermi:
            txt += '#     with chopper %s at %3i Hz\n' % (chtyp, freqs[0])
        else:
            txt += '#     in %s mode with:\n' % (chtyp)
            freq_names = obj.chopper_system.frequency_names
            for idx in range(len(freq_names)):
                txt += '#     %s at %3i Hz\n' % (freq_names[idx], freqs[idx])
        txt += '# ------------------------------------------------------------- #\n'
        txt += '# Flux = %8.2f n/cm2/s\n' % (flux)
        txt += '# Elastic resolution = %6.2f meV\n' % (res[0])
        txt += '# Time width at sample = %6.2f us, of which:\n' % (
            1e6 * np.sqrt(tsqvan))
        for ky, val in list(tsqdic.items()):
            txt += '#     %20s : %6.2f us\n' % (ky, 1e6 * np.sqrt(val))
        txt += '# %s distances:\n' % (instname)
        txt += '#     x0 = %6.2f m (%s to Fermi)\n' % (x0, first_component)
        txt += '#     x1 = %6.2f m (Fermi to sample)\n' % (x1)
        txt += '#     x2 = %6.2f m (sample to detector)\n' % (x2)
        txt += '# Approximate inelastic resolution is given by:\n'
        txt += '#     dE = 2 * E2V * sqrt(ef**3 * t_van**2) / x2\n'
        txt += '#     where:  E2V = 4.373e-4 meV/(m/us) conversion from energy to speed\n'
        txt += '#             t_van**2 = (geom*t_mod)**2 + ((1+geom)*t_chop)**2\n'
        txt += '#             geom = (x1 + x2*(ei/ef)**1.5) / x0\n'
        txt += '#     and t_mod and t_chop are the moderator and chopper time widths at the\n'
        txt += '#     moderator and chopper positions (not at the sample as listed above).\n'
        txt += '# Which in this case is:\n'
        txt += '#     %.4e*sqrt(ef**3 * ( (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2 \n' % (
            874.78672e-6 / x2, v_mod, x1 / x0, x2 / x0)
        txt += '#                              + (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2) )\n' % (
            v_chop, 1 + x1 / x0, x2 / x0)
        txt += '#  EN (meV)   Full dE (meV)   Approx dE (meV)\n'
        for ii in range(len(res)):
            ef = ei - en[ii]
            approx = (874.78672e-6 / x2) * np.sqrt(ef**3 * (
                (v_mod * ((x1 / x0) + (x2 / x0) * (ei / ef)**1.5))**2 +
                (v_chop * (1 + (x1 / x0) + (x2 / x0) * (ei / ef)**1.5))**2))
            txt += '%12.5f %12.5f %12.5f\n' % (en[ii], res[ii], approx)
        return txt

    def showText(self):
        """
        Creates a dialog to show the generated text output.
        """
        try:
            generatedText = self.genText()
        except ValueError:
            return
        self.txtwin = QDialog()
        self.txtedt = QTextEdit()
        self.txtbtn = QPushButton('OK')
        self.txtwin.layout = QVBoxLayout(self.txtwin)
        self.txtwin.layout.addWidget(self.txtedt)
        self.txtwin.layout.addWidget(self.txtbtn)
        self.txtbtn.clicked.connect(self.txtwin.deleteLater)
        self.txtedt.setText(generatedText)
        self.txtedt.setReadOnly(True)
        self.txtwin.setWindowTitle('Resolution information')
        self.txtwin.setWindowModality(Qt.ApplicationModal)
        self.txtwin.setAttribute(Qt.WA_DeleteOnClose)
        self.txtwin.setMinimumSize(400, 600)
        self.txtwin.resize(400, 600)
        self.txtwin.show()
        self.txtloop = QEventLoop()
        self.txtloop.exec_()

    def saveText(self):
        """
        Saves the generated text to a file (opens file dialog).
        """
        fname = QFileDialog.getSaveFileName(self, 'Open file', '')
        if isinstance(fname, tuple):
            fname = fname[0]
        fid = open(fname, 'w')
        fid.write(self.genText())
        fid.close()

    def update_script(self):
        """
        Updates the text window with information about the previous calculation.
        """
        if self.widgets['MultiRepCheck'].isChecked():
            out = self.engine.getMultiWidths()
            new_str = '\n'
            for ie, ee in enumerate(out['Eis']):
                res = out['Energy'][ie]
                percent = res / ee * 100
                chop_width = out['chopper'][ie]
                mod_width = out['moderator'][ie]
                new_str += 'Ei is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (
                    ee, res * 1000, percent)
                new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (
                    chop_width, mod_width)
        else:
            ei = self.engine.getEi()
            out = self.engine.getWidths()
            res = out['Energy']
            percent = res / ei * 100
            chop_width = out['chopper']
            mod_width = out['moderator']
            new_str = '\nEi is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (
                ei, res * 1000, percent)
            new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (
                chop_width, mod_width)
        self.scredt.append(new_str)

    def onHelp(self):
        """
        Shows the help page
        """
        try:
            from pymantidplot.proxies import showCustomInterfaceHelp
            showCustomInterfaceHelp("PyChop")
        except ImportError:
            helpTxt = "PyChop is a tool to allow direct inelastic neutron\nscattering users to estimate the inelastic resolution\n"
            helpTxt += "and incident flux for a given spectrometer setting.\n\nFirst select the instrument, chopper settings and\n"
            helpTxt += "Ei, and then click 'Calculate and Plot'. Data for all\nthe graphs will be generated (may take 1-2s) and\n"
            helpTxt += "all graphs will be updated. If the 'Hold current plot'\ncheck box is ticked, additional settings will be\n"
            helpTxt += "overplotted on the existing graphs if they are\ndifferent from previous settings.\n\nMore in-depth help "
            helpTxt += "can be obtained from the\nMantid help pages."
            self.hlpwin = QDialog()
            self.hlpedt = QLabel(helpTxt)
            self.hlpbtn = QPushButton('OK')
            self.hlpwin.layout = QVBoxLayout(self.hlpwin)
            self.hlpwin.layout.addWidget(self.hlpedt)
            self.hlpwin.layout.addWidget(self.hlpbtn)
            self.hlpbtn.clicked.connect(self.hlpwin.deleteLater)
            self.hlpwin.setWindowTitle('Help')
            self.hlpwin.setWindowModality(Qt.ApplicationModal)
            self.hlpwin.setAttribute(Qt.WA_DeleteOnClose)
            self.hlpwin.setMinimumSize(370, 300)
            self.hlpwin.resize(370, 300)
            self.hlpwin.show()
            self.hlploop = QEventLoop()
            self.hlploop.exec_()

    def drawLayout(self):
        """
        Draws the GUI layout.
        """
        self.widgetslist = [[
            'pair', 'show', 'Instrument', 'combo', self.instruments,
            self.setInstrument, 'InstrumentCombo'
        ],
                            [
                                'pair', 'show', 'Chopper', 'combo', '',
                                self.setChopper, 'ChopperCombo'
                            ],
                            [
                                'pair', 'show', 'Frequency', 'combo', '',
                                self.setFreq, 'FrequencyCombo'
                            ],
                            [
                                'pair', 'hide', 'Pulse remover chopper freq',
                                'combo', '', self.setFreq, 'PulseRemoverCombo'
                            ],
                            [
                                'pair', 'show', 'Ei', 'edit', '', self.setEi,
                                'EiEdit'
                            ],
                            [
                                'pair', 'hide', 'Chopper 2 phase delay time',
                                'edit', '5', self.setFreq, 'Chopper2Phase'
                            ], ['spacer'],
                            [
                                'single', 'show', 'Calculate and Plot',
                                'button', self.calc_callback, 'CalculateButton'
                            ],
                            [
                                'single', 'show', 'Hold current plot', 'check',
                                lambda: None, 'HoldCheck'
                            ],
                            [
                                'single', 'show', 'Show multi-reps', 'check',
                                lambda: None, 'MultiRepCheck'
                            ], ['spacer'],
                            [
                                'single', 'show', 'Show data ascii window',
                                'button', self.showText, 'ShowAsciiButton'
                            ],
                            [
                                'single', 'show', 'Save data as ascii',
                                'button', self.saveText, 'SaveAsciiButton'
                            ]]
        self.droplabels = []
        self.dropboxes = []
        self.singles = []
        self.widgets = {}

        self.leftPanel = QVBoxLayout()
        self.rightPanel = QVBoxLayout()
        self.tabs = QTabWidget(self)
        self.fullWindow = QGridLayout()
        for widget in self.widgetslist:
            if 'pair' in widget[0]:
                self.droplabels.append(QLabel(widget[2]))
                if 'combo' in widget[3]:
                    self.dropboxes.append(QComboBox(self))
                    self.dropboxes[-1].activated['QString'].connect(widget[5])
                    for item in widget[4]:
                        self.dropboxes[-1].addItem(item)
                    self.widgets[widget[-1]] = {
                        'Combo': self.dropboxes[-1],
                        'Label': self.droplabels[-1]
                    }
                elif 'edit' in widget[3]:
                    self.dropboxes.append(QLineEdit(self))
                    self.dropboxes[-1].returnPressed.connect(widget[5])
                    self.widgets[widget[-1]] = {
                        'Edit': self.dropboxes[-1],
                        'Label': self.droplabels[-1]
                    }
                else:
                    raise RuntimeError(
                        'Bug in code - widget %s is not recognised.' %
                        (widget[3]))
                self.leftPanel.addWidget(self.droplabels[-1])
                self.leftPanel.addWidget(self.dropboxes[-1])
                if 'hide' in widget[1]:
                    self.droplabels[-1].hide()
                    self.dropboxes[-1].hide()
            elif 'single' in widget[0]:
                if 'check' in widget[3]:
                    self.singles.append(QCheckBox(widget[2], self))
                    self.singles[-1].stateChanged.connect(widget[4])
                elif 'button' in widget[3]:
                    self.singles.append(QPushButton(widget[2]))
                    self.singles[-1].clicked.connect(widget[4])
                else:
                    raise RuntimeError(
                        'Bug in code - widget %s is not recognised.' %
                        (widget[3]))
                self.leftPanel.addWidget(self.singles[-1])
                if 'hide' in widget[1]:
                    self.singles[-1].hide()
                self.widgets[widget[-1]] = self.singles[-1]
            elif 'spacer' in widget[0]:
                self.leftPanel.addItem(QSpacerItem(0, 35))
            else:
                raise RuntimeError(
                    'Bug in code - widget class %s is not recognised.' %
                    (widget[0]))

        # Right panel, matplotlib figures
        self.resfig = Figure()
        self.resfig.patch.set_facecolor('white')
        self.rescanvas = FigureCanvas(self.resfig)
        self.resaxes = self.resfig.add_subplot(111)
        self.resaxes.axhline(color='k')
        self.resaxes.set_xlabel('Energy Transfer (meV)')
        self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)')
        self.resfig_controls = NavigationToolbar(self.rescanvas, self)
        self.restab = QWidget(self.tabs)
        self.restabbox = QVBoxLayout()
        self.restabbox.addWidget(self.rescanvas)
        self.restabbox.addWidget(self.resfig_controls)
        self.restab.setLayout(self.restabbox)

        self.flxfig = Figure()
        self.flxfig.patch.set_facecolor('white')
        self.flxcanvas = FigureCanvas(self.flxfig)
        self.flxaxes1 = self.flxfig.add_subplot(121)
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.flxaxes2 = self.flxfig.add_subplot(122)
        self.flxaxes2.set_xlabel('Incident Energy (meV)')
        self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        self.flxfig_controls = NavigationToolbar(self.flxcanvas, self)
        self.flxsldfg = Figure()
        self.flxsldfg.patch.set_facecolor('white')
        self.flxsldcv = FigureCanvas(self.flxsldfg)
        self.flxsldax = self.flxsldfg.add_subplot(111)
        self.flxslder = Slider(self.flxsldax, 'Ei (meV)', 0, 100, valinit=100)
        self.flxslder.valtext.set_visible(False)
        self.flxslder.on_changed(self.update_slider)
        self.flxedt = QLineEdit()
        self.flxedt.setText('1000')
        self.flxedt.returnPressed.connect(self.update_slider)
        self.flxtab = QWidget(self.tabs)
        self.flxsldbox = QHBoxLayout()
        self.flxsldbox.addWidget(self.flxsldcv)
        self.flxsldbox.addWidget(self.flxedt)
        self.flxsldwdg = QWidget()
        self.flxsldwdg.setLayout(self.flxsldbox)
        sz = self.flxsldwdg.maximumSize()
        sz.setHeight(50)
        self.flxsldwdg.setMaximumSize(sz)
        self.flxtabbox = QVBoxLayout()
        self.flxtabbox.addWidget(self.flxcanvas)
        self.flxtabbox.addWidget(self.flxsldwdg)
        self.flxtabbox.addWidget(self.flxfig_controls)
        self.flxtab.setLayout(self.flxtabbox)

        self.frqfig = Figure()
        self.frqfig.patch.set_facecolor('white')
        self.frqcanvas = FigureCanvas(self.frqfig)
        self.frqaxes1 = self.frqfig.add_subplot(121)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.frqaxes2 = self.frqfig.add_subplot(122)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        self.frqfig_controls = NavigationToolbar(self.frqcanvas, self)
        self.frqtab = QWidget(self.tabs)
        self.frqtabbox = QVBoxLayout()
        self.frqtabbox.addWidget(self.frqcanvas)
        self.frqtabbox.addWidget(self.frqfig_controls)
        self.frqtab.setLayout(self.frqtabbox)

        self.repfig = Figure()
        self.repfig.patch.set_facecolor('white')
        self.repcanvas = FigureCanvas(self.repfig)
        self.repaxes = self.repfig.add_subplot(111)
        self.repaxes.axhline(color='k')
        self.repaxes.set_xlabel(r'TOF ($\mu$sec)')
        self.repaxes.set_ylabel('Distance (m)')
        self.repfig_controls = NavigationToolbar(self.repcanvas, self)
        self.repfig_nframe_label = QLabel('Number of frames to plot')
        self.repfig_nframe_edit = QLineEdit('1')
        self.repfig_nframe_button = QPushButton('Replot')
        self.repfig_nframe_button.clicked.connect(lambda: self.plot_frame())
        self.repfig_nframe_box = QHBoxLayout()
        self.repfig_nframe_box.addWidget(self.repfig_nframe_label)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_edit)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_button)
        self.reptab = QWidget(self.tabs)
        self.repfig_nframe = QWidget(self.reptab)
        self.repfig_nframe.setLayout(self.repfig_nframe_box)
        self.repfig_nframe.setSizePolicy(
            QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))
        self.reptabbox = QVBoxLayout()
        self.reptabbox.addWidget(self.repcanvas)
        self.reptabbox.addWidget(self.repfig_nframe)
        self.reptabbox.addWidget(self.repfig_controls)
        self.reptab.setLayout(self.reptabbox)

        self.qefig = Figure()
        self.qefig.patch.set_facecolor('white')
        self.qecanvas = FigureCanvas(self.qefig)
        self.qeaxes = self.qefig.add_subplot(111)
        self.qeaxes.axhline(color='k')
        self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$')
        self.qeaxes.set_ylabel('Energy Transfer (meV)')
        self.qefig_controls = NavigationToolbar(self.qecanvas, self)
        self.qetabbox = QVBoxLayout()
        self.qetabbox.addWidget(self.qecanvas)
        self.qetabbox.addWidget(self.qefig_controls)
        self.qetab = QWidget(self.tabs)
        self.qetab.setLayout(self.qetabbox)

        self.scrtab = QWidget(self.tabs)
        self.scredt = QTextEdit()
        self.scrcls = QPushButton("Clear")
        self.scrcls.clicked.connect(lambda: self.scredt.clear())
        self.scrbox = QVBoxLayout()
        self.scrbox.addWidget(self.scredt)
        self.scrbox.addWidget(self.scrcls)
        self.scrtab.setLayout(self.scrbox)
        self.scrtab.hide()

        self.tabs.addTab(self.restab, 'Resolution')
        self.tabs.addTab(self.flxtab, 'Flux-Ei')
        self.tabs.addTab(self.frqtab, 'Flux-Freq')
        self.tabs.addTab(self.reptab, 'Time-Distance')
        self.tdtabID = 3
        self.tabs.setTabEnabled(self.tdtabID, False)
        self.tabs.addTab(self.qetab, 'Q-E')
        self.qetabID = 4
        self.tabs.setTabEnabled(self.qetabID, False)
        self.scrtabID = 5
        self.rightPanel.addWidget(self.tabs)

        self.menuLoad = QMenu('Load')
        self.loadAct = QAction('Load YAML', self.menuLoad)
        self.loadAct.triggered.connect(self.loadYaml)
        self.menuLoad.addAction(self.loadAct)
        self.menuOptions = QMenu('Options')
        self.instSciAct = QAction('Instrument Scientist Mode',
                                  self.menuOptions,
                                  checkable=True)
        self.instSciAct.triggered.connect(self.instSciCB)
        self.menuOptions.addAction(self.instSciAct)
        self.eiPlots = QAction('Press Enter in Ei box updates plots',
                               self.menuOptions,
                               checkable=True)
        self.menuOptions.addAction(self.eiPlots)
        self.overwriteload = QAction('Always overwrite instruments in memory',
                                     self.menuOptions,
                                     checkable=True)
        self.menuOptions.addAction(self.overwriteload)
        self.menuBar().addMenu(self.menuLoad)
        self.menuBar().addMenu(self.menuOptions)

        self.leftPanelWidget = QWidget()
        self.leftPanelWidget.setLayout(self.leftPanel)
        self.leftPanelWidget.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred))
        self.fullWindow.addWidget(self.leftPanelWidget, 0, 0)
        self.fullWindow.addLayout(self.rightPanel, 0, 1)
        self.helpbtn = QPushButton("?", self)
        self.helpbtn.setMaximumWidth(30)
        self.helpbtn.clicked.connect(self.onHelp)
        self.fullWindow.addWidget(self.helpbtn, 1, 0, 1, -1)

        self.mainWidget = QWidget()
        self.mainWidget.setLayout(self.fullWindow)
        self.setCentralWidget(self.mainWidget)
        self.setWindowTitle('PyChopGUI')
        self.show()
コード例 #21
0
class QtPluginDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.refresh_state = RefreshState.DONE
        self.already_installed = set()

        installer_type = "mamba" if running_as_constructor_app() else "pip"

        self.installer = Installer(installer=installer_type)
        self.setup_ui()
        self.installer.set_output_widget(self.stdout_text)
        self.installer.started.connect(self._on_installer_start)
        self.installer.finished.connect(self._on_installer_done)
        self.refresh()

    def _on_installer_start(self):
        self.cancel_all_btn.setVisible(True)
        self.working_indicator.show()
        self.process_error_indicator.hide()
        self.close_btn.setDisabled(True)

    def _on_installer_done(self, exit_code):
        self.working_indicator.hide()
        if exit_code:
            self.process_error_indicator.show()

        self.cancel_all_btn.setVisible(False)
        self.close_btn.setDisabled(False)
        self.refresh()

    def closeEvent(self, event):
        if self.close_btn.isEnabled():
            super().closeEvent(event)

        event.ignore()

    def refresh(self):
        if self.refresh_state != RefreshState.DONE:
            self.refresh_state = RefreshState.OUTDATED
            return
        self.refresh_state = RefreshState.REFRESHING
        self.installed_list.clear()
        self.available_list.clear()

        # fetch installed
        from npe2 import PluginManager

        from ...plugins import plugin_manager

        plugin_manager.discover()  # since they might not be loaded yet

        self.already_installed = set()

        def _add_to_installed(distname, enabled, npe_version=1):
            norm_name = normalized_name(distname or '')
            if distname:
                try:
                    meta = metadata(distname)
                except PackageNotFoundError:
                    self.refresh_state = RefreshState.OUTDATED
                    return  # a race condition has occurred and the package is uninstalled by another thread
                if len(meta) == 0:
                    # will not add builtins.
                    return
                self.already_installed.add(norm_name)
            else:
                meta = {}

            self.installed_list.addItem(
                PackageMetadata(
                    metadata_version="1.0",
                    name=norm_name,
                    version=meta.get('version', ''),
                    summary=meta.get('summary', ''),
                    home_page=meta.get('url', ''),
                    author=meta.get('author', ''),
                    license=meta.get('license', ''),
                ),
                installed=True,
                enabled=enabled,
                npe_version=npe_version,
            )

        pm2 = PluginManager.instance()
        for manifest in pm2.iter_manifests():
            distname = normalized_name(manifest.name or '')
            if distname in self.already_installed or distname == 'napari':
                continue
            enabled = not pm2.is_disabled(manifest.name)
            _add_to_installed(distname, enabled, npe_version=2)

        for (
                plugin_name,
                _mod_name,
                distname,
        ) in plugin_manager.iter_available():
            # not showing these in the plugin dialog
            if plugin_name in ('napari_plugin_engine', ):
                continue
            if distname in self.already_installed:
                continue
            _add_to_installed(distname,
                              not plugin_manager.is_blocked(plugin_name))

        self.installed_label.setText(
            trans._(
                "Installed Plugins ({amount})",
                amount=len(self.already_installed),
            ))

        # fetch available plugins
        settings = get_settings()
        use_hub = (running_as_bundled_app() or running_as_constructor_app()
                   or settings.plugins.plugin_api.name == "napari_hub")
        if use_hub:
            conda_forge = running_as_constructor_app()
            self.worker = create_worker(iter_hub_plugin_info,
                                        conda_forge=conda_forge)
        else:
            self.worker = create_worker(iter_napari_plugin_info)

        self.worker.yielded.connect(self._handle_yield)
        self.worker.finished.connect(self.working_indicator.hide)
        self.worker.finished.connect(self._update_count_in_label)
        self.worker.finished.connect(self._end_refresh)
        self.worker.start()

    def setup_ui(self):
        self.resize(1080, 640)
        vlay_1 = QVBoxLayout(self)
        self.h_splitter = QSplitter(self)
        vlay_1.addWidget(self.h_splitter)
        self.h_splitter.setOrientation(Qt.Horizontal)
        self.v_splitter = QSplitter(self.h_splitter)
        self.v_splitter.setOrientation(Qt.Vertical)
        self.v_splitter.setMinimumWidth(500)

        installed = QWidget(self.v_splitter)
        lay = QVBoxLayout(installed)
        lay.setContentsMargins(0, 2, 0, 2)
        self.installed_label = QLabel(trans._("Installed Plugins"))
        self.packages_filter = QLineEdit()
        self.packages_filter.setPlaceholderText(trans._("filter..."))
        self.packages_filter.setMaximumWidth(350)
        self.packages_filter.setClearButtonEnabled(True)
        mid_layout = QVBoxLayout()
        mid_layout.addWidget(self.packages_filter)
        mid_layout.addWidget(self.installed_label)
        lay.addLayout(mid_layout)

        self.installed_list = QPluginList(installed, self.installer)
        self.packages_filter.textChanged.connect(self.installed_list.filter)
        lay.addWidget(self.installed_list)

        uninstalled = QWidget(self.v_splitter)
        lay = QVBoxLayout(uninstalled)
        lay.setContentsMargins(0, 2, 0, 2)
        self.avail_label = QLabel(trans._("Available Plugins"))
        mid_layout = QHBoxLayout()
        mid_layout.addWidget(self.avail_label)
        mid_layout.addStretch()
        lay.addLayout(mid_layout)
        self.available_list = QPluginList(uninstalled, self.installer)
        self.packages_filter.textChanged.connect(self.available_list.filter)
        lay.addWidget(self.available_list)

        self.stdout_text = QTextEdit(self.v_splitter)
        self.stdout_text.setReadOnly(True)
        self.stdout_text.setObjectName("pip_install_status")
        self.stdout_text.hide()

        buttonBox = QHBoxLayout()
        self.working_indicator = QLabel(trans._("loading ..."), self)
        sp = self.working_indicator.sizePolicy()
        sp.setRetainSizeWhenHidden(True)
        self.working_indicator.setSizePolicy(sp)
        self.process_error_indicator = QLabel(self)
        self.process_error_indicator.setObjectName("error_label")
        self.process_error_indicator.hide()
        load_gif = str(Path(napari.resources.__file__).parent / "loading.gif")
        mov = QMovie(load_gif)
        mov.setScaledSize(QSize(18, 18))
        self.working_indicator.setMovie(mov)
        mov.start()

        visibility_direct_entry = not running_as_constructor_app()
        self.direct_entry_edit = QLineEdit(self)
        self.direct_entry_edit.installEventFilter(self)
        self.direct_entry_edit.setPlaceholderText(
            trans._('install by name/url, or drop file...'))
        self.direct_entry_edit.setVisible(visibility_direct_entry)
        self.direct_entry_btn = QPushButton(trans._("Install"), self)
        self.direct_entry_btn.setVisible(visibility_direct_entry)
        self.direct_entry_btn.clicked.connect(self._install_packages)

        self.show_status_btn = QPushButton(trans._("Show Status"), self)
        self.show_status_btn.setFixedWidth(100)

        self.cancel_all_btn = QPushButton(trans._("cancel all actions"), self)
        self.cancel_all_btn.setObjectName("remove_button")
        self.cancel_all_btn.setVisible(False)
        self.cancel_all_btn.clicked.connect(lambda: self.installer.cancel())

        self.close_btn = QPushButton(trans._("Close"), self)
        self.close_btn.clicked.connect(self.accept)
        self.close_btn.setObjectName("close_button")
        buttonBox.addWidget(self.show_status_btn)
        buttonBox.addWidget(self.working_indicator)
        buttonBox.addWidget(self.direct_entry_edit)
        buttonBox.addWidget(self.direct_entry_btn)
        if not visibility_direct_entry:
            buttonBox.addStretch()
        buttonBox.addWidget(self.process_error_indicator)
        buttonBox.addSpacing(20)
        buttonBox.addWidget(self.cancel_all_btn)
        buttonBox.addSpacing(20)
        buttonBox.addWidget(self.close_btn)
        buttonBox.setContentsMargins(0, 0, 4, 0)
        vlay_1.addLayout(buttonBox)

        self.show_status_btn.setCheckable(True)
        self.show_status_btn.setChecked(False)
        self.show_status_btn.toggled.connect(self._toggle_status)

        self.v_splitter.setStretchFactor(1, 2)
        self.h_splitter.setStretchFactor(0, 2)

        self.packages_filter.setFocus()

    def _update_count_in_label(self):
        count = self.available_list.count()
        self.avail_label.setText(
            trans._("Available Plugins ({count})", count=count))

    def _end_refresh(self):
        refresh_state = self.refresh_state
        self.refresh_state = RefreshState.DONE
        if refresh_state == RefreshState.OUTDATED:
            self.refresh()

    def eventFilter(self, watched, event):
        if event.type() == QEvent.DragEnter:
            # we need to accept this event explicitly to be able
            # to receive QDropEvents!
            event.accept()
        if event.type() == QEvent.Drop:
            md = event.mimeData()
            if md.hasUrls():
                files = [url.toLocalFile() for url in md.urls()]
                self.direct_entry_edit.setText(files[0])
                return True
        return super().eventFilter(watched, event)

    def _toggle_status(self, show):
        if show:
            self.show_status_btn.setText(trans._("Hide Status"))
            self.stdout_text.show()
        else:
            self.show_status_btn.setText(trans._("Show Status"))
            self.stdout_text.hide()

    def _install_packages(self, packages: Sequence[str] = ()):
        if not packages:
            _packages = self.direct_entry_edit.text()
            if os.path.exists(_packages):
                packages = [_packages]
            else:
                packages = _packages.split()
            self.direct_entry_edit.clear()

        if packages:
            self.installer.install(packages)

    def _handle_yield(self, data: Tuple[PackageMetadata, bool]):
        project_info, is_available = data
        if project_info.name in self.already_installed:
            self.installed_list.tag_outdated(project_info, is_available)
        else:
            self.available_list.addItem(project_info)
            if not is_available:
                self.available_list.tag_unavailable(project_info)

        self.filter()

    def filter(self, text: str = None) -> None:
        """Filter by text or set current text as filter."""
        if text is None:
            text = self.packages_filter.text()
        else:
            self.packages_filter.setText(text)

        self.installed_list.filter(text)
        self.available_list.filter(text)
コード例 #22
0
class QtPluginDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.installer = Installer()
        self.setup_ui()
        self.installer.set_output_widget(self.stdout_text)
        self.installer.process.started.connect(self._on_installer_start)
        self.installer.process.finished.connect(self._on_installer_done)
        self.refresh()

    def _on_installer_start(self):
        self.show_status_btn.setChecked(True)
        self.working_indicator.show()
        self.process_error_indicator.hide()

    def _on_installer_done(self, exit_code, exit_status):
        self.working_indicator.hide()
        if exit_code:
            self.process_error_indicator.show()
        else:
            self.show_status_btn.setChecked(False)
        self.refresh()
        self.plugin_sorter.refresh()

    def refresh(self):
        self.installed_list.clear()
        self.available_list.clear()

        # fetch installed
        from ...plugins import plugin_manager

        plugin_manager.discover()  # since they might not be loaded yet

        already_installed = set()

        for plugin_name, mod_name, distname in plugin_manager.iter_available():
            # not showing these in the plugin dialog
            if plugin_name in ('napari_plugin_engine', ):
                continue
            if distname:
                already_installed.add(distname)
                meta = standard_metadata(distname)
            else:
                meta = {}
            self.installed_list.addItem(
                ProjectInfo(
                    normalized_name(distname or ''),
                    meta.get('version', ''),
                    meta.get('url', ''),
                    meta.get('summary', ''),
                    meta.get('author', ''),
                    meta.get('license', ''),
                ),
                plugin_name=plugin_name,
                enabled=plugin_name in plugin_manager.plugins,
            )
        # self.v_splitter.setSizes([70 * self.installed_list.count(), 10, 10])

        # fetch available plugins
        self.worker = create_worker(iter_napari_plugin_info)

        def _handle_yield(project_info):
            if project_info.name in already_installed:
                self.installed_list.tag_outdated(project_info)
            else:
                self.available_list.addItem(project_info)

        self.worker.yielded.connect(_handle_yield)
        self.worker.finished.connect(self.working_indicator.hide)
        self.worker.finished.connect(self._update_count_in_label)
        self.worker.start()

    def setup_ui(self):
        self.resize(1080, 640)
        vlay_1 = QVBoxLayout(self)
        self.h_splitter = QSplitter(self)
        vlay_1.addWidget(self.h_splitter)
        self.h_splitter.setOrientation(Qt.Horizontal)
        self.v_splitter = QSplitter(self.h_splitter)
        self.v_splitter.setOrientation(Qt.Vertical)
        self.v_splitter.setMinimumWidth(500)
        self.plugin_sorter = QtPluginSorter(parent=self.h_splitter)
        self.plugin_sorter.layout().setContentsMargins(2, 0, 0, 0)
        self.plugin_sorter.hide()

        installed = QWidget(self.v_splitter)
        lay = QVBoxLayout(installed)
        lay.setContentsMargins(0, 2, 0, 2)
        lay.addWidget(QLabel(trans._("Installed Plugins")))
        self.installed_list = QPluginList(installed, self.installer)
        lay.addWidget(self.installed_list)

        uninstalled = QWidget(self.v_splitter)
        lay = QVBoxLayout(uninstalled)
        lay.setContentsMargins(0, 2, 0, 2)
        self.avail_label = QLabel(trans._("Available Plugins"))
        lay.addWidget(self.avail_label)
        self.available_list = QPluginList(uninstalled, self.installer)
        lay.addWidget(self.available_list)

        self.stdout_text = QTextEdit(self.v_splitter)
        self.stdout_text.setReadOnly(True)
        self.stdout_text.setObjectName("pip_install_status")
        self.stdout_text.hide()

        buttonBox = QHBoxLayout()
        self.working_indicator = QLabel(trans._("loading ..."), self)
        sp = self.working_indicator.sizePolicy()
        sp.setRetainSizeWhenHidden(True)
        self.working_indicator.setSizePolicy(sp)
        self.process_error_indicator = QLabel(self)
        self.process_error_indicator.setObjectName("error_label")
        self.process_error_indicator.hide()
        load_gif = str(Path(napari.resources.__file__).parent / "loading.gif")
        mov = QMovie(load_gif)
        mov.setScaledSize(QSize(18, 18))
        self.working_indicator.setMovie(mov)
        mov.start()

        self.direct_entry_edit = QLineEdit(self)
        self.direct_entry_edit.installEventFilter(self)
        self.direct_entry_edit.setPlaceholderText(
            trans._('install by name/url, or drop file...'))
        self.direct_entry_btn = QPushButton(trans._("Install"), self)
        self.direct_entry_btn.clicked.connect(self._install_packages)

        self.show_status_btn = QPushButton(trans._("Show Status"), self)
        self.show_status_btn.setFixedWidth(100)
        self.show_sorter_btn = QPushButton(trans._("<< Show Sorter"), self)
        self.close_btn = QPushButton(trans._("Close"), self)
        self.close_btn.clicked.connect(self.reject)
        buttonBox.addWidget(self.show_status_btn)
        buttonBox.addWidget(self.working_indicator)
        buttonBox.addWidget(self.direct_entry_edit)
        buttonBox.addWidget(self.direct_entry_btn)
        buttonBox.addWidget(self.process_error_indicator)
        buttonBox.addSpacing(60)
        buttonBox.addWidget(self.show_sorter_btn)
        buttonBox.addWidget(self.close_btn)
        buttonBox.setContentsMargins(0, 0, 4, 0)
        vlay_1.addLayout(buttonBox)

        self.show_status_btn.setCheckable(True)
        self.show_status_btn.setChecked(False)
        self.show_status_btn.toggled.connect(self._toggle_status)

        self.show_sorter_btn.setCheckable(True)
        self.show_sorter_btn.setChecked(False)
        self.show_sorter_btn.toggled.connect(self._toggle_sorter)

        self.v_splitter.setStretchFactor(1, 2)
        self.h_splitter.setStretchFactor(0, 2)

    def _update_count_in_label(self):
        count = self.available_list.count()
        self.avail_label.setText(
            trans._("Available Plugins ({count})".format(count=count)))

    def eventFilter(self, watched, event):
        if event.type() == QEvent.DragEnter:
            # we need to accept this event explicitly to be able
            # to receive QDropEvents!
            event.accept()
        if event.type() == QEvent.Drop:
            md = event.mimeData()
            if md.hasUrls():
                files = [url.toLocalFile() for url in md.urls()]
                self.direct_entry_edit.setText(files[0])
                return True
        return super().eventFilter(watched, event)

    def _toggle_sorter(self, show):
        if show:
            self.show_sorter_btn.setText(trans._(">> Hide Sorter"))
            self.plugin_sorter.show()
        else:
            self.show_sorter_btn.setText(trans._("<< Show Sorter"))
            self.plugin_sorter.hide()

    def _toggle_status(self, show):
        if show:
            self.show_status_btn.setText(trans._("Hide Status"))
            self.stdout_text.show()
        else:
            self.show_status_btn.setText(trans._("Show Status"))
            self.stdout_text.hide()

    def _install_packages(self, packages: Sequence[str] = ()):
        if not packages:
            _packages = self.direct_entry_edit.text()
            if os.path.exists(_packages):
                packages = [_packages]
            else:
                packages = _packages.split()
            self.direct_entry_edit.clear()
        if packages:
            self.installer.install(packages)
コード例 #23
0
ファイル: PyChopGui.py プロジェクト: mantidproject/mantid
class PyChopGui(QMainWindow):
    """
    GUI Class using PyQT for PyChop to help users plan inelastic neutron experiments
    at spallation sources by calculating the resolution and flux at a given neutron energies.
    """

    instruments = {}
    choppers = {}
    minE = {}
    maxE = {}

    def __init__(self):
        super(PyChopGui, self).__init__()
        self.folder = os.path.dirname(sys.modules[self.__module__].__file__)
        for fname in os.listdir(self.folder):
            if fname.endswith('.yaml'):
                instobj = Instrument(os.path.join(self.folder, fname))
                self.instruments[instobj.name] = instobj
                self.choppers[instobj.name] = instobj.getChopperNames()
                self.minE[instobj.name] = max([instobj.emin, 0.01])
                self.maxE[instobj.name] = instobj.emax
        self.drawLayout()
        self.setInstrument(list(self.instruments.keys())[0])
        self.resaxes_xlim = 0
        self.qeaxes_xlim = 0
        self.isFramePlotted = 0

    def setInstrument(self, instname):
        """
        Defines the instrument parameters by the name of the instrument.
        """
        self.engine = self.instruments[str(instname)]
        self.tabs.setTabEnabled(self.tdtabID, False)
        self.widgets['ChopperCombo']['Combo'].clear()
        self.widgets['FrequencyCombo']['Combo'].clear()
        self.widgets['FrequencyCombo']['Label'].setText('Frequency')
        self.widgets['PulseRemoverCombo']['Combo'].clear()
        for item in self.choppers[str(instname)]:
            self.widgets['ChopperCombo']['Combo'].addItem(item)
        rep = self.engine.moderator.source_rep
        maxfreq = self.engine.chopper_system.max_frequencies
        # At the moment, the GUI only supports up to two independent frequencies
        if not hasattr(maxfreq, '__len__') or len(maxfreq) == 1:
            self.widgets['PulseRemoverCombo']['Combo'].hide()
            self.widgets['PulseRemoverCombo']['Label'].hide()
            for fq in range(rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1, rep):
                self.widgets['FrequencyCombo']['Combo'].addItem(str(fq))
            if hasattr(self.engine.chopper_system, 'frequency_names'):
                self.widgets['FrequencyCombo']['Label'].setText(self.engine.chopper_system.frequency_names[0])
        else:
            self.widgets['PulseRemoverCombo']['Combo'].show()
            self.widgets['PulseRemoverCombo']['Label'].show()
            if hasattr(self.engine.chopper_system, 'frequency_names'):
                for idx, chp in enumerate([self.widgets['FrequencyCombo']['Label'], self.widgets['PulseRemoverCombo']['Label']]):
                    chp.setText(self.engine.chopper_system.frequency_names[idx])
            for fq in range(rep, maxfreq[0] + 1, rep):
                self.widgets['FrequencyCombo']['Combo'].addItem(str(fq))
            for fq in range(rep, maxfreq[1] + 1, rep):
                self.widgets['PulseRemoverCombo']['Combo'].addItem(str(fq))
        if len(self.engine.chopper_system.choppers) > 1:
            self.widgets['MultiRepCheck'].setEnabled(True)
            self.tabs.setTabEnabled(self.tdtabID, True)
        else:
            self.widgets['MultiRepCheck'].setEnabled(False)
            self.widgets['MultiRepCheck'].setChecked(False)
        self.widgets['Chopper2Phase']['Edit'].hide()
        self.widgets['Chopper2Phase']['Label'].hide()
        if self.engine.chopper_system.isPhaseIndependent:
            self.widgets['Chopper2Phase']['Edit'].show()
            self.widgets['Chopper2Phase']['Label'].show()
            self.widgets['Chopper2Phase']['Edit'].setText(str(self.engine.chopper_system.defaultPhase[0]))
            self.widgets['Chopper2Phase']['Label'].setText(self.engine.chopper_system.phaseNames[0])
            # Special case for MERLIN - hide phase control from normal users
            if 'MERLIN' in str(instname) and not self.instSciAct.isChecked():
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()
        self.engine.setChopper(str(self.widgets['ChopperCombo']['Combo'].currentText()))
        self.engine.setFrequency(float(self.widgets['FrequencyCombo']['Combo'].currentText()))
        val = self.flxslder.val * self.maxE[self.engine.instname] / 100
        self.flxedt.setText('%3.2f' % (val))
        nframe = self.engine.moderator.n_frame if hasattr(self.engine.moderator, 'n_frame') else 1
        self.repfig_nframe_edit.setText(str(nframe))
        self.repfig_nframe_rep1only.setChecked(False)
        if hasattr(self.engine.chopper_system, 'default_frequencies'):
            cb = [self.widgets['FrequencyCombo']['Combo'], self.widgets['PulseRemoverCombo']['Combo']]
            for idx, freq in enumerate(self.engine.chopper_system.default_frequencies):
                cb[idx].setCurrentIndex([i for i in range(cb[idx].count()) if str(freq) in cb[idx].itemText(i)][0])
                if idx > 1:
                    break
        self.tabs.setTabEnabled(self.qetabID, False)
        if self.engine.has_detector and hasattr(self.engine.detector, 'tthlims'):
            self.tabs.setTabEnabled(self.qetabID, True)

    def setChopper(self, choppername):
        """
        Defines the Fermi chopper slit package type by name, or the disk chopper arrangement variant.
        """
        self.engine.setChopper(str(choppername))
        self.engine.setFrequency(float(self.widgets['FrequencyCombo']['Combo'].currentText()))
        # Special case for MERLIN - only enable multirep for 'G' chopper
        if 'MERLIN' in self.engine.instname:
            if 'G' in str(choppername):
                self.widgets['MultiRepCheck'].setEnabled(True)
                self.tabs.setTabEnabled(self.tdtabID, True)
                self.widgets['Chopper2Phase']['Edit'].setText('1500')
                self.widgets['Chopper2Phase']['Label'].setText('Disk chopper phase delay time')
                if self.instSciAct.isChecked():
                    self.widgets['Chopper2Phase']['Edit'].show()
                    self.widgets['Chopper2Phase']['Label'].show()
            else:
                self.widgets['MultiRepCheck'].setEnabled(False)
                self.widgets['MultiRepCheck'].setChecked(False)
                self.tabs.setTabEnabled(self.tdtabID, False)
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()

    def setFreq(self, freqtext=None, **kwargs):
        """
        Sets the chopper frequency(ies), in Hz.
        """
        freq_gui = float(self.widgets['FrequencyCombo']['Combo'].currentText())
        freq_in = kwargs['manual_freq'] if ('manual_freq' in kwargs.keys()) else freq_gui
        if len(self.engine.getFrequency()) > 1 and (not hasattr(freq_in, '__len__') or len(freq_in)==1):
            freqpr = float(self.widgets['PulseRemoverCombo']['Combo'].currentText())
            freq_in = [freq_in, freqpr]
        if not self.widgets['Chopper2Phase']['Label'].isHidden():
            chop2phase = self.widgets['Chopper2Phase']['Edit'].text()
            if isinstance(self.engine.chopper_system.defaultPhase[0], string_types):
                chop2phase = str(chop2phase)
            else:
                chop2phase = float(chop2phase) % (1e6 / self.engine.moderator.source_rep)
            self.engine.setFrequency(freq_in, phase=chop2phase)
        else:
            self.engine.setFrequency(freq_in)

    def setEi(self):
        """
        Sets the incident energy (or focused incident energy for multi-rep case).
        """
        try:
            eitxt = float(self.widgets['EiEdit']['Edit'].text())
            self.engine.setEi(eitxt)
            if self.eiPlots.isChecked():
                self.calc_callback()
        except ValueError:
            raise ValueError('No Ei specified, or Ei string not understood')

    def calc_callback(self):
        """
        Calls routines to calculate the resolution / flux and to update the Matplotlib graphs.
        """
        try:
            if self.engine.getChopper() is None:
                self.setChopper(self.widgets['ChopperCombo']['Combo'].currentText())
            self.setEi()
            self.setFreq()
            self.calculate()
            if self.errormess:
                idx = [i for i, ei in enumerate(self.eis) if np.abs(ei - self.engine.getEi()) < 1.e-4]
                if idx and self.flux[idx[0]] == 0:
                    raise ValueError(self.errormess)
                self.errormessage(self.errormess)
            self.plot_res()
            self.plot_frame()
            if self.instSciAct.isChecked():
                self.update_script()
        except ValueError as err:
            self.errormessage(err)
        self.plot_flux_ei()
        self.plot_flux_hz()

    def calculate(self):
        """
        Performs the resolution and flux calculations.
        """
        self.errormess = None
        if self.engine.getEi() is None:
            self.setEi()
        if self.widgets['MultiRepCheck'].isChecked():
            en = np.linspace(0, 0.95, 200)
            self.eis = self.engine.getAllowedEi()
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always', UserWarning)
                self.res = self.engine.getMultiRepResolution(en)
                self.flux = self.engine.getMultiRepFlux()
                if len(w) > 0:
                    mess = [str(w[i].message) for i in range(len(w))]
                    self.errormess = '\n'.join([m for m in mess if 'tchop' in m])
        else:
            en = np.linspace(0, 0.95*self.engine.getEi(), 200)
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always', UserWarning)
                self.res = self.engine.getResolution(en)
                self.flux = self.engine.getFlux()
                if len(w) > 0:
                    raise ValueError(w[0].message)

    def _set_overplot(self, overplot, axisname):
        axis = getattr(self, axisname)
        if overplot:
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                axis.hold(True)
        else:
            setattr(self, axisname+'_xlim', 0)
            axis.clear()
            axis.axhline(color='k')

    def plot_res(self):
        """
        Plots the resolution in the resolution tab
        """
        overplot = self.widgets['HoldCheck'].isChecked()
        multiplot = self.widgets['MultiRepCheck'].isChecked()
        self._set_overplot(overplot, 'resaxes')
        self._set_overplot(overplot, 'qeaxes')
        inst = self.engine.instname
        freq = self.engine.getFrequency()
        if hasattr(freq, '__len__'):
            freq = freq[0]
        if multiplot:
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                self.resaxes.hold(True)
            for ie, Ei in enumerate(self.eis):
                en = np.linspace(0, 0.95*Ei, 200)
                if any(self.res[ie]):
                    if not self.flux[ie]:
                        continue
                    line, = self.resaxes.plot(en, self.res[ie])
                    label_text = '%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (inst, Ei, freq, self.flux[ie])
                    line.set_label(label_text)
                    if self.tabs.isTabEnabled(self.qetabID):
                        self.plot_qe(Ei, label_text, hold=True)
                    self.resaxes_xlim = max(Ei, self.resaxes_xlim)
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                self.resaxes.hold(False)
        else:
            ei = self.engine.getEi()
            en = np.linspace(0, 0.95*ei, 200)
            line, = self.resaxes.plot(en, self.res)
            chopper = self.engine.getChopper()
            label_text = '%s_%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (inst, chopper, ei, freq, self.flux)
            line.set_label(label_text)
            if self.tabs.isTabEnabled(self.qetabID):
                self.plot_qe(ei, label_text, overplot)
            self.resaxes_xlim = max(ei, self.resaxes_xlim)
        self.resaxes.set_xlim([0, self.resaxes_xlim])
        self.resaxes.legend().draggable()
        self.resaxes.set_xlabel('Energy Transfer (meV)')
        self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)')
        self.rescanvas.draw()

    def plot_qe(self, Ei, label_text, hold=False):
        """ Plots the Q-E diagram """
        from scipy import constants
        E2q, meV2J = (2. * constants.m_n / (constants.hbar ** 2), constants.e / 1000.)
        en = np.linspace(-Ei / 5., Ei, 100)
        q2 = []
        for tth in self.engine.detector.tthlims:
            q = np.sqrt(E2q * (2 * Ei - en - 2 * np.sqrt(Ei * (Ei - en)) * np.cos(np.deg2rad(tth))) * meV2J) / 1e10
            q2.append(np.concatenate((np.flipud(q), q)))
        self._set_overplot(hold, 'qeaxes')
        self.qeaxes_xlim = max(np.max(q2), self.qeaxes_xlim)
        line, = self.qeaxes.plot(np.hstack(q2), np.concatenate((np.flipud(en), en)).tolist() * len(self.engine.detector.tthlims))
        line.set_label(label_text)
        self.qeaxes.set_xlim([0, self.qeaxes_xlim])
        self.qeaxes.legend().draggable()
        self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$')
        self.qeaxes.set_ylabel('Energy Transfer (meV)')
        self.qecanvas.draw()

    def plot_flux_ei(self, **kwargs):
        """
        Plots the flux vs Ei in the middle tab
        """
        inst = self.engine.instname
        chop = self.engine.getChopper()
        freq = self.engine.getFrequency()
        overplot = self.widgets['HoldCheck'].isChecked()
        if hasattr(freq, '__len__'):
            freq = freq[0]
        update = kwargs['update'] if 'update' in kwargs.keys() else False
        # Do not recalculate if all relevant parameters still the same.
        _, labels = self.flxaxes2.get_legend_handles_labels()
        searchStr = '([A-Z]+) "(.+)" ([0-9]+) Hz'
        tmpinst = []
        if (labels and (overplot or len(labels) == 1)) or update:
            for prevtitle in labels:
                prevInst, prevChop, prevFreq = re.search(searchStr, prevtitle).groups()
                if update:
                    tmpinst.append(copy.deepcopy(Instrument(self.instruments[prevInst], prevChop, float(prevFreq))))
                else:
                    if inst == prevInst and chop == prevChop and freq == float(prevFreq):
                        return
        ne = 25
        mn = self.minE[inst]
        mx = (self.flxslder.val/100)*self.maxE[inst]
        eis = np.linspace(mn, mx, ne)
        flux = eis*0
        elres = eis*0
        if update:
            self.flxaxes1.clear()
            self.flxaxes2.clear()
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                self.flxaxes1.hold(True)
                self.flxaxes2.hold(True)
            for ii, instrument in enumerate(tmpinst):
                for ie, ei in enumerate(eis):
                    with warnings.catch_warnings(record=True):
                        warnings.simplefilter('always', UserWarning)
                        flux[ie] = instrument.getFlux(ei)
                        elres[ie] = instrument.getResolution(0., ei)[0]
                self.flxaxes1.plot(eis, flux)
                line, = self.flxaxes2.plot(eis, elres)
                line.set_label(labels[ii])
        else:
            for ie, ei in enumerate(eis):
                with warnings.catch_warnings(record=True):
                    warnings.simplefilter('always', UserWarning)
                    flux[ie] = self.engine.getFlux(ei)
                    elres[ie] = self.engine.getResolution(0., ei)[0]
            if overplot:
                if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                    self.flxaxes1.hold(True)
                    self.flxaxes2.hold(True)
            else:
                self.flxaxes1.clear()
                self.flxaxes2.clear()
            self.flxaxes1.plot(eis, flux)
            line, = self.flxaxes2.plot(eis, elres)
            line.set_label('%s "%s" %d Hz' % (inst, chop, freq))
        self.flxaxes1.set_xlim([mn, mx])
        self.flxaxes2.set_xlim([mn, mx])
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        lg = self.flxaxes2.legend()
        lg.draggable()
        self.flxcanvas.draw()

    def update_slider(self, val=None):
        """
        Callback function for the x-axis slider of the flux tab
        """
        if val is None:
            val = float(self.flxedt.text()) / self.maxE[self.engine.instname] * 100
            if val < self.minE[self.engine.instname]:
                self.errormessage("Max Ei must be greater than %2.1f" % (self.minE[self.engine.instname]))
                val = (self.minE[self.engine.instname]+0.1) / self.maxE[self.engine.instname] * 100
            self.flxslder.set_val(val)
        else:
            val = self.flxslder.val * self.maxE[self.engine.instname] / 100
            self.flxedt.setText('%3.2f' % (val))
        self.plot_flux_ei(update=True)
        self.flxcanvas.draw()

    def plot_flux_hz(self):
        """
        Plots the flux vs freq in the middle tab
        """
        inst = self.engine.instname
        chop = self.engine.getChopper()
        ei = float(self.widgets['EiEdit']['Edit'].text())
        overplot = self.widgets['HoldCheck'].isChecked()
        # Do not recalculate if one of the plots has the same parametersc
        _, labels = self.frqaxes2.get_legend_handles_labels()
        searchStr = '([A-Z]+) "(.+)" Ei = ([0-9.-]+) meV'
        if labels and (overplot or len(labels) == 1):
            for prevtitle in labels:
                prevInst, prevChop, prevEi = re.search(searchStr, prevtitle).groups()
                if inst == prevInst and chop == prevChop and abs(ei-float(prevEi)) < 0.01:
                    return
        freq0 = self.engine.getFrequency()
        rep = self.engine.moderator.source_rep
        maxfreq = self.engine.chopper_system.max_frequencies
        freqs = range(rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1, rep)
        flux = np.zeros(len(freqs))
        elres = np.zeros(len(freqs))
        for ie, freq in enumerate(freqs):
            if hasattr(freq0, '__len__'):
                self.setFreq(manual_freq=[freq] + freq0[1:])
            else:
                self.setFreq(manual_freq=freq)
            with warnings.catch_warnings(record=True):
                warnings.simplefilter('always', UserWarning)
                flux[ie] = self.engine.getFlux(ei)
                elres[ie] = self.engine.getResolution(0., ei)[0]
        if overplot:
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                self.frqaxes1.hold(True)
                self.frqaxes2.hold(True)
        else:
            self.frqaxes1.clear()
            self.frqaxes2.clear()
        self.setFreq(manual_freq=freq0)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        line, = self.frqaxes1.plot(freqs, flux, 'o-')
        self.frqaxes1.set_xlim([0, np.max(freqs)])
        self.frqaxes2.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        line, = self.frqaxes2.plot(freqs, elres, 'o-')
        line.set_label('%s "%s" Ei = %5.3f meV' % (inst, chop, ei))
        lg = self.frqaxes2.legend()
        lg.draggable()
        self.frqaxes2.set_xlim([0, np.max(freqs)])
        self.frqcanvas.draw()

    def instSciCB(self):
        """
        Callback function for the "Instrument Scientist Mode" menu option
        """
        # MERLIN is a special case - want to hide ability to change phase from users
        if 'MERLIN' in self.engine.instname and 'G' in self.engine.getChopper():
            if self.instSciAct.isChecked():
                self.widgets['Chopper2Phase']['Edit'].show()
                self.widgets['Chopper2Phase']['Label'].show()
                self.widgets['Chopper2Phase']['Edit'].setText('1500')
                self.widgets['Chopper2Phase']['Label'].setText('Disk chopper phase delay time')
            else:
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()
        if self.instSciAct.isChecked():
            self.tabs.insertTab(self.scrtabID, self.scrtab, 'ScriptOutput')
            self.scrtab.show()
        else:
            self.tabs.removeTab(self.scrtabID)
            self.scrtab.hide()

    def errormessage(self, message):
        msg = QMessageBox()
        msg.setText(str(message))
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()

    def loadYaml(self):
        yaml_file = QFileDialog().getOpenFileName(self.mainWidget, 'Open Instrument YAML File', self.folder, 'Files (*.yaml)')
        if isinstance(yaml_file, tuple):
            yaml_file = yaml_file[0]
        yaml_file = str(yaml_file)
        new_folder = os.path.dirname(yaml_file)
        if new_folder != self.folder:
            self.folder = new_folder
        try:
            new_inst = Instrument(yaml_file)
        except (RuntimeError, AttributeError, ValueError) as err:
            self.errormessage(err)
        newname = new_inst.name
        if newname in self.instruments.keys() and not self.overwriteload.isChecked():
            overwrite, newname = self._ask_overwrite()
            if overwrite == 1:
                return
            elif overwrite == 0:
                newname = new_inst.name
        self.instruments[newname] = new_inst
        self.choppers[newname] = new_inst.getChopperNames()
        self.minE[newname] = max([new_inst.emin, 0.01])
        self.maxE[newname] = new_inst.emax
        self.updateInstrumentList()
        combo = self.widgets['InstrumentCombo']['Combo']
        idx = [i for i in range(combo.count()) if str(combo.itemText(i)) == newname]
        combo.setCurrentIndex(idx[0])
        self.setInstrument(newname)

    def _ask_overwrite(self):
        msg = QDialog()
        msg.setWindowTitle('Load overwrite')
        layout = QGridLayout()
        layout.addWidget(QLabel('Instrument %s already exists in memory. Overwrite this?'), 0, 0, 1, -1)
        buttons = [QPushButton(label) for label in ['Load and overwrite', 'Cancel Load', 'Load and rename to']]
        locations = [[1, 0], [1, 1], [2, 0]]
        self.overwrite_flag = 1

        def overwriteCB(idx):
            self.overwrite_flag = idx
            msg.accept()
        for idx, button in enumerate(buttons):
            button.clicked.connect(lambda _, idx=idx: overwriteCB(idx))
            layout.addWidget(button, locations[idx][0], locations[idx][1])
        newname = QLineEdit()
        newname.editingFinished.connect(lambda: overwriteCB(2))
        layout.addWidget(newname, 2, 1)
        msg.setLayout(layout)
        msg.exec_()
        newname = str(newname.text())
        if not newname or newname in self.instruments:
            self.errormessage('Invalid instrument name. Cancelling load.')
            self.overwrite_flag = 1
        return self.overwrite_flag, newname

    def updateInstrumentList(self):
        combo = self.widgets['InstrumentCombo']['Combo']
        old_instruments = [str(combo.itemText(i)) for i in range(combo.count())]
        new_instruments = [inst for inst in self.instruments if inst not in old_instruments]
        for inst in new_instruments:
            combo.addItem(inst)

    def plot_frame(self):
        """
        Plots the distance-time diagram in the right tab
        """
        if len(self.engine.chopper_system.choppers) > 1:
            self.engine.n_frame = int(self.repfig_nframe_edit.text())
            self.repaxes.clear()
            self.engine.plotMultiRepFrame(self.repaxes, first_rep=self.repfig_nframe_rep1only.isChecked())
            self.repcanvas.draw()

    def _gen_text_ei(self, ei, obj_in):
        obj = Instrument(obj_in)
        obj.setEi(ei)
        en = np.linspace(0, 0.95*ei, 10)
        try:
            flux = self.engine.getFlux()
            res = self.engine.getResolution(en)
        except ValueError as err:
            self.errormessage(err)
            raise ValueError(err)
        tsqvan, tsqdic, tsqmodchop = obj.getVanVar()
        v_mod, v_chop = tuple(np.sqrt(tsqmodchop[:2]) * 1e6)
        x0, _, x1, x2, _ = obj.chopper_system.getDistances()
        first_component = 'moderator'
        if x0 != tsqmodchop[2]:
            x0 = tsqmodchop[2]
            first_component = 'chopper 1'
        txt = '# ------------------------------------------------------------- #\n'
        txt += '# Ei = %8.2f meV\n' % (ei)
        txt += '# Flux = %8.2f n/cm2/s\n' % (flux)
        txt += '# Elastic resolution = %6.2f meV\n' % (res[0])
        txt += '# Time width at sample = %6.2f us, of which:\n' % (1e6*np.sqrt(tsqvan))
        for ky, val in list(tsqdic.items()):
            txt += '#     %20s : %6.2f us\n' % (ky, 1e6*np.sqrt(val))
        txt += '# %s distances:\n' % (obj.instname)
        txt += '#     x0 = %6.2f m (%s to Fermi)\n' % (x0, first_component)
        txt += '#     x1 = %6.2f m (Fermi to sample)\n' % (x1)
        txt += '#     x2 = %6.2f m (sample to detector)\n' % (x2)
        txt += '# Approximate inelastic resolution is given by:\n'
        txt += '#     dE = 2 * E2V * sqrt(ef**3 * t_van**2) / x2\n'
        txt += '#     where:  E2V = 4.373e-4 meV/(m/us) conversion from energy to speed\n'
        txt += '#             t_van**2 = (geom*t_mod)**2 + ((1+geom)*t_chop)**2\n'
        txt += '#             geom = (x1 + x2*(ei/ef)**1.5) / x0\n'
        txt += '#     and t_mod and t_chop are the moderator and chopper time widths at the\n'
        txt += '#     moderator and chopper positions (not at the sample as listed above).\n'
        txt += '# Which in this case is:\n'
        txt += '#     %.4e*sqrt(ef**3 * ( (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2 \n' % (874.78672e-6/x2, v_mod, x1/x0, x2/x0)
        txt += '#                              + (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2) )\n' % (v_chop, 1+x1/x0, x2/x0)
        txt += '#  EN (meV)   Full dE (meV)   Approx dE (meV)\n'
        for ii in range(len(res)):
            ef = ei-en[ii]
            approx = (874.78672e-6/x2)*np.sqrt(ef**3 * ((v_mod*((x1/x0)+(x2/x0)*(ei/ef)**1.5))**2
                                                        + (v_chop*(1+(x1/x0)+(x2/x0)*(ei/ef)**1.5))**2))
            txt += '%12.5f %12.5f %12.5f\n' % (en[ii], res[ii], approx)
        return txt

    def genText(self):
        """
        Generates text output of the resolution function versus energy transfer and other information.
        """
        multiplot = self.widgets['MultiRepCheck'].isChecked()
        obj = self.engine
        if obj.getChopper() is None:
            self.setChopper(self.widgets['ChopperCombo']['Combo'].currentText())
        if obj.getEi() is None:
            self.setEi()
        instname, chtyp, freqs, ei_in = tuple([obj.instname, obj.getChopper(), obj.getFrequency(), obj.getEi()])
        txt = '# ------------------------------------------------------------- #\n'
        txt += '# Chop calculation for instrument %s\n' % (instname)
        if obj.isFermi:
            txt += '#     with chopper %s at %3i Hz\n' % (chtyp, freqs[0])
        else:
            txt += '#     in %s mode with:\n' % (chtyp)
            freq_names = obj.chopper_system.frequency_names
            for idx in range(len(freq_names)):
                txt += '#     %s at %3i Hz\n' % (freq_names[idx], freqs[idx])
        txt += self._gen_text_ei(ei_in, obj)
        if multiplot:
            for ei in sorted(self.engine.getAllowedEi()):
                if np.abs(ei - ei_in) > 0.001:
                    txt += self._gen_text_ei(ei, obj)
        return txt

    def showText(self):
        """
        Creates a dialog to show the generated text output.
        """
        try:
            generatedText = self.genText()
        except ValueError:
            return
        self.txtwin = QDialog()
        self.txtedt = QTextEdit()
        self.txtbtn = QPushButton('OK')
        self.txtwin.layout = QVBoxLayout(self.txtwin)
        self.txtwin.layout.addWidget(self.txtedt)
        self.txtwin.layout.addWidget(self.txtbtn)
        self.txtbtn.clicked.connect(self.txtwin.deleteLater)
        self.txtedt.setText(generatedText)
        self.txtedt.setReadOnly(True)
        self.txtwin.setWindowTitle('Resolution information')
        self.txtwin.setWindowModality(Qt.ApplicationModal)
        self.txtwin.setAttribute(Qt.WA_DeleteOnClose)
        self.txtwin.setMinimumSize(400, 600)
        self.txtwin.resize(400, 600)
        self.txtwin.show()
        self.txtloop = QEventLoop()
        self.txtloop.exec_()

    def saveText(self):
        """
        Saves the generated text to a file (opens file dialog).
        """
        fname = QFileDialog.getSaveFileName(self, 'Open file', '')
        if isinstance(fname, tuple):
            fname = fname[0]
        fid = open(fname, 'w')
        fid.write(self.genText())
        fid.close()

    def update_script(self):
        """
        Updates the text window with information about the previous calculation.
        """
        if self.widgets['MultiRepCheck'].isChecked():
            out = self.engine.getMultiWidths()
            new_str = '\n'
            for ie, ee in enumerate(out['Eis']):
                res = out['Energy'][ie]
                percent = res / ee * 100
                chop_width = out['chopper'][ie]
                mod_width = out['moderator'][ie]
                new_str += 'Ei is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (ee, res * 1000, percent)
                new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (chop_width, mod_width)
        else:
            ei =  self.engine.getEi()
            out = self.engine.getWidths()
            res = out['Energy']
            percent = res / ei * 100
            chop_width = out['chopper']
            mod_width = out['moderator']
            new_str = '\nEi is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (ei, res * 1000, percent)
            new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (chop_width, mod_width)
        self.scredt.append(new_str)

    def onHelp(self):
        """
        Shows the help page
        """
        try:
            from pymantidplot.proxies import showCustomInterfaceHelp
            showCustomInterfaceHelp("PyChop")
        except ImportError:
            helpTxt = "PyChop is a tool to allow direct inelastic neutron\nscattering users to estimate the inelastic resolution\n"
            helpTxt += "and incident flux for a given spectrometer setting.\n\nFirst select the instrument, chopper settings and\n"
            helpTxt += "Ei, and then click 'Calculate and Plot'. Data for all\nthe graphs will be generated (may take 1-2s) and\n"
            helpTxt += "all graphs will be updated. If the 'Hold current plot'\ncheck box is ticked, additional settings will be\n"
            helpTxt += "overplotted on the existing graphs if they are\ndifferent from previous settings.\n\nMore in-depth help "
            helpTxt += "can be obtained from the\nMantid help pages."
            self.hlpwin = QDialog()
            self.hlpedt = QLabel(helpTxt)
            self.hlpbtn = QPushButton('OK')
            self.hlpwin.layout = QVBoxLayout(self.hlpwin)
            self.hlpwin.layout.addWidget(self.hlpedt)
            self.hlpwin.layout.addWidget(self.hlpbtn)
            self.hlpbtn.clicked.connect(self.hlpwin.deleteLater)
            self.hlpwin.setWindowTitle('Help')
            self.hlpwin.setWindowModality(Qt.ApplicationModal)
            self.hlpwin.setAttribute(Qt.WA_DeleteOnClose)
            self.hlpwin.setMinimumSize(370, 300)
            self.hlpwin.resize(370, 300)
            self.hlpwin.show()
            self.hlploop = QEventLoop()
            self.hlploop.exec_()

    def drawLayout(self):
        """
        Draws the GUI layout.
        """
        self.widgetslist = [
            ['pair', 'show', 'Instrument', 'combo', self.instruments, self.setInstrument, 'InstrumentCombo'],
            ['pair', 'show', 'Chopper', 'combo', '', self.setChopper, 'ChopperCombo'],
            ['pair', 'show', 'Frequency', 'combo', '', self.setFreq, 'FrequencyCombo'],
            ['pair', 'hide', 'Pulse remover chopper freq', 'combo', '', self.setFreq, 'PulseRemoverCombo'],
            ['pair', 'show', 'Ei', 'edit', '', self.setEi, 'EiEdit'],
            ['pair', 'hide', 'Chopper 2 phase delay time', 'edit', '5', self.setFreq, 'Chopper2Phase'],
            ['spacer'],
            ['single', 'show', 'Calculate and Plot', 'button', self.calc_callback, 'CalculateButton'],
            ['single', 'show', 'Hold current plot', 'check', lambda: None, 'HoldCheck'],
            ['single', 'show', 'Show multi-reps', 'check', lambda: None, 'MultiRepCheck'],
            ['spacer'],
            ['single', 'show', 'Show data ascii window', 'button', self.showText, 'ShowAsciiButton'],
            ['single', 'show', 'Save data as ascii', 'button', self.saveText, 'SaveAsciiButton']
        ]
        self.droplabels = []
        self.dropboxes = []
        self.singles = []
        self.widgets = {}

        self.leftPanel = QVBoxLayout()
        self.rightPanel = QVBoxLayout()
        self.tabs = QTabWidget(self)
        self.fullWindow = QGridLayout()
        for widget in self.widgetslist:
            if 'pair' in widget[0]:
                self.droplabels.append(QLabel(widget[2]))
                if 'combo' in widget[3]:
                    self.dropboxes.append(QComboBox(self))
                    self.dropboxes[-1].activated['QString'].connect(widget[5])
                    for item in widget[4]:
                        self.dropboxes[-1].addItem(item)
                    self.widgets[widget[-1]] = {'Combo':self.dropboxes[-1], 'Label':self.droplabels[-1]}
                elif 'edit' in widget[3]:
                    self.dropboxes.append(QLineEdit(self))
                    self.dropboxes[-1].returnPressed.connect(widget[5])
                    self.widgets[widget[-1]] = {'Edit':self.dropboxes[-1], 'Label':self.droplabels[-1]}
                else:
                    raise RuntimeError('Bug in code - widget %s is not recognised.' % (widget[3]))
                self.leftPanel.addWidget(self.droplabels[-1])
                self.leftPanel.addWidget(self.dropboxes[-1])
                if 'hide' in widget[1]:
                    self.droplabels[-1].hide()
                    self.dropboxes[-1].hide()
            elif 'single' in widget[0]:
                if 'check' in widget[3]:
                    self.singles.append(QCheckBox(widget[2], self))
                    self.singles[-1].stateChanged.connect(widget[4])
                elif 'button' in widget[3]:
                    self.singles.append(QPushButton(widget[2]))
                    self.singles[-1].clicked.connect(widget[4])
                else:
                    raise RuntimeError('Bug in code - widget %s is not recognised.' % (widget[3]))
                self.leftPanel.addWidget(self.singles[-1])
                if 'hide' in widget[1]:
                    self.singles[-1].hide()
                self.widgets[widget[-1]] = self.singles[-1]
            elif 'spacer' in widget[0]:
                self.leftPanel.addItem(QSpacerItem(0, 35))
            else:
                raise RuntimeError('Bug in code - widget class %s is not recognised.' % (widget[0]))

        # Right panel, matplotlib figures
        self.resfig = Figure()
        self.resfig.patch.set_facecolor('white')
        self.rescanvas = FigureCanvas(self.resfig)
        self.resaxes = self.resfig.add_subplot(111)
        self.resaxes.axhline(color='k')
        self.resaxes.set_xlabel('Energy Transfer (meV)')
        self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)')
        self.resfig_controls = NavigationToolbar(self.rescanvas, self)
        self.restab = QWidget(self.tabs)
        self.restabbox = QVBoxLayout()
        self.restabbox.addWidget(self.rescanvas)
        self.restabbox.addWidget(self.resfig_controls)
        self.restab.setLayout(self.restabbox)

        self.flxfig = Figure()
        self.flxfig.patch.set_facecolor('white')
        self.flxcanvas = FigureCanvas(self.flxfig)
        self.flxaxes1 = self.flxfig.add_subplot(121)
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.flxaxes2 = self.flxfig.add_subplot(122)
        self.flxaxes2.set_xlabel('Incident Energy (meV)')
        self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        self.flxfig_controls = NavigationToolbar(self.flxcanvas, self)
        self.flxsldfg = Figure()
        self.flxsldfg.patch.set_facecolor('white')
        self.flxsldcv = FigureCanvas(self.flxsldfg)
        self.flxsldax = self.flxsldfg.add_subplot(111)
        self.flxslder = Slider(self.flxsldax, 'Ei (meV)', 0, 100, valinit=100)
        self.flxslder.valtext.set_visible(False)
        self.flxslder.on_changed(self.update_slider)
        self.flxedt = QLineEdit()
        self.flxedt.setText('1000')
        self.flxedt.returnPressed.connect(self.update_slider)
        self.flxtab = QWidget(self.tabs)
        self.flxsldbox = QHBoxLayout()
        self.flxsldbox.addWidget(self.flxsldcv)
        self.flxsldbox.addWidget(self.flxedt)
        self.flxsldwdg = QWidget()
        self.flxsldwdg.setLayout(self.flxsldbox)
        sz = self.flxsldwdg.maximumSize()
        sz.setHeight(50)
        self.flxsldwdg.setMaximumSize(sz)
        self.flxtabbox = QVBoxLayout()
        self.flxtabbox.addWidget(self.flxcanvas)
        self.flxtabbox.addWidget(self.flxsldwdg)
        self.flxtabbox.addWidget(self.flxfig_controls)
        self.flxtab.setLayout(self.flxtabbox)

        self.frqfig = Figure()
        self.frqfig.patch.set_facecolor('white')
        self.frqcanvas = FigureCanvas(self.frqfig)
        self.frqaxes1 = self.frqfig.add_subplot(121)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.frqaxes2 = self.frqfig.add_subplot(122)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        self.frqfig_controls = NavigationToolbar(self.frqcanvas, self)
        self.frqtab = QWidget(self.tabs)
        self.frqtabbox = QVBoxLayout()
        self.frqtabbox.addWidget(self.frqcanvas)
        self.frqtabbox.addWidget(self.frqfig_controls)
        self.frqtab.setLayout(self.frqtabbox)

        self.repfig = Figure()
        self.repfig.patch.set_facecolor('white')
        self.repcanvas = FigureCanvas(self.repfig)
        self.repaxes = self.repfig.add_subplot(111)
        self.repaxes.axhline(color='k')
        self.repaxes.set_xlabel(r'TOF ($\mu$sec)')
        self.repaxes.set_ylabel('Distance (m)')
        self.repfig_controls = NavigationToolbar(self.repcanvas, self)
        self.repfig_nframe_label = QLabel('Number of frames to plot')
        self.repfig_nframe_edit = QLineEdit('1')
        self.repfig_nframe_button = QPushButton('Replot')
        self.repfig_nframe_button.clicked.connect(lambda: self.plot_frame())
        self.repfig_nframe_rep1only = QCheckBox('First Rep Only')
        self.repfig_nframe_box = QHBoxLayout()
        self.repfig_nframe_box.addWidget(self.repfig_nframe_label)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_edit)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_button)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_rep1only)
        self.reptab = QWidget(self.tabs)
        self.repfig_nframe = QWidget(self.reptab)
        self.repfig_nframe.setLayout(self.repfig_nframe_box)
        self.repfig_nframe.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))
        self.reptabbox = QVBoxLayout()
        self.reptabbox.addWidget(self.repcanvas)
        self.reptabbox.addWidget(self.repfig_nframe)
        self.reptabbox.addWidget(self.repfig_controls)
        self.reptab.setLayout(self.reptabbox)

        self.qefig = Figure()
        self.qefig.patch.set_facecolor('white')
        self.qecanvas = FigureCanvas(self.qefig)
        self.qeaxes = self.qefig.add_subplot(111)
        self.qeaxes.axhline(color='k')
        self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$')
        self.qeaxes.set_ylabel('Energy Transfer (meV)')
        self.qefig_controls = NavigationToolbar(self.qecanvas, self)
        self.qetabbox = QVBoxLayout()
        self.qetabbox.addWidget(self.qecanvas)
        self.qetabbox.addWidget(self.qefig_controls)
        self.qetab = QWidget(self.tabs)
        self.qetab.setLayout(self.qetabbox)

        self.scrtab = QWidget(self.tabs)
        self.scredt = QTextEdit()
        self.scrcls = QPushButton("Clear")
        self.scrcls.clicked.connect(lambda: self.scredt.clear())
        self.scrbox = QVBoxLayout()
        self.scrbox.addWidget(self.scredt)
        self.scrbox.addWidget(self.scrcls)
        self.scrtab.setLayout(self.scrbox)
        self.scrtab.hide()

        self.tabs.addTab(self.restab, 'Resolution')
        self.tabs.addTab(self.flxtab, 'Flux-Ei')
        self.tabs.addTab(self.frqtab, 'Flux-Freq')
        self.tabs.addTab(self.reptab, 'Time-Distance')
        self.tdtabID = 3
        self.tabs.setTabEnabled(self.tdtabID, False)
        self.tabs.addTab(self.qetab, 'Q-E')
        self.qetabID = 4
        self.tabs.setTabEnabled(self.qetabID, False)
        self.scrtabID = 5
        self.rightPanel.addWidget(self.tabs)

        self.menuLoad = QMenu('Load')
        self.loadAct = QAction('Load YAML', self.menuLoad)
        self.loadAct.triggered.connect(self.loadYaml)
        self.menuLoad.addAction(self.loadAct)
        self.menuOptions = QMenu('Options')
        self.instSciAct = QAction('Instrument Scientist Mode', self.menuOptions, checkable=True)
        self.instSciAct.triggered.connect(self.instSciCB)
        self.menuOptions.addAction(self.instSciAct)
        self.eiPlots = QAction('Press Enter in Ei box updates plots', self.menuOptions, checkable=True)
        self.menuOptions.addAction(self.eiPlots)
        self.overwriteload = QAction('Always overwrite instruments in memory', self.menuOptions, checkable=True)
        self.menuOptions.addAction(self.overwriteload)
        self.menuBar().addMenu(self.menuLoad)
        self.menuBar().addMenu(self.menuOptions)

        self.leftPanelWidget = QWidget()
        self.leftPanelWidget.setLayout(self.leftPanel)
        self.leftPanelWidget.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred))
        self.fullWindow.addWidget(self.leftPanelWidget, 0, 0)
        self.fullWindow.addLayout(self.rightPanel, 0, 1)
        self.helpbtn = QPushButton("?", self)
        self.helpbtn.setMaximumWidth(30)
        self.helpbtn.clicked.connect(self.onHelp)
        self.fullWindow.addWidget(self.helpbtn, 1, 0, 1, -1)

        self.mainWidget = QWidget()
        self.mainWidget.setLayout(self.fullWindow)
        self.setCentralWidget(self.mainWidget)
        self.setWindowTitle('PyChopGUI')
        self.show()
コード例 #24
0
ファイル: packages.py プロジェクト: irfanalamt/JSnoobie
class PackagesDialog(DialogBase):
    """Package dependencies dialog."""

    sig_setup_ready = Signal()

    def __init__(
        self,
        parent=None,
        packages=None,
        pip_packages=None,
        remove_only=False,
        update_only=False,
    ):
        """About dialog."""
        super(PackagesDialog, self).__init__(parent=parent)

        # Variables
        self.api = AnacondaAPI()
        self.actions = None
        self.packages = packages or []
        self.pip_packages = pip_packages or []

        # Widgets
        self.stack = QStackedWidget()
        self.table = QTableWidget()
        self.text = QTextEdit()
        self.label_description = LabelBase()
        self.label_status = LabelBase()
        self.progress_bar = QProgressBar()
        self.button_ok = ButtonPrimary('Apply')
        self.button_cancel = ButtonNormal('Cancel')

        # Widget setup
        self.text.setReadOnly(True)
        self.stack.addWidget(self.table)
        self.stack.addWidget(self.text)
        if remove_only:
            text = 'The following packages will be removed:<br>'
        else:
            text = 'The following packages will be modified:<br>'
        self.label_description.setText(text)
        self.label_description.setWordWrap(True)
        self.label_description.setWordWrap(True)
        self.label_status.setWordWrap(True)
        self.table.horizontalScrollBar().setVisible(False)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setAlternatingRowColors(True)
        self.table.setSelectionMode(QAbstractItemView.NoSelection)
        self.table.setSortingEnabled(True)
        self._hheader = self.table.horizontalHeader()
        self._vheader = self.table.verticalHeader()
        self._hheader.setStretchLastSection(True)
        self._hheader.setDefaultAlignment(Qt.AlignLeft)
        self._hheader.setSectionResizeMode(self._hheader.Fixed)
        self._vheader.setSectionResizeMode(self._vheader.Fixed)
        self.button_ok.setMinimumWidth(70)
        self.button_ok.setDefault(True)
        self.base_minimum_width = 300 if remove_only else 420
        if remove_only:
            self.setWindowTitle("Remove Packages")
        elif update_only:
            self.setWindowTitle("Update Packages")
        else:
            self.setWindowTitle("Install Packages")

        self.setMinimumWidth(self.base_minimum_width)

        # Layouts
        layout_progress = QHBoxLayout()
        layout_progress.addWidget(self.label_status)
        layout_progress.addWidget(SpacerHorizontal())
        layout_progress.addWidget(self.progress_bar)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

        layout = QVBoxLayout()
        layout.addWidget(self.label_description)
        layout.addWidget(SpacerVertical())
        layout.addWidget(self.stack)
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_progress)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)
        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_ok.setDisabled(True)

        # Setup
        self.table.setDisabled(True)
        self.update_status('Solving package specifications',
                           value=0,
                           max_value=0)

    def setup(self, worker, output, error):
        """Setup the widget to include the list of dependencies."""
        if not isinstance(output, dict):
            output = {}

        packages = sorted(pkg.split('==')[0] for pkg in self.packages)
        success = output.get('success')
        error = output.get('error', '')
        exception_name = output.get('exception_name', '')
        actions = output.get('actions', [])
        prefix = worker.prefix

        if exception_name:
            message = exception_name
        else:
            # All requested packages already installed
            message = output.get('message', ' ')

        navi_deps_error = self.api.check_navigator_dependencies(
            actions, prefix)
        description = self.label_description.text()

        if error:
            description = 'No packages will be modified.'
            self.stack.setCurrentIndex(1)
            self.button_ok.setDisabled(True)
            if self.api.is_offline():
                error = ("Some of the functionality of Anaconda Navigator "
                         "will be limited in <b>offline mode</b>. <br><br>"
                         "Installation and upgrade actions will be subject to "
                         "the packages currently available on your package "
                         "cache.")
            self.text.setText(error)
        elif navi_deps_error:
            description = 'No packages will be modified.'
            error = ('Downgrading/removing these packages will modify '
                     'Anaconda Navigator dependencies.')
            self.text.setText(error)
            self.stack.setCurrentIndex(1)
            message = 'NavigatorDependenciesError'
            self.button_ok.setDisabled(True)
        elif success and actions:
            self.stack.setCurrentIndex(0)
            # Conda 4.3.x
            if isinstance(actions, list):
                actions_link = actions[0].get('LINK', [])
                actions_unlink = actions[0].get('UNLINK', [])
            # Conda 4.4.x
            else:
                actions_link = actions.get('LINK', [])
                actions_unlink = actions.get('UNLINK', [])

            deps = set()
            deps = deps.union({p['name'] for p in actions_link})
            deps = deps.union({p['name'] for p in actions_unlink})
            deps = deps - set(packages)
            deps = sorted(list(deps))

            count_total_packages = len(packages) + len(deps)
            plural_total = 's' if count_total_packages != 1 else ''
            plural_selected = 's' if len(packages) != 1 else ''

            self.table.setRowCount(count_total_packages)
            self.table.setColumnCount(4)
            if actions_link:
                description = '{0} package{1} will be installed'.format(
                    count_total_packages, plural_total)
                self.table.showColumn(2)
                self.table.showColumn(3)
            elif actions_unlink and not actions_link:
                self.table.hideColumn(2)
                self.table.hideColumn(3)
                self.table.setHorizontalHeaderLabels(
                    ['Name', 'Unlink', 'Link', 'Channel'])
                description = '{0} package{1} will be removed'.format(
                    count_total_packages, plural_total)

            for row, pkg in enumerate(packages + deps):
                link_item = [p for p in actions_link if p['name'] == pkg]
                if not link_item:
                    link_item = {
                        'version': '-'.center(len('link')),
                        'channel': '-'.center(len('channel')),
                    }
                else:
                    link_item = link_item[0]

                unlink_item = [p for p in actions_unlink if p['name'] == pkg]
                if not unlink_item:
                    unlink_item = {
                        'version': '-'.center(len('link')),
                    }
                else:
                    unlink_item = unlink_item[0]

                unlink_version = str(unlink_item['version'])
                link_version = str(link_item['version'])

                item_unlink_v = QTableWidgetItem(unlink_version)
                item_link_v = QTableWidgetItem(link_version)
                item_link_c = QTableWidgetItem(link_item['channel'])
                if pkg in packages:
                    item_name = QTableWidgetItem(pkg)
                else:
                    item_name = QTableWidgetItem('*' + pkg)

                items = [item_name, item_unlink_v, item_link_v, item_link_c]
                for column, item in enumerate(items):
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self.table.setItem(row, column, item)

            if deps:
                message = (
                    '<b>*</b> indicates the package is a dependency of a '
                    'selected package{0}<br>').format(plural_selected)

            self.button_ok.setEnabled(True)
            self.table.resizeColumnsToContents()
            unlink_width = self.table.columnWidth(1)
            if unlink_width < 60:
                self.table.setColumnWidth(1, 60)
            self.table.setHorizontalHeaderLabels(
                ['Name  ', 'Unlink  ', 'Link  ', 'Channel  '])

        self.table.setEnabled(True)
        self.update_status(message=message)
        self.label_description.setText(description)

        # Adjust size after data has populated the table
        self.table.resizeColumnsToContents()
        width = sum(
            self.table.columnWidth(i) for i in range(self.table.columnCount()))
        delta = (self.width() - self.table.width() +
                 self.table.verticalHeader().width() + 10)

        new_width = width + delta

        if new_width < self.base_minimum_width:
            new_width = self.base_minimum_width

        self.setMinimumWidth(new_width)
        self.setMaximumWidth(new_width)

        self.sig_setup_ready.emit()

    def update_status(self, message='', value=None, max_value=None):
        """Update status of packages dialog."""
        self.label_status.setText(message)

        if max_value is None and value is None:
            self.progress_bar.setVisible(False)
        else:
            self.progress_bar.setVisible(True)
            self.progress_bar.setMaximum(max_value)
            self.progress_bar.setValue(value)
コード例 #25
0
class MeasurementSettings(QWidget):
    """
    :type settings: Settings
    """
    def __init__(self, settings: PartSettings, parent=None):
        super().__init__(parent)
        self.chosen_element: Optional[MeasurementListWidgetItem] = None
        self.chosen_element_area: Optional[Tuple[AreaType, float]] = None
        self.settings = settings
        self.profile_list = QListWidget(self)
        self.profile_description = QTextEdit(self)
        self.profile_description.setReadOnly(True)
        self.profile_options = QListWidget()
        self.profile_options_chosen = QListWidget()
        self.measurement_area_choose = QEnumComboBox(enum_class=AreaType)
        self.per_component = QEnumComboBox(enum_class=PerComponent)
        self.power_num = QDoubleSpinBox()
        self.power_num.setDecimals(3)
        self.power_num.setRange(-100, 100)
        self.power_num.setValue(1)
        self.choose_butt = QPushButton("→", self)
        self.discard_butt = QPushButton("←", self)
        self.proportion_butt = QPushButton("Ratio", self)
        self.proportion_butt.setToolTip("Create proportion from two parameter")
        self.move_up = QPushButton("↑", self)
        self.move_down = QPushButton("↓", self)
        self.remove_button = QPushButton("Remove")
        self.save_butt = QPushButton("Save")
        self.save_butt.setToolTip(
            "Set name for set and choose at least one parameter")
        self.save_butt_with_name = QPushButton(
            "Save with custom parameters designation")
        self.save_butt_with_name.setToolTip(
            "Set name for set and choose at least one parameter")
        self.reset_butt = QPushButton("Clear")
        self.soft_reset_butt = QPushButton("Remove user parameters")
        self.profile_name = QLineEdit(self)

        self.delete_profile_butt = QPushButton("Delete ")
        self.export_profiles_butt = QPushButton("Export")
        self.import_profiles_butt = QPushButton("Import")
        self.edit_profile_butt = QPushButton("Edit")

        self.choose_butt.setDisabled(True)
        self.choose_butt.clicked.connect(self.choose_option)
        self.discard_butt.setDisabled(True)
        self.discard_butt.clicked.connect(self.discard_option)
        self.proportion_butt.setDisabled(True)
        self.proportion_butt.clicked.connect(self.proportion_action)
        self.save_butt.setDisabled(True)
        self.save_butt.clicked.connect(self.save_action)
        self.save_butt_with_name.setDisabled(True)
        self.save_butt_with_name.clicked.connect(self.named_save_action)
        self.profile_name.textChanged.connect(self.name_changed)
        self.move_down.setDisabled(True)
        self.move_down.clicked.connect(self.move_down_fun)
        self.move_up.setDisabled(True)
        self.move_up.clicked.connect(self.move_up_fun)
        self.remove_button.setDisabled(True)
        self.remove_button.clicked.connect(self.remove_element)
        self.reset_butt.clicked.connect(self.reset_action)
        self.soft_reset_butt.clicked.connect(self.soft_reset)
        self.delete_profile_butt.setDisabled(True)
        self.delete_profile_butt.clicked.connect(self.delete_profile)
        self.export_profiles_butt.clicked.connect(
            self.export_measurement_profiles)
        self.import_profiles_butt.clicked.connect(
            self.import_measurement_profiles)
        self.edit_profile_butt.clicked.connect(self.edit_profile)

        self.profile_list.itemSelectionChanged.connect(self.profile_chosen)
        self.profile_options.itemSelectionChanged.connect(
            self.create_selection_changed)
        self.profile_options_chosen.itemSelectionChanged.connect(
            self.create_selection_chosen_changed)
        self.settings.measurement_profiles_changed.connect(
            self._refresh_profiles)

        layout = QVBoxLayout()
        layout.addWidget(QLabel("Measurement set:"))
        profile_layout = QHBoxLayout()
        profile_layout.addWidget(self.profile_list)
        profile_layout.addWidget(self.profile_description)
        profile_buttons_layout = QHBoxLayout()
        profile_buttons_layout.addWidget(self.delete_profile_butt)
        profile_buttons_layout.addWidget(self.export_profiles_butt)
        profile_buttons_layout.addWidget(self.import_profiles_butt)
        profile_buttons_layout.addWidget(self.edit_profile_butt)
        profile_buttons_layout.addStretch()
        layout.addLayout(profile_layout)
        layout.addLayout(profile_buttons_layout)
        heading_layout = QHBoxLayout()
        # heading_layout.addWidget(QLabel("Create profile"), 1)
        heading_layout.addWidget(h_line(), 6)
        layout.addLayout(heading_layout)
        name_layout = QHBoxLayout()
        name_layout.addWidget(QLabel("Set name:"))
        name_layout.addWidget(self.profile_name)
        name_layout.addStretch()
        name_layout.addWidget(QLabel("Per component:"))
        name_layout.addWidget(self.per_component)
        name_layout.addWidget(QLabel("Area:"))
        name_layout.addWidget(self.measurement_area_choose)
        name_layout.addWidget(QLabel("to power:"))
        name_layout.addWidget(self.power_num)
        layout.addLayout(name_layout)
        create_layout = QHBoxLayout()
        create_layout.addWidget(self.profile_options)
        butt_op_layout = QVBoxLayout()
        butt_op_layout.addStretch()
        butt_op_layout.addWidget(self.choose_butt)
        butt_op_layout.addWidget(self.discard_butt)
        butt_op_layout.addWidget(self.proportion_butt)
        butt_op_layout.addWidget(self.reset_butt)
        butt_op_layout.addStretch()
        create_layout.addLayout(butt_op_layout)
        create_layout.addWidget(self.profile_options_chosen)
        butt_move_layout = QVBoxLayout()
        butt_move_layout.addStretch()
        butt_move_layout.addWidget(self.move_up)
        butt_move_layout.addWidget(self.move_down)
        butt_move_layout.addWidget(self.remove_button)
        butt_move_layout.addStretch()
        create_layout.addLayout(butt_move_layout)
        layout.addLayout(create_layout)
        save_butt_layout = QHBoxLayout()
        save_butt_layout.addWidget(self.soft_reset_butt)
        save_butt_layout.addStretch()
        save_butt_layout.addWidget(self.save_butt)
        save_butt_layout.addWidget(self.save_butt_with_name)
        layout.addLayout(save_butt_layout)
        self.setLayout(layout)

        for profile in MEASUREMENT_DICT.values():
            help_text = profile.get_description()
            lw = MeasurementListWidgetItem(profile.get_starting_leaf())
            lw.setToolTip(help_text)
            self.profile_options.addItem(lw)
        self._refresh_profiles()

    def _refresh_profiles(self):
        item = self.profile_list.currentItem()
        items = list(self.settings.measurement_profiles.keys())
        try:
            index = items.index(item.text())
        except (ValueError, AttributeError):
            index = -1

        self.profile_list.clear()
        self.profile_list.addItems(items)
        self.profile_list.setCurrentRow(index)

        if self.profile_list.count() == 0:
            self.export_profiles_butt.setDisabled(True)
        else:
            self.export_profiles_butt.setEnabled(True)

    def remove_element(self):
        elem = self.profile_options_chosen.currentItem()
        if elem is None:
            return
        index = self.profile_options_chosen.currentRow()
        self.profile_options_chosen.takeItem(index)
        if self.profile_options_chosen.count() == 0:
            self.move_down.setDisabled(True)
            self.move_up.setDisabled(True)
            self.remove_button.setDisabled(True)
            self.discard_butt.setDisabled(True)
            self.save_butt.setDisabled(True)
            self.save_butt_with_name.setDisabled(True)

    def delete_profile(self):
        item = self.profile_list.currentItem()
        del self.settings.measurement_profiles[str(item.text())]

    def profile_chosen(self):
        self.delete_profile_butt.setEnabled(True)
        if self.profile_list.count() == 0:
            self.profile_description.setText("")
            return
        item = self.profile_list.currentItem()
        if item is None:
            self.profile_description.setText("")
            return
        profile = self.settings.measurement_profiles[item.text()]
        self.profile_description.setText(str(profile))

    def create_selection_changed(self):
        self.choose_butt.setEnabled(True)
        self.proportion_butt.setEnabled(True)

    def proportion_action(self):
        # TODO use get_parameters
        if self.chosen_element is None:
            item = self.profile_options.currentItem()
            self.chosen_element_area = self.get_parameters(
                deepcopy(item.stat),
                self.measurement_area_choose.currentEnum(),
                self.per_component.currentEnum(),
                self.power_num.value(),
            )
            if self.chosen_element_area is None:
                return
            self.chosen_element = item
            item.setIcon(QIcon(os.path.join(icons_dir, "task-accepted.png")))
        elif (self.profile_options.currentItem() == self.chosen_element
              and self.measurement_area_choose.currentEnum()
              == self.chosen_element_area.area
              and self.per_component.currentEnum()
              == self.chosen_element_area.per_component):
            self.chosen_element.setIcon(QIcon())
            self.chosen_element = None
        else:
            item: MeasurementListWidgetItem = self.profile_options.currentItem(
            )
            leaf = self.get_parameters(
                deepcopy(item.stat),
                self.measurement_area_choose.currentEnum(),
                self.per_component.currentEnum(),
                self.power_num.value(),
            )
            if leaf is None:
                return
            lw = MeasurementListWidgetItem(
                Node(op="/", left=self.chosen_element_area, right=leaf))
            lw.setToolTip("User defined")
            self._add_option(lw)
            self.chosen_element.setIcon(QIcon())
            self.chosen_element = None
            self.chosen_element_area = None

    def _add_option(self, item: MeasurementListWidgetItem):
        for i in range(self.profile_options_chosen.count()):
            if item.text() == self.profile_options_chosen.item(i).text():
                return
        self.profile_options_chosen.addItem(item)
        if self.good_name():
            self.save_butt.setEnabled(True)
            self.save_butt_with_name.setEnabled(True)
        if self.profile_options.count() == 0:
            self.choose_butt.setDisabled(True)

    def create_selection_chosen_changed(self):
        # print(self.profile_options_chosen.count())
        self.remove_button.setEnabled(True)
        if self.profile_options_chosen.count() == 0:
            self.move_down.setDisabled(True)
            self.move_up.setDisabled(True)
            self.remove_button.setDisabled(True)
            return
        self.discard_butt.setEnabled(True)
        if self.profile_options_chosen.currentRow() != 0:
            self.move_up.setEnabled(True)
        else:
            self.move_up.setDisabled(True)
        if self.profile_options_chosen.currentRow(
        ) != self.profile_options_chosen.count() - 1:
            self.move_down.setEnabled(True)
        else:
            self.move_down.setDisabled(True)

    def good_name(self):
        return str(self.profile_name.text()).strip() != ""

    def move_down_fun(self):
        row = self.profile_options_chosen.currentRow()
        item = self.profile_options_chosen.takeItem(row)
        self.profile_options_chosen.insertItem(row + 1, item)
        self.profile_options_chosen.setCurrentRow(row + 1)
        self.create_selection_chosen_changed()

    def move_up_fun(self):
        row = self.profile_options_chosen.currentRow()
        item = self.profile_options_chosen.takeItem(row)
        self.profile_options_chosen.insertItem(row - 1, item)
        self.profile_options_chosen.setCurrentRow(row - 1)
        self.create_selection_chosen_changed()

    def name_changed(self):
        if self.good_name() and self.profile_options_chosen.count() > 0:
            self.save_butt.setEnabled(True)
            self.save_butt_with_name.setEnabled(True)
        else:
            self.save_butt.setDisabled(True)
            self.save_butt_with_name.setDisabled(True)

    def form_dialog(self, arguments):
        return FormDialog(arguments, settings=self.settings, parent=self)

    def get_parameters(self, node: Union[Node, Leaf], area: AreaType,
                       component: PerComponent, power: float):
        if isinstance(node, Node):
            return node
        node = node.replace_(power=power)
        if node.area is None:
            node = node.replace_(area=area)
        if node.per_component is None:
            node = node.replace_(per_component=component)
        with suppress(KeyError):
            arguments = MEASUREMENT_DICT[str(node.name)].get_fields()
            if len(arguments) > 0 and len(node.dict) == 0:
                dial = self.form_dialog(arguments)
                if dial.exec_():
                    node = node._replace(dict=dial.get_values())
                else:
                    return
        return node

    def choose_option(self):
        selected_item = self.profile_options.currentItem()
        # selected_row = self.profile_options.currentRow()
        if not isinstance(selected_item, MeasurementListWidgetItem):
            raise ValueError(
                f"Current item (type: {type(selected_item)} is not instance of MeasurementListWidgetItem"
            )
        node = deepcopy(selected_item.stat)
        # noinspection PyTypeChecker
        node = self.get_parameters(node,
                                   self.measurement_area_choose.currentEnum(),
                                   self.per_component.currentEnum(),
                                   self.power_num.value())
        if node is None:
            return
        lw = MeasurementListWidgetItem(node)
        lw.setToolTip(selected_item.toolTip())
        self._add_option(lw)

    def discard_option(self):
        selected_item: MeasurementListWidgetItem = self.profile_options_chosen.currentItem(
        )
        #  selected_row = self.profile_options_chosen.currentRow()
        lw = MeasurementListWidgetItem(deepcopy(selected_item.stat))
        lw.setToolTip(selected_item.toolTip())
        self.create_selection_chosen_changed()
        for i in range(self.profile_options.count()):
            if lw.text() == self.profile_options.item(i).text():
                return
        self.profile_options.addItem(lw)

    def edit_profile(self):
        item = self.profile_list.currentItem()
        if item is None:
            return
        profile = self.settings.measurement_profiles[str(item.text())]
        self.profile_options_chosen.clear()
        self.profile_name.setText(item.text())
        for ch in profile.chosen_fields:
            self.profile_options_chosen.addItem(
                MeasurementListWidgetItem(ch.calculation_tree))
        # self.gauss_img.setChecked(profile.use_gauss_image)
        self.save_butt.setEnabled(True)
        self.save_butt_with_name.setEnabled(True)

    def save_action(self):
        if self.profile_name.text() in self.settings.measurement_profiles:
            ret = QMessageBox.warning(
                self,
                "Profile exist",
                "Profile exist\nWould you like to overwrite it?",
                QMessageBox.No | QMessageBox.Yes,
            )
            if ret == QMessageBox.No:
                return
        selected_values = []
        for i in range(self.profile_options_chosen.count()):
            element: MeasurementListWidgetItem = self.profile_options_chosen.item(
                i)
            selected_values.append(
                MeasurementEntry(element.text(), element.stat))
        stat_prof = MeasurementProfile(self.profile_name.text(),
                                       selected_values)
        self.settings.measurement_profiles[stat_prof.name] = stat_prof
        self.settings.dump()
        self.export_profiles_butt.setEnabled(True)

    def named_save_action(self):
        if self.profile_name.text() in self.settings.measurement_profiles:
            ret = QMessageBox.warning(
                self,
                "Profile exist",
                "Profile exist\nWould you like to overwrite it?",
                QMessageBox.No | QMessageBox.Yes,
            )
            if ret == QMessageBox.No:
                return
        selected_values = []
        for i in range(self.profile_options_chosen.count()):
            txt = str(self.profile_options_chosen.item(i).text())
            selected_values.append((txt, str, txt))
        val_dialog = MultipleInput("Set fields name",
                                   list(selected_values),
                                   parent=self)
        if val_dialog.exec_():
            selected_values = []
            for i in range(self.profile_options_chosen.count()):
                element: MeasurementListWidgetItem = self.profile_options_chosen.item(
                    i)
                selected_values.append(
                    MeasurementEntry(val_dialog.result[element.text()],
                                     element.stat))
            stat_prof = MeasurementProfile(self.profile_name.text(),
                                           selected_values)
            self.settings.measurement_profiles[stat_prof.name] = stat_prof
            self.export_profiles_butt.setEnabled(True)

    def reset_action(self):
        self.profile_options.clear()
        self.profile_options_chosen.clear()
        self.profile_name.setText("")
        self.save_butt.setDisabled(True)
        self.save_butt_with_name.setDisabled(True)
        self.move_down.setDisabled(True)
        self.move_up.setDisabled(True)
        self.proportion_butt.setDisabled(True)
        self.choose_butt.setDisabled(True)
        self.discard_butt.setDisabled(True)
        for profile in MEASUREMENT_DICT.values():
            help_text = profile.get_description()
            lw = MeasurementListWidgetItem(profile.get_starting_leaf())
            lw.setToolTip(help_text)
            self.profile_options.addItem(lw)

    def soft_reset(self):
        # TODO rim should not be removed
        shift = 0
        for i in range(self.profile_options.count()):
            item = self.profile_options.item(i - shift)
            if str(item.text()) not in MEASUREMENT_DICT:
                self.profile_options.takeItem(i - shift)
                if item == self.chosen_element:
                    self.chosen_element = None
                del item
                shift += 1
        self.create_selection_changed()

    def export_measurement_profiles(self):
        exp = ExportDialog(self.settings.measurement_profiles,
                           StringViewer,
                           parent=self)
        if not exp.exec_():
            return
        dial = PSaveDialog(
            "Measurement profile (*.json)",
            settings=self.settings,
            path="io.export_directory",
            caption="Export settings profiles",
        )
        dial.selectFile("measurements_profile.json")

        if dial.exec_():
            file_path = str(dial.selectedFiles()[0])
            data = {
                x: self.settings.measurement_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_measurement_profiles(self):
        dial = PLoadDialog(
            "Measurement profile (*.json)",
            settings=self.settings,
            path="io.export_directory",
            caption="Import settings profiles",
            parent=self,
        )
        if dial.exec_():
            file_path = str(dial.selectedFiles()[0])
            stat, err = self.settings.load_part(file_path)
            if err:
                QMessageBox.warning(
                    self, "Import error",
                    "error during importing, part of data were filtered.")
            measurement_dict = self.settings.measurement_profiles
            imp = ImportDialog(stat, measurement_dict, StringViewer)
            if not imp.exec_():
                return
            for original_name, final_name in imp.get_import_list():
                measurement_dict[final_name] = stat[original_name]
            self.settings.dump()
コード例 #26
0
ファイル: main_window.py プロジェクト: 4DNucleome/PartSeg
class ImageInformation(QWidget):
    def __init__(self, settings: StackSettings, parent=None):
        """:type settings: ImageSettings"""
        super().__init__(parent)
        self._settings = settings
        self.path = QTextEdit("<b>Path:</b> example image")
        self.path.setWordWrapMode(QTextOption.WrapAnywhere)
        self.path.setReadOnly(True)
        self.setMinimumHeight(20)
        self.spacing = [QDoubleSpinBox() for _ in range(3)]
        self.multiple_files = QCheckBox("Show multiple files panel")
        self.multiple_files.setChecked(
            settings.get("multiple_files_widget", True))
        self.multiple_files.stateChanged.connect(self.set_multiple_files)
        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, 100000)
            # 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)

        self.add_files = AddFiles(settings, btn_layout=FlowLayout)

        spacing_layout = QFormLayout()
        spacing_layout.addRow("x:", self.spacing[0])
        spacing_layout.addRow("y:", self.spacing[1])
        spacing_layout.addRow("z:", self.spacing[2])
        spacing_layout.addRow("Units:", self.units)

        layout = QVBoxLayout()
        layout.addWidget(self.path)
        layout.addWidget(QLabel("Image spacing:"))
        layout.addLayout(spacing_layout)
        layout.addWidget(self.add_files)
        layout.addStretch(1)
        layout.addWidget(self.multiple_files)
        self.setLayout(layout)
        self._settings.image_changed[str].connect(self.set_image_path)

    @Slot(int)
    def set_multiple_files(self, val):
        self._settings.set("multiple_files_widget", val)

    def update_spacing(self, index=None):
        units_value = self.units.currentEnum()
        if index is not None:
            self._settings.set("units_value", units_value)
        for el, val in zip(self.spacing, self._settings.image_spacing[::-1]):
            el.blockSignals(True)
            el.setValue(val * UNIT_SCALE[units_value.value])
            el.blockSignals(False)
        if self._settings.is_image_2d():
            # self.spacing[2].setValue(0)
            self.spacing[2].setDisabled(True)
        else:
            self.spacing[2].setDisabled(False)

    def set_image_path(self, value):
        self.path.setText(f"<b>Path:</b> {value}")
        self.update_spacing()

    def image_spacing_change(self):
        self._settings.image_spacing = [
            el.value() / UNIT_SCALE[self.units.currentIndex()]
            for i, el in enumerate(self.spacing[::-1])
        ]

    def showEvent(self, _a0):
        units_value = self._settings.get("units_value", Units.nm)
        for el, val in zip(self.spacing, self._settings.image_spacing[::-1]):
            el.setValue(val * UNIT_SCALE[units_value.value])
        if self._settings.is_image_2d():
            self.spacing[2].setValue(0)
            self.spacing[2].setDisabled(True)
        else:
            self.spacing[2].setDisabled(False)
コード例 #27
0
class LogViewerDialog(DialogBase):
    def __init__(self):
        super(LogViewerDialog, self).__init__()

        # Widgets
        self.label = QLabel('Select log file:')
        self.combobox = QComboBox()
        self.text_edit = QTextEdit()

        # Widget setup
        self.text_edit.setFrameStyle(QFrame.Plain)
        self.setWindowTitle('Log Viewer')
        self.setMinimumWidth(600)
        self.setMinimumHeight(400)
        self.text_edit.setReadOnly(True)

        # Layouts
        top_layout = QHBoxLayout()
        top_layout.addWidget(self.label)
        top_layout.addWidget(self.combobox)
        layout = QVBoxLayout()
        layout.addLayout(top_layout)
        layout.addWidget(self.text_edit)
        self.setLayout(layout)

        # Signals
        self.combobox.currentIndexChanged.connect(self.update_text)

        # Setup()
        self.setup()
        self.update_style_sheet()

    def update_style_sheet(self, style_sheet=None):
        if style_sheet is None:
            style_sheet = load_style_sheet()
        self.setStyleSheet(style_sheet)

    def setup(self):
        """
        """
        self.combobox.clear()
        logfiles = [conda_manager_logfile]

        navigator_logfiles = [os.path.join(LOG_FOLDER, fname) for fname in
                              os.listdir(LOG_FOLDER)]

        for logf in sorted(navigator_logfiles) + logfiles:
            if os.path.isfile(logf):
                self.combobox.addItem(logf)

    def update_text(self, index):
        """
        """
        text = self.combobox.currentText()
        if os.path.isfile(text):
            with open(text, 'r') as f:
                data = f.read()
        self.text_edit.setText(data)

    def refresh(self):
        """
コード例 #28
0
class MessageBox(DialogBase):
    """Base message box dialog."""

    QUESTION_BOX = 100
    INFORMATION_BOX = 101
    ERROR_BOX = 102
    REMOVE_BOX = 103

    sig_url_clicked = Signal(object)

    def __init__(self, type_, error='', title='', text='', learn_more=None):
        """Base message box dialog."""
        super(MessageBox, self).__init__()
        from anaconda_navigator.utils.analytics import GATracker

        self.tracker = GATracker()
        self.label_text = QLabel(to_text_string(text))
        self.textbox_error = QTextEdit()
        self.button_ok = ButtonPrimary('Ok')
        self.button_yes = ButtonPrimary('Yes')
        self.button_no = ButtonNormal('No')
        self.button_copy = ButtonNormal('Copy text')
        self.button_learn = ButtonNormal('Learn more')
        self.button_remove = ButtonDanger('Remove')
        self.button_cancel = ButtonNormal('Cancel')
        self.button_send = ButtonNormal('Report Issue', parent=self)

        self.label_text.setOpenExternalLinks(False)
        self.label_text.setWordWrap(True)
        self.label_text.linkActivated.connect(self.url_clicked)
        self.textbox_error.setReadOnly(True)
        self.textbox_error.setFrameStyle(QTextEdit.Plain)
        self.textbox_error.setFrameShape(QTextEdit.NoFrame)
        self.setMinimumWidth(260)
        self.textbox_error.verticalScrollBar().show()
        self.setWindowTitle(to_text_string(title))

        error = to_text_string(error).split('\n')
        error = '<br>'.join(error)
        self.textbox_error.setText(error)

        # Layouts
        layout = QVBoxLayout()
        layout.addWidget(self.label_text)
        layout.addWidget(SpacerVertical())
        if error:
            layout.addWidget(self.textbox_error)
            layout.addWidget(SpacerVertical())
            layout.addWidget(self.button_copy)
            layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()

        layout.addLayout(layout_buttons)

        self.layout = layout
        self.setLayout(layout)

        # Signals
        self.button_copy.clicked.connect(self.copy_text)
        self.button_ok.clicked.connect(self.accept)
        self.button_yes.clicked.connect(self.accept)
        self.button_no.clicked.connect(self.reject)
        self.button_remove.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_send.clicked.connect(self.send)

        # Setup
        self.button_learn.setVisible(bool(learn_more))
        if bool(learn_more):
            layout_buttons.addWidget(self.button_learn)
            layout_buttons.addWidget(SpacerHorizontal())
            self.button_learn.clicked.connect(
                lambda: self.show_url(learn_more)
            )

        if type_ == self.ERROR_BOX:
            layout_buttons.addWidget(self.button_send)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_ok)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.INFORMATION_BOX:
            layout_buttons.addWidget(self.button_ok)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
            self.textbox_error.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.QUESTION_BOX:
            layout_buttons.addStretch()
            layout_buttons.addWidget(self.button_no)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_yes)
            layout_buttons.addWidget(SpacerHorizontal())
            self.textbox_error.setVisible(False)
            self.button_ok.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.REMOVE_BOX:
            layout_buttons.addStretch()
            layout_buttons.addWidget(self.button_cancel)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_remove)
            layout_buttons.addWidget(SpacerHorizontal())
            self.textbox_error.setVisible(False)
            self.button_ok.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)

        self.button_send.setVisible(False)
        self.layout_buttons = layout_buttons

    def url_clicked(self, url):
        """Emit url interaction."""
        self.sig_url_clicked.emit(url)

    def copy_text(self):
        """Copy all the content of the displayed error message."""
        self.textbox_error.selectAll()
        self.textbox_error.copy()

    def show_url(self, url=None):
        """Open url in default browser."""
        if url:
            qurl = QUrl(url)
            QDesktopServices.openUrl(qurl)
            self.tracker.track_event('help', 'documentation', url)

    def send(self):
        """Send error report to github and create an issue with a template."""
        import webbrowser
        from anaconda_navigator.utils.analytics import GATracker
        base = "https://github.com/ContinuumIO/anaconda-issues/issues/new?{0}"
        template = '''
## Main error
{text}
## Traceback
```
{trace}
```
## System information
```
{info}
```
'''
        info = GATracker().info
        info = '\n'.join('{}: {}'.format(k, v) for k, v in info.items())
        query = parse.urlencode(
            {
                'title': "Navigator Error",
                'labels': "tag:navigator",
                'body': template.format(
                    text=self.text, trace=self.error, info=info
                )
            }
        )
        url = base.format(query)
        webbrowser.open_new_tab(url)
コード例 #29
0
class MainWindow(QMainWindow):
    def selectFileToOpen(self):
        def getPreProcessingChoice(self, filename, filestructure):
            items = ("Choose the longest", "Merge all")
            item, okPressed = QInputDialog.getItem(
                self, "Multiple tracks/segments", "File '" + filename +
                "' contains more than one track/segment\n\n" + infos +
                "\nWhat to do?", items, 0, False)
            if okPressed and item:
                return items.index(item)
            else:
                return 0

        # Try to recover the last used directory
        old_directory = self.settings.value("lastdirectory", str)

        # Check if the setting exists
        if old_directory is not None:
            # Check if it's not empty
            if old_directory:
                old_directory = old_directory
            else:
                old_directory = bombo.TRACKS_FOLDER
        else:
            old_directory = bombo.TRACKS_FOLDER

        # Open the dialog box
        fullfilename_list = QFileDialog.getOpenFileNames(
            self, 'Open .gpx', old_directory, "GPX files (*.gpx)")
        if os.environ['QT_API'] == 'pyqt':
            pass
        elif os.environ['QT_API'] == 'pyqt5':
            fullfilename_list = fullfilename_list[0]

        # Process every selected file
        for i, fullfilename in enumerate(fullfilename_list):
            # Process filename
            directory, filename = os.path.split(str(fullfilename))
            filename, fileextension = os.path.splitext(filename)

            # Save the new directory in the application settings (it only
            # needs to be done once)
            if i == 0:
                # print "New directory to be saved: {}\n".format(directory)
                if os.environ['QT_API'] == 'pyqt':
                    self.settings.setValue("lastdirectory", str(directory))
                elif os.environ['QT_API'] == 'pyqt5':
                    self.settings.setValue("lastdirectory",
                                           QtCore.QVariant(str(directory)))

            # Open file and inspect what's inside
            gpxraw, longest_traseg, Ntracks, Nsegments, infos = bombo.LoadGPX(
                fullfilename)

            # If there's more than one track or segment, ask how to proceed
            if (Ntracks > 1) or (Nsegments > 1):
                preprocessingchoice = getPreProcessingChoice(
                    self, filename, infos)
                if preprocessingchoice == 0:
                    preprocessedgpx = bombo.SelectOneTrackAndSegmentFromGPX(
                        gpxraw, longest_traseg[0], longest_traseg[1])
                    listname = filename + " (longest)"
                elif preprocessingchoice == 1:
                    preprocessedgpx = bombo.MergeAllTracksAndSegmentsFromGPX(
                        gpxraw)
                    listname = filename + " (merged)"
            else:
                preprocessedgpx = gpxraw
                listname = filename

            # Append the list of open GPX files using the next available color (that's the size of the list -1)
            self.gpxlist.append(preprocessedgpx)
            self.gpxnamelist.append(listname)
            newitem = QListWidgetItem(listname)
            newitem.setBackground(
                QtGui.QColor(self.palette[len(self.gpxlist) - 1]))
            self.tracklist.addItem(newitem)

        return

    def Go(self):
        if len(self.gpxselectedlist) > 0:
            # Temporarily change cursor
            QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)

            # Clear up global variables
            self.proc_coords = []
            self.proc_measurements = []
            self.proc_state_means = []
            self.proc_state_vars = []
            self.proc_new_coords = []
            self.proc_new_gpx = []
            self.proc_coords_to_plot = []
            self.proc_coords_to_plot2 = []
            self.proc_balloondata = []

            # For every GPX file that is selected
            self.textWarningConsole.clear()
            for i, currentgpx in enumerate(self.gpxselectedlist):
                # Parse the GPX file
                gpx, coords, dinfos_before, warnings = bombo.ParseGPX(
                    currentgpx,
                    track_nr=0,
                    segment_nr=0,
                    use_srtm_elevation=bool(self.checkUseSRTM.isChecked()))
                self.textWarningConsole.append(warnings)

                # Kalman processing
                coords, measurements, state_means, state_vars, dinfos_during = bombo.ApplyKalmanFilter(
                    coords,
                    gpx,
                    method=self.comboBoxProcessingMethod.currentIndex(),
                    use_acceleration=self.checkUseAcceleration.isChecked(),
                    extra_smooth=self.checkExtraSmooth.isChecked(),
                    debug_plot=False)

                # Save data in GPX structure to compute speed and elevations
                new_coords, new_gpx, dinfos_after = bombo.SaveDataToCoordsAndGPX(
                    coords, state_means)

                # Update GUI with the computed stats
                parent = QtGui.QStandardItem(self.gpxselectednamelist[i])

                parent_beforeprocessing = QtGui.QStandardItem("Raw GPX stats")
                parent_beforeprocessing.appendRow([
                    QtGui.QStandardItem("Total distance"),
                    QtGui.QStandardItem(dinfos_before['total_distance'])
                ])
                parent_beforeprocessing_moving = QtGui.QStandardItem("Moving")
                parent_beforeprocessing_moving.appendRow([
                    QtGui.QStandardItem("Time"),
                    QtGui.QStandardItem(dinfos_before['moving_time'])
                ])
                parent_beforeprocessing_moving.appendRow([
                    QtGui.QStandardItem("Distance"),
                    QtGui.QStandardItem(dinfos_before['moving_distance'])
                ])
                parent_beforeprocessing.appendRow(
                    parent_beforeprocessing_moving)
                parent_beforeprocessing_idle = QtGui.QStandardItem("Idle")
                parent_beforeprocessing_idle.appendRow([
                    QtGui.QStandardItem("Time"),
                    QtGui.QStandardItem(dinfos_before['idle_time'])
                ])
                parent_beforeprocessing_idle.appendRow([
                    QtGui.QStandardItem("Distance"),
                    QtGui.QStandardItem(dinfos_before['idle_distance'])
                ])
                parent_beforeprocessing.appendRow(parent_beforeprocessing_idle)
                parent_beforeprocessing.appendRow([
                    QtGui.QStandardItem("Elevation"),
                    QtGui.QStandardItem(dinfos_before['elevation'])
                ])
                parent_beforeprocessing.appendRow([
                    QtGui.QStandardItem("Climb"),
                    QtGui.QStandardItem(dinfos_before['climb'])
                ])
                parent.appendRow(parent_beforeprocessing)

                parent.appendRow([
                    QtGui.QStandardItem("Samples"),
                    QtGui.QStandardItem(dinfos_during['nsamples'])
                ])
                parent.appendRow([
                    QtGui.QStandardItem("Total distance"),
                    QtGui.QStandardItem(dinfos_after['total_distance'])
                ])
                parent_moving = QtGui.QStandardItem("Moving")
                parent_moving.appendRow([
                    QtGui.QStandardItem("Time"),
                    QtGui.QStandardItem(dinfos_after['moving_time'])
                ])
                parent_moving.appendRow([
                    QtGui.QStandardItem("Distance"),
                    QtGui.QStandardItem(dinfos_after['moving_distance'])
                ])
                parent.appendRow(parent_moving)
                parent_idle = QtGui.QStandardItem("Idle")
                parent_idle.appendRow([
                    QtGui.QStandardItem("Time"),
                    QtGui.QStandardItem(dinfos_after['idle_time'])
                ])
                parent_idle.appendRow([
                    QtGui.QStandardItem("Distance"),
                    QtGui.QStandardItem(dinfos_after['idle_distance'])
                ])
                parent.appendRow(parent_idle)
                parent.appendRow([
                    QtGui.QStandardItem("Elevation"),
                    QtGui.QStandardItem(dinfos_after['elevation'])
                ])
                parent.appendRow([
                    QtGui.QStandardItem("Climb"),
                    QtGui.QStandardItem(dinfos_after['climb'])
                ])
                self.treemodel.appendRow(parent)

                # Create balloondata for the html plot
                balloondata = {
                    'distance':
                    np.cumsum(
                        bombo.HaversineDistance(np.asarray(new_coords['lat']),
                                                np.asarray(
                                                    new_coords['lon']))),
                    'elevation':
                    np.asarray(new_coords['ele']),
                    'speed':
                    None
                }

                # Create extra data for the html plot (fully implemented in bombo, not here)
                """
                data = np.ones((len(lat_cleaned),2))
                data[:,0] = h_filtered / np.max(h_filtered) * 0.0004
                data[:,1] = np.hstack((np.asarray([0]), speed_h)) / np.max(np.hstack((np.asarray([0]), speed_h))) * 0.0004
                tangentdata = {'data': data,
                               'sides': (0, 1),
                               'palette': ('blue','red')}
                """

                # Save relevant output in global variables
                self.proc_coords.append(coords)
                self.proc_measurements.append(measurements)
                self.proc_state_means.append(state_means)
                self.proc_state_vars.append(state_vars)
                self.proc_new_coords.append(new_coords)
                self.proc_new_gpx.append(new_gpx)
                self.proc_coords_to_plot.append(
                    np.vstack((new_coords['lat'], new_coords['lon'])).T)
                self.proc_coords_to_plot2.append(
                    np.vstack((coords['lat'], coords['lon'])).T)
                self.proc_balloondata.append(balloondata)

            # Restore original cursor
            QApplication.restoreOverrideCursor()

            # Generate embedded plots
            if len(self.gpxselectedlist) == 1:
                self.plotEmbeddedElevationAndSpeed.update_figure(
                    measurements, state_means, new_gpx.tracks[0].segments[0])
                self.plotEmbeddedDetails.update_figure(
                    measurements, state_means, state_vars,
                    new_gpx.tracks[0].segments[0])
            else:
                # Commentato per adesso
                # self.plotEmbeddedElevationAndSpeed.update_figure_multiple_tracks(self.proc_measurements, self.proc_state_means, self.proc_new_gpx)
                self.plotEmbeddedElevationAndSpeed.clear_figure()
                self.plotEmbeddedDetails.clear_figure()

            # Generate html plot, if only one track is selected, proceed with the complete output, otherwise just plot the traces
            if len(self.gpxselectedlist) is 1:
                bombo.PlotOnMap(
                    coords_array_list=self.proc_coords_to_plot,
                    coords_array2_list=self.proc_coords_to_plot2,
                    coords_palette=self.selectedpalette,
                    tangentdata=None,
                    balloondata_list=self.proc_balloondata,
                    rdp_reduction=self.checkUseRDP.isChecked(),
                    showmap=bool(self.check2DMapInExternalBrowser.isChecked()))
            else:
                bombo.PlotOnMap(
                    coords_array_list=self.proc_coords_to_plot,
                    coords_array2_list=None,
                    coords_palette=self.selectedpalette,
                    tangentdata=None,
                    balloondata_list=self.proc_balloondata,
                    rdp_reduction=self.checkUseRDP.isChecked(),
                    showmap=bool(self.check2DMapInExternalBrowser.isChecked()))

            self.map2d.load(QtCore.QUrl(bombo.MAP_2D_FILENAME))
            self.map2d.show()

            # Generate 3D plot, only with one track for the moment
            if len(self.gpxselectedlist) == 1:
                if self.check3DMapSelection.isChecked():
                    tile_selection = 'auto'
                else:
                    tile_selection = self.text3DMapName.text()
                terrain, track, warnings = bombo.Generate3DMap(
                    new_coords['lat'],
                    new_coords['lon'],
                    tile_selection=tile_selection,
                    margin=self.spinbox3DMargin.value(),
                    elevation_scale=self.spinbox3DElevationScale.value(),
                    mapping='coords',
                    use_osm_texture=True,
                    texture_type='osm',
                    texture_zoom=self.spinbox3DOSMZoom.value(),
                    texture_invert=self.check3DOSMInvert.isChecked(),
                    use_proxy=self.use_proxy,
                    proxy_data=self.proxy_config,
                    verbose=False)

                self.textWarningConsole.append(warnings)

                if terrain is not None:
                    self.map3d.update_plot(terrain, track)

        else:
            self.textWarningConsole.setText(
                "You need to open a .gpx file before!")
        return

    def PlotSpecificAreaDialog(self):
        def PlotSpecificArea():
            # Save coordinates for the next time
            if os.environ['QT_API'] == 'pyqt':
                self.settings.setValue("last_point_coord_lat",
                                       self.spinboxLatDec.value())
                self.settings.setValue("last_point_coord_lon",
                                       self.spinboxLonDec.value())
            elif os.environ['QT_API'] == 'pyqt5':
                self.settings.setValue(
                    "last_point_coord_lat",
                    QtCore.QVariant(self.spinboxLatDec.value()))
                self.settings.setValue(
                    "last_point_coord_lon",
                    QtCore.QVariant(self.spinboxLonDec.value()))

            # Select the 3D Map tab
            self.tab.setCurrentIndex(2)

            # Plot
            if self.check3DMapSelection.isChecked():
                tile_selection = 'auto'
            else:
                tile_selection = self.text3DMapName.text()

            terrain, track, warnings = bombo.Generate3DMap(
                [self.spinboxLatDec.value()], [self.spinboxLonDec.value()],
                tile_selection=tile_selection,
                margin=self.spinbox3DMargin.value(),
                elevation_scale=self.spinbox3DElevationScale.value(),
                mapping='coords',
                use_osm_texture=True,
                texture_type='osm',
                texture_zoom=self.spinbox3DOSMZoom.value(),
                texture_invert=self.check3DOSMInvert.isChecked(),
                use_proxy=self.use_proxy,
                proxy_data=self.proxy_config,
                verbose=False)

            self.textWarningConsole.append(warnings)

            if terrain is not None:
                self.map3d.update_plot(terrain, track)
            d.done(0)

        def Convert():
            try:
                dd = bombo.parse_dms(self.textLatLonGMS.text())
                self.spinboxLatDec.setValue(dd[0])
                self.spinboxLonDec.setValue(dd[1])
            except:
                pass

        d = QDialog()
        grid = QGridLayout()

        hBox_coordsGMS = QHBoxLayout()
        hBox_coordsGMS.setSpacing(5)
        label = QLabel('Coordinates (gms)')
        grid.addWidget(label, 0, 0)
        self.textLatLonGMS = QLineEdit()
        self.textLatLonGMS.setText("")
        grid.addWidget(self.textLatLonGMS, 0, 1, 1, 2)

        button1 = QPushButton("Convert to decimal")
        button1.clicked.connect(Convert)
        grid.addWidget(button1, 0, 3)

        label = QLabel('Coordinates (decimal)')
        grid.addWidget(label, 1, 0)
        self.spinboxLatDec = QDoubleSpinBox()
        self.spinboxLatDec.setRange(-90, +90)
        self.spinboxLatDec.setSingleStep(0.0000001)
        self.spinboxLatDec.setDecimals(7)
        grid.addWidget(self.spinboxLatDec, 1, 1)
        self.spinboxLonDec = QDoubleSpinBox()
        self.spinboxLonDec.setRange(-180, +180)
        self.spinboxLonDec.setSingleStep(0.0000001)
        self.spinboxLonDec.setDecimals(7)
        grid.addWidget(self.spinboxLonDec, 1, 2)

        # Try to recover the last used points
        try:
            old_lat = self.settings.value("last_point_coord_lat", type=float)
            old_lon = self.settings.value("last_point_coord_lon", type=float)
            self.spinboxLatDec.setValue(old_lat)
            self.spinboxLonDec.setValue(old_lon)
        except:
            # Coordinates of Mt. Rinjani in Indonesia
            self.spinboxLatDec.setValue(-8.4166000)
            self.spinboxLonDec.setValue(116.4666000)

        button2 = QPushButton("Show 3D map")
        button2.clicked.connect(PlotSpecificArea)
        grid.addWidget(button2, 1, 3)

        d.setWindowTitle("Show point on 3D map")
        d.setLayout(grid)
        d.setWindowModality(QtCore.Qt.ApplicationModal)
        d.exec_()

    def ProxyDialog(self):
        def SetProxy():
            self.use_proxy = bool(self.checkUseProxy.isChecked())
            self.proxy_config = self.textProxyConfig.text()

            if os.environ['QT_API'] == 'pyqt':
                self.settings.setValue("use_proxy", self.use_proxy)
                self.settings.setValue("proxy_config", str(self.proxy_config))
            elif os.environ['QT_API'] == 'pyqt5':
                self.settings.setValue("use_proxy",
                                       QtCore.QVariant(self.use_proxy))
                self.settings.setValue("proxy_config",
                                       QtCore.QVariant(str(self.proxy_config)))

            d.done(0)

        d = QDialog()

        box = QVBoxLayout()

        hBox_proxy = QHBoxLayout()
        hBox_proxy.setSpacing(5)
        label = QLabel('Proxy')
        hBox_proxy.addWidget(label)
        self.textProxyConfig = QLineEdit()
        try:
            self.textProxyConfig.setText(
                self.settings.value('proxy_config', str))
        except:
            self.textProxyConfig.setText(bombo.PROXY_DATA)
        self.textProxyConfig.setMinimumWidth(200)
        hBox_proxy.addWidget(self.textProxyConfig)
        box.addLayout(hBox_proxy)

        self.checkUseProxy = QCheckBox("Use proxy")
        try:
            self.checkUseProxy.setChecked(
                self.settings.value('use_proxy', bool))
        except:
            self.checkUseProxy.setChecked(bool(bombo.USE_PROXY))
        box.addWidget(self.checkUseProxy)

        button = QPushButton("Save configuration")
        button.clicked.connect(SetProxy)
        box.addWidget(button)

        d.setWindowTitle("Proxy configuration")
        d.setLayout(box)
        d.setWindowModality(QtCore.Qt.ApplicationModal)
        d.exec_()

    def __init__(self, parent=None):
        super(MainWindow, self).__init__()
        self.initVariables()
        self.initUI()

    def initVariables(self):
        self.gpxlist = list()
        self.gpxnamelist = list()
        self.gpxselectedlist = list()
        self.gpxselectednamelist = list()
        self.palette = bombo.GeneratePalette(N=10) * 5  # replicated 5 times
        #self.palette = ["#0000FF", "#00FF00", "#00FFFF", "#FF0000", "#FF00FF", "#FFFF00", "#FFFFFF"] # test palette
        self.selectedpalette = list()

        self.proc_coords = list()
        self.proc_measurements = list()
        self.proc_state_means = list()
        self.proc_state_vars = list()
        self.proc_new_coords = list()
        self.proc_new_gpx = list()
        self.proc_coords_to_plot = list()
        self.proc_coords_to_plot2 = list()
        self.proc_balloondata = list()

    def initUI(self):
        def selection_changed():
            # Retrieve selected items
            # selecteditems = self.tracklist.selectedItems()
            selectedindexes = self.tracklist.selectedIndexes()

            # Adding the selected items to the processing list
            self.gpxselectedlist[:] = []
            self.gpxselectednamelist[:] = []
            self.selectedpalette[:] = []
            for i in selectedindexes:
                # print str(i.text())
                self.gpxselectedlist.append(self.gpxlist[i.row()])
                self.gpxselectednamelist.append(self.gpxnamelist[i.row()])
                self.selectedpalette.append(self.palette[i.row()])

        def ClearStats():
            """
            # Some other code that could be used in the future
            index = self.treemodel.indexFromItem(parent1)
            self.tree.expand(index)
            selmod = self.tree.selectionModel()
            index2 = self.treemodel.indexFromItem(child2)
            selmod.select(index2, QtCore.QItemSelectionModel.Select|QtCore.QItemSelectionModel.Rows)
            
            root = self.treemodel.invisibleRootItem()
            (item.parent() or root).removeChild(item)
            """
            # Returns a list of indexes. In our case, for each row there are 2 indexes, cos there are 2 columns.
            for index in self.tree.selectedIndexes():
                # Consider only the first columns
                if index.column() == 0:
                    # Need to check if it's a top item (i.e. track), otherwise if a subitem (i.e. distance or time) is selected, the result might be buggy
                    parent = index.parent()
                    parent_item = self.treemodel.itemFromIndex(parent)
                    if parent_item is None:
                        self.treemodel.removeRow(index.row())

        # Application Settings
        QtCore.QCoreApplication.setOrganizationName("Ste")
        QtCore.QCoreApplication.setOrganizationDomain(
            "https://github.com/stesalati/sport/")
        QtCore.QCoreApplication.setApplicationName("TrackAnalyser")

        # Config settings
        self.settings = QtCore.QSettings(self)

        # Proxy settings
        try:
            self.use_proxy = self.settings.value('use_proxy', bool)
            self.proxy_config = self.settings.value('proxy_config', str)
        except:
            self.use_proxy = bombo.USE_PROXY
            self.proxy_config = bombo.PROXY_DATA

        # Actions
        openfile = QAction(QtGui.QIcon("icons/openfile.png"), "Open .gpx",
                           self)
        openfile.setShortcut("Ctrl+O")
        openfile.setStatusTip("Open file")
        openfile.triggered.connect(self.selectFileToOpen)

        go = QAction(QtGui.QIcon("icons/go.png"), "Go!", self)
        go.setShortcut("Ctrl+R")
        go.setStatusTip("Run analysis")
        go.triggered.connect(self.Go)

        clearstats = QAction(QtGui.QIcon("icons/clear.png"), "Clear stats",
                             self)
        clearstats.setShortcut("Ctrl+C")
        clearstats.setStatusTip("Clear stats")
        clearstats.triggered.connect(ClearStats)

        sep1 = QAction(self)
        sep1.setSeparator(True)

        showpoint = QAction(QtGui.QIcon("icons/point.png"), "Show point", self)
        showpoint.setShortcut("Ctrl+P")
        showpoint.setStatusTip("Show point")
        showpoint.triggered.connect(self.PlotSpecificAreaDialog)

        sep2 = QAction(self)
        sep2.setSeparator(True)

        quitapp = QAction(QtGui.QIcon("icons/quit.png"), "Quit", self)
        quitapp.setShortcut("Ctrl+Q")
        quitapp.setStatusTip("Quit application")
        quitapp.triggered.connect(qApp.quit)

        configs = QAction(QtGui.QIcon("icons/configs.png"), "Configs", self)
        configs.setStatusTip("Configs")
        configs.triggered.connect(self.ProxyDialog)

        # Menubar
        mainMenu = self.menuBar()
        configMenu = mainMenu.addMenu('&Config')
        configMenu.addAction(configs)

        # Toolbar
        toolbar = self.addToolBar('My tools')
        toolbar.addAction(openfile)
        toolbar.addAction(go)
        toolbar.addAction(clearstats)
        toolbar.addAction(sep1)
        toolbar.addAction(showpoint)
        toolbar.addAction(sep2)
        toolbar.addAction(quitapp)
        toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
        toolbar.setIconSize(QtCore.QSize(30, 30))

        # Status bar
        self.statusBar().show()

        # Main widget (everything that's not toolbar, statusbar or menubar must be in this widget)
        self.scatola = QWidget()

        # Main horizontal impagination
        hBox = QHBoxLayout()
        hBox.setSpacing(5)

        # Vertical left column
        vBox_left = QVBoxLayout()
        vBox_left.setSpacing(5)

        # 1st vertical box, a list
        self.tracklist = QListWidget()
        vBox_left.addWidget(self.tracklist)
        self.tracklist.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.tracklist.itemSelectionChanged.connect(selection_changed)
        self.tracklist.setMaximumHeight(120)

        # 2nd vertical box, containing several horizontal boxes, one for each setting
        vBox2 = QVBoxLayout()
        vBox2.setSpacing(5)

        # Just the group label
        labelSettings = QLabel('Settings')
        vBox2.addWidget(labelSettings)

        # Use/don't use corrected altitude
        self.checkUseSRTM = QCheckBox(
            "Use SRTM corrected elevation (needs Internet)")
        self.checkUseSRTM.setChecked(False)
        vBox2.addWidget(self.checkUseSRTM)

        # Choose processing method + use/don't use acceleration
        hBoxProcessingMethod = QHBoxLayout()
        labelProcessingMethod = QLabel('Processing method')
        hBoxProcessingMethod.addWidget(labelProcessingMethod)
        self.comboBoxProcessingMethod = QComboBox()
        self.comboBoxProcessingMethod.addItem("Just use available data")
        self.comboBoxProcessingMethod.addItem(
            "Fill all gaps at T=1s (resample)")
        self.comboBoxProcessingMethod.addItem("Fill only smaller gaps at T=1s")
        hBoxProcessingMethod.addWidget(self.comboBoxProcessingMethod)
        self.checkUseAcceleration = QCheckBox("Use acceleration")
        self.checkUseAcceleration.setChecked(False)
        hBoxProcessingMethod.addWidget(self.checkUseAcceleration)
        vBox2.addLayout(hBoxProcessingMethod)

        # Use/don't use variance smooth
        self.checkExtraSmooth = QCheckBox("Extra smooth")
        self.checkExtraSmooth.setChecked(False)
        vBox2.addWidget(self.checkExtraSmooth)

        # 2D interactive map settings
        hBox2DMap = QHBoxLayout()
        self.checkUseRDP = QCheckBox("Use RDP to reduce points")
        self.checkUseRDP.setChecked(False)
        hBox2DMap.addWidget(self.checkUseRDP)
        self.check2DMapInExternalBrowser = QCheckBox(
            "Show in external browser")
        self.check2DMapInExternalBrowser.setChecked(False)
        hBox2DMap.addWidget(self.check2DMapInExternalBrowser)
        vBox2.addLayout(hBox2DMap)

        # Settings for the 3D map
        line3DViewSettings = QFrame()
        #line3DViewSettings.setGeometry(QtCore.QRect(320, 150, 118, 3))
        line3DViewSettings.setFrameShape(QFrame.HLine)
        line3DViewSettings.setFrameShadow(QFrame.Sunken)
        vBox2.addWidget(line3DViewSettings)

        label3DViewSettings = QLabel('3D view settings')
        vBox2.addWidget(label3DViewSettings)

        hBox3DMapSelection = QHBoxLayout()
        self.check3DMapSelection = QCheckBox(
            "Select elevation tiles automatically, otherwise")
        self.check3DMapSelection.setChecked(True)
        hBox3DMapSelection.addWidget(self.check3DMapSelection)
        self.text3DMapName = QLineEdit()
        self.text3DMapName.setText("Iceland.tif")
        hBox3DMapSelection.addWidget(self.text3DMapName)
        vBox2.addLayout(hBox3DMapSelection)

        hBox3D = QHBoxLayout()
        label3DMargin = QLabel('Margin')
        hBox3D.addWidget(label3DMargin)
        self.spinbox3DMargin = QSpinBox()
        self.spinbox3DMargin.setRange(50, 1000)
        self.spinbox3DMargin.setValue(100)
        self.spinbox3DMargin.setSingleStep(10)
        hBox3D.addWidget(self.spinbox3DMargin)

        labelSpace = QLabel('  ')
        hBox3D.addWidget(labelSpace)

        label3DElevationScale = QLabel('Elev. scale')
        hBox3D.addWidget(label3DElevationScale)
        self.spinbox3DElevationScale = QDoubleSpinBox()
        self.spinbox3DElevationScale.setRange(1, 50)
        self.spinbox3DElevationScale.setSingleStep(0.1)
        hBox3D.addWidget(self.spinbox3DElevationScale)

        hBox3D.addWidget(labelSpace)

        label3DOSMZoom = QLabel('Zoom')
        hBox3D.addWidget(label3DOSMZoom)
        self.spinbox3DOSMZoom = QSpinBox()
        self.spinbox3DOSMZoom.setRange(8, 15)
        self.spinbox3DOSMZoom.setValue(13)
        self.spinbox3DOSMZoom.setSingleStep(1)
        hBox3D.addWidget(self.spinbox3DOSMZoom)

        hBox3D.addWidget(labelSpace)

        self.check3DOSMInvert = QCheckBox("Invert")
        self.check3DOSMInvert.setChecked(False)
        hBox3D.addWidget(self.check3DOSMInvert)
        vBox2.addLayout(hBox3D)

        vBox_left.addLayout(vBox2)

        # 3rd stats tree
        lineTree = QFrame()
        lineTree.setFrameShape(QFrame.HLine)
        lineTree.setFrameShadow(QFrame.Sunken)
        vBox2.addWidget(lineTree)
        labelTree = QLabel('Track stats')
        vBox2.addWidget(labelTree)

        self.tree = QTreeView()
        self.tree.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.treemodel = QtGui.QStandardItemModel()
        self.treemodel.setHorizontalHeaderLabels(['Name', 'Value'])
        self.tree.setModel(self.treemodel)
        self.tree.setUniformRowHeights(True)
        self.tree.setColumnWidth(0, 200)
        vBox_left.addWidget(self.tree)

        # 4th text, containing text messages/errors
        self.textWarningConsole = QTextEdit()
        self.textWarningConsole.setReadOnly(True)
        self.textWarningConsole.setFont(QtGui.QFont("Courier New", FONTSIZE))
        self.textWarningConsole.clear()
        self.textWarningConsole.setMaximumHeight(50)
        vBox_left.addWidget(self.textWarningConsole)

        # I put "vBox_left" inside a widget and then the widget inside "hBox"
        # instead of just doing "hBox.addLayout(vBox_left) so I can set its
        # maximum width.
        vBox_left_widget = QWidget()
        vBox_left_widget.setLayout(vBox_left)
        vBox_left_widget.setMinimumWidth(400)
        vBox_left_widget.setMaximumWidth(500)
        hBox.addWidget(vBox_left_widget)

        # Vertical right column
        self.tab = QTabWidget()

        # Tab 1: Summary: elevation and speed
        tab1 = QWidget()
        # The tab layout
        vBox_tab = QVBoxLayout()
        vBox_tab.setSpacing(5)
        # Plot area
        self.plotEmbeddedElevationAndSpeed = EmbeddedPlot_ElevationSpeed(
            width=5, height=4, dpi=100)
        self.plotEmbeddedElevationAndSpeed.setMinimumWidth(800)
        # Add toolbar to the plot
        self.mpl_toolbar1 = NavigationToolbar(
            self.plotEmbeddedElevationAndSpeed, self.scatola)
        # Add widgets to the layout
        vBox_tab.addWidget(self.plotEmbeddedElevationAndSpeed)
        vBox_tab.addWidget(self.mpl_toolbar1)
        # Associate the layout to the tab
        tab1.setLayout(vBox_tab)

        # Tab 2: html 2D map
        tab2 = QWidget()
        # The tab layout
        vBox_tab = QVBoxLayout()
        vBox_tab.setSpacing(5)
        # Area
        self.map2d = QtWebEngineWidgets.QWebEngineView()
        # Add widgets to the layout
        vBox_tab.addWidget(self.map2d)
        # Associate the layout to the tab
        tab2.setLayout(vBox_tab)

        # Tab 3: 3D plot
        tab3 = QWidget()
        # The tab layout
        vBox_tab = QVBoxLayout()
        vBox_tab.setSpacing(5)
        # Area
        self.map3d = MayaviQWidget()
        # Add widgets to the layout
        vBox_tab.addWidget(self.map3d)
        # Associate the layout to the tab
        tab3.setLayout(vBox_tab)

        # Tab 4: Details
        tab4 = QWidget()
        # The tab layout
        vBox_tab = QVBoxLayout()
        vBox_tab.setSpacing(5)
        # Plot area
        self.plotEmbeddedDetails = EmbeddedPlot_Details(width=5,
                                                        height=4,
                                                        dpi=100)
        self.plotEmbeddedDetails.setMinimumWidth(800)
        # Add toolbar to the plot
        self.mpl_toolbar2 = NavigationToolbar(self.plotEmbeddedDetails,
                                              self.scatola)
        # Add widgets to the layout
        vBox_tab.addWidget(self.plotEmbeddedDetails)
        vBox_tab.addWidget(self.mpl_toolbar2)
        # Associate the layout to the tab
        tab4.setLayout(vBox_tab)

        # Associate tabs
        self.tab.addTab(tab1, "Summary")
        self.tab.addTab(tab2, "2D Map")
        self.tab.addTab(tab3, "3D Map")
        self.tab.addTab(tab4, "Details")

        hBox.addWidget(self.tab)

        # Setting hBox as main box
        self.scatola.setLayout(hBox)
        self.setCentralWidget(self.scatola)

        # Application settings
        self.setWindowTitle('TrackAnalyser')
        self.setWindowIcon((QtGui.QIcon('icons/app.png')))
        self.setGeometry(100, 100, 1200, 700)
        self.show()
コード例 #30
0
ファイル: texteditor.py プロジェクト: burrbull/spyder
class TextEditor(QDialog):
    """Array Editor Dialog"""
    def __init__(self, text, title='', font=None, parent=None,
                 readonly=False, size=(400, 300)):
        QDialog.__init__(self, parent)
        
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)
        
        self.text = None
        self.btn_save_and_close = None
        
        # Display text as unicode if it comes as bytes, so users see 
        # its right representation
        if is_binary_string(text):
            self.is_binary = True
            text = to_text_string(text, 'utf8')
        else:
            self.is_binary = False
        
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        # Text edit
        self.edit = QTextEdit(parent)
        self.edit.setReadOnly(readonly)
        self.edit.textChanged.connect(self.text_changed)
        self.edit.setPlainText(text)
        if font is None:
            font = get_font()
        self.edit.setFont(font)
        self.layout.addWidget(self.edit)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        btn_layout.addStretch()
        if not readonly:
            self.btn_save_and_close = QPushButton(_('Save and Close'))
            self.btn_save_and_close.setDisabled(True)
            self.btn_save_and_close.clicked.connect(self.accept)
            btn_layout.addWidget(self.btn_save_and_close)

        self.btn_close = QPushButton(_('Close'))
        self.btn_close.setAutoDefault(True)
        self.btn_close.setDefault(True)
        self.btn_close.clicked.connect(self.reject)
        btn_layout.addWidget(self.btn_close)

        self.layout.addLayout(btn_layout)

        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)
        
        self.setWindowIcon(ima.icon('edit'))
        self.setWindowTitle(_("Text editor") + \
                            "%s" % (" - "+str(title) if str(title) else ""))
        self.resize(size[0], size[1])

    @Slot()
    def text_changed(self):
        """Text has changed"""
        # Save text as bytes, if it was initially bytes
        if self.is_binary:
            self.text = to_binary_string(self.edit.toPlainText(), 'utf8')
        else:
            self.text = to_text_string(self.edit.toPlainText())
        if self.btn_save_and_close:
            self.btn_save_and_close.setEnabled(True)
            self.btn_save_and_close.setAutoDefault(True)
            self.btn_save_and_close.setDefault(True)

    def get_value(self):
        """Return modified text"""
        # It is import to avoid accessing Qt C++ object as it has probably
        # already been destroyed, due to the Qt.WA_DeleteOnClose attribute
        return self.text

    def setup_and_check(self, value):
        """Verify if TextEditor is able to display strings passed to it."""
        try:
            to_text_string(value, 'utf8')
            return True
        except:
            return False
コード例 #31
0
ファイル: importwizard.py プロジェクト: zhoufan766/spyder
class ContentsWidget(QWidget):
    """Import wizard contents widget"""
    asDataChanged = Signal(bool)

    def __init__(self, parent, text):
        QWidget.__init__(self, parent)

        self.text_editor = QTextEdit(self)
        self.text_editor.setText(text)
        self.text_editor.setReadOnly(True)

        # Type frame
        type_layout = QHBoxLayout()
        type_label = QLabel(_("Import as"))
        type_layout.addWidget(type_label)
        data_btn = QRadioButton(_("data"))
        data_btn.setChecked(True)
        self._as_data = True
        type_layout.addWidget(data_btn)
        code_btn = QRadioButton(_("code"))
        self._as_code = False
        type_layout.addWidget(code_btn)
        txt_btn = QRadioButton(_("text"))
        type_layout.addWidget(txt_btn)

        h_spacer = QSpacerItem(40, 20, QSizePolicy.Expanding,
                               QSizePolicy.Minimum)
        type_layout.addItem(h_spacer)
        type_frame = QFrame()
        type_frame.setLayout(type_layout)

        # Opts frame
        grid_layout = QGridLayout()
        grid_layout.setSpacing(0)

        col_label = QLabel(_("Column separator:"))
        grid_layout.addWidget(col_label, 0, 0)
        col_w = QWidget()
        col_btn_layout = QHBoxLayout()
        self.tab_btn = QRadioButton(_("Tab"))
        self.tab_btn.setChecked(False)
        col_btn_layout.addWidget(self.tab_btn)
        self.ws_btn = QRadioButton(_("Whitespace"))
        self.ws_btn.setChecked(False)
        col_btn_layout.addWidget(self.ws_btn)
        other_btn_col = QRadioButton(_("other"))
        other_btn_col.setChecked(True)
        col_btn_layout.addWidget(other_btn_col)
        col_w.setLayout(col_btn_layout)
        grid_layout.addWidget(col_w, 0, 1)
        self.line_edt = QLineEdit(",")
        self.line_edt.setMaximumWidth(30)
        self.line_edt.setEnabled(True)
        other_btn_col.toggled.connect(self.line_edt.setEnabled)
        grid_layout.addWidget(self.line_edt, 0, 2)

        row_label = QLabel(_("Row separator:"))
        grid_layout.addWidget(row_label, 1, 0)
        row_w = QWidget()
        row_btn_layout = QHBoxLayout()
        self.eol_btn = QRadioButton(_("EOL"))
        self.eol_btn.setChecked(True)
        row_btn_layout.addWidget(self.eol_btn)
        other_btn_row = QRadioButton(_("other"))
        row_btn_layout.addWidget(other_btn_row)
        row_w.setLayout(row_btn_layout)
        grid_layout.addWidget(row_w, 1, 1)
        self.line_edt_row = QLineEdit(";")
        self.line_edt_row.setMaximumWidth(30)
        self.line_edt_row.setEnabled(False)
        other_btn_row.toggled.connect(self.line_edt_row.setEnabled)
        grid_layout.addWidget(self.line_edt_row, 1, 2)

        grid_layout.setRowMinimumHeight(2, 15)

        other_group = QGroupBox(_("Additional options"))
        other_layout = QGridLayout()
        other_group.setLayout(other_layout)

        skiprows_label = QLabel(_("Skip rows:"))
        other_layout.addWidget(skiprows_label, 0, 0)
        self.skiprows_edt = QLineEdit('0')
        self.skiprows_edt.setMaximumWidth(30)
        intvalid = QIntValidator(0, len(to_text_string(text).splitlines()),
                                 self.skiprows_edt)
        self.skiprows_edt.setValidator(intvalid)
        other_layout.addWidget(self.skiprows_edt, 0, 1)

        other_layout.setColumnMinimumWidth(2, 5)

        comments_label = QLabel(_("Comments:"))
        other_layout.addWidget(comments_label, 0, 3)
        self.comments_edt = QLineEdit('#')
        self.comments_edt.setMaximumWidth(30)
        other_layout.addWidget(self.comments_edt, 0, 4)

        self.trnsp_box = QCheckBox(_("Transpose"))
        #self.trnsp_box.setEnabled(False)
        other_layout.addWidget(self.trnsp_box, 1, 0, 2, 0)

        grid_layout.addWidget(other_group, 3, 0, 2, 0)

        opts_frame = QFrame()
        opts_frame.setLayout(grid_layout)

        data_btn.toggled.connect(opts_frame.setEnabled)
        data_btn.toggled.connect(self.set_as_data)
        code_btn.toggled.connect(self.set_as_code)
        #        self.connect(txt_btn, SIGNAL("toggled(bool)"),
        #                     self, SLOT("is_text(bool)"))

        # Final layout
        layout = QVBoxLayout()
        layout.addWidget(type_frame)
        layout.addWidget(self.text_editor)
        layout.addWidget(opts_frame)
        self.setLayout(layout)

    def get_as_data(self):
        """Return if data type conversion"""
        return self._as_data

    def get_as_code(self):
        """Return if code type conversion"""
        return self._as_code

    def get_as_num(self):
        """Return if numeric type conversion"""
        return self._as_num

    def get_col_sep(self):
        """Return the column separator"""
        if self.tab_btn.isChecked():
            return u"\t"
        elif self.ws_btn.isChecked():
            return None
        return to_text_string(self.line_edt.text())

    def get_row_sep(self):
        """Return the row separator"""
        if self.eol_btn.isChecked():
            return u"\n"
        return to_text_string(self.line_edt_row.text())

    def get_skiprows(self):
        """Return number of lines to be skipped"""
        return int(to_text_string(self.skiprows_edt.text()))

    def get_comments(self):
        """Return comment string"""
        return to_text_string(self.comments_edt.text())

    @Slot(bool)
    def set_as_data(self, as_data):
        """Set if data type conversion"""
        self._as_data = as_data
        self.asDataChanged.emit(as_data)

    @Slot(bool)
    def set_as_code(self, as_code):
        """Set if code type conversion"""
        self._as_code = as_code
コード例 #32
0
ファイル: importwizard.py プロジェクト: ShenggaoZhu/spyder
class ContentsWidget(QWidget):
    """Import wizard contents widget"""
    asDataChanged = Signal(bool)
    
    def __init__(self, parent, text):
        QWidget.__init__(self, parent)

        self.text_editor = QTextEdit(self)
        self.text_editor.setText(text)
        self.text_editor.setReadOnly(True)

        # Type frame
        type_layout = QHBoxLayout()
        type_label = QLabel(_("Import as"))
        type_layout.addWidget(type_label)
        data_btn = QRadioButton(_("data"))
        data_btn.setChecked(True)
        self._as_data= True
        type_layout.addWidget(data_btn)
        code_btn = QRadioButton(_("code"))
        self._as_code = False
        type_layout.addWidget(code_btn)
        txt_btn = QRadioButton(_("text"))
        type_layout.addWidget(txt_btn)

        h_spacer = QSpacerItem(40, 20,
                               QSizePolicy.Expanding, QSizePolicy.Minimum)
        type_layout.addItem(h_spacer)
        type_frame = QFrame()
        type_frame.setLayout(type_layout)

        # Opts frame
        grid_layout = QGridLayout()
        grid_layout.setSpacing(0)

        col_label = QLabel(_("Column separator:"))
        grid_layout.addWidget(col_label, 0, 0)
        col_w = QWidget()
        col_btn_layout = QHBoxLayout()
        self.tab_btn = QRadioButton(_("Tab"))
        self.tab_btn.setChecked(False)
        col_btn_layout.addWidget(self.tab_btn)
        other_btn_col = QRadioButton(_("other"))
        other_btn_col.setChecked(True)
        col_btn_layout.addWidget(other_btn_col)
        col_w.setLayout(col_btn_layout)
        grid_layout.addWidget(col_w, 0, 1)
        self.line_edt = QLineEdit(",")
        self.line_edt.setMaximumWidth(30)
        self.line_edt.setEnabled(True)
        other_btn_col.toggled.connect(self.line_edt.setEnabled)
        grid_layout.addWidget(self.line_edt, 0, 2)

        row_label = QLabel(_("Row separator:"))
        grid_layout.addWidget(row_label, 1, 0)
        row_w = QWidget()
        row_btn_layout = QHBoxLayout()
        self.eol_btn = QRadioButton(_("EOL"))
        self.eol_btn.setChecked(True)
        row_btn_layout.addWidget(self.eol_btn)
        other_btn_row = QRadioButton(_("other"))
        row_btn_layout.addWidget(other_btn_row)
        row_w.setLayout(row_btn_layout)
        grid_layout.addWidget(row_w, 1, 1)
        self.line_edt_row = QLineEdit(";")
        self.line_edt_row.setMaximumWidth(30)
        self.line_edt_row.setEnabled(False)
        other_btn_row.toggled.connect(self.line_edt_row.setEnabled)
        grid_layout.addWidget(self.line_edt_row, 1, 2)

        grid_layout.setRowMinimumHeight(2, 15)

        other_group = QGroupBox(_("Additional options"))
        other_layout = QGridLayout()
        other_group.setLayout(other_layout)

        skiprows_label = QLabel(_("Skip rows:"))
        other_layout.addWidget(skiprows_label, 0, 0)
        self.skiprows_edt = QLineEdit('0')
        self.skiprows_edt.setMaximumWidth(30)
        intvalid = QIntValidator(0, len(to_text_string(text).splitlines()),
                                 self.skiprows_edt)
        self.skiprows_edt.setValidator(intvalid)
        other_layout.addWidget(self.skiprows_edt, 0, 1)

        other_layout.setColumnMinimumWidth(2, 5)

        comments_label = QLabel(_("Comments:"))
        other_layout.addWidget(comments_label, 0, 3)
        self.comments_edt = QLineEdit('#')
        self.comments_edt.setMaximumWidth(30)
        other_layout.addWidget(self.comments_edt, 0, 4)

        self.trnsp_box = QCheckBox(_("Transpose"))
        #self.trnsp_box.setEnabled(False)
        other_layout.addWidget(self.trnsp_box, 1, 0, 2, 0)

        grid_layout.addWidget(other_group, 3, 0, 2, 0)

        opts_frame = QFrame()
        opts_frame.setLayout(grid_layout)

        data_btn.toggled.connect(opts_frame.setEnabled)
        data_btn.toggled.connect(self.set_as_data)
        code_btn.toggled.connect(self.set_as_code)
#        self.connect(txt_btn, SIGNAL("toggled(bool)"),
#                     self, SLOT("is_text(bool)"))

        # Final layout
        layout = QVBoxLayout()
        layout.addWidget(type_frame)
        layout.addWidget(self.text_editor)
        layout.addWidget(opts_frame)
        self.setLayout(layout)

    def get_as_data(self):
        """Return if data type conversion"""
        return self._as_data

    def get_as_code(self):
        """Return if code type conversion"""
        return self._as_code

    def get_as_num(self):
        """Return if numeric type conversion"""
        return self._as_num

    def get_col_sep(self):
        """Return the column separator"""
        if self.tab_btn.isChecked():
            return u"\t"
        return to_text_string(self.line_edt.text())

    def get_row_sep(self):
        """Return the row separator"""
        if self.eol_btn.isChecked():
            return u"\n"
        return to_text_string(self.line_edt_row.text())

    def get_skiprows(self):
        """Return number of lines to be skipped"""
        return int(to_text_string(self.skiprows_edt.text()))

    def get_comments(self):
        """Return comment string"""
        return to_text_string(self.comments_edt.text())

    @Slot(bool)
    def set_as_data(self, as_data):
        """Set if data type conversion"""
        self._as_data = as_data
        self.asDataChanged.emit(as_data)

    @Slot(bool)
    def set_as_code(self, as_code):
        """Set if code type conversion"""
        self._as_code = as_code
コード例 #33
0
ファイル: texteditor.py プロジェクト: zerocewl/spyder
class TextEditor(BaseDialog):
    """Array Editor Dialog"""
    def __init__(self, text, title='', font=None, parent=None, readonly=False):
        QDialog.__init__(self, parent)

        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.text = None
        self.btn_save_and_close = None

        # Display text as unicode if it comes as bytes, so users see
        # its right representation
        if is_binary_string(text):
            self.is_binary = True
            text = to_text_string(text, 'utf8')
        else:
            self.is_binary = False

        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        # Text edit
        self.edit = QTextEdit(parent)
        self.edit.setReadOnly(readonly)
        self.edit.textChanged.connect(self.text_changed)
        self.edit.setPlainText(text)
        if font is None:
            font = get_font()
        self.edit.setFont(font)
        self.layout.addWidget(self.edit)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        btn_layout.addStretch()
        if not readonly:
            self.btn_save_and_close = QPushButton(_('Save and Close'))
            self.btn_save_and_close.setDisabled(True)
            self.btn_save_and_close.clicked.connect(self.accept)
            btn_layout.addWidget(self.btn_save_and_close)

        self.btn_close = QPushButton(_('Close'))
        self.btn_close.setAutoDefault(True)
        self.btn_close.setDefault(True)
        self.btn_close.clicked.connect(self.reject)
        btn_layout.addWidget(self.btn_close)

        self.layout.addLayout(btn_layout)

        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)

        self.setWindowIcon(ima.icon('edit'))
        if title:
            try:
                unicode_title = to_text_string(title)
            except UnicodeEncodeError:
                unicode_title = u''
        else:
            unicode_title = u''

        self.setWindowTitle(_("Text editor") + \
                            u"%s" % (u" - " + unicode_title
                                     if unicode_title else u""))

    @Slot()
    def text_changed(self):
        """Text has changed"""
        # Save text as bytes, if it was initially bytes
        if self.is_binary:
            self.text = to_binary_string(self.edit.toPlainText(), 'utf8')
        else:
            self.text = to_text_string(self.edit.toPlainText())
        if self.btn_save_and_close:
            self.btn_save_and_close.setEnabled(True)
            self.btn_save_and_close.setAutoDefault(True)
            self.btn_save_and_close.setDefault(True)

    def get_value(self):
        """Return modified text"""
        # It is import to avoid accessing Qt C++ object as it has probably
        # already been destroyed, due to the Qt.WA_DeleteOnClose attribute
        return self.text

    def setup_and_check(self, value):
        """Verify if TextEditor is able to display strings passed to it."""
        try:
            to_text_string(value, 'utf8')
            return True
        except:
            return False
コード例 #34
0
class TextEditor(QDialog):
    """Array Editor Dialog"""
    def __init__(self,
                 text,
                 title='',
                 font=None,
                 parent=None,
                 readonly=False,
                 size=(400, 300)):
        QDialog.__init__(self, parent)

        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.text = None

        # Display text as unicode if it comes as bytes, so users see
        # its right representation
        if is_binary_string(text):
            self.is_binary = True
            text = to_text_string(text, 'utf8')
        else:
            self.is_binary = False

        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        # Text edit
        self.edit = QTextEdit(parent)
        self.edit.textChanged.connect(self.text_changed)
        self.edit.setReadOnly(readonly)
        self.edit.setPlainText(text)
        if font is None:
            font = get_font()
        self.edit.setFont(font)
        self.layout.addWidget(self.edit)

        # Buttons configuration
        buttons = QDialogButtonBox.Ok
        if not readonly:
            buttons = buttons | QDialogButtonBox.Cancel
        bbox = QDialogButtonBox(buttons)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        self.layout.addWidget(bbox)

        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)

        self.setWindowIcon(ima.icon('edit'))
        self.setWindowTitle(_("Text editor") + \
                            "%s" % (" - "+str(title) if str(title) else ""))
        self.resize(size[0], size[1])

    def text_changed(self):
        """Text has changed"""
        # Save text as bytes, if it was initially bytes
        if self.is_binary:
            self.text = to_binary_string(self.edit.toPlainText(), 'utf8')
        else:
            self.text = to_text_string(self.edit.toPlainText())

    def get_value(self):
        """Return modified text"""
        # It is import to avoid accessing Qt C++ object as it has probably
        # already been destroyed, due to the Qt.WA_DeleteOnClose attribute
        return self.text

    def setup_and_check(self, value):
        """Verify if TextEditor is able to display strings passed to it."""
        try:
            to_text_string(value, 'utf8')
            return True
        except:
            return False