Merge lp:~pitti/software-properties/pygi into lp:software-properties

Proposed by Martin Pitt
Status: Merged
Merged at revision: 643
Proposed branch: lp:~pitti/software-properties/pygi
Merge into: lp:software-properties
Diff against target: 952 lines (+205/-173)
15 files modified
debian/changelog (+28/-0)
debian/control (+1/-1)
software-properties-gtk (+6/-11)
softwareproperties/MirrorTest.py (+18/-5)
softwareproperties/gtk/CdromProgress.py (+16/-16)
softwareproperties/gtk/DialogAdd.py (+2/-2)
softwareproperties/gtk/DialogAddSourcesList.py (+7/-6)
softwareproperties/gtk/DialogCacheOutdated.py (+9/-5)
softwareproperties/gtk/DialogEdit.py (+2/-2)
softwareproperties/gtk/DialogMirror.py (+36/-55)
softwareproperties/gtk/SimpleGtkbuilderApp.py (+7/-7)
softwareproperties/gtk/SoftwarePropertiesGtk.py (+65/-55)
softwareproperties/gtk/dialogs.py (+3/-3)
softwareproperties/gtk/utils.py (+4/-4)
softwareproperties/kde/DialogMirror.py (+1/-1)
To merge this branch: bzr merge lp:~pitti/software-properties/pygi
Reviewer Review Type Date Requested Status
Martin Pitt Needs Resubmitting
Michael Vogt Pending
Review via email: mp+47463@code.launchpad.net

Description of the change

This ports the GTK UI from pygtk2 to gobject-introspection. It's mostly working now, except for the Drag & Drop parts; they work fine when using GTK3, but since in GTK 2 GtkTargetEntry is not a class and thus not constructable.

To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

Note that with our current GTK package I get crashes when clicking wildly in the mirror selection tree view. I just debugged that and fixed it in upstream GTK:

  http://git.gnome.org/browse/gtk+/commit/?h=gtk-2-24&id=461d71f6aa7f93a7a57a1d19053cd79597a09070

So we'll get this fix into Natty.

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks, code looks good. However when I run it on my box I get:
sudo ./software-properties-gtk

** (software-properties-gtk:17374): WARNING **: Out argument 0 in get_cursor returns a struct with a transfer mode of "full". Transfer mode should be set to "none" for struct type returns as there is no way to free them safely. Ignoring transfer mode to prevent a potential invalid free. This may cause a leak in your application.

I guess that is ok for now?

Revision history for this message
Michael Vogt (mvo) wrote :

The other thing that does not work is the ping test on:
"Download from: Other…", "Select best server"
Its supposed to open a window with a lot of pings. That does not work currently, it hangs instead.

Revision history for this message
Martin Pitt (pitti) wrote :

Right, the get_cursor() warning is expected; that eventually needs to be fixed in the GTK overrides or pygobject. It doesn't hurt, though.

I'll look at the ping window, thanks for the review!

Revision history for this message
Martin Pitt (pitti) wrote :

So the problem is that this uses GTK from two different threads: the main one, and the MirrorTestGtk class, which is instantiated and run in a parallel one. Apparently pygtk had some magic to have that work, but with plain GTK it has never worked well. In particular, the worker thread doesn't seem to have a main loop, so there are no updates.

It would be a lot better if the GTK bits could be in the main thread only, and the worker thread would just do the pings and update callbacks, and the main thread would receive the callbacks and continue to run the main loop. Michael, would you mind if I reorganized the code accordingly?

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks for looking into this Martin! I am absolutely in favour for this reorganizing.

lp:~pitti/software-properties/pygi updated
649. By Martin Pitt

software-properties-gtk, softwareproperties/gtk/DialogMirror.py: Drop
calls to Gdk.threads_*(). They were insufficient and causing lockups, and
since multi-threaded Gtk is a pain to get right, we'll rather change the
code structure to only use Gtk from the main thread.

650. By Martin Pitt

softwareproperties/gtk/DialogMirror.py: Run GTK operations in main thread,
to avoid thread lockups.

651. By Martin Pitt

softwareproperties/MirrorTest.py: Move additional MirrorTestGtk
functionality (setting threading.Event flag and storing current
action/progress into main MirrorTest class, so that we can entirely drop
MirrorTestGtk from softwareproperties/gtk/DialogMirror.py. KDE still uses
its own implementation with real threads, as this is still easier to do.

Revision history for this message
Martin Pitt (pitti) wrote :

r650 moved all GTK operations to the main thread, to avoid the thread lockups. r649 removed all the Gdk.thread_* stuff, as this is now obsolete.

r651 is not a functional change, but simplifies the code a bit.

This now works fine, so I resubmit this. I verified that the kde frontend still works well, too.

review: Needs Resubmitting

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2010-12-13 08:42:40 +0000
3+++ debian/changelog 2011-02-11 12:13:56 +0000
4@@ -1,3 +1,31 @@
5+software-properties (0.79) UNRELEASED; urgency=low
6+
7+ * software-properties-gtk, ./softwareproperties/gtk/*: Port from pygtk to
8+ gobject-introspection. This works with both GTK 2 and 3, but in GTK2 we
9+ have to disable the drag&drop functionality (as current GTK2 does not have
10+ a gtk_target_entry_new() and thus you can't construct those).
11+ * softwareproperties/gtk/SoftwarePropertiesGtk.py: When doing string
12+ interpolation with translated text and unicode objects, encode the latter
13+ into UTF-8 first. pygtk did that for us using some black magic (by calling
14+ PyUnicode_SetDefaultEncoding()).
15+ * software-properties-gtk: Force using GTK 2 for now, as in Natty we don't
16+ currently ship GTK 3 on the default installation (we don't have a theme
17+ yet).
18+ * debian/control: Update dependencies for the pygtk → pygi switch.
19+ * software-properties-gtk, softwareproperties/gtk/DialogMirror.py: Drop
20+ calls to Gdk.threads_*(). They were insufficient and causing lockups, and
21+ since multi-threaded Gtk is a pain to get right, we'll rather change the
22+ code structure to only use Gtk from the main thread.
23+ * softwareproperties/gtk/DialogMirror.py: Run GTK operations in main thread,
24+ to avoid thread lockups.
25+ * softwareproperties/MirrorTest.py: Move additional MirrorTestGtk
26+ functionality (setting threading.Event flag and storing current
27+ action/progress into main MirrorTest class, so that we can entirely drop
28+ MirrorTestGtk from softwareproperties/gtk/DialogMirror.py. KDE still uses
29+ its own implementation with real threads, as this is still easier to do.
30+
31+ -- Martin Pitt <martin.pitt@ubuntu.com> Tue, 25 Jan 2011 18:54:54 +0100
32+
33 software-properties (0.78.1) natty; urgency=low
34
35 * use port 80 by default when getting gpg keys for PPAs
36
37=== modified file 'debian/control'
38--- debian/control 2010-08-25 18:06:17 +0000
39+++ debian/control 2011-02-11 12:13:56 +0000
40@@ -22,7 +22,7 @@
41 Replaces: update-manager (<< 0.55)
42 Conflicts: update-manager (<< 0.55)
43 XB-Python-Version: ${python:Versions}
44-Depends: ${python:Depends}, ${misc:Depends}, python, synaptic (>= 0.57.8), gksu, python-software-properties, python-gtk2
45+Depends: ${python:Depends}, ${misc:Depends}, python, synaptic (>= 0.57.8), gksu, python-software-properties, python-gobject (>= 2.27), gir1.2-gtk-2.0 (>= 2.23.90-0ubuntu3)
46 Description: manage the repositories that you install software from
47 This software provides an abstraction of the used apt repositories.
48 It allows you to easily manage your distribution and independent software
49
50=== modified file 'software-properties-gtk'
51--- software-properties-gtk 2010-08-25 08:51:00 +0000
52+++ software-properties-gtk 2011-02-11 12:13:56 +0000
53@@ -22,16 +22,13 @@
54 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
55 # USA
56
57-import pygtk
58-pygtk.require('2.0')
59-import gtk
60-import gtk.gdk
61+from gi.repository import Gtk
62+Gtk.require_version('2.0')
63 import gobject
64 import gettext
65 import os
66 import sys
67-
68-gtk.gdk.threads_init()
69+import locale
70
71 from optparse import OptionParser
72
73@@ -75,21 +72,19 @@
74 parser.add_option("--data-dir", "",
75 action="store", type="string", default="/usr/share/software-properties/",
76 help="Use data files (UI) from the given directory")
77- gtk.init_check()
78
79 (options, args) = parser.parse_args()
80 # Check for root permissions
81 if os.geteuid() != 0:
82- dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
83+ dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
84 _("You need to be root to run this program") )
85- dialog.set_default_response(gtk.RESPONSE_CLOSE)
86+ dialog.set_default_response(Gtk.ResponseType.CLOSE)
87 dialog.run()
88 dialog.destroy()
89 sys.exit(1)
90
91+ locale.setlocale(locale.LC_ALL, '')
92 localesApp="software-properties"
93- localesDir="/usr/share/locale"
94- gettext.bindtextdomain(localesApp, localesDir)
95 gettext.textdomain(localesApp)
96
97 # force new files to be 644 (LP: #497778)
98
99=== modified file 'softwareproperties/MirrorTest.py'
100--- softwareproperties/MirrorTest.py 2009-11-14 14:13:30 +0000
101+++ softwareproperties/MirrorTest.py 2011-02-11 12:13:56 +0000
102@@ -52,8 +52,12 @@
103 self.results.append([float(result[0]), host, mirror])
104 MirrorTest.completed_lock.release()
105
106- def __init__(self, mirrors, test_file, running=None):
107+ def __init__(self, mirrors, test_file, event, running=None):
108 threading.Thread.__init__(self)
109+ self.action = ''
110+ self.progress = (0, 0, 0.0) # cur, max, %
111+ self.event = event
112+ self.best = None
113 self.test_file = test_file
114 self.threads = []
115 MirrorTest.completed = 0
116@@ -66,14 +70,18 @@
117 self.running = running
118
119 def report_action(self, text):
120- """Subclasses should override this method to receive
121- action message updates"""
122- print text
123+ self.action = text
124+ if self.event:
125+ self.event.set()
126
127 def report_progress(self, current, max, borders=(0,100), mod=(0,0)):
128 """Subclasses should override this method to receive
129 progress status updates"""
130- print "Completed %s of %s" % (current + mod[0], max + mod[1])
131+ self.progress = (current + mod[0],
132+ max + mod[1],
133+ borders[0] + (borders[1] - borders[0]) / max * current)
134+ if self.event:
135+ self.event.set()
136
137 def run_full_test(self):
138 # Determinate the 5 top ping servers
139@@ -149,6 +157,11 @@
140 results.sort()
141 return results[0:max]
142
143+ def run(self):
144+ """Complete test exercise, set self.best when done"""
145+ self.best = self.run_full_test()
146+ self.running.clear()
147+
148 if __name__ == "__main__":
149 distro = aptsources.distro.get_distro()
150 distro.get_sources(aptsources.SourcesList())
151
152=== modified file 'softwareproperties/gtk/CdromProgress.py'
153--- softwareproperties/gtk/CdromProgress.py 2010-09-20 12:58:51 +0000
154+++ softwareproperties/gtk/CdromProgress.py 2011-02-11 12:13:56 +0000
155@@ -20,7 +20,7 @@
156 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
157 # USA
158 import apt
159-import gtk
160+from gi.repository import Gtk
161 from gettext import gettext as _
162
163 from softwareproperties.gtk.utils import *
164@@ -43,34 +43,34 @@
165 self.button_cdrom_close.set_sensitive(True)
166 if text != "":
167 self.label_cdrom.set_text(text)
168- while gtk.events_pending():
169- gtk.main_iteration()
170+ while Gtk.events_pending():
171+ Gtk.main_iteration()
172 def askCdromName(self):
173- dialog = gtk.MessageDialog(parent=self.dialog_cdrom_progress,
174- flags=gtk.DIALOG_MODAL,
175- type=gtk.MESSAGE_QUESTION,
176- buttons=gtk.BUTTONS_OK_CANCEL,
177+ dialog = Gtk.MessageDialog(parent=self.dialog_cdrom_progress,
178+ flags=Gtk.DialogFlags.MODAL,
179+ type=Gtk.MessageType.QUESTION,
180+ buttons=Gtk.ButtonsType.OK_CANCEL,
181 message_format=None)
182 dialog.set_markup(_("Please enter a name for the disc"))
183- entry = gtk.Entry()
184+ entry = Gtk.Entry()
185 entry.show()
186- dialog.vbox.pack_start(entry)
187+ dialog.vbox.pack_start(entry, True, True, 0)
188 res = dialog.run()
189 dialog.destroy()
190- if res == gtk.RESPONSE_OK:
191+ if res == Gtk.ResponseType.OK:
192 name = entry.get_text()
193 return (True,name)
194 return (False,"")
195 def changeCdrom(self):
196- dialog = gtk.MessageDialog(parent=self.dialog_cdrom_progress,
197- flags=gtk.DIALOG_MODAL,
198- type=gtk.MESSAGE_QUESTION,
199- buttons=gtk.BUTTONS_OK_CANCEL,
200+ dialog = Gtk.MessageDialog(parent=self.dialog_cdrom_progress,
201+ flags=Gtk.DialogFlags.MODAL,
202+ type=Gtk.MessageType.QUESTION,
203+ buttons=Gtk.ButtonsType.OK_CANCEL,
204 message_format=None)
205 dialog.set_markup(_("Please insert a disk in the drive:"))
206- dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
207+ dialog.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
208 res = dialog.run()
209 dialog.destroy()
210- if res == gtk.RESPONSE_OK:
211+ if res == Gtk.ResponseType.OK:
212 return True
213 return False
214
215=== modified file 'softwareproperties/gtk/DialogAdd.py'
216--- softwareproperties/gtk/DialogAdd.py 2010-09-20 12:58:51 +0000
217+++ softwareproperties/gtk/DialogAdd.py 2011-02-11 12:13:56 +0000
218@@ -25,7 +25,7 @@
219
220 import os
221 import gobject
222-import gtk
223+from gi.repository import Gtk
224 from gettext import gettext as _
225
226 from aptsources.sourceslist import SourceEntry
227@@ -65,7 +65,7 @@
228 def run(self):
229 res = self.dialog.run()
230 self.dialog.hide()
231- if res == gtk.RESPONSE_OK:
232+ if res == Gtk.ResponseType.OK:
233 line = self.entry.get_text() + "\n"
234 else:
235 line = None
236
237=== modified file 'softwareproperties/gtk/DialogAddSourcesList.py'
238--- softwareproperties/gtk/DialogAddSourcesList.py 2010-09-20 12:58:51 +0000
239+++ softwareproperties/gtk/DialogAddSourcesList.py 2011-02-11 12:13:56 +0000
240@@ -1,6 +1,7 @@
241 #!/usr/bin/env python
242 import pygtk
243-import gtk
244+from gi.repository import Gdk
245+from gi.repository import Gtk
246 import gobject
247 import os
248 from optparse import OptionParser
249@@ -34,15 +35,15 @@
250 self.dialog.set_transient_for(parent)
251 else:
252 self.dialog.set_title(_("Add Software Channels"))
253- self.dialog.window.set_functions(gtk.gdk.FUNC_MOVE)
254+ self.dialog.get_window().set_functions(Gdk.FUNC_MOVE)
255
256 # Setup the treeview
257- self.store = gtk.ListStore(gobject.TYPE_STRING)
258+ self.store = Gtk.ListStore(gobject.TYPE_STRING)
259 self.treeview.set_model(self.store)
260- cell = gtk.CellRendererText()
261+ cell = Gtk.CellRendererText()
262 cell.set_property("xpad", 2)
263 cell.set_property("ypad", 2)
264- column = gtk.TreeViewColumn("Software Channel", cell, markup=0)
265+ column = Gtk.TreeViewColumn("Software Channel", cell, markup=0)
266 column.set_max_width(500)
267 self.treeview.append_column(column)
268
269@@ -93,7 +94,7 @@
270 self.button_cancel.set_label("gtk-close")
271 self.button_replace.hide()
272 self.scrolled.hide()
273- self.image.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
274+ self.image.set_from_stock(Gtk.STOCK_DIALOG_ERROR, Gtk.IconSize.DIALOG)
275 header = _("There are no sources to install software from")
276 body = _("The file '%s' does not contain any valid "
277 "software sources." % self.file)
278
279=== modified file 'softwareproperties/gtk/DialogCacheOutdated.py'
280--- softwareproperties/gtk/DialogCacheOutdated.py 2010-09-20 12:58:51 +0000
281+++ softwareproperties/gtk/DialogCacheOutdated.py 2011-02-11 12:13:56 +0000
282@@ -25,7 +25,7 @@
283 import thread
284 import time
285 import gobject
286-import gtk
287+from gi.repository import Gtk
288 import apt_pkg
289
290 from softwareproperties.gtk.utils import *
291@@ -56,15 +56,19 @@
292 """run the dialog, and if reload was pressed run synaptic"""
293 res = self.dialog.run()
294 self.dialog.hide()
295- if res == gtk.RESPONSE_APPLY:
296+ if res == Gtk.ResponseType.APPLY:
297 self.parent.set_sensitive(False)
298 lock = thread.allocate_lock()
299 lock.acquire()
300+ try:
301+ xid = self.parent.get_window().get_xid()
302+ except AttributeError:
303+ xid = ''
304 t = thread.start_new_thread(self.update_cache,
305- (self.parent.window.xid, lock))
306+ (xid, lock))
307 while lock.locked():
308- while gtk.events_pending():
309- gtk.main_iteration()
310+ while Gtk.events_pending():
311+ Gtk.main_iteration()
312 time.sleep(0.05)
313 self.parent.set_sensitive(True)
314 return res
315
316=== modified file 'softwareproperties/gtk/DialogEdit.py'
317--- softwareproperties/gtk/DialogEdit.py 2010-09-20 12:58:51 +0000
318+++ softwareproperties/gtk/DialogEdit.py 2011-02-11 12:13:56 +0000
319@@ -24,7 +24,7 @@
320
321 import os
322 import gobject
323-import gtk
324+from gi.repository import Gtk
325
326 from aptsources.sourceslist import SourceEntry
327 from softwareproperties.gtk.utils import *
328@@ -115,7 +115,7 @@
329
330 def run(self):
331 res = self.main.run()
332- if res == gtk.RESPONSE_OK:
333+ if res == Gtk.ResponseType.OK:
334 line = self.get_line()
335
336 # change repository
337
338=== modified file 'softwareproperties/gtk/DialogMirror.py'
339--- softwareproperties/gtk/DialogMirror.py 2010-09-20 12:58:51 +0000
340+++ softwareproperties/gtk/DialogMirror.py 2011-02-11 12:13:56 +0000
341@@ -22,7 +22,7 @@
342
343 import os
344 import gobject
345-import gtk
346+from gi.repository import Gtk
347 from gettext import gettext as _
348 import threading
349 import string
350@@ -35,22 +35,11 @@
351 from softwareproperties.gtk.utils import *
352
353
354-testing = threading.Event()
355-
356 (COLUMN_PROTO, COLUMN_DIR) = range(2)
357 (COLUMN_URI, COLUMN_SEPARATOR, COLUMN_CUSTOM, COLUMN_MIRROR) = range(4)
358
359 from softwareproperties.CountryInformation import CountryInformation
360
361-def threaded(f):
362- ''' Thanks to Ross Burton for this piece of code '''
363- def wrapper(*args, **kwargs):
364- t = threading.Thread(target=f, args=args, kwargs=kwargs)
365- t.setDaemon(True)
366- t.start()
367- wrapper.__name__ = f.__name__
368- return wrapper
369-
370 def sort_mirrors(model, iter1, iter2, data=None):
371 ''' sort function for the mirror list:
372 - at first show all custom urls
373@@ -103,31 +92,31 @@
374 self.label_action = self.label_test_mirror
375
376 # store each proto and its dir
377- model_proto = gtk.ListStore(gobject.TYPE_STRING,
378+ model_proto = Gtk.ListStore(gobject.TYPE_STRING,
379 gobject.TYPE_STRING)
380 self.combobox.set_model(model_proto)
381- cr = gtk.CellRendererText()
382- self.combobox.pack_start(cr)
383+ cr = Gtk.CellRendererText()
384+ self.combobox.pack_start(cr, True)
385 self.combobox.add_attribute(cr, "markup", 0)
386
387- self.model = gtk.TreeStore(gobject.TYPE_STRING, # COLUMN_URI
388+ self.model = Gtk.TreeStore(gobject.TYPE_STRING, # COLUMN_URI
389 gobject.TYPE_BOOLEAN, # COLUMN_SEPARATOR
390 gobject.TYPE_BOOLEAN, # COLUMN_CUSTOM
391 gobject.TYPE_PYOBJECT)# COLUMN_MIRROR
392- self.treeview.set_row_separator_func(is_separator)
393- self.model_sort = gtk.TreeModelSort(self.model)
394- self.model_sort.set_default_sort_func(sort_mirrors)
395+ self.treeview.set_row_separator_func(is_separator, None)
396+ self.model_sort = Gtk.TreeModelSort(model=self.model)
397+ self.model_sort.set_default_sort_func(sort_mirrors, None)
398
399 self.distro = distro
400
401 self.treeview.set_model(self.model_sort)
402 # the cell renderer for the mirror uri
403- self.renderer_mirror = gtk.CellRendererText()
404+ self.renderer_mirror = Gtk.CellRendererText()
405 self.renderer_mirror.connect('edited',
406 self.on_edited_custom_mirror,
407 self.model)
408 # the visible column that holds the mirror uris
409- self.column_mirror = gtk.TreeViewColumn("URI",
410+ self.column_mirror = Gtk.TreeViewColumn("URI",
411 self.renderer_mirror,
412 text=COLUMN_URI)
413 self.treeview.append_column(self.column_mirror)
414@@ -161,9 +150,9 @@
415 model.append(None, [hostname, False, False, mirror])
416 # Scroll to the local mirror set
417 if patriot != None:
418- path_sort = self.model_sort.get_path(self.model_sort.convert_child_iter_to_iter(None, patriot))
419+ path_sort = self.model_sort.get_path(self.model_sort.convert_child_iter_to_iter(patriot)[1])
420 self.treeview.expand_row(path_sort, False)
421- self.treeview.set_cursor(path_sort)
422+ self.treeview.set_cursor(path_sort, None, False)
423 self.treeview.scroll_to_cell(path_sort, use_align=True, row_align=0.5)
424
425 def on_edited_custom_mirror(self, cell, path, new_text, model):
426@@ -273,7 +262,7 @@
427 mirror = model.get_value(iter, COLUMN_MIRROR)
428
429 # FIXME: we should also return the list of custom servers
430- if res == gtk.RESPONSE_OK:
431+ if res == Gtk.ResponseType.OK:
432 if mirror == None:
433 # Return the URL of the selected custom mirror
434 return model.get_value(iter, COLUMN_URI)
435@@ -288,63 +277,55 @@
436 else:
437 return None
438
439- @threaded
440 def on_button_test_clicked(self, button, data=None):
441 ''' Perform a test to find the best mirror and select it
442 afterwards in the treeview '''
443- class MirrorTestGtk(MirrorTest):
444- def __init__(self, mirrors, test_file, running, progressbar, label):
445- MirrorTest.__init__(self, mirrors, test_file, running)
446- self.progress = progressbar
447- self.label = label
448- def report_action(self, text):
449- gtk.gdk.threads_enter()
450- self.label.set_label(str("<i>%s</i>" % text))
451- gtk.gdk.threads_leave()
452- def report_progress(self, current, max, borders=(0,1), mod=(0,0)):
453- gtk.gdk.threads_enter()
454- self.progress.set_text(_("Completed %s of %s tests") % \
455- (current + mod[0], max + mod[1]))
456- frac = borders[0] + (borders[1] - borders[0]) / max * current
457- self.progress.set_fraction(frac)
458- gtk.gdk.threads_leave()
459-
460- gtk.gdk.threads_enter()
461 self.button_cancel.set_sensitive(True)
462 self.dialog_test.show()
463- gtk.gdk.threads_leave()
464 self.running = threading.Event()
465 self.running.set()
466+ progress_update = threading.Event()
467 pipe = os.popen("dpkg --print-architecture")
468 arch = pipe.read().strip()
469 test_file = "dists/%s/%s/binary-%s/Packages.gz" % \
470 (self.distro.source_template.name,
471 self.distro.source_template.components[0].name,
472 arch)
473- test = MirrorTestGtk(self.distro.source_template.mirror_set.values(),
474+ test = MirrorTest(self.distro.source_template.mirror_set.values(),
475 test_file,
476- self.running,
477- self.progress,
478- self.label_action)
479+ progress_update,
480+ self.running)
481 test.start()
482- rocker = test.run_full_test()
483- gtk.gdk.threads_enter()
484- testing.clear()
485+
486+ # now run the tests in a background thread, and update the UI on each event
487+ while self.running.is_set():
488+ while Gtk.events_pending():
489+ Gtk.main_iteration_do(False)
490+
491+ # don't spin the CPU until there's something to update; but update the
492+ # UI at least every 100 ms
493+ progress_update.wait(0.1)
494+
495+ if progress_update.is_set():
496+ self.progress.set_text(_("Completed %s of %s tests") % \
497+ (test.progress[0], test.progress[1]))
498+ self.progress.set_fraction(test.progress[2])
499+ progress_update.clear()
500+
501 self.dialog_test.hide()
502 # Select the mirror in the list or show an error dialog
503- if rocker != None:
504- self.model_sort.foreach(self.select_mirror, rocker)
505+ if test.best != None:
506+ self.model_sort.foreach(self.select_mirror, test.best)
507 else:
508 dialogs.show_error_dialog(self.dialog,
509 _("No suitable download server was found"),
510 _("Please check your Internet connection."))
511- gtk.gdk.threads_leave()
512
513 def select_mirror(self, model, path, iter, mirror):
514 """Select and expand the path to a matching mirror in the list"""
515 if model.get_value(iter, COLUMN_URI) == mirror:
516 self.treeview.expand_to_path(path)
517- self.treeview.set_cursor(path)
518+ self.treeview.set_cursor(path, None, False)
519 self.treeview.scroll_to_cell(path, use_align=True, row_align=0.5)
520 self.treeview.grab_focus()
521 # breaks foreach
522
523=== modified file 'softwareproperties/gtk/SimpleGtkbuilderApp.py'
524--- softwareproperties/gtk/SimpleGtkbuilderApp.py 2010-08-25 08:51:00 +0000
525+++ softwareproperties/gtk/SimpleGtkbuilderApp.py 2011-02-11 12:13:56 +0000
526@@ -20,19 +20,19 @@
527 # USA
528
529 import sys
530-import gtk
531+from gi.repository import Gtk
532
533 # based on SimpleGladeApp
534 class SimpleGtkbuilderApp:
535
536 def __init__(self, path, domain):
537- self.builder = gtk.Builder()
538+ self.builder = Gtk.Builder()
539 self.builder.set_translation_domain(domain)
540 self.builder.add_from_file(path)
541 self.builder.connect_signals(self)
542 for o in self.builder.get_objects():
543- if issubclass(type(o), gtk.Buildable):
544- name = gtk.Buildable.get_name(o)
545+ if issubclass(type(o), Gtk.Buildable):
546+ name = Gtk.Buildable.get_name(o)
547 setattr(self, name, o)
548 else:
549 print >> sys.stderr, "WARNING: can not get name for '%s'" % o
550@@ -47,7 +47,7 @@
551 Use this method for starting programs.
552 """
553 try:
554- gtk.main()
555+ Gtk.main()
556 except KeyboardInterrupt:
557 self.on_keyboard_interrupt()
558
559@@ -61,11 +61,11 @@
560 def quit(self):
561 """
562 Quit processing events.
563- The default implementation calls gtk.main_quit()
564+ The default implementation calls Gtk.main_quit()
565
566 Useful for applications that needs a non gtk main loop.
567 For example, applications based on gstreamer needs to override
568 this method with gst.main_quit()
569 """
570- gtk.main_quit()
571+ Gtk.main_quit()
572
573
574=== modified file 'softwareproperties/gtk/SoftwarePropertiesGtk.py'
575--- softwareproperties/gtk/SoftwarePropertiesGtk.py 2010-09-13 08:15:42 +0000
576+++ softwareproperties/gtk/SoftwarePropertiesGtk.py 2011-02-11 12:13:56 +0000
577@@ -30,7 +30,8 @@
578 import re
579 from xml.sax.saxutils import escape
580
581-import gtk
582+from gi.repository import Gdk
583+from gi.repository import Gtk
584 import gobject
585
586 from SimpleGtkbuilderApp import SimpleGtkbuilderApp
587@@ -68,10 +69,10 @@
588
589 def error(parent_window, summary, msg):
590 " show a error dialog "
591- dialog = gtk.MessageDialog(parent=parent_window,
592- flags=gtk.DIALOG_MODAL,
593- type=gtk.MESSAGE_ERROR,
594- buttons=gtk.BUTTONS_OK,
595+ dialog = Gtk.MessageDialog(parent=parent_window,
596+ flags=Gtk.DialogFlags.MODAL,
597+ type=Gtk.MessageType.ERROR,
598+ buttons=Gtk.ButtonsType.OK,
599 message_format=None)
600 dialog.set_markup("<big><b>%s</b></big>\n\n%s" % (summary, msg))
601 res = dialog.run()
602@@ -84,13 +85,13 @@
603 the used software repositories, corresponding authentication keys
604 and update automation """
605 SoftwareProperties.__init__(self, options=options, datadir=datadir)
606- gtk.window_set_default_icon_name("software-properties")
607+ Gtk.Window.set_default_icon_name("software-properties")
608
609 SimpleGtkbuilderApp.__init__(self, os.path.join(datadir, "gtkbuilder", "main.ui"),
610 domain="software-properties")
611
612 if parent:
613- self.window_main.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
614+ self.window_main.set_type_hint(Gdk.WindowTypeHint.DIALOG)
615 self.window_main.show()
616 try:
617 self.window_main.set_transient_for(parent)
618@@ -100,12 +101,12 @@
619 # If externally called, reparent to external application.
620 self.options = options
621 if options and options.toplevel != None:
622- self.window_main.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
623+ self.window_main.set_type_hint(Gdk.WindowTypeHint.DIALOG)
624 self.window_main.show()
625- toplevel = gtk.gdk.window_foreign_new(int(options.toplevel))
626+ toplevel = Gdk.window_foreign_new(int(options.toplevel))
627 if (toplevel):
628 try:
629- self.window_main.window.set_transient_for(toplevel)
630+ self.window_main.set_transient_for(toplevel)
631 except:
632 pass
633 if options and options.open_tab:
634@@ -147,8 +148,8 @@
635
636 def update_interface(self):
637 " abstract interface to keep the UI alive "
638- while gtk.events_pending():
639- gtk.main_iteration()
640+ while Gtk.events_pending():
641+ Gtk.main_iteration()
642
643 def init_popcon(self):
644 """ If popcon is enabled show the statistics tab and an explanation
645@@ -167,8 +168,8 @@
646
647 def init_auto_update(self):
648 """ Set up the widgets that allow to configure the update automation """
649- self.combobox_update_interval = gtk.combo_box_new_text()
650- self.hbox_check_for_updates.pack_start(self.combobox_update_interval)
651+ self.combobox_update_interval = Gtk.ComboBoxText.new_with_entry()
652+ self.hbox_check_for_updates.pack_start(self.combobox_update_interval, True, True, 0)
653 self.combobox_update_interval.show()
654
655 # this maps the key (combo-box-index) to the auto-update-interval value
656@@ -185,7 +186,7 @@
657 self.combobox_update_interval.append_text(_("Weekly"))
658 self.combobox_update_interval.append_text(_("Every two weeks"))
659
660- model_check_interval = gtk.ListStore(gobject.TYPE_STRING,
661+ model_check_interval = Gtk.ListStore(gobject.TYPE_STRING,
662 gobject.TYPE_INT)
663 update_days = self.get_update_interval()
664
665@@ -261,9 +262,9 @@
666
667 # TRANS: %s stands for the distribution name e.g. Debian or Ubuntu
668 self.label_updates.set_label("<b>%s</b>" % (_("%s updates") %\
669- self.distro.id))
670+ self.distro.id.encode('UTF-8')))
671 # TRANS: %s stands for the distribution name e.g. Debian or Ubuntu
672- self.label_dist_name.set_label(_("%s Software") % self.distro.id)
673+ self.label_dist_name.set_label(_("%s Software") % self.distro.id.encode('UTF-8'))
674
675
676 self.handlers.append((self.checkbutton_source_code,
677@@ -278,7 +279,7 @@
678 # first %s is the description of the component
679 # second %s is the code name of the comp, eg main, universe
680 label = _("%s (%s)") % (comp.get_description(), comp.name)
681- checkbox = gtk.CheckButton(label)
682+ checkbox = Gtk.CheckButton(label)
683
684 checkbox.comp = comp
685 # setup the callback and show the checkbutton
686@@ -295,7 +296,7 @@
687 if len(self.distro.source_template.children) < 1:
688 self.frame_children.hide()
689 for template in self.distro.source_template.children:
690- checkbox = gtk.CheckButton(label="%s (%s)" % (template.description,
691+ checkbox = Gtk.CheckButton(label="%s (%s)" % (template.description,
692 template.name))
693 checkbox.template = template
694 self.handlers.append((checkbox,
695@@ -307,13 +308,13 @@
696
697
698 # setup the server chooser
699- cell = gtk.CellRendererText()
700+ cell = Gtk.CellRendererText()
701 self.combobox_server.pack_start(cell, True)
702 self.combobox_server.add_attribute(cell, 'text', 0)
703 self.handlers.append((self.combobox_server,
704 self.combobox_server.connect("changed",
705 self.on_combobox_server_changed)))
706- server_store = gtk.ListStore(gobject.TYPE_STRING,
707+ server_store = Gtk.ListStore(gobject.TYPE_STRING,
708 gobject.TYPE_STRING,
709 gobject.TYPE_BOOLEAN)
710 self.combobox_server.set_model(server_store)
711@@ -421,7 +422,7 @@
712 '''Call the backend to set the update automation level to the given
713 value'''
714 if widget.get_active() == True:
715- self.vbox_auto_updates.foreach(lambda b: b.set_inconsistent(False))
716+ self.vbox_auto_updates.foreach(lambda b, d: b.set_inconsistent(False), None)
717 SoftwareProperties.set_update_automation_level(self, state)
718 self.set_modified_config()
719
720@@ -536,13 +537,13 @@
721 # STORE_SOURCE - the source entry object
722 # STORE_SEPARATOR - if the entry is a separator
723 # STORE_VISIBLE - if the entry is shown or hidden
724- self.cdrom_store = gtk.ListStore(gobject.TYPE_BOOLEAN,
725+ self.cdrom_store = Gtk.ListStore(gobject.TYPE_BOOLEAN,
726 gobject.TYPE_STRING,
727 gobject.TYPE_PYOBJECT,
728 gobject.TYPE_BOOLEAN,
729 gobject.TYPE_BOOLEAN)
730 self.treeview_cdroms.set_model(self.cdrom_store)
731- self.source_store = gtk.ListStore(gobject.TYPE_BOOLEAN,
732+ self.source_store = Gtk.ListStore(gobject.TYPE_BOOLEAN,
733 gobject.TYPE_STRING,
734 gobject.TYPE_PYOBJECT,
735 gobject.TYPE_BOOLEAN,
736@@ -551,51 +552,55 @@
737 self.treeview_sources.set_row_separator_func(self.is_separator,
738 STORE_SEPARATOR)
739
740- cell_desc = gtk.CellRendererText()
741+ cell_desc = Gtk.CellRendererText()
742 cell_desc.set_property("xpad", 2)
743 cell_desc.set_property("ypad", 2)
744- col_desc = gtk.TreeViewColumn(_("Software Sources"), cell_desc,
745+ col_desc = Gtk.TreeViewColumn(_("Software Sources"), cell_desc,
746 markup=COLUMN_DESC)
747 col_desc.set_max_width(1000)
748
749- cell_toggle = gtk.CellRendererToggle()
750+ cell_toggle = Gtk.CellRendererToggle()
751 cell_toggle.set_property("xpad", 2)
752 cell_toggle.set_property("ypad", 2)
753 self.handlers.append([cell_toggle,
754 cell_toggle.connect('toggled',
755 self.on_isv_source_toggled,
756 self.cdrom_store)])
757- col_active = gtk.TreeViewColumn(_("Active"), cell_toggle,
758+ col_active = Gtk.TreeViewColumn(_("Active"), cell_toggle,
759 active=COLUMN_ACTIVE)
760
761 self.treeview_cdroms.append_column(col_active)
762 self.treeview_cdroms.append_column(col_desc)
763
764- cell_desc = gtk.CellRendererText()
765+ cell_desc = Gtk.CellRendererText()
766 cell_desc.set_property("xpad", 2)
767 cell_desc.set_property("ypad", 2)
768- col_desc = gtk.TreeViewColumn(_("Software Sources"), cell_desc,
769+ col_desc = Gtk.TreeViewColumn(_("Software Sources"), cell_desc,
770 markup=COLUMN_DESC)
771 col_desc.set_max_width(1000)
772
773- cell_toggle = gtk.CellRendererToggle()
774+ cell_toggle = Gtk.CellRendererToggle()
775 cell_toggle.set_property("xpad", 2)
776 cell_toggle.set_property("ypad", 2)
777 self.handlers.append([cell_toggle,
778 cell_toggle.connect('toggled',
779 self.on_isv_source_toggled,
780 self.source_store)])
781- col_active = gtk.TreeViewColumn(_("Active"), cell_toggle,
782+ col_active = Gtk.TreeViewColumn(_("Active"), cell_toggle,
783 active=COLUMN_ACTIVE)
784
785 self.treeview_sources.append_column(col_active)
786 self.treeview_sources.append_column(col_desc)
787 # drag and drop support for sources.list
788- self.treeview_sources.drag_dest_set(gtk.DEST_DEFAULT_ALL,
789- [('text/uri-list',0, 0)],
790- gtk.gdk.ACTION_COPY)
791- self.treeview_sources.connect("drag_data_received",
792- self.on_sources_drag_data_received)
793+ try:
794+ Gtk.drag_dest_set(self.treeview_sources, Gtk.DestDefaults.ALL,
795+ [Gtk.TargetEntry.new('text/uri-list', 0, 0)],
796+ Gdk.DragAction.COPY)
797+ self.treeview_sources.connect("drag_data_received",
798+ self.on_sources_drag_data_received)
799+ except AttributeError:
800+ # does not work with Gtk2/GI
801+ pass
802
803 def on_isv_source_activate(self, treeview, path, column):
804 """Open the edit dialog if a channel was double clicked"""
805@@ -621,22 +626,27 @@
806
807 def init_keys(self):
808 """Setup the user interface parts needed for the key handling"""
809- self.keys_store = gtk.ListStore(str)
810+ self.keys_store = Gtk.ListStore(str)
811 self.treeview_auth.set_model(self.keys_store)
812- tr = gtk.CellRendererText()
813- keys_col = gtk.TreeViewColumn("Key", tr, text=0)
814+ tr = Gtk.CellRendererText()
815+ keys_col = Gtk.TreeViewColumn("Key", tr, text=0)
816 self.treeview_auth.append_column(keys_col)
817- self.treeview_auth.enable_model_drag_dest(
818- [('text/plain', 0, 0)], gtk.gdk.ACTION_COPY)
819- self.treeview_auth.connect("drag_data_received",
820- self.on_auth_drag_data_received)
821+ try:
822+ self.treeview_auth.enable_model_drag_dest(
823+ [('text/plain', 0, 0)], Gdk.DragAction.COPY)
824+ self.treeview_auth.connect("drag_data_received",
825+ self.on_auth_drag_data_received)
826+ except AttributeError:
827+ # Does not work with GTK 2/GI
828+ pass
829+
830 self.treeview_auth.connect("button-press-event",
831 self.show_auth_context_menu)
832
833 def show_auth_context_menu(self, widget, event):
834- if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
835- menu = gtk.Menu()
836- item_paste = gtk.MenuItem(_("_Add key from paste data"))
837+ if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
838+ menu = Gtk.Menu()
839+ item_paste = Gtk.MenuItem(_("_Add key from paste data"))
840 item_paste.connect("activate", self.on_auth_add_key_from_paste)
841 menu.add(item_paste)
842 menu.popup(None, None, None, 0, event.time)
843@@ -644,7 +654,7 @@
844 return True
845
846 def on_auth_add_key_from_paste(self, widget):
847- keydata = gtk.Clipboard().wait_for_text()
848+ keydata = Gtk.Clipboard().wait_for_text()
849 if not keydata:
850 return
851 if not self.add_key_from_data(keydata):
852@@ -744,7 +754,7 @@
853 self.combobox_update_interval.set_active(i)
854 value = self.combobox_interval_mapping[i]
855 # A little hack to re-set the former selected update automation level
856- self.vbox_auto_updates.foreach(lambda b: b.toggled())
857+ self.vbox_auto_updates.foreach(lambda b, d: b.toggled(), None)
858 else:
859 self.combobox_update_interval.set_sensitive(False)
860 self.vbox_auto_updates.set_sensitive(False)
861@@ -770,7 +780,7 @@
862 source_entry = model.get_value(iter, LIST_ENTRY_OBJ)
863 dialog = DialogEdit(self.window_main, self.sourceslist,
864 source_entry, self.datadir)
865- if dialog.run() == gtk.RESPONSE_OK:
866+ if dialog.run() == Gtk.ResponseType.OK:
867 self.set_modified_sourceslist()
868
869 # FIXME:outstanding from merge
870@@ -812,17 +822,17 @@
871 def add_key_clicked(self, widget):
872 """Provide a file chooser that allows to add the gnupg of a trusted
873 software vendor"""
874- chooser = gtk.FileChooserDialog(title=_("Import key"),
875+ chooser = Gtk.FileChooserDialog(title=_("Import key"),
876 parent=self.window_main,
877- buttons=(gtk.STOCK_CANCEL,
878- gtk.RESPONSE_REJECT,
879- gtk.STOCK_OK,gtk.RESPONSE_ACCEPT))
880+ buttons=(Gtk.STOCK_CANCEL,
881+ Gtk.ResponseType.REJECT,
882+ Gtk.STOCK_OK,Gtk.ResponseType.ACCEPT))
883 if "SUDO_USER" in os.environ:
884 home = os.path.expanduser("~%s" % os.environ["SUDO_USER"])
885 chooser.set_current_folder(home)
886 res = chooser.run()
887 chooser.hide()
888- if res == gtk.RESPONSE_ACCEPT:
889+ if res == Gtk.ResponseType.ACCEPT:
890 if not self.add_key(chooser.get_filename()):
891 error(self.window_main,
892 _("Error importing selected file"),
893
894=== modified file 'softwareproperties/gtk/dialogs.py'
895--- softwareproperties/gtk/dialogs.py 2007-02-02 19:45:52 +0000
896+++ softwareproperties/gtk/dialogs.py 2011-02-11 12:13:56 +0000
897@@ -21,12 +21,12 @@
898 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
899 # USA
900
901-import gtk
902+from gi.repository import Gtk
903
904 def show_error_dialog(parent, primary, secondary):
905 p = "<span weight=\"bold\" size=\"larger\">%s</span>" % primary
906- dialog = gtk.MessageDialog(parent,gtk.DIALOG_MODAL,
907- gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,"")
908+ dialog = Gtk.MessageDialog(parent,Gtk.DialogFlags.MODAL,
909+ Gtk.MessageType.ERROR,Gtk.ButtonsType.CLOSE,"")
910 dialog.set_markup(p);
911 dialog.format_secondary_text(secondary);
912 dialog.run()
913
914=== modified file 'softwareproperties/gtk/utils.py'
915--- softwareproperties/gtk/utils.py 2010-09-20 12:58:51 +0000
916+++ softwareproperties/gtk/utils.py 2011-02-11 12:13:56 +0000
917@@ -16,19 +16,19 @@
918 # this program; if not, write to the Free Software Foundation, Inc.,
919 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
920
921-import gtk
922+from gi.repository import Gtk
923 import os
924 import sys
925
926 def setup_ui(self, path, domain):
927 # setup ui
928- self.builder = gtk.Builder()
929+ self.builder = Gtk.Builder()
930 self.builder.set_translation_domain(domain)
931 self.builder.add_from_file(path)
932 self.builder.connect_signals(self)
933 for o in self.builder.get_objects():
934- if issubclass(type(o), gtk.Buildable):
935- name = gtk.Buildable.get_name(o)
936+ if issubclass(type(o), Gtk.Buildable):
937+ name = Gtk.Buildable.get_name(o)
938 setattr(self, name, o)
939 else:
940 print >> sys.stderr, "WARNING: can not get name for '%s'" % o
941
942=== modified file 'softwareproperties/kde/DialogMirror.py'
943--- softwareproperties/kde/DialogMirror.py 2010-08-16 12:31:06 +0000
944+++ softwareproperties/kde/DialogMirror.py 2011-02-11 12:13:56 +0000
945@@ -160,7 +160,7 @@
946 afterwards in the treeview '''
947 class MirrorTestKDE(MirrorTest):
948 def __init__(self, mirrors, test_file, running, dialog, parent):
949- MirrorTest.__init__(self, mirrors, test_file, running)
950+ MirrorTest.__init__(self, mirrors, test_file, None, running)
951 self.dialog = dialog
952 self.parent = parent
953

Subscribers

People subscribed via source and target branches

to status/vote changes: