Merge lp:~shakaran/userwebkit/class-refactor into lp:userwebkit

Proposed by Angel Guzman Maeso
Status: Needs review
Proposed branch: lp:~shakaran/userwebkit/class-refactor
Merge into: lp:userwebkit
Diff against target: 705 lines (+334/-204)
8 files modified
couchview.py (+158/-0)
debian/copyright (+1/-1)
demo-app.py (+7/-7)
demo-app2.py (+6/-6)
hub.py (+81/-0)
inspector.py (+54/-0)
test_userwebkit.py (+12/-10)
userwebkit.py (+15/-180)
To merge this branch: bzr merge lp:~shakaran/userwebkit/class-refactor
Reviewer Review Type Date Requested Status
Jason Gerard DeRose Disapprove
Review via email: mp+123169@code.launchpad.net

Description of the change

Major changes:
- Split userwebkit classes (Hub, Inspector and CouchView) in different files for reuse components easily. Useful when you want use only a component from other project and don't import all module or only a part. Also it makes more easily to track changes with more reduced files.
- Update test according to last refactor.

Minor changes:
- Update copyright year (debian file), demo-app.py and demo-app2.py
- Remove blank lines and update shebang in demo-app.py and demo-app2.py

Problems to review:
- I cannot fix one test regarding to FactoryHub signals. I think that signals are not properly erased in teardown test. - Also check if setup.py include all refactored files in installation.

To post a comment you must log in.
Revision history for this message
Jason Gerard DeRose (jderose) wrote :

Sorry Angel, I'm gonna say no on this one :)

As userwebkit.py is less than 500 lines, I don't think this split is yet needed for maintainability. Plus, I think we need more work on the existing API before we know how such a split should be done.

The startup time of a Python app also has a somewhat steep per-module overhead. So in terms of looking at performance and memory usage, it's generally more import to split modules based on what those modules themselves then import. And in this case, everything needs to import `gi.repository.Gtk` (which is where the biggest overhead is), so if you're using anything from userwebkit.py, there is little benefit to importing just a fragment of it.

One more note: once a split like this is done, the best way to do it is to bzr mv the current userwebkit.py into userwebkit/__init__.py and put new modules inside the userwebkit/ Python package.

I'm going to do the above when I bring back the JavaScript unit tester that used to be in Dmedia (it hasn't yet been ported to Python3 because of dependency issues). But this is something that will only be used for unit tests, not at run-time, so having it in a separate module makes sense.

review: Disapprove

Unmerged revisions

70. By Angel Guzman Maeso <email address hidden>

Update test according to last refactor. Only I cannot fix one test. I think that signals are not properly erased in teardown test. Also check if setup.py include all refactored files in installation. Needs review

69. By Angel Guzman Maeso <email address hidden>

Split userwebkit classes (Hub, Inspector and CouchView) in different files for reuse components easily

68. By Angel Guzman Maeso <email address hidden>

Update copyright year, remove blank lines and update shebang

67. By Angel Guzman Maeso <email address hidden>

Update copyright year

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'couchview.py'
2--- couchview.py 1970-01-01 00:00:00 +0000
3+++ couchview.py 2012-09-06 20:58:20 +0000
4@@ -0,0 +1,158 @@
5+#!/usr/bin/env python
6+# -*- coding: utf-8; tab-width: 4; mode: python -*-
7+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
8+# vi: set ft=python sts=4 ts=4 sw=4 noet
9+#
10+# novacut: the collaborative video editor
11+# Copyright (C) 2011-2012 Novacut Inc
12+#
13+# This file is part of `novacut`.
14+#
15+# `novacut` is free software: you can redistribute it and/or modify it under
16+# the terms of the GNU Affero General Public License as published by the Free
17+# Software Foundation, either version 3 of the License, or (at your option)
18+# any later version.
19+#
20+# `novacut` is distributed in the hope that it will be useful, but WITHOUT ANY
21+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
23+# more details.
24+#
25+# You should have received a copy of the GNU Affero General Public License
26+# along with `novacut`. If not, see <http://www.gnu.org/licenses/>.
27+#
28+# Authors:
29+# Jason Gerard DeRose <jderose@novacut.com>
30+
31+from urllib.parse import urlparse, parse_qsl
32+from gi.repository import GObject, Gtk, WebKit
33+from gi.repository.GObject import TYPE_PYOBJECT
34+from microfiber import _oauth_header, _basic_auth_header
35+
36+import logging
37+log = logging.getLogger('userwebkit')
38+
39+class CouchView(WebKit.WebView):
40+ __gsignals__ = {
41+ 'open': (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
42+ [TYPE_PYOBJECT]
43+ ),
44+ }
45+
46+ def __init__(self, env=None, dmedia_resolver=None):
47+ super().__init__()
48+ self._logging_enabled = False
49+ ##
50+ self.connect('resource-request-starting', self._on_request)
51+ self.connect('navigation-policy-decision-requested',
52+ self._on_nav_policy_decision
53+ )
54+ self.set_env(env)
55+ self._dmedia_resolver = dmedia_resolver
56+
57+ def set_env(self, env):
58+ self._env = env
59+ if env is None:
60+ self._u = None
61+ self._oauth = None
62+ self._basic = None
63+ return
64+ self._u = urlparse(env['url'])
65+ self._oauth = env.get('oauth')
66+ self._basic = env.get('basic')
67+
68+ def set_recv(self, recv):
69+ """ Executed by Hub object when recieve data """
70+ self._recv = recv
71+ self.connect('notify::title', self._on_notify_title)
72+
73+ def enable_logging(self):
74+ """
75+ Enables send console message to Python logging
76+ """
77+ if not self._logging_enabled:
78+ self._logging_enabled = True
79+ self.connect('console-message', self._on_console_message)
80+
81+ def _on_console_message(self, view, message, line, source_id):
82+ log.debug('%s @%s: %s', source_id, line, message)
83+
84+ def enable_view_editable(self):
85+ """
86+ Enables the webkit view as editable
87+ """
88+ self.set_editable(True)
89+
90+ def _on_request(self, view, frame, resource, request, response):
91+ if self._env is None:
92+ return
93+ uri = request.get_uri()
94+ message = request.get_message()
95+ if uri.startswith('dmedia'):
96+ if self._dmedia_resolver is None:
97+ request.set_uri('')
98+ else:
99+ request.set_uri(self._dmedia_resolver(uri))
100+ return
101+ u = urlparse(uri)
102+ if u.netloc != self._u.netloc:
103+ return
104+ if u.scheme != self._u.scheme:
105+ return
106+ if self._oauth:
107+ query = dict(parse_qsl(u.query))
108+ if u.query and not query:
109+ query = {u.query: ''}
110+ baseurl = ''.join([u.scheme, '://', u.netloc, u.path])
111+ print ('baseurl:' + str(baseurl))
112+ method = message.method
113+ print ('method:' + str(method))
114+ h = _oauth_header(self._oauth, method, baseurl, query)
115+ elif self._basic:
116+ h = _basic_auth_header(self._basic)
117+ else:
118+ return
119+
120+ # Clean headers previously to add new headers (reset)
121+ # Removes all the headers listed in the Connection header.
122+ message.request_headers.clean_connection_headers()
123+
124+ for (key, value) in h.items():
125+ message.request_headers.append(key, value)
126+
127+ def _on_nav_policy_decision(self, view, frame, request, nav, policy):
128+ """
129+ Handle user trying to Navigate away from current page.
130+
131+ Note that this will be called before `CouchView._on_resource_request()`.
132+
133+ The *policy* arg is a ``WebPolicyDecision`` instance. To handle the
134+ decision, call one of:
135+
136+ * ``WebPolicyDecision.ignore()``
137+ * ``WebPolicyDecision.use()``
138+ * ``WebPolicyDecision.download()``
139+
140+ And then return ``True``.
141+
142+ Otherwise, return ``False`` or ``None`` to have the WebKit default
143+ behavior apply.
144+ """
145+ if self._env is None:
146+ return
147+ uri = request.get_uri()
148+ u = urlparse(uri)
149+ if u.netloc == self._u.netloc or u.scheme == 'file':
150+ return False
151+ if u.scheme in ('http', 'https'):
152+ self.emit('open', uri)
153+ policy.ignore()
154+ return True
155+
156+ def _on_notify_title(self, view, notify):
157+ title = view.get_property('title')
158+ log.debug('Title %s', str(title))
159+ if title is None:
160+ return
161+ self._recv(title)
162+
163\ No newline at end of file
164
165=== modified file 'debian/copyright'
166--- debian/copyright 2011-09-26 10:41:04 +0000
167+++ debian/copyright 2012-09-06 20:58:20 +0000
168@@ -6,7 +6,7 @@
169
170 Copyright and license:
171
172-| Copyright (C) 2011 Novacut Inc
173+| Copyright (C) 2011-2012 Novacut Inc
174 |
175 | This file is part of `userwebkit`.
176 |
177
178=== modified file 'demo-app.py'
179--- demo-app.py 2012-04-14 08:20:09 +0000
180+++ demo-app.py 2012-09-06 20:58:20 +0000
181@@ -1,7 +1,10 @@
182-#!/usr/bin/python3
183-
184+#!/usr/bin/env python3
185+# -*- coding: utf-8; tab-width: 4; mode: python -*-
186+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
187+# vi: set ft=python sts=4 ts=4 sw=4 noet
188+#
189 # novacut: the collaborative video editor
190-# Copyright (C) 2011 Novacut Inc
191+# Copyright (C) 2011-2012 Novacut Inc
192 #
193 # This file is part of `novacut`.
194 #
195@@ -27,7 +30,6 @@
196 import userwebkit
197 from userwebkit import BaseApp
198
199-
200 class App(BaseApp):
201 name = 'demo'
202 dbname = 'demo-0'
203@@ -86,7 +88,5 @@
204 def dmedia_resolver(self, uri):
205 return self.proxy.ResolveURI(uri)
206
207-
208 app = App()
209-app.run()
210-
211+app.run()
212\ No newline at end of file
213
214=== modified file 'demo-app2.py'
215--- demo-app2.py 2012-04-14 08:10:53 +0000
216+++ demo-app2.py 2012-09-06 20:58:20 +0000
217@@ -1,5 +1,8 @@
218-#!/usr/bin/python3
219-
220+#!/usr/bin/env python3
221+# -*- coding: utf-8; tab-width: 4; mode: python -*-
222+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
223+# vi: set ft=python sts=4 ts=4 sw=4 noet
224+#
225 # novacut: the collaborative video editor
226 # Copyright (C) 2011 Novacut Inc
227 #
228@@ -24,7 +27,6 @@
229 import userwebkit
230 from userwebkit import BaseApp
231
232-
233 class App(BaseApp):
234 name = 'demo'
235 dbname = 'demo-0'
236@@ -34,7 +36,5 @@
237 page = 'index2.html'
238 proxy_bus = 'org.freedesktop.Dmedia'
239
240-
241 app = App()
242-app.run()
243-
244+app.run()
245\ No newline at end of file
246
247=== added file 'hub.py'
248--- hub.py 1970-01-01 00:00:00 +0000
249+++ hub.py 2012-09-06 20:58:20 +0000
250@@ -0,0 +1,81 @@
251+# -*- coding: utf-8; tab-width: 4; mode: python -*-
252+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
253+# vi: set ft=python sts=4 ts=4 sw=4 noet
254+#
255+# userwebkit: so WebKitGtk apps can to talk to a usercouch
256+# Copyright (C) 2011-2012 Novacut Inc
257+#
258+# This file is part of `userwebkit`.
259+#
260+# `userwebkit` is free software: you can redistribute it and/or modify it under
261+# the terms of the GNU Lesser General Public License as published by the Free
262+# Software Foundation, either version 3 of the License, or (at your option) any
263+# later version.
264+#
265+# `userwebkit` is distributed in the hope that it will be useful, but WITHOUT
266+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
267+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
268+# details.
269+#
270+# You should have received a copy of the GNU Lesser General Public License along
271+# with `userwebkit`. If not, see <http://www.gnu.org/licenses/>.
272+#
273+# Authors:
274+# Jason Gerard DeRose <jderose@novacut.com>
275+from gi.repository import GObject
276+
277+import json
278+
279+import logging
280+log = logging.getLogger('userwebkit')
281+
282+def iter_gsignals(signals):
283+ print (signals)
284+ assert isinstance(signals, dict)
285+ for (name, argnames) in signals.items():
286+ assert isinstance(argnames, list)
287+ args = [GObject.TYPE_PYOBJECT for argname in argnames]
288+ yield (name, (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, args))
289+
290+def hub_factory(signals):
291+ if signals:
292+ class FactoryHub(Hub):
293+ __gsignals__ = dict(iter_gsignals(signals))
294+ return FactoryHub
295+ return Hub
296+
297+class Hub(GObject.GObject):
298+ def __init__(self, view):
299+ super().__init__()
300+ self._view = view
301+
302+ # Key aspect, send via notify::title to view the recieved data
303+ view.set_recv(self.recv)
304+
305+ def recv(self, data):
306+ try:
307+ obj = json.loads(data)
308+ log.debug('Hub event(recv): %s', str(obj))
309+
310+ if obj['args'] == None:
311+ self.emit(obj['signal'], None)
312+ else:
313+ self.emit(obj['signal'], *obj['args'])
314+ except ValueError:
315+ pass
316+
317+ def send(self, signal, *args):
318+ """
319+ Emit a signal by calling the JavaScript Signal.recv() function.
320+ """
321+
322+ log.debug('Hub event(send): signal=%s', str(signal))
323+
324+ script = 'Hub.recv({!r})'.format(
325+ json.dumps({'signal': signal, 'args': args})
326+ )
327+
328+ log.debug('Executing javascript:\n%s', str(script))
329+
330+ self._view.execute_script(script)
331+ self.emit(signal, *args)
332\ No newline at end of file
333
334=== added file 'inspector.py'
335--- inspector.py 1970-01-01 00:00:00 +0000
336+++ inspector.py 2012-09-06 20:58:20 +0000
337@@ -0,0 +1,54 @@
338+# -*- coding: utf-8; tab-width: 4; mode: python -*-
339+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
340+# vi: set ft=python sts=4 ts=4 sw=4 noet
341+#
342+# userwebkit: so WebKitGtk apps can to talk to a usercouch
343+# Copyright (C) 2011-2012 Novacut Inc
344+#
345+# This file is part of `userwebkit`.
346+#
347+# `userwebkit` is free software: you can redistribute it and/or modify it under
348+# the terms of the GNU Lesser General Public License as published by the Free
349+# Software Foundation, either version 3 of the License, or (at your option) any
350+# later version.
351+#
352+# `userwebkit` is distributed in the hope that it will be useful, but WITHOUT
353+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
354+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
355+# details.
356+#
357+# You should have received a copy of the GNU Lesser General Public License along
358+# with `userwebkit`. If not, see <http://www.gnu.org/licenses/>.
359+#
360+# Authors:
361+# Jason Gerard DeRose <jderose@novacut.com>
362+
363+from gi.repository import Gtk
364+from couchview import CouchView
365+
366+class Inspector(Gtk.VBox):
367+ def __init__(self, env):
368+ super().__init__()
369+
370+ hbox = Gtk.HBox()
371+ self.pack_start(hbox, False, False, 0)
372+
373+ close = Gtk.Button(stock=Gtk.STOCK_CLOSE)
374+ hbox.pack_start(close, False, False, 2)
375+ close.connect('clicked', self.on_close)
376+
377+ self.reload = Gtk.Button('Reload')
378+ hbox.pack_start(self.reload, False, False, 2)
379+
380+ self.futon = Gtk.Button('CouchDB Futon')
381+ hbox.pack_start(self.futon, False, False, 2)
382+
383+ scroll = Gtk.ScrolledWindow()
384+ self.pack_start(scroll, True, True, 0)
385+ scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
386+
387+ self.view = CouchView(env)
388+ scroll.add(self.view)
389+
390+ def on_close(self, button):
391+ self.destroy()
392\ No newline at end of file
393
394=== modified file 'test_userwebkit.py'
395--- test_userwebkit.py 2012-08-13 06:56:11 +0000
396+++ test_userwebkit.py 2012-09-06 20:58:20 +0000
397@@ -37,7 +37,8 @@
398 from gi.repository.GObject import SIGNAL_RUN_LAST, TYPE_NONE, TYPE_PYOBJECT
399
400 import userwebkit
401-
402+import hub
403+import couchview
404
405 orig_log = userwebkit.log
406 random = SystemRandom()
407@@ -144,7 +145,7 @@
408 class TestFunctions(TestCase):
409 def test_iter_gsignals(self):
410 self.assertEqual(
411- dict(userwebkit.iter_gsignals({})),
412+ dict(hub.iter_gsignals({})), # Breaks here, signals are filled by test_hub_factory and it get no reset?
413 {}
414 )
415 signals = {
416@@ -158,21 +159,22 @@
417 'baz': (SIGNAL_RUN_LAST, TYPE_NONE, [TYPE_PYOBJECT, TYPE_PYOBJECT]),
418 }
419 self.assertEqual(
420- dict(userwebkit.iter_gsignals(signals)),
421+ dict(hub.iter_gsignals(signals)),
422 gsignals
423 )
424
425 def test_hub_factory(self):
426- self.assertIs(userwebkit.hub_factory(None), userwebkit.Hub)
427- self.assertIs(userwebkit.hub_factory({}), userwebkit.Hub)
428+ global hub # Avoid UnboundLocalError
429+ self.assertIs(hub.hub_factory(None), hub.Hub)
430+ self.assertIs(hub.hub_factory({}), hub.Hub)
431 signals = {
432 'foo': [],
433 'bar': ['one'],
434 'baz': ['one', 'two'],
435 }
436- klass = userwebkit.hub_factory(signals)
437- self.assertIsNot(klass, userwebkit.Hub)
438- self.assertTrue(issubclass(klass, userwebkit.Hub))
439+ klass = hub.hub_factory(signals)
440+ self.assertIsNot(klass, hub.Hub)
441+ self.assertTrue(issubclass(klass, hub.Hub))
442 self.assertEqual(klass.__name__, 'FactoryHub')
443
444 # Make sure we can connect to all the expected signals:
445@@ -263,9 +265,9 @@
446 self.assertIsNone(view.enable_logging())
447
448 def test_on_console_message(self):
449- view = userwebkit.CouchView()
450+ view = couchview.CouchView()
451 log = DummyLogger()
452- userwebkit.log = log
453+ couchview.log = log
454 message = 'hello ' + random_id()
455 line = 25
456 source_id = 'http://localhost:36163/_intree/index.html'
457
458=== modified file 'userwebkit.py'
459--- userwebkit.py 2012-08-29 00:04:45 +0000
460+++ userwebkit.py 2012-09-06 20:58:20 +0000
461@@ -1,5 +1,9 @@
462+# -*- coding: utf-8; tab-width: 4; mode: python -*-
463+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
464+# vi: set ft=python sts=4 ts=4 sw=4 noet
465+#
466 # userwebkit: so WebKitGtk apps can to talk to a usercouch
467-# Copyright (C) 2011 Novacut Inc
468+# Copyright (C) 2011-2012 Novacut Inc
469 #
470 # This file is part of `userwebkit`.
471 #
472@@ -29,14 +33,11 @@
473 import json
474 import optparse
475 import logging
476+from gi.repository import GObject, Gtk
477
478 import microfiber
479-from microfiber import _oauth_header, _basic_auth_header
480 import dbus
481 from dbus.mainloop.glib import DBusGMainLoop
482-from gi.repository import GObject, Gtk, WebKit
483-from gi.repository.GObject import TYPE_PYOBJECT
484-
485
486 __version__ = '12.09.0'
487 APPS = '/usr/share/couchdb/apps/'
488@@ -44,187 +45,15 @@
489 DBusGMainLoop(set_as_default=True)
490 log = logging.getLogger('userwebkit')
491
492-
493 def handler(d):
494 assert path.abspath(d) == d
495 return '{{couch_httpd_misc_handlers, handle_utils_dir_req, {}}}'.format(
496 json.dumps(d)
497 )
498
499-
500-class CouchView(WebKit.WebView):
501- __gsignals__ = {
502- 'open': (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
503- [TYPE_PYOBJECT]
504- ),
505- }
506-
507- def __init__(self, env=None, dmedia_resolver=None):
508- super().__init__()
509- self._logging_enabled = False
510- self.connect('resource-request-starting', self._on_request)
511- self.connect('navigation-policy-decision-requested',
512- self._on_nav_policy_decision
513- )
514- self.set_env(env)
515- self._dmedia_resolver = dmedia_resolver
516-
517- def set_env(self, env):
518- self._env = env
519- if env is None:
520- self._u = None
521- self._oauth = None
522- self._basic = None
523- return
524- self._u = urlparse(env['url'])
525- self._oauth = env.get('oauth')
526- self._basic = env.get('basic')
527-
528- def set_recv(self, recv):
529- self._recv = recv
530- self.connect('notify::title', self._on_notify_title)
531-
532- def enable_logging(self):
533- if not self._logging_enabled:
534- self._logging_enabled = True
535- self.connect('console-message', self._on_console_message)
536-
537- def _on_console_message(self, view, message, line, source_id):
538- log.debug('%s @%s: %s', source_id, line, message)
539-
540- def _on_request(self, view, frame, resource, request, response):
541- if self._env is None:
542- return
543- uri = request.get_uri()
544- message = request.get_message()
545- if uri.startswith('dmedia'):
546- if self._dmedia_resolver is None:
547- request.set_uri('')
548- else:
549- request.set_uri(self._dmedia_resolver(uri))
550- return
551- u = urlparse(uri)
552- if u.netloc != self._u.netloc:
553- return
554- if u.scheme != self._u.scheme:
555- return
556- if self._oauth:
557- query = dict(parse_qsl(u.query))
558- if u.query and not query:
559- query = {u.query: ''}
560- baseurl = ''.join([u.scheme, '://', u.netloc, u.path])
561- method = message.method
562- h = _oauth_header(self._oauth, method, baseurl, query)
563- elif self._basic:
564- h = _basic_auth_header(self._basic)
565- else:
566- return
567- for (key, value) in h.items():
568- message.request_headers.append(key, value)
569-
570- def _on_nav_policy_decision(self, view, frame, request, nav, policy):
571- """
572- Handle user trying to Navigate away from current page.
573-
574- Note that this will be called before `CouchView._on_resource_request()`.
575-
576- The *policy* arg is a ``WebPolicyDecision`` instance. To handle the
577- decision, call one of:
578-
579- * ``WebPolicyDecision.ignore()``
580- * ``WebPolicyDecision.use()``
581- * ``WebPolicyDecision.download()``
582-
583- And then return ``True``.
584-
585- Otherwise, return ``False`` or ``None`` to have the WebKit default
586- behavior apply.
587- """
588- if self._env is None:
589- return
590- uri = request.get_uri()
591- u = urlparse(uri)
592- if u.netloc == self._u.netloc or u.scheme == 'file':
593- return False
594- if u.scheme in ('http', 'https'):
595- self.emit('open', uri)
596- policy.ignore()
597- return True
598-
599- def _on_notify_title(self, view, notify):
600- title = view.get_property('title')
601- if title is None:
602- return
603- self._recv(title)
604-
605-
606-class Inspector(Gtk.VBox):
607- def __init__(self, env):
608- super().__init__()
609-
610- hbox = Gtk.HBox()
611- self.pack_start(hbox, False, False, 0)
612-
613- close = Gtk.Button(stock=Gtk.STOCK_CLOSE)
614- hbox.pack_start(close, False, False, 2)
615- close.connect('clicked', self.on_close)
616-
617- self.reload = Gtk.Button('Reload')
618- hbox.pack_start(self.reload, False, False, 2)
619-
620- self.futon = Gtk.Button('CouchDB Futon')
621- hbox.pack_start(self.futon, False, False, 2)
622-
623- scroll = Gtk.ScrolledWindow()
624- self.pack_start(scroll, True, True, 0)
625- scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
626-
627- self.view = CouchView(env)
628- scroll.add(self.view)
629-
630- def on_close(self, button):
631- self.destroy()
632-
633-
634-class Hub(GObject.GObject):
635- def __init__(self, view):
636- super().__init__()
637- self._view = view
638- view.set_recv(self.recv)
639-
640- def recv(self, data):
641- try:
642- obj = json.loads(data)
643- self.emit(obj['signal'], *obj['args'])
644- except ValueError:
645- pass
646-
647- def send(self, signal, *args):
648- """
649- Emit a signal by calling the JavaScript Signal.recv() function.
650- """
651- script = 'Hub.recv({!r})'.format(
652- json.dumps({'signal': signal, 'args': args})
653- )
654- self._view.execute_script(script)
655- self.emit(signal, *args)
656-
657-
658-def iter_gsignals(signals):
659- assert isinstance(signals, dict)
660- for (name, argnames) in signals.items():
661- assert isinstance(argnames, list)
662- args = [TYPE_PYOBJECT for argname in argnames]
663- yield (name, (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, args))
664-
665-
666-def hub_factory(signals):
667- if signals:
668- class FactoryHub(Hub):
669- __gsignals__ = dict(iter_gsignals(signals))
670- return FactoryHub
671- return Hub
672-
673+from couchview import CouchView
674+from inspector import Inspector
675+from hub import hub_factory
676
677 class BaseApp(object):
678 name = 'userwebkit' # The namespace of your app, likely source package name
679@@ -235,6 +64,7 @@
680
681 enable_inspector = True # If True, enable WebKit inspector
682 enable_logging = True # If True, send console message to Python logging
683+ view_editable = False # If True, the webkit view is editable
684
685 dmedia_resolver = None # Callback to resolve Dmedia URIs
686
687@@ -352,6 +182,10 @@
688
689 # Add the CouchView
690 self.view = CouchView(None, self.dmedia_resolver)
691+
692+ if self.view_editable:
693+ self.view.enable_view_editable()
694+
695 self.view.connect('open', self.on_open)
696 self.scroll.add(self.view)
697 if self.enable_inspector:
698@@ -360,6 +194,7 @@
699 inspector.connect('inspect-web-view', self.on_inspect)
700 if self.enable_logging:
701 self.view.enable_logging()
702+
703 self.view.show()
704
705 # Create the hub

Subscribers

People subscribed via source and target branches