Exemplo n.º 1
0
    def _add_plot_type_option_menu(self, menu, ax):
        with errorbar_caps_removed(ax):
            # Able to change the plot type to waterfall if there is only one axes, it is a MantidAxes, and there is more
            # than one line on the axes.
            if len(ax.get_figure().get_axes()) > 1 or not isinstance(
                    ax, MantidAxes) or len(ax.get_lines()) <= 1:
                return

        plot_type_menu = QMenu("Plot Type", menu)
        plot_type_action_group = QActionGroup(plot_type_menu)
        standard = plot_type_menu.addAction(
            "1D", lambda: self._change_plot_type(
                ax, plot_type_action_group.checkedAction()))
        waterfall = plot_type_menu.addAction(
            "Waterfall", lambda: self._change_plot_type(
                ax, plot_type_action_group.checkedAction()))

        for action in [waterfall, standard]:
            plot_type_action_group.addAction(action)
            action.setCheckable(True)

        if ax.is_waterfall():
            waterfall.setChecked(True)
        else:
            standard.setChecked(True)

        menu.addMenu(plot_type_menu)
Exemplo n.º 2
0
    def _add_normalization_option_menu(self, menu, ax):
        # Check if toggling normalization makes sense
        can_toggle_normalization = self._can_toggle_normalization(ax)
        if not can_toggle_normalization:
            return None

        # Create menu
        norm_menu = QMenu("Normalization", menu)
        norm_actions_group = QActionGroup(norm_menu)
        none_action = norm_menu.addAction(
            'None', lambda: self._set_normalization_none(ax))
        norm_action = norm_menu.addAction(
            'Bin Width', lambda: self._set_normalization_bin_width(ax))
        for action in [none_action, norm_action]:
            norm_actions_group.addAction(action)
            action.setCheckable(True)

        # Update menu state
        is_normalized = self._is_normalized(ax)
        if is_normalized:
            norm_action.setChecked(True)
        else:
            none_action.setChecked(True)

        menu.addMenu(norm_menu)
Exemplo n.º 3
0
 def _connect_buttons(self, widget):
     w = widget.findChild(QPushButton, 'dclink_button')
     if w:
         psname = self._psname[0]
         dclinks = PSSearch.conv_psname_2_dclink(psname)
         if dclinks:
             dclink_type = PSSearch.conv_psname_2_psmodel(dclinks[0])
             if dclink_type != 'REGATRON_DCLink':
                 connect_window(w, PSDetailWindow, self, psname=dclinks)
             else:
                 if len(dclinks) > 1:
                     menu = QMenu(w)
                     for dcl in dclinks:
                         act = QAction(dcl, menu)
                         connect_newprocess(act, [
                             'sirius-hla-as-ps-regatron-individual', '-dev',
                             dcl
                         ],
                                            parent=self,
                                            is_pydm=True)
                         menu.addAction(act)
                     w.setMenu(menu)
                 else:
                     connect_newprocess(w, [
                         'sirius-hla-as-ps-regatron-individual', '-dev',
                         dclinks[0]
                     ],
                                        parent=self,
                                        is_pydm=True)
         else:
             w.setHidden(True)
Exemplo n.º 4
0
class ProgressWindow(FramelessWindow):
    def __init__(self, parent, generator):
        super(ProgressWindow, self).__init__(parent)
        self.__generator = generator

        self.__progress_view = QPlainTextEdit(self)
        self.__highlighter = Highlighter(self.__progress_view.document())
        self.__progress_view.textChanged.connect(self.__on_progress_text)
        self.addContentWidget(self.__progress_view)

        self.menu = QMenu(self.__generator, self)
        close_action = QAction("Close", self.menu)
        close_action.triggered.connect(self.close)
        self.menu.addAction(close_action)
        self.addMenu(self.menu)

    def generator(self):
        return self.__generator

    @Slot(str)
    def appendProgress(self, text):
        self.__progress_view.appendPlainText(text)

    @Slot()
    def __on_progress_text(self):
        self.__progress_view.verticalScrollBar().setValue(
            self.__progress_view.verticalScrollBar().maximum())
Exemplo n.º 5
0
    def open_menu(self, position):
        menu = QMenu(self)

        preset_menu = menu.addMenu('Presets')
        preset_menu.addAction('New preset', self.new_preset_dialog)
        preset_menu.addSeparator()

        preset_names = CONFIG.get_header_presets()

        if len(preset_names) == 0:
            action = preset_menu.addAction('No presets')
            action.setEnabled(False)
        else:
            delete_menu = menu.addMenu('Delete preset')
            for name in preset_names:
                preset_menu.addAction(name, partial(self.load_preset, name))
                delete_menu.addAction(name, partial(self.delete_preset, name))

        menu.addSeparator()
        menu.addAction('New column...', self.create_new_column_dialog)

        if len(self.columnList.selectedIndexes()) > 0:
            menu.addAction('Delete selected', self.delete_selected)

        menu.popup(self.columnList.viewport().mapToGlobal(position))
Exemplo n.º 6
0
    def __init__(self):
        QWidget.__init__(self)
        self._line_edit = ClearableLineEdit()

        self._calendar_button = QToolButton()
        self._calendar_button.setPopupMode(QToolButton.InstantPopup)
        self._calendar_button.setFixedSize(26, 26)
        self._calendar_button.setAutoRaise(True)
        self._calendar_button.setIcon(resourceIcon("calendar.png"))
        self._calendar_button.setStyleSheet(
            "QToolButton::menu-indicator { image: none; }")

        tool_menu = QMenu(self._calendar_button)
        self._calendar_widget = QCalendarWidget(tool_menu)
        action = QWidgetAction(tool_menu)
        action.setDefaultWidget(self._calendar_widget)
        tool_menu.addAction(action)
        self._calendar_button.setMenu(tool_menu)

        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self._line_edit)
        layout.addWidget(self._calendar_button)
        self.setLayout(layout)

        self._calendar_widget.activated.connect(self.setDate)
Exemplo n.º 7
0
    def _customContextMenu(self, pos):

        menu = QMenu(self)
        menu.addAction(self.closeAction)
        menu.addSeparator()
        menu.addAction(self.dockProperties)
        menu.popup(self.mapToGlobal(pos))
Exemplo n.º 8
0
    def __zoom(self) -> None:
        """Zoom functions.

        + 'zoom to fit' function connections.
        + Zoom text buttons
        """
        self.action_zoom_to_fit.triggered.connect(self.main_canvas.zoom_to_fit)
        self.ResetCanvas.clicked.connect(self.main_canvas.zoom_to_fit)

        def zoom_level(value: int) -> Callable[[], None]:
            """Return a function that set the specified zoom value."""
            @Slot()
            def func() -> None:
                self.zoom_bar.setValue(value)

            return func

        zoom_menu = QMenu(self)
        zoom_min = self.zoom_bar.minimum()
        zoom_min = zoom_min - zoom_min % 100 + 100
        for level in range(zoom_min, 500 + 1, 100):
            action = QAction(f'{level}%', self)
            action.triggered.connect(zoom_level(level))
            zoom_menu.addAction(action)
        action = QAction("customize", self)
        action.triggered.connect(self.customize_zoom)
        zoom_menu.addAction(action)
        self.zoom_button.setMenu(zoom_menu)
Exemplo n.º 9
0
    def __free_move(self) -> None:
        """Menu of free move mode."""
        free_move_mode_menu = QMenu(self)

        def free_move_mode_func(j: int, icon_qt: QIcon) -> Callable[[], None]:
            @Slot()
            def func() -> None:
                self.free_move_button.setIcon(icon_qt)
                self.main_canvas.set_free_move(j)
                self.entities_tab.setCurrentIndex(0)
                self.inputs_widget.variable_stop.click()

            return func

        for i, (text, icon, tip) in enumerate([
            ("View mode", "free_move_off", "Disable free move mode."),
            ("Translate mode", "translate", "Edit by 2 DOF moving."),
            ("Rotate mode", "rotate", "Edit by 1 DOF moving."),
            ("Reflect mode", "reflect", "Edit by flip axis."),
        ]):
            action = QAction(QIcon(QPixmap(f":/icons/{icon}.png")), text, self)
            action.triggered.connect(free_move_mode_func(i, action.icon()))
            action.setShortcut(f"Ctrl+{i + 1}")
            action.setShortcutContext(Qt.WindowShortcut)
            action.setStatusTip(tip)
            free_move_mode_menu.addAction(action)
            if i == 0:
                self.free_move_disable = action
        self.free_move_button.setMenu(free_move_mode_menu)
Exemplo n.º 10
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        loadUi(
            os.path.abspath(
                os.path.join(os.path.dirname(__file__), ".",
                             "model_editor.ui")), self)

        # Populate the add mode button with a dropdown containing available
        # fittable model objects
        self.add_model_button.setPopupMode(QToolButton.InstantPopup)
        models_menu = QMenu(self.add_model_button)
        self.add_model_button.setMenu(models_menu)

        for k, v in MODELS.items():
            action = QAction(k, models_menu)
            action.triggered.connect(
                lambda x, m=v: self._add_fittable_model(m))
            models_menu.addAction(action)

        # Initially hide the model editor tools until user has selected an
        # editable model spectrum object
        self.editor_holder_widget.setHidden(True)
        self.setup_holder_widget.setHidden(False)

        self.equation_edit_button.clicked.connect(
            self._on_equation_edit_button_clicked)
        self.new_model_button.clicked.connect(self._on_create_new_model)
        self.remove_model_button.clicked.connect(self._on_remove_model)

        # When a plot data item is select, get its model editor model
        # representation
        self.hub.workspace.current_selected_changed.connect(
            self._on_plot_item_selected)
Exemplo n.º 11
0
    def _add_plot_type_option_menu(self, menu, ax):
        # Error bar caps are considered lines so they are removed before checking the number of lines on the axes so
        # they aren't confused for "actual" lines.
        error_bar_caps = datafunctions.remove_and_return_errorbar_cap_lines(ax)

        # Able to change the plot type to waterfall if there is only one axes, it is a MantidAxes, and there is more
        # than one line on the axes.
        if len(ax.get_figure().get_axes()) > 1 or not isinstance(
                ax, MantidAxes) or len(ax.get_lines()) <= 1:
            return

        # Re-add error bar caps
        ax.lines += error_bar_caps

        plot_type_menu = QMenu("Plot Type", menu)
        plot_type_action_group = QActionGroup(plot_type_menu)
        standard = plot_type_menu.addAction(
            "1D", lambda: self._change_plot_type(
                ax, plot_type_action_group.checkedAction()))
        waterfall = plot_type_menu.addAction(
            "Waterfall", lambda: self._change_plot_type(
                ax, plot_type_action_group.checkedAction()))

        for action in [waterfall, standard]:
            plot_type_action_group.addAction(action)
            action.setCheckable(True)

        if ax.is_waterfall():
            waterfall.setChecked(True)
        else:
            standard.setChecked(True)

        menu.addMenu(plot_type_menu)
Exemplo n.º 12
0
    def show_contextmenu(self, pos: QPoint):
        menu = QMenu(self)
        index = self.currentIndex()
        node = index.internalPointer()

        # insert item and node
        menu.addAction(self.insertitemAction)
        menu.addAction(self.insertnodeAction)

        # edit key
        if isinstance(node, (JsonNode, JsonItem)):
            menu.addSeparator()
            menu.addAction(self.editkeyAction)
            if isinstance(node, JsonItem):
                menu.addAction(self.editAction)
                self.editAction.setEnabled(not node.readonly)

        # remove
        if isinstance(node, (JsonNode, JsonItem)):
            menu.addSeparator()
            menu.addAction(self.removeitemAction)

        # properties
        if isinstance(node, JsonItem):
            menu.addSeparator()
            menu.addAction(self.propertiesAction)
            menu.setDefaultAction(self.propertiesAction)

        menu.popup(self.viewport().mapToGlobal(pos), self.editAction)
Exemplo n.º 13
0
    def _add_marker_option_menu(self, menu, event):
        """
        Entry in main context menu to:
         - add horizontal/vertical markers
         - open a marker editor window.
        The editor window allows editing of all currently plotted markers
        :param menu: instance of QMenu to append this submenu to
        :param event: mpl event that generated the call
        """
        marker_menu = QMenu("Markers", menu)
        marker_action_group = QActionGroup(marker_menu)
        x0, x1 = event.inaxes.get_xlim()
        y0, y1 = event.inaxes.get_ylim()
        horizontal = marker_menu.addAction(
            "Horizontal", lambda: self._add_horizontal_marker(
                event.ydata, y0, y1, event.inaxes))
        vertical = marker_menu.addAction(
            "Vertical", lambda: self._add_vertical_marker(
                event.xdata, x0, x1, event.inaxes))
        edit = marker_menu.addAction("Edit",
                                     lambda: self._global_edit_markers())

        for action in [horizontal, vertical, edit]:
            marker_action_group.addAction(action)

        menu.addMenu(marker_menu)
Exemplo n.º 14
0
class WorkflowWidget(QWidget):
    sigAddFunction = Signal(object)

    def __init__(self, workflowview: QAbstractItemView):
        super(WorkflowWidget, self).__init__()

        self.view = workflowview

        self.toolbar = QToolBar()
        self.addfunctionmenu = QToolButton()
        self.addfunctionmenu.setIcon(QIcon(path("icons/addfunction.png")))
        self.addfunctionmenu.setText("Add Function")
        # Defer menu population to once the plugins have been loaded; otherwise, the menu may not contain anything
        # if this widget is init'd before all plugins have been loaded.
        self.functionmenu = QMenu()
        self.functionmenu.aboutToShow.connect(self.populateFunctionMenu)
        self.addfunctionmenu.setMenu(self.functionmenu)
        self.addfunctionmenu.setPopupMode(QToolButton.InstantPopup)
        self.toolbar.addWidget(self.addfunctionmenu)
        # self.toolbar.addAction(QIcon(path('icons/up.png')), 'Move Up')
        # self.toolbar.addAction(QIcon(path('icons/down.png')), 'Move Down')
        self.toolbar.addAction(QIcon(path("icons/folder.png")),
                               "Load Workflow")
        self.toolbar.addAction(QIcon(path("icons/trash.png")),
                               "Delete Operation", self.deleteOperation)

        v = QVBoxLayout()
        v.addWidget(self.view)
        v.addWidget(self.toolbar)
        v.setContentsMargins(0, 0, 0, 0)
        self.setLayout(v)

    def populateFunctionMenu(self):
        self.functionmenu.clear()
        sortingDict = {}
        for plugin in pluginmanager.get_plugins_of_type("OperationPlugin"):
            typeOfOperationPlugin = plugin.getCategory()
            if not typeOfOperationPlugin in sortingDict.keys():
                sortingDict[typeOfOperationPlugin] = []
            sortingDict[typeOfOperationPlugin].append(plugin)
        for key in sortingDict.keys():
            self.functionmenu.addSeparator()
            self.functionmenu.addAction(key)
            self.functionmenu.addSeparator()
            for plugin in sortingDict[key]:
                self.functionmenu.addAction(
                    plugin.name,
                    partial(self.addOperation, plugin, autoconnectall=True))

    def addOperation(self, operation: OperationPlugin, autoconnectall=True):
        self.view.model().workflow.addOperation(operation(), autoconnectall)
        print("selected new row:", self.view.model().rowCount() - 1)
        self.view.setCurrentIndex(self.view.model().index(
            self.view.model().rowCount() - 1, 0))

    def deleteOperation(self):
        for index in self.view.selectedIndexes():
            operation = self.view.model().workflow.operations[index.row()]
            self.view.model().workflow.remove_operation(operation)
Exemplo n.º 15
0
 def tableMenu(self, event):
     """Right click menu for table, can plot or print selected logs"""
     menu = QMenu(self)
     plotAction = menu.addAction("Plot selected")
     plotAction.triggered.connect(self.presenter.new_plot_logs)
     plotAction = menu.addAction("Print selected")
     plotAction.triggered.connect(self.presenter.print_selected_logs)
     menu.exec_(event.globalPos())
Exemplo n.º 16
0
 def tableMenu(self, event):
     """Right click menu for table, can plot or print selected logs"""
     menu = QMenu(self)
     plotAction = menu.addAction("Plot selected")
     plotAction.triggered.connect(self.presenter.new_plot_logs)
     plotAction = menu.addAction("Print selected")
     plotAction.triggered.connect(self.presenter.print_selected_logs)
     menu.exec_(event.globalPos())
Exemplo n.º 17
0
class MOSViewerToolbar(BasicToolbar):
    def __init__(self, *args, **kwargs):
        super(MOSViewerToolbar, self).__init__(*args, **kwargs)
        self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)

        # Define the toolbar actions
        self.cycle_previous_action = QAction(
            QIcon(os.path.join(ICON_DIR, "Previous-96.png")), "Previous", self)
        self.cycle_next_action = QAction(
            QIcon(os.path.join(ICON_DIR, "Next-96.png")), "Next", self)

        # Include the dropdown widget
        self.source_select = QComboBox()

        # Add the items to the toolbar
        self.addAction(self.cycle_previous_action)
        self.addAction(self.cycle_next_action)
        self.addWidget(self.source_select)

        # Include a button to open spectrum in specviz
        self.open_specviz = QAction(
            QIcon(os.path.join(ICON_DIR, "External-96.png")),
            "Open in SpecViz", self)

        # Create a tool button to hold the lock axes menu object
        tool_button = QToolButton(self)
        tool_button.setText("Axes Settings")
        tool_button.setIcon(QIcon(os.path.join(ICON_DIR, "Settings-96.png")))
        tool_button.setPopupMode(QToolButton.MenuButtonPopup)
        tool_button.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)

        # Create a menu for the axes settings drop down
        self.settings_menu = QMenu(self)

        # Add lock x axis action
        self.lock_x_action = QAction("Lock spectral axis", self.settings_menu)
        self.lock_x_action.setCheckable(True)

        # Add lock y axis action
        self.lock_y_action = QAction("Lock vertical displacement axis",
                                     self.settings_menu)
        self.lock_y_action.setCheckable(True)

        # Add the actions to the menu
        self.settings_menu.addAction(self.lock_x_action)
        self.settings_menu.addAction(self.lock_y_action)

        # Set the menu object on the tool button
        tool_button.setMenu(self.settings_menu)

        # Create a widget action object to hold the tool button, this way the
        # toolbar behaves the way it's expected to
        tool_button_action = QWidgetAction(self)
        tool_button_action.setDefaultWidget(tool_button)

        self.addAction(tool_button_action)
        self.addSeparator()
        self.addAction(self.open_specviz)
Exemplo n.º 18
0
class RightClickTreeView(QTreeView2):
    """
    creates a QTreeView with:
     - all the features of QTreeView2
     - a right click context menu with:
       - Edit - TODO
       - Cut - TODO
       - Copy - TODO
       - Rename - TODO
       - Delete
    """
    def __init__(self, parent, data, choices):
        QTreeView2.__init__(self, parent, data, choices)

        self.right_click_menu = QMenu()
        edit = self.right_click_menu.addAction("Edit...")
        cut = self.right_click_menu.addAction("Cut...")
        copy = self.right_click_menu.addAction("Copy...")
        paste = self.right_click_menu.addAction("Paste...")
        rename = self.right_click_menu.addAction("Rename...")
        delete = self.right_click_menu.addAction("Delete...")

        edit.triggered.connect(self.on_edit)
        cut.triggered.connect(self.on_cut)
        copy.triggered.connect(self.on_copy)
        paste.triggered.connect(self.on_paste)
        rename.triggered.connect(self.on_rename)
        delete.triggered.connect(self.on_delete)

    def on_edit(self):
        rows = self.find_list_index()
        print('edit...rows=%s' % rows)
    def on_cut(self):
        pass
    def on_copy(self):
        pass
    def on_paste(self):
        pass
    def on_rename(self):
        rows = self.find_list_index()
        list_data = self.data
        for row in rows[:-1]:
            list_datai = list_data[row]
            list_data = list_datai[2]

        list_datai = list_data[rows[-1]]
        list_data = list_datai[0]
        print('list_datai* = ', list_datai)
        #print('list_data2* = %r' % list_data)
        list_datai[0] = 'cat'
        self.parent.update_data(self.data)
        #self.update()

    #def on_delete(self):
        #pass

    def on_right_mouse_button(self):
        self.right_click_menu.popup(QtGui.QCursor.pos())
Exemplo n.º 19
0
 def context_menu_requested(self, event):
     """Popup context menu."""
     if self.fig:
         pos = QPoint(event.x(), event.y())
         context_menu = QMenu(self)
         context_menu.addAction(ima.icon('editcopy'), "Copy Image",
                                self.copy_figure,
                                QKeySequence(get_shortcut('plots', 'copy')))
         context_menu.popup(self.mapToGlobal(pos))
Exemplo n.º 20
0
class TasksWidget(QWidget):
    """Shows tasks table."""
    stop_task = Signal(Bot, Task)
    force_start_task = Signal(Bot, Task)

    def __init__(self, bot, parent):
        super(TasksWidget, self).__init__(parent)
        self.bot = bot

        self.menu = None
        self.task_details_window = None

        self.widget_layout = QHBoxLayout(self)
        self.setLayout(self.widget_layout)
        self.table = TasksTableView(self)
        self.table.context_menu_requested.connect(self.show_context_menu)
        self.table.task_double_clicked.connect(self.create_task_details_window)
        self.model = TasksTableModel(self)
        self.model.setBot(bot)
        self.table.setModel(self.model)
        self.widget_layout.addWidget(self.table)

    @Slot(list)
    def update_tasks(self, data):
        self.model.setEvents(data)

    @Slot(Task)
    def create_task_details_window(self, task: Task):
        self.task_details_window = TaskDetailsWindow(self)
        self.task_details_window.setTask(task)
        self.task_details_window.show()

    @Slot(int)
    def show_context_menu(self, task_id) -> None:
        self.menu = QMenu(self)
        force_start_action = QAction('Force start', self)
        force_start_action.triggered.connect(
            lambda: self.on_force_start_action_triggered(task_id))

        stop_action = QAction('Stop', self)
        stop_action.triggered.connect(
            lambda: self.on_stop_action_triggered(task_id))
        self.menu.addAction(force_start_action)
        self.menu.addAction(stop_action)
        self.menu.popup(QCursor.pos())

    @Slot(int)
    def on_force_start_action_triggered(self, task_id):
        task = self.table.model().getEventById(task_id)
        if task:
            self.force_start_task.emit(self.bot, task)

    @Slot(int)
    def on_stop_action_triggered(self, task_id):
        task = self.table.model().getEventById(task_id)
        if task:
            self.stop_task.emit(self.bot, task)
Exemplo n.º 21
0
 def context_menu(self):
     try:
         menu = super(PyDMRelatedDisplayButton, self).context_menu()
     except:
         menu = QMenu(self)
     if len(menu.findChildren(QAction)) > 0:
         menu.addSeparator()
     menu.addAction(self.open_in_new_window_action)
     return menu
Exemplo n.º 22
0
 def context_menu(self):
     try:
         menu = super(PyDMRelatedDisplayButton, self).context_menu()
     except:
         menu = QMenu(self)
     if len(menu.findChildren(QAction)) > 0:
         menu.addSeparator()
     menu.addAction(self.open_in_new_window_action)
     return menu
Exemplo n.º 23
0
 def __make_minidsp_menu(self, func):
     menu = QMenu(self)
     current_config = QAction(menu)
     current_config.setText('Current')
     current_config.triggered.connect(func)
     menu.addAction(current_config)
     for i in range(4):
         self.__add_send_action(i, menu, func)
     return menu
Exemplo n.º 24
0
def main():
    logmodule = qrainbowstyle.extras.OutputLogger()
    qInstallMessageHandler(qrainbowstyle.extras.qt_message_handler)

    QtWidgets.QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
    QtWidgets.QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)

    app = QtWidgets.QApplication(sys.argv)
    app.setStyleSheet(qrainbowstyle.load_stylesheet(style="oceanic"))

    # Package options
    # qrainbowstyle.align_buttons_left()
    # qrainbowstyle.use_darwin_buttons()
    qrainbowstyle.setAppIcon(
        os.path.join(os.path.dirname(os.path.realpath(__file__)),
                     "github_logo.png"))

    # Create frameless mainwindow
    win = qrainbowstyle.windows.FramelessWindow()

    menu = QMenu(win)
    menu.setTitle("Some menu")
    menu.addAction(QAction("TEST ACTION", menu))
    win.addMenu(menu)

    # Example for spinner
    spinner = qrainbowstyle.widgets.WaitingSpinner(win,
                                                   centerOnParent=True,
                                                   modality=Qt.WindowModal,
                                                   roundness=70.0,
                                                   fade=70.0,
                                                   radius=9.0,
                                                   lines=24,
                                                   line_length=35.0,
                                                   line_width=2.0)
    spinner.start()
    spinner.fadeIn()
    t = QTimer()
    t.setSingleShot(True)
    t.timeout.connect(spinner.fadeOut)
    t.start(5000)

    win.setMinimumSize(QSize(500, 300))
    # Example of using signals
    win.closeClicked.connect(lambda: print("Close clicked!"))

    # Create content widget and pass reference to main window
    widget = WidgetGallery(win)

    # Add widget to main window and show it
    win.addContentWidget(widget)
    win.show()

    # Fullscreen test
    # win.showFullScreen()
    sys.exit(app.exec())
Exemplo n.º 25
0
 def context_menu_requested(self, event):
     """Popup context menu."""
     if self.fig:
         pos = QPoint(event.x(), event.y())
         context_menu = QMenu(self)
         context_menu.addAction(ima.icon('editcopy'), "Copy Image",
                                self.copy_figure,
                                QKeySequence(
                                    get_shortcut('plots', 'copy')))
         context_menu.popup(self.mapToGlobal(pos))
Exemplo n.º 26
0
 def contextMenuEvent(self, event):
     """Show a custom context menu."""
     point = event.pos()
     menu = QMenu("Actions", self)
     menu.addAction(self.blctrl_enbl_act)
     menu.addAction(self.blctrl_dsbl_act)
     menu.addSeparator()
     action = menu.addAction('Show Connections...')
     action.triggered.connect(self.show_connections)
     menu.popup(self.mapToGlobal(point))
Exemplo n.º 27
0
    def _display_roi_context_menu(self, roi_index):
        def delete_roi(event):
            self._dc.remove_subset_group(self._dc.subset_groups[roi_index])

        context_menu = QMenu()
        action = QAction("Delete ROI", context_menu)
        action.triggered.connect(delete_roi)
        context_menu.addAction(action)
        pos = self._viewer.mapToParent(QCursor().pos())
        context_menu.exec_(pos)
Exemplo n.º 28
0
    def _add_plugins_menu(self):
        """Add 'Plugins' menu to app menubar."""
        self.plugins_menu = self.main_menu.addMenu('&Plugins')

        pip_install_action = QAction("Install/Uninstall Package(s)...",
                                     self._qt_window)
        pip_install_action.triggered.connect(self._show_plugin_install_dialog)
        self.plugins_menu.addAction(pip_install_action)

        order_plugin_action = QAction("Plugin Call Order...", self._qt_window)
        order_plugin_action.setStatusTip('Change call order for plugins')
        order_plugin_action.triggered.connect(self._show_plugin_sorter)
        self.plugins_menu.addAction(order_plugin_action)

        report_plugin_action = QAction("Plugin Errors...", self._qt_window)
        report_plugin_action.setStatusTip(
            'Review stack traces for plugin exceptions and notify developers')
        report_plugin_action.triggered.connect(self._show_plugin_err_reporter)
        self.plugins_menu.addAction(report_plugin_action)

        self._plugin_dock_widget_menu = QMenu('Add Dock Widget',
                                              self._qt_window)

        if not plugins.dock_widgets:
            plugins.discover_dock_widgets()

        # Add a menu item (QAction) for each available plugin widget
        docks = zip(repeat("dock"), plugins.dock_widgets.items())
        funcs = zip(repeat("func"), plugins.function_widgets.items())
        for hook_type, (plugin_name, widgets) in chain(docks, funcs):
            multiprovider = len(widgets) > 1
            if multiprovider:
                menu = QMenu(plugin_name, self._qt_window)
                self._plugin_dock_widget_menu.addMenu(menu)
            else:
                menu = self._plugin_dock_widget_menu

            for wdg_name in widgets:
                key = (plugin_name, wdg_name)
                if multiprovider:
                    action = QAction(wdg_name, parent=self._qt_window)
                else:
                    full_name = plugins.menu_item_template.format(*key)
                    action = QAction(full_name, parent=self._qt_window)

                def _add_widget(*args, key=key, hook_type=hook_type):
                    if hook_type == 'dock':
                        self.add_plugin_dock_widget(*key)
                    else:
                        self._add_plugin_function_widget(*key)

                menu.addAction(action)
                action.triggered.connect(_add_widget)

        self.plugins_menu.addMenu(self._plugin_dock_widget_menu)
Exemplo n.º 29
0
    def _display_roi_context_menu(self, roi_index):

        def delete_roi(event):
            self._dc.remove_subset_group(self._dc.subset_groups[roi_index])

        context_menu = QMenu()
        action = QAction("Delete ROI", context_menu)
        action.triggered.connect(delete_roi)
        context_menu.addAction(action)
        pos = self._viewer.mapToParent(QCursor().pos())
        context_menu.exec_(pos)
Exemplo n.º 30
0
 def openWidgetMenu(self,position):
     indexes = self.ui.treeWidget_2.selectedIndexes()
     item = self.ui.treeWidget_2.itemAt(position)
     #item = self.ui.listWidget.itemAt(position)
     if len(indexes) > 0:
         menu = QMenu()
         menu.addAction(QAction("Delete", menu,checkable = True))#This function is perhaps useless
         #menu.triggered.connect(self.eraseItem)
         item = self.ui.treeWidget_2.itemAt(position)
         #collec = str(item.text())
         menu.triggered.connect(lambda action: self.ListMethodSelected(action, item))
     menu.exec_(self.ui.treeWidget_2.viewport().mapToGlobal(position))
Exemplo n.º 31
0
 def _make_export_button(self):
     """
     Makes the export button menu, containing a list of export
     types
     :return: The export button menu
     """
     export_button = QPushButton("Export")
     export_menu = QMenu()
     for text, extension in EXPORT_TYPES:
         export_menu.addAction(text, lambda ext=extension: self.presenter.export_plots_called(ext))
     export_button.setMenu(export_menu)
     return export_button
Exemplo n.º 32
0
    def contextMenuEvent(self, event):
        """也叫弹出式菜单, 就是右键单击弹出"""
        cmenu = QMenu(self)

        newAct = cmenu.addAction('New')
        openAct = cmenu.addAction('Open')
        quitAct = cmenu.addAction('Quit')

        # 从event获取位置信息, 通过mapToGlobal
        action = cmenu.exec_(self.mapToGlobal(event.pos()))
        if action == quitAct:
            qApp.quit()
Exemplo n.º 33
0
 def _make_export_button(self):
     """
     Makes the export button menu, containing a list of export
     types
     :return: The export button menu
     """
     export_button = QPushButton("Export")
     export_menu = QMenu()
     for text, extension in EXPORT_TYPES:
         export_menu.addAction(text, lambda ext=extension: self.presenter.export_plots_called(ext))
     export_button.setMenu(export_menu)
     return export_button
Exemplo n.º 34
0
    def _init_ui(self):
        loadUi(os.path.abspath(
            os.path.join(os.path.dirname(__file__),
                         ".", "model_editor.ui")), self)

        # Populate the add mode button with a dropdown containing available
        # fittable model objects
        self.add_model_button.setPopupMode(QToolButton.InstantPopup)
        models_menu = QMenu(self.add_model_button)
        self.add_model_button.setMenu(models_menu)

        for k, v in MODELS.items():
            action = QAction(k, models_menu)
            action.triggered.connect(lambda x, m=v: self._add_fittable_model(m))
            models_menu.addAction(action)

        self.fit_model_thread = None

        # Initially hide the model editor tools until user has selected an
        # editable model spectrum object
        self.editor_holder_widget.setHidden(True)
        self.setup_holder_widget.setHidden(False)

        self.equation_edit_button.clicked.connect(
            self._on_equation_edit_button_clicked)
        self.new_model_button.clicked.connect(self._on_create_new_model)
        self.remove_model_button.clicked.connect(self._on_remove_model)

        self.advanced_settings_button.clicked.connect(
            lambda: ModelAdvancedSettingsDialog(self, self).exec())

        self.save_model_button.clicked.connect(self._on_save_model)
        self.load_model_button.clicked.connect(self._on_load_from_file)

        self._data_item_proxy_model = DataItemProxyModel()
        self._data_item_proxy_model.setSourceModel(self.hub.model)
        self.data_selection_combo.setModel(self._data_item_proxy_model)
        self.data_selection_combo.currentIndexChanged.connect(self._redraw_model)

        # When a plot data item is select, get its model editor model
        # representation
        self.hub.workspace.current_selected_changed.connect(
            self._on_plot_item_selected)

        # When the plot window changes, reset model editor
        self.hub.workspace.mdi_area.subWindowActivated.connect(self._on_new_plot_activated)

        # Listen for when data items are added to internal model
        self.hub.model.data_added.connect(self._on_data_item_added)

        # Connect the fit model button
        self.fit_button.clicked.connect(self._on_fit_clicked)
Exemplo n.º 35
0
    def _init_ui(self):
        loadUi(
            os.path.abspath(
                os.path.join(os.path.dirname(__file__), ".",
                             "model_editor.ui")), self)

        # Populate the add mode button with a dropdown containing available
        # fittable model objects
        self.add_model_button.setPopupMode(QToolButton.InstantPopup)
        models_menu = QMenu(self.add_model_button)
        self.add_model_button.setMenu(models_menu)

        for k, v in MODELS.items():
            action = QAction(k, models_menu)
            action.triggered.connect(
                lambda x, m=v: self._add_fittable_model(m))
            models_menu.addAction(action)

        # Initially hide the model editor tools until user has selected an
        # editable model spectrum object
        self.editor_holder_widget.setHidden(True)
        self.setup_holder_widget.setHidden(False)

        self.equation_edit_button.clicked.connect(
            self._on_equation_edit_button_clicked)
        self.new_model_button.clicked.connect(self._on_create_new_model)
        self.remove_model_button.clicked.connect(self._on_remove_model)

        self.advanced_settings_button.clicked.connect(
            lambda: ModelAdvancedSettingsDialog(self, self).exec())

        self.save_model_button.clicked.connect(self._on_save_model)
        self.load_model_button.clicked.connect(self._on_load_from_file)

        self.data_selection_combo.setModel(self.hub.model)
        self.data_selection_combo.currentIndexChanged.connect(
            self._redraw_model)

        # When a plot data item is select, get its model editor model
        # representation
        self.hub.workspace.current_selected_changed.connect(
            self._on_plot_item_selected)

        # When the plot window changes, reset model editor
        self.hub.workspace.mdi_area.subWindowActivated.connect(
            self._on_new_plot_activated)

        # Listen for when data items are added to internal model
        self.hub.model.data_added.connect(self._on_data_item_added)

        # Connect the fit model button
        self.fit_button.clicked.connect(self._on_fit_clicked)
Exemplo n.º 36
0
class WorkflowableROI(ROI):
    # FIXME: do we still want this for our (e.g.) CorrelationStage process_actions???
    def __init__(self, *args, **kwargs):
        super(WorkflowableROI, self).__init__(*args, **kwargs)
        self.operation = ROIOperation(self)
        self._param = None

    def parameter(self) -> Parameter:
        raise NotImplementedError

    def getMenu(self):
        if self.menu is None:
            self.menu = QMenu()
            self.menu.setTitle("ROI")
            if self.removable:  # FIXME: if the removable attr is changed, the menu will not react and remAct won't show
                remAct = QAction("Remove ROI", self.menu)
                remAct.triggered.connect(self.removeClicked)
                self.menu.addAction(remAct)
                self.menu.remAct = remAct
            editAct = QAction("Edit ROI", self.menu)
            editAct.triggered.connect(self.edit_parameters)
            self.menu.addAction(editAct)
            self.menu.editAct = editAct
        self.menu.setEnabled(True)
        return self.menu

    def contextMenuEnabled(self):
        return True

    def edit_parameters(self):
        class DefocusParameterTree(QWidget):
            def __init__(self, *args, **kwargs):
                super(DefocusParameterTree, self).__init__(*args, **kwargs)
                self.setLayout(QVBoxLayout())
                self.parameter_tree = ParameterTree()
                self.layout().addWidget(self.parameter_tree)
                self.layout().setContentsMargins(0, 0, 0, 0)

            def setParameters(self, *args, **kwargs):
                self.parameter_tree.setParameters(*args, **kwargs)

        # self.parameter_tree = DefocusParameterTree()
        self.parameter_tree = DefocusParameterTree()
        self.parameter_tree.setParameters(self.parameter())
        self.parameter_tree.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)
        # self.parameter_tree = QLabel('blah')
        self.parameter_tree.show()
        self.parameter_tree.activateWindow()
        self.parameter_tree.raise_()
        self.parameter_tree.move(QCursor().pos())
        self.parameter_tree.setFocus(Qt.PopupFocusReason)
        self.parameter_tree.resize(QSize(300, 400))
Exemplo n.º 37
0
 def context_menu(self):
     try:
         menu = super(PyDMRelatedDisplayButton, self).context_menu()
     except:
         menu = QMenu(self)
     if len(menu.findChildren(QAction)) > 0:
         menu.addSeparator()
     if len(self.filenames) <= 1:
         menu.addAction(self.open_in_new_window_action)
         return menu
     sub_menu = menu.addMenu("Open in New Window")
     self._assemble_menu(sub_menu, target=self.NEW_WINDOW)
     return menu
Exemplo n.º 38
0
 def openWidgetMenu(self,position):
     indexes = self.ui.treeWidget_2.selectedIndexes()
     item = self.ui.treeWidget_2.itemAt(position)
     if item == None:
         return
     #item = self.ui.listWidget.itemAt(position)
     if len(indexes) > 0:
         menu = QMenu()
         menu.addAction(QAction("Delete", menu,checkable = True))#This function is perhaps useless
         #menu.triggered.connect(self.eraseItem)
         item = self.ui.treeWidget_2.itemAt(position)
         #collec = str(item.text())
         menu.triggered.connect(lambda action: self.ListMethodSelected(action, item))
     menu.exec_(self.ui.treeWidget_2.viewport().mapToGlobal(position))
Exemplo n.º 39
0
 def build_context_menu(self, index):
     """Build context menu for test item that given index points to."""
     contextMenu = QMenu(self)
     if self.isExpanded(index):
         menuItem = create_action(self, _('Collapse'),
                                  triggered=lambda: self.collapse(index))
     else:
         menuItem = create_action(self, _('Expand'),
                                  triggered=lambda: self.expand(index))
         menuItem.setEnabled(self.model().hasChildren(index))
     contextMenu.addAction(menuItem)
     menuItem = create_action(
             self, _('Go to definition'),
             triggered=lambda: self.go_to_test_definition(index))
     test_location = self.model().data(index, Qt.UserRole)
     menuItem.setEnabled(test_location[0] is not None)
     contextMenu.addAction(menuItem)
     return contextMenu
Exemplo n.º 40
0
    def _dict_to_menu(self, menu_dict,  menu_widget=None):
        '''Stolen shamelessly from specviz. Thanks!'''
        if not menu_widget:
            menu_widget = QMenu()
        for k, v in menu_dict.items():
            if isinstance(v, dict):
                new_menu = menu_widget.addMenu(k)
                self._dict_to_menu(v, menu_widget=new_menu)
            else:
                act = QAction(k, menu_widget)

                if isinstance(v, list):
                    if v[0] == 'checkable':
                        v = v[1]
                        act.setCheckable(True)
                        act.setChecked(False)

                act.triggered.connect(v)
                menu_widget.addAction(act)
        return menu_widget
Exemplo n.º 41
0
 def _add_axes_scale_menu(self, menu):
     """Add the Axes scale options menu to the given menu"""
     axes_menu = QMenu("Axes", menu)
     axes_actions = QActionGroup(axes_menu)
     current_scale_types = self._get_axes_scale_types()
     for label, scale_types in iteritems(AXES_SCALE_MENU_OPTS):
         action = axes_menu.addAction(label, partial(self._quick_change_axes, scale_types))
         if current_scale_types == scale_types:
             action.setCheckable(True)
             action.setChecked(True)
         axes_actions.addAction(action)
     menu.addMenu(axes_menu)
Exemplo n.º 42
0
    def initialize_toolbar(self):
        """

        """
        # Merge the main tool bar and the plot tool bar to get back some
        # real estate
        self.current_workspace.addToolBar(
            self.current_workspace.current_plot_window.tool_bar)
        self.current_workspace.main_tool_bar.setIconSize(QSize(15, 15))

        # Hide the first five actions in the default specviz tool bar
        for act in self.current_workspace.main_tool_bar.actions()[:6]:
            act.setVisible(False)

        # Hide the tabs of the mdiarea in specviz.
        self.current_workspace.mdi_area.setViewMode(QMdiArea.SubWindowView)
        self.current_workspace.current_plot_window.setWindowFlags(Qt.FramelessWindowHint)
        self.current_workspace.current_plot_window.showMaximized()

        if self._layout is not None:
            cube_ops = QAction(QIcon(":/icons/cube.svg"), "Cube Operations",
                               self.current_workspace.main_tool_bar)
            self.current_workspace.main_tool_bar.addAction(cube_ops)
            self.current_workspace.main_tool_bar.addSeparator()

            button = self.current_workspace.main_tool_bar.widgetForAction(cube_ops)
            button.setPopupMode(QToolButton.InstantPopup)
            menu = QMenu(self.current_workspace.main_tool_bar)
            button.setMenu(menu)

            # Create operation actions
            menu.addSection("2D Operations")

            act = QAction("Simple Linemap", self)
            act.triggered.connect(lambda: simple_linemap(self))
            menu.addAction(act)

            act = QAction("Fitted Linemap", self)
            act.triggered.connect(lambda: fitted_linemap(self))
            menu.addAction(act)

            menu.addSection("3D Operations")

            act = QAction("Fit Spaxels", self)
            act.triggered.connect(lambda: fit_spaxels(self))
            menu.addAction(act)

            act = QAction("Spectral Smoothing", self)
            act.triggered.connect(lambda: spectral_smoothing(self))
            menu.addAction(act)
Exemplo n.º 43
0
    def _make_sort_button(self):
        """
        Make the sort button, with separate groups for ascending and
        descending, sorting by name or last shown
        :return: The sort menu button
        """
        sort_button = QPushButton("Sort")
        sort_menu = QMenu()

        ascending_action = QAction("Ascending", sort_menu, checkable=True)
        ascending_action.setChecked(True)
        ascending_action.toggled.connect(self.presenter.set_sort_order)
        descending_action = QAction("Descending", sort_menu, checkable=True)

        order_group = QActionGroup(sort_menu)
        order_group.addAction(ascending_action)
        order_group.addAction(descending_action)

        number_action = QAction("Number", sort_menu, checkable=True)
        number_action.setChecked(True)
        number_action.toggled.connect(lambda: self.presenter.set_sort_type(Column.Number))
        name_action = QAction("Name", sort_menu, checkable=True)
        name_action.toggled.connect(lambda: self.presenter.set_sort_type(Column.Name))
        last_active_action = QAction("Last Active", sort_menu, checkable=True)
        last_active_action.toggled.connect(lambda: self.presenter.set_sort_type(Column.LastActive))

        sort_type_group = QActionGroup(sort_menu)
        sort_type_group.addAction(number_action)
        sort_type_group.addAction(name_action)
        sort_type_group.addAction(last_active_action)

        sort_menu.addAction(ascending_action)
        sort_menu.addAction(descending_action)
        sort_menu.addSeparator()
        sort_menu.addAction(number_action)
        sort_menu.addAction(name_action)
        sort_menu.addAction(last_active_action)

        sort_button.setMenu(sort_menu)
        return sort_button
Exemplo n.º 44
0
    def _make_context_menu(self):
        """
        Makes the context menu with options relating to plots
        :return: The context menu, and export sub-menu with a list of
                 export types
        """
        context_menu = QMenu()
        context_menu.addAction("Show", self.presenter.show_multiple_selected)
        context_menu.addAction("Hide", self.presenter.hide_selected_plots)
        context_menu.addAction("Delete", self.presenter.close_action_called)
        context_menu.addAction("Rename", self.rename_selected_in_context_menu)

        export_menu = context_menu.addMenu("Export")
        for text, extension in EXPORT_TYPES:
            export_menu.addAction(text, lambda ext=extension: self.presenter.export_plots_called(ext))

        return context_menu, export_menu
Exemplo n.º 45
0
 def _rebuild_menu(self):
     if not any(self._filenames):
         self._filenames = []
     if not any(self._titles):
         self._titles = []
     if len(self._filenames) == 0:
         self.setEnabled(False)
     if len(self._filenames) <= 1:
         self.setMenu(None)
         self._menu_needs_rebuild = False
         return
     menu = QMenu(self)
     for i, filename in enumerate(self._filenames):
         if i >= len(self._titles):
             title = filename
         else:
             title = self._titles[i]
         action = menu.addAction(title)
         macros = ""
         if i < len(self._macros):
             macros = self._macros[i]
         action.triggered.connect(partial(self.open_display, filename, macros, target=None))
     self.setMenu(menu)
     self._menu_needs_rebuild = False
Exemplo n.º 46
0
class SofQView(MplGraphicsView):
    """
    Graphics view for S(Q)
    """
    # boundary moving signal (1) int for left/right boundary indicator (2)
    boundaryMoveSignal = Signal(int, float)

    # resolution of boundary indicator to be selected
    IndicatorResolution = 0.01

    def __init__(self, parent):
        """
        Initialization
        :param parent:t
        """
        self._myParent = parent

        MplGraphicsView.__init__(self, parent)

        # declare event handling to indicators
        self._myCanvas.mpl_connect('button_press_event', self.on_mouse_press_event)
        self._myCanvas.mpl_connect('button_release_event', self.on_mouse_release_event)
        self._myCanvas.mpl_connect('motion_notify_event', self.on_mouse_motion)

        self._mainApp = None

        # dictionary to record all the plots, key: (usually) SofQ's name, value: plot ID
        self._sqLineDict = dict()
        # S(Q) plot's information including color, marker and etc.
        self._sqPlotInfoDict = dict()

        # list of SofQ that are plot on the canvas
        self._shownSQNameList = list()

        # link signal
        # self.boundaryMoveSignal.connect(self._myParent.update_sq_boundary)

        # declare class variables for moving boundary
        self._showBoundary = False
        self._leftID = None
        self._rightID = None

        self._selectedBoundary = 0
        self._prevCursorPos = None

        return

    def get_plot_info(self, sofq_name):
        """
        get the information of a plot including color, marker and etc.
        :param sofq_name:
        :return:
        """
        # check
        assert isinstance(sofq_name, str), 'SofQ {0} must be a string but not a {1}'.format(sofq_name, type(sofq_name))

        if sofq_name not in self._sqPlotInfoDict:
            raise RuntimeError('SofQ-view does not have S(Q) plot {0}'.format(sofq_name))

        # return
        return self._sqPlotInfoDict[sofq_name]

    def get_shown_sq_names(self):
        """
        get the names of S(q) workspaces that are shown on the canvas
        Returns
        -------

        """
        return self._shownSQNameList[:]

    def is_boundary_shown(self):
        """

        Returns
        -------

        """
        return self._showBoundary

    def is_on_canvas(self, sq_name):
        """
        check whether an S(Q) is on canvas now
        Args:
            sq_name:

        Returns: boolean. True if on canvas; Otherwise False

        """
        # check input
        assert isinstance(sq_name, str), 'SofQ name %s must be a string but not of type %s.' \
                                         '' % (str(sq_name), str(type(sq_name)))

        # return
        return sq_name in self._sqLineDict

    def move_left_indicator(self, displacement, relative):
        """

        Args:
            displacement:
            relative:

        Returns:

        """
        # check
        assert isinstance(displacement, float)
        assert isinstance(relative, bool)

        if relative:
            self.move_indicator(self._leftID, displacement)
        else:
            self.set_indicator_position(self._leftID, displacement, 0)

        return

    def move_right_indicator(self, displacement, relative):
        """

        Args:
            displacement:
            relative:

        Returns:

        """
        # check
        assert isinstance(displacement, float)
        assert isinstance(relative, bool)

        if relative:
            self.move_indicator(self._rightID, displacement)
        else:
            self.set_indicator_position(self._rightID, displacement, 0)

        return

    def on_mouse_motion(self, event):
        """

        Returns
        -------

        """
        # ignore if boundary is not shown
        if not self._showBoundary:
            return

        # ignore if no boundary is selected
        if self._selectedBoundary == 0:
            return
        elif self._selectedBoundary > 2:
            raise RuntimeError('Impossible to have selected boundary mode %d' % self._selectedBoundary)

        cursor_pos = event.xdata

        # ignore if the cursor is out of canvas
        if cursor_pos is None:
            return

        cursor_displace = cursor_pos - self._prevCursorPos

        left_bound_pos = self.get_indicator_position(self._leftID)[0]
        right_bound_pos = self.get_indicator_position(self._rightID)[0]

        x_range = self.getXLimit()
        resolution = (x_range[1] - x_range[0]) * self.IndicatorResolution

        if self._selectedBoundary == 1:
            # left boundary
            new_left_bound = left_bound_pos + cursor_displace

            # return if the left boundary is too close to right
            if new_left_bound > right_bound_pos - resolution * 5:
                return

            # move left boundary
            self.move_indicator(self._leftID, cursor_displace, 0)

            # signal main
            self.boundaryMoveSignal.emit(1, new_left_bound)

        else:
            # right boundary
            new_right_bound = right_bound_pos + cursor_displace

            # return if the right boundary is too close or left to the left boundary
            if new_right_bound < left_bound_pos + resolution * 5:
                return

            # move right boundary
            self.move_indicator(self._rightID, cursor_displace, 0)

            # emit signal to the main app
            self.boundaryMoveSignal.emit(2, new_right_bound)

        # update cursor position
        self._prevCursorPos = cursor_pos

        return

    def on_mouse_press_event(self, event):
        """
        Handle mouse pressing event
        (1) left mouse: in show-boundary mode, check the action to select a boundary indicator
        (2) right mouse: pop up the menu
        Returns
        -------

        """
        # get the button
        button = event.button

        if button == 3:
            # right button:
            # Pop-out menu
            self.menu = QMenu(self)

            if self.get_canvas().is_legend_on:
                # figure has legend: remove legend
                action1 = QAction('Hide legend', self)
                action1.triggered.connect(self._myCanvas.hide_legend)

                action2 = QAction('Legend font larger', self)
                action2.triggered.connect(self._myCanvas.increase_legend_font_size)

                action3 = QAction('Legend font smaller', self)
                action3.triggered.connect(self._myCanvas.decrease_legend_font_size)

                self.menu.addAction(action2)
                self.menu.addAction(action3)

            else:
                # figure does not have legend: add legend
                action1 = QAction('Show legend', self)
                action1.triggered.connect(self._myCanvas.show_legend)

            self.menu.addAction(action1)
            # pop up menu
            self.menu.popup(QCursor.pos())

            return
        # END-IF

        # ignore if boundary is not shown and the pressed mouse button is left or middle
        if not self._showBoundary:
            return

        # get mouse cursor x position
        mouse_x_pos = event.xdata
        if mouse_x_pos is None:
            return
        else:
            self._prevCursorPos = mouse_x_pos

        # get absolute resolution
        x_range = self.getXLimit()
        resolution = (x_range[1] - x_range[0]) * self.IndicatorResolution

        # see whether it is close enough to any boundary
        left_bound_pos = self.get_indicator_position(self._leftID)[0]
        right_bound_pos = self.get_indicator_position(self._rightID)[0]
        if abs(mouse_x_pos - left_bound_pos) < resolution:
            self._selectedBoundary = 1
        elif abs(mouse_x_pos - right_bound_pos) < resolution:
            self._selectedBoundary = 2
        else:
            self._selectedBoundary = 0
        # END-IF-ELSE

        return

    def on_mouse_release_event(self, event):
        """
        handling the event that mouse is released
        The operations include setting some flags' values
        :param event:
        :return:
        """
        # ignore if boundary is not shown
        if not self._showBoundary:
            return

        # get mouse cursor position
        self._prevCursorPos = event.xdata

        self._prevCursorPos = None
        self._selectedBoundary = 0

        return

    def plot_sq(self, ws_name,
                sq_y_label=None, reset_color_mark=None,
                color=None, marker=None, plotError=False):
        """Plot S(Q)
        :param sq_name:
        :param sq_y_label: label for Y-axis
        :param reset_color_mark:  boolean to reset color marker
        :param color:
        :param color_marker:
        :return:
        """
        # check whether it is a new plot or an update
        if ws_name in self._sqLineDict:
            # exiting S(q) workspace, do update
            sq_key = self._sqLineDict[ws_name]
            self.updateLine(ikey=sq_key, wkspname=ws_name, wkspindex=0)
        else:
            # new S(Q) plot on the canvas
            assert isinstance(sq_y_label, str), 'S(Q) label {0} must be a string but not a {1}.' \
                                                ''.format(sq_y_label, type(sq_y_label))

            # define color
            if color is None:
                if reset_color_mark:
                    self.reset_line_color_marker_index()
                marker, color = self.getNextLineMarkerColorCombo()
            else:
                marker = None

            # plot
            plot_id = self.add_plot_1d(ws_name, wkspindex=0, color=color, x_label='Q', y_label=sq_y_label,
                                       marker=marker, label=ws_name, plotError=plotError)
            self._sqLineDict[ws_name] = plot_id
            self._sqPlotInfoDict[ws_name] = color, marker
            if ws_name not in self._shownSQNameList:
                self._shownSQNameList.append(ws_name)

        # auto scale
        self.auto_scale_y(room_percent=0.05, lower_boundary=0.)

        return

    def set_main(self, main_app):
        """

        Returns
        -------

        """
        self._mainApp = main_app

        # link signal
        self.boundaryMoveSignal.connect(self._mainApp.update_sq_boundary)

        return

    def remove_sq(self, sq_ws_name):
        """
        Remove 1 S(q) line from canvas
        Args:
            sq_ws_name: workspace name as plot key

        Returns:

        """
        # check whether S(Q) does exist
        assert isinstance(sq_ws_name, str), 'S(Q) workspace name {0} must be a string but not a {1}.' \
                                            ''.format(sq_ws_name, type(sq_ws_name))
        if sq_ws_name not in self._sqLineDict:
            raise RuntimeError('key (SofQ name) {0} does not exist on the S(Q) canvas.'.format(sq_ws_name))

        # retrieve the plot and remove it from the dictionary
        plot_id = self._sqLineDict[sq_ws_name]
        sq_color, sq_marker = self._sqPlotInfoDict[sq_ws_name]

        del self._sqLineDict[sq_ws_name]
        del self._sqPlotInfoDict[sq_ws_name]

        # delete from canvas
        self.remove_line(plot_id)

        # delete from on-show S(q) list
        self._shownSQNameList.remove(sq_ws_name)

        return sq_color, sq_marker

    def reset(self):
        """
        Reset the canvas including removing all the 1-D plots and boundary indicators
        Returns:

        """
        # clear the dictionary and on-show Sq list
        self._sqLineDict.clear()
        self._sqPlotInfoDict.clear()
        self._shownSQNameList = list()

        # clear the image and reset the marker/color scheme
        self.clear_all_lines()
        self.reset_line_color_marker_index()

        # clear the boundary flag
        self._showBoundary = False

        return

    def toggle_boundary(self, q_left, q_right):
        """ Turn on or off the left and right boundary to select Q-range
        Parameters
        ----------
        q_left ::
        q_right ::
        Returns
        -------

        """
        # check
        assert isinstance(q_left, float) and isinstance(q_right, float)
        assert q_left < q_right

        if self._showBoundary:
            # Q-boundary indicator is on. turn off
            self.remove_indicator(self._leftID)
            self.remove_indicator(self._rightID)
            self._leftID = None
            self._rightID = None
            self._showBoundary = False
        else:
            self._leftID = self.add_vertical_indicator(q_left, 'red')
            self._rightID = self.add_vertical_indicator(q_right, 'red')
            self._showBoundary = True

            # reset the x-range
            x_range = self.getXLimit()
            if x_range[0] > q_left - 1:
                self.setXYLimit(xmin=q_left-1)

        # END-IF-ELSE (show boundary)

        return
Exemplo n.º 47
0
class PyDMLineEdit(QLineEdit, TextFormatter, PyDMWritableWidget, DisplayFormat):
    Q_ENUMS(DisplayFormat)
    DisplayFormat = DisplayFormat
    """
    A QLineEdit (writable text field) with support for Channels and more
    from PyDM.
    This widget offers an unit conversion menu when users Right Click
    into it.

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Label
    init_channel : str, optional
        The channel to be used by the widget.
    """

    def __init__(self, parent=None, init_channel=None):
        QLineEdit.__init__(self, parent)
        PyDMWritableWidget.__init__(self, init_channel=init_channel)
        self.app = QApplication.instance()
        self._display = None
        self._scale = 1

        self.returnPressed.connect(self.send_value)
        self.unitMenu = QMenu('Convert Units', self)
        self.create_unit_options()
        self._display_format_type = self.DisplayFormat.Default
        self._string_encoding = "utf_8"
        if utilities.is_pydm_app():
            self._string_encoding = self.app.get_string_encoding()

    @Property(DisplayFormat)
    def displayFormat(self):
        return self._display_format_type

    @displayFormat.setter
    def displayFormat(self, new_type):
        if self._display_format_type != new_type:
            self._display_format_type = new_type
            # Trigger the update of display format
            self.value_changed(self.value)

    def value_changed(self, new_val):
        """
        Receive and update the PyDMLineEdit for a new channel value

        The actual value of the input is saved as well as the type received.
        This also resets the PyDMLineEdit display text using
        :meth:`.set_display`

        Parameters
        ----------
        value: str, float or int
            The new value of the channel
        """
        super(PyDMLineEdit, self).value_changed(new_val)
        self.set_display()

    def send_value(self):
        """
        Emit a :attr:`send_value_signal` to update channel value.

        The text is cleaned of all units, user-formatting and scale values
        before being sent back to the channel. This function is attached the
        ReturnPressed signal of the PyDMLineEdit
        """
        send_value = str(self.text())
        # Clean text of unit string
        if self._show_units and self._unit and self._unit in send_value:
            send_value = send_value[:-len(self._unit)].strip()
        try:
            if self.channeltype not in [str, np.ndarray]:
                scale = self._scale
                if scale is None or scale == 0:
                    scale = 1.0

                if self._display_format_type in [DisplayFormat.Default, DisplayFormat.String]:
                    if self.channeltype == float:
                        num_value = locale.atof(send_value)
                    else:
                        num_value = self.channeltype(send_value)
                    scale = self.channeltype(scale)
                elif self._display_format_type == DisplayFormat.Hex:
                    num_value = int(send_value, 16)
                elif self._display_format_type == DisplayFormat.Binary:
                    num_value = int(send_value, 2)
                elif self._display_format_type in [DisplayFormat.Exponential, DisplayFormat.Decimal]:
                    num_value = locale.atof(send_value)

                num_value = num_value / scale
                self.send_value_signal[self.channeltype].emit(num_value)
            elif self.channeltype == np.ndarray:
                # Arrays will be in the [1.2 3.4 22.214] format
                if self._display_format_type == DisplayFormat.String:
                    self.send_value_signal[str].emit(send_value)
                else:
                    arr_value = list(filter(None, send_value.replace("[", "").replace("]", "").split(" ")))
                    arr_value = np.array(arr_value, dtype=self.subtype)
                    self.send_value_signal[np.ndarray].emit(arr_value)
            else:
                # Channel Type is String
                # Lets just send what we have after all
                self.send_value_signal[str].emit(send_value)
        except ValueError:
            logger.exception("Error trying to set data '{0}' with type '{1}' and format '{2}' at widget '{3}'."
                         .format(self.text(), self.channeltype, self._display_format_type, self.objectName()))

        self.clearFocus()
        self.set_display()

    def write_access_changed(self, new_write_access):
        """
        Change the PyDMLineEdit to read only if write access is denied
        """
        super(PyDMLineEdit, self).write_access_changed(new_write_access)
        self.setReadOnly(not new_write_access)

    def unit_changed(self, new_unit):
        """
        Accept a unit to display with a channel's value

        The unit may or may not be displayed based on the :attr:`showUnits`
        attribute. Receiving a new value for the unit causes the display to
        reset.
        """
        super(PyDMLineEdit, self).unit_changed(new_unit)
        self._scale = 1
        self.create_unit_options()

    def create_unit_options(self):
        """
        Create the menu for displaying possible unit values

        The menu is filled with possible unit conversions based on the
        current PyDMLineEdit. If either the unit is not found in the by
        the :func:`utilities.find_unit_options` function, or, the
        :attr:`.showUnits` attribute is set to False, the menu will tell
        the user that there are no available conversions
        """
        self.unitMenu.clear()
        units = utilities.find_unit_options(self._unit)
        if units and self._show_units:
            for choice in units:
                self.unitMenu.addAction(choice,
                                        partial(
                                            self.apply_conversion,
                                            choice
                                            )
                                        )
        else:
            self.unitMenu.addAction('No Unit Conversions found')

    def apply_conversion(self, unit):
        """
        Convert the current unit to a different one

        This function will attempt to find a scalar to convert the current
        unit type to the desired one and reset the display with the new
        conversion.

        Parameters
        ----------
        unit : str
            String name of desired units
        """
        if not self._unit:
            logger.warning("Warning: Attempting to convert PyDMLineEdit unit, but no initial units supplied.")
            return None

        scale = utilities.convert(str(self._unit), unit)
        if scale:
            self._scale = scale * float(self._scale)
            self._unit = unit
            self.update_format_string()
            self.clearFocus()
            self.set_display()
        else:
            logging.warning("Warning: Attempting to convert PyDMLineEdit unit, but '{0}' can not be converted to '{1}'."
                            .format(self._unit, unit))

    def widget_ctx_menu(self):
        """
        Fetch the Widget specific context menu which will be populated with additional tools by `assemble_tools_menu`.

        Returns
        -------
        QMenu or None
            If the return of this method is None a new QMenu will be created by `assemble_tools_menu`.
        """
        menu = self.createStandardContextMenu()
        menu.addSeparator()
        menu.addMenu(self.unitMenu)
        return menu

    def set_display(self):
        """
        Set the text display of the PyDMLineEdit.

        The original value given by the PV is converted to a text entry based
        on the current settings for scale value, precision, a user-defined
        format, and the current units. If the user is currently entering a
        value in the PyDMLineEdit the text will not be changed.
        """
        if self.value is None:
            return

        if self.hasFocus():
            return

        new_value = self.value

        if self._display_format_type in [DisplayFormat.Default,
                                         DisplayFormat.Decimal,
                                         DisplayFormat.Exponential,
                                         DisplayFormat.Hex,
                                         DisplayFormat.Binary]:
            if not isinstance(new_value, (str, np.ndarray)):
                try:
                    new_value *= self.channeltype(self._scale)
                except TypeError:
                    logger.error("Cannot convert the value '{0}', for channel '{1}', to type '{2}'. ".format(
                        self._scale, self._channel, self.channeltype))

        new_value = parse_value_for_display(value=new_value,  precision=self._prec,
                                            display_format_type=self._display_format_type,
                                            string_encoding=self._string_encoding,
                                            widget=self)

        self._display = str(new_value)

        if self._display_format_type == DisplayFormat.Default:
            if isinstance(new_value, (int, float)):
                self._display = str(self.format_string.format(new_value))
                self.setText(self._display)
                return

        if self._show_units:
            self._display += " {}".format(self._unit)

        self.setText(self._display)

    def focusOutEvent(self, event):
        """
        Overwrites the function called when a user leaves a PyDMLineEdit
        without pressing return.  Resets the value of the text field to the
        current channel value.
        """
        if self._display is not None:
            self.setText(self._display)
        super(PyDMLineEdit, self).focusOutEvent(event)
Exemplo n.º 48
0
class MOSViewerToolbar(BasicToolbar):
    def __init__(self, *args, **kwargs):
        super(MOSViewerToolbar, self).__init__(*args, **kwargs)
        # self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)

        # Define icon path
        icon_path = os.path.join(os.path.dirname(__file__),
                                 'ui', 'icons')

        # Define the toolbar actions
        self.cycle_previous_action = QAction(
            QIcon(os.path.join(icon_path, "Previous-96.png")),
            "Previous", self)
        self.cycle_next_action = QAction(
            QIcon(os.path.join(icon_path, "Next-96.png")),
            "Next", self)

        # Include the dropdown widget
        self.source_select = QComboBox()

        # Add the items to the toolbar
        self.addAction(self.cycle_previous_action)
        self.addAction(self.cycle_next_action)
        self.addWidget(self.source_select)

        # Include a button to open spectrum in specviz
        self.open_specviz = QAction(
            QIcon(os.path.join(icon_path, "External-96.png")),
            "Open in SpecViz", self)

        # Create a tool button to hold the lock axes menu object
        tool_button = QToolButton(self)
        tool_button.setText("Axes Settings")
        tool_button.setIcon(QIcon(os.path.join(icon_path, "Settings-96.png")))
        tool_button.setPopupMode(QToolButton.MenuButtonPopup)

        # Create a menu for the axes settings drop down
        self.settings_menu = QMenu(self)

        # Add lock x axis action
        self.lock_x_action = QAction("Lock X Axis",
                                     self.settings_menu)
        self.lock_x_action.setCheckable(True)

        # Add lock y axis action
        self.lock_y_action = QAction("Lock Y Axis",
                                     self.settings_menu)
        self.lock_y_action.setCheckable(True)

        # Add the actions to the menu
        self.settings_menu.addAction(self.lock_x_action)
        self.settings_menu.addAction(self.lock_y_action)

        # Set the menu object on the tool button
        tool_button.setMenu(self.settings_menu)

        # Create a widget action object to hold the tool button, this way the
        # toolbar behaves the way it's expected to
        tool_button_action = QWidgetAction(self)
        tool_button_action.setDefaultWidget(tool_button)

        self.addAction(tool_button_action)
        self.addSeparator()
        self.addAction(self.open_specviz)
Exemplo n.º 49
0
class BaseTabs(QTabWidget):
    """TabWidget with context menu and corner widgets"""
    sig_close_tab = Signal(int)
    
    def __init__(self, parent, actions=None, menu=None,
                 corner_widgets=None, menu_use_tooltips=False):
        QTabWidget.__init__(self, parent)
        self.setUsesScrollButtons(True)

        # To style tabs on Mac
        if sys.platform == 'darwin':
            self.setObjectName('plugin-tab')

        self.corner_widgets = {}
        self.menu_use_tooltips = menu_use_tooltips
        
        if menu is None:
            self.menu = QMenu(self)
            if actions:
                add_actions(self.menu, actions)
        else:
            self.menu = menu
            
        # Corner widgets
        if corner_widgets is None:
            corner_widgets = {}
        corner_widgets.setdefault(Qt.TopLeftCorner, [])
        corner_widgets.setdefault(Qt.TopRightCorner, [])
        self.browse_button = create_toolbutton(self,
                                          icon=ima.icon('browse_tab'),
                                          tip=_("Browse tabs"))
        self.browse_tabs_menu = QMenu(self)
        self.browse_button.setMenu(self.browse_tabs_menu)
        self.browse_button.setPopupMode(self.browse_button.InstantPopup)
        self.browse_tabs_menu.aboutToShow.connect(self.update_browse_tabs_menu)
        corner_widgets[Qt.TopLeftCorner] += [self.browse_button]

        self.set_corner_widgets(corner_widgets)
        
    def update_browse_tabs_menu(self):
        """Update browse tabs menu"""
        self.browse_tabs_menu.clear()
        names = []
        dirnames = []
        for index in range(self.count()):
            if self.menu_use_tooltips:
                text = to_text_string(self.tabToolTip(index))
            else:
                text = to_text_string(self.tabText(index))
            names.append(text)
            if osp.isfile(text):
                # Testing if tab names are filenames
                dirnames.append(osp.dirname(text))
        offset = None
        
        # If tab names are all filenames, removing common path:
        if len(names) == len(dirnames):
            common = get_common_path(dirnames)
            if common is None:
                offset = None
            else:
                offset = len(common)+1
                if offset <= 3:
                    # Common path is not a path but a drive letter...
                    offset = None
                
        for index, text in enumerate(names):
            tab_action = create_action(self, text[offset:],
                                       icon=self.tabIcon(index),
                                       toggled=lambda state, index=index:
                                               self.setCurrentIndex(index),
                                       tip=self.tabToolTip(index))
            tab_action.setChecked(index == self.currentIndex())
            self.browse_tabs_menu.addAction(tab_action)
        
    def set_corner_widgets(self, corner_widgets):
        """
        Set tabs corner widgets
        corner_widgets: dictionary of (corner, widgets)
        corner: Qt.TopLeftCorner or Qt.TopRightCorner
        widgets: list of widgets (may contains integers to add spacings)
        """
        assert isinstance(corner_widgets, dict)
        assert all(key in (Qt.TopLeftCorner, Qt.TopRightCorner)
                   for key in corner_widgets)
        self.corner_widgets.update(corner_widgets)
        for corner, widgets in list(self.corner_widgets.items()):
            cwidget = QWidget()
            cwidget.hide()
            prev_widget = self.cornerWidget(corner)
            if prev_widget:
                prev_widget.close()
            self.setCornerWidget(cwidget, corner)
            clayout = QHBoxLayout()
            clayout.setContentsMargins(0, 0, 0, 0)
            for widget in widgets:
                if isinstance(widget, int):
                    clayout.addSpacing(widget)
                else:
                    clayout.addWidget(widget)
            cwidget.setLayout(clayout)
            cwidget.show()
            
    def add_corner_widgets(self, widgets, corner=Qt.TopRightCorner):
        self.set_corner_widgets({corner:
                                 self.corner_widgets.get(corner, [])+widgets})
        
    def contextMenuEvent(self, event):
        """Override Qt method"""
        self.setCurrentIndex(self.tabBar().tabAt(event.pos()))
        if self.menu:
            self.menu.popup(event.globalPos())
            
    def mousePressEvent(self, event):
        """Override Qt method"""
        if event.button() == Qt.MidButton:
            index = self.tabBar().tabAt(event.pos())
            if index >= 0:
                self.sig_close_tab.emit(index)
                event.accept()
                return
        QTabWidget.mousePressEvent(self, event)
        
    def keyPressEvent(self, event):
        """Override Qt method"""
        ctrl = event.modifiers() & Qt.ControlModifier
        key = event.key()
        handled = False
        if ctrl and self.count() > 0:
            index = self.currentIndex()
            if key == Qt.Key_PageUp:
                if index > 0:
                    self.setCurrentIndex(index - 1)
                else:
                    self.setCurrentIndex(self.count() - 1)
                handled = True
            elif key == Qt.Key_PageDown:
                if index < self.count() - 1:
                    self.setCurrentIndex(index + 1)
                else:
                    self.setCurrentIndex(0)
                handled = True
        if not handled:
            QTabWidget.keyPressEvent(self, event)
        
    def set_close_function(self, func):
        """Setting Tabs close function
        None -> tabs are not closable"""
        state = func is not None
        if state:
            self.sig_close_tab.connect(func)
        try:
            # Assuming Qt >= 4.5
            QTabWidget.setTabsClosable(self, state)
            self.tabCloseRequested.connect(func)
        except AttributeError:
            # Workaround for Qt < 4.5
            close_button = create_toolbutton(self, triggered=func,
                                             icon=ima.icon('fileclose'),
                                             tip=_("Close current tab"))
            self.setCornerWidget(close_button if state else None)
Exemplo n.º 50
0
    def right_click(self, position=None):
        _duplicate_row = -1
        _plot_sofq = -1
        _remove_row = -1
        _new_row = -1
        _copy = -1
        _paste = -1
        _cut = -1
        _refresh_table = -1
        _clear_table = -1
        # _import = -1
        # _export = -1        _check_all = -1
        _uncheck_all = -1
        _undo = -1
        _redo = -1
        _plot_sofq_diff_first_run_row = -1
        _plot_sofq_diff_average_row = -1
        _plot_cryostat = -1
        _plot_furnace = -1
        _invert_selection = -1

        menu = QMenu(self.main_window)

        if self.main_window.table_selection_buffer == {}:
            paste_status = False
        else:
            paste_status = True

        if (self.main_window.postprocessing_ui.table.rowCount() > 0):
            _undo = menu.addAction("Undo")
            _undo.setEnabled(self.main_window.undo_button_enabled)
            _redo = menu.addAction("Redo")
            _redo.setEnabled(self.main_window.redo_button_enabled)
            menu.addSeparator()
            _copy = menu.addAction("Copy")
            _paste = menu.addAction("Paste")
            self._paste_menu = _paste
            _paste.setEnabled(paste_status)
            _cut = menu.addAction("Clear")
            menu.addSeparator()
            _check_all = menu.addAction("Check All")
            _uncheck_all = menu.addAction("Unchecked All")
            menu.addSeparator()
            _invert_selection = menu.addAction("Inverse Selection")
            menu.addSeparator()

        _new_row = menu.addAction("Insert Blank Row")

        if (self.main_window.postprocessing_ui.table.rowCount() > 0):
            _duplicate_row = menu.addAction("Duplicate Row")
            _remove_row = menu.addAction("Remove Row(s)")

            menu.addSeparator()
            _plot_menu = menu.addMenu('Plot')
            _plot_sofq = _plot_menu.addAction("S(Q) ...")
            _plot_sofq_diff_first_run_row = _plot_menu.addAction("S(Q) Diff (1st run)...")
            _plot_sofq_diff_average_row = _plot_menu.addAction("S(Q) Diff (Avg.)...")

            _temp_menu = _plot_menu.addMenu("Temperature")
            _plot_cryostat = _temp_menu.addAction("Cyrostat...")
            _plot_furnace = _temp_menu.addAction("Furnace...")

            menu.addSeparator()
            _refresh_table = menu.addAction("Refresh/Reset Table")
            _clear_table = menu.addAction("Clear Table")

        action = menu.exec_(QCursor.pos())
        self.current_row = self.current_row()

        if action == _undo:
            self.main_window.action_undo_clicked()
        elif action == _redo:
            self.main_window.action_redo_clicked()
        elif action == _copy:
            self._copy()
        elif action == _paste:
            self._paste()
        elif action == _cut:
            self._cut()
        elif action == _duplicate_row:
            self._duplicate_row()
        elif action == _plot_sofq:
            self._plot_sofq()
        elif action == _plot_sofq_diff_first_run_row:
            self._plot_sofq_diff_first_run_row()
        elif action == _plot_sofq_diff_average_row:
            self._plot_sofq_diff_average_row()
        elif action == _plot_cryostat:
            self._plot_temperature(samp_env_choice='cryostat')
        elif action == _plot_furnace:
            self._plot_temperature(samp_env_choice='furnace')
        elif action == _invert_selection:
            self._inverse_selection()
        elif action == _new_row:
            self._new_row()
        elif action == _remove_row:
            self._remove_selected_rows()
        elif action == _refresh_table:
            self._refresh_table()
        elif action == _clear_table:
            self._clear_table()
        elif action == _check_all:
            self.check_all()
        elif action == _uncheck_all:
            self.uncheck_all()
Exemplo n.º 51
0
    def custom_context_menu(self, position):
        menu_main = QMenu()
        plot = QMenu("Plot...", menu_main)
        plot_line = QAction("Line", plot)
        plot_line.triggered.connect(partial(self.presenter.action_plot, PlotType.LINEAR))

        plot_line_with_yerr = QAction("Line with Y Errors", plot)
        plot_line_with_yerr.triggered.connect(partial(self.presenter.action_plot, PlotType.LINEAR_WITH_ERR))

        plot_scatter = QAction("Scatter", plot)
        plot_scatter.triggered.connect(partial(self.presenter.action_plot, PlotType.SCATTER))

        plot_line_and_points = QAction("Line + Symbol", plot)
        plot_line_and_points.triggered.connect(partial(self.presenter.action_plot, PlotType.LINE_AND_SYMBOL))

        plot.addAction(plot_line)
        plot.addAction(plot_line_with_yerr)
        plot.addAction(plot_scatter)
        plot.addAction(plot_line_and_points)
        menu_main.addMenu(plot)

        copy_bin_values = QAction(self.COPY_ICON, "Copy", menu_main)
        copy_bin_values.triggered.connect(self.presenter.action_copy_bin_values)

        set_as_x = QAction("Set as X", menu_main)
        set_as_x.triggered.connect(self.presenter.action_set_as_x)

        set_as_y = QAction("Set as Y", menu_main)
        set_as_y.triggered.connect(self.presenter.action_set_as_y)

        set_as_none = QAction("Set as None", menu_main)
        set_as_none.triggered.connect(self.presenter.action_set_as_none)

        statistics_on_columns = QAction("Statistics on Columns", menu_main)
        statistics_on_columns.triggered.connect(self.presenter.action_statistics_on_columns)

        hide_selected = QAction("Hide Selected", menu_main)
        hide_selected.triggered.connect(self.presenter.action_hide_selected)

        show_all_columns = QAction("Show All Columns", menu_main)
        show_all_columns.triggered.connect(self.presenter.action_show_all_columns)

        sort_ascending = QAction("Sort Ascending", menu_main)
        sort_ascending.triggered.connect(partial(self.presenter.action_sort_ascending, Qt.AscendingOrder))

        sort_descending = QAction("Sort Descending", menu_main)
        sort_descending.triggered.connect(partial(self.presenter.action_sort_ascending, Qt.DescendingOrder))

        menu_main.addAction(copy_bin_values)
        menu_main.addAction(self.make_separator(menu_main))
        menu_main.addAction(set_as_x)
        menu_main.addAction(set_as_y)

        marked_y_cols = self.presenter.get_columns_marked_as_y()
        num_y_cols = len(marked_y_cols)

        # If any columns are marked as Y then generate the set error menu
        if num_y_cols > 0:
            menu_set_as_y_err = QMenu("Set error for Y...")
            for col in range(num_y_cols):
                set_as_y_err = QAction("Y{}".format(col), menu_main)
                # the column index of the column relative to the whole table, this is necessary
                # so that later the data of the column marked as error can be retrieved
                real_column_index = marked_y_cols[col]
                # col here holds the index in the LABEL (multiple Y columns have labels Y0, Y1, YN...)
                # this is NOT the same as the column relative to the WHOLE table
                set_as_y_err.triggered.connect(partial(self.presenter.action_set_as_y_err, real_column_index, col))
                menu_set_as_y_err.addAction(set_as_y_err)
            menu_main.addMenu(menu_set_as_y_err)

        menu_main.addAction(set_as_none)
        menu_main.addAction(self.make_separator(menu_main))
        menu_main.addAction(statistics_on_columns)
        menu_main.addAction(self.make_separator(menu_main))
        menu_main.addAction(hide_selected)
        menu_main.addAction(show_all_columns)
        menu_main.addAction(self.make_separator(menu_main))
        menu_main.addAction(sort_ascending)
        menu_main.addAction(sort_descending)

        menu_main.exec_(self.mapToGlobal(position))
Exemplo n.º 52
0
 def openMenu(self,position):
     indexes = self.ui.treeWidget.selectedIndexes()
     item = self.ui.treeWidget.itemAt(position)
     db_origin = ""
     #if item.parent():
      #   db_origin = item.parent().text(0)
     collec = str(item.text(0).encode("utf-8"))
     if len(indexes) > 0:
         level = 0
         index = indexes[0]
         while index.parent().isValid():
             index = index.parent()
             level = level + 1
         menu = QMenu()
         #print((collec, db_origin))
         if level ==0:
             pass
         else:
             #keyarray = GetKeys(collec, db_origin)
             #if "Open" in keyarray:
             if self.ui.combobox.currentText()==u"K线":
                 menu.addAction(QAction("Kline", menu, checkable=True))
                 menu.addAction(QAction("Open", menu, checkable=True))
                 menu.addAction(QAction("Close", menu, checkable=True))#open up different menu with different kind of graphs
                 menu.addAction(QAction("High", menu, checkable=True))
                 menu.addAction(QAction("Low", menu, checkable=True))
                 menu.addAction(QAction("Volume", menu, checkable=True))
                 #menu.addAction(QAction("P_change", menu, checkable=True))
                 #menu.addAction(QAction("Turnover",menu,checkable=True))
             if self.ui.combobox.currentText()==u"复权":
                 menu.addAction(QAction("Kline", menu, checkable=True))
                 menu.addAction(QAction("Open", menu, checkable=True))
                 menu.addAction(QAction("Close", menu, checkable=True))
                 menu.addAction(QAction("High", menu, checkable=True))
                 menu.addAction(QAction("Low", menu, checkable=True))
                 menu.addAction(QAction("Volume", menu, checkable=True))
                 menu.addAction(QAction("Amount", menu, checkable=True))
             if self.ui.combobox.currentText()==u"分笔数据":
                 menu.addAction(QAction("分笔", menu, checkable=True))
             if self.ui.combobox.currentText()==u"历史分钟":
                 menu.addAction(QAction("Kline", menu, checkable=True))
                 menu.addAction(QAction("Open", menu, checkable=True))
                 menu.addAction(QAction("Close", menu, checkable=True))
                 menu.addAction(QAction("High", menu, checkable=True))
                 menu.addAction(QAction("Low", menu, checkable=True))
                 menu.addAction(QAction("Volume", menu, checkable=True))
                 menu.addAction(QAction("Amount", menu, checkable=True))
             if self.ui.combobox.currentText()==u"十大股东":
                 menu.addAction(QAction("季度饼图", menu, checkable=True))
                 #menu.addAction(QAction("持股比例", menu, checkable=True))
             #for g in keyarray:
             #menu.addAction(QAction(g, menu, checkable=True))
     menu.triggered.connect(lambda action: self.methodSelected(action, collec))
     menu.exec_(self.ui.treeWidget.viewport().mapToGlobal(position))
Exemplo n.º 53
0
class GofRView(MplGraphicsView):
    """
    Graphics view for G(R)
    """

    def __init__(self, parent):
        """
        Initialization
        """
        MplGraphicsView.__init__(self, parent)

        # class variable containers
        self._grDict = dict()

        self._colorList = ['black', 'red', 'blue', 'green', 'brown', 'orange']
        self._colorIndex = 0

        # define the event handlers to the mouse actions
        self._myCanvas.mpl_connect('button_press_event', self.on_mouse_press_event)
        # self._myCanvas.mpl_connect('button_release_event', self.on_mouse_release_event)
        # self._myCanvas.mpl_connect('motion_notify_event', self.on_mouse_motion)

        # class variable
        self._minY = None
        self._maxY = None

        # variable
        self._isLegendOn = False

        return

    def on_mouse_press_event(self, event):
        """
        Event handling for mouse press action
        Args:
            event:

        Returns:

        """
        # get the button and position information.
        curr_x = event.xdata
        curr_y = event.ydata
        if curr_x is None or curr_y is None:
            # outside of canvas
            return

        button = event.button
        if button == 1:
            # left button: no operation
            pass

        elif button == 3:
            # right button:
            # Pop-out menu
            self.menu = QMenu(self)

            if self.get_canvas().is_legend_on:
                # figure has legend: remove legend
                action1 = QAction('Hide legend', self)
                action1.triggered.connect(self._myCanvas.hide_legend)

                action2 = QAction('Legend font larger', self)
                action2.triggered.connect(self._myCanvas.increase_legend_font_size)

                action3 = QAction('Legend font smaller', self)
                action3.triggered.connect(self._myCanvas.decrease_legend_font_size)

                self.menu.addAction(action2)
                self.menu.addAction(action3)

            else:
                # figure does not have legend: add legend
                action1 = QAction('Show legend', self)
                action1.triggered.connect(self._myCanvas.show_legend)

            self.menu.addAction(action1)

            # pop up menu
            self.menu.popup(QCursor.pos())
        # END-IF-ELSE

        return

    def plot_gr(self, plot_key, ws_name, plotError=False, color='black', style='.', marker=None,
                alpha=1., label=None):
        """
        Plot G(r)
        :param plot_key: a key to the current plot
        :param vec_r: numpy array for R
        :param vec_g: numpy array for G(r)
        :param vec_e: numpy array for G(r) error
        :param plot_error:
        :param color:
        :param style:
        :param marker:
        :param alpha:
        :param label: label for the line to plot
        :return:
        """
        # q_min = 10., q_max = 50.
        # alpha = 1. - (q_now - q_min)/(q_max - q_min)
        if not label:
            label = str(plot_key)

        line_id = self.add_plot_1d(ws_name, wkspindex=0, marker=marker,
                                   color=color, line_style=style, alpha=alpha,
                                   label=label, x_label=r'r ($\AA$)', plotError=plotError)
        self._colorIndex += 1
        self._grDict[str(plot_key)] = line_id

        # check the low/max
        self.auto_scale_y()

    def _reset_y_range(self, vec_gr):
        """
        reset the Y range
        :param vec_gr:
        :return:
        """
        this_min = min(vec_gr)
        this_max = max(vec_gr)

        if self._minY is None or this_min < self._minY:
            self._minY = this_min

        if self._maxY is None or this_max > self._maxY:
            self._maxY = this_max

        return

    def _auto_rescale_y(self):
        """

        :return:
        """
        if self._minY is None or self._maxY is None:
            return

        delta_y = self._maxY - self._minY

        lower_boundary = self._minY - delta_y * 0.05
        upper_boundary = self._maxY + delta_y * 0.05

        self.setXYLimit(ymin=lower_boundary, ymax=upper_boundary)

        return

    def has_gr(self, gr_ws_name):
        """Check whether a plot of G(r) exists on the canvas
        :param gr_ws_name:
        :return:
        """
        return gr_ws_name in self._grDict

    def get_current_grs(self):
        """
        list all the G(r) plotted on the figure now
        :return:
        """
        return list(self._grDict.keys())

    def remove_gr(self, plot_key):
        """Remove a plotted G(r) from canvas
        :param plot_key: key to locate the 1-D plot on canvas
        :return: boolean, string (as error message)
        """
        # check
        assert isinstance(plot_key, str), 'Key for the plot must be a string but not %s.' % str(type(plot_key))
        if plot_key not in self._grDict:
            return False, 'Workspace %s cannot be found in GofR dictionary of canvas' % plot_key

        # get line ID
        line_id = self._grDict[plot_key]

        # remove from plot
        self.remove_line(line_id)

        # clean G(r) plot
        del self._grDict[plot_key]

        # reset min and max
        self._minY = None
        self._maxY = None

        return

    def reset_color(self):
        """Reset color scheme
        :return:
        """
        self._colorIndex = 0

    def reset(self):
        """
        Reset the canvas by deleting all lines and clean the dictionary
        Returns:
        """
        # remove all lines and reset marker/color default sequence
        self.clear_all_lines()
        self.reset_line_color_marker_index()
        self._colorIndex = 0

        # clean dictionary
        self._grDict.clear()

        return

    def update_gr(self, plot_key, ws_name, plotError=False):
        """update the value of an existing G(r)
        :param plot_key:
        :param vec_r:
        :param vec_g:
        :param vec_ge:
        :return:
        """
        # check existence
        if plot_key not in self._grDict:
            raise RuntimeError('Plot with key/workspace name {0} does not exist on plot.  Current plots are '
                               '{1}'.format(plot_key, list(self._grDict.keys())))

        # update
        line_key = self._grDict[plot_key]
        self.updateLine(ikey=line_key, wkspname=ws_name)

        # update range
        self.auto_scale_y()

        return
Exemplo n.º 54
0
class PlotWindow(QMdiSubWindow):
    """
    Displayed plotting subwindow available in the ``QMdiArea``.
    """
    window_removed = Signal()
    color_changed = Signal(PlotDataItem, QColor)
    width_changed = Signal(int)

    def __init__(self, model, *args, **kwargs):
        super(PlotWindow, self).__init__(*args, **kwargs)
        # Hide the icon in the title bar
        self.setWindowIcon(qta.icon('fa.circle', opacity=0))

        # The central widget of the sub window will be a main window so that it
        # can support having tab bars
        self._central_widget = QMainWindow()
        self.setWidget(self._central_widget)

        loadUi(os.path.join(os.path.dirname(__file__), "ui", "plot_window.ui"),
               self._central_widget)

        # The central widget of the main window widget will be the plot
        self._model = model
        self._current_item_index = None

        self._plot_widget = PlotWidget(model=self._model)
        self._plot_widget.plotItem.setMenuEnabled(False)

        self._central_widget.setCentralWidget(self._plot_widget)

        # Setup action group for interaction modes
        mode_group = QActionGroup(self.tool_bar)
        mode_group.addAction(self._central_widget.pan_mode_action)
        self._central_widget.pan_mode_action.setChecked(True)
        mode_group.addAction(self._central_widget.zoom_mode_action)

        def _toggle_mode(state):
            view_state = self.plot_widget.plotItem.getViewBox().state.copy()
            view_state.update({'mouseMode': pg.ViewBox.RectMode
                               if state else pg.ViewBox.PanMode})
            self.plot_widget.plotItem.getViewBox().setState(view_state)

        # Setup plot settings options menu
        self.plot_settings_button = self.tool_bar.widgetForAction(
            self._central_widget.plot_settings_action)
        self.plot_settings_button.setPopupMode(QToolButton.InstantPopup)

        self.plot_settings_menu = QMenu(self.plot_settings_button)
        self.plot_settings_button.setMenu(self.plot_settings_menu)

        self.color_change_action = QAction("Line Color")
        self.plot_settings_menu.addAction(self.color_change_action)

        self.line_width_menu = QMenu("Line Widths")
        self.plot_settings_menu.addMenu(self.line_width_menu)

        # Setup the line width plot setting options
        for i in range(1, 4):
            act = QAction(str(i), self.line_width_menu)
            self.line_width_menu.addAction(act)
            act.triggered.connect(lambda *args, size=i:
                                  self._on_change_width(size))

        # Setup connections
        self._central_widget.pan_mode_action.triggered.connect(
            lambda: _toggle_mode(False))
        self._central_widget.zoom_mode_action.triggered.connect(
            lambda: _toggle_mode(True))
        self._central_widget.linear_region_action.triggered.connect(
            self.plot_widget._on_add_linear_region)
        self._central_widget.remove_region_action.triggered.connect(
            self.plot_widget._on_remove_linear_region)
        self.color_change_action.triggered.connect(
            self._on_change_color)
        self._central_widget.export_plot_action.triggered.connect(
            self._on_export_plot)
        self._central_widget.reset_view_action.triggered.connect(
            lambda: self._on_reset_view())

    @property
    def tool_bar(self):
        """
        Return the tool bar for the embedded plot widget.
        """
        return self._central_widget.tool_bar

    @property
    def current_item(self):
        """
        The currently selected plot data item.
        """
        if self._current_item_index is not None:
            return self.proxy_model.item_from_index(self._current_item_index)

    @property
    def plot_widget(self):
        """
        Return the embedded plot widget
        """
        return self._plot_widget

    @property
    def proxy_model(self):
        """
        The proxy model defined in the internal plot widget.
        """
        return self.plot_widget.proxy_model

    def closeEvent(self, event):
        """
        Called by qt when window closes, upon which
        it emits the window_removed signal.

        Parameters
        ----------
        event :
            ignored in this implementation.
        """
        self.window_removed.emit()

    def _on_current_item_changed(self, current_idx, prev_idx):
        self._current_item_index = current_idx

    def _on_reset_view(self):
        """
        Resets the visible range of the plot taking into consideration only the
        PlotDataItem objects currently attached.
        """
        self.plot_widget.autoRange(
                items=[item for item in self.plot_widget.listDataItems()
                       if isinstance(item, PlotDataItem)])
        self.plot_widget.sigRangeChanged.emit(*self.plot_widget.viewRange())

    def _on_change_color(self):
        """
        Listens for color changed events in plot windows, gets the currently
        selected item in the data list view, and changes the stored color
        value.
        """
        # If there is no currently selected rows, raise an error
        if self.current_item is None:
            message_box = QMessageBox()
            message_box.setText("No item selected, cannot change color.")
            message_box.setIcon(QMessageBox.Warning)
            message_box.setInformativeText(
                "There is currently no item selected. Please select an item "
                "before changing its plot color.")

            message_box.exec()
            return

        color = QColorDialog.getColor(options=QColorDialog.ShowAlphaChannel)

        if color.isValid():
            self.current_item.color = color.toRgb()
            self.color_changed.emit(self.current_item, self.current_item.color)

    def _on_change_width(self, size):
        self.plot_widget.change_width(size)
        self.width_changed.emit(size)

    def _on_export_plot(self):
        file_path, key = compat.getsavefilename(filters=";;".join(
            EXPORT_FILTERS.keys()))

        if key == '':
            return

        exporter = EXPORT_FILTERS[key](self.plot_widget.plotItem)

        # TODO: Current issue in pyqtgraph where the user cannot explicitly
        # define the output size. Fix incoming.

        # plot_size_dialog = PlotSizeDialog(self)
        # plot_size_dialog.height_line_edit.setText(
        #     str(int(exporter.params.param('height').value())))
        # plot_size_dialog.width_line_edit.setText(
        #     str(int(exporter.params.param('width').value())))
        #
        # if key != "*.svg":
        #     if plot_size_dialog.exec_():
        #         exporter.params.param('height').setValue(int(exporter.params.param('height').value()),
        #                                                  blockSignal=exporter.heightChanged)
        #         exporter.params.param('width').setValue(int(exporter.params.param('height').value()),
        #                                                  blockSignal=exporter.widthChanged)
        #     else:
        #         return

        exporter.export(file_path)
Exemplo n.º 55
0
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()