Exemplo n.º 1
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.º 2
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.º 3
0
def populate_qmenu(menu: QMenu, menu_key: str):
    """Populate `menu` from a `menu_key` offering in the manifest."""
    # TODO: declare somewhere what menu_keys are valid.
    pm = npe2.PluginManager.instance()
    for item in pm.iter_menu(menu_key):
        if hasattr(item, 'submenu'):
            subm_contrib = pm.get_submenu(item.submenu)
            subm = menu.addMenu(subm_contrib.label)
            populate_qmenu(subm, subm_contrib.id)
        else:
            cmd = pm.get_command(item.command)
            action = menu.addAction(cmd.title)
            action.triggered.connect(lambda *args: cmd.exec(args=args))
Exemplo n.º 4
0
 def _set_bpm_menu(self, sec):
     cmd = ['sirius-hla-as-di-bpm.py', sec]
     menu = QMenu('BPMs', self)
     menu.setObjectName(sec.upper() + 'App')
     menu.setIcon(qta.icon('mdi.currency-sign'))
     action = menu.addAction('Monitor')
     action.setIcon(util.get_monitor_icon('mdi.currency-sign'))
     self.connect_newprocess(action, cmd + [
         '-w',
         'Monitor',
     ])
     typs = ('Single Pass', 'Multi Turn')
     acts = ('SPass', 'MTurn')
     for typ, act in zip(typs, acts):
         if sec in {'bo', 'si'}:
             menu2 = menu.addMenu(typ)
             menu2.setObjectName(sec.upper() + 'App')
             if act == 'SPass':
                 self._create_bpm_actions(sec, menu2, act, cmd)
             else:
                 for mode in ('Antennas', 'Positions'):
                     menu3 = menu2.addMenu(mode)
                     menu3.setObjectName(sec.upper() + 'App')
                     cmd2 = cmd + ['-m', mode]
                     self._create_bpm_actions(sec, menu3, act, cmd2)
         else:
             if act == 'SPass':
                 self._create_bpm_actions(sec, menu, act, cmd, typ)
             else:
                 menu2 = menu.addMenu(typ)
                 menu2.setObjectName(sec.upper() + 'App')
                 for mode in ('Antennas', 'Positions'):
                     cmd2 = cmd + ['-m', mode]
                     self._create_bpm_actions(sec, menu2, act, cmd2,
                                              mode)
     return menu
Exemplo n.º 5
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.º 6
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.º 7
0
def dict_to_menu(parent, menu_dict, menu_widget=None):
    if not menu_widget:
        menu_widget = QMenu(parent)
    for k, v in menu_dict.items():
        if isinstance(v, dict):
            new_menu = menu_widget.addMenu(k)
            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.º 8
0
    def _dict_to_menu(self, menu_dict):
        '''Stolen shamelessly from specviz. Thanks!'''
        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(True)

                act.triggered.connect(v)
                menu_widget.addAction(act)
        return menu_widget
Exemplo n.º 9
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.º 10
0
    def header_menu(self, position):
        '''
        Creates a menu allowing users to show and hide columns
        '''
        header = self.sender()  # type: QHeaderView
        index = header.logicalIndexAt(position)
        menu = QMenu("Options")
        action = menu.addAction("Hide Column")  # type: QAction
        column_name = str(header.model().headerData(index, Qt.Horizontal))
        action.triggered.connect(lambda: self.hide_column(header, index))
        show_columns_menu = menu.addMenu("Show Columns")

        for i in range(header.count()):
            if header.isSectionHidden(i):
                column_name = str(header.model().headerData(i, Qt.Horizontal))
                action = show_columns_menu.addAction(column_name)
                action.triggered.connect(
                    functools.partial(self.unhide_column, header, i))
                # why does below work, but not: lambda: self.unhide_column(header, i)
                # action.triggered.connect(lambda triggered, logicalIndex=i: self.unhide_column(header, logicalIndex))

        menu.exec_(header.mapToGlobal(position))
Exemplo n.º 11
0
    def _popup_menu(self, event):
        axes = self._find_calling_axes(event)  # find axes calling right click
        if axes is None: return

        pos = self.parent.mapFromGlobal(QtGui.QCursor().pos())

        popup_menu = QMenu(self.parent)
        xScaleMenu = popup_menu.addMenu('x-scale')
        yScaleMenu = popup_menu.addMenu('y-scale')

        for coord in ['x', 'y']:
            menu = eval(coord + 'ScaleMenu')
            for type in axes.scale[coord].keys():
                action = QAction(type, menu, checkable=True)
                if axes.scale[coord][type]:  # if it's checked
                    action.setEnabled(False)
                else:
                    action.setEnabled(True)
                menu.addAction(action)
                action.setChecked(axes.scale[coord][type])
                fcn = lambda event, coord=coord, type=type: self._set_scale(
                    coord, type, axes, True)
                action.triggered.connect(fcn)

        # Create menu for AutoScale options X Y All
        popup_menu.addSeparator()
        autoscale_options = ['AutoScale X', 'AutoScale Y', 'AutoScale All']
        for n, text in enumerate(autoscale_options):
            action = QAction(text, menu, checkable=True)
            if n < len(self.autoScale):
                action.setChecked(self.autoScale[n])
            else:
                action.setChecked(all(self.autoScale))
            popup_menu.addAction(action)
            action.toggled.connect(
                lambda event, n=n: self._setAutoScale(n, event, axes))

        popup_menu.exec_(self.parent.mapToGlobal(pos))
Exemplo n.º 12
0
class InjSysStbyControlWidget(QWidget):
    """Injection System Control Widget."""

    expand = Signal()

    def __init__(self,
                 parent=None,
                 prefix=VACA_PREFIX,
                 is_summary=False,
                 handler=None):
        """Init."""
        super().__init__(parent)
        self.prefix = prefix
        self._inj_prefix = SiriusPVName('AS-Glob:AP-InjCtrl').substitute(
            prefix=prefix)
        self._is_summary = is_summary
        self._last_comm = None

        self._handler = handler or InjSysStandbyHandler()
        self._icon_off = qta.icon('mdi.power-off')
        self._icon_on = qta.icon('mdi.power-on')
        self._icon_check = qta.icon('fa5s.check')
        self._pixmap_check = self._icon_check.pixmap(
            self._icon_check.actualSize(QSize(16, 16)))
        self._icon_not = qta.icon('fa5s.times')
        self._pixmap_not = self._icon_not.pixmap(
            self._icon_not.actualSize(QSize(16, 16)))

        self.menu = QMenu(self)
        self.rstord_act = self.menu.addAction('Reset Commands')
        self.rstord_act.triggered.connect(self._reset_commands_order)

        if is_summary:
            self._setupSummary()
        else:
            self._setupFull()

        self._ch_cmdsts = SiriusConnectionSignal(
            self._inj_prefix.substitute(propty='InjSysCmdSts-Mon'))

        self._ch_off_order_sp = SiriusConnectionSignal(
            self._inj_prefix.substitute(propty='InjSysTurnOffOrder-SP'))
        self._ch_off_order_rb = SiriusConnectionSignal(
            self._inj_prefix.substitute(propty='InjSysTurnOffOrder-RB'))
        self._ch_on_order_sp = SiriusConnectionSignal(
            self._inj_prefix.substitute(propty='InjSysTurnOnOrder-SP'))
        self._ch_on_order_rb = SiriusConnectionSignal(
            self._inj_prefix.substitute(propty='InjSysTurnOnOrder-RB'))
        if not is_summary:
            self._ch_cmdone = SiriusConnectionSignal(
                self._inj_prefix.substitute(propty='InjSysCmdDone-Mon'))

        self._ch_cmdsts.new_value_signal[int].connect(
            self._handle_cmdsts_buttons_enbl)
        self._ch_off_order_rb.new_value_signal[str].connect(
            self._handle_buttons_color)
        self._ch_on_order_rb.new_value_signal[str].connect(
            self._handle_buttons_color)
        self._ch_off_order_rb.new_value_signal[str].connect(
            self._handle_actions_state)
        self._ch_on_order_rb.new_value_signal[str].connect(
            self._handle_actions_state)
        if not is_summary:
            self._ch_cmdone.new_value_signal[str].connect(
                self._handle_cmdone_labels)
            self._ch_cmdsts.new_value_signal[int].connect(
                self._handle_cmdsts_labels)

    def _setupSummary(self):
        self._pb_off = PyDMPushButton(self,
                                      label='',
                                      icon=self._icon_off,
                                      init_channel=self._inj_prefix.substitute(
                                          propty='InjSysTurnOff-Cmd'),
                                      pressValue=0)
        self._pb_off.setObjectName('bt')
        self._pb_off.setStyleSheet(
            '#bt{min-width:25px; max-width:25px; icon-size:20px;}')
        self._pb_on = PyDMPushButton(self,
                                     label='',
                                     icon=self._icon_on,
                                     init_channel=self._inj_prefix.substitute(
                                         propty='InjSysTurnOn-Cmd'),
                                     pressValue=0)
        self._pb_on.setObjectName('bt')
        self._pb_on.setStyleSheet(
            '#bt{min-width:25px; max-width:25px; icon-size:20px;}')

        self._led_sts = InjSysStbyLed(self)
        self._led_sts.setStyleSheet(
            'QLed{min-width:1.29em; max-width:1.29em;}')

        lay = QGridLayout(self)
        lay.setAlignment(Qt.AlignCenter)
        lay.addWidget(self._pb_off, 0, 0)
        lay.addWidget(self._pb_on, 0, 1)
        lay.addWidget(self._led_sts, 1, 0, 1, 2, alignment=Qt.AlignCenter)

        # menu
        for cmmtype in ['on', 'off']:
            order = getattr(self._handler, cmmtype + '_order')
            menu = QMenu('Select Turn ' + cmmtype.upper() + ' Commands', self)
            setattr(self, cmmtype + '_menu', menu)
            self.menu.addMenu(menu)
            for cmm in order:
                act = menu.addAction(self._handler.HANDLER_DESC[cmm])
                act.setObjectName(cmm)
                act.setCheckable(True)

    def _setupFull(self):
        lay = QGridLayout(self)

        lay.addWidget(QLabel('Off', self, alignment=Qt.AlignCenter), 0, 1)
        lay.addWidget(QLabel('On', self, alignment=Qt.AlignCenter), 0, 2)
        lay.addWidget(QLabel('Status', self, alignment=Qt.AlignCenter), 0, 3)

        self._checkbox_off = dict()
        self._checkbox_on = dict()
        self._labels_sts = dict()
        for idx, name in enumerate(self._handler.DEF_ON_ORDER):
            cb_off = QCheckBox(self)
            cb_off.setObjectName(name)
            self._checkbox_off[name] = cb_off
            cb_on = QCheckBox(self)
            cb_on.setObjectName(name)
            self._checkbox_on[name] = cb_on
            desc = self._handler.HANDLER_DESC[name]
            splitd = desc.split('(')
            lbl_txt = splitd[0]
            tip = ''
            if len(splitd) > 1:
                lbl_txt, tip = splitd
            lb_dsc = QLabel(lbl_txt, self, alignment=Qt.AlignLeft)
            if tip:
                lb_dsc.setToolTip(tip[:-1])
            lb_sts = QLabel('', self, alignment=Qt.AlignCenter)
            lb_sts.setObjectName(name)
            self._labels_sts[name] = lb_sts
            lay.addWidget(lb_dsc, idx + 1, 0)
            lay.addWidget(cb_off, idx + 1, 1, alignment=Qt.AlignCenter)
            lay.addWidget(cb_on, idx + 1, 2, alignment=Qt.AlignCenter)
            lay.addWidget(lb_sts, idx + 1, 3)

        self._pb_off = PyDMPushButton(self,
                                      label='',
                                      icon=self._icon_off,
                                      init_channel=self._inj_prefix.substitute(
                                          propty='InjSysTurnOff-Cmd'),
                                      pressValue=0)
        self._pb_off.setObjectName('bt')
        self._pb_off.setStyleSheet(
            '#bt{min-width:25px; max-width:25px; icon-size:20px;}')
        self._pb_on = PyDMPushButton(self,
                                     label='',
                                     icon=self._icon_on,
                                     init_channel=self._inj_prefix.substitute(
                                         propty='InjSysTurnOn-Cmd'),
                                     pressValue=0)
        self._pb_on.setObjectName('bt')
        self._pb_on.setStyleSheet(
            '#bt{min-width:25px; max-width:25px; icon-size:20px;}')

        self._led_sts = InjSysStbyLed(self)

        lay.addWidget(self._pb_off, 6, 1)
        lay.addWidget(self._pb_on, 6, 2)
        lay.addWidget(self._led_sts, 6, 3)

    @Slot(int)
    def _handle_cmdsts_buttons_enbl(self, new_sts):
        if new_sts == _Const.InjSysCmdSts.On:
            self._pb_on.setEnabled(False)
            self._pb_on.setIcon(
                qta.icon('fa5s.spinner', animation=qta.Spin(self._pb_on)))
            self._pb_off.setEnabled(False)
        elif new_sts == _Const.InjSysCmdSts.Off:
            self._pb_on.setEnabled(False)
            self._pb_off.setEnabled(False)
            self._pb_off.setIcon(
                qta.icon('fa5s.spinner', animation=qta.Spin(self._pb_off)))
        else:
            if not self._pb_on.isEnabled():
                self._pb_on.setEnabled(True)
                self._pb_off.setEnabled(True)
                self._pb_on.setIcon(self._icon_on)
                self._pb_off.setIcon(self._icon_off)

    @Slot(str)
    def _handle_cmdone_labels(self, new_done):
        for name in self._handler.DEF_ON_ORDER:
            lbl = self._labels_sts[name]
            if name in new_done:
                lbl.setPixmap(self._pixmap_check)
            elif self._ch_cmdsts.value == _Const.InjSysCmdSts.Idle:
                lbl.setPixmap(self._pixmap_not)

    @Slot(int)
    def _handle_cmdsts_labels(self, new_sts):
        if new_sts == _Const.InjSysCmdSts.On:
            self._last_comm = new_sts
            for name in self._handler.DEF_ON_ORDER:
                if self._ch_on_order_rb.value is None:
                    break
                lbl = self._labels_sts[name]
                if name in self._ch_on_order_rb.value:
                    icon = qta.icon('fa5s.spinner')
                    pixmap = icon.pixmap(icon.actualSize(QSize(16, 16)))
                    lbl.setPixmap(pixmap)
                else:
                    lbl.setPixmap(QPixmap())
        elif new_sts == _Const.InjSysCmdSts.Off:
            self._last_comm = new_sts
            for name in self._handler.DEF_OFF_ORDER:
                if self._ch_off_order_rb.value is None:
                    break
                lbl = self._labels_sts[name]
                if name in self._ch_off_order_rb.value:
                    icon = qta.icon('fa5s.spinner')
                    pixmap = icon.pixmap(icon.actualSize(QSize(16, 16)))
                    lbl.setPixmap(pixmap)
                else:
                    lbl.setPixmap(QPixmap())
        else:
            done = self._ch_cmdone.value
            for name in self._handler.DEF_ON_ORDER:
                if done is None or name in done:
                    continue
                lbl = self._labels_sts[name]
                if self._last_comm == _Const.InjSysCmdSts.On and \
                        name in self._ch_on_order_rb.value:
                    lbl.setPixmap(self._pixmap_not)
                elif self._last_comm == _Const.InjSysCmdSts.Off and \
                        name in self._ch_off_order_rb.value:
                    lbl.setPixmap(self._pixmap_not)
            self._last_comm = None

    @Slot()
    def _set_commands_order(self):
        if self._is_summary:
            if self.sender() in self.on_menu.actions():
                on_order = [
                    a.objectName() for a in self.on_menu.actions()
                    if a.isChecked()
                ]
                self._ch_on_order_sp.send_value_signal[str].emit(
                    ','.join(on_order))
            elif self.sender() in self.off_menu.actions():
                off_order = [
                    a.objectName() for a in self.off_menu.actions()
                    if a.isChecked()
                ]
                self._ch_off_order_sp.send_value_signal[str].emit(
                    ','.join(off_order))
        else:
            if self.sender() in self._checkbox_on.values():
                checked = [
                    w.objectName() for w in self._checkbox_on.values()
                    if w.isChecked()
                ]
                on_order = [
                    h for h in self._handler.DEF_ON_ORDER if h in checked
                ]
                self._ch_on_order_sp.send_value_signal[str].emit(
                    ','.join(on_order))
            elif self.sender() in self._checkbox_off.values():
                checked = [
                    w.objectName() for w in self._checkbox_off.values()
                    if w.isChecked()
                ]
                off_order = [
                    h for h in self._handler.DEF_OFF_ORDER if h in checked
                ]
                self._ch_off_order_sp.send_value_signal[str].emit(
                    ','.join(off_order))

    @Slot()
    def _reset_commands_order(self):
        self._ch_off_order_sp.send_value_signal[str].emit(','.join(
            self._handler.DEF_OFF_ORDER))
        self._ch_on_order_sp.send_value_signal[str].emit(','.join(
            self._handler.DEF_ON_ORDER))
        if self._is_summary:
            for menu in [self.off_menu, self.on_menu]:
                for act in menu.actions():
                    act.toggled.disconnect()
                    act.setChecked(True)
                    act.toggled.connect(self._set_commands_order)
        else:
            for group in [self._checkbox_off, self._checkbox_on]:
                for wid in group.values():
                    wid.toggled.disconnect()
                    wid.setChecked(True)
                    wid.toggled.connect(self._set_commands_order)

    @Slot()
    def _handle_buttons_color(self):
        off_color = 'yellow' if self._ch_off_order_rb.value != \
            ','.join(self._handler.DEF_OFF_ORDER) else 'white'
        self._pb_off.setStyleSheet(
            '#bt{min-width:25px; max-width:25px; icon-size:20px;'
            'background-color: ' + off_color + ';}')
        on_color = 'yellow' if self._ch_on_order_rb.value != \
            ','.join(self._handler.DEF_ON_ORDER) else 'white'
        self._pb_on.setStyleSheet(
            '#bt{min-width:25px; max-width:25px; icon-size:20px;'
            'background-color: ' + on_color + ';}')

    @Slot(str)
    def _handle_actions_state(self, sts):
        state = 'on' if 'On' in self.sender().address else 'off'
        channel = getattr(self, '_ch_' + state + '_order_rb')
        if channel.value is None:
            return

        if self._is_summary:
            group = getattr(self, state + '_menu').actions()
        else:
            group = getattr(self, '_checkbox_' + state).values()
        for obj in group:
            obj.disconnect()
            ost = obj.objectName() in sts
            obj.setChecked(ost)
            obj.toggled.connect(self._set_commands_order)

    def contextMenuEvent(self, event):
        """Show a custom context menu."""
        self.menu.popup(self.mapToGlobal(event.pos()))
Exemplo n.º 13
0
def populate_menu(menu: QMenu, actions: List['MenuItem']):
    """Populate a QMenu from a declarative list of QAction dicts.

    Parameters
    ----------
    menu : QMenu
        the menu to populate
    actions : list of dict
        A list of dicts with one or more of the following keys

        **Required: One of "text" or "menu" MUST be present in the dict**
        text: str
            the name of the QAction to add
        menu: str
            if present, creates a submenu instead.  "menu" keys may also
            provide an "items" key to populate the menu.

        **Optional:**
        slot: callable
            a callback to call when the action is triggered
        shortcut: str
            a keyboard shortcut to trigger the actoin
        statusTip: str
            used for setStatusTip
        menuRole: QAction.MenuRole
            used for setMenuRole
        checkable: bool
            used for setCheckable
        checked: bool
            used for setChecked (only if `checkable` is provided and True)
        check_on: EventEmitter
            If provided, and `checkable` is True, this EventEmitter will be
            connected to action.setChecked:

            `dct['check_on'].connect(lambda e: action.setChecked(e.value))`
    """
    for ax in actions:
        if not ax:
            menu.addSeparator()
            continue
        if not ax.get("when", True):
            continue
        if 'menu' in ax:
            sub = ax['menu']
            if isinstance(sub, QMenu):
                menu.addMenu(sub)
                sub.setParent(menu)
            else:
                sub = menu.addMenu(sub)
            populate_menu(sub, ax.get("items", []))
            continue
        action: QAction = menu.addAction(ax['text'])
        if 'slot' in ax:
            if ax.get("checkable"):
                action.toggled.connect(ax['slot'])
            else:
                action.triggered.connect(ax['slot'])
        action.setShortcut(ax.get('shortcut', ''))
        action.setStatusTip(ax.get('statusTip', ''))
        if 'menuRole' in ax:
            action.setMenuRole(ax['menuRole'])
        if ax.get("checkable"):
            action.setCheckable(True)
            action.setChecked(ax.get("checked", False))
            if 'check_on' in ax:
                emitter = ax['check_on']

                @emitter.connect
                def _setchecked(e, action=action):
                    action.setChecked(e.value if hasattr(e, 'value') else e)

        action.setData(ax)
Exemplo n.º 14
0
class Window:
    """Application window that contains the menu bar and viewer.

    Parameters
    ----------
    viewer : napari.components.ViewerModel
        Contained viewer widget.

    Attributes
    ----------
    file_menu : qtpy.QtWidgets.QMenu
        File menu.
    help_menu : qtpy.QtWidgets.QMenu
        Help menu.
    main_menu : qtpy.QtWidgets.QMainWindow.menuBar
        Main menubar.
    qt_viewer : QtViewer
        Contained viewer widget.
    view_menu : qtpy.QtWidgets.QMenu
        View menu.
    window_menu : qtpy.QtWidgets.QMenu
        Window menu.
    """
    def __init__(self, viewer, *, show: bool = True):
        # create QApplication if it doesn't already exist
        get_app()

        self._unnamed_dockwidget_count = 1

        # Connect the Viewer and create the Main Window
        self.qt_viewer = QtViewer(viewer, show_welcome_screen=True)
        self._qt_window = _QtMainWindow(self.qt_viewer)
        self._status_bar = self._qt_window.statusBar()

        # Dictionary holding dock widgets
        self._dock_widgets: Dict[str, QtViewerDockWidget] = {}

        # since we initialize canvas before window, we need to manually connect them again.
        if self._qt_window.windowHandle() is not None:
            self._qt_window.windowHandle().screenChanged.connect(
                self.qt_viewer.canvas._backend.screen_changed)

        self._add_menubar()
        self._add_file_menu()
        self._add_view_menu()
        self._add_window_menu()
        self._add_plugins_menu()
        self._add_help_menu()

        self._status_bar.showMessage(trans._('Ready'))
        self._help = QLabel('')
        self._status_bar.addPermanentWidget(self._help)

        self.qt_viewer.viewer.theme = SETTINGS.appearance.theme
        self._update_theme()

        self._add_viewer_dock_widget(self.qt_viewer.dockConsole, tabify=False)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerControls,
                                     tabify=False)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerList,
                                     tabify=False)
        self._add_viewer_dock_widget(self.qt_viewer.activityDock, tabify=False)
        self.window_menu.addSeparator()

        SETTINGS.appearance.events.theme.connect(self._update_theme)

        viewer.events.status.connect(self._status_changed)
        viewer.events.help.connect(self._help_changed)
        viewer.events.title.connect(self._title_changed)
        viewer.events.theme.connect(self._update_theme)

        if perf.USE_PERFMON:
            # Add DebugMenu and dockPerformance if using perfmon.
            self._debug_menu = DebugMenu(self)
            self._add_viewer_dock_widget(self.qt_viewer.dockPerformance)
        else:
            self._debug_menu = None

        if show:
            self.show()

    def _add_menubar(self):
        """Add menubar to napari app."""
        self.main_menu = self._qt_window.menuBar()
        # Menubar shortcuts are only active when the menubar is visible.
        # Therefore, we set a global shortcut not associated with the menubar
        # to toggle visibility, *but*, in order to not shadow the menubar
        # shortcut, we disable it, and only enable it when the menubar is
        # hidden. See this stackoverflow link for details:
        # https://stackoverflow.com/questions/50537642/how-to-keep-the-shortcuts-of-a-hidden-widget-in-pyqt5
        self._main_menu_shortcut = QShortcut(QKeySequence('Ctrl+M'),
                                             self._qt_window)
        self._main_menu_shortcut.activated.connect(
            self._toggle_menubar_visible)
        self._main_menu_shortcut.setEnabled(False)

    def _toggle_menubar_visible(self):
        """Toggle visibility of app menubar.

        This function also disables or enables a global keyboard shortcut to
        show the menubar, since menubar shortcuts are only available while the
        menubar is visible.
        """
        if self.main_menu.isVisible():
            self.main_menu.setVisible(False)
            self._main_menu_shortcut.setEnabled(True)
        else:
            self.main_menu.setVisible(True)
            self._main_menu_shortcut.setEnabled(False)

    def _add_file_menu(self):
        """Add 'File' menu to app menubar."""
        open_images = QAction(trans._('Open File(s)...'), self._qt_window)
        open_images.setShortcut('Ctrl+O')
        open_images.setStatusTip(trans._('Open file(s)'))
        open_images.triggered.connect(self.qt_viewer._open_files_dialog)

        open_stack = QAction(trans._('Open Files as Stack...'),
                             self._qt_window)
        open_stack.setShortcut('Ctrl+Alt+O')
        open_stack.setStatusTip(trans._('Open files'))
        open_stack.triggered.connect(
            self.qt_viewer._open_files_dialog_as_stack_dialog)

        open_folder = QAction(trans._('Open Folder...'), self._qt_window)
        open_folder.setShortcut('Ctrl+Shift+O')
        open_folder.setStatusTip(trans._('Open a folder'))
        open_folder.triggered.connect(self.qt_viewer._open_folder_dialog)

        # OS X will rename this to Quit and put it in the app menu.
        preferences = QAction(trans._('Preferences'), self._qt_window)
        preferences.setShortcut('Ctrl+Shift+P')
        preferences.setStatusTip(trans._('Open preferences dialog'))
        preferences.setMenuRole(QAction.PreferencesRole)
        preferences.triggered.connect(self._open_preferences)

        save_selected_layers = QAction(trans._('Save Selected Layer(s)...'),
                                       self._qt_window)
        save_selected_layers.setShortcut('Ctrl+S')
        save_selected_layers.setStatusTip(trans._('Save selected layers'))
        save_selected_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=True))

        save_all_layers = QAction(trans._('Save All Layers...'),
                                  self._qt_window)
        save_all_layers.setShortcut('Ctrl+Shift+S')
        save_all_layers.setStatusTip(trans._('Save all layers'))
        save_all_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=False))

        screenshot = QAction(trans._('Save Screenshot...'), self._qt_window)
        screenshot.setShortcut('Alt+S')
        screenshot.setStatusTip(
            trans._('Save screenshot of current display, default .png'))
        screenshot.triggered.connect(self.qt_viewer._screenshot_dialog)

        screenshot_wv = QAction(trans._('Save Screenshot with Viewer...'),
                                self._qt_window)
        screenshot_wv.setShortcut('Alt+Shift+S')
        screenshot_wv.setStatusTip(
            trans.
            _('Save screenshot of current display with the viewer, default .png'
              ))
        screenshot_wv.triggered.connect(self._screenshot_dialog)

        # OS X will rename this to Quit and put it in the app menu.
        # This quits the entire QApplication and all windows that may be open.
        quitAction = QAction(trans._('Exit'), self._qt_window)
        quitAction.setShortcut('Ctrl+Q')
        quitAction.setMenuRole(QAction.QuitRole)
        quitAction.triggered.connect(
            lambda: self._qt_window.close(quit_app=True))

        if running_as_bundled_app():
            restartAction = QAction(trans._('Restart'), self._qt_window)
            restartAction.triggered.connect(self._qt_window.restart)

        closeAction = QAction(trans._('Close Window'), self._qt_window)
        closeAction.setShortcut('Ctrl+W')
        closeAction.triggered.connect(self._qt_window.close_window)

        plugin_manager.discover_sample_data()
        open_sample_menu = QMenu(trans._('Open Sample'), self._qt_window)
        for plugin_name, samples in plugin_manager._sample_data.items():
            multiprovider = len(samples) > 1
            if multiprovider:
                menu = QMenu(plugin_name, self._qt_window)
                open_sample_menu.addMenu(menu)
            else:
                menu = open_sample_menu

            for samp_name, samp_dict in samples.items():
                display_name = samp_dict['display_name']
                if multiprovider:
                    action = QAction(display_name, parent=self._qt_window)
                else:
                    full_name = plugin_menu_item_template.format(
                        plugin_name, display_name)
                    action = QAction(full_name, parent=self._qt_window)

                def _add_sample(*args, plg=plugin_name, smp=samp_name):
                    self.qt_viewer.viewer.open_sample(plg, smp)

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

        self.file_menu = self.main_menu.addMenu(trans._('&File'))
        self.file_menu.addAction(open_images)
        self.file_menu.addAction(open_stack)
        self.file_menu.addAction(open_folder)
        self.file_menu.addMenu(open_sample_menu)
        self.file_menu.addSeparator()
        self.file_menu.addAction(preferences)
        self.file_menu.addSeparator()
        self.file_menu.addAction(save_selected_layers)
        self.file_menu.addAction(save_all_layers)
        self.file_menu.addAction(screenshot)
        self.file_menu.addAction(screenshot_wv)
        self.file_menu.addSeparator()
        self.file_menu.addAction(closeAction)

        if running_as_bundled_app():
            self.file_menu.addAction(restartAction)

        self.file_menu.addAction(quitAction)

    def _open_preferences(self):
        """Edit preferences from the menubar."""
        if self._qt_window._preferences_dialog is None:
            win = PreferencesDialog(parent=self._qt_window)
            win.resized.connect(
                self._qt_window._update_preferences_dialog_size)

            if self._qt_window._preferences_dialog_size:
                win.resize(self._qt_window._preferences_dialog_size)

            self._qt_window._preferences_dialog = win
            win.closed.connect(self._on_preferences_closed)
            win.show()
        else:
            self._qt_window._preferences_dialog.raise_()

    def _on_preferences_closed(self):
        """Reset preferences dialog variable."""
        self._qt_window._preferences_dialog = None

    def _add_view_menu(self):
        """Add 'View' menu to app menubar."""
        toggle_visible = QAction(trans._('Toggle Menubar Visibility'),
                                 self._qt_window)
        toggle_visible.setShortcut('Ctrl+M')
        toggle_visible.setStatusTip(trans._('Hide Menubar'))
        toggle_visible.triggered.connect(self._toggle_menubar_visible)
        toggle_fullscreen = QAction(trans._('Toggle Full Screen'),
                                    self._qt_window)
        toggle_fullscreen.setShortcut('Ctrl+F')
        toggle_fullscreen.setStatusTip(trans._('Toggle full screen'))
        toggle_fullscreen.triggered.connect(self._toggle_fullscreen)
        toggle_play = QAction(trans._('Toggle Play'), self._qt_window)
        toggle_play.triggered.connect(self._toggle_play)
        toggle_play.setShortcut('Ctrl+Alt+P')
        toggle_play.setStatusTip(trans._('Toggle Play'))

        self.view_menu = self.main_menu.addMenu(trans._('&View'))
        self.view_menu.addAction(toggle_fullscreen)
        self.view_menu.addAction(toggle_visible)
        self.view_menu.addAction(toggle_play)
        self.view_menu.addSeparator()

        # Add octree actions.
        if config.async_octree:
            toggle_outline = QAction(trans._('Toggle Chunk Outlines'),
                                     self._qt_window)
            toggle_outline.triggered.connect(
                self.qt_viewer._toggle_chunk_outlines)
            toggle_outline.setShortcut('Ctrl+Alt+O')
            toggle_outline.setStatusTip(trans._('Toggle Chunk Outlines'))
            self.view_menu.addAction(toggle_outline)

        # Add axes menu
        axes = self.qt_viewer.viewer.axes
        axes_menu = QMenu(trans._('Axes'), parent=self._qt_window)
        axes_visible_action = QAction(
            trans._('Visible'),
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.visible,
        )
        axes_visible_action.triggered.connect(self._toggle_axes_visible)
        self._event_to_action(axes_visible_action, axes.events.visible)
        axes_colored_action = QAction(
            trans._('Colored'),
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.colored,
        )
        axes_colored_action.triggered.connect(self._toggle_axes_colored)
        self._event_to_action(axes_colored_action, axes.events.colored)
        axes_labels_action = QAction(
            trans._('Labels'),
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.labels,
        )
        axes_labels_action.triggered.connect(self._toggle_axes_labels)
        self._event_to_action(axes_labels_action, axes.events.labels)
        axes_dashed_action = QAction(
            trans._('Dashed'),
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.dashed,
        )
        axes_dashed_action.triggered.connect(self._toggle_axes_dashed)
        self._event_to_action(axes_dashed_action, axes.events.dashed)
        axes_arrows_action = QAction(
            trans._('Arrows'),
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.arrows,
        )
        axes_arrows_action.triggered.connect(self._toggle_axes_arrows)
        self._event_to_action(axes_arrows_action, axes.events.arrows)

        axes_menu.addAction(axes_visible_action)
        axes_menu.addAction(axes_colored_action)
        axes_menu.addAction(axes_labels_action)
        axes_menu.addAction(axes_dashed_action)
        axes_menu.addAction(axes_arrows_action)
        self.view_menu.addMenu(axes_menu)

        # Add scale bar menu
        scale_bar = self.qt_viewer.viewer.scale_bar
        scale_bar_menu = QMenu(trans._('Scale Bar'), parent=self._qt_window)
        scale_bar_visible_action = QAction(
            trans._('Visible'),
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.scale_bar.visible,
        )
        scale_bar_visible_action.triggered.connect(
            self._toggle_scale_bar_visible)
        self._event_to_action(scale_bar_visible_action,
                              scale_bar.events.visible)
        scale_bar_colored_action = QAction(
            trans._('Colored'),
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.scale_bar.colored,
        )
        scale_bar_colored_action.triggered.connect(
            self._toggle_scale_bar_colored)
        self._event_to_action(scale_bar_colored_action,
                              scale_bar.events.colored)
        scale_bar_ticks_action = QAction(
            trans._('Ticks'),
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.scale_bar.ticks,
        )
        scale_bar_ticks_action.triggered.connect(self._toggle_scale_bar_ticks)
        self._event_to_action(scale_bar_ticks_action, scale_bar.events.ticks)

        scale_bar_menu.addAction(scale_bar_visible_action)
        scale_bar_menu.addAction(scale_bar_colored_action)
        scale_bar_menu.addAction(scale_bar_ticks_action)
        self.view_menu.addMenu(scale_bar_menu)

        self.view_menu.addSeparator()

    def _event_to_action(self, action, event):
        """Connect triggered event in model to respective action in menu."""
        # TODO: use action manager to keep in sync
        event.connect(lambda e: action.setChecked(e.value))

    def _add_window_menu(self):
        """Add 'Window' menu to app menubar."""
        clear_action = QAction(trans._("Remove Dock Widgets"), self._qt_window)
        clear_action.setStatusTip(trans._('Remove all dock widgets'))
        clear_action.triggered.connect(
            lambda e: self.remove_dock_widget('all'))

        self.window_menu = self.main_menu.addMenu(trans._('&Window'))
        self.window_menu.addAction(clear_action)
        self.window_menu.addSeparator()

    def _add_plugins_menu(self):
        """Add 'Plugins' menu to app menubar."""
        self.plugins_menu = self.main_menu.addMenu(trans._('&Plugins'))

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

        report_plugin_action = QAction(trans._("Plugin Errors..."),
                                       self._qt_window)
        report_plugin_action.setStatusTip(
            trans.
            _('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(trans._('Add Dock Widget'),
                                              self._qt_window)

        plugin_manager.discover_widgets()

        # Add a menu item (QAction) for each available plugin widget
        for hook_type, (plugin_name, widgets) in plugin_manager.iter_widgets():
            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 = plugin_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)

    def _show_plugin_install_dialog(self):
        """Show dialog that allows users to sort the call order of plugins."""

        self.plugin_dialog = QtPluginDialog(self._qt_window)
        self.plugin_dialog.exec_()

    def _show_plugin_err_reporter(self):
        """Show dialog that allows users to review and report plugin errors."""
        QtPluginErrReporter(parent=self._qt_window).exec_()

    def _add_help_menu(self):
        """Add 'Help' menu to app menubar."""
        self.help_menu = self.main_menu.addMenu(trans._('&Help'))

        about_action = QAction(trans._("napari Info"), self._qt_window)
        about_action.setShortcut("Ctrl+/")
        about_action.setStatusTip(trans._('About napari'))
        about_action.triggered.connect(
            lambda e: QtAbout.showAbout(self.qt_viewer, self._qt_window))
        self.help_menu.addAction(about_action)

        about_key_bindings = QAction(trans._("Show Key Bindings"),
                                     self._qt_window)
        about_key_bindings.setShortcut("Ctrl+Alt+/")
        about_key_bindings.setShortcutContext(Qt.ApplicationShortcut)
        about_key_bindings.setStatusTip(trans._('key_bindings'))
        about_key_bindings.triggered.connect(
            self.qt_viewer.show_key_bindings_dialog)
        self.help_menu.addAction(about_key_bindings)

    def _toggle_scale_bar_visible(self, state):
        self.qt_viewer.viewer.scale_bar.visible = state

    def _toggle_scale_bar_colored(self, state):
        self.qt_viewer.viewer.scale_bar.colored = state

    def _toggle_scale_bar_ticks(self, state):
        self.qt_viewer.viewer.scale_bar.ticks = state

    def _toggle_axes_visible(self, state):
        self.qt_viewer.viewer.axes.visible = state

    def _toggle_axes_colored(self, state):
        self.qt_viewer.viewer.axes.colored = state

    def _toggle_axes_labels(self, state):
        self.qt_viewer.viewer.axes.labels = state

    def _toggle_axes_dashed(self, state):
        self.qt_viewer.viewer.axes.dashed = state

    def _toggle_axes_arrows(self, state):
        self.qt_viewer.viewer.axes.arrows = state

    def _toggle_fullscreen(self, event):
        """Toggle fullscreen mode."""
        if self._qt_window.isFullScreen():
            self._qt_window.showNormal()
        else:
            self._qt_window.showFullScreen()

    def _toggle_play(self, state):
        """Toggle play."""
        if self.qt_viewer.dims.is_playing:
            self.qt_viewer.dims.stop()
        else:
            axis = self.qt_viewer.viewer.dims.last_used or 0
            self.qt_viewer.dims.play(axis)

    def add_plugin_dock_widget(
            self,
            plugin_name: str,
            widget_name: str = None) -> Tuple[QtViewerDockWidget, Any]:
        """Add plugin dock widget if not already added.

        Parameters
        ----------
        plugin_name : str
            Name of a plugin providing a widget
        widget_name : str, optional
            Name of a widget provided by `plugin_name`. If `None`, and the
            specified plugin provides only a single widget, that widget will be
            returned, otherwise a ValueError will be raised, by default None

        Returns
        -------
        tuple
            A 2-tuple containing (the DockWidget instance, the plugin widget
            instance).
        """
        from ..viewer import Viewer

        Widget, dock_kwargs = plugin_manager.get_widget(
            plugin_name, widget_name)
        if not widget_name:
            # if widget_name wasn't provided, `get_widget` will have
            # ensured that there is a single widget available.
            widget_name = list(plugin_manager._dock_widgets[plugin_name])[0]

        full_name = plugin_menu_item_template.format(plugin_name, widget_name)
        if full_name in self._dock_widgets:
            dock_widget = self._dock_widgets[full_name]
            dock_widget.show()
            wdg = dock_widget.widget()
            if hasattr(wdg, '_magic_widget'):
                wdg = wdg._magic_widget
            return dock_widget, wdg

        # if the signature is looking a for a napari viewer, pass it.
        kwargs = {}
        for param in inspect.signature(Widget.__init__).parameters.values():
            if param.name == 'napari_viewer':
                kwargs['napari_viewer'] = self.qt_viewer.viewer
                break
            if param.annotation in ('napari.viewer.Viewer', Viewer):
                kwargs[param.name] = self.qt_viewer.viewer
                break
            # cannot look for param.kind == param.VAR_KEYWORD because
            # QWidget allows **kwargs but errs on unknown keyword arguments

        # instantiate the widget
        wdg = Widget(**kwargs)

        # Add dock widget
        dock_kwargs.pop('name', None)
        dock_widget = self.add_dock_widget(wdg, name=full_name, **dock_kwargs)
        return dock_widget, wdg

    def _add_plugin_function_widget(self, plugin_name: str, widget_name: str):
        """Add plugin function widget if not already added.

        Parameters
        ----------
        plugin_name : str
            Name of a plugin providing a widget
        widget_name : str, optional
            Name of a widget provided by `plugin_name`. If `None`, and the
            specified plugin provides only a single widget, that widget will be
            returned, otherwise a ValueError will be raised, by default None
        """
        full_name = plugin_menu_item_template.format(plugin_name, widget_name)
        if full_name in self._dock_widgets:
            self._dock_widgets[full_name].show()
            return

        func = plugin_manager._function_widgets[plugin_name][widget_name]

        # Add function widget
        self.add_function_widget(func,
                                 name=full_name,
                                 area=None,
                                 allowed_areas=None)

    def add_dock_widget(
        self,
        widget: QWidget,
        *,
        name: str = '',
        area: str = 'right',
        allowed_areas: Optional[Sequence[str]] = None,
        shortcut=_sentinel,
        add_vertical_stretch=True,
    ):
        """Convenience method to add a QDockWidget to the main window.

        If name is not provided a generic name will be addded to avoid
        `saveState` warnings on close.

        Parameters
        ----------
        widget : QWidget
            `widget` will be added as QDockWidget's main widget.
        name : str, optional
            Name of dock widget to appear in window menu.
        area : str
            Side of the main window to which the new dock widget will be added.
            Must be in {'left', 'right', 'top', 'bottom'}
        allowed_areas : list[str], optional
            Areas, relative to main window, that the widget is allowed dock.
            Each item in list must be in {'left', 'right', 'top', 'bottom'}
            By default, all areas are allowed.
        shortcut : str, optional
            Keyboard shortcut to appear in dropdown menu.
        add_vertical_stretch : bool, optional
            Whether to add stretch to the bottom of vertical widgets (pushing
            widgets up towards the top of the allotted area, instead of letting
            them distribute across the vertical space).  By default, True.

            .. deprecated:: 0.4.8

                The shortcut parameter is deprecated since version 0.4.8, please use
                the action and shortcut manager APIs. The new action manager and
                shortcut API allow user configuration and localisation.

        Returns
        -------
        dock_widget : QtViewerDockWidget
            `dock_widget` that can pass viewer events.
        """
        if not name:
            try:
                name = widget.objectName()
            except AttributeError:
                name = trans._(
                    "Dock widget {number}",
                    number=self._unnamed_dockwidget_count,
                )

            self._unnamed_dockwidget_count += 1
        if shortcut is not _sentinel:
            warnings.warn(
                _SHORTCUT_DEPRECATION_STRING.format(shortcut=shortcut),
                FutureWarning,
                stacklevel=2,
            )
            dock_widget = QtViewerDockWidget(
                self.qt_viewer,
                widget,
                name=name,
                area=area,
                allowed_areas=allowed_areas,
                shortcut=shortcut,
                add_vertical_stretch=add_vertical_stretch,
            )
        else:
            dock_widget = QtViewerDockWidget(
                self.qt_viewer,
                widget,
                name=name,
                area=area,
                allowed_areas=allowed_areas,
                add_vertical_stretch=add_vertical_stretch,
            )

        self._add_viewer_dock_widget(dock_widget)

        if hasattr(widget, 'reset_choices'):
            # Keep the dropdown menus in the widget in sync with the layer model
            # if widget has a `reset_choices`, which is true for all magicgui
            # `CategoricalWidget`s
            layers_events = self.qt_viewer.viewer.layers.events
            layers_events.inserted.connect(widget.reset_choices)
            layers_events.removed.connect(widget.reset_choices)
            layers_events.reordered.connect(widget.reset_choices)

        # Add dock widget to dictionary
        self._dock_widgets[dock_widget.name] = dock_widget

        return dock_widget

    def _add_viewer_dock_widget(self,
                                dock_widget: QtViewerDockWidget,
                                tabify=False):
        """Add a QtViewerDockWidget to the main window

        If other widgets already present in area then will tabify.

        Parameters
        ----------
        dock_widget : QtViewerDockWidget
            `dock_widget` will be added to the main window.
        tabify : bool
            Flag to tabify dockwidget or not.
        """
        # Find if any othe dock widgets are currently in area
        current_dws_in_area = [
            dw for dw in self._qt_window.findChildren(QDockWidget)
            if self._qt_window.dockWidgetArea(dw) == dock_widget.qt_area
        ]
        self._qt_window.addDockWidget(dock_widget.qt_area, dock_widget)

        # If another dock widget present in area then tabify
        if current_dws_in_area:
            if tabify:
                self._qt_window.tabifyDockWidget(current_dws_in_area[-1],
                                                 dock_widget)
                dock_widget.show()
                dock_widget.raise_()
            elif dock_widget.area in ('right', 'left'):
                _wdg = current_dws_in_area + [dock_widget]
                # add sizes to push lower widgets up
                sizes = list(range(1, len(_wdg) * 4, 4))
                self._qt_window.resizeDocks(_wdg, sizes, Qt.Vertical)

        action = dock_widget.toggleViewAction()
        action.setStatusTip(dock_widget.name)
        action.setText(dock_widget.name)
        import warnings

        with warnings.catch_warnings():
            warnings.simplefilter("ignore", FutureWarning)
            # deprecating with 0.4.8, but let's try to keep compatibility.
            shortcut = dock_widget.shortcut
        if shortcut is not None:
            action.setShortcut(shortcut)
        self.window_menu.addAction(action)

    def remove_dock_widget(self, widget: QWidget):
        """Removes specified dock widget.

        If a QDockWidget is not provided, the existing QDockWidgets will be
        searched for one whose inner widget (``.widget()``) is the provided
        ``widget``.

        Parameters
        ----------
        widget : QWidget | str
            If widget == 'all', all docked widgets will be removed.
        """
        if widget == 'all':
            for dw in list(self._dock_widgets.values()):
                self.remove_dock_widget(dw)
            return

        if not isinstance(widget, QDockWidget):
            for dw in self._qt_window.findChildren(QDockWidget):
                if dw.widget() is widget:
                    _dw: QDockWidget = dw
                    break
            else:
                raise LookupError(
                    trans._(
                        "Could not find a dock widget containing: {widget}",
                        deferred=True,
                        widget=widget,
                    ))
        else:
            _dw = widget

        if _dw.widget():
            _dw.widget().setParent(None)
        self._qt_window.removeDockWidget(_dw)
        self.window_menu.removeAction(_dw.toggleViewAction())

        # Remove dock widget from dictionary
        del self._dock_widgets[_dw.name]

        # Deleting the dock widget means any references to it will no longer
        # work but it's not really useful anyway, since the inner widget has
        # been removed. and anyway: people should be using add_dock_widget
        # rather than directly using _add_viewer_dock_widget
        _dw.deleteLater()

    def add_function_widget(
        self,
        function,
        *,
        magic_kwargs=None,
        name: str = '',
        area=None,
        allowed_areas=None,
        shortcut=_sentinel,
    ):
        """Turn a function into a dock widget via magicgui.

        Parameters
        ----------
        function : callable
            Function that you want to add.
        magic_kwargs : dict, optional
            Keyword arguments to :func:`magicgui.magicgui` that
            can be used to specify widget.
        name : str, optional
            Name of dock widget to appear in window menu.
        area : str, optional
            Side of the main window to which the new dock widget will be added.
            Must be in {'left', 'right', 'top', 'bottom'}. If not provided the
            default will be determined by the widget.layout, with 'vertical'
            layouts appearing on the right, otherwise on the bottom.
        allowed_areas : list[str], optional
            Areas, relative to main window, that the widget is allowed dock.
            Each item in list must be in {'left', 'right', 'top', 'bottom'}
            By default, only provided areas is allowed.
        shortcut : str, optional
            Keyboard shortcut to appear in dropdown menu.

        Returns
        -------
        dock_widget : QtViewerDockWidget
            `dock_widget` that can pass viewer events.
        """
        from magicgui import magicgui

        if magic_kwargs is None:
            magic_kwargs = {
                'auto_call': False,
                'call_button': "run",
                'layout': 'vertical',
            }

        widget = magicgui(function, **magic_kwargs or {})

        if area is None:
            if str(widget.layout) == 'vertical':
                area = 'right'
            else:
                area = 'bottom'

        if allowed_areas is None:
            allowed_areas = [area]
        if shortcut is not _sentinel:
            return self.add_dock_widget(
                widget,
                name=name or function.__name__.replace('_', ' '),
                area=area,
                allowed_areas=allowed_areas,
                shortcut=shortcut,
            )
        else:
            return self.add_dock_widget(
                widget,
                name=name or function.__name__.replace('_', ' '),
                area=area,
                allowed_areas=allowed_areas,
            )

    def resize(self, width, height):
        """Resize the window.

        Parameters
        ----------
        width : int
            Width in logical pixels.
        height : int
            Height in logical pixels.
        """
        self._qt_window.resize(width, height)

    def show(self):
        """Resize, show, and bring forward the window.

        Raises
        ------
        RuntimeError
            If the viewer.window has already been closed and deleted.
        """
        try:
            self._qt_window.show()
        except (AttributeError, RuntimeError):
            raise RuntimeError(
                trans._(
                    "This viewer has already been closed and deleted. Please create a new one.",
                    deferred=True,
                ))

        if SETTINGS.application.first_time:
            SETTINGS.application.first_time = False
            try:
                self._qt_window.resize(self._qt_window.layout().sizeHint())
            except (AttributeError, RuntimeError):
                raise RuntimeError(
                    trans._(
                        "This viewer has already been closed and deleted. Please create a new one.",
                        deferred=True,
                    ))
        else:
            try:
                if SETTINGS.application.save_window_geometry:
                    self._qt_window._set_window_settings(
                        *self._qt_window._load_window_settings())
            except Exception as err:
                import warnings

                warnings.warn(
                    trans._(
                        "The window geometry settings could not be loaded due to the following error: {err}",
                        deferred=True,
                        err=err,
                    ),
                    category=RuntimeWarning,
                    stacklevel=2,
                )

        # Resize axis labels now that window is shown
        self.qt_viewer.dims._resize_axis_labels()

        # We want to bring the viewer to the front when
        # A) it is our own event loop OR we are running in jupyter
        # B) it is not the first time a QMainWindow is being created

        # `app_name` will be "napari" iff the application was instantiated in
        # get_app(). isActiveWindow() will be True if it is the second time a
        # _qt_window has been created.
        # See #721, #732, #735, #795, #1594
        app_name = QApplication.instance().applicationName()
        if (app_name == 'napari'
                or in_jupyter()) and self._qt_window.isActiveWindow():
            self.activate()

    def activate(self):
        """Make the viewer the currently active window."""
        self._qt_window.raise_()  # for macOS
        self._qt_window.activateWindow()  # for Windows

    def _update_theme(self, event=None):
        """Update widget color theme."""
        if event:
            value = event.value
            SETTINGS.appearance.theme = value
            self.qt_viewer.viewer.theme = value
        else:
            value = self.qt_viewer.viewer.theme

        try:
            self._qt_window.setStyleSheet(get_stylesheet(value))
        except AttributeError:
            pass
        except RuntimeError:
            # wrapped C/C++ object may have been deleted
            pass

    def _status_changed(self, event):
        """Update status bar.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._status_bar.showMessage(event.value)

    def _title_changed(self, event):
        """Update window title.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._qt_window.setWindowTitle(event.value)

    def _help_changed(self, event):
        """Update help message on status bar.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._help.setText(event.value)

    def _screenshot_dialog(self):
        """Save screenshot of current display with viewer, default .png"""
        hist = get_save_history()
        dial = ScreenshotDialog(self.screenshot, self.qt_viewer, hist[0], hist)

        if dial.exec_():
            update_save_history(dial.selectedFiles()[0])

    def _restart(self):
        """Restart the napari application."""
        self._qt_window.restart()

    def screenshot(self, path=None):
        """Take currently displayed viewer and convert to an image array.

        Parameters
        ----------
        path : str
            Filename for saving screenshot image.

        Returns
        -------
        image : array
            Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the
            upper-left corner of the rendered region.
        """
        img = self._qt_window.grab().toImage()
        if path is not None:
            imsave(path, QImg2array(img))  # scikit-image imsave method
        return QImg2array(img)

    def close(self):
        """Close the viewer window and cleanup sub-widgets."""
        # Someone is closing us twice? Only try to delete self._qt_window
        # if we still have one.
        if hasattr(self, '_qt_window'):
            self.qt_viewer.close()
            self._qt_window.close()
            del self._qt_window
Exemplo n.º 15
0
    def _add_file_menu(self):
        """Add 'File' menu to app menubar."""
        open_images = QAction(trans._('Open File(s)...'), self._qt_window)
        open_images.setShortcut('Ctrl+O')
        open_images.setStatusTip(trans._('Open file(s)'))
        open_images.triggered.connect(self.qt_viewer._open_files_dialog)

        open_stack = QAction(trans._('Open Files as Stack...'),
                             self._qt_window)
        open_stack.setShortcut('Ctrl+Alt+O')
        open_stack.setStatusTip(trans._('Open files'))
        open_stack.triggered.connect(
            self.qt_viewer._open_files_dialog_as_stack_dialog)

        open_folder = QAction(trans._('Open Folder...'), self._qt_window)
        open_folder.setShortcut('Ctrl+Shift+O')
        open_folder.setStatusTip(trans._('Open a folder'))
        open_folder.triggered.connect(self.qt_viewer._open_folder_dialog)

        # OS X will rename this to Quit and put it in the app menu.
        preferences = QAction(trans._('Preferences'), self._qt_window)
        preferences.setShortcut('Ctrl+Shift+P')
        preferences.setStatusTip(trans._('Open preferences dialog'))
        preferences.setMenuRole(QAction.PreferencesRole)
        preferences.triggered.connect(self._open_preferences)

        save_selected_layers = QAction(trans._('Save Selected Layer(s)...'),
                                       self._qt_window)
        save_selected_layers.setShortcut('Ctrl+S')
        save_selected_layers.setStatusTip(trans._('Save selected layers'))
        save_selected_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=True))

        save_all_layers = QAction(trans._('Save All Layers...'),
                                  self._qt_window)
        save_all_layers.setShortcut('Ctrl+Shift+S')
        save_all_layers.setStatusTip(trans._('Save all layers'))
        save_all_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=False))

        screenshot = QAction(trans._('Save Screenshot...'), self._qt_window)
        screenshot.setShortcut('Alt+S')
        screenshot.setStatusTip(
            trans._('Save screenshot of current display, default .png'))
        screenshot.triggered.connect(self.qt_viewer._screenshot_dialog)

        screenshot_wv = QAction(trans._('Save Screenshot with Viewer...'),
                                self._qt_window)
        screenshot_wv.setShortcut('Alt+Shift+S')
        screenshot_wv.setStatusTip(
            trans.
            _('Save screenshot of current display with the viewer, default .png'
              ))
        screenshot_wv.triggered.connect(self._screenshot_dialog)

        # OS X will rename this to Quit and put it in the app menu.
        # This quits the entire QApplication and all windows that may be open.
        quitAction = QAction(trans._('Exit'), self._qt_window)
        quitAction.setShortcut('Ctrl+Q')
        quitAction.setMenuRole(QAction.QuitRole)
        quitAction.triggered.connect(
            lambda: self._qt_window.close(quit_app=True))

        if running_as_bundled_app():
            restartAction = QAction(trans._('Restart'), self._qt_window)
            restartAction.triggered.connect(self._qt_window.restart)

        closeAction = QAction(trans._('Close Window'), self._qt_window)
        closeAction.setShortcut('Ctrl+W')
        closeAction.triggered.connect(self._qt_window.close_window)

        plugin_manager.discover_sample_data()
        open_sample_menu = QMenu(trans._('Open Sample'), self._qt_window)
        for plugin_name, samples in plugin_manager._sample_data.items():
            multiprovider = len(samples) > 1
            if multiprovider:
                menu = QMenu(plugin_name, self._qt_window)
                open_sample_menu.addMenu(menu)
            else:
                menu = open_sample_menu

            for samp_name, samp_dict in samples.items():
                display_name = samp_dict['display_name']
                if multiprovider:
                    action = QAction(display_name, parent=self._qt_window)
                else:
                    full_name = plugin_menu_item_template.format(
                        plugin_name, display_name)
                    action = QAction(full_name, parent=self._qt_window)

                def _add_sample(*args, plg=plugin_name, smp=samp_name):
                    self.qt_viewer.viewer.open_sample(plg, smp)

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

        self.file_menu = self.main_menu.addMenu(trans._('&File'))
        self.file_menu.addAction(open_images)
        self.file_menu.addAction(open_stack)
        self.file_menu.addAction(open_folder)
        self.file_menu.addMenu(open_sample_menu)
        self.file_menu.addSeparator()
        self.file_menu.addAction(preferences)
        self.file_menu.addSeparator()
        self.file_menu.addAction(save_selected_layers)
        self.file_menu.addAction(save_all_layers)
        self.file_menu.addAction(screenshot)
        self.file_menu.addAction(screenshot_wv)
        self.file_menu.addSeparator()
        self.file_menu.addAction(closeAction)

        if running_as_bundled_app():
            self.file_menu.addAction(restartAction)

        self.file_menu.addAction(quitAction)
Exemplo n.º 16
0
class PMGFilesTreeview(QTreeView):
    """
        文件树
    """
    open_signal = Signal(str)
    open_folder_signal = Signal(str)
    new_file_signal = Signal(str)
    new_folder_signal = Signal(str)
    delete_file_signal = Signal(str)
    rename_file_signal = Signal(str, str)

    signal_ext_filter_adapt = Signal(bool)
    signal_ext_filter_changed = Signal(dict)

    def __init__(self, initial_dir: str = '', parent=None):
        super().__init__(parent)
        self.initial_dir = initial_dir
        self.setup_ui()
        self.bind_events()

        self.filter_exts = True
        self.exts_to_filter = {
            'Program Scripts': {
                '.pyx': True,
                '.py': True,
                '.c': True,
                '.pyi': True,
                '.dll': True,
                '.h': True,
                '.cpp': True,
                '.ipynb': True,
                '.sh': True,
                '.cmd': True,
                '.bat': True
            },
            'Documents': {
                '.txt': True,
                '.md': True,
                '.doc': True,
                '.docx': True,
                '.ppt': True,
                '.pptx': True,
                '.html': True
            },
            'Data Files': {
                '.csv': True,
                '.xls': True,
                '.xlsx': True,
                '.tab': True,
                '.dat': True,
                '.tsv': True,
                '.sav': True,
                '.zsav': True,
                '.sas7bdat': True,
                '.pkl': True,
                '.json': True,
                '.mat': True,
                '.pmjson': True,
                '.pmd': True
            },
            'Medias': {
                '.mp3': False,
                '.mp4': False,
                '.avi': False,
                '.wma': False,
                '.png': True,
                '.jpg': True,
                '.svg': True
            },
            'Resources': {
                '.qm': True,
                '.ts': True
            }
        }

    def setup_ui(self):
        """
        界面初始化
        :return:
        """

        self.translator = create_translator(path=os.path.join(
            os.path.dirname(__file__), 'translations',
            'qt_{0}.qm'.format(QLocale.system().name())))  # translator

        self.setTabKeyNavigation(True)
        self.setDragEnabled(True)
        self.setDragDropOverwriteMode(True)
        self.setAlternatingRowColors(False)
        self.setUniformRowHeights(True)
        self.setSortingEnabled(True)
        self.setAnimated(True)
        self.setAllColumnsShowFocus(False)
        self.setWordWrap(False)
        self.setHeaderHidden(False)
        self.setObjectName("treeView_files")
        self.header().setSortIndicatorShown(True)

        self.model = PMFileSystemModel()
        self.model.setRootPath(self.initial_dir)

        self.setModel(self.model)
        self.setRootIndex(self.model.index(self.initial_dir))
        self.setAnimated(False)
        self.setSortingEnabled(True)  # 启用排序
        self.header().setSortIndicatorShown(True)  # 启用标题排序
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.show_context_menu)
        self.init_context_menu()

    def bind_events(self):
        """
        回调、事件与信号初始化
        :return:
        """
        self.doubleClicked.connect(lambda index: self.on_open())

        self.openAction.triggered.connect(self.on_open)
        self.importAction.triggered.connect(self.on_import)
        self.renameAction.triggered.connect(self.on_rename)
        self.deleteAction.triggered.connect(self.on_delete)

        self.copyAction.triggered.connect(self.on_copy)
        self.pasteAction.triggered.connect(self.on_paste)
        self.filterAction.triggered.connect(
            self.show_ext_filter_selection_dialog)

        self.copyPathAction.triggered.connect(self.copy_path)
        self.new_file_action.triggered.connect(lambda: self.on_new_file(''))
        self.new_python_file_action.triggered.connect(
            lambda: self.on_new_file('py'))
        self.new_folder_action.triggered.connect(self.on_new_folder)
        self.open_file_manager_action.triggered.connect(
            self.on_open_file_manager)

        self.rename_shortcut.activated.connect(self.on_rename)
        self.paste_shortcut.activated.connect(self.on_paste)
        self.copy_shortcut.activated.connect(self.on_copy)
        self.open_shortcut.activated.connect(self.on_open)
        self.delete_shortcut.activated.connect(self.on_delete)
        self.goto_parent_path_shortcut.activated.connect(
            self.slot_goto_parent_path)

        self.customContextMenuRequested.connect(self.show_context_menu)

    def init_context_menu(self):
        """
        初始化右键菜单
        :return:
        """
        self.contextMenu = QMenu(self)
        self.openAction = self.contextMenu.addAction(self.tr('Open'))

        self.importAction = self.contextMenu.addAction(self.tr('Import'))
        self.importAction.setEnabled(False)

        self.new_file_or_folder_menu = QMenu(self.tr('New..'))
        self.contextMenu.addMenu(self.new_file_or_folder_menu)
        self.new_file_action = self.new_file_or_folder_menu.addAction(
            self.tr('File..'))
        self.new_python_file_action = self.new_file_or_folder_menu.addAction(
            self.tr('Python File'))
        self.new_folder_action = self.new_file_or_folder_menu.addAction(
            self.tr('Folder'))
        self.new_file_or_folder_menu.addSeparator()

        self.copyAction = self.contextMenu.addAction(self.tr("Copy"))
        self.pasteAction = self.contextMenu.addAction(self.tr("Paste"))
        self.pasteAction.setEnabled(False)

        self.renameAction = self.contextMenu.addAction(self.tr('Rename'))
        self.deleteAction = self.contextMenu.addAction(self.tr('Delete'))

        self.filterAction = self.contextMenu.addAction(self.tr('Filter'))
        self.copyPathAction = self.contextMenu.addAction(self.tr('Copy Path'))

        self.open_file_manager_action = self.contextMenu.addAction(
            self.tr('Open Explorer'))

        self.renameAction.setShortcut(QKeySequence('F2'))
        self.copyAction.setShortcut(QKeySequence('Ctrl+C'))
        self.pasteAction.setShortcut(QKeySequence('Ctrl+V'))
        self.deleteAction.setShortcut(QKeySequence('Delete'))

        self.rename_shortcut = QShortcut(QKeySequence('F2'),
                                         self,
                                         context=Qt.WidgetShortcut)
        self.copy_shortcut = QShortcut(QKeySequence.Copy,
                                       self,
                                       context=Qt.WidgetShortcut)
        self.paste_shortcut = QShortcut(QKeySequence.Paste,
                                        self,
                                        context=Qt.WidgetShortcut)
        self.delete_shortcut = QShortcut(QKeySequence('Delete'),
                                         self,
                                         context=Qt.WidgetShortcut)
        self.open_shortcut = QShortcut(QKeySequence('Return'),
                                       self,
                                       context=Qt.WidgetShortcut)
        self.goto_parent_path_shortcut = QShortcut(QKeySequence('Backspace'),
                                                   self,
                                                   context=Qt.WidgetShortcut)

    def show_context_menu(self):
        """
        显示上下文右键菜单
        :return:
        """
        self.contextMenu.popup(QCursor.pos())
        self.contextMenu.show()

    def get_current_file_path(self):
        """
        获取当前选中文件的路径。
        如果当前没有选中的文件,就返回根路径。
        :return:
        """
        if len(self.selectedIndexes()) > 0:
            index = self.currentIndex()
            file_info = self.model.fileInfo(index)
            return file_info.absoluteFilePath()
        else:
            return self.get_root_path()

    def get_root_path(self):
        """
        获取根路径
        :return:
        """
        return self.model.rootPath()

    def set_item_focus(self, file_path: str):
        """
        set item focus in TreeView
        :param file_path: File or Dir
        :return:
        """
        self.setCurrentIndex(self.model.index(file_path))

    def on_open_file_manager(self):
        path = self.get_current_file_path()
        print(path)
        if os.path.isdir(path):
            open_file_manager(path)
        else:
            open_file_manager(os.path.dirname(path))

        # if os.path.exists(new_folder_path):
        #     self.set_item_focus(new_folder_path)  # 设置focus liugang 200923
        #     QMessageBox.critical(self, self.tr('Error'),
        #                          self.tr('Folder %s already exists!' % name))
        #     return
        # else:
        #     os.mkdir(new_folder_path)
        #     self.new_folder_signal[str].emit(new_folder_path)
        #     self.set_item_focus(new_folder_path)  # 设置focus liugang 200923

    def on_new_folder(self):
        """
        新建文件夹时出发的回调
        :return:
        """
        path = self.get_current_file_path()
        name, stat = QInputDialog.getText(self,
                                          self.tr('Please Input folder name'),
                                          '', QLineEdit.Normal, '')
        if name.find('.') != -1:
            QMessageBox.critical(self, self.tr('Error'),
                                 self.tr('Folder name %s is illeagal!' % name))
            return
        if stat:
            if os.path.isdir(path):
                new_folder_path = os.path.join(path, name)
            else:
                new_folder_path = os.path.join(os.path.dirname(path), name)

            if os.path.exists(new_folder_path):
                self.set_item_focus(new_folder_path)  # 设置focus liugang 200923
                QMessageBox.critical(
                    self, self.tr('Error'),
                    self.tr('Folder %s already exists!' % name))
                return
            else:
                os.mkdir(new_folder_path)
                self.new_folder_signal[str].emit(new_folder_path)
                self.set_item_focus(new_folder_path)  # 设置focus liugang 200923

    def on_new_file(self, ext: str = ''):
        """
        新建文件时触发的回调
        :return:
        """
        path = self.get_current_file_path()
        dlg = InputFilenameDialog(parent=self,
                                  title=self.tr('Please input file name'),
                                  ext=ext)

        dlg.exec_()
        name = dlg.name_input.text()
        stat = dlg.status
        if stat:
            if os.path.isdir(path):
                new_file_path = os.path.join(path, name)
            else:
                new_file_path = os.path.join(os.path.dirname(path), name)

            if os.path.exists(new_file_path):
                self.set_item_focus(new_file_path)  # 设置focus  liugang 200923
                QMessageBox.critical(self, self.tr('Error'),
                                     self.tr('File %s already exists!' % name))
                return
            with open(new_file_path, 'wb') as f:
                f.close()
                self.new_file_signal[str].emit(new_file_path)

            self.set_item_focus(new_file_path)
            self.on_open()  # 创建文件后打开  liugang 200923

    def on_open(self):
        """
        点击‘open’时候触发的回调, 等效的方式还有双击以及按下回车键。
        :return:
        """
        path = self.get_current_file_path()
        if os.path.isdir(path):
            self.open_folder_signal.emit(path)
        else:
            self.open_signal[str].emit(path)

    def on_import(self):
        """

        :return:
        """
        pass

    def on_rename(self):
        """
        点击’重命名‘时候的回调。
        :return:
        """
        from pmgwidgets import rename_file
        path = self.get_current_file_path()
        basename = os.path.basename(path)
        dir_name = os.path.dirname(path)
        name, stat = QInputDialog.getText(self,
                                          self.tr('Please Input file name'),
                                          '', QLineEdit.Normal, basename)
        if stat:
            new_absolute_path = os.path.join(dir_name, name)
            rename_result = rename_file(path, new_absolute_path)
            if not rename_result:
                QMessageBox.critical(self, self.tr('Error'),
                                     self.tr('Unable to Rename this file.'))
            else:
                self.rename_file_signal[str, str].emit(path, new_absolute_path)

    def on_delete(self):
        """
        点击’删除‘时的回调
        :return:
        """
        from pmgwidgets import move_to_trash
        path = self.get_current_file_path()

        moved_successful = move_to_trash(path)
        if not moved_successful:
            QMessageBox.critical(
                self, self.tr('Error'),
                self.tr('Unable to Move this file to recycle bin.'))
        else:
            self.delete_file_signal[str].emit(path)

    def on_copy(self):
        """
        copy file or dir , save path in pasteAction data.
        :return:
        """
        path = self.get_current_file_path()
        self.pasteAction.setEnabled(True)
        self.pasteAction.setData(path)

        data = QMimeData()
        data.setUrls([QUrl.fromLocalFile(path)])  # 复制到系统剪贴板

        clip = QApplication.clipboard()
        clip.setMimeData(data)

    def on_paste(self):
        """
        Paste file or dir in pasteAction data
        :return:
        """
        from pmgwidgets import copy_paste
        path = self.get_current_file_path()
        target_dir_name = path if os.path.isdir(path) else os.path.dirname(
            path)
        url: QUrl = None

        mimedata = QApplication.clipboard().mimeData(mode=QClipboard.Clipboard)
        print(mimedata)
        urls: List[QUrl] = mimedata.urls()
        for url in urls:
            source_path = url.toLocalFile()  # self.pasteAction.data()
            # File
            if os.path.isfile(source_path):
                source_file_name = os.path.basename(source_path)
                # if exist ,rename to copy_xxx
                if os.path.isfile(
                        os.path.join(target_dir_name, source_file_name)):
                    target_file_name = "copy_{0}".format(source_file_name)
                else:
                    target_file_name = source_file_name
                target_path = os.path.join(target_dir_name, target_file_name)
            # Directory
            else:
                last_dir_name = os.path.split(source_path)[-1]
                # if exist , rename dir copy_xxxx
                if os.path.isdir(os.path.join(target_dir_name, last_dir_name)):
                    target_name = "copy_{0}".format(last_dir_name)
                else:
                    target_name = last_dir_name
                target_path = os.path.join(target_dir_name, target_name)

            copy_succ = copy_paste(source_path, target_path)
            if not copy_succ:
                QMessageBox.critical(self, self.tr('Error'),
                                     self.tr('Copy File or Directory Error.'))
            else:
                self.set_item_focus(target_path)

    def show_ext_filter_selection_dialog(self):

        self.dlg = QDialog(self)
        self.dlg.setWindowTitle(self.tr('Extension Name To Show'))
        self.dlg.setLayout(QVBoxLayout())
        self.dlg.layout().addWidget(QLabel('过滤文件名'))
        check_box = QCheckBox()
        self.dlg.check_box = check_box
        check_box.setChecked(self.filter_exts)
        self.dlg.layout().addWidget(check_box)
        check_box.stateChanged.connect(
            lambda stat: self.signal_ext_filter_adapt.emit(stat))
        check_widget = PMCheckTree(data=self.exts_to_filter)
        self.dlg.check_widget = check_widget
        self.dlg.layout().addWidget(check_widget)
        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok
                                     | QDialogButtonBox.Cancel)

        buttonBox.accepted.connect(self.on_ext_filter_changed)
        buttonBox.rejected.connect(self.dlg.deleteLater)
        # 清除选择功能不完善目前禁用
        # button_clear = buttonBox.addButton(self.tr('Clear Filter'), QDialogButtonBox.ApplyRole)
        # button_clear.clicked.connect(self.clear_ext_filter)
        self.dlg.layout().addWidget(buttonBox)
        self.dlg.exec_()

    def on_ext_filter_changed(self):
        """
        当扩展名过滤改变的时候。
        :return:
        """
        self.exts_to_filter = self.dlg.check_widget.get_data()
        self.filter_exts = self.dlg.check_box.isChecked()
        self.update_ext_filter()
        self.dlg.deleteLater()
        self.signal_ext_filter_changed.emit(self.exts_to_filter)

    def clear_ext_filter(self):
        self.set_ext_filter(None)
        self.dlg.deleteLater()

    def update_ext_filter(self):
        """
        刷新扩展名过滤。
        :return:
        """
        ext_list = []
        for key in self.exts_to_filter.keys():
            for name in self.exts_to_filter[key].keys():
                if self.exts_to_filter[key][name]:
                    ext_list.append('*' + name)
        self.set_ext_filter(ext_list)

    def set_ext_filter(self, ext_names: List[str]):
        """
        文件名过滤
        例如要过滤出.py和.pyx文件,就是ext_names=['*.py','*.pyx']
        discard功能不太完善,目前先禁用。
        :param ext_names:
        :return:
        """
        if ext_names is not None and self.filter_exts:
            self.model.setNameFilterDisables(False)
            self.model.setNameFilters(ext_names)
        else:
            self.model.setNameFilterDisables(True)
            self.model.setNameFilters(["*"])

    def slot_goto_parent_path(self):
        """

        Returns:

        """
        root = self.get_root_path()
        parent = os.path.dirname(root)
        if os.path.exists(parent):
            pass
        self.open_folder_signal.emit(parent)

    def copy_path(self):
        """
        复制当前文件的路径的回调
        Returns:

        """
        path = self.get_current_file_path()
        # data = QMimeData()
        clipboard = QApplication.clipboard()
        clipboard.setText(path)
Exemplo n.º 17
0
class Q7Tree(Q7Window, Ui_Q7TreeWindow):
    def __init__(self, control, path, fgprintindex):
        Q7Window.__init__(self, Q7Window.VIEW_TREE, control, path, fgprintindex)
        self._depthExpanded = 0
        self._lastEntered = None
        self.lastdiag = None
        self._linkwindow = None
        self._querywindow = None
        self._vtkwindow = None
        self._selectwindow = None
        self._column = {NMT.COLUMN_SIDS: OCTXT.ShowSIDSColumn,
                        NMT.COLUMN_FLAG_LINK: OCTXT.ShowLinkColumn,
                        NMT.COLUMN_FLAG_SELECT: OCTXT.ShowSelectColumn,
                        NMT.COLUMN_FLAG_CHECK: OCTXT.ShowCheckColumn,
                        NMT.COLUMN_FLAG_USER: OCTXT.ShowUserColumn,
                        NMT.COLUMN_SHAPE: OCTXT.ShowShapeColumn,
                        NMT.COLUMN_DATATYPE: OCTXT.ShowDataTypeColumn}
        self.selectForLinkSrc = None  # one link source per tree view allowed

        #self.treeview.expanded[QModelIndex].connect(self.expandNode)
        self.treeview.collapsed.connect(self.collapseNode)
        self.treeview.pressed[QModelIndex].connect(self.clickedPressedNode)
        self.treeview.customContextMenuRequested.connect(self.clickedNode)

        # QObject.connect(self.treeview,
        #                SIGNAL("expanded(QModelIndex)"),
        #                self.expandNode)
        # QObject.connect(self.treeview,
        #                SIGNAL("collapsed()"),
        #                self.collapseNode)
        # QObject.connect(self.treeview,
        #                SIGNAL("pressed(QModelIndex)"),
        #                self.clickedPressedNode)
        # QObject.connect(self.treeview,
        #                SIGNAL("customContextMenuRequested(QPoint)"),
        #                self.clickedNode)

        self.bSave.clicked.connect(self.savetree)
        self.lockable(self.bSave)
        self.bQueryView.clicked.connect(self.queryview)
        self.lockable(self.bQueryView)
        self.bSaveAs.clicked.connect(self.savetreeas)
        self.lockable(self.bSaveAs)
        self.bInfo.clicked.connect(self.infoTreeView)
        self.bZoomIn.clicked.connect(self.expandLevel)
        self.bZoomOut.clicked.connect(self.collapseLevel)
        self.bZoomAll.clicked.connect(self.expandMinMax)
        self.bFormView.clicked.connect(self.formview)
        self.bMarkAll.clicked.connect(self.markall)
        self.bUnmarkAll_1.clicked.connect(self.unmarkall)
        self.bUnmarkAll_2.clicked.connect(self.unmarkall)
        self.bPreviousMark.clicked.connect(self.previousmark)
        self.bNextMark.clicked.connect(self.nextmark)
        self.bSwapMarks.clicked.connect(self.swapmarks)
        self.bMarksAsList.clicked.connect(self.selectionlist)
        self.bVTKView.clicked.connect(self.vtkview)
        self.lockable(self.bVTKView)
        self.bScreenShot.clicked.connect(self.screenshot)
        self.bCheck.clicked.connect(self.check)
        self.bCheckList.clicked.connect(self.checklist)
        self.bClearChecks.clicked.connect(self.clearchecks)
        self.bLinkView.clicked.connect(self.linklist)
        self.bPatternView.clicked.connect(self.patternlist)
        self.bToolsView.clicked.connect(self.tools)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.popupmenu = QMenu()
        self.diagview = None
        lmodel = self.FG.model
        self.treeview.setModel(lmodel)
        self.treeview.setItemDelegate(Q7TreeItemDelegate(self.treeview, lmodel))
        self.treeview.setControlWindow(self, self.FG.index)
        if (self._control.transientRecurse or OCTXT.RecursiveTreeDisplay):
            self.expandMinMax()
        if (self._control.transientVTK): self.vtkview()
        self._control.transientRecurse = False
        self._control.transientVTK = False
        self.clearchecks()
        #
        self.bCheckList.setDisabled(True)
        if (not OCTXT._HasProPackage):
            self.bToolsView.setDisabled(True)
        self.bCheckView.setDisabled(True)
        self.bPatternDB.setDisabled(True)
        self.bAddLink.clicked.connect(self.linkadd)
        self.bSelectLinkSrc.clicked.connect(self.linkselectsrc)
        self.bSelectLinkDst.clicked.connect(self.linkselectdst)
        self.bAddLink.setDisabled(True)
        self.lineEdit.returnPressed.connect(self.jumpToNode)
        # QObject.connect(self.lineEdit,
        #                SIGNAL("returnPressed()"),
        #                self.jumpToNode)
        tvh = self.treeview.header()
        tvh.setContextMenuPolicy(Qt.CustomContextMenu)
        tvh.customContextMenuRequested.connect(self.headerMenu)
        self._hmenu = QMenu()
        self._hmenu._idx = {}
        self._tlist = (('SIDS type', NMT.COLUMN_SIDS),
                       ('Link flag', NMT.COLUMN_FLAG_LINK),
                       ('Mark flag', NMT.COLUMN_FLAG_SELECT),
                       ('Check flag', NMT.COLUMN_FLAG_CHECK),
                       ('User flag', NMT.COLUMN_FLAG_USER),
                       ('Shape', NMT.COLUMN_SHAPE),
                       ('Data type', NMT.COLUMN_DATATYPE))
        for (tag, idx) in self._tlist:
            a = QAction(tag, self._hmenu, checkable=True)
            self._hmenu._idx[idx] = a
            if (self._column[idx]):
                a.setChecked(True)
            else:
                a.setChecked(False)
            self._hmenu.addAction(a)
            self.treeview.setColumnHidden(idx, not self._column[idx])
        self._recursiveAddNewNode = False
        self.updateTreeStatus()

    def headerMenu(self, pos):
        for (tag, idx) in self._tlist:
            self._hmenu._idx[idx].setChecked(self._column[idx])
        self._hmenu.exec_(self.treeview.mapToGlobal(pos))
        for (tag, idx) in self._tlist:
            if (self._hmenu._idx[idx].isChecked()):
                self._column[idx] = True
            else:
                self._column[idx] = False
            self.treeview.setColumnHidden(idx, not self._column[idx])

    def model(self):
        return self.FG.model

    def modelIndex(self, idx):
        if not idx.isValid():
            return -1
        midx = idx
        if idx.model() != self.treeview.M():
            midx = self.treeview.model().mapToSource(idx)
        return midx

    def modelData(self, idx):
        if not idx.isValid():
            return None
        return self.modelIndex(idx).internalPointer()

    def savetree(self):
        if (not (self.FG.isSaveable() and self.FG.isModified())):
            return
        self._control.savedirect(self.FG)
        self.updateTreeStatus()

    def tools(self):
        from CGNS.NAV.wtools import Q7ToolsView
        if (self._control._toolswindow is None):
            self._control._toolswindow = Q7ToolsView(self._control,
                                                     self.FG,
                                                     self)
            self._control._toolswindow.show()
        else:
            self._control._toolswindow.raise_()

    def savetreeas(self):
        self._control.save(self.FG)
        self.updateTreeStatus()

    def infoTreeView(self):
        self._control.helpWindow('Tree')

    def screenshot(self):
        self.treeview.model().sort(0)
        sshot = QScreen.grabWindow(self.treeview.winId())
        sshot.save('/tmp/foo.png', 'png')

    def expandMinMax(self):
        if (self._depthExpanded == self.FG.depth - 2):
            self._depthExpanded = -1
            self.treeview.collapseAll()
        else:
            self._depthExpanded = self.FG.depth - 2
            self.treeview.expandAll()
        self.resizeAll()

    def resetOptions(self):
        if (OCTXT.AutoExpand):
            self.treeview.setAutoExpandDelay(1000)
        else:
            self.treeview.setAutoExpandDelay(-1)

    def expandLevel(self):
        if (self._depthExpanded < self.FG.depth - 2):
            self._depthExpanded += 1
        self.treeview.expandToDepth(self._depthExpanded)
        self.resizeAll()

    def collapseLevel(self):
        if (self._depthExpanded != -1): self._depthExpanded -= 1
        if (self._depthExpanded == -1):
            self.treeview.collapseAll()
        else:
            self.treeview.expandToDepth(self._depthExpanded)
        self.resizeAll()

    def updateStatus(self, node):
        if (not self.lineEditLock.isChecked()):
            self.lineEdit.clear()
            self.lineEdit.insert(node.sidsPath())

    def jumpToNode(self):
        path = self.lineEdit.text()
        self.treeview.selectByPath(path)

    def popform(self):
        self.formview()

    def openLkTree(self):
        self.busyCursor()
        filename = self.getLastEntered().sidsLinkFilename()
        if (filename is not None):
            self._control.loadfile(filename)
        self.readyCursor()

    def openSubTree(self):
        self.busyCursor()
        node = self.getLastEntered().sidsPath()
        child = Q7Tree(self._control, node, self.FG)
        self.readyCursor()
        child.show()

    def pop0(self):
        pass

    def newnodebrother(self):
        if (self.getLastEntered() is not None):
            self.model().newNodeBrother(self.getLastEntered())

    def newnodechild(self):
        if (self.getLastEntered() is not None):
            self.model().newNodeChild(self.getLastEntered())

    def marknode(self):
        if (self.getLastEntered() is not None):
            self.treeview.markNode(self.getLastEntered())

    def mcopy(self):
        if (self.getLastEntered() is not None):
            self.model().copyNode(self.getLastEntered())
            self.clearOtherSelections()

    def mcutselected(self):
        self.model().cutAllSelectedNodes()
        self.clearLastEntered()
        self.clearOtherSelections()

    def mcut(self):
        if (self.getLastEntered() is not None):
            self.model().cutNode(self.getLastEntered())
            self.clearLastEntered()
            self.clearOtherSelections()

    def mpasteasbrotherselected(self):
        self.model().pasteAsBrotherAllSelectedNodes()

    def mpasteasbrother(self):
        if (self.getLastEntered() is not None):
            self.model().pasteAsBrother(self.getLastEntered())

    def mpasteaschildselected(self):
        self.model().pasteAsChildAllSelectedNodes()

    def mpasteaschild(self):
        if (self.getLastEntered() is not None):
            self.model().pasteAsChild(self.getLastEntered())

    def updateMenu(self, nodeidxs):
        nodeidx = self.modelIndex(nodeidxs)
        if (not nodeidx.isValid): return False
        if (nodeidx.internalPointer() is None): return False
        if (nodeidx.internalPointer().sidsPath() == '/CGNSTree'): return False
        self.setLastEntered(nodeidxs)
        if (nodeidx != -1):
            node = nodeidx.internalPointer()
            lknode = not node.sidsIsLink()
            lznode = node.hasLazyLoad()
            actlist = (
                ("%s goodies" % node.sidsType(),),
                None,
                ("Expand sub-tree from this node", self.expand_sb, 'Ctrl++', False),
                ("Collapses sub-tree from this node", self.collapse_sb, 'Ctrl+-', False),                
                None,
                ['Mark nodes...',[
                ("Mark/unmark node", self.marknode, 'Space', False),
                 None,
                ("Mark all nodes same SIDS type", self.marknode_t, 'Ctrl+1', False),
                ("Mark all nodes same name", self.marknode_n, 'Ctrl+2', False),
                ("Mark all nodes same value", self.marknode_v, 'Ctrl+3', False),
                 None,
                ("Mark parent path", self.marknode_p, 'Ctrl+4', False)]],
                ("Add new child node", self.newnodechild, 'Ctrl+A', False),
                ("Add new brother node", self.newnodebrother, 'Ctrl+Z', False),
                #            None,
                #            ("Open form",self.popform,'Ctrl+F',False),
                #            ("Open view",self.openSubTree,'Ctrl+W',False),
                #            ("Open view on linked-to file",self.openLkTree,'Ctrl+O',lknode),
                None,
                ("Load node data in memory", self.dataLoad, 'Ctrl+L', not lznode),
                ("Release memory node data", self.dataRelease, 'Ctrl+R', lznode),
                None,
                ("Copy current", self.mcopy, 'Ctrl+C', False),
                ("Cut current", self.mcut, 'Ctrl+X', False),
                ("Paste as brother", self.mpasteasbrother, 'Ctrl+V', False),
                ("Paste as child", self.mpasteaschild, 'Ctrl+Y', False),
                None,
                ['On selected nodes...',[
                ("Expand sub-tree from all selected nodes", self.sexpand_sb, 'Ctrl+Shift++', False),
                ("Collapses sub-tree from all selected nodes", self.scollapse_sb, 'Ctrl+Shift+-', False),
                None,
                ("Cut all selected", self.mcutselected, 'Ctrl+Shift+X', False),
                ("Paste as brother for each selected",
                 self.mpasteasbrotherselected, 'Ctrl+Shift+V', False),
                ("Paste as child for each selected",
                 self.mpasteaschildselected, 'Ctrl+Shift+Y', False),
                 None,
                ("Load nodes data in memory for each selected",
                 self.dataLoadSelected, 'Ctrl+Shift+L', False),
                ("Release memory node data for each selected",
                 self.dataReleaseSelected, 'Ctrl+Shift+R', False)]],
            )
            self.popupmenu.clear()
            self.popupmenu.setTitle('Node menu')
            for aparam in actlist:
                if (aparam is None):
                    self.popupmenu.addSeparator()
                elif (len(aparam) == 1):
                    stp = node.sidsType()
                    tag = '_GM_{}'.format(stp)
                    subm = self.popupmenu.addMenu('{}...'.format(stp))
                    a = QAction("About %s" % node.sidsType(), self,
                                triggered=self.aboutSIDS)
                    subm.addAction(a)
                    patmenu = subm.addMenu('Insert pattern')
                    self.patternMenu(patmenu, node.sidsNode())
                    subm.addSeparator()
                    if (hasattr(self, tag)):
                        getattr(self, tag)(subm, node)
                else:
                    if isinstance(aparam,list):
                        subm = self.popupmenu.addMenu(aparam[0])
                        for aaparam in aparam[1]:
                            if (aaparam is None):
                                 subm.addSeparator()
                            else:
                                a = QAction(aaparam[0], self, triggered=aaparam[1])
                                if (aaparam[2] is not None): a.setShortcut(aaparam[2])
                                subm.addAction(a)
                                a.setDisabled(aaparam[3])
                    else:
                        a = QAction(aparam[0], self, triggered=aparam[1])
                        if (aparam[2] is not None): a.setShortcut(aparam[2])
                        self.popupmenu.addAction(a)
                        a.setDisabled(aparam[3])
            return True

    def _runAndSelect(self, qname, value):
        q = Q7Query.getQuery(qname)
        sl = q.run(self.FG.tree, self.FG.links, list(self.FG.lazy), False, value)
        self.model().markExtendToList(sl)
        self.model().updateSelected()
        self.treeview.refreshView()

    def patternMenu(self, menu, node):
        a = QAction("Recursive sub-pattern add", self, checkable=True)
        menu.addAction(a)
        a.setChecked(self._recursiveAddNewNode)
        menu.addSeparator()
        for t in [n[0] for n in CGU.getAuthChildren(node)]:
            def genCopyPattern(arg):
                def copyPattern():
                    self.model().copyNodeRaw(CGS.profile[arg][0])
                    if (self.getLastEntered() is not None):
                        self.model().pasteAsChild(self.getLastEntered())
                return copyPattern
            a = QAction("{}".format(t), self, triggered=genCopyPattern(t))
            menu.addAction(a)
        
    def _gm_family_1(self, node):
        self._runAndSelect('013. FamilyName reference', "'%s'" % node.sidsName())

    def _gm_family_2(self, node):
        self._runAndSelect('003. Node type', "'Family_t'")

    def _GM_Family_t(self, m, node):
        a = QAction('Select references to myself', self)
        a.triggered.connect(functools.partial(self._gm_family_1, node))
        m.addAction(a)
        a = QAction('Select all families', self)
        a.triggered.connect(functools.partial(self._gm_family_2, node))
        m.addAction(a)
        m.addSeparator()
        return True

    def _GM_IndexRange_t(self, m, node):
        if (node.sidsName() != CGK.ElementRange_s):
            v = 0
            a = QAction('Range size: %d' % (v), self)
            m.addAction(a)
        else:
            v = node.sidsValue()[1] - node.sidsValue()[0]
            etp = CGU.getEnumAsString(node.sidsParent())
            a = QAction('Number of elements of type [%s]: %d' % (etp, v), self)
            m.addAction(a)
        return True

    def _GM_Elements_t(self, m, node):
        etp = CGU.getEnumAsString(node.sidsNode())
        npe = CGK.ElementTypeNPE[etp]
        a = QAction('Element type [%s] npe [%d]' % (etp, npe), self)
        m.addAction(a)
        return True

    def marknode_t(self):
        node = self.getLastEntered()
        self._runAndSelect('003. Node type', "'%s'" % node.sidsType())

    def marknode_n(self):
        node = self.getLastEntered()
        self._runAndSelect('001. Node name', "'%s'" % node.sidsName())

    def marknode_v(self):
        node = self.getLastEntered()
        value = node.sidsValue()
        self._runAndSelect('005. Node value', value)

    def marknode_p(self):
        node = self.getLastEntered()
        node.switchMarked()
        path = node.sidsPath()
        while path is not None:
            path = CGU.getPathAncestor(path)
            if (path not in ['/', None]):
                node = self.model().nodeFromPath('/CGNSTree' + path)
                node.switchMarked()
        self.model().updateSelected()

    def setLastEntered(self, nix=None):
        if ((nix is None) or (not nix.isValid())):
            nix = self.treeview.modelCurrentIndex()
        self._lastEntered = None
        if (nix.isValid()):
            self.treeview.exclusiveSelectRow(nix, False)
            self._lastEntered = self.modelData(nix)

    def getLastEntered(self):
        return self._lastEntered

    def clearLastEntered(self):
        self._lastEntered = None
        self.treeview.selectionModel().clearSelection()
        return None

    def clearOtherSelections(self):
        if (self._control._patternwindow is not None):
            self._control._patternwindow.clearSelection()

    def clickedPressedNode(self, index):
        self.clickedNode(index)

    def clickedNode(self, index):
        self.treeview.exclusiveSelectRow(index)
        if (self.treeview.lastButton == Qt.RightButton):
            if (self.updateMenu(index)):
                self.popupmenu.popup(self.treeview.lastPos)

    def expandNode(self, *args):
        self.resizeAll()

    def collapseNode(self, *args):
        pass

    def expand_sb(self):
        self.treeview.expand_sb()
    
    def collapse_sb(self):
        self.treeview.collapse_sb()
    
    def sexpand_sb(self):
        self.treeview.sexpand_sb()
    
    def scollapse_sb(self):
        self.treeview.scollapse_sb()
    
    def resizeAll(self):
        for n in range(NMT.COLUMN_LAST + 1):
            self.treeview.resizeColumnToContents(n)

    def show(self):
        super(Q7Tree, self).show()

    def linkselectsrc(self):
        if (self.bSelectLinkSrc.isChecked()):
            if (self.getLastEntered() is None): return
            self.bAddLink.setDisabled(False)
            node = self.getLastEntered()
            self.selectForLinkSrc = (node, node.sidsPath())
        else:
            self.bAddLink.setDisabled(True)
            self.selectForLinkSrc = None

    def linkselectdst(self):
        if (self.getLastEntered() is None): return
        node = self.getLastEntered()
        if (node is None):  return
        if (node.sidsIsLink()): return
        if (node.sidsType() == CGK.CGNSTree_ts): return
        if (self._control.selectForLinkDst is not None):
            bt = self._control.selectForLinkDst[-1].bSelectLinkDst
            bt.setChecked(Qt.Unchecked)
            if (self._control.selectForLinkDst[-1] == self):
                self._control.selectForLinkDst = None
                return
        self._control.selectForLinkDst = (node, node.sidsPath(),
                                          self.FG.filedir,
                                          self.FG.filename,
                                          self)
        self.bSelectLinkDst.setChecked(Qt.Checked)
        if (self._linkwindow is not None):
            n = node.sidsPath()
            d = self.FG.filedir
            f = self.FG.filename
            self._linkwindow.updateSelected(d, f, n)

    def linkadd(self):
        if (self._control.selectForLinkDst is None): return
        dst = self._control.selectForLinkDst
        str_dst = "%s:%s" % (dst[3], dst[1])
        tpath = 'relative'
        newname = CGU.getPathLeaf(dst[1])
        if (CGU.checkDuplicatedName(self.selectForLinkSrc[0].sidsNode(),
                                    newname, dienow=False)):
            str_cnm = "New child node name is <b>%s</b>" % newname
        else:
            count = 0
            while (not CGU.checkDuplicatedName(self.selectForLinkSrc[0].sidsNode(),
                                               newname, dienow=False)):
                count += 1
                newname = '{%s#%.3d}' % (dst[0].sidsType(), count)
            str_cnm = """As a child with this name already exists, the name <b>%s</b> is used (generated name)""" % \
                      newname
        str_src = "%s:%s/%s" % (self.FG.filename,
                                self.selectForLinkSrc[1], newname)
        str_msg = "you want to create a link from <b>%s</b> to <b>%s</b><br>%s<br>Your current user options do force " \
                  "the link to use <b>%s</b> destination file path.""" % (
        str_src, str_dst, str_cnm, tpath)
        reply = MSG.wQuestion(self, 231, 'Create link as a new node', str_msg)

    def linklist(self):
        if (self._linkwindow is None):
            self._linkwindow = Q7LinkList(self._control, self.FG.index, self)
            self._linkwindow.show()
        else:
            self._linkwindow.raise_()

    def patternlist(self):
        if (self._control._patternwindow is None):
            self._control._patternwindow = Q7PatternList(self._control, self.FG)
            self._control._patternwindow.show()
        self._control._patternwindow.raise_()

    def check(self):
        self.busyCursor()
        if (self.diagview is not None):
            self.diagview.close()
            self.diagview = None
        self.lastdiag = self.model().checkSelected()
        self.readyCursor()
        self.treeview.refreshView()
        self.bCheckList.setDisabled(False)

    def checklist(self):
        if (self.lastdiag is None): return
        self.diagview = Q7CheckList(self, self.lastdiag, self.FG.index)
        self.diagview.show()

    def clearchecks(self):
        self.model().checkClear()
        self.treeview.refreshView()
        self.lastdiag = None
        self.bCheckList.setDisabled(True)

    def selectionlist(self):
        if (self._selectwindow is not None):
            self._selectwindow.close()
            self._selectwindow = None
        self._selectwindow = Q7SelectionList(self, self.model(), self.FG.index)
        self._selectwindow.show()
        self._selectwindow.raise_()

    def previousmark(self):
        self.treeview.changeSelectedMark(-1)

    def nextmark(self):
        self.treeview.changeSelectedMark(+1)

    def markall(self):
        self.model().markAll()
        self.model().updateSelected()
        self.treeview.refreshView()

    def unmarkall(self):
        self.model().unmarkAll()
        self.model().updateSelected()
        self.treeview.refreshView()

    def swapmarks(self):
        self.model().swapMarks()
        self.model().updateSelected()
        self.treeview.refreshView()

    def formview(self):
        ix = self.treeview.modelCurrentIndex()
        node = self.modelData(ix)
        if (node is None):
            MSG.wInfo(self, 254, "Form view:",
                      """You have to select a node to open its form view""",
                      again=False)
            return
        if (node.sidsType() == CGK.CGNSTree_ts): return
        form = Q7Form(self._control, node, self.FG.index)
        form.show()

    def vtkview(self):
        if (not HAS_VTK): return
        from CGNS.NAV.wvtk import Q7VTK
        if (self._vtkwindow is None):
            self.busyCursor()
            ix = self.treeview.modelCurrentIndex()
            zlist = self.model().getSelectedZones()
            node = self.modelData(ix)
            self._vtkwindow = Q7VTK(self._control, self, node, self.FG.index,
                                    self.model(), zlist)
            if (self._vtkwindow._vtkstatus):
                self._vtkwindow.show()
            else:
                self._vtkwindow.close()
                self._vtkwindow = None
            self.readyCursor()
        else:
            self._vtkwindow.raise_()

    def plotview(self):
        return

    def queryview(self):
        if (self._querywindow is None):
            self._querywindow = Q7Query(self._control, self.FG.index, self)
            self._querywindow.show()
        else:
            self._querywindow.raise_()

    def aboutSIDS(self):
        path = self.getLastEntered().sidsPath()

    def dataLoadSelected(self):
        self.model().dataLoadSelected()

    def dataReleaseSelected(self):
        self.model().dataReleaseSelected()

    def dataLoad(self):
        node = self.getLastEntered()
        self.model().dataLoadSelected(single=node.sidsPath())

    def dataRelease(self):
        node = self.getLastEntered()
        self.model().dataReleaseSelected(single=node.sidsPath())

    def forceapply(self):
        pass

    def updateTreeStatus(self):
        if ((Q7FingerPrint.STATUS_MODIFIED in self.FG._status)
            and (Q7FingerPrint.STATUS_SAVEABLE in self.FG._status)):
            self.bSave.setEnabled(True)
        else:
            self.bSave.setEnabled(False)

    def doRelease(self):
        # break cyclic refs to allow garbage
        self.treeview.itemDelegate().doRelease()
        self.treeview.setItemDelegate(None)
        self.treeview.doRelease()
        self.treeview = None
Exemplo n.º 18
0
class Window:
    """Application window that contains the menu bar and viewer.

    Parameters
    ----------
    viewer : napari.components.ViewerModel
        Contained viewer widget.

    Attributes
    ----------
    file_menu : qtpy.QtWidgets.QMenu
        File menu.
    help_menu : qtpy.QtWidgets.QMenu
        Help menu.
    main_menu : qtpy.QtWidgets.QMainWindow.menuBar
        Main menubar.
    qt_viewer : QtViewer
        Contained viewer widget.
    view_menu : qtpy.QtWidgets.QMenu
        View menu.
    window_menu : qtpy.QtWidgets.QMenu
        Window menu.
    """

    raw_stylesheet = get_stylesheet()

    def __init__(self, viewer, *, show: bool = True):
        # create QApplication if it doesn't already exist
        # note: the return value must be retained to prevent garbage collection
        _ = get_app()

        # Connect the Viewer and create the Main Window
        self.qt_viewer = QtViewer(viewer)
        self._qt_window = _QtMainWindow()
        self._qt_window.setWindowTitle(self.qt_viewer.viewer.title)
        self._qt_center = self._qt_window.centralWidget()
        self._status_bar = self._qt_window.statusBar()

        # Dictionary holding dock widgets
        self._dock_widgets: Dict[str, QtViewerDockWidget] = {}
        self._plugin_menus: Dict[str, QMenu] = {}

        # since we initialize canvas before window, we need to manually connect them again.
        if self._qt_window.windowHandle() is not None:
            self._qt_window.windowHandle().screenChanged.connect(
                self.qt_viewer.canvas._backend.screen_changed)

        self._add_menubar()
        self._add_file_menu()
        self._add_view_menu()
        self._add_window_menu()
        self._add_plugins_menu()
        self._add_help_menu()

        self._status_bar.showMessage('Ready')
        self._help = QLabel('')
        self._status_bar.addPermanentWidget(self._help)

        self._qt_center.layout().addWidget(self.qt_viewer)
        self._qt_center.layout().setContentsMargins(4, 0, 4, 0)

        self._update_theme()

        self._add_viewer_dock_widget(self.qt_viewer.dockConsole, tabify=False)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerControls,
                                     tabify=False)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerList,
                                     tabify=False)
        self.window_menu.addSeparator()

        self.qt_viewer.viewer.events.status.connect(self._status_changed)
        self.qt_viewer.viewer.events.help.connect(self._help_changed)
        self.qt_viewer.viewer.events.title.connect(self._title_changed)
        self.qt_viewer.viewer.events.theme.connect(self._update_theme)

        if perf.USE_PERFMON:
            # Add DebugMenu and dockPerformance if using perfmon.
            self._debug_menu = DebugMenu(self)
            self._add_viewer_dock_widget(self.qt_viewer.dockPerformance)
        else:
            self._debug_menu = None

        if show:
            self.show()

    def _add_menubar(self):
        """Add menubar to napari app."""
        self.main_menu = self._qt_window.menuBar()
        # Menubar shortcuts are only active when the menubar is visible.
        # Therefore, we set a global shortcut not associated with the menubar
        # to toggle visibility, *but*, in order to not shadow the menubar
        # shortcut, we disable it, and only enable it when the menubar is
        # hidden. See this stackoverflow link for details:
        # https://stackoverflow.com/questions/50537642/how-to-keep-the-shortcuts-of-a-hidden-widget-in-pyqt5
        self._main_menu_shortcut = QShortcut(QKeySequence('Ctrl+M'),
                                             self._qt_window)
        self._main_menu_shortcut.activated.connect(
            self._toggle_menubar_visible)
        self._main_menu_shortcut.setEnabled(False)

    def _toggle_menubar_visible(self):
        """Toggle visibility of app menubar.

        This function also disables or enables a global keyboard shortcut to
        show the menubar, since menubar shortcuts are only available while the
        menubar is visible.
        """
        if self.main_menu.isVisible():
            self.main_menu.setVisible(False)
            self._main_menu_shortcut.setEnabled(True)
        else:
            self.main_menu.setVisible(True)
            self._main_menu_shortcut.setEnabled(False)

    def _add_file_menu(self):
        """Add 'File' menu to app menubar."""
        open_images = QAction('Open File(s)...', self._qt_window)
        open_images.setShortcut('Ctrl+O')
        open_images.setStatusTip('Open file(s)')
        open_images.triggered.connect(self.qt_viewer._open_files_dialog)

        open_stack = QAction('Open Files as Stack...', self._qt_window)
        open_stack.setShortcut('Ctrl+Alt+O')
        open_stack.setStatusTip('Open files')
        open_stack.triggered.connect(
            self.qt_viewer._open_files_dialog_as_stack_dialog)

        open_folder = QAction('Open Folder...', self._qt_window)
        open_folder.setShortcut('Ctrl+Shift+O')
        open_folder.setStatusTip('Open a folder')
        open_folder.triggered.connect(self.qt_viewer._open_folder_dialog)

        save_selected_layers = QAction('Save Selected Layer(s)...',
                                       self._qt_window)
        save_selected_layers.setShortcut('Ctrl+S')
        save_selected_layers.setStatusTip('Save selected layers')
        save_selected_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=True))

        save_all_layers = QAction('Save All Layers...', self._qt_window)
        save_all_layers.setShortcut('Ctrl+Shift+S')
        save_all_layers.setStatusTip('Save all layers')
        save_all_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=False))

        screenshot = QAction('Save Screenshot...', self._qt_window)
        screenshot.setShortcut('Alt+S')
        screenshot.setStatusTip(
            'Save screenshot of current display, default .png')
        screenshot.triggered.connect(self.qt_viewer._screenshot_dialog)

        screenshot_wv = QAction('Save Screenshot with Viewer...',
                                self._qt_window)
        screenshot_wv.setShortcut('Alt+Shift+S')
        screenshot_wv.setStatusTip(
            'Save screenshot of current display with the viewer, default .png')
        screenshot_wv.triggered.connect(self._screenshot_dialog)

        # OS X will rename this to Quit and put it in the app menu.
        exitAction = QAction('Exit', self._qt_window)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setMenuRole(QAction.QuitRole)

        def handle_exit():
            # if the event loop was started in gui_qt() then the app will be
            # named 'napari'. Since the Qapp was started by us, just close it.
            if QApplication.applicationName() == 'napari':
                QApplication.closeAllWindows()
                QApplication.quit()
            # otherwise, something else created the QApp before us (such as
            # %gui qt IPython magic).  If we quit the app in this case, then
            # *later* attempts to instantiate a napari viewer won't work until
            # the event loop is restarted with app.exec_().  So rather than
            # quit just close all the windows (and clear our app icon).
            else:
                QApplication.setWindowIcon(QIcon())
                self.close()

            if perf.USE_PERFMON:
                # Write trace file before exit, if we were writing one.
                # Is there a better place to make sure this is done on exit?
                perf.timers.stop_trace_file()

            _stop_monitor()
            _shutdown_chunkloader()

        exitAction.triggered.connect(handle_exit)

        self.file_menu = self.main_menu.addMenu('&File')
        self.file_menu.addAction(open_images)
        self.file_menu.addAction(open_stack)
        self.file_menu.addAction(open_folder)
        self.file_menu.addSeparator()
        self.file_menu.addAction(save_selected_layers)
        self.file_menu.addAction(save_all_layers)
        self.file_menu.addAction(screenshot)
        self.file_menu.addAction(screenshot_wv)
        self.file_menu.addSeparator()
        self.file_menu.addAction(exitAction)

    def _add_view_menu(self):
        """Add 'View' menu to app menubar."""
        toggle_visible = QAction('Toggle Menubar Visibility', self._qt_window)
        toggle_visible.setShortcut('Ctrl+M')
        toggle_visible.setStatusTip('Hide Menubar')
        toggle_visible.triggered.connect(self._toggle_menubar_visible)
        toggle_theme = QAction('Toggle Theme', self._qt_window)
        toggle_theme.setShortcut('Ctrl+Shift+T')
        toggle_theme.setStatusTip('Toggle theme')
        toggle_theme.triggered.connect(self.qt_viewer.viewer._toggle_theme)
        toggle_fullscreen = QAction('Toggle Full Screen', self._qt_window)
        toggle_fullscreen.setShortcut('Ctrl+F')
        toggle_fullscreen.setStatusTip('Toggle full screen')
        toggle_fullscreen.triggered.connect(self._toggle_fullscreen)
        toggle_play = QAction('Toggle Play', self._qt_window)
        toggle_play.triggered.connect(self._toggle_play)
        toggle_play.setShortcut('Ctrl+Alt+P')
        toggle_play.setStatusTip('Toggle Play')

        self.view_menu = self.main_menu.addMenu('&View')
        self.view_menu.addAction(toggle_fullscreen)
        self.view_menu.addAction(toggle_visible)
        self.view_menu.addAction(toggle_theme)
        self.view_menu.addAction(toggle_play)
        self.view_menu.addSeparator()

        # Add octree actions.
        if config.async_octree:
            toggle_outline = QAction('Toggle Chunk Outlines', self._qt_window)
            toggle_outline.triggered.connect(
                self.qt_viewer._toggle_chunk_outlines)
            toggle_outline.setShortcut('Ctrl+Alt+O')
            toggle_outline.setStatusTip('Toggle Chunk Outlines')
            self.view_menu.addAction(toggle_outline)

        # Add axes menu
        axes_menu = QMenu('Axes', parent=self._qt_window)
        axes_visible_action = QAction(
            'Visible',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.visible,
        )
        axes_visible_action.triggered.connect(self._toggle_axes_visible)
        axes_colored_action = QAction(
            'Colored',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.colored,
        )
        axes_colored_action.triggered.connect(self._toggle_axes_colored)
        axes_labels_action = QAction(
            'Labels',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.labels,
        )
        axes_labels_action.triggered.connect(self._toggle_axes_labels)
        axes_dashed_action = QAction(
            'Dashed',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.dashed,
        )
        axes_dashed_action.triggered.connect(self._toggle_axes_dashed)
        axes_arrows_action = QAction(
            'Arrows',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.arrows,
        )
        axes_arrows_action.triggered.connect(self._toggle_axes_arrows)
        axes_menu.addAction(axes_visible_action)
        axes_menu.addAction(axes_colored_action)
        axes_menu.addAction(axes_labels_action)
        axes_menu.addAction(axes_dashed_action)
        axes_menu.addAction(axes_arrows_action)
        self.view_menu.addMenu(axes_menu)

        # Add scale bar menu
        scale_bar_menu = QMenu('Scale Bar', parent=self._qt_window)
        scale_bar_visible_action = QAction(
            'Visible',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.scale_bar.visible,
        )
        scale_bar_visible_action.triggered.connect(
            self._toggle_scale_bar_visible)
        scale_bar_colored_action = QAction(
            'Colored',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.scale_bar.colored,
        )
        scale_bar_colored_action.triggered.connect(
            self._toggle_scale_bar_colored)
        scale_bar_ticks_action = QAction(
            'Ticks',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.scale_bar.ticks,
        )
        scale_bar_ticks_action.triggered.connect(self._toggle_scale_bar_ticks)
        scale_bar_menu.addAction(scale_bar_visible_action)
        scale_bar_menu.addAction(scale_bar_colored_action)
        scale_bar_menu.addAction(scale_bar_ticks_action)
        self.view_menu.addMenu(scale_bar_menu)

        self.view_menu.addSeparator()

    def _add_window_menu(self):
        """Add 'Window' menu to app menubar."""
        exit_action = QAction("Close Window", self._qt_window)
        exit_action.setShortcut("Ctrl+W")
        exit_action.setStatusTip('Close napari window')
        exit_action.triggered.connect(self._qt_window.close)

        clear_action = QAction("Remove Dock Widgets", self._qt_window)
        clear_action.setStatusTip('Remove all dock widgets')
        clear_action.triggered.connect(
            lambda e: self.remove_dock_widget('all'))

        self.window_menu = self.main_menu.addMenu('&Window')
        self.window_menu.addAction(exit_action)
        self.window_menu.addAction(clear_action)
        self.window_menu.addSeparator()

    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)

        # Get names of all plugins providing dock widgets or functions
        plugin_widgets = chain(plugins.dock_widgets, plugins.function_widgets)
        plugin_counts = Counter(plug_name for plug_name, _ in plugin_widgets)

        # Add submenu for each plugin with more than 1 item
        for plugin_name, count in plugin_counts.items():
            if count > 1:
                menu = QMenu(plugin_name, self._qt_window)
                self._plugin_menus[plugin_name] = menu
                self._plugin_dock_widget_menu.addMenu(menu)

        # Add a menu item (QAction) for each available plugin widget
        docks = zip(repeat("dock"), plugins.dock_widgets)
        funcs = zip(repeat("func"), plugins.function_widgets)
        for hook_type, key in chain(docks, funcs):
            plugin_name, wdg_name = key
            if plugin_name in self._plugin_menus:
                # this plugin has a submenu.
                action = QAction(wdg_name, parent=self._qt_window)
                self._plugin_menus[plugin_name].addAction(action)
            else:
                # this plugin only has one widget, add a namespaced menu item
                full_name = plugins.menu_item_template.format(*key)
                action = QAction(full_name, parent=self._qt_window)
                self._plugin_dock_widget_menu.addAction(action)

            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)

            action.triggered.connect(_add_widget)

        self.plugins_menu.addMenu(self._plugin_dock_widget_menu)

    def _show_plugin_sorter(self):
        """Show dialog that allows users to sort the call order of plugins."""
        plugin_sorter = QtPluginSorter(parent=self._qt_window)
        if hasattr(self, 'plugin_sorter_widget'):
            self.plugin_sorter_widget.show()
        else:
            self.plugin_sorter_widget = self.add_dock_widget(
                plugin_sorter, name='Plugin Sorter', area="right")

    def _show_plugin_install_dialog(self):
        """Show dialog that allows users to sort the call order of plugins."""

        self.plugin_dialog = QtPluginDialog(self._qt_window)
        self.plugin_dialog.exec_()

    def _show_plugin_err_reporter(self):
        """Show dialog that allows users to review and report plugin errors."""
        QtPluginErrReporter(parent=self._qt_window).exec_()

    def _add_help_menu(self):
        """Add 'Help' menu to app menubar."""
        self.help_menu = self.main_menu.addMenu('&Help')

        about_action = QAction("napari Info", self._qt_window)
        about_action.setShortcut("Ctrl+/")
        about_action.setStatusTip('About napari')
        about_action.triggered.connect(
            lambda e: QtAbout.showAbout(self.qt_viewer))
        self.help_menu.addAction(about_action)

        about_key_bindings = QAction("Show Key Bindings", self._qt_window)
        about_key_bindings.setShortcut("Ctrl+Alt+/")
        about_key_bindings.setShortcutContext(Qt.ApplicationShortcut)
        about_key_bindings.setStatusTip('key_bindings')
        about_key_bindings.triggered.connect(
            self.qt_viewer.show_key_bindings_dialog)
        self.help_menu.addAction(about_key_bindings)

    def _toggle_scale_bar_visible(self, state):
        self.qt_viewer.viewer.scale_bar.visible = state

    def _toggle_scale_bar_colored(self, state):
        self.qt_viewer.viewer.scale_bar.colored = state

    def _toggle_scale_bar_ticks(self, state):
        self.qt_viewer.viewer.scale_bar.ticks = state

    def _toggle_axes_visible(self, state):
        self.qt_viewer.viewer.axes.visible = state

    def _toggle_axes_colored(self, state):
        self.qt_viewer.viewer.axes.colored = state

    def _toggle_axes_labels(self, state):
        self.qt_viewer.viewer.axes.labels = state

    def _toggle_axes_dashed(self, state):
        self.qt_viewer.viewer.axes.dashed = state

    def _toggle_axes_arrows(self, state):
        self.qt_viewer.viewer.axes.arrows = state

    def _toggle_fullscreen(self, event):
        """Toggle fullscreen mode."""
        if self._qt_window.isFullScreen():
            self._qt_window.showNormal()
        else:
            self._qt_window.showFullScreen()

    def _toggle_play(self, state):
        """Toggle play."""
        if self.qt_viewer.dims.is_playing:
            self.qt_viewer.dims.stop()
        else:
            axis = self.qt_viewer.viewer.dims.last_used or 0
            self.qt_viewer.dims.play(axis)

    def _add_plugin_dock_widget(self, key):
        """Add plugin dock widget if not already added.

        Parameters
        ----------
        key : 2-tuple of str
            Plugin name and widget name.
        """
        from ..viewer import Viewer

        full_name = plugins.menu_item_template.format(*key)
        if full_name in self._dock_widgets:
            warnings.warn(f'Dock widget {key!r} already added')
            return

        Widget, dock_kwargs = plugins.dock_widgets[key]

        # if the signature is looking a for a napari viewer, pass it.
        kwargs = {}
        for param in inspect.signature(Widget.__init__).parameters.values():
            if param.name == 'napari_viewer':
                kwargs['napari_viewer'] = self.qt_viewer.viewer
                break
            if param.annotation in ('napari.viewer.Viewer', Viewer):
                kwargs[param.name] = self.qt_viewer.viewer
                break
            # cannot look for param.kind == param.VAR_KEYWORD because
            # QWidget allows **kwargs but errs on unknown keyword arguments

        # instantiate the widget
        wdg = Widget(**kwargs)

        # Add dock widget
        self.add_dock_widget(
            wdg,
            name=plugins.menu_item_template.format(*key),
            area=dock_kwargs.get('area', 'right'),
            allowed_areas=dock_kwargs.get('allowed_areas', None),
        )

    def _add_plugin_function_widget(self, key):
        """Add plugin function widget if not already added.

        Parameters
        ----------
        key : 2-tuple of str
            Plugin name and function name.
        """
        full_name = plugins.menu_item_template.format(*key)
        if full_name in self._dock_widgets:
            warnings.warn(f'Dock widget {key!r} already added')
            return

        func, magic_kwargs, dock_kwargs = plugins.function_widgets[key]

        # Add function widget
        self.add_function_widget(
            func,
            magic_kwargs=magic_kwargs,
            name=plugins.menu_item_template.format(*key),
            area=dock_kwargs.get('area', None),
            allowed_areas=dock_kwargs.get('allowed_areas', None),
        )

    def add_dock_widget(
        self,
        widget: QWidget,
        *,
        name: str = '',
        area: str = 'bottom',
        allowed_areas=None,
        shortcut=None,
    ):
        """Convenience method to add a QDockWidget to the main window

        Parameters
        ----------
        widget : QWidget
            `widget` will be added as QDockWidget's main widget.
        name : str, optional
            Name of dock widget to appear in window menu.
        area : str
            Side of the main window to which the new dock widget will be added.
            Must be in {'left', 'right', 'top', 'bottom'}
        allowed_areas : list[str], optional
            Areas, relative to main window, that the widget is allowed dock.
            Each item in list must be in {'left', 'right', 'top', 'bottom'}
            By default, all areas are allowed.
        shortcut : str, optional
            Keyboard shortcut to appear in dropdown menu.

        Returns
        -------
        dock_widget : QtViewerDockWidget
            `dock_widget` that can pass viewer events.
        """

        dock_widget = QtViewerDockWidget(
            self.qt_viewer,
            widget,
            name=name,
            area=area,
            allowed_areas=allowed_areas,
            shortcut=shortcut,
        )
        self._add_viewer_dock_widget(dock_widget)

        if hasattr(widget, 'reset_choices'):
            # Keep the dropdown menus in the widget in sync with the layer model
            # if widget has a `reset_choices`, which is true for all magicgui
            # `CategoricalWidget`s
            layers_events = self.qt_viewer.viewer.layers.events
            layers_events.inserted.connect(widget.reset_choices)
            layers_events.removed.connect(widget.reset_choices)
            layers_events.reordered.connect(widget.reset_choices)

        # Add dock widget to dictionary
        self._dock_widgets[dock_widget.name] = dock_widget

        return dock_widget

    def _add_viewer_dock_widget(self,
                                dock_widget: QtViewerDockWidget,
                                tabify=False):
        """Add a QtViewerDockWidget to the main window

        If other widgets already present in area then will tabify.

        Parameters
        ----------
        dock_widget : QtViewerDockWidget
            `dock_widget` will be added to the main window.
        tabify : bool
            Flag to tabify dockwidget or not.
        """
        # Find if any othe dock widgets are currently in area
        current_dws_in_area = []
        for dw in self._qt_window.findChildren(QDockWidget):
            if self._qt_window.dockWidgetArea(dw) == dock_widget.qt_area:
                current_dws_in_area.append(dw)

        self._qt_window.addDockWidget(dock_widget.qt_area, dock_widget)

        # If another dock widget present in area then tabify
        if len(current_dws_in_area) > 0 and tabify:
            self._qt_window.tabifyDockWidget(current_dws_in_area[-1],
                                             dock_widget)
            dock_widget.show()
            dock_widget.raise_()

        action = dock_widget.toggleViewAction()
        action.setStatusTip(dock_widget.name)
        action.setText(dock_widget.name)
        if dock_widget.shortcut is not None:
            action.setShortcut(dock_widget.shortcut)
        self.window_menu.addAction(action)

    def remove_dock_widget(self, widget: QWidget):
        """Removes specified dock widget.

        If a QDockWidget is not provided, the existing QDockWidgets will be
        searched for one whose inner widget (``.widget()``) is the provided
        ``widget``.

        Parameters
        ----------
        widget : QWidget | str
            If widget == 'all', all docked widgets will be removed.
        """
        if widget == 'all':
            for dw in list(self._dock_widgets.values()):
                self.remove_dock_widget(dw)
            return

        if not isinstance(widget, QDockWidget):
            for dw in self._qt_window.findChildren(QDockWidget):
                if dw.widget() is widget:
                    _dw: QDockWidget = dw
                    break
            else:
                raise LookupError(
                    f"Could not find a dock widget containing: {widget}")
        else:
            _dw = widget

        if _dw.widget():
            _dw.widget().setParent(None)
        self._qt_window.removeDockWidget(_dw)
        self.window_menu.removeAction(_dw.toggleViewAction())

        # Remove dock widget from dictionary
        del self._dock_widgets[_dw.name]

        # Deleting the dock widget means any references to it will no longer
        # work but it's not really useful anyway, since the inner widget has
        # been removed. and anyway: people should be using add_dock_widget
        # rather than directly using _add_viewer_dock_widget
        _dw.deleteLater()

    def add_function_widget(
        self,
        function,
        *,
        magic_kwargs=None,
        name: str = '',
        area=None,
        allowed_areas=None,
        shortcut=None,
    ):
        """Turn a function into a dock widget via magicgui.

        Parameters
        ----------
        function : callable
            Function that you want to add.
        magic_kwargs : dict, optional
            Keyword arguments to :func:`magicgui.magicgui` that
            can be used to specify widget.
        name : str, optional
            Name of dock widget to appear in window menu.
        area : str, optional
            Side of the main window to which the new dock widget will be added.
            Must be in {'left', 'right', 'top', 'bottom'}. If not provided the
            default will be determined by the widget.layout, with 'vertical'
            layouts appearing on the right, otherwise on the bottom.
        allowed_areas : list[str], optional
            Areas, relative to main window, that the widget is allowed dock.
            Each item in list must be in {'left', 'right', 'top', 'bottom'}
            By default, only provided areas is allowed.
        shortcut : str, optional
            Keyboard shortcut to appear in dropdown menu.

        Returns
        -------
        dock_widget : QtViewerDockWidget
            `dock_widget` that can pass viewer events.
        """
        from magicgui import magicgui

        widget = magicgui(function, **magic_kwargs or {})

        if area is None:
            if str(widget.layout) == 'vertical':
                area = 'right'
            else:
                area = 'bottom'

        if allowed_areas is None:
            allowed_areas = [area]

        return self.add_dock_widget(
            widget,
            name=name or function.__name__.replace('_', ' '),
            area=area,
            allowed_areas=allowed_areas,
            shortcut=shortcut,
        )

    def resize(self, width, height):
        """Resize the window.

        Parameters
        ----------
        width : int
            Width in logical pixels.
        height : int
            Height in logical pixels.
        """
        self._qt_window.resize(width, height)

    def show(self):
        """Resize, show, and bring forward the window."""
        self._qt_window.resize(self._qt_window.layout().sizeHint())
        self._qt_window.show()
        # Resize axis labels now that window is shown
        self.qt_viewer.dims._resize_axis_labels()

        # We want to bring the viewer to the front when
        # A) it is our own (gui_qt) event loop OR we are running in jupyter
        # B) it is not the first time a QMainWindow is being created

        # `app_name` will be "napari" iff the application was instantiated in
        # gui_qt(). isActiveWindow() will be True if it is the second time a
        # _qt_window has been created.
        # See #721, #732, #735, #795, #1594
        app_name = QApplication.instance().applicationName()
        if (app_name == 'napari'
                or in_jupyter()) and self._qt_window.isActiveWindow():
            self.activate()

    def activate(self):
        """Make the viewer the currently active window."""
        self._qt_window.raise_()  # for macOS
        self._qt_window.activateWindow()  # for Windows

    def _update_theme(self, event=None):
        """Update widget color theme."""
        # set window styles which don't use the primary stylesheet
        # FIXME: this is a problem with the stylesheet not using properties
        theme = get_theme(self.qt_viewer.viewer.theme)
        self._status_bar.setStyleSheet(
            template(
                'QStatusBar { background: {{ background }}; '
                'color: {{ text }}; }',
                **theme,
            ))
        self._qt_center.setStyleSheet(
            template('QWidget { background: {{ background }}; }', **theme))
        self._qt_window.setStyleSheet(template(self.raw_stylesheet, **theme))

    def _status_changed(self, event):
        """Update status bar.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._status_bar.showMessage(event.value)

    def _title_changed(self, event):
        """Update window title.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._qt_window.setWindowTitle(event.value)

    def _help_changed(self, event):
        """Update help message on status bar.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._help.setText(event.value)

    def _screenshot_dialog(self):
        """Save screenshot of current display with viewer, default .png"""
        dial = ScreenshotDialog(self.screenshot, self.qt_viewer,
                                self.qt_viewer._last_visited_dir)
        if dial.exec_():
            self._last_visited_dir = os.path.dirname(dial.selectedFiles()[0])

    def screenshot(self, path=None):
        """Take currently displayed viewer and convert to an image array.

        Parameters
        ----------
        path : str
            Filename for saving screenshot image.

        Returns
        -------
        image : array
            Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the
            upper-left corner of the rendered region.
        """
        img = self._qt_window.grab().toImage()
        if path is not None:
            imsave(path, QImg2array(img))  # scikit-image imsave method
        return QImg2array(img)

    def close(self):
        """Close the viewer window and cleanup sub-widgets."""

        # Someone is closing us twice? Only try to delete self._qt_window
        # if we still have one.
        if hasattr(self, '_qt_window'):
            self._delete_qt_window()

    def _delete_qt_window(self):
        """Delete our self._qt_window."""

        # On some versions of Darwin, exiting while fullscreen seems to tickle
        # some bug deep in NSWindow.  This forces the fullscreen keybinding
        # test to complete its draw cycle, then pop back out of fullscreen.
        if self._qt_window.isFullScreen():
            self._qt_window.showNormal()
            for i in range(8):
                time.sleep(0.1)
                QApplication.processEvents()
        self.qt_viewer.close()
        self._qt_window.close()
        del self._qt_window
Exemplo n.º 19
0
    def setup_ui(self):
        """Set up Ui of Context Menu."""
        self.setStyleSheet("""
            #{}{{
                min-width:11.29em;
            }}""".format(self.objectName()))
        hbl = QHBoxLayout(self)

        btn = QPushButton(self.name, self)
        hbl.addWidget(btn)
        btn.setEnabled(True)
        btn.setAutoDefault(False)

        lbl = QLabel(self)
        hbl.addWidget(lbl)
        sz_pol = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sz_pol.setHorizontalStretch(1)
        lbl.setSizePolicy(sz_pol)
        lbl.setMouseTracking(True)
        lbl.setAcceptDrops(True)
        lbl.setTextInteractionFlags(Qt.TextEditorInteraction)
        self.new_string_signal.connect(lbl.setText)

        menu = QMenu(btn)
        btn.setContextMenuPolicy(Qt.CustomContextMenu)
        btn.setMenu(menu)
        btn.clicked.connect(btn.showMenu)

        act = menu.addAction('Get From &File')
        act.setIcon(qta.icon('mdi.file-download-outline'))
        act.triggered.connect(self._load_orbit_from_file)
        act = menu.addAction('Get From &ServConf')
        act.setIcon(qta.icon('mdi.cloud-download-outline'))
        act.triggered.connect(self._load_orbit_from_servconf)
        menu2 = menu.addMenu('Get from &IOC')
        menu2.setIcon(qta.icon('mdi.download-network-outline'))
        if self._csorb.acc == 'SI':
            act = menu2.addAction('&SlowOrb')
            act.setIcon(qta.icon('mdi.turtle'))
            act.triggered.connect(_part(self._register_orbit, 'orb'))
        if self._csorb.isring:
            act = menu2.addAction('&MTurnOrb')
            act.setIcon(qta.icon('mdi.alarm-multiple'))
            act.triggered.connect(_part(self._register_orbit, 'mti'))
        act = menu2.addAction('S&PassOrb')
        act.setIcon(qta.icon('mdi.clock-fast'))
        act.triggered.connect(_part(self._register_orbit, 'sp'))
        act = menu2.addAction('&RefOrb')
        act.triggered.connect(_part(self._register_orbit, 'ref'))
        act = menu2.addAction('&OfflineOrb')
        act.setIcon(qta.icon('mdi.signal-off'))
        act.triggered.connect(_part(self._register_orbit, 'off'))
        act = menu2.addAction('&BPM Offsets')
        act.setIcon(qta.icon('mdi.currency-sign'))
        act.triggered.connect(_part(self._register_orbit, 'bpm'))
        act = menu2.addAction('RespMat')
        act.setIcon(qta.icon('mdi.matrix'))
        act.triggered.connect(self._open_matrix_sel)
        act = menu.addAction('&Edit Orbit')
        act.setIcon(qta.icon('mdi.table-edit'))
        act.triggered.connect(self._edit_orbit)
        if self._csorb.acc == 'SI':
            act = menu.addAction('Create &Bump')
            act.setIcon(
                qta.icon('mdi.chart-bell-curve',
                         scale_factor=1.2,
                         offset=(-0.2, 0.2)))
            act.triggered.connect(self._create_bump)
        act = menu.addAction('&Clear')
        act.setIcon(qta.icon('mdi.delete-empty'))
        act.triggered.connect(self._reset_orbit)
        act = menu.addAction('Save To File')
        act.setIcon(qta.icon('mdi.file-upload-outline'))
        act.triggered.connect(self._save_orbit_to_file)
        act = menu.addAction('Save To ServConf')
        act.setIcon(qta.icon('mdi.cloud-upload-outline'))
        act.triggered.connect(self._save_orbit_to_servconf)
Exemplo n.º 20
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.º 21
0
Arquivo: Menu.py Projeto: pixpil/gii
class MenuNode(object):
    _currentMenuContext = None
    """docstring for MenuNode"""
    def __init__(self, option, parent, menubar=None):
        if isinstance(option, str):
            blobs = option.split('|')
            _option = {'label': blobs[0]}
            l = len(blobs)
            if l > 1: _option['shortcut'] = blobs[1]
            if l > 2: _option['help'] = blobs[2]
            option = _option

        self.qtmenubar = menubar
        self.qtaction = None
        self.qtmenu = None

        # self.qtaction = None
        self.owner = None
        self.parent = parent
        self.groupName = None

        signal = option.get('signal', None)
        self.setSignal(signal)

        self.mgr = parent and parent.mgr
        self.owner = parent and parent.owner

        self.children = []
        self.actionGroups = {}

        self.label = option.get('label', 'UNNAMED')
        self.name = option.get('name',
                               self.label.replace('&', '').replace(' ', '_'))
        self.name = self.name.lower()

        self.shortcut = option.get('shortcut', False)
        self.help = option.get('help', '')
        self.priority = option.get('priority', 0)
        self.itemType = option.get('type', False)
        self.onClick = option.get('on_click', None)
        self.cmd = option.get('command', None)
        self.cmdArgs = option.get('command_args', None)
        self.link = None

        self.groupName = option.get('group', None)

        self.menuType = self.qtmenubar and 'menubar' or 'item'

        children = option.get('children', None)
        link = option.get('link', None)

        if children or self.itemType == 'menu':
            if self.menuType != 'menubar':
                self.menuType = 'menu'
                self.itemType = False

        elif link:
            self.link = link
            if self.menuType != 'menubar':
                self.menuType = 'link'

        elif parent and parent.menuType == 'menubar':
            self.menuType = 'menu'

        if self.menuType == 'menu':
            self.qtmenu = QMenu(self.label)

        if not parent or parent.menuType == 'root': return

        parent.addChildControl(self)
        if self.itemType == 'check':
            checked = option.get('checked', False)
            self.setValue(checked or False)
        if children:
            for data in children:
                self.addChild(data)
        # self.mgr.addNodeIndex(self)

    def getFullName(self):
        if parent:
            return parent.getFullName() + '/' + self.name
        return self.name

    def addChild(self, option, owner=None):
        if option == '----':
            if self.qtmenu:
                self.qtmenu.addSeparator()
        elif isinstance(option, list):
            output = []
            for data in option:
                n = self.addChild(data)
                if n:
                    output.append(n)
                    if owner: n.owner = owner
            return output
        else:
            node = MenuNode(option, self)
            if owner: node.owner = owner
            self.children.append(node)
            return node

    def affirmQtActionGroup(self, name):
        group = self.actionGroups.get(name, None)
        if not group:
            group = QtWidgets.QActionGroup(self.qtmenu)
            self.actionGroups[name] = group
        return group

    def addChildControl(self, child):
        childType = child.menuType
        selfType = self.menuType

        if selfType == 'menu':
            if childType == 'menu':
                child.qtaction = self.qtmenu.addMenu(child.qtmenu)

            elif child.link:
                qtmenu = child.link.qtmenu
                child.qtaction = self.qtmenu.addMenu(qtmenu)

            else:

                action = QtWidgets.QAction(child.label,
                                           None,
                                           shortcut=child.shortcut,
                                           statusTip=child.help,
                                           checkable=child.itemType == 'check',
                                           triggered=child.handleEvent)

                self.qtmenu.addAction(action)
                child.qtaction = action

                if child.groupName:
                    self.affirmQtActionGroup(child.groupName).addAction(action)

        elif selfType == 'menubar':
            if childType == 'menu':
                self.qtmenubar.addMenu(child.qtmenu)
                child.qtaction = child.qtmenu.menuAction()
            else:
                logging.warning('attempt to add menuitem/link to a menubar')
                return
        else:
            logging.warning('menuitem has no child')

    def setEnabled(self, enabled):
        #todo: set state of linked item
        selfType = self.menuType
        if selfType == 'menubar':
            self.qtmenubar.setEnable(enabled)
            return

        if self.qtmenu:
            self.qtmenu.setEnabled(enabled)
        else:
            self.qtaction.setEnabled(enabled)

    def remove(self):
        self.clear()
        self.parent.children.remove(self)
        selfType = self.menuType

        if not self.parent: return

        if selfType == 'menubar':
            return

        parentType = self.parent.menuType

        if parentType == 'menu':
            self.parent.qtmenu.removeAction(self.qtaction)
        elif parentType == 'menubar':
            self.parent.qtmenubar.removeAction(self.qtaction)
        logging.info('remove menunode:' + self.name)

    def clear(self):
        if self.menuType in ['menu', 'menubar']:
            for node in self.children[:]:
                node.remove()

    def findChild(self, name):
        name = name.lower()
        for c in self.children:
            if c.name == name: return c
        return None

    def getValue(self):
        if self.itemType in ('check', 'radio'):
            return self.qtaction.isChecked()
        return True

    def setValue(self, v):
        if self.itemType in ('check', 'radio'):
            self.qtaction.setChecked(v and True or False)

    def setSignal(self, signal):
        if isinstance(signal, str):
            signal = signals.get(signal)
        self.signal = signal

    def popUp(self, **option):
        if self.qtmenu:
            context = option.get('context', None)
            MenuNode._currentMenuContext = context
            self.qtmenu.exec_(QtGui.QCursor.pos())

    def getContext(self):
        return MenuNode._currentMenuContext

    def setOnClick(self, onClick):
        self.onClick = onClick

    def handleEvent(self):
        itemtype = self.itemType
        value = self.getValue()
        logging.debug('menu event:' + self.name)
        if self.owner:
            if hasattr(self.owner, 'onMenu'):
                self.owner.onMenu(self)

        if self.signal:
            self.signal(value)
        if self.onClick != None:
            self.onClick(value)
        if self.cmd:
            args = self.cmdArgs or {}
            app.doCommand(self.cmd, **args)
        MenuNode._currentMenuContext = None
Exemplo n.º 22
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.º 23
0
class CustomMenu(QtWidgets.QPushButton):
    """合约管理组件"""
    signal = QtCore.Signal(type(Event()))

    # ----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        super(CustomMenu, self).__init__()
        self.parent = parent

        # self.initUi()
        self.initMenu()

    #-----------------------------------------------------------------------
    def initMenu(self):
        self.setStyleSheet(
            "QMenu{background:purple;}"
            "QMenu{border:1px solid lightgray;}"
            "QMenu{border-color:green;}"
            "QMenu::item{padding:0px 20px 0px 15px;}"
            "QMenu::item{height:30px;}"
            "QMenu::item{color:blue;}"
            "QMenu::item{background:white;}"
            "QMenu::item{margin:1px 0px 0px 0px;}"
            "QMenu::item:selected:enabled{background:lightgray;}"
            "QMenu::item:selected:enabled{color:blue;}"
            "QMenu::item:selected:!enabled{background:transparent;}"
            "QMenu::separator{height:50px;}"
            "QMenu::separator{width:1px;}"
            "QMenu::separator{background:white;}"
            "QMenu::separator{margin:1px 1px 1px 1px;}"
            "QMenu#menu{background:white;}"
            "QMenu#menu{border:1px solid lightgray;}"
            "QMenu#menu::item{padding:0px 20px 0px 15px;}"
            "QMenu#menu::item{height:15px;}"
            "QMenu#menu::item:selected:enabled{background:lightgray;}"
            "QMenu#menu::item:selected:enabled{color:white;}"
            "QMenu#menu::item:selected:!enabled{background:transparent;}"
            "QMenu#menu::separator{height:1px;}"
            "QMenu#menu::separator{background:lightgray;}"
            "QMenu#menu::separator{margin:2px 0px 2px 0px;}"
            "QMenu#menu::indicator {padding:5px;}")
        self.color = QColor(Qt.gray)
        self.opacity = 1.0
        '''''' ' 创建右键菜单 ' ''
        # 必须将ContextMenuPolicy设置为Qt.CustomContextMenu
        # 否则无法使用customContextMenuRequested信号
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showContextMenu)

        # 创建QMenu
        self.contextMenu = QMenu(self)
        self.trendMenu = self.contextMenu.addMenu(u"k线形态")
        self.swingMenu = self.contextMenu.addMenu(u"技术指标")
        self.amountMenu = self.contextMenu.addMenu(u"策略研究")
        self.pzxzMenu = self.contextMenu.addMenu(u"品种选择")
        # 添加二级菜单

        #趋势分析指标
        self.actionSAR = self.trendMenu.addAction(u'k线')
        self.actionSAR.triggered.connect(
            lambda: self.parent.initIndicator(u"KLINE"))

        self.actionMA = self.trendMenu.addAction(u'信号隐藏')
        self.actionMA.triggered.connect(
            lambda: self.parent.initIndicator(u"信号隐藏"))

        self.actionMA = self.trendMenu.addAction(u'信号显示')
        self.actionMA.triggered.connect(
            lambda: self.parent.initIndicator(u"信号显示"))

        #摆动分析
        self.actionCCI = self.swingMenu.addAction(u'MA SHORT')
        self.actionCCI.triggered.connect(
            lambda: self.parent.initIndicator(u"MA SHORT"))
        self.actionROC = self.swingMenu.addAction(u'MA LONG')
        self.actionROC.triggered.connect(
            lambda: self.parent.initIndicator(u"MA LONG"))
        self.actionSHORTTERM = self.swingMenu.addAction(u'SHORT TERM(Limit)')
        self.actionSHORTTERM.triggered.connect(
            lambda: self.parent.initIndicator(u"SHORT TERM(Limit)"))
        self.actionSHORTTERMF = self.swingMenu.addAction(u'SHORT TERM(First)')
        self.actionSHORTTERMF.triggered.connect(
            lambda: self.parent.initIndicator(u"SHORT TERM(First)"))
        self.actionSHORTTERMALL = self.swingMenu.addAction(u'SHORT TERM(All)')
        self.actionSHORTTERMALL.triggered.connect(
            lambda: self.parent.initIndicator(u"SHORT TERM(All)"))
        self.actionWAIBAORI = self.swingMenu.addAction(u'外包日')
        self.actionWAIBAORI.triggered.connect(
            lambda: self.parent.initIndicator(u"外包日"))
        self.actionGJR_BUY = self.swingMenu.addAction(u'攻击日(买入)')
        self.actionGJR_BUY.triggered.connect(
            lambda: self.parent.initIndicator(u"攻击日(买入)"))
        self.actionGJR_SELL = self.swingMenu.addAction(u'攻击日(卖出)')
        self.actionGJR_SELL.triggered.connect(
            lambda: self.parent.initIndicator(u"攻击日(卖出)"))

        ##设为起始日期
        self.actionOPI = self.amountMenu.addAction(u'设为起始日期')
        self.actionOPI.triggered.connect(
            lambda: self.parent.initIndicator(u"设为起始日期"))

        self.actionOPI1 = self.amountMenu.addAction(u'设为结束日期')
        self.actionOPI1.triggered.connect(
            lambda: self.parent.initIndicator(u"设为结束日期"))

        ##量仓分析
        self.actionOPI2 = self.amountMenu.addAction(u'MA_螺纹空_PLUS')
        self.actionOPI2.triggered.connect(
            lambda: self.parent.initIndicator(u"MA_螺纹空_PLUS"))

        ##成交量分析
        self.actionVOL = self.amountMenu.addAction(u'SHORTTERM_螺纹_多')
        self.actionVOL.triggered.connect(
            lambda: self.parent.initIndicator(u"SHORTTERM_螺纹_多"))

        self.action1 = self.amountMenu.addAction(u'SHORTTERM_螺纹_空')
        self.action1.triggered.connect(
            lambda: self.parent.initIndicator(u"SHORTTERM_螺纹_空"))

        self.action2 = self.amountMenu.addAction(u'SHORTTERM_螺纹_多_加仓')
        self.action2.triggered.connect(
            lambda: self.parent.initIndicator(u"SHORTTERM_螺纹_多_加仓"))

        self.action5 = self.amountMenu.addAction(u'VOLATILITY_螺纹_多')
        self.action5.triggered.connect(
            lambda: self.parent.initIndicator(u"VOLATILITY_螺纹_多"))

        self.action6 = self.amountMenu.addAction(u'VOLATILITY_螺纹_空')
        self.action6.triggered.connect(
            lambda: self.parent.initIndicator(u"VOLATILITY_螺纹_空"))

        self.action9 = self.amountMenu.addAction(u'VOLATILITY_螺纹_V1')
        self.action9.triggered.connect(
            lambda: self.parent.initIndicator(u"VOLATILITY_螺纹_V1"))

        self.action7 = self.amountMenu.addAction(u'外包日_螺纹_多')
        self.action7.triggered.connect(
            lambda: self.parent.initIndicator(u"外包日_螺纹_多"))

        self.action3 = self.pzxzMenu.addAction(u'RB9999')
        self.action3.triggered.connect(
            lambda: self.parent.initIndicator(u"RB9999"))

        self.action4 = self.pzxzMenu.addAction(u'BU9999')
        self.action4.triggered.connect(
            lambda: self.parent.initIndicator(u"BU9999"))

        #self.contextMenu.exec_(QCursor.pos())  # 在鼠标位置显示
        #添加二级菜单

    def showContextMenu(self, pos):
        '''''
        右键点击时调用的函数
        '''
        # 菜单显示前,将它移动到鼠标点击的位置
        # self.contextMenu.move(self.pos() + pos)
        self.contextMenu.show()
        self.contextMenu.exec_(QCursor.pos())
Exemplo n.º 24
0
class PreviewTable(QTableView):
    """Import wizard preview widget"""
    def __init__(self, parent):
        QTableView.__init__(self, parent)
        self._model = None

        # Setting up actions
        self.date_dayfirst_action = create_action(self,
                                                  "dayfirst",
                                                  triggered=ft_partial(
                                                      self.parse_to_type,
                                                      atype="date",
                                                      dayfirst=True))
        self.date_monthfirst_action = create_action(self,
                                                    "monthfirst",
                                                    triggered=ft_partial(
                                                        self.parse_to_type,
                                                        atype="date",
                                                        dayfirst=False))
        self.perc_action = create_action(self,
                                         "perc",
                                         triggered=ft_partial(
                                             self.parse_to_type, atype="perc"))
        self.acc_action = create_action(self,
                                        "account",
                                        triggered=ft_partial(
                                            self.parse_to_type,
                                            atype="account"))
        self.str_action = create_action(self,
                                        "unicode",
                                        triggered=ft_partial(
                                            self.parse_to_type,
                                            atype="unicode"))
        self.int_action = create_action(self,
                                        "int",
                                        triggered=ft_partial(
                                            self.parse_to_type, atype="int"))
        self.float_action = create_action(self,
                                          "float",
                                          triggered=ft_partial(
                                              self.parse_to_type,
                                              atype="float"))

        # Setting up menus
        self.date_menu = QMenu()
        self.date_menu.setTitle("Date")
        add_actions(self.date_menu,
                    (self.date_dayfirst_action, self.date_monthfirst_action))
        self.parse_menu = QMenu(self)
        self.parse_menu.addMenu(self.date_menu)
        add_actions(self.parse_menu, (self.perc_action, self.acc_action))
        self.parse_menu.setTitle("String to")
        self.opt_menu = QMenu(self)
        self.opt_menu.addMenu(self.parse_menu)
        add_actions(self.opt_menu,
                    (self.str_action, self.int_action, self.float_action))

    def _shape_text(self,
                    text,
                    colsep=u"\t",
                    rowsep=u"\n",
                    transpose=False,
                    skiprows=0,
                    comments='#'):
        """Decode the shape of the given text"""
        assert colsep != rowsep
        out = []
        text_rows = text.split(rowsep)[skiprows:]
        for row in text_rows:
            stripped = to_text_string(row).strip()
            if len(stripped) == 0 or stripped.startswith(comments):
                continue
            line = to_text_string(row).split(colsep)
            line = [try_to_parse(to_text_string(x)) for x in line]
            out.append(line)
        # Replace missing elements with np.nan's or None's
        if programs.is_module_installed('numpy'):
            from numpy import nan
            out = list(zip_longest(*out, fillvalue=nan))
        else:
            out = list(zip_longest(*out, fillvalue=None))
        # Tranpose the last result to get the expected one
        out = [[r[col] for r in out] for col in range(len(out[0]))]
        if transpose:
            return [[r[col] for r in out] for col in range(len(out[0]))]
        return out

    def get_data(self):
        """Return model data"""
        if self._model is None:
            return None
        return self._model.get_data()

    def process_data(self,
                     text,
                     colsep=u"\t",
                     rowsep=u"\n",
                     transpose=False,
                     skiprows=0,
                     comments='#'):
        """Put data into table model"""
        data = self._shape_text(text, colsep, rowsep, transpose, skiprows,
                                comments)
        self._model = PreviewTableModel(data)
        self.setModel(self._model)

    @Slot()
    def parse_to_type(self, **kwargs):
        """Parse to a given type"""
        indexes = self.selectedIndexes()
        if not indexes: return
        for index in indexes:
            self.model().parse_data_type(index, **kwargs)

    def contextMenuEvent(self, event):
        """Reimplement Qt method"""
        self.opt_menu.popup(event.globalPos())
        event.accept()
Exemplo n.º 25
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

        menu = QMenu(self.parent_no_ui)

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

        if (self.parent.table.rowCount() > 0):
            _undo = menu.addAction("Undo")
            _undo.setEnabled(self.parent_no_ui.undo_button_enabled)
            _redo = menu.addAction("Redo")
            _redo.setEnabled(self.parent_no_ui.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.parent.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.parent_no_ui.action_undo_clicked()
        elif action == _redo:
            self.parent_no_ui.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.º 26
0
class CustomMenu(QtWidgets.QPushButton):
    """合约管理组件"""
    signal = QtCore.Signal(type(Event()))

    # ----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        super(CustomMenu, self).__init__()
        self.parent = parent

        # self.initUi()
        self.initMenu()

    #-----------------------------------------------------------------------
    def initMenu(self):
        self.setStyleSheet(
            "QMenu{background:purple;}"
            "QMenu{border:1px solid lightgray;}"
            "QMenu{border-color:green;}"
            "QMenu::item{padding:0px 20px 0px 15px;}"
            "QMenu::item{height:30px;}"
            "QMenu::item{color:blue;}"
            "QMenu::item{background:white;}"
            "QMenu::item{margin:1px 0px 0px 0px;}"
            "QMenu::item:selected:enabled{background:lightgray;}"
            "QMenu::item:selected:enabled{color:blue;}"
            "QMenu::item:selected:!enabled{background:transparent;}"
            "QMenu::separator{height:50px;}"
            "QMenu::separator{width:1px;}"
            "QMenu::separator{background:white;}"
            "QMenu::separator{margin:1px 1px 1px 1px;}"
            "QMenu#menu{background:white;}"
            "QMenu#menu{border:1px solid lightgray;}"
            "QMenu#menu::item{padding:0px 20px 0px 15px;}"
            "QMenu#menu::item{height:15px;}"
            "QMenu#menu::item:selected:enabled{background:lightgray;}"
            "QMenu#menu::item:selected:enabled{color:white;}"
            "QMenu#menu::item:selected:!enabled{background:transparent;}"
            "QMenu#menu::separator{height:1px;}"
            "QMenu#menu::separator{background:lightgray;}"
            "QMenu#menu::separator{margin:2px 0px 2px 0px;}"
            "QMenu#menu::indicator {padding:5px;}")
        self.color = QColor(Qt.gray)
        self.opacity = 1.0
        '''''' ' 创建右键菜单 ' ''
        # 必须将ContextMenuPolicy设置为Qt.CustomContextMenu
        # 否则无法使用customContextMenuRequested信号
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showContextMenu)

        # 创建QMenu
        self.contextMenu = QMenu(self)
        self.trendMenu = self.contextMenu.addMenu(u"k线形态")
        self.swingMenu = self.contextMenu.addMenu(u"技术指标")
        self.amountMenu = self.contextMenu.addMenu(u"策略研究")
        # 添加二级菜单

        #趋势分析指标
        self.actionSAR = self.trendMenu.addAction(u'k线')
        self.actionSAR.triggered.connect(
            lambda: self.parent.initIndicator(u"KLINE"))

        self.actionMA = self.trendMenu.addAction(u'信号隐藏')
        self.actionMA.triggered.connect(
            lambda: self.parent.initIndicator(u"信号隐藏"))

        self.actionMA = self.trendMenu.addAction(u'信号显示')
        self.actionMA.triggered.connect(
            lambda: self.parent.initIndicator(u"信号显示"))

        #摆动分析
        self.actionCCI = self.swingMenu.addAction(u'MA SHORT')
        self.actionCCI.triggered.connect(
            lambda: self.parent.initIndicator(u"MA SHORT"))
        self.actionROC = self.swingMenu.addAction(u'MA LONG')
        self.actionROC.triggered.connect(
            lambda: self.parent.initIndicator(u"MA LONG"))

        ##设为起始日期
        self.actionOPI = self.amountMenu.addAction(u'设为起始日期')
        self.actionOPI.triggered.connect(
            lambda: self.parent.initIndicator(u"设为起始日期"))

        ##量仓分析
        self.actionOPI = self.amountMenu.addAction(u'MA_螺纹多_PLUS')
        self.actionOPI.triggered.connect(
            lambda: self.parent.initIndicator(u"MA_螺纹多_PLUS"))

        ##成交量分析
        self.actionVOL = self.amountMenu.addAction(u'CJL')
        self.actionVOL.triggered.connect(
            lambda: self.parent.initIndicator(u"CJL"))

        #self.contextMenu.exec_(QCursor.pos())  # 在鼠标位置显示
        #添加二级菜单

    def showContextMenu(self, pos):
        '''''
        右键点击时调用的函数
        '''
        # 菜单显示前,将它移动到鼠标点击的位置
        # self.contextMenu.move(self.pos() + pos)
        self.contextMenu.show()
        self.contextMenu.exec_(QCursor.pos())
Exemplo n.º 27
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_scatter_with_yerr = QAction("Scatter with Y Errors", plot)
        plot_scatter_with_yerr.triggered.connect(
            partial(self.presenter.action_plot, PlotType.SCATTER_WITH_ERR))

        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_scatter_with_yerr)
        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, True))

        sort_descending = QAction("Sort Descending", menu_main)
        sort_descending.triggered.connect(
            partial(self.presenter.action_sort, False))

        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 label_index in range(num_y_cols):
                set_as_y_err = QAction("Y{}".format(label_index), menu_main)

                # This is the column index of the Y column for which a YERR column is being added.
                # The column index is relative to the whole table, this is necessary
                # so that later the data of the column marked as error can be retrieved
                related_y_column = marked_y_cols[label_index]

                # label_index 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,
                            related_y_column))
                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.º 28
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.º 29
0
class PreviewTable(QTableView):
    """Import wizard preview widget"""
    def __init__(self, parent):
        QTableView.__init__(self, parent)
        self._model = None

        # Setting up actions
        self.date_dayfirst_action = create_action(self, "dayfirst",
            triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=True))
        self.date_monthfirst_action = create_action(self, "monthfirst",
            triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=False))
        self.perc_action = create_action(self, "perc",
            triggered=ft_partial(self.parse_to_type, atype="perc"))
        self.acc_action = create_action(self, "account",
            triggered=ft_partial(self.parse_to_type, atype="account"))
        self.str_action = create_action(self, "unicode",
            triggered=ft_partial(self.parse_to_type, atype="unicode"))
        self.int_action = create_action(self, "int",
            triggered=ft_partial(self.parse_to_type, atype="int"))
        self.float_action = create_action(self, "float",
            triggered=ft_partial(self.parse_to_type, atype="float"))

        # Setting up menus
        self.date_menu = QMenu()
        self.date_menu.setTitle("Date")
        add_actions( self.date_menu, (self.date_dayfirst_action,
                                      self.date_monthfirst_action))
        self.parse_menu = QMenu(self)
        self.parse_menu.addMenu(self.date_menu)
        add_actions( self.parse_menu, (self.perc_action, self.acc_action))
        self.parse_menu.setTitle("String to")
        self.opt_menu = QMenu(self)
        self.opt_menu.addMenu(self.parse_menu)
        add_actions( self.opt_menu, (self.str_action, self.int_action,
                                     self.float_action))

    def _shape_text(self, text, colsep=u"\t", rowsep=u"\n",
                    transpose=False, skiprows=0, comments='#'):
        """Decode the shape of the given text"""
        assert colsep != rowsep
        out = []
        text_rows = text.split(rowsep)[skiprows:]
        for row in text_rows:
            stripped = to_text_string(row).strip()
            if len(stripped) == 0 or stripped.startswith(comments):
                continue
            line = to_text_string(row).split(colsep)
            line = [try_to_parse(to_text_string(x)) for x in line]
            out.append(line)
        # Replace missing elements with np.nan's or None's
        if programs.is_module_installed('numpy'):
            from numpy import nan
            out = list(zip_longest(*out, fillvalue=nan))
        else:
            out = list(zip_longest(*out, fillvalue=None))
        # Tranpose the last result to get the expected one
        out = [[r[col] for r in out] for col in range(len(out[0]))]
        if transpose:
            return [[r[col] for r in out] for col in range(len(out[0]))]
        return out

    def get_data(self):
        """Return model data"""
        if self._model is None:
            return None
        return self._model.get_data()

    def process_data(self, text, colsep=u"\t", rowsep=u"\n",
                     transpose=False, skiprows=0, comments='#'):
        """Put data into table model"""
        data = self._shape_text(text, colsep, rowsep, transpose, skiprows,
                                comments)
        self._model = PreviewTableModel(data)
        self.setModel(self._model)

    @Slot()
    def parse_to_type(self,**kwargs):
        """Parse to a given type"""
        indexes = self.selectedIndexes()
        if not indexes: return
        for index in indexes:
            self.model().parse_data_type(index, **kwargs)

    def contextMenuEvent(self, event):
        """Reimplement Qt method"""
        self.opt_menu.popup(event.globalPos())
        event.accept()
Exemplo n.º 30
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)