Merge lp:~mikemc/ubuntuone-control-panel/catch-quit into lp:ubuntuone-control-panel

Proposed by Mike McCracken on 2012-10-02
Status: Merged
Approved by: Mike McCracken on 2012-10-04
Approved revision: 365
Merged at revision: 362
Proposed branch: lp:~mikemc/ubuntuone-control-panel/catch-quit
Merge into: lp:ubuntuone-control-panel
Diff against target: 228 lines (+186/-0)
4 files modified
ubuntuone/controlpanel/gui/qt/main/__init__.py (+12/-0)
ubuntuone/controlpanel/gui/qt/main/darwin.py (+60/-0)
ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py (+104/-0)
ubuntuone/controlpanel/gui/qt/main/tests/test_main.py (+10/-0)
To merge this branch: bzr merge lp:~mikemc/ubuntuone-control-panel/catch-quit
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Approve on 2012-10-04
Roberto Alsina (community) 2012-10-02 Approve on 2012-10-04
Review via email: mp+127605@code.launchpad.net

Commit Message

- On darwin, create menu item to intercept cmd-Q events. (LP: #1049973)

Description of the Change

- On darwin, create menu item to intercept cmd-Q events. (LP: #1049973)

We add a menu to the main menubar with a "Quit" item, which Qt moves into the application menu. If we don't do this, Qt will create a default "Quit" item that only calls QApplication::quit().

To post a comment you must log in.
Roberto Alsina (ralsina) :
review: Approve
Alejandro J. Cura (alecu) wrote :

Looks very good.

review: Approve
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (162.9 KiB)

The attempt to merge lp:~mikemc/ubuntuone-control-panel/catch-quit into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.

*** Running DBus test suite ***
ubuntuone.controlpanel.dbustests.test_dbus_service
  BaseTestCase
    runTest ... [OK]
  DBusServiceMainTestCase
    test_dbus_service_cant_register ... Control panel backend already running.
                                   [OK]
    test_dbus_service_main ... [OK]
  DBusServiceTestCase
    test_cant_register_twice ... [SKIPPED]
    test_dbus_busname_created ... [OK]
    test_error_handler_default ... [OK]
    test_error_handler_with_exception ... [OK]
    test_error_handler_with_failure ... [OK]
    test_error_handler_with_non_string_dict ... [OK]
    test_error_handler_with_string_dict ... [OK]
    test_register_service ... [OK]
  FileSyncTestCase
    test_file_sync_status_changed ... [OK]
    test_file_sync_status_disabled ... [OK]
    test_file_sync_status_disconnected ... [OK]
    test_file_sync_status_error ... [OK]
    test_file_sync_status_idle ... [OK]
    test_file_sync_status_starting ... [OK]
    test_file_sync_status_stopped ... [OK]
    test_file_sync_status_syncing ... [OK]
    test_file_sync_status_unknown ... [OK]
    test_status_changed_handler ... [OK]
    test_status_changed_handler_after_status_requested ... [OK]
    test_status_changed_handler_after_status_requested_twice ... [OK]
  OperationsAuthErrorTestCase
    test_account_info_returned ... [OK]
    test_change_device_settings ... [OK]
    test_change_replication_settings ... [OK]
    test_change_volume_settings ... [OK]
    test_connect_files ... [OK]
    test_devices_info_returned ... [OK]
    test_disable_files ... [OK]
    test_disconnect_files ... [OK]
    test_enable_files ... [OK]
    test_remove_device ... [OK]
    test_replications_info ... [OK]
    test_restart_files ... [OK]
    test_start_...

Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (162.5 KiB)

The attempt to merge lp:~mikemc/ubuntuone-control-panel/catch-quit into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.

*** Running DBus test suite ***
ubuntuone.controlpanel.dbustests.test_dbus_service
  BaseTestCase
    runTest ... [OK]
  DBusServiceMainTestCase
    test_dbus_service_cant_register ... Control panel backend already running.
                                   [OK]
    test_dbus_service_main ... [OK]
  DBusServiceTestCase
    test_cant_register_twice ... [SKIPPED]
    test_dbus_busname_created ... [OK]
    test_error_handler_default ... [OK]
    test_error_handler_with_exception ... [OK]
    test_error_handler_with_failure ... [OK]
    test_error_handler_with_non_string_dict ... [OK]
    test_error_handler_with_string_dict ... [OK]
    test_register_service ... [OK]
  FileSyncTestCase
    test_file_sync_status_changed ... [OK]
    test_file_sync_status_disabled ... [OK]
    test_file_sync_status_disconnected ... [OK]
    test_file_sync_status_error ... [OK]
    test_file_sync_status_idle ... [OK]
    test_file_sync_status_starting ... [OK]
    test_file_sync_status_stopped ... [OK]
    test_file_sync_status_syncing ... [OK]
    test_file_sync_status_unknown ... [OK]
    test_status_changed_handler ... [OK]
    test_status_changed_handler_after_status_requested ... [OK]
    test_status_changed_handler_after_status_requested_twice ... [OK]
  OperationsAuthErrorTestCase
    test_account_info_returned ... [OK]
    test_change_device_settings ... [OK]
    test_change_replication_settings ... [OK]
    test_change_volume_settings ... [OK]
    test_connect_files ... [OK]
    test_devices_info_returned ... [OK]
    test_disable_files ... [OK]
    test_disconnect_files ... [OK]
    test_enable_files ... [OK]
    test_remove_device ... [OK]
    test_replications_info ... [OK]
    test_restart_files ... [OK]
    test_start_...

365. By Mike McCracken on 2012-10-04

pep8 whitespace fix

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntuone/controlpanel/gui/qt/main/__init__.py'
2--- ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-08-30 18:34:13 +0000
3+++ ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-10-04 20:49:21 +0000
4@@ -36,6 +36,12 @@
5 else:
6 from ubuntuone.controlpanel.gui.qt.main import dbus_main
7 source = dbus_main
8+
9+if sys.platform == 'darwin':
10+ from ubuntuone.controlpanel.gui.qt.main.darwin import (
11+ install_platform_event_handlers)
12+else:
13+ install_platform_event_handlers = lambda app: None
14 # pylint: enable=C0103
15
16 from ubuntuone.controlpanel.utils import install_config_and_daemons
17@@ -127,6 +133,12 @@
18
19 install_config_and_daemons()
20
21+ # Unused variable 'menubar', pylint: disable=W0612
22+ # need to keep a reference to the menu or our handler will be
23+ # removed
24+ menubar = install_platform_event_handlers(app)
25+ # pylint: enable=W0612
26+
27 # Unused variable 'window', 'icon', pylint: disable=W0612
28 icon, window = start(lambda: source.main_quit(app),
29 minimized=minimized, with_icon=with_icon,
30
31=== added file 'ubuntuone/controlpanel/gui/qt/main/darwin.py'
32--- ubuntuone/controlpanel/gui/qt/main/darwin.py 1970-01-01 00:00:00 +0000
33+++ ubuntuone/controlpanel/gui/qt/main/darwin.py 2012-10-04 20:49:21 +0000
34@@ -0,0 +1,60 @@
35+# -*- coding: utf-8 -*-
36+#
37+# Copyright 2012 Canonical Ltd.
38+#
39+# This program is free software: you can redistribute it and/or modify it
40+# under the terms of the GNU General Public License version 3, as published
41+# by the Free Software Foundation.
42+#
43+# This program is distributed in the hope that it will be useful, but
44+# WITHOUT ANY WARRANTY; without even the implied warranties of
45+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
46+# PURPOSE. See the GNU General Public License for more details.
47+#
48+# You should have received a copy of the GNU General Public License along
49+# with this program. If not, see <http://www.gnu.org/licenses/>.
50+
51+"""Darwin-specific GUI event handling code."""
52+
53+from PyQt4 import QtGui
54+from twisted.internet.defer import inlineCallbacks
55+
56+from ubuntuone.platform.tools import SyncDaemonTool
57+
58+from ubuntuone.controlpanel.gui.qt.main import twisted_main
59+
60+
61+@inlineCallbacks
62+def handle_cmd_q(app, event):
63+ """Handle the quit event on darwin."""
64+ # pylint: disable=W0702
65+ try:
66+ st = SyncDaemonTool()
67+ yield st.quit()
68+ except:
69+ pass
70+ twisted_main.main_quit(app)
71+ app.aboutToQuit.emit()
72+
73+
74+def install_platform_event_handlers(app):
75+ """Add code to catch cmd-Q."""
76+ # on darwin, a menu item with 'quit' in its title is moved to
77+ # the application menu and given the cmd-Q shortcut by Qt. If
78+ # we don't provide one, Qt will give us one that just calls
79+ # QApplication::quit(), which doesn't quit syncdaemon and
80+ # causes problems with the qtreactor.
81+
82+ # QMenuBar with no parent gives us an app-wide menu bar.
83+ # Note that Qt does not make this a singleton, though, so we have
84+ # to keep it around.
85+ menubar = QtGui.QMenuBar(None)
86+
87+ quit_action = QtGui.QAction("quit", menubar,
88+ triggered=lambda x:
89+ handle_cmd_q(app, x))
90+ quit_action.setObjectName("darwin-cmd-q")
91+ menu = menubar.addMenu("This string is not used.")
92+ menu.addAction(quit_action)
93+
94+ return menubar
95
96=== added file 'ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py'
97--- ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py 1970-01-01 00:00:00 +0000
98+++ ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py 2012-10-04 20:49:21 +0000
99@@ -0,0 +1,104 @@
100+# -*- coding: utf-8 -*-
101+#
102+# Copyright 2012 Canonical Ltd.
103+#
104+# This program is free software: you can redistribute it and/or modify it
105+# under the terms of the GNU General Public License version 3, as published
106+# by the Free Software Foundation.
107+#
108+# This program is distributed in the hope that it will be useful, but
109+# WITHOUT ANY WARRANTY; without even the implied warranties of
110+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
111+# PURPOSE. See the GNU General Public License for more details.
112+#
113+# You should have received a copy of the GNU General Public License along
114+# with this program. If not, see <http://www.gnu.org/licenses/>.
115+
116+"""Tests for darwin-specific parts of control panel's Qt main."""
117+
118+from PyQt4 import QtGui
119+
120+from twisted.internet.defer import (inlineCallbacks, succeed)
121+
122+from ubuntuone.controlpanel.gui.qt import main
123+from ubuntuone.controlpanel.tests import TestCase
124+from ubuntuone.controlpanel.gui.tests import FakeSignal
125+
126+
127+class FakeApplication(object):
128+ """Fake Application with quit signal."""
129+ def __init__(self):
130+ """Set up fake signal."""
131+ # pylint: disable=C0103
132+ self.aboutToQuit = FakeSignal()
133+ self.aboutToQuit.connect(self.record_call)
134+ self.signal_called = None
135+
136+ def record_call(self, *args):
137+ """record call"""
138+ self.signal_called = args
139+
140+
141+class FakeSDTool(object):
142+ """Fake tool."""
143+ called = False
144+
145+ def quit(self):
146+ """Quit successfully."""
147+ FakeSDTool.called = True
148+ return succeed(None)
149+
150+
151+class FakeSDToolRaising(object):
152+ """Fake tool that raises."""
153+ shouldRaise = False
154+ called = False
155+
156+ def quit(self):
157+ """Raise."""
158+ self.called = True
159+ raise Exception("exception")
160+
161+
162+class QuitTestCase(TestCase):
163+ """Test quit event handling."""
164+
165+ @inlineCallbacks
166+ def setUp(self):
167+ """Set up common fakes."""
168+ yield super(QuitTestCase, self).setUp()
169+ self.fake_app = FakeApplication()
170+ self.patch(main.darwin.twisted_main, 'main_quit',
171+ self._set_called)
172+
173+ @inlineCallbacks
174+ def test_cmd_q_func_quits_sd(self):
175+ """Test that we call syncdaemontool.quit """
176+ self.patch(main.darwin, 'SyncDaemonTool', FakeSDTool)
177+
178+ yield main.darwin.handle_cmd_q(self.fake_app, None)
179+ self.assertEqual(FakeSDTool.called, True)
180+
181+ @inlineCallbacks
182+ def test_cmd_q_func_ignores_exception(self):
183+ """Test that we keep going when SDT raises."""
184+ self.patch(main.darwin, 'SyncDaemonTool', FakeSDToolRaising)
185+
186+ yield main.darwin.handle_cmd_q(self.fake_app, None)
187+ self.assertEqual(self._called, ((self.fake_app,), {}))
188+
189+ self.assertEqual(self.fake_app.signal_called, ())
190+
191+
192+class InstallEventHandlersTestCase(TestCase):
193+ """Test creating Qt menu items."""
194+
195+ def test_install_handlers_creates_action(self):
196+ """Test menu items created"""
197+ self.patch(main.darwin, 'handle_cmd_q',
198+ self._set_called)
199+ app = QtGui.QApplication.instance()
200+ menubar = main.darwin.install_platform_event_handlers(app)
201+ quit_action = menubar.findChild(QtGui.QAction, "darwin-cmd-q")
202+ quit_action.trigger()
203+ self.assertEqual(self._called, ((app, False), {}))
204
205=== modified file 'ubuntuone/controlpanel/gui/qt/main/tests/test_main.py'
206--- ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-08-30 18:34:13 +0000
207+++ ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-10-04 20:49:21 +0000
208@@ -136,6 +136,8 @@
209 self.patch(main, "start", self.start)
210 self.patch(main.source, "main_start", lambda app: None)
211 self.patch(QtCore, "QTranslator", lambda: self.translator)
212+ self.patch(main, 'install_platform_event_handlers',
213+ lambda app: None)
214
215 self.qt4reactor_installed = False
216
217@@ -305,3 +307,11 @@
218
219 main.main([sys.argv[0]], install_reactor_darwin=False)
220 self.assertEqual(self._called, ((), {}))
221+
222+ def test_install_event_handlers(self):
223+ """Test that install_platform_event_handlers is called."""
224+ self.patch(main, 'install_platform_event_handlers',
225+ self._set_called)
226+
227+ main.main([sys.argv[0]], install_reactor_darwin=False)
228+ self.assertEqual(self._called, ((self.app,), {}))

Subscribers

People subscribed via source and target branches