-
-
Notifications
You must be signed in to change notification settings - Fork 653
/
app.py
259 lines (203 loc) · 8.11 KB
/
app.py
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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
import asyncio
import os
import os.path
import signal
import sys
from urllib.parse import unquote, urlparse
import gbulb
import toga
from toga import Icon
from toga.command import GROUP_BREAK, SECTION_BREAK, Command
from .keys import gtk_accel
from .libs import Gio, GLib, Gtk
from .window import Window
def gtk_menu_item_activate(cmd):
"""Convert a GTK menu item activation into a command invocation"""
def _handler(action, data):
cmd.action(cmd)
return _handler
class MainWindow(Window):
_IMPL_CLASS = Gtk.ApplicationWindow
def create(self):
super().create()
self.native.set_role("MainWindow")
self.native.set_icon(Icon.app_icon._impl.native_72.get_pixbuf())
def set_app(self, app):
super().set_app(app)
# The GTK docs list set_wmclass() as deprecated (and "pointless")
# but it's the only way I've found that actually sets the
# Application name to something other than '__main__.py'.
self.native.set_wmclass(app.interface.name, app.interface.name)
def on_close(self, *args):
pass
class App:
"""
Todo:
* Creation of Menus is not working.
* Disabling of menu items is not working.
* App Icon is not showing up
"""
def __init__(self, interface):
self.interface = interface
self.interface._impl = self
gbulb.install(gtk=True)
self.loop = asyncio.get_event_loop()
self.create()
def create(self):
Icon.app_icon = Icon.load(self.interface.icon, default=Icon.TIBERIUS_ICON)
Icon.app_icon.bind(self.interface.factory)
# Stimulate the build of the app
self.native = Gtk.Application(
application_id=self.interface.app_id,
flags=Gio.ApplicationFlags.FLAGS_NONE
)
# Connect the GTK signal that will cause app startup to occur
self.native.connect('startup', self.gtk_startup)
self.native.connect('activate', self.gtk_activate)
# self.native.connect('shutdown', self.shutdown)
self.actions = None
def gtk_startup(self, data=None):
self.interface.commands.add(
Command(None, 'About ' + self.interface.name, group=toga.Group.APP),
Command(None, 'Preferences', group=toga.Group.APP),
# Quit should always be the last item, in a section on it's own
Command(
lambda widget, data: self.exit(),
'Quit ' + self.interface.name,
shortcut=toga.Key.MOD_1 + 'q',
group=toga.Group.APP,
section=sys.maxsize
),
Command(None, 'Visit homepage', group=toga.Group.HELP)
)
self._create_app_commands()
self.interface.startup()
# Create the lookup table of menu items,
# then force the creation of the menus.
self._actions = {}
self.create_menus()
# self.interface.main_window._impl.create_toolbar()
def _create_app_commands(self):
# No extra menus
pass
def gtk_activate(self, data=None):
pass
def create_menus(self):
# Only create the menu if the menu item index has been created.
if hasattr(self, '_actions'):
self._actions = {}
menubar = Gio.Menu()
label = None
submenu = None
section = None
for cmd in self.interface.commands:
if cmd == GROUP_BREAK:
if section:
submenu.append_section(None, section)
if label == '*':
self.native.set_app_menu(submenu)
else:
menubar.append_submenu(label, submenu)
label = None
submenu = None
section = None
elif cmd == SECTION_BREAK:
submenu.append_section(None, section)
section = None
else:
if submenu is None:
label = cmd.group.label
submenu = Gio.Menu()
if section is None:
section = Gio.Menu()
try:
action = self._actions[cmd]
except KeyError:
cmd_id = "command-%s" % id(cmd)
action = Gio.SimpleAction.new(cmd_id, None)
if cmd.action:
action.connect("activate", gtk_menu_item_activate(cmd))
cmd._impl.native.append(action)
cmd._impl.set_enabled(cmd.enabled)
self._actions[cmd] = action
self.native.add_action(action)
item = Gio.MenuItem.new(cmd.label, 'app.' + cmd_id)
if cmd.shortcut:
item.set_attribute_value('accel', GLib.Variant('s', gtk_accel(cmd.shortcut)))
section.append_item(item)
if section:
submenu.append_section(None, section)
if submenu:
if label == '*':
self.native.set_app_menu(submenu)
else:
menubar.append_submenu(label, submenu)
# Set the menu for the app.
self.native.set_menubar(menubar)
def main_loop(self):
# Modify signal handlers to make sure Ctrl-C is caught and handled.
signal.signal(signal.SIGINT, signal.SIG_DFL)
self.loop.run_forever(application=self.native)
def set_main_window(self, window):
pass
def exit(self):
self.native.quit()
def set_on_exit(self, value):
pass
def current_window(self):
return self.native.get_active_window()._impl
def enter_full_screen(self, windows):
for window in windows:
window._impl.set_full_screen(True)
def exit_full_screen(self, windows):
for window in windows:
window._impl.set_full_screen(False)
def show_cursor(self):
self.interface.factory.not_implemented('App.show_cursor()')
def hide_cursor(self):
self.interface.factory.not_implemented('App.hide_cursor()')
def add_background_task(self, handler):
self.interface.factory.not_implemented('App.add_background_task()')
class DocumentApp(App):
def _create_app_commands(self):
self.interface.commands.add(
toga.Command(
self.open_file,
label='Open...',
shortcut=toga.Key.MOD_1 + 'o',
group=toga.Group.FILE,
section=0
),
)
def gtk_startup(self, data=None):
super().gtk_startup(data=data)
try:
# Look for a filename specified on the command line
file_name = os.path.abspath(sys.argv[1])
except IndexError:
# Nothing on the command line; open a file dialog instead.
# TODO: This causes a blank window to be shown.
# Is there a way to open a file dialog without having a window?
m = toga.Window()
file_name = m.select_folder_dialog(self.interface.name, None, False)[0]
self.open_document(file_name)
def open_file(self, widget, **kwargs):
# TODO: This causes a blank window to be shown.
# Is there a way to open a file dialog without having a window?
m = toga.Window()
file_name = m.select_folder_dialog(self.interface.name, None, False)[0]
self.open_document(file_name)
def open_document(self, fileURL):
"""Open a new document in this app.
Args:
fileURL (str): The URL/path to the file to add as a document.
"""
# Convert the fileURL to a file path.
fileURL = fileURL.rstrip('/')
path = unquote(urlparse(fileURL).path)
extension = os.path.splitext(path)[1][1:]
# Create the document instance
DocType = self.interface.document_types[extension]
document = DocType(fileURL, self.interface)
self.interface._documents.append(document)
document.show()