File: view.py

package info (click to toggle)
pyxrd 0.8.4-5
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 7,644 kB
  • sloc: python: 26,501; sh: 301; makefile: 128
file content (242 lines) | stat: -rw-r--r-- 8,139 bytes parent folder | download | duplicates (2)
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# coding=UTF-8
# ex:ts=4:sw=4:et=on
#  -------------------------------------------------------------------------
#  Copyright (C) 2014 by Mathijs Dumon <mathijs dot dumon at gmail dot com>
#  Copyright (c) 2007 by Guillaume Libersat <glibersat AT linux62.org>
#  Copyright (C) 2005 by Roberto Cavada <roboogle@gmail.com>
#
#  mvc is a framework derived from the original pygtkmvc framework
#  hosted at: <http://sourceforge.net/projects/pygtkmvc/>
#
#  mvc is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 2 of the License, or (at your option) any later version.
#
#  mvc is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor,
#  Boston, MA 02110, USA.
#  -------------------------------------------------------------------------

# TODO remove gladeXML: deprecated!

from .support.exceptions import ViewError

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
Builder = Gtk.Builder

# ----------------------------------------------------------------------

class View (object):
    top = None
    builder = None

    def __init__(self, top=None,
                 parent=None,
                 builder=None, *args, **kwargs):
        """
        Only the first three may be given as positional arguments. If an
        argument is empty a class attribute of the same name is used. This
        does not work for *parent*.

        *builder* is a path to a Glade XML file.

        *top* is a string containing the name of our top level widget.

        *parent* is used to call :meth:`set_parent_view`.

        The last two only work if *builder* is used, not if you
        intend to create widgets later from code.
        """
        super(View, self).__init__(*args, **kwargs)

        self.manualWidgets = {}
        self.autoWidgets = {}
        self.__autoWidgets_calculated = False

        if top: self._top = top
        else: self._top = self.top

        # retrieves objects from builder if available
        if builder: _builder = builder
        else: _builder = self.builder
        if _builder is not None:
            # if the user passed a Builder, use it as it is, otherwise
            # build one
            if isinstance(_builder, Builder):
                self._builder = _builder
            else:
                self._builder = Gtk.Builder()
                self._builder.add_from_file(_builder)
                pass
            pass
        else: self._builder = None # no gtk builder

        if parent is not None: self.set_parent_view(parent)

        return

    def __getitem__(self, key):
        """
        Return the widget named *key*, or ``None``.
        
        .. note::
        
           In future versions this will likely change to raise ``KeyError``.
        """
        wid = None

        # first try with manually-added widgets:
        if key in self.manualWidgets:
            wid = self.manualWidgets[key]
            pass

        if wid is None:
            # then try with glade and builder, starting from memoized
            if key in self.autoWidgets: wid = self.autoWidgets[key]
            else:
                # try with Gtk.builder
                if wid is None and self._builder is not None:
                    wid = self._builder.get_object(key)
                    if wid is not None:
                        self.autoWidgets[key] = wid
                        pass
                    pass
                pass
            pass

        return wid

    def __setitem__(self, key, wid):
        """
        Add a widget. This overrides widgets of the same name that were loaded
        from XML. It does not affect GTK container/child relations.
        
        If no top widget is known, this sets it.
        """
        self.manualWidgets[key] = wid
        return

    def show(self):
        """
        Call `show()` on each top widget or `show_all()` if only one is known. 
        Otherwise does nothing.
        """
        top = self.get_top_widget()
        if type(top) in (list, tuple):
            for t in top:
                if t is not None: t.show()
                pass
        elif (top is not None): top.show_all()
        return


    def hide(self):
        """
        Call `hide()` on all known top widgets.
        """
        top = self.get_top_widget()
        if type(top) in (list, tuple):
            for t in top:
                if t is not None: t.hide()
                pass
        elif top is not None: top.hide()
        return

    def get_top_widget(self):
        return self[self._top]

    def set_parent_view(self, parent_view):
        """
        Set ``self.``:meth:`get_top_widget` transient for 
        ``parent_view.get_top_widget()``.
        """
        top = self.get_top_widget()
        if type(top) in (list, tuple):
            for t in top:
                if t is not None and hasattr(t, "set_transient_for"):
                    t.set_transient_for(parent_view.get_top_widget())
                    pass
                pass
        elif (top is not None) and hasattr(top, "set_transient_for"):
            top.set_transient_for(parent_view.get_top_widget())
            pass

        return

    def set_transient(self, transient_view):
        """
        Set ``transient_view.get_top_widget()`` transient for
        ``self.``:meth:`get_top_widget`.
        """
        top = self.get_top_widget()
        if type(top) in (list, tuple):
            for t in top:
                if t is not None:
                    transient_view.get_top_widget().set_transient_for(t)
                    pass
                pass
        elif (top is not None):
            transient_view.get_top_widget().set_transient_for(top)
            pass
        return

    # Finds the right callback for custom widget creation and calls it
    # Returns None if an undefined or invalid  handler is found
    def _custom_widget_create(self, glade, function_name, widget_name,
                              str1, str2, int1, int2):
        # This code was kindly provided by Allan Douglas <zalguod at
        # users.sourceforge.net>
        if function_name is not None:
            handler = getattr(self, function_name, None)
            if handler is not None: return handler(str1, str2, int1, int2)
            pass
        return None

    def __iter__(self):
        """
        Return an iterator over widgets added with :meth:`__setitem__` and
        those loaded from XML.
        
        .. note::
           In case of name conflicts this yields widgets that are not 
           accessible via :meth:`__getitem__`.
        """
        # precalculates if needed
        self.__extract_autoWidgets()

        import itertools
        for i in itertools.chain(self.manualWidgets, self.autoWidgets): yield i
        return

    def __extract_autoWidgets(self):
        """Extract autoWidgets map if needed, out of the glade
        specifications and gtk builder"""
        if self.__autoWidgets_calculated: return

        if self._builder is not None:
            for wid in self._builder.get_objects():
                # General workaround for issue
                # https://bugzilla.gnome.org/show_bug.cgi?id=607492
                try: name = Gtk.Buildable.get_name(wid)
                except TypeError: continue

                if name in self.autoWidgets and self.autoWidgets[name] != wid:
                    raise ViewError("Widget '%s' in builder also found in glade specification" % name)

                self.autoWidgets[name] = wid
                pass
            pass

        self.__autowidgets_calculated = True
        return

    pass # end of class View