File: mimic_pmg_tk.py

package info (click to toggle)
pymol 2.4.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 43,312 kB
  • sloc: cpp: 480,106; python: 79,860; ansic: 28,343; javascript: 6,792; sh: 47; makefile: 30; csh: 8
file content (182 lines) | stat: -rw-r--r-- 4,829 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
'''
Mimic the pmg_tk API for plugin legacy support
'''

from __future__ import absolute_import

import sys

tkinter = None

def tkinter_init():
    global tkinter

    if hasattr(_BaseWidget_setup, '_super'):
        raise RuntimeError('tkinter init failed')

    if sys.version_info[0] < 3:
        import Tkinter as tkinter
    else:
        import tkinter

    _BaseWidget_setup._super = tkinter.BaseWidget._setup
    tkinter.BaseWidget._setup = _BaseWidget_setup


# monkeypatch the "_default_root" assignment of Tkinter
def _BaseWidget_setup(self, master, cnf):
    if not master and not tkinter._default_root:
        from pymol import plugins
        tkinter._default_root = plugins.get_tk_root()
    return _BaseWidget_setup._super(self, master, cnf)


class PmwMenuBar:
    def __init__(self, menudict):
        self._menudict = menudict

    def _get_menu(self, menuName):
        try:
            return self._menudict[menuName]
        except KeyError:
            print('Error: no such menu: ' + repr(menuName))
            return None

    def addmenu(self, menuName, *args, **kw):
        self.addcascademenu('', menuName)

    def deletemenuitems(self, menuName, start, end=None):
        menu = self._get_menu(menuName)
        if menu is None:
            return

        for a in menu.actions()[start - 1:(end or start)]:
            menu.removeAction(a)

    def addmenuitem(self, menuName, itemType, statusHelp='',
                    traverseSpec=None, **kw):
        menu = self._get_menu(menuName)
        if menu is None:
            return

        if itemType == 'separator':
            menu.addSeparator()
            return

        if itemType != 'command' or 'command' not in kw:
            return

        # Wrapper for exception safety. PyMOL would crash if an exception
        # is not caught!
        def wrapper(command=kw['command']):
            try:
                command()
            except BaseException as e:
                from pymol import colorprinting
                colorprinting.print_exc([__file__])

                from pymol.Qt import QtWidgets
                QtWidgets.QMessageBox.critical(None, 'Error', str(e))

        label = kw.get('label', statusHelp)
        menu.addAction(label, wrapper)

    def addcascademenu(self, parentMenuName, menuName, statusHelp='',
                       traverseSpec=None, **kw):
        menu = self._get_menu(parentMenuName)
        if menu is None:
            return

        if menuName in self._menudict:
            raise ValueError('menu ' + repr(menuName) + ' exists')

        label = kw.get('label', statusHelp) or menuName
        menu = menu.addMenu(label)
        menu.setTearOffEnabled(True)
        self._menudict[menuName] = menu


class PMGSkin(object):
    def __init__(self, pmgapp):
        self._pmgapp = pmgapp
        self._setting = None

    @property
    def setting(self):
        if self._setting is None:
            from pmg_tk.Setting import Setting
            self._setting = Setting(self._pmgapp)

        return self._setting


class tkapp_proxy(object):
    def __init__(self, proxied, pmgapp):
        self._proxied = proxied
        self._pmgapp = pmgapp

    def __getattr__(self, name):
        return getattr(self._proxied, name)

    def call(self, tkcmd, *args):
        # suspend our own updates for commands which enter the event loop
        pause = tkcmd in ('update', 'tkwait', 'vwait')

        if pause:
            self._pmgapp._tk_update_paused += 1

        try:
            r = self._proxied.call(tkcmd, *args)
        finally:
            if pause:
                self._pmgapp._tk_update_paused -= 1

        return r


class PMGApp(object):
    def __init__(self):
        import pymol
        self._root = None
        self.pymol = pymol
        self.skin = PMGSkin(self)
        self._tk_update_paused = 0

    @property
    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

    def execute(self, c):
        return eval(c) if isinstance(c, str) else c()

    def my_show(self, w, c=1):
        w.show()


# vi:expandtab:sw=4