示例#1
0
    def __init__(self):  # noqa
        QtWidgets.QMainWindow.__init__(self)
        self.setDockOptions(QtWidgets.QMainWindow.AllowTabbedDocks
                            | QtWidgets.QMainWindow.AllowNestedDocks)

        # resize Window before it is shown
        options = pymol.invocation.options
        self.resize(options.win_x + (220 if options.internal_gui else 0),
                    options.win_y + (246 if options.external_gui else 18))

        # for thread-safe viewport command
        self.viewportsignal.connect(self.pymolviewport)

        # reusable dialogs
        self.dialog_png = None
        self.advanced_settings_dialog = None
        self.props_dialog = None
        self.builder = None

        # setting index -> callable
        self.setting_callbacks = defaultdict(list)

        # "session_file" setting in window title
        self.setting_callbacks[440].append(lambda v: self.setWindowTitle(
            "PyMOL (" + os.path.basename(v) + ")"))

        # "External" Command Line and Loggin Widget
        self._setup_history()
        self.lineedit = CommandLineEdit()
        self.lineedit.setObjectName("command_line")
        self.browser = QtWidgets.QPlainTextEdit()
        self.browser.setObjectName("feedback_browser")
        self.browser.setReadOnly(True)

        # convenience: clicking into feedback browser gives focus to command
        # line. Drawback: Copying with CTRL+C doesn't work in feedback
        # browser -> clear focus proxy while text selected
        self.browser.setFocusProxy(self.lineedit)

        @self.browser.copyAvailable.connect
        def _(yes):
            self.browser.setFocusProxy(None if yes else self.lineedit)
            self.browser.setFocus()

        # Font
        self.browser.setFont(getMonospaceFont())
        connectFontContextMenu(self.browser)

        lineeditlayout = QtWidgets.QHBoxLayout()
        command_label = QtWidgets.QLabel("PyMOL>")
        command_label.setObjectName("command_label")
        lineeditlayout.addWidget(command_label)
        lineeditlayout.addWidget(self.lineedit)
        self.lineedit.setToolTip('''Command Input Area

Get the list of commands by hitting <TAB>

Get the list of arguments for one command with a question mark:
PyMOL> color ?

Read the online help for a command with "help":
PyMOL> help color

Get autocompletion for many arguments by hitting <TAB>
PyMOL> color ye<TAB>    (will autocomplete "yellow")
''')

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.browser)
        layout.addLayout(lineeditlayout)

        quickbuttonslayout = QtWidgets.QVBoxLayout()
        quickbuttonslayout.setSpacing(2)

        extguilayout = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.LeftToRight)
        extguilayout.setContentsMargins(2, 2, 2, 2)
        extguilayout.addLayout(layout)
        extguilayout.addLayout(quickbuttonslayout)

        class ExtGuiFrame(QtWidgets.QFrame):
            def mouseDoubleClickEvent(_, event):
                self.toggle_ext_window_dockable(True)

            _size_hint = QtCore.QSize(options.win_x, options.ext_y)

            def sizeHint(self):
                return self._size_hint

        dockWidgetContents = ExtGuiFrame(self)
        dockWidgetContents.setLayout(extguilayout)
        dockWidgetContents.setObjectName("extgui")

        self.ext_window = \
            dockWidget = QtWidgets.QDockWidget(self)
        dockWidget.setWindowTitle("External GUI")
        dockWidget.setWidget(dockWidgetContents)
        if options.external_gui:
            dockWidget.setTitleBarWidget(QtWidgets.QWidget())
        else:
            dockWidget.hide()

        self.addDockWidget(Qt.TopDockWidgetArea, dockWidget)

        # rearrange vertically if docking left or right
        @dockWidget.dockLocationChanged.connect
        def _(area):
            if area == Qt.LeftDockWidgetArea or area == Qt.RightDockWidgetArea:
                extguilayout.setDirection(QtWidgets.QBoxLayout.BottomToTop)
                quickbuttonslayout.takeAt(quickbuttons_stretch_index)
            else:
                extguilayout.setDirection(QtWidgets.QBoxLayout.LeftToRight)
                if quickbuttons_stretch_index >= quickbuttonslayout.count():
                    quickbuttonslayout.addStretch()

        # OpenGL Widget
        self.pymolwidget = PyMOLGLWidget(self)
        self.setCentralWidget(self.pymolwidget)

        cmd = self.cmd = self.pymolwidget.cmd
        '''
        # command completion
        completer = QtWidgets.QCompleter(cmd.kwhash.keywords, self)
        self.lineedit.setCompleter(completer)
        '''

        # overload <Tab> action
        self.lineedit.installEventFilter(self)
        self.pymolwidget.installEventFilter(self)

        # Quick Buttons
        for row in [
            [
                ('Reset', cmd.reset),
                ('Zoom', lambda: cmd.zoom(animate=1.0)),
                ('Orient', lambda: cmd.orient(animate=1.0)),

                # render dialog will be constructed when the menu is shown
                # for the first time. This way it's populated with the current
                # viewport and settings. Also defers parsing of the ui file.
                ('Draw/Ray', WidgetMenu(self).setSetupUi(self.render_dialog)),
            ],
            [
                ('Unpick', cmd.unpick),
                ('Deselect', cmd.deselect),
                ('Rock', cmd.rock),
                ('Get View', self.get_view),
            ],
            [
                ('|<', cmd.rewind),
                ('<', cmd.backward),
                ('Stop', cmd.mstop),
                ('Play', cmd.mplay),
                ('>', cmd.forward),
                ('>|', cmd.ending),
                ('MClear', cmd.mclear),
            ],
            [
                ('Builder', self.open_builder_panel),
                ('Properties', self.open_props_dialog),
                ('Rebuild', cmd.rebuild),
            ],
        ]:
            hbox = QtWidgets.QHBoxLayout()
            hbox.setSpacing(2)

            for name, callback in row:
                btn = QtWidgets.QPushButton(name)
                btn.setProperty("quickbutton", True)
                btn.setAttribute(Qt.WA_LayoutUsesWidgetRect)  # OS X workaround
                hbox.addWidget(btn)

                if callback is None:
                    btn.setEnabled(False)
                elif isinstance(callback, QtWidgets.QMenu):
                    btn.setMenu(callback)
                else:
                    btn.released.connect(callback)

            quickbuttonslayout.addLayout(hbox)

        # progress bar
        hbox = QtWidgets.QHBoxLayout()
        self.progressbar = QtWidgets.QProgressBar()
        self.progressbar.setSizePolicy(QtWidgets.QSizePolicy.Minimum,
                                       QtWidgets.QSizePolicy.Minimum)
        hbox.addWidget(self.progressbar)
        self.abortbutton = QtWidgets.QPushButton('Abort')
        self.abortbutton.setStyleSheet("background: #FF0000; color: #FFFFFF")
        self.abortbutton.released.connect(cmd.interrupt)
        hbox.addWidget(self.abortbutton)
        quickbuttonslayout.addLayout(hbox)

        quickbuttonslayout.addStretch()
        quickbuttons_stretch_index = quickbuttonslayout.count() - 1

        # menu top level
        self.menubar = menubar = self.menuBar()

        # action groups
        actiongroups = {}

        def _addmenu(data, menu):
            '''Fill a menu from "data"'''
            menu.setTearOffEnabled(True)
            menu.setWindowTitle(menu.title())  # needed for Windows
            for item in data:
                if item[0] == 'separator':
                    menu.addSeparator()
                elif item[0] == 'menu':
                    _addmenu(item[2], menu.addMenu(item[1].replace('&', '&&')))
                elif item[0] == 'command':
                    command = item[2]
                    if command is None:
                        print('warning: skipping', item)
                    else:
                        if isinstance(command, str):
                            command = lambda c=command: cmd.do(c)
                        menu.addAction(item[1], command)
                elif item[0] == 'check':
                    if len(item) > 4:
                        menu.addAction(
                            SettingAction(self, cmd, item[2], item[1], item[3],
                                          item[4]))
                    else:
                        menu.addAction(
                            SettingAction(self, cmd, item[2], item[1]))
                elif item[0] == 'radio':
                    label, name, value = item[1:4]
                    try:
                        group, type_, values = actiongroups[item[2]]
                    except KeyError:
                        group = QtWidgets.QActionGroup(self)
                        type_, values = cmd.get_setting_tuple(name)
                        actiongroups[item[2]] = group, type_, values
                    action = QtWidgets.QAction(label, self)
                    action.triggered.connect(lambda _=0, args=(name, value):
                                             cmd.set(*args, log=1, quiet=0))

                    self.setting_callbacks[cmd.setting._get_index(
                        name)].append(
                            lambda v, V=value, a=action: a.setChecked(v == V))

                    group.addAction(action)
                    menu.addAction(action)
                    action.setCheckable(True)
                    if values[0] == value:
                        action.setChecked(True)
                elif item[0] == 'open_recent_menu':
                    self.open_recent_menu = menu.addMenu('Open Recent...')
                else:
                    print('error:', item)

        # recent files menu
        self.open_recent_menu = None

        # for plugins
        self.menudict = {'': menubar}

        # menu
        for _, label, data in self.get_menudata(cmd):
            assert _ == 'menu'
            menu = menubar.addMenu(label)
            self.menudict[label] = menu
            _addmenu(data, menu)

        # hack for macOS to hide "Edit > Start Dictation"
        # https://bugreports.qt.io/browse/QTBUG-43217
        if pymol.IS_MACOS:
            self.menudict['Edit'].setTitle('Edit_')
            QtCore.QTimer.singleShot(
                10, lambda: self.menudict['Edit'].setTitle('Edit'))

        # recent files menu
        if self.open_recent_menu:

            @self.open_recent_menu.aboutToShow.connect
            def _():
                self.open_recent_menu.clear()
                for fname in self.recent_filenames:
                    self.open_recent_menu.addAction(
                        fname if len(fname) < 128 else '...' + fname[-120:],
                        lambda fname=fname: self.load_dialog(fname))

        # some experimental window control
        menu = self.menudict['Display'].addSeparator()
        menu = self.menudict['Display'].addMenu('External GUI')
        menu.addAction('Toggle floating', self.toggle_ext_window_dockable,
                       QtGui.QKeySequence('Ctrl+E'))
        ext_vis_action = self.ext_window.toggleViewAction()
        ext_vis_action.setText('Visible')
        menu.addAction(ext_vis_action)

        # extra key mappings (MacPyMOL compatible)
        QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+O'),
                            self).activated.connect(self.file_open)
        QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+S'),
                            self).activated.connect(self.session_save)

        # feedback
        self.feedback_timer = QtCore.QTimer()
        self.feedback_timer.setSingleShot(True)
        self.feedback_timer.timeout.connect(self.update_feedback)
        self.feedback_timer.start(100)

        # legacy plugin system
        self.menudict['Plugin'].addAction('Initialize Plugin System',
                                          self.initializePlugins)

        # focus in command line
        if options.external_gui:
            self.lineedit.setFocus()
        else:
            self.pymolwidget.setFocus()

        # Apply PyMOL stylesheet
        try:
            with open(
                    cmd.exp_path('$PYMOL_DATA/pmg_qt/styles/pymol.sty')) as f:
                style = f.read()
        except IOError:
            print('Could not read PyMOL stylesheet.')
            print('DEBUG: PYMOL_DATA=' + repr(os.getenv('PYMOL_DATA')))
            style = ""

        if style:
            self.setStyleSheet(style)
示例#2
0
    def __init__(self, pymod, parent=None):
        super().__init__(parent)

        # Initial settings.
        self.pymod = pymod
        self.title = self.pymod.pymod_plugin_name + "." + self.pymod.pymod_revision
        self.statusBar_message = "Ready"
        self.left = 50
        self.top = 50
        self.width = 1100  # 2200, 1100
        self.height = 800  # 400
        self.bg_color = 'rgb(0, 0, 0)'
        self.text_color = "rgb(255, 255, 255)"

        # Fonts and styles.
        self.font_weight = "bold"
        self.font_size = 10  # 9, 10, 12
        self.font = "courier new"

        self.vertical_spacing = 1
        self.horizontal_spacing = 20

        # Creating a menu bar.
        self.make_main_menu()  # create_menu_bar()

        # Creating central widget and setting it as central.
        self.central_widget = Centralwid(self)
        self.setCentralWidget(self.central_widget)
        self.update_qt_font()

        # Creating status bar.
        self.statusBar().showMessage(self.statusBar_message, 3000)
        self.statusBar().setSizeGripEnabled(1)

        # Initialize User Interface.
        self.initUI()

        # Set the layout.
        self.central_widget.id_form_layout.setFormAlignment(
            QtCore.Qt.AlignLeft)
        self.central_widget.id_form_layout.setVerticalSpacing(
            self.vertical_spacing)
        self.central_widget.sequence_ID_groupbox.setLayout(
            self.central_widget.id_form_layout)

        self.central_widget.seq_form_layout.setHorizontalSpacing(
            self.horizontal_spacing)
        self.central_widget.seq_form_layout.setFormAlignment(
            QtCore.Qt.AlignLeft)
        self.central_widget.seq_form_layout.setLabelAlignment(
            QtCore.Qt.AlignLeft)
        self.central_widget.seq_form_layout.setVerticalSpacing(
            self.vertical_spacing)
        self.central_widget.sequence_SEQ_groupbox.setLayout(
            self.central_widget.seq_form_layout)

        # Initializes a clipboard.
        self.clipboard = QtWidgets.QApplication.clipboard()

        #----------------
        # Key bindings. -
        #----------------

        # Select elements.
        self.select_all_shortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence("Ctrl+A"), self)
        self.select_all_shortcut.activated.connect(self.select_all_binding)

        self.show_all_structures_shortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence("Ctrl+S"), self)
        self.show_all_structures_shortcut.activated.connect(
            self.show_all_structures_binding)
        self.hide_all_structures_shortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence("Ctrl+H"), self)
        self.hide_all_structures_shortcut.activated.connect(
            self.hide_all_structures_binding)

        self.deselect_all_shortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence("Esc"), self)
        self.deselect_all_shortcut.activated.connect(self.deselect_all_binding)

        # Move elements.
        self.press_up_key_shortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence("up"), self)
        self.press_up_key_shortcut.activated.connect(self.press_up_key_binding)

        self.press_down_key_shortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence("down"), self)
        self.press_down_key_shortcut.activated.connect(
            self.press_down_key_binding)