#!/usr/bin/python # # File: geneticone/gui/main.py # This file is part of the Genetic/One-Project, a graphical portage-frontend. # # Copyright (C) 2006 Necoro d.M. # This is free software. You may redistribute copies of it under the terms of # the GNU General Public License version 2. # There is NO WARRANTY, to the extent permitted by law. # # Written by Necoro d.M. # our backend stuff import geneticone from geneticone import flags # gtk stuff import pygtk pygtk.require("2.0") import gtk import gobject # for doing emerge from subprocess import * # threading from threading import Thread # for the terminal import pty import vte # other from portage_util import unique_array class EmergeQueue: """This class manages the emerge queue.""" def __init__ (self, tree = None, console = None, packages = None): """Constructor. @param tree: Tree to append all the items to. Default: None. @type tree: gtk.TreeStore @param console: Output is shown here. Default: None @type console: vte.Terminal @param packages: The list of packages sorted by categories. We will delete the appropriate category if we updated sth. Default: None @type packages: dictionary: {category: list_of_packages}.""" self.mergequeue = [] self.unmergequeue = [] self.iters = {} self.deps = {} self.tree = tree self.console = console self.packages = packages if self.tree: self.emergeIt = self.tree.append(None, ["Emerge"]) self.unmergeIt = self.tree.append(None, ["Unmerge"]) else: self.emergeIt = self.unmergeIt = None def update_tree (self, it, cpv): """This updates the tree recursivly. @param it: iterator where to append @type it: gtk.TreeIter @param cpv: The package to append. @type cpv: string (cat/pkg-ver) @raise geneticone.BlockedException: When occured during dependency-calculation.""" # get depencies if cpv in self.deps: return # in list already else: deps = geneticone.find_packages("="+cpv)[0].get_dep_packages() self.deps.update({cpv : deps}) subIt = self.tree.append(it, [cpv]) # recursive call for d in deps: try: self.update_tree(subIt, d) except geneticone.BlockedException, e: # remove the project while self.tree.iter_parent(subIt): subIt = self.tree.iter_parent(subIt) self.remove_children(subIt) raise e # add iter self.iters.update({cpv: subIt}) def append (self, cpv, unmerge = False, update = False): """Appends a cpv either to the merge queue or to the unmerge-queue. Also updates the tree-view. @param cpv: Package to add @type cpv: string (cat/pkg-ver) @param unmerge: Set to True if you want to unmerge this package - else False. Default: False @type unmerge: boolean @param update: Set to True if a package is going to be updated (e.g. if the use-flags changed). Default: False @type update: boolean""" if not unmerge: try: # insert dependencies pkg = geneticone.find_packages("="+cpv)[0] deps = pkg.get_dep_packages() if update: if deps == self.deps[cpv]: return # nothing changed - return else: parentIt = self.tree.iter_parent(self.iters[cpv]) self.remove_children(self.iters[cpv], False) # this is needed to def delete everything self.remove(self.iters[cpv], False) self.update_tree(parentIt, cpv) else: # not update self.mergequeue.append(cpv) if self.emergeIt: self.update_tree(self.emergeIt, cpv) except geneticone.BlockedException, e : # there is sth blocked --> call blocked_dialog blocks = e[0] blocked_dialog(cpv, blocks) return else: # unmerge self.unmergequeue.append(cpv) if self.unmergeIt: # update tree self.tree.append(self.unmergeIt, [cpv]) def _update_packages(self, packages, process = None): """This updates the packages-list. It simply removes all affected categories so they have to be rebuilt. @param packages: The packages which we emerged. @type packages: list of cpvs @param process: The process we have to wait for before we can do our work. Default: None. @type process: subprocess.Popen""" if process: process.wait() for p in packages: try: cat = geneticone.split_package_name(p)[0] # get category while cat[0] in ["=",">","<","!"]: cat = cat[1:] print "Category: "+cat , del self.packages[cat] print "marked for refreshing" except KeyError: # not in self.packages - ignore pass def _emerge (self, options, packages, it): """Calls emerge and updates the terminal. @param options: options to send to emerge @type options: list @param packages: packages to emerge @type packages: list @param it: Iterator which points to an entry whose children will be removed after completion. @type it: gtk.TreeIter""" # open tty (master, slave) = pty.openpty() self.console.set_pty(master) # start emerge process = Popen(["/usr/bin/python","/usr/bin/emerge"]+options+packages, stdout = slave, stderr = STDOUT, shell = False) Thread(target=self._update_packages, args=(packages, process)).start() # remove self.remove_children(it) def emerge (self, force = False): """Emerges everything in the merge-queue. @param force: If False, '-pv' is send to emerge. Default: False. @type force: boolean""" if len(self.mergequeue) == 0: return # nothing in queue # prepare package-list list = [] for k in self.mergequeue: list += ["="+k] s = [] if not force: s = ["-pv"] self._emerge(s, list, self.emergeIt) def unmerge (self, force = False): """Unmerges everything in the umerge-queue. @param force: If False, '-pv' is send to emerge. Default: False. @type force: boolean""" if len(self.unmergequeue) == 0: return # nothing in queue list = self.unmergequeue[:] # copy the unmerge-queue # set options s = ["-C"] if not force: s = ["-Cpv"] self._emerge(s,list, self.unmergeIt) def remove_children (self, parentIt, removeNewFlags = True): """Removes all children of a given parent TreeIter recursivly. @param parentIt: The iter from which to remove all children. @type parentIt: gtk.TreeIter @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True. @type removeNewFlags: boolean""" childIt = self.tree.iter_children(parentIt) while childIt: if (self.tree.iter_has_child(childIt)): # recursive call self.remove_children(childIt, removeNewFlags) temp = childIt childIt = self.tree.iter_next(childIt) self.remove(temp, removeNewFlags) def remove (self, it, removeNewFlags = True): """Removes a specific item in the tree. This does not remove the top-entries. @param it: Iterator which points to the entry we are going to remove. @type it: gtk.TreeIter @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True. @type removeNewFlags: boolean""" if self.tree.iter_parent(it): # NEVER remove our top stuff cpv = self.tree.get_value(it,0) if self.tree.get_string_from_iter(it).split(":")[0] == self.tree.get_string_from_iter(self.emergeIt): # in Emerge del self.iters[cpv] del self.deps[cpv] try: self.mergequeue.remove(cpv) except ValueError: # this is a dependency - ignore pass if removeNewFlags: flags.remove_new_use_flags(cpv) else: # in Unmerge self.unmergequeue.remove(cpv) self.tree.remove(it) class PackageWindow: """A window with data about a specfic package.""" def __init__ (self, parent, cp, queue = None, version = None, delOnClose = True, doEmerge = True): """Build up window contents.""" self.parent = parent # parent window self.cp = cp # category/package self.version = version # version - if not None this is used self.queue = queue self.delOnClose = delOnClose self.doEmerge = doEmerge self.flagChanged = False # window self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_title(cp) self.window.set_modal(True) self.window.set_transient_for(parent) self.window.set_destroy_with_parent(True) self.window.set_resizable(False) self.window.set_default_size(1,1) # as small as possible self.window.connect("delete-event", lambda a,b: False) #self.window.connect("configure-event", self.cbSizeCheck) # packages and installed packages self.packages = geneticone.sort_package_list(geneticone.find_packages(cp, masked=True)) self.instPackages = geneticone.sort_package_list(geneticone.find_installed_packages(cp, masked=True)) # main structure - the table self.table = gtk.Table(rows=4,columns=2) self.window.add(self.table) # version-combo-box self.vCombo = self.build_vers_combo() self.table.attach(self.vCombo, 0, 1, 1, 2, yoptions = gtk.FILL) # the label (must be here, because it depends on the combo box) desc = self.actual_package().get_env_var("DESCRIPTION") use_markup = True if not desc: desc = "" use_markup = False else: desc = ""+desc+"" self.descLabel = gtk.Label(desc) self.descLabel.set_line_wrap(True) self.descLabel.set_justify(gtk.JUSTIFY_CENTER) self.descLabel.set_use_markup(use_markup) self.table.attach(self.descLabel, 0, 2, 0, 1, xoptions = gtk.FILL, ypadding = 10) # the check boxes checkHB = gtk.HBox (True, 1) self.table.attach(checkHB, 1, 2, 1, 2, yoptions = gtk.FILL) self.installedCheck = gtk.CheckButton() self.installedCheck.connect("button-press-event", self.cb_button_pressed) self.installedCheck.set_label("Installed") checkHB.pack_start(self.installedCheck, True, False) self.maskedCheck = gtk.CheckButton() self.maskedCheck.connect("button-press-event", self.cb_button_pressed) self.maskedCheck.set_label("Masked") checkHB.pack_start(self.maskedCheck, True, False) self.testingCheck = gtk.CheckButton() self.testingCheck.connect("button-press-event", self.cb_button_pressed) self.testingCheck.set_label("Testing") checkHB.pack_start(self.testingCheck, True, False) # use list self.useList = self.build_use_list() self.useListScroll = gtk.ScrolledWindow() self.useListScroll.add(self.useList) self.useListScroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER) # XXX: make this work correctly self.table.attach(self.useListScroll, 0, 2, 2, 3, ypadding = 10) # buttons buttonHB = gtk.HButtonBox() buttonHB.set_layout(gtk.BUTTONBOX_SPREAD) self.table.attach(buttonHB, 0, 2, 3, 4) self.emergeBtn = gtk.Button("_Emerge") self.unmergeBtn = gtk.Button("_Unmerge") if not self.queue or not doEmerge: self.emergeBtn.set_sensitive(False) self.unmergeBtn.set_sensitive(False) self.cancelBtn = gtk.Button("_Cancel") if not self.delOnClose: self.cancelBtn.set_label("_Close") self.cancelBtn.connect("clicked", self.cb_cancel_clicked) self.emergeBtn.connect("clicked", self.cb_emerge_clicked) self.unmergeBtn.connect("clicked", self.cb_unmerge_clicked) buttonHB.pack_start(self.emergeBtn) buttonHB.pack_start(self.unmergeBtn) buttonHB.pack_start(self.cancelBtn) # current status self.cb_changed(self.vCombo) # show self.window.show_all() def cb_changed (self, combo, data = None): """Callback for the changed ComboBox. It then rebuilds the useList and the checkboxes.""" # remove old useList self.useListScroll.remove(self.useList) # build new self.useList = self.build_use_list() self.useListScroll.add(self.useList) self.update_checkboxes() self.useListScroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER) # set emerge-button-label if not self.actual_package().is_installed(): self.emergeBtn.set_label("_Emerge") self.unmergeBtn.set_sensitive(False) else: self.emergeBtn.set_label("R_emerge") self.unmergeBtn.set_sensitive(True) # refresh - make window as small as possible self.table.show_all() self.window.resize(1,1) return True def build_vers_combo (self): """Creates the combo box with the different versions.""" combo = gtk.combo_box_new_text() # append versions for s in [x.get_version() for x in self.packages]: combo.append_text(s) # activate the first one try: best_version = "" if self.version: best_version = self.version else: best_version = geneticone.find_best_match(self.packages[0].get_cp(), (self.instPackages != [])).get_version() for i in range(len(self.packages)): if self.packages[i].get_version() == best_version: combo.set_active(i) break except AttributeError: # no package found combo.set_active(0) combo.connect("changed", self.cb_changed) return combo def actual_package (self): """Returns the actual package (a geneticone.Package-object).""" return self.packages[self.vCombo.get_active()] def cb_button_pressed (self, b, event, data = None): """Callback for pressed checkboxes. Just quits the event-loop - no redrawing.""" if not isinstance(b, gtk.CellRendererToggle): b.emit_stop_by_name("button-press-event") return True def cb_cancel_clicked (self, button, data = None): if self.delOnClose: self.actual_package().remove_new_use_flags() if self.flagChanged: if self.queue: self.queue.append(self.actual_package().get_cpv(), update = True) self.window.destroy() return True def cb_emerge_clicked (self, button, data = None): """Adds the package to the EmergeQueue.""" if not geneticone.am_i_root(): errorMB = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "You cannot (un)merge without being root.") errorMB.run() errorMB.destroy() else: self.queue.append(self.actual_package().get_cpv(), False) self.window.destroy() return True def cb_unmerge_clicked (self, button, data = None): """Adds the package to the UnmergeQueue.""" if not geneticone.am_i_root(): errorMB = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "You cannot (un)merge without being root.") errorMB.run() errorMB.destroy() else: self.queue.append(self.actual_package().get_cpv(), True) self.window.destroy() return True def cb_use_flag_toggled (self, cell, path, store, data = None): store[path][0] = not store[path][0] prefix = "" if not store[path][0]: prefix = "-" self.actual_package().set_use_flag(prefix+store[path][1]) self.flagChanged = True return True def update_checkboxes (self): """Updates the checkboxes.""" self.installedCheck.set_active(self.actual_package().is_installed()) self.maskedCheck.set_active(self.actual_package().is_masked()) self.testingCheck.set_active((self.actual_package().get_mask_status() % 3) == 1) def build_use_list (self): """Builds the useList.""" store = gtk.ListStore(bool, str, str) pkg = self.actual_package() for use in pkg.get_all_use_flags(): if pkg.is_installed() and use in pkg.get_actual_use_flags(): # flags set during install enabled = True elif (not pkg.is_installed()) and use in pkg.get_settings("USE").split() and not flags.invert_use_flag(use) in pkg.get_new_use_flags(): # flags that would be set enabled = True elif use in pkg.get_new_use_flags(): enabled = True else: enabled = False store.append([enabled, use, geneticone.get_use_desc(use, self.cp)]) # build view view = gtk.TreeView(store) cell = gtk.CellRendererText() tCell = gtk.CellRendererToggle() tCell.set_property("activatable", True) tCell.connect("toggled", self.cb_use_flag_toggled, store) view.append_column(gtk.TreeViewColumn("Enabled", tCell, active = 0)) view.append_column(gtk.TreeViewColumn("Flags", cell, text = 1)) view.append_column(gtk.TreeViewColumn("Description", cell, text = 2)) if store.iter_n_children(None) == 0: view.set_child_visible(False) else: view.set_child_visible(True) return view class SearchWindow: """A window showing the results of a search process.""" def __init__ (self, parent, list, jump_to): # window self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_title("Search results") self.window.set_modal(True) self.window.set_transient_for(parent) self.window.set_destroy_with_parent(True) self.window.set_resizable(False) self.window.set_default_size(1,1) self.list = list self.jump_to = jump_to box = gtk.HBox(False) self.window.add(box) self.combo = gtk.combo_box_new_text() for x in list: self.combo.append_text(x) self.combo.set_active(0) box.pack_start(self.combo) okBtn = gtk.Button("OK") okBtn.connect("clicked", self.cb_ok_btn_clicked) box.pack_start(okBtn) self.window.show_all() def cb_ok_btn_clicked (self, button, data = None): self.window.destroy() self.jump_to(self.list[self.combo.get_active()]) class MainWindow: """Application main window.""" def __init__ (self): """Build up window""" # window self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_title("Genetic/One") self.window.connect("delete_event", self.cb_delete) self.window.connect("destroy", self.cb_destroy) self.window.set_border_width(2) self.window.set_geometry_hints (self.window, min_width = 600, min_height = 800, max_height = gtk.gdk.screen_height(), max_width = gtk.gdk.screen_width()) self.window.set_resizable(True) # main vb vb = gtk.VBox(False, 1) self.window.add(vb) # menubar menubar = self.create_main_menu() vb.pack_start(menubar, False) # search self.searchEntry = gtk.Entry() self.searchBtn = gtk.Button("_Search") self.searchBtn.connect("clicked", self.cb_search_clicked) hbSearch = gtk.HBox(False, 5) hbSearch.pack_start(self.searchEntry, True, True) hbSearch.pack_start(self.searchBtn, False, False) vb.pack_start(hbSearch, False, False, 5) # VPaned holding the lists and the Terminal vpaned = gtk.VPaned() vpaned.set_position(400) vb.pack_start(vpaned, True, True) # a HB holding the lists hb = gtk.HBox(True, 5) hbFrame = gtk.Frame() hbFrame.add(hb) hbFrame.set_shadow_type(gtk.SHADOW_IN) vpaned.pack1(hbFrame, shrink = True, resize = True) self.scroll_1 = gtk.ScrolledWindow() self.scroll_2 = gtk.ScrolledWindow() self.scroll_1.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) self.scroll_2.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) hb.pack_start(self.scroll_1, True, True) hb.pack_start(self.scroll_2, True, True) # create cat List self.catList = self.create_cat_list() self.scroll_1.add(self.catList) # create pkg list self.pkgList = self.create_pkg_list() self.scroll_2.add(self.pkgList) # queue list queueVB = gtk.VBox(False, 0) hb.pack_start(queueVB, True, True) queueScroll = gtk.ScrolledWindow() queueScroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) emergeStore = gtk.TreeStore(str) self.emergeView = gtk.TreeView(emergeStore) cell = gtk.CellRendererText() col = gtk.TreeViewColumn("Queue", cell, text = 0) self.emergeView.append_column(col) self.emergeView.connect("row-activated", self.cb_row_activated, emergeStore) queueScroll.add(self.emergeView) queueVB.pack_start(queueScroll, True, True) # buttons right unter the queue list buttonBox = gtk.HButtonBox() queueVB.pack_start(buttonBox, False) self.emergeBtn = gtk.Button("_Emerge") self.emergeBtn.connect("clicked", self.cb_emerge_clicked) self.unmergeBtn = gtk.Button("_Unmerge") self.unmergeBtn.connect("clicked", self.cb_emerge_clicked) self.removeBtn = gtk.Button("_Remove") self.removeBtn.connect("clicked", self.cb_remove_clicked) buttonBox.pack_start(self.emergeBtn) buttonBox.pack_start(self.removeBtn) buttonBox.pack_start(self.unmergeBtn) # the terminal term = vte.Terminal() term.set_scrollback_lines(1024) term.set_scroll_on_output(True) # XXX why is this not working with the colors term.set_color_background(gtk.gdk.color_parse("white")) term.set_color_foreground(gtk.gdk.color_parse("black")) termBox = gtk.HBox(False, 0) termScroll = gtk.VScrollbar(term.get_adjustment()) termBox.pack_start(term, True, True) termBox.pack_start(termScroll, False) termFrame = gtk.Frame("Console") termFrame.set_shadow_type(gtk.SHADOW_IN) termFrame.add(termBox) vpaned.pack2(termFrame, shrink = True, resize = True) # the status line self.statusLabel = gtk.Label("Genetic/One - ") self.statusLabel.set_alignment(0.0,0.7) self.statusLabel.set_single_line_mode(True) vb.pack_start(self.statusLabel, False, False) # show self.window.show_all() # set emerge queue self.queue = EmergeQueue(console=term, tree = emergeStore, packages = self.packages) def cb_delete (self, widget, data = None): """Returns false -> window is deleted.""" return False def cb_destroy (self, widget, data = None): """Calls main_quit().""" gtk.main_quit() def create_main_menu (self): """Creates the main menu. XXX: Rebuild to use UIManager""" # the menu-list mainMenuDesc = [ ( "/_File", None, None, 0, ""), ( "/File/_Close", None, self.cb_destroy, 0, ""), ( "/_Emerge", None, None, 0, ""), ( "/Emerge/_Emerge", None, self.cb_emerge_clicked, 0, ""), ( "/Emerge/_Unmerge", None, self.cb_emerge_clicked, 0, ""), ( "/_?", None, None, 0, ""), ( "/?/_About", None, None, 0, "") ] self.itemFactory = gtk.ItemFactory(gtk.MenuBar, "
", None) self.itemFactory.create_items(mainMenuDesc) return self.itemFactory.get_widget("
") def cb_cat_list_selection (self, view, data = None, force = False): """Callback for a category-list selection. Updates the package list with these packages in the category.""" if view == self.catList: # be sure it is the catList # get the selected category sel = view.get_selection() store, it = sel.get_selected() if it: # remove old one self.scroll_2.remove(self.pkgList) # create new package list self.pkgList = self.create_pkg_list(store.get_value(it,0), force) self.scroll_2.add(self.pkgList) self.scroll_2.show_all() return False def cb_row_activated (self, view, path, col, store = None): """Callback for an activated row in the pkgList. Opens a package window.""" if view == self.pkgList: package = store.get_value(store.get_iter(path), 0) if package[-1] == '*': package = package[:-1] PackageWindow(self.window, self.selCatName+"/"+package, self.queue) elif view == self.emergeView: if len(path) > 1: package = store.get_value(store.get_iter(path), 0) cat, name, vers, rev = geneticone.split_package_name(package) if rev != "r0": vers = vers+"-"+rev PackageWindow(self.window, cat+"/"+name, queue = self.queue, version = vers, delOnClose = False, doEmerge = False) return True def create_cat_list (self): """Creates the category list.""" store = gtk.ListStore(str) # build categories for p in geneticone.list_categories(): store.append([p]) # sort them alphabetically store.set_sort_column_id(0, gtk.SORT_ASCENDING) view = gtk.TreeView(store) cell = gtk.CellRendererText() col = gtk.TreeViewColumn("Categories", cell, text = 0) view.append_column(col) view.connect("cursor-changed", self.cb_cat_list_selection) view.connect("row-activated", lambda v,p,c : self.cb_cat_list_selection(v)) view.set_search_column(0) return view packages = {} # directory category -> [packages] def create_pkg_list (self, name = None, force = False): """Creates the package list. Gets the name of the category.""" self.selCatName = name # actual category store = gtk.ListStore(str) # calculate packages if name: if name not in self.packages and not force: # only calc packages if not already done self.packages[name] = [] for p in unique_array([x.get_name() for x in geneticone.find_all_packages(name+"/")]): if geneticone.find_installed_packages(name+"/"+p, masked=True) != []: p += "*" # append a '*' if the package is installed self.packages[name].append(p) for p in self.packages[name]: store.append([p]) # sort alphabetically store.set_sort_column_id(0, gtk.SORT_ASCENDING) # build view pkgList = gtk.TreeView(store) cell = gtk.CellRendererText() col = gtk.TreeViewColumn("Packages", cell, text = 0) pkgList.append_column(col) pkgList.connect("row-activated", self.cb_row_activated, store) return pkgList def cb_remove_clicked (self, button, data = None): """Removes a selected item in the (un)emerge-queue if possible.""" selected = self.emergeView.get_selection() if selected: model, iter = selected.get_selected() if not model.iter_parent(iter): # top-level if model.iter_n_children(iter) > 0: # and has children which can be removed :) askMB = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Do you really want to clear the whole queue?") if askMB.run() == gtk.RESPONSE_YES : self.queue.remove_children(iter) askMB.destroy() elif model.iter_parent(model.iter_parent(iter)): # this is in the 3rd level => dependency infoMB = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "You cannot remove dependencies. :)") infoMB.run() infoMB.destroy() else: self.queue.remove(iter) return True def cb_emerge_clicked (self, button, data = None): """Do emerge or unemerge.""" if button == self.emergeBtn: if len(flags.newUseFlags) > 0: hintMB = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "You have changed use flags. Genetic/One will write these changes into the appropriate files. Please backup them if you think it is necessairy.") hintMB.run() hintMB.destroy() flags.write_use_flags() self.queue.emerge(force=True) elif button == self.unmergeBtn: self.queue.unmerge(force=True) return True def cb_search_clicked (self, button, data = None): """Do a search.""" if self.searchEntry.get_text() != "": packages = geneticone.find_all_packages(self.searchEntry.get_text()) if packages == []: dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Package not found!") dialog.run() dialog.destroy() else: packages = unique_array([p.get_cp() for p in packages]) if len(packages) == 1: self.jump_to(packages[0]) else: SearchWindow(self.window, packages, self.jump_to) def jump_to (self, cp): """Is called when we want to jump to a specific package.""" PackageWindow(self.window, cp, self.queue) def main (self): """Main.""" gobject.threads_init() # now subthreads can run normally, but are not allowed to touch the GUI. If threads should change sth there - use gobject.idle_add(). # for more informations on threading and gtk: http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq20.006.htp gtk.main() def blocked_dialog (blocked, blocks): dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, blocked+" is blocked by "+blocks+".\nPlease unmerge the blocking package.") dialog.run() dialog.destroy()