예제 #1
0
    def root(self):
        if self._root is None:
            from pymol.Qt import QtCore

            tkinter_init()

            # create Tk instance in this thread
            self._root = tkinter.Tk()
            self._root.tk = tkapp_proxy(self._root.tk, self)
            self._root.withdraw()

            # feed Tk event loop from this thread
            timer = QtCore.QTimer()

            @timer.timeout.connect
            def _():
                if not self._tk_update_paused:
                    self._root.update()
                timer.start()

            timer.setSingleShot(True)
            timer.start(50)

            # keep reference to timer
            self._tk_update_timer = timer

            import Pmw
            Pmw.initialise(self._root)

        return self._root
예제 #2
0
    def __init__(self, parent):
        self.gui = parent
        self.fb_scale = 1.0

        # OpenGL context setup
        if USE_QOPENGLWIDGET:
            f = QtGui.QSurfaceFormat()
        else:
            f = QtOpenGL.QGLFormat()

        from pymol.invocation import options

        # logic equivalent to layer5/main.cpp:launch

        if options.multisample:
            f.setSamples(4)

        if options.force_stereo != -1:
            # See layer1/Setting.h for stereo modes

            if options.stereo_mode in (1, 12) or (
                    options.stereo_mode == 0 and AUTO_DETECT_STEREO):
                f.setStereo(True)

            if options.stereo_mode in (11, 12) and not USE_QOPENGLWIDGET:
                f.setAccum(True)

        if USE_QOPENGLWIDGET:
            super(PyMOLGLWidget, self).__init__(parent=parent)
            self.setFormat(f)
            self.setUpdateBehavior(QtWidgets.QOpenGLWidget.PartialUpdate)
        else:
            super(PyMOLGLWidget, self).__init__(f, parent=parent)

        # pymol instance
        self.pymol = PyMOL()
        self.pymol.start()
        self.cmd = self.pymol.cmd

        # capture python output for feedback
        import pcatch
        pcatch._install()

        # for passive move drag
        self.setMouseTracking(True)

        # for accepting keyboard input (command line, shortcuts)
        self.setFocusPolicy(Qt.ClickFocus)

        # for idle rendering
        self._timer = QtCore.QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self._pymolProcess)

        # drag n drop
        self.setAcceptDrops(True)

        # pinch-zoom
        self.grabGesture(Qt.PinchGesture)
예제 #3
0
        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
예제 #4
0
def make_pymol_qicon():
    icons_dir = os.path.expandvars('$PYMOL_DATA/pymol/icons')
    icon = QtGui.QIcon()

    for size in (16, 32, 128):
        icon.addFile(os.path.join(icons_dir, 'icon2_%dx%d.png' % (size, size)),
                     QtCore.QSize(size, size))

    return icon
예제 #5
0
    def __init__(self,
                 i,
                 j,
                 tags,
                 mark_size,
                 parent_window,
                 pen=None,
                 brush=None):

        # Build the polygon object representing a triangle.
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            polygon = QtGui.QPolygonF([
                QtCore.QPoint(i, j - 2 * mark_size),
                QtCore.QPoint(i - 1.7 * mark_size, j + mark_size),
                QtCore.QPoint(i + 1.7 * mark_size, j + mark_size)
            ])
        # Initialize.
        super(Ramachandran_plot_triangle, self).__init__(polygon)
        self.common_initialization(i, j, tags, parent_window, pen, brush)
예제 #6
0
 def paintValueBox(self,
                   painter,
                   font_metrics,
                   x,
                   y,
                   right_just,
                   value,
                   format="%.3f"):
     s = format % value
     sw = font_metrics.width(s)
     sh = font_metrics.height()
     if right_just:
         rect = QtCore.QRect(x - sw - 4, y - sh, sw + 4, sh + 2)
     else:
         rect = QtCore.QRect(x, y - sh, sw + 4, sh + 2)
     painter.fillRect(rect,
             QtGui.QColor(96, 96, 128) if self.line_color == Qt.lightGray else
             QtGui.QColor(0xFF, 0xFF, 0xFF))
     painter.drawRect(rect)
     painter.drawText(rect.x() + 2, y - 2, s)
     return rect
예제 #7
0
class PyMod_protocol_thread(QtCore.QThread):
    """
    Class for a 'QThread' to launch a function where a time-consuming process is
    executed. It is used when showing a 'Protocol_exec_dialog' dialog.
    """

    # Signals.
    terminate_thread_signal = QtCore.pyqtSignal(int)
    exception_thread_signal = QtCore.pyqtSignal(Exception)

    def set_params(self, function, args, wait_start, wait_end):
        self.function = function
        self.args = args
        self.wait_start = wait_start
        self.wait_end = wait_end

    def run(self):
        if self.wait_start is not None:
            time.sleep(self.wait_start)

        # Attempts to execute the function in this thread.
        try:
            if type(self.args) is dict:
                self.function(**self.args)
            else:
                self.function(*self.args)
            self._wait_end()
            self.terminate_thread_signal.emit(0)  # Terminate sucessully.

        # If there was an error, emit the exception, so that it can be handled in the main thread.
        except Exception as e:
            self._wait_end()
            self.exception_thread_signal.emit(
                e)  # Terminate by raising an exception.

    def _wait_end(self):
        if self.wait_end is not None:
            time.sleep(self.wait_end)
예제 #8
0
def update_pyvol(form):
    """ GUI wrapper for updating PyVOL using pip

    """

    update_pypi_pyvol()

    msg = QtWidgets.QMessageBox()
    msg.setIcon(QtWidgets.QMessageBox.Information)
    msg.setWindowTitle("PyVOL Updated")
    msg.setInformativeText("The PyVOL backend has been updated; however, PyMOL will not load the new code until it is restarted.")
    msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
    msg.setMinimumSize(QtCore.QSize(600, 200)) # Doesn't seem to work
    msg.exec_()

    refresh_installation_status(form)
예제 #9
0
def uninstall_pyvol(form):
    """ Attempts to uninstall PyVOL using pip

    """

    subprocess.check_output([sys.executable, "-m", "pip", "uninstall", "-y", "bio-pyvol"])

    msg = QtWidgets.QMessageBox()
    msg.setIcon(QtWidgets.QMessageBox.Information)
    msg.setWindowTitle("PyVOL Backend Uninstalled")
    msg.setInformativeText("The PyVOL backend has been uninstalled; however, the plugin must also be uninstalled using PyMOL's plugin manager.")
    msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
    msg.setMinimumSize(QtCore.QSize(600, 200)) # Doesn't seem to work
    msg.exec_()

    refresh_installation_status(form)
예제 #10
0
def update_pyvol():
    """ Attempts to update PyVOL using conda

    """
    from conda.cli import python_api
    python_api.run_command(python_api.Commands.UPDATE, "bio-pyvol")

    msg = QtWidgets.QMessageBox()
    msg.setIcon(QtWidgets.QMessageBox.Information)
    msg.setWindowTitle("PyVOL Updated")
    msg.setInformativeText(
        "The PyVOL backend has been updated; however, PyMOL will not load the new code until it is restarted."
    )
    msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
    msg.setMinimumSize(QtCore.QSize(600, 200))  # Doesn't seem to work
    msg.exec_()

    refresh_installation_status(form)
예제 #11
0
    def pymolviewport(self, w, h):
        cw, ch = self.cmd.get_viewport()
        pw = self.pymolwidget
        scale = pw.fb_scale

        # maintain aspect ratio
        if h < 1:
            if w < 1:
                pw.resizeGL(int(scale * pw.width()), int(scale * pw.height()))
                return
            h = (w * ch) / cw
        if w < 1:
            w = (h * cw) / ch

        win_size = self.size()
        delta = QtCore.QSize(w - cw, h - ch) / scale

        # window resize
        self.resize(delta + win_size)
예제 #12
0
    def update_pyvol(form):
        """ Attempts to update PyVOL using pip

        """
        import subprocess
        import sys

        form.status_label.setText("Updating PyVOL")
        subprocess.check_output(
            [sys.executable, "-m", "pip", "install", "--upgrade", "bio-pyvol"])

        msg = QtWidgets.QMessageBox()
        msg.setIcon(QtWidgets.QMessageBox.Information)
        msg.setWindowTitle("PyVOL Updated")
        msg.setInformativeText(
            "The PyVOL backend has been updated; however, PyMOL will not load the new code until it is restarted."
        )
        msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
        msg.setMinimumSize(QtCore.QSize(600, 200))  # Doesn't seem to work
        msg.exec_()

        refresh_installation_status(form)
예제 #13
0
def dialog(_self=None):
    if _self is None:
        from pymol import cmd as _self

    dialog = QtWidgets.QDialog()
    uifile = os.path.join(os.path.dirname(__file__), 'apbs.ui')
    form = loadUi(uifile, dialog)
    form._dialog = dialog
    form._proclist = []

    def set_apbs_in(contents):
        form.apbs_template.setPlainText(contents.strip())

    # hide options widgets
    form.optarea_prep.setVisible(False)
    form.optarea_apbs.setVisible(False)
    form.optarea_surf.setVisible(False)
    form.optarea_other.setVisible(False)

    # pre-fill form with likely data
    names = _self.get_object_list()
    names += ['(' + n + ')' for n in _self.get_names('public_selections')]
    if names:
        form.input_sele.clear()
        form.input_sele.addItems([
            ('polymer & ' +
             name) if _self.count_atoms('polymer & ' + name) > 0 else name
            for name in names
        ])
    form.surf_map.addItems(_self.get_names_of_type('object:map'))
    set_apbs_in(electrostatics.template_apbs_in)

    # executables
    from distutils.spawn import find_executable
    form.apbs_exe.setText(electrostatics.find_apbs_exe() or 'apbs')
    form.pdb2pqr_exe.setText(
        find_executable('pdb2pqr') or
        # acellera::htmd-pdb2pqr provides pdb2pqr_cli
        find_executable('pdb2pqr_cli') or find_executable(
            'share/pdb2pqr/pdb2pqr.py', os.getenv('FREEMOL', '/usr'))
        or 'pdb2pqr')

    # for async panels
    form._callInMainThread = MainThreadCaller()
    run_impl_async = AsyncFunc(run_impl)

    # "Run" button callback
    def run():
        form.tabWidget.setEnabled(False)
        form.button_ok.clicked.disconnect()
        form.button_ok.clicked.connect(abort)
        form.button_ok.setText('Abort')

        form._capture = StdOutCapture()

        # detach from main thread
        run_impl_async(form, _self)

    # "Run" button "finally" actions (main thread)
    @run_impl_async.finished.connect
    def run_finally(args):
        _, exception = args

        form._proclist[:] = []
        stdout = form._capture.release()
        print(stdout)

        form.button_ok.setText('Run')
        form.button_ok.clicked.disconnect()
        form.button_ok.clicked.connect(run)
        form.button_ok.setEnabled(True)
        form.tabWidget.setEnabled(True)

        if exception is not None:
            handle_exception(exception, stdout)
            return

        quit_msg = "Finished with Success. Close the APBS dialog?"
        if QMessageBox.Yes == QMessageBox.question(form._dialog, 'Finished',
                                                   quit_msg, QMessageBox.Yes,
                                                   QMessageBox.No):
            form._dialog.close()

    def handle_exception(e, stdout):
        if isinstance(e, SilentAbort):
            return

        msg = str(e) or 'unknown error'
        msgbox = QMessageBox(QMessageBox.Critical, 'Error', msg,
                             QMessageBox.Close, form._dialog)
        if stdout.strip():
            msgbox.setDetailedText(stdout)
        msgbox.exec_()

    # "Abort" button callback
    def abort():
        form.button_ok.setEnabled(False)
        while form._proclist:
            p = form._proclist.pop()
            try:
                p.terminate()
                p.returncode = -15  # SIGTERM
            except OSError as e:
                print(e)

    # selection checker
    check_sele_timer = QtCore.QTimer()
    check_sele_timer.setSingleShot(True)

    # grid auto-value
    form.apbs_grid_userchanged = False
    form.apbs_grid.setStyleSheet('background: #ff6')

    @form.apbs_grid.editingFinished.connect
    def _():
        form.apbs_grid_userchanged = True
        form.apbs_grid.setStyleSheet('')

    @check_sele_timer.timeout.connect
    def _():
        has_props = ['no', 'no']

        def callback(partial_charge, elec_radius):
            if partial_charge: has_props[0] = 'YES'
            if elec_radius > 0: has_props[1] = 'YES'

        n = _self.iterate(form.input_sele.currentText(),
                          'callback(partial_charge, elec_radius)',
                          space={'callback': callback})

        # grid auto-value (keep map size in the order of 200x200x200)
        if n > 1 and not form.apbs_grid_userchanged:
            e = _self.get_extent(form.input_sele.currentText())
            volume = (e[1][0] - e[0][0]) * (e[1][1] - e[0][1]) * (e[1][2] -
                                                                  e[0][2])
            grid = max(0.5, volume**0.333 / 200.0)
            form.apbs_grid.setValue(grid)

        if n < 1:
            label = 'Selection is invalid'
            color = '#f66'
        elif has_props == ['YES', 'YES']:
            label = 'No preparation necessary, selection has charges and radii'
            form.do_prepare.setChecked(False)
            color = '#6f6'
        else:
            label = 'Selection needs preparation (partial_charge: %s, elec_radius: %s)' % tuple(
                has_props)
            form.do_prepare.setChecked(True)
            color = '#fc6'

        form.label_sele_has.setText(label)
        form.label_sele_has.setStyleSheet('background: %s; padding: 5' % color)

    check_sele_timer.start(0)

    @form.apbs_exe_browse.clicked.connect
    def _():
        fnames = getOpenFileNames(None,
                                  filter='apbs (apbs*);;All Files (*)')[0]
        if fnames:
            form.apbs_exe.setText(fnames[0])

    @form.pdb2pqr_exe_browse.clicked.connect
    def _():
        fnames = getOpenFileNames(
            None, filter='pdb2pqr (pdb2pqr*);;All Files (*)')[0]
        if fnames:
            form.pdb2pqr_exe.setText(fnames[0])

    # hook up events
    form.input_sele.currentIndexChanged.connect(
        lambda: check_sele_timer.start(0))
    form.input_sele.editTextChanged.connect(
        lambda: check_sele_timer.start(1000))

    form.button_ok.clicked.connect(run)

    # "Register" opens a web browser
    @form.button_register.clicked.connect
    def _():
        import webbrowser
        webbrowser.open("http://www.poissonboltzmann.org/")

    @form.button_load.clicked.connect
    def _():
        fnames = getOpenFileNames(None,
                                  filter='APBS Input (*.in);;All Files (*)')[0]
        if fnames:
            contents = load_apbs_in(form, fnames[0])
            set_apbs_in(contents)

    @form.button_reset.clicked.connect
    def _():
        set_apbs_in(electrostatics.template_apbs_in)

    form._dialog.show()
    form._dialog.resize(500, 600)
예제 #14
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)
예제 #15
0
        def __init__(self):
            QtWidgets.QWidget.__init__(self, parent, Qt.Window)
            self.setMinimumSize(400, 500)
            self.setWindowTitle('Register File Extensions')

            self.model = QtGui.QStandardItemModel(self)

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

            label = QtWidgets.QLabel(
                "Select file types to register them with PyMOL", self)
            layout.addWidget(label)

            alluserslayout = QtWidgets.QHBoxLayout()
            alluserslayout.setObjectName("alluserslayout")
            layout.addLayout(alluserslayout)

            buttonlayout = QtWidgets.QHBoxLayout()
            buttonlayout.setObjectName("buttonlayout")
            layout.addLayout(buttonlayout)

            self.table = QtWidgets.QTableView(self)
            self.table.setModel(self.model)
            layout.addWidget(self.table)

            button = QtWidgets.QPushButton("Register Recommended (*)", self)
            buttonlayout.addWidget(button)
            button.pressed.connect(self.setRecommended)

            button = QtWidgets.QPushButton("Register All", self)
            buttonlayout.addWidget(button)
            button.pressed.connect(self.setAll)

            button = QtWidgets.QPushButton("Clear", self)
            button.setToolTip("Clean up Registry")
            buttonlayout.addWidget(button)
            button.pressed.connect(self.clear)

            if isAdmin():
                r0 = QtWidgets.QRadioButton("Only for me")
                r0.setToolTip("HKEY_CURRENT_USER registry branch")
                r0.setChecked(True)
                r1 = QtWidgets.QRadioButton("For all users")
                r1.setToolTip("HKEY_LOCAL_MACHINE registry branch")
                allusersgroup = QtWidgets.QButtonGroup(self)
                allusersgroup.addButton(r0)
                allusersgroup.addButton(r1)
                allusersgroup.buttonClicked.connect(self.populateData)
                alluserslayout.addWidget(r0)
                alluserslayout.addWidget(r1)
                alluserslayout.addStretch()
                self.allusersbutton = r1
            else:
                self.allusersbutton = None

            self.finalize_timer = QtCore.QTimer()
            self.finalize_timer.setSingleShot(True)
            self.finalize_timer.setInterval(500)
            self.finalize_timer.timeout.connect(finalize)

            self.populateData()

            # keep reference to window, otherwise Qt will auto-close it
            self._self_ref = self
예제 #16
0
 def sizeHint(self):
     # default 640 + internal_gui, 480 + internal_feedback
     return QtCore.QSize(860, 498)
예제 #17
0
class InstallerQThread(QtCore.QThread):
    """
    A QThread wrapper for the installers.
    Contains methods that make sense only when this class is extended
    together with a PyMod_component_installer (or sub) class, creating a subclass
    that inherits both from this class and PyMod_component_installer.
    """

    critical_error = QtCore.pyqtSignal(str, PyMod_component)
    info_message = QtCore.pyqtSignal(str, PyMod_component)
    established_connection = QtCore.pyqtSignal(PyMod_component)

    retrieved_size = QtCore.pyqtSignal(PyMod_component, int)
    set_update_status = QtCore.pyqtSignal(PyMod_component, str, str)

    terminated_installation = QtCore.pyqtSignal(PyMod_component)
    freeze_thread = QtCore.pyqtSignal(PyMod_component, int)

    def run(self):
        """this method is never called alone (see QThread documentation)
        but is executed when the Qthread.start() method is called
        if the flag 'install_mode' is True, the thread will install the databases,
        if not, it will only retrieve the file size from the server."""

        # Only pings the remote source.
        if self.install_mode == "ping":
            # Returns 'False' if there is a connection error.
            time.sleep(0.5)
            connection = self.ping()
            if connection:
                self.component.can_be_downloaded = True

        else:
            try:
                # Actually performs the installation.
                self.download_and_install()

            except TerminateQThread as e:
                pass

            # emette un type error, oltre al gaierror, se non c'e' connessione internet.
            # Emette anche un EOFError e un TimeoutError se non fa in tempo a scaricare.
            except (gaierror, TypeError) as e:
                self.critical_error.emit(
                    "Cannot connect to server. Please check Internet connection.",
                    self.component)
            except EOFError as e:
                self.critical_error.emit(
                    "Timeout expired for connection. Try again later.",
                    self.component)
            except Exception as e:
                msg = str(e)
                self.critical_error.emit(msg, self.component)
                traceback.print_exc()

    ##########################################################
    # Wrapper for the signals. Used in installer subclasses. #
    ##########################################################

    def emit_signal(self, signal_name, *args, **kwargs):
        # getattr(self, signal_name).emit(*args, **kwargs)
        if signal_name == "set_update_status":
            self.set_update_status.emit(*args, **kwargs)
        elif signal_name == "retrieved_size":
            self.retrieved_size.emit(*args, **kwargs)
        elif signal_name == "critical_error":
            self.critical_error.emit(*args, **kwargs)
        elif signal_name == "terminated_installation":
            self.terminated_installation.emit(*args, **kwargs)
        else:
            raise KeyError("Unknown 'signal_name': %s" % signal_name)
예제 #18
0
class PyMOLQtGUI(QtWidgets.QMainWindow, pymol._gui.PyMOLDesktopGUI):
    '''
    PyMOL QMainWindow GUI
    '''

    from pmg_qt.file_dialogs import (load_dialog, load_mae_dialog,
                                     file_fetch_pdb, file_save_png,
                                     file_save_mpeg, file_save_map,
                                     file_save_aln, file_save)

    _ext_window_visible = True
    _initialdir = ''

    def keyPressEvent(self, ev):
        args = keymapping.keyPressEventToPyMOLButtonArgs(ev)

        if args is not None:
            self.pymolwidget.pymol.button(*args)

    def closeEvent(self, event):
        self.cmd.quit()

    # for thread-safe viewport command
    viewportsignal = QtCore.Signal(int, int)

    def pymolviewport(self, w, h):
        cw, ch = self.cmd.get_viewport()
        pw = self.pymolwidget
        scale = pw.fb_scale

        # maintain aspect ratio
        if h < 1:
            if w < 1:
                pw.pymol.reshape(int(scale * pw.width()),
                                 int(scale * pw.height()), True)
                return
            h = (w * ch) / cw
        if w < 1:
            w = (h * cw) / ch

        win_size = self.size()
        delta = QtCore.QSize(w - cw, h - ch) / scale

        # window resize
        self.resize(delta + win_size)

    def get_view(self):
        self.cmd.get_view(2, quiet=0)
        QtWidgets.QApplication.clipboard().setText(self.cmd.get_view(3))
        print(" get_view: matrix copied to clipboard.")

    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)

    def lineeditKeyPressEventFilter(self, watched, event):
        key = event.key()
        if key == Qt.Key_Tab:
            self.complete()
        elif key == Qt.Key_Up:
            if event.modifiers() & Qt.ControlModifier:
                self.back_search()
            else:
                self.back()
        elif key == Qt.Key_Down:
            self.forward()
        elif key == Qt.Key_Return or key == Qt.Key_Enter:
            # filter out "Return" instead of binding lineedit.returnPressed,
            # because otherwise OrthoKey would capture it as well.
            self.doPrompt()
        else:
            return False
        return True

    def eventFilter(self, watched, event):
        '''
        Filter out <Tab> event to do tab-completion instead of move focus
        '''
        type_ = event.type()
        if type_ == QtCore.QEvent.KeyRelease:
            if event.key() == Qt.Key_Tab:
                # silently skip tab release
                return True
        elif type_ == QtCore.QEvent.KeyPress:
            if watched is self.lineedit:
                return self.lineeditKeyPressEventFilter(watched, event)
            elif event.key() == Qt.Key_Tab:
                self.keyPressEvent(event)
                return True
        return False

    def toggle_ext_window_dockable(self, neverfloat=False):
        '''
        Toggle whether the "external" GUI is dockable
        '''
        dockWidget = self.ext_window

        if dockWidget.titleBarWidget() is None:
            tbw = QtWidgets.QWidget()
        else:
            tbw = None

        dockWidget.setFloating(tbw is None and not neverfloat)
        dockWidget.setTitleBarWidget(tbw)
        dockWidget.show()

    def toggle_fullscreen(self, toggle=-1):
        '''
        Full screen
        '''
        is_fullscreen = self.windowState() == Qt.WindowFullScreen

        if toggle == -1:
            toggle = not is_fullscreen

        if not is_fullscreen:
            self._ext_window_visible = self.ext_window.isVisible()

        if toggle:
            self.menubar.hide()
            self.ext_window.hide()
            self.showFullScreen()
            self.pymolwidget.setFocus()
        else:
            self.menubar.show()
            if self._ext_window_visible:
                self.ext_window.show()
            self.showNormal()

    @property
    def initialdir(self):
        '''
        Be in sync with cd/pwd on the console until the first file has been
        browsed, then remember the last directory.
        '''
        return self._initialdir or os.getcwd()

    @initialdir.setter
    def initialdir(self, value):
        self._initialdir = value

    ##################
    # UI Forms
    ##################

    def load_form(self, name, dialog=None):
        '''Load a form from pmg_qt/forms/{name}.py'''
        import importlib
        if dialog is None:
            dialog = QtWidgets.QDialog(self)
            widget = dialog
        elif dialog == 'floating':
            widget = QtWidgets.QWidget(self)
        else:
            widget = dialog

        try:
            m = importlib.import_module('.forms.' + name, 'pmg_qt')
        except ImportError as e:
            if pymol.Qt.DEBUG:
                print('load_form import failed (%s)' % (e, ))
            uifile = os.path.join(os.path.dirname(__file__), 'forms',
                                  '%s.ui' % name)
            form = pymol.Qt.utils.loadUi(uifile, widget)
        else:
            if hasattr(m, 'Ui_Form'):
                form = m.Ui_Form()
            else:
                form = m.Ui_Dialog()

            form.setupUi(widget)

        if dialog == 'floating':
            dialog = QtWidgets.QDockWidget(widget.windowTitle(), self)
            dialog.setFloating(True)
            dialog.setWidget(widget)
            dialog.resize(widget.size())

        form._dialog = dialog
        return form

    def open_props_dialog(self):  #noqa
        if not self.props_dialog:
            self.props_dialog = properties_dialog.props_dialog(self)

        self.props_dialog.show()
        self.props_dialog.raise_()

    def edit_colors_dialog(self):
        form = self.load_form('colors')
        form.list_colors.setSortingEnabled(True)

        # populate list with named colors
        for color_index in self.cmd.get_color_indices():
            form.list_colors.addItem(color_index[0])

        # update spinboxes for given color
        def load_color(name):
            index = self.cmd.get_color_index(name)
            if index == -1:
                return
            rgb = self.cmd.get_color_tuple(index)
            form.input_R.setValue(rgb[0])
            form.input_G.setValue(rgb[1])
            form.input_B.setValue(rgb[2])

        # update spinbox from slider
        spinbox_lock = [False]

        def update_spinbox(spinbox, value):
            if not spinbox_lock[0]:
                spinbox.setValue(value / 100.)

        # update sliders and colored frame
        def update_gui(*args):
            spinbox_lock[0] = True
            R = form.input_R.value()
            G = form.input_G.value()
            B = form.input_B.value()
            form.slider_R.setValue(R * 100)
            form.slider_G.setValue(G * 100)
            form.slider_B.setValue(B * 100)
            form.frame_color.setStyleSheet("background-color: rgb(%d,%d,%d)" %
                                           (R * 0xFF, G * 0xFF, B * 0xFF))
            spinbox_lock[0] = False

        def run():
            name = form.input_name.text()
            R = form.input_R.value()
            G = form.input_G.value()
            B = form.input_B.value()

            self.cmd.do('set_color %s, [%.2f, %.2f, %.2f]\nrecolor' %
                        (name, R, G, B))

            # if new color, insert and make current row
            if not form.list_colors.findItems(name, Qt.MatchExactly):
                form.list_colors.addItem(name)
                form.list_colors.setCurrentItem(
                    form.list_colors.findItems(name, Qt.MatchExactly)[0])

        # hook up events
        form.slider_R.valueChanged.connect(
            lambda v: update_spinbox(form.input_R, v))
        form.slider_G.valueChanged.connect(
            lambda v: update_spinbox(form.input_G, v))
        form.slider_B.valueChanged.connect(
            lambda v: update_spinbox(form.input_B, v))
        form.input_R.valueChanged.connect(update_gui)
        form.input_G.valueChanged.connect(update_gui)
        form.input_B.valueChanged.connect(update_gui)
        form.input_name.textChanged.connect(load_color)
        form.list_colors.currentTextChanged.connect(form.input_name.setText)
        form.button_apply.clicked.connect(run)

        form._dialog.show()

    def open_builder_panel(self):
        from pmg_qt.builder import BuilderPanelDocked
        from pymol import plugins

        app = plugins.get_pmgapp()
        if not self.builder:
            self.builder = BuilderPanelDocked(self, app)
            self.addDockWidget(Qt.TopDockWidgetArea, self.builder)

        self.builder.show()
        self.builder.raise_()

    def edit_pymolrc(self):
        from . import TextEditor
        from pymol import plugins
        TextEditor.edit_pymolrc(plugins.get_pmgapp())

    ##################
    # Menu callbacks
    ##################

    def file_open(self):
        fnames = getOpenFileNames(self, 'Open file', self.initialdir)[0]
        partial = 0
        for fname in fnames:
            if not self.load_dialog(fname, partial=partial):
                break
            partial = 1

    def session_save(self):
        fname = self.cmd.get('session_file')
        fname = self.cmd.as_pathstr(fname)
        return self.session_save_as(fname)

    @PopupOnException.decorator
    def session_save_as(self, fname=''):
        formats = [
            'PyMOL Session File (*.pse *.pze *.pse.gz)',
            'PyMOL Show File (*.psw *.pzw *.psw.gz)',
        ]
        if not fname:
            fname = getSaveFileNameWithExt(self,
                                           'Save Session As...',
                                           self.initialdir,
                                           filter=';;'.join(formats))
        if fname:
            self.initialdir = os.path.dirname(fname)
            self.cmd.save(fname, format='pse', quiet=0)
            self.recent_filenames_add(fname)

    def render_dialog(self, widget=None):
        form = self.load_form('render', widget)
        lock = UpdateLock([ZeroDivisionError])

        def get_factor():
            units = form.input_units.currentText()
            factor = 1.0 if units == 'inch' else 2.54
            return factor / float(form.input_dpi.currentText())

        @lock.skipIfCircular
        def update_units(*args):
            width = form.input_width.value()
            height = form.input_height.value()
            factor = get_factor()
            form.input_width_units.setValue(width * factor)
            form.input_height_units.setValue(height * factor)

        @lock.skipIfCircular
        def update_pixels(*args):
            width = form.input_width_units.value()
            height = form.input_height_units.value()
            factor = get_factor()
            form.input_width.setValue(width / factor)
            form.input_height.setValue(height / factor)

        @lock.skipIfCircular
        def update_width(*args):
            if form.aspectratio > 0:
                width = form.input_height.value() * form.aspectratio
                form.input_width.setValue(int(width))
                form.input_width_units.setValue(width * get_factor())

        @lock.skipIfCircular
        def update_height(*args):
            if form.aspectratio > 0:
                height = form.input_width.value() / form.aspectratio
                form.input_height.setValue(int(height))
                form.input_height_units.setValue(height * get_factor())

        def update_aspectratio(checked=True):
            if checked:
                try:
                    form.aspectratio = (float(form.input_width.value()) /
                                        float(form.input_height.value()))
                except ZeroDivisionError:
                    form.button_lock.setChecked(False)
            else:
                form.aspectratio = 0

        def update_from_viewport():
            w, h = self.cmd.get_viewport()
            form.aspectratio = 0
            form.input_width.setValue(w)
            form.input_height.setValue(h)
            update_aspectratio(form.button_lock.isChecked())

        def run_draw(ray=False):
            width = form.input_width.value()
            height = form.input_height.value()
            if ray:
                self.cmd.set('opaque_background',
                             not form.input_transparent.isChecked())
                self.cmd.do('ray %d, %d, async=1' % (width, height))
            else:
                self.cmd.do('draw %d, %d' % (width, height))
            form.stack.setCurrentIndex(1)

        def run_ray():
            run_draw(ray=True)

        def run_save():
            fname = getSaveFileNameWithExt(self,
                                           'Save As...',
                                           self.initialdir,
                                           filter='PNG File (*.png)')
            if not fname:
                return
            self.initialdir = os.path.dirname(fname)
            self.cmd.png(fname, prior=1, dpi=form.input_dpi.currentText())

        def run_copy_clipboard():
            with PopupOnException():
                _copy_image(self.cmd, False, form.input_dpi.currentText())

        dpi = self.cmd.get_setting_int('image_dots_per_inch')
        if dpi > 0:
            form.input_dpi.setEditText(str(dpi))
        form.input_dpi.setValidator(QtGui.QIntValidator())

        form.input_units.currentIndexChanged.connect(update_units)
        form.input_dpi.editTextChanged.connect(update_pixels)
        form.input_width.valueChanged.connect(update_units)
        form.input_height.valueChanged.connect(update_units)
        form.input_width_units.valueChanged.connect(update_pixels)
        form.input_height_units.valueChanged.connect(update_pixels)

        # set values before connecting mutual width<->height updates
        update_from_viewport()

        form.input_width.valueChanged.connect(update_height)
        form.input_height.valueChanged.connect(update_width)
        form.input_width_units.valueChanged.connect(update_height)
        form.input_height_units.valueChanged.connect(update_width)
        form.button_lock.toggled.connect(update_aspectratio)

        form.button_draw.clicked.connect(run_draw)
        form.button_ray.clicked.connect(run_ray)
        form.button_current.clicked.connect(update_from_viewport)
        form.button_back.clicked.connect(lambda: form.stack.setCurrentIndex(0))
        form.button_clip.clicked.connect(run_copy_clipboard)
        form.button_save.clicked.connect(run_save)

        if widget is None:
            form._dialog.show()

    def _file_save(self, filter, format):
        fname = getSaveFileNameWithExt(self,
                                       'Save As...',
                                       self.initialdir,
                                       filter=filter)
        if fname:
            self.cmd.save(fname, format=format, quiet=0)

    def file_save_wrl(self):
        self._file_save('VRML 2 WRL File (*.wrl)', 'wrl')

    def file_save_dae(self):
        self._file_save('COLLADA File (*.dae)', 'dae')

    def file_save_pov(self):
        self._file_save('POV File (*.pov)', 'pov')

    def file_save_mpng(self):
        self.file_save_mpeg('png')

    def file_save_mov(self):
        self.file_save_mpeg('mov')

    def file_save_stl(self):
        self._file_save('STL File (*.stl)', 'stl')

    def file_save_gltf(self):
        self._file_save('GLTF File (*.gltf)', 'gltf')

    LOG_FORMATS = [
        'PyMOL Script (*.pml)',
        'Python Script (*.py *.pym)',
        'All (*)',
    ]

    def log_open(self, fname='', mode='w'):
        if not fname:
            fname = getSaveFileNameWithExt(self,
                                           'Open Logfile...',
                                           self.initialdir,
                                           filter=';;'.join(self.LOG_FORMATS))
        if fname:
            self.initialdir = os.path.dirname(fname)
            self.cmd.log_open(fname, mode)

    def log_append(self):
        return self.log_open(mode='a')

    def log_resume(self):
        fname = getSaveFileNameWithExt(self,
                                       'Open Logfile...',
                                       self.initialdir,
                                       filter=';;'.join(self.LOG_FORMATS))
        if fname:
            self.initialdir = os.path.dirname(fname)
            self.cmd.resume(fname)

    def file_run(self):
        formats = [
            'All Runnable (*.pml *.py *.pym)',
            'PyMOL Command Script (*.pml)',
            'PyMOL Command Script (*.txt)',
            'Python Script (*.py *.pym)',
            'Python Script (*.txt)',
            'All Files(*)',
        ]
        fnames, selectedfilter = getOpenFileNames(self,
                                                  'Open file',
                                                  self.initialdir,
                                                  filter=';;'.join(formats))
        is_py = selectedfilter.startswith('Python')

        with PopupOnException():
            for fname in fnames:
                self.initialdir = os.path.dirname(fname)
                self.cmd.cd(self.initialdir, quiet=0)
                # detect: .py, .pym, .pyc, .pyo, .py.txt
                if is_py or re.search(r'\.py(|m|c|o|\.txt)$', fname, re.I):
                    self.cmd.run(fname)
                else:
                    self.cmd.do("@" + fname)

    def cd_dialog(self):
        dname = QFileDialog.getExistingDirectory(self,
                                                 "Change Working Directory",
                                                 self.initialdir)
        self.cmd.cd(dname or '.', quiet=0)

    def confirm_quit(self):
        QtWidgets.qApp.quit()

    def settings_edit_all_dialog(self):
        from .advanced_settings_gui import PyMOLAdvancedSettings
        if self.advanced_settings_dialog is None:
            self.advanced_settings_dialog = PyMOLAdvancedSettings(
                self, self.cmd)
        self.advanced_settings_dialog.show()

    def show_about(self):
        msg = [
            'The PyMOL Molecular Graphics System\n',
            'Version %s' % (self.cmd.get_version()[0]),
            u'Copyright (C) Schr\xF6dinger, LLC.',
            'All rights reserved.\n',
            'License information:',
        ]

        msg.append('Open-Source Build')

        msg += [
            '',
            'For more information:',
            'https://pymol.org',
            '*****@*****.**',
        ]
        QtWidgets.QMessageBox.about(self, "About PyMOL", '\n'.join(msg))

    #################
    # GUI callbacks
    #################

    if sys.version_info[0] < 3:

        def command_get(self):
            return self.lineedit.text().encode('utf-8')
    else:

        def command_get(self):
            return self.lineedit.text()

    def command_set(self, v):
        return self.lineedit.setText(v)

    def command_set_cursor(self, i):
        return self.lineedit.setCursorPosition(i)

    def update_progress(self):
        progress = self.cmd.get_progress()
        if progress >= 0:
            self.progressbar.setValue(progress * 100)
            self.progressbar.show()
            self.abortbutton.show()
        else:
            self.progressbar.hide()
            self.abortbutton.hide()

    def update_feedback(self):
        self.update_progress()

        feedback = self.cmd._get_feedback()
        if feedback:
            html = colorprinting.text2html('\n'.join(feedback))
            self.browser.appendHtml(html)

            scrollbar = self.browser.verticalScrollBar()
            scrollbar.setValue(scrollbar.maximum())

        for setting in self.cmd.get_setting_updates() or ():
            if setting in self.setting_callbacks:
                current_value = self.cmd.get_setting_tuple(setting)[1][0]
                for callback in self.setting_callbacks[setting]:
                    callback(current_value)

        self.feedback_timer.start(500)

    def doPrompt(self):
        self.doTypedCommand(self.command_get())
        self.pymolwidget._pymolProcess()
        self.lineedit.clear()
        self.feedback_timer.start(0)

    ##########################
    # legacy plugin system
    ##########################

    @PopupOnException.decorator
    def initializePlugins(self):
        from pymol import plugins
        from . import mimic_tk

        self.menudict['Plugin'].clear()

        app = plugins.get_pmgapp()

        plugins.legacysupport.addPluginManagerMenuItem()

        # Redirect to Legacy submenu
        self.menudict['PluginQt'] = self.menudict['Plugin']
        self.menudict['Plugin'] = self.menudict['PluginQt'].addMenu(
            'Legacy Plugins')
        self.menudict['Plugin'].setTearOffEnabled(True)
        self.menudict['PluginQt'].addSeparator()

        plugins.HAVE_QT = True
        plugins.initialize(app)

    def createlegacypmgapp(self):
        from . import mimic_pmg_tk as mimic
        pmgapp = mimic.PMGApp()
        pmgapp.menuBar = mimic.PmwMenuBar(self.menudict)
        return pmgapp

    def window_cmd(self, action, x, y, w, h):
        if action == 0:  # hide
            self.hide()
        elif action == 1:  # show
            self.show()
        elif action == 2:  # position
            self.move(x, y)
        elif action == 3:  # size (first two arguments)
            self.resize(x, y)
        elif action == 4:  # box
            self.move(x, y)
            self.resize(w, h)
        elif action == 5:  # maximize
            self.showMaximized()
        elif action == 6:  # fit
            if hasattr(QtGui,
                       'QWindow') and self.windowHandle().visibility() in (
                           QtGui.QWindow.Maximized, QtGui.QWindow.FullScreen):
                return
            a = QtWidgets.QApplication.desktop().availableGeometry(self)
            g = self.geometry()
            f = self.frameGeometry()
            w = min(f.width(), a.width())
            h = min(f.height(), a.height())
            x = max(min(f.x(), a.right() - w), a.x())
            y = max(min(f.y(), a.bottom() - h), a.y())
            self.setGeometry(
                x - f.x() + g.x(),
                y - f.y() + g.y(),
                w - f.width() + g.width(),
                h - f.height() + g.height(),
            )
        elif action == 7:  # focus
            self.setFocus(Qt.OtherFocusReason)
        elif action == 8:  # defocus
            self.clearFocus()
예제 #19
0
    def __init__(self, parent=None, app=None):
        super(_BuilderPanel, self).__init__(parent)

        self.setWindowTitle("Builder")
        self.setObjectName("builder")
        self.cmd = app.pymol.cmd

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

        self.tabs = QtWidgets.QTabWidget(self)
        self.layout.setContentsMargins(5, 5, 5, 5);
        self.layout.setSpacing(5);
        self.layout.addWidget(self.tabs)
        self.layout.addLayout(self.buttons_layout)
        self.layout.addStretch()

        self.fragments_layout = QtWidgets.QGridLayout()
        self.fragments_layout.setContentsMargins(5, 5, 5, 5);
        self.fragments_layout.setSpacing(5);
        self.fragments_tab = QtWidgets.QWidget()
        self.fragments_tab.setLayout(self.fragments_layout)
        self.protein_layout = QtWidgets.QGridLayout()
        self.protein_layout.setContentsMargins(5, 5, 5, 5);
        self.protein_layout.setSpacing(5);
        self.protein_tab = QtWidgets.QWidget()
        self.protein_tab.setLayout(self.protein_layout)

        self.tabs.addTab(self.fragments_tab, "Chemical")
        self.tabs.addTab(self.protein_tab, "Protein")

        self.getIcons()

        buttons = [
            [ ("H", "Hydrogen", lambda: self.replace("H", 1, 1, "Hydrogen")),
              ("C", "Carbon", lambda: self.replace("C", 4, 4, "Carbon")),
              ("N", "Nitrogen", lambda: self.replace("N", 4, 3, "Nitrogen")),
              ("O", "Oxygen", lambda: self.replace("O", 4, 2, "Oxygen")),
              ("P", "Phosphorus", lambda: self.replace("P",4,3, "Phosphorous")),
              ("S", "Sulfur", lambda: self.replace("S",2,2, "Sulfur")),
              ("F", "Fluorine", lambda: self.replace("F",1,1, "Fluorine")),
              ("Cl", "Chlorrine", lambda: self.replace("Cl",1,1, "Chlorine")),
              ("Br", "Bromine", lambda: self.replace("Br",1,1, "Bromine")),
              ("I", "Iodine", lambda: self.replace("I",1,1, "Iodine")),
              ("-CF3", "Trifluoromethane", lambda: self.replace("trifluoromethane",4,0, "trifluoro")),
              ("-OMe", "Methanol", lambda: self.replace("methanol",5,0, "methoxy")),
            ],
            [ ("CH4", "Methyl", lambda: self.grow("methane",1,0,"methyl")),
              ("C=C", "Ethylene", lambda: self.grow("ethylene",4,0,"vinyl")),
              ("C#C", "Acetylene", lambda: self.grow("acetylene",2,0,"alkynl")),
              ("C#N", "Cyanide", lambda: self.grow("cyanide",2,0,"cyano")),
              ("C=O", "Aldehyde", lambda: self.grow("formaldehyde",2,0,"carbonyl",)),
              ("C=OO", "Formic Acid", lambda: self.grow("formic",4,0,"carboxyl")),
              ("C=ON", "C->N amide", lambda: self.grow("formamide",5,0,"C->N amide")),
              ("NC=O", "N->C amide", lambda: self.grow("formamide",3,1,"N->C amide")),
              ("S=O2", "Sulfone", lambda: self.grow("sulfone",3,1,"sulfonyl")),
              ("P=O3", "Phosphite", lambda: self.grow("phosphite",4,0,"phosphoryl")),
              ("N=O2", "Nitro", lambda: self.grow("nitro",3,0,"nitro")),
            ],
            [
              ("#cyc3", "Cyclopropane", lambda: self.grow("cyclopropane",4,0,"cyclopropyl")),
              ("#cyc4", "Cyclobutane", lambda: self.grow("cyclobutane",4,0,"cyclobutyl")),
              ("#cyc5", "Cyclopentane", lambda: self.grow("cyclopentane",5,0,"cyclopentyl")),
              ("#cyc6", "Cyclohexane", lambda: self.grow("cyclohexane",7,0,"cyclohexyl")),
              ("#cyc7", "Cycloheptane", lambda: self.grow("cycloheptane",8,0,"cycloheptyl")),
              ("#aro5", "Cyclopentadiene", lambda: self.grow("cyclopentadiene",5,0,"cyclopentadienyl")),
              ("#aro6", "Benzene", lambda: self.grow("benzene",6,0,"phenyl")),
              ("#aro65", "Indane", lambda: self.grow("indane",12,0,"indanyl")),
              ("#aro66", "Napthylene", lambda: self.grow("napthylene",13,0,"napthyl")),
              ("#aro67", "Benzocycloheptane", lambda: self.grow("benzocycloheptane",13,0, "benzocycloheptyl")),
            ]
        ]

        self.btn_icons = {}

        requestsize = QtCore.QSize(48, 48)
        for row, btn_row in enumerate(buttons):
            for col, bb in enumerate(btn_row):
                btn_label, btn_tooltip, btn_command = bb
                btn = makeFragmentButton()
                if btn_label.startswith('#'):
                    icons = self.icons[btn_label[1:]]
                    btn.setIcon(icons[0])
                    btn.setIconSize(icons[1].actualSize(requestsize))
                    self.btn_icons[btn] = icons
                else:
                    btn.setText(btn_label)
                btn.setToolTip(btn_tooltip)
                btn.clicked.connect(btn_command)
                self.fragments_layout.addWidget(btn, row, col)

        buttons = [
            [ 'Ace', 'Ala', 'Arg', 'Asn', 'Asp', 'Cys', 'Gln', 'Glu', 'Gly', 'His', 'Ile', 'Leu' ],
            [ 'Lys', 'Met', 'Phe', 'Pro', 'Ser', 'Thr', 'Trp', 'Tyr', 'Val', 'NMe', 'NHH' ]
        ]
        for row, btn_row in enumerate(buttons):
            for col, btn_label in enumerate(btn_row):
                btn = makeFragmentButton()
                btn.setText(btn_label)
                btn.setToolTip("Build %s residue" % btn_label)
                res = btn_label.lower()
                slot = lambda val=None, s=self,r=res: s.attach(r)
                btn.clicked.connect(slot)
                self.protein_layout.addWidget(btn, row, col)

        lab = QtWidgets.QLabel('Secondary Structure:')
        lab_cols = 3
        self.ss_cbox = QtWidgets.QComboBox()
        self.ss_cbox.addItem("Alpha Helix")
        self.ss_cbox.addItem("Beta Sheet (Anti-Parallel)")
        self.ss_cbox.addItem("Beta Sheet (Parallel)")
        self.protein_layout.addWidget(lab, 2, 0, 1, lab_cols)
        self.protein_layout.addWidget(self.ss_cbox, 2, lab_cols, 1, 4)
        self.ss_cbox.currentIndexChanged[int].connect(self.ssIndexChanged)

        buttons = [
            [
              ( "@Atoms:", None, None),
              ( "Fix H", "Fix hydrogens on picked atoms", self.fixH),
              ( "Add H", "Add hydrogens to entire molecule", self.addH),
              ( "Invert", "Invert stereochemistry around pk1 (pk2 and pk3 will remain fixed)", self.invert),
              ( "Delete", "Remove atoms", self.removeAtom),
              ( "Clear", "Delete everything", self.clear),
              ( "@   Charge:", None, None),
              ( " +1 ", "Positive Charge", lambda: self.setCharge(1,"+1")),
              ( "  0 ", "Neutral Charge", lambda: self.setCharge(0,"neutral")),
              ( " -1 ", "Negative Charge", lambda: self.setCharge(-1,"-1")),
            ],
            [
              ( "@Bonds:", None, None),
              ( "Create", "Create bond between pk1 and pk2", self.createBond),
              ( "Delete", "Delete bond between pk1 and pk2", self.deleteBond),
              ( "Cycle", "Cycle bond valence", self.cycleBond),
              ( "  |  ", "Create single bond", lambda: self.setOrder("1", "single")),
              ( " || ", "Create double bond", lambda: self.setOrder("2", "double")),
              ( " ||| ", "Create triple bond", lambda: self.setOrder("3", "triple")),
              ( "Arom", "Create aromatic bond", lambda: self.setOrder("4", "aromatic")),
              ( "@   Model:", None, None),
              ( "Clean", "Cleanup structure", self.clean),
              ( "Sculpt", "Molecular sculpting", self.sculpt),
              ( "Fix", "Fix atom positions", self.fix),
              ( "Rest", "Restrain atom positions", self.rest),
            ],
            [
              ( "$El-stat", "Electrostatics term for 'Clean' action", "clean_electro_mode"),
              ( "@   ", None, None),
              ( "$Bumps", "Show VDW contacts during sculpting", "sculpt_vdw_vis_mode"),
              ( "@   ", None, None),
              ( "#Undo Enabled", "", "suspend_undo"),
              ( "Undo", "Undo last change", self.undo),
              ( "Redo", "Redo last change", self.redo),
            ]
        ]

        for row, btn_row in enumerate(buttons):
            btn_row_layout = QtWidgets.QHBoxLayout()
            self.buttons_layout.addLayout(btn_row_layout)
            for col, bb in enumerate(btn_row):
                btn_label, btn_tooltip, btn_command = bb
                if btn_label[0] == '@':
                    btn = QtWidgets.QLabel(btn_label[1:])
                elif btn_label[0] in ('#', '$'):
                    btn = QtWidgets.QCheckBox(btn_label[1:])
                    setting = btn_command
                    value = self.cmd.get_setting_int(setting)
                    if btn_label[0] == '$':
                        btn.setChecked(bool(value))
                        @btn.toggled.connect
                        def _(checked, n=setting):
                            self.cmd.set(n, checked, quiet=0)
                    else:
                        btn.setChecked(not value)
                        @btn.toggled.connect
                        def _(checked, n=setting):
                            self.cmd.set(n, not checked, quiet=0)
                else:
                    btn = makeFragmentButton()
                    btn.setText(btn_label)
                    btn.clicked.connect(btn_command)
                if btn_tooltip:
                    btn.setToolTip(btn_tooltip)
                btn_row_layout.addWidget(btn)
            btn_row_layout.addStretch()
예제 #20
0
파일: volume.py 프로젝트: icamps/AMDock-win
 def sizeHint(self):
     return QtCore.QSize(600, 200)
예제 #21
0
    def __init__(self, parent):
        self.gui = parent

        # OpenGL context setup
        f = QtOpenGL.QGLFormat()
        f.setRgba(True)
        f.setDepth(True)
        f.setDoubleBuffer(True)

        from pymol.invocation import options

        # logic equivalent to layer5/main.cpp:launch

        if options.multisample:
            f.setSampleBuffers(True)

        if options.force_stereo != -1:
            # See layer1/Setting.h for stereo modes

            if options.stereo_mode in (0, 1, 12):
                # this effectively disables stereo detection
                # on Linux that is faulty in QGLWidget / PyQt5
                if not (options.stereo_mode == 0
                        and sys.platform.startswith("linux")):
                    f.setStereo(True)

            if options.stereo_mode in (11, 12):
                f.setAccum(True)

            if options.stereo_mode in (0, 6, 7, 8, 9):
                f.setStencil(True)

        QtOpenGL.QGLWidget.__init__(self, f, parent=parent)

        if not self.isValid():
            raise RuntimeError('OpenGL initialization failed')

        f_actual = self.format()

        # report if quad buffer available
        options.stereo_capable = int(f_actual.stereo()
                                     or (options.force_stereo == 1))

        # feedback if stereo request failed
        if options.stereo_mode and (
                # QTBUG-59636 f.stereo() and not f_actual.stereo() or
                f.accum() and not f_actual.accum()
                or f.stencil() and not f_actual.stencil()):
            # cPyMOLGlobals_LaunchStatus_StereoFailed
            options.launch_status |= 0x1

        # feedback if multisample request failed
        if options.multisample and not f_actual.sampleBuffers():
            # cPyMOLGlobals_LaunchStatus_MultisampleFailed
            options.launch_status |= 0x2

        # pymol instance
        self.pymol = PyMOL()
        self.pymol.start()
        self.cmd = self.pymol.cmd

        # capture python output for feedback
        import pcatch
        pcatch._install()

        # for passive move drag
        self.setMouseTracking(True)

        # for accepting keyboard input (command line, shortcuts)
        self.setFocusPolicy(Qt.ClickFocus)

        # for idle rendering
        self._timer = QtCore.QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self._pymolProcess)

        # drag n drop
        self.setAcceptDrops(True)

        # pinch-zoom
        self.grabGesture(Qt.PinchGesture)