Merge lp:~mikemc/ubuntuone-control-panel/fix-native-dialog into lp:ubuntuone-control-panel

Proposed by Mike McCracken
Status: Merged
Approved by: Mike McCracken
Approved revision: 396
Merged at revision: 392
Proposed branch: lp:~mikemc/ubuntuone-control-panel/fix-native-dialog
Merge into: lp:ubuntuone-control-panel
Diff against target: 282 lines (+116/-48)
6 files modified
ubuntuone/controlpanel/gui/qt/addfolder.py (+12/-4)
ubuntuone/controlpanel/gui/qt/main/__init__.py (+2/-1)
ubuntuone/controlpanel/gui/qt/main/darwin.py (+21/-0)
ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py (+40/-0)
ubuntuone/controlpanel/gui/qt/tests/__init__.py (+0/-40)
ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py (+41/-3)
To merge this branch: bzr merge lp:~mikemc/ubuntuone-control-panel/fix-native-dialog
Reviewer Review Type Date Requested Status
dobey (community) Approve
Michał Karnicki (community) Approve
Review via email: mp+141104@code.launchpad.net

Commit message

- Use applescript on OS X to show native file-chooser dialog. (LP: #1044201)

Description of the change

- Use applescript on OS X to show native file-chooser dialog. (LP: #1044201)

Adds a darwin-specific class to replace QFileDialog.getExistingDirectory which calls 'osascript' via subprocess, to pass an applescript on the command line. The script simply shows a file picker using the System Events pseudo-application, and prints the result (or returns an error code if the user cancels).

Tests pass on darwin and linux.

To test IRL, start control panel and press the "add a folder" button. It should be non-ugly.

To post a comment you must log in.
Revision history for this message
Michał Karnicki (karni) wrote :

diff line 201 - intentional comment?
Looks good to me, can't test (no mac).

review: Approve
395. By Mike McCracken

re-add test cleanup that crashes test suite on darwin. Commenting it out is a hack that shouldn't end up in trunk, probably.

Revision history for this message
dobey (dobey) wrote :

259 + # Invalid name "getExistingDirectory"
260 + # pylint: disable=C0103

Can you drop the pylint comments you add here, as we're not using it any more?

review: Needs Fixing
396. By Mike McCracken

Strip pylint comments from moved FakedFileDialog

Revision history for this message
dobey (dobey) wrote :

Looks ok here.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntuone/controlpanel/gui/qt/addfolder.py'
2--- ubuntuone/controlpanel/gui/qt/addfolder.py 2012-08-24 20:38:16 +0000
3+++ ubuntuone/controlpanel/gui/qt/addfolder.py 2013-01-11 17:15:25 +0000
4@@ -39,11 +39,18 @@
5 # time only, pinky-swear, cross my heart and hope to die if
6 # I lie.
7 FILE_CHOOSER_OPTIONS = QtGui.QFileDialog.ShowDirsOnly
8-if sys.platform in ('win32', 'darwin'):
9+if sys.platform in ('win32'):
10 FILE_CHOOSER_OPTIONS |= QtGui.QFileDialog.DontUseNativeDialog
11 # Yes, that is true. If you do that again, you will be hunted
12 # down and taught a lesson. You will be sorry.
13
14+if sys.platform == 'darwin':
15+ from ubuntuone.controlpanel.gui.qt.main.darwin import (
16+ DarwinFileDialog)
17+ FileDialog = DarwinFileDialog
18+else:
19+ FileDialog = QtGui.QFileDialog
20+
21
22 class AddFolderButton(cache.Cache, QtGui.QPushButton):
23 """The AddFolderButton widget"""
24@@ -68,9 +75,10 @@
25 """The 'Sync another folder' button was clicked."""
26 # The options argument is because of LP: #835013
27 home_dir = yield self.backend.get_home_dir()
28- folder = QtGui.QFileDialog.getExistingDirectory(
29- parent=self, directory=home_dir,
30- options=FILE_CHOOSER_OPTIONS)
31+ folder = FileDialog.getExistingDirectory(parent=self,
32+ directory=home_dir,
33+ options=FILE_CHOOSER_OPTIONS)
34+ self.window().raise_()
35 folder = unicode(QtCore.QDir.toNativeSeparators(folder))
36 logger.info('on_add_folder_button_clicked: user requested folder '
37 'creation for path %r.', folder)
38
39=== modified file 'ubuntuone/controlpanel/gui/qt/main/__init__.py'
40--- ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-12-08 00:13:34 +0000
41+++ ubuntuone/controlpanel/gui/qt/main/__init__.py 2013-01-11 17:15:25 +0000
42@@ -40,7 +40,8 @@
43
44 if sys.platform == 'darwin':
45 from ubuntuone.controlpanel.gui.qt.main.darwin import (
46- install_platform_event_handlers, MenubarIconLauncher)
47+ install_platform_event_handlers,
48+ MenubarIconLauncher)
49 assert(install_platform_event_handlers)
50 assert(MenubarIconLauncher)
51 else:
52
53=== modified file 'ubuntuone/controlpanel/gui/qt/main/darwin.py'
54--- ubuntuone/controlpanel/gui/qt/main/darwin.py 2012-11-08 17:44:01 +0000
55+++ ubuntuone/controlpanel/gui/qt/main/darwin.py 2013-01-11 17:15:25 +0000
56@@ -35,6 +35,27 @@
57 logger = setup_logging('qt.main.darwin')
58
59
60+class DarwinFileDialog(object):
61+ FOLDER_CHOOSER_SCRIPT = """
62+ tell application "System Events"
63+ activate
64+ set home to POSIX file "%s" as alias
65+ POSIX path of( choose folder default location home )
66+ end tell
67+ """
68+
69+ @classmethod
70+ def getExistingDirectory(cls, directory=None, **kwargs):
71+ """Use command-line applescript to avoid QTBUG-28146."""
72+ try:
73+ s = cls.FOLDER_CHOOSER_SCRIPT % directory
74+ folder = subprocess.check_output(['osascript', '-e', s]).strip()
75+ except subprocess.CalledProcessError as e:
76+ logger.error("Could not call script to show open panel: %r" % e)
77+ folder = ''
78+ return folder
79+
80+
81 @inlineCallbacks
82 def handle_cmd_q(app, event, should_quit_sd=False):
83 """Handle the quit event on darwin."""
84
85=== modified file 'ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py'
86--- ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py 2012-11-08 17:44:01 +0000
87+++ ubuntuone/controlpanel/gui/qt/main/tests/test_darwin.py 2013-01-11 17:15:25 +0000
88@@ -86,6 +86,46 @@
89 self.handlers = []
90
91
92+class ExternalGetDirectoryTestCase(TestCase):
93+ """Test calling applescript to show file picker."""
94+
95+ @inlineCallbacks
96+ def setUp(self):
97+ """Simplify applescript for test."""
98+ yield super(ExternalGetDirectoryTestCase, self).setUp()
99+ self.patch(main.darwin.DarwinFileDialog,
100+ "FOLDER_CHOOSER_SCRIPT",
101+ "testscript: %s")
102+ self.patch(main.darwin.subprocess, 'check_output',
103+ self._fake_check_output)
104+
105+ self.args = []
106+ self.fake_raises = False
107+
108+ def _fake_check_output(self, args):
109+ """mock for subprocess.check_output"""
110+ self.args += args
111+ if self.fake_raises:
112+ raise subprocess.CalledProcessError(-1, 'fakecmd')
113+ return "testpath"
114+
115+ def test_path_ok(self):
116+ """Test returning the path."""
117+
118+ rv = main.darwin.DarwinFileDialog.getExistingDirectory(
119+ directory="HOME")
120+ self.assertEqual(rv, "testpath")
121+ self.assertEqual(self.args, ["osascript", "-e", "testscript: HOME"])
122+
123+ def test_path_raises(self):
124+ """Test returning the path."""
125+ self.fake_raises = True
126+ rv = main.darwin.DarwinFileDialog.getExistingDirectory(
127+ directory="HOME")
128+ self.assertEqual(rv, "")
129+ self.assertEqual(self.args, ["osascript", "-e", "testscript: HOME"])
130+
131+
132 class QuitTestCase(TestCase):
133 """Test quit event handling."""
134
135
136=== modified file 'ubuntuone/controlpanel/gui/qt/tests/__init__.py'
137--- ubuntuone/controlpanel/gui/qt/tests/__init__.py 2012-10-23 14:24:16 +0000
138+++ ubuntuone/controlpanel/gui/qt/tests/__init__.py 2013-01-11 17:15:25 +0000
139@@ -17,7 +17,6 @@
140 """The test suite for the Qt UI for the control panel for Ubuntu One."""
141
142 import logging
143-import os
144
145 from PyQt4 import QtGui, QtCore
146 from twisted.internet import defer
147@@ -85,17 +84,6 @@
148 NO_OP = lambda *args: None
149
150
151-def set_path_on_file_dialog(folder_name=None, file_dialog=None):
152- """Set a valid path in the file dialog to create a folder."""
153- if folder_name is None:
154- folder_name = 'Documents'
155- folder = os.path.join(USER_HOME, folder_name)
156- if file_dialog is None:
157- file_dialog = FakedFileDialog
158- file_dialog.response = QtCore.QString(folder)
159- return folder
160-
161-
162 class FakeUi(FakedObject):
163 """A fake Ui object."""
164
165@@ -264,30 +252,6 @@
166 FakedDialog.properties['shown'] += 1
167
168
169-class FakedFileDialog(object):
170- """Fake a file chooser dialog."""
171-
172- response = args = kwargs = None
173- DontUseNativeDialog = object()
174-
175- @classmethod
176- def reset(cls, *a, **kw):
177- """Clear all values."""
178- cls.response = cls.args = cls.kwargs = None
179-
180- # Invalid name "getExistingDirectory"
181- # pylint: disable=C0103
182-
183- @classmethod
184- def getExistingDirectory(cls, *a, **kw):
185- """Simulate a directory chooser."""
186- cls.args = a
187- cls.kwargs = kw
188- return cls.response
189-
190- # pylint: enable=C0103
191-
192-
193 class BaseTestCase(TestCase):
194 """Base Test Case."""
195
196@@ -326,10 +290,6 @@
197 logger.addHandler(self.memento)
198 self.addCleanup(logger.removeHandler, self.memento)
199
200- # default response if user does nothing
201- FakedFileDialog.reset()
202- self.patch(QtGui, 'QFileDialog', FakedFileDialog)
203-
204 FakedDialog.reset()
205 self.patch(QtGui, 'QMessageBox', FakedDialog)
206
207
208=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py'
209--- ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py 2012-03-19 20:55:52 +0000
210+++ ubuntuone/controlpanel/gui/qt/tests/test_addfolder.py 2013-01-11 17:15:25 +0000
211@@ -18,6 +18,10 @@
212
213 """Tests for the AddFolderButton widget."""
214
215+import os
216+
217+from PyQt4 import QtCore
218+
219 from twisted.internet import defer
220
221 from ubuntuone.controlpanel.gui.tests import (
222@@ -28,9 +32,7 @@
223 BaseTestCase,
224 CrashyBackend,
225 CrashyBackendException,
226- FakedDialog,
227- FakedFileDialog,
228- set_path_on_file_dialog,
229+ FakedDialog
230 )
231
232 # Access to a protected member
233@@ -38,6 +40,36 @@
234 # pylint: disable=W0212, E1103
235
236
237+def set_path_on_file_dialog(folder_name=None, file_dialog=None):
238+ """Set a valid path in the file dialog to create a folder."""
239+ if folder_name is None:
240+ folder_name = 'Documents'
241+ folder = os.path.join(USER_HOME, folder_name)
242+ if file_dialog is None:
243+ file_dialog = FakedFileDialog
244+ file_dialog.response = QtCore.QString(folder)
245+ return folder
246+
247+
248+class FakedFileDialog(object):
249+ """Fake a file chooser dialog."""
250+
251+ response = args = kwargs = None
252+ DontUseNativeDialog = object()
253+
254+ @classmethod
255+ def reset(cls, *a, **kw):
256+ """Clear all values."""
257+ cls.response = cls.args = cls.kwargs = None
258+
259+ @classmethod
260+ def getExistingDirectory(cls, *a, **kw):
261+ """Simulate a directory chooser."""
262+ cls.args = a
263+ cls.kwargs = kw
264+ return cls.response
265+
266+
267 class AddFolderButtonTestCase(BaseTestCase):
268 """The test suite for the AddFolderButton."""
269
270@@ -47,6 +79,12 @@
271 def setUp(self):
272 yield super(AddFolderButtonTestCase, self).setUp()
273 self.created_folders = []
274+
275+ # default response if user does nothing
276+ FakedFileDialog.reset()
277+
278+ self.patch(gui, 'FileDialog', FakedFileDialog)
279+
280 self.patch(FakedFileDialog, 'response', gui.QtCore.QString(''))
281 self.patch(self.ui.backend, 'validate_path_for_folder', lambda p: True)
282 self.patch(self.ui, 'add_folder_func', self.add_folder)

Subscribers

People subscribed via source and target branches