mirror of
https://git.yoctoproject.org/poky
synced 2026-04-28 06:32:34 +02:00
After the reparse we were setting the model to reflect the values before the reparse was triggered but clearing the internal variables used to test whether these values are set, leading to the UI erroneously reporting that selections had not been made. (Bitbake rev: 656eafe0f2c9ec7730d33e15705b8c720f787c49) Signed-off-by: Joshua Lock <josh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
1110 lines
44 KiB
Python
1110 lines
44 KiB
Python
#
|
|
# BitBake Graphical GTK User Interface
|
|
#
|
|
# Copyright (C) 2011 Intel Corporation
|
|
#
|
|
# Authored by Joshua Lock <josh@linux.intel.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License version 2 as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
import glib
|
|
import gobject
|
|
import gtk
|
|
from bb.ui.crumbs.tasklistmodel import TaskListModel, BuildRep
|
|
from bb.ui.crumbs.hobeventhandler import HobHandler
|
|
from bb.ui.crumbs.configurator import Configurator
|
|
from bb.ui.crumbs.hobprefs import HobPrefs
|
|
from bb.ui.crumbs.layereditor import LayerEditor
|
|
from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
|
|
from bb.ui.crumbs.hig import CrumbsDialog
|
|
import xmlrpclib
|
|
import logging
|
|
import Queue
|
|
|
|
extraCaches = ['bb.cache_extra:HobRecipeInfo']
|
|
|
|
class MainWindow (gtk.Window):
|
|
|
|
def __init__(self, taskmodel, handler, configurator, prefs, layers, mach):
|
|
gtk.Window.__init__(self)
|
|
# global state
|
|
self.curr_mach = mach
|
|
self.machine_handler_id = None
|
|
self.image_combo_id = None
|
|
self.generating = False
|
|
self.files_to_clean = []
|
|
self.selected_image = None
|
|
self.selected_packages = None
|
|
self.stopping = False
|
|
|
|
self.model = taskmodel
|
|
self.model.connect("tasklist-populated", self.update_model)
|
|
self.model.connect("image-changed", self.image_changed_string_cb)
|
|
self.handler = handler
|
|
self.configurator = configurator
|
|
self.prefs = prefs
|
|
self.layers = layers
|
|
self.save_path = None
|
|
self.dirty = False
|
|
self.build_succeeded = False
|
|
|
|
self.connect("delete-event", self.destroy_window)
|
|
self.set_title("Image Creator")
|
|
self.set_icon_name("applications-development")
|
|
self.set_default_size(1000, 650)
|
|
|
|
self.build = RunningBuild(sequential=True)
|
|
self.build.connect("build-failed", self.running_build_failed_cb)
|
|
self.build.connect("build-succeeded", self.running_build_succeeded_cb)
|
|
self.build.connect("build-started", self.build_started_cb)
|
|
self.build.connect("build-complete", self.build_complete_cb)
|
|
|
|
vbox = gtk.VBox(False, 0)
|
|
vbox.set_border_width(0)
|
|
vbox.show()
|
|
self.add(vbox)
|
|
self.menu = self.create_menu()
|
|
vbox.pack_start(self.menu, False)
|
|
createview = self.create_build_gui()
|
|
self.back = None
|
|
self.cancel = None
|
|
buildview = self.view_build_gui()
|
|
self.nb = gtk.Notebook()
|
|
self.nb.append_page(createview)
|
|
self.nb.append_page(buildview)
|
|
self.nb.set_current_page(0)
|
|
self.nb.set_show_tabs(False)
|
|
vbox.pack_start(self.nb, expand=True, fill=True)
|
|
|
|
def destroy_window(self, widget, event):
|
|
self.quit()
|
|
|
|
def menu_quit(self, action):
|
|
self.quit()
|
|
|
|
def quit(self):
|
|
if self.dirty and len(self.model.contents):
|
|
question = "Would you like to save your customisations?"
|
|
dialog = CrumbsDialog(self, question, gtk.STOCK_DIALOG_WARNING)
|
|
dialog.add_buttons(gtk.STOCK_NO, gtk.RESPONSE_NO,
|
|
gtk.STOCK_YES, gtk.RESPONSE_YES)
|
|
resp = dialog.run()
|
|
dialog.destroy()
|
|
if resp == gtk.RESPONSE_YES:
|
|
if not self.save_path:
|
|
self.get_save_path()
|
|
|
|
if self.save_path:
|
|
self.save_recipe_file()
|
|
rep = self.model.get_build_rep()
|
|
rep.writeRecipe(self.save_path, self.model)
|
|
|
|
# Prevent the busy cursor being shown after hob exits if quit is called
|
|
# whilst the busy cursor is set
|
|
self.set_busy_cursor(False)
|
|
|
|
self.handler.remove_temp_dir()
|
|
|
|
gtk.main_quit()
|
|
|
|
"""
|
|
In the case of a fatal error give the user as much information as possible
|
|
and then exit.
|
|
"""
|
|
def fatal_error_cb(self, handler, errormsg, phase):
|
|
lbl = "<b>Error!</b>\nThere was an unrecoverable error during the"
|
|
lbl = lbl + " <i>%s</i> phase of BitBake. This must be" % phase
|
|
lbl = lbl + " rectified before the GUI will function. The error"
|
|
lbl = lbl + " message which which caused this is:\n\n\"%s\"" % errormsg
|
|
dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
|
|
dialog.add_button("Exit", gtk.RESPONSE_OK)
|
|
response = dialog.run()
|
|
dialog.destroy()
|
|
self.set_busy_cursor(False)
|
|
gtk.main_quit()
|
|
|
|
def scroll_tv_cb(self, model, path, it, view):
|
|
view.scroll_to_cell(path)
|
|
|
|
def running_build_succeeded_cb(self, running_build):
|
|
self.build_succeeded = True
|
|
|
|
def running_build_failed_cb(self, running_build):
|
|
self.build_succeeded = False
|
|
|
|
def image_changed_string_cb(self, model, new_image):
|
|
self.selected_image = new_image
|
|
# disconnect the image combo's signal handler
|
|
if self.image_combo_id:
|
|
self.image_combo.disconnect(self.image_combo_id)
|
|
self.image_combo_id = None
|
|
cnt = 0
|
|
it = self.model.images.get_iter_first()
|
|
while it:
|
|
path = self.model.images.get_path(it)
|
|
if self.model.images[path][self.model.COL_NAME] == new_image:
|
|
self.image_combo.set_active(cnt)
|
|
break
|
|
it = self.model.images.iter_next(it)
|
|
cnt = cnt + 1
|
|
# Reconnect the signal handler
|
|
if not self.image_combo_id:
|
|
self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
|
|
|
|
def image_changed_cb(self, combo):
|
|
model = self.image_combo.get_model()
|
|
it = self.image_combo.get_active_iter()
|
|
if it:
|
|
path = model.get_path(it)
|
|
# Firstly, deselect the previous image
|
|
userp, _ = self.model.get_selected_packages()
|
|
self.model.reset()
|
|
# Now select the new image and save its path in case we
|
|
# change the image later
|
|
self.toggle_package(path, model, image=True)
|
|
if len(userp):
|
|
self.model.set_selected_packages(userp)
|
|
self.selected_image = model[path][self.model.COL_NAME]
|
|
|
|
def reload_triggered_cb(self, handler, image, packages):
|
|
if image:
|
|
self.selected_image = image
|
|
if len(packages):
|
|
self.selected_packages = packages.split()
|
|
|
|
def data_generated(self, handler):
|
|
self.generating = False
|
|
self.enable_widgets()
|
|
|
|
def machine_combo_changed_cb(self, combo, handler):
|
|
mach = combo.get_active_text()
|
|
if mach != self.curr_mach:
|
|
self.curr_mach = mach
|
|
# Flush this straight to the file as MACHINE is changed
|
|
# independently of other 'Preferences'
|
|
self.configurator.setConfVar('MACHINE', mach)
|
|
self.configurator.writeConf()
|
|
handler.set_machine(mach)
|
|
handler.reload_data()
|
|
|
|
def update_machines(self, handler, machines):
|
|
active = 0
|
|
# disconnect the signal handler before updating the combo model
|
|
if self.machine_handler_id:
|
|
self.machine_combo.disconnect(self.machine_handler_id)
|
|
self.machine_handler_id = None
|
|
|
|
model = self.machine_combo.get_model()
|
|
if model:
|
|
model.clear()
|
|
|
|
for machine in machines:
|
|
self.machine_combo.append_text(machine)
|
|
if machine == self.curr_mach:
|
|
self.machine_combo.set_active(active)
|
|
active = active + 1
|
|
|
|
self.machine_handler_id = self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler)
|
|
|
|
def set_busy_cursor(self, busy=True):
|
|
"""
|
|
Convenience method to set the cursor to a spinner when executing
|
|
a potentially lengthy process.
|
|
A busy value of False will set the cursor back to the default
|
|
left pointer.
|
|
"""
|
|
if busy:
|
|
cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
|
|
else:
|
|
# TODO: presumably the default cursor is different on RTL
|
|
# systems. Can we determine the default cursor? Or at least
|
|
# the cursor which is set before we change it?
|
|
cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
|
|
window = self.get_root_window()
|
|
window.set_cursor(cursor)
|
|
|
|
def busy_idle_func(self):
|
|
if self.generating:
|
|
self.progress.pulse()
|
|
return True
|
|
else:
|
|
if not self.image_combo_id:
|
|
self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
|
|
self.progress.set_text("Loaded")
|
|
self.progress.set_fraction(0.0)
|
|
self.set_busy_cursor(False)
|
|
return False
|
|
|
|
def busy(self, handler):
|
|
self.generating = True
|
|
self.progress.set_text("Loading...")
|
|
self.set_busy_cursor()
|
|
if self.image_combo_id:
|
|
self.image_combo.disconnect(self.image_combo_id)
|
|
self.image_combo_id = None
|
|
self.progress.pulse()
|
|
gobject.timeout_add (100, self.busy_idle_func)
|
|
self.disable_widgets()
|
|
|
|
def enable_widgets(self):
|
|
self.menu.set_sensitive(True)
|
|
self.machine_combo.set_sensitive(True)
|
|
self.image_combo.set_sensitive(True)
|
|
self.nb.set_sensitive(True)
|
|
self.contents_tree.set_sensitive(True)
|
|
|
|
def disable_widgets(self):
|
|
self.menu.set_sensitive(False)
|
|
self.machine_combo.set_sensitive(False)
|
|
self.image_combo.set_sensitive(False)
|
|
self.nb.set_sensitive(False)
|
|
self.contents_tree.set_sensitive(False)
|
|
|
|
def update_model(self, model):
|
|
# We want the packages model to be alphabetised and sortable so create
|
|
# a TreeModelSort to use in the view
|
|
pkgsaz_model = gtk.TreeModelSort(self.model.packages_model())
|
|
pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
|
|
# Unset default sort func so that we only toggle between A-Z and
|
|
# Z-A sorting
|
|
pkgsaz_model.set_default_sort_func(None)
|
|
self.pkgsaz_tree.set_model(pkgsaz_model)
|
|
|
|
self.image_combo.set_model(self.model.images_model())
|
|
# Without this the image combo is incorrectly sized on first load of the GUI
|
|
self.image_combo.set_active(0)
|
|
self.image_combo.set_active(-1)
|
|
|
|
if not self.image_combo_id:
|
|
self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
|
|
|
|
# We want the contents to be alphabetised so create a TreeModelSort to
|
|
# use in the view
|
|
contents_model = gtk.TreeModelSort(self.model.contents_model())
|
|
contents_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
|
|
# Unset default sort func so that we only toggle between A-Z and
|
|
# Z-A sorting
|
|
contents_model.set_default_sort_func(None)
|
|
self.contents_tree.set_model(contents_model)
|
|
self.tasks_tree.set_model(self.model.tasks_model())
|
|
|
|
if self.selected_image:
|
|
if self.image_combo_id:
|
|
self.image_combo.disconnect(self.image_combo_id)
|
|
self.image_combo_id = None
|
|
self.model.set_selected_image(self.selected_image)
|
|
if not self.image_combo_id:
|
|
self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
|
|
|
|
if self.selected_packages:
|
|
self.model.set_selected_packages(self.selected_packages)
|
|
|
|
def reset_clicked_cb(self, button):
|
|
lbl = "<b>Reset your selections?</b>\n\nAny new changes you have made will be lost"
|
|
dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
|
|
dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
|
dialog.add_button("Reset", gtk.RESPONSE_OK)
|
|
response = dialog.run()
|
|
dialog.destroy()
|
|
if response == gtk.RESPONSE_OK:
|
|
self.reset_build()
|
|
self.search.set_text("")
|
|
self.selected_image = None
|
|
return
|
|
|
|
def reset_build(self):
|
|
if self.image_combo_id:
|
|
self.image_combo.disconnect(self.image_combo_id)
|
|
self.image_combo_id = None
|
|
self.image_combo.set_active(-1)
|
|
self.model.reset()
|
|
if not self.image_combo_id:
|
|
self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
|
|
|
|
def layers_cb(self, action):
|
|
resp = self.layers.run()
|
|
self.layers.save_current_layers()
|
|
self.layers.hide()
|
|
|
|
def add_layer_cb(self, action):
|
|
self.layers.find_layer(self)
|
|
self.layers.save_current_layers()
|
|
|
|
def preferences_cb(self, action):
|
|
resp = self.prefs.run()
|
|
self.prefs.write_changes()
|
|
self.prefs.hide()
|
|
|
|
def about_cb(self, action):
|
|
about = gtk.AboutDialog()
|
|
about.set_name("Image Creator")
|
|
about.set_copyright("Copyright (C) 2011 Intel Corporation")
|
|
about.set_authors(["Joshua Lock <josh@linux.intel.com>"])
|
|
about.set_logo_icon_name("applications-development")
|
|
about.run()
|
|
about.destroy()
|
|
|
|
def save_recipe_file(self):
|
|
rep = self.model.get_build_rep()
|
|
rep.writeRecipe(self.save_path, self.model)
|
|
self.dirty = False
|
|
|
|
def get_save_path(self):
|
|
chooser = gtk.FileChooserDialog(title=None, parent=self,
|
|
action=gtk.FILE_CHOOSER_ACTION_SAVE,
|
|
buttons=(gtk.STOCK_CANCEL,
|
|
gtk.RESPONSE_CANCEL,
|
|
gtk.STOCK_SAVE,
|
|
gtk.RESPONSE_OK,))
|
|
chooser.set_current_name("myimage.bb")
|
|
response = chooser.run()
|
|
if response == gtk.RESPONSE_OK:
|
|
save_path = chooser.get_filename()
|
|
else:
|
|
save_path = None
|
|
chooser.destroy()
|
|
self.save_path = save_path
|
|
|
|
def save_cb(self, action):
|
|
if not self.save_path:
|
|
self.get_save_path()
|
|
if self.save_path:
|
|
self.save_recipe_file()
|
|
|
|
def save_as_cb(self, action):
|
|
self.get_save_path()
|
|
if self.save_path:
|
|
self.save_recipe_file()
|
|
|
|
def open_cb(self, action):
|
|
chooser = gtk.FileChooserDialog(title=None, parent=self,
|
|
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
|
buttons=(gtk.STOCK_CANCEL,
|
|
gtk.RESPONSE_CANCEL,
|
|
gtk.STOCK_OPEN,
|
|
gtk.RESPONSE_OK))
|
|
response = chooser.run()
|
|
rep = BuildRep(None, None, None)
|
|
if response == gtk.RESPONSE_OK:
|
|
rep.loadRecipe(chooser.get_filename())
|
|
chooser.destroy()
|
|
self.model.load_image_rep(rep)
|
|
self.dirty = False
|
|
|
|
def bake_clicked_cb(self, button):
|
|
build_image = True
|
|
|
|
rep = self.model.get_build_rep()
|
|
|
|
# If no base image and no user selected packages don't build anything
|
|
if not self.selected_image and not len(rep.userpkgs):
|
|
lbl = "<b>No selections made</b>\nYou have not made any selections"
|
|
lbl = lbl + " so there isn't anything to bake at this time."
|
|
dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
|
|
dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
|
|
dialog.run()
|
|
dialog.destroy()
|
|
return
|
|
# Else if no base image, ask whether to just build packages or whether
|
|
# to build a rootfs with the selected packages in
|
|
elif not self.selected_image:
|
|
lbl = "<b>Build empty image or only packages?</b>\nA base image"
|
|
lbl = lbl + " has not been selected.\n\'Empty image' will build"
|
|
lbl = lbl + " an image with only the selected packages as its"
|
|
lbl = lbl + " contents.\n'Packages Only' will build only the"
|
|
lbl = lbl + " selected packages, no image will be created"
|
|
dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
|
|
dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
|
dialog.add_button("Empty Image", gtk.RESPONSE_OK)
|
|
dialog.add_button("Packages Only", gtk.RESPONSE_YES)
|
|
response = dialog.run()
|
|
dialog.destroy()
|
|
if response == gtk.RESPONSE_CANCEL:
|
|
return
|
|
elif response == gtk.RESPONSE_YES:
|
|
build_image = False
|
|
elif response == gtk.RESPONSE_OK:
|
|
rep.base_image = "empty"
|
|
|
|
# Ensure at least one value is set in IMAGE_FSTYPES.
|
|
have_selected_fstype = False
|
|
if (len(self.prefs.selected_image_types) and
|
|
len(self.prefs.selected_image_types[0])):
|
|
have_selected_fstype = True
|
|
|
|
if build_image and not have_selected_fstype:
|
|
lbl = "<b>No image output type selected</b>\nThere is no image output"
|
|
lbl = lbl + " selected for the build. Please set an output image type"
|
|
lbl = lbl + " in the preferences (Edit -> Preferences)."
|
|
dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
|
|
dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
|
|
dialog.run()
|
|
dialog.destroy()
|
|
return
|
|
elif build_image:
|
|
self.handler.make_temp_dir()
|
|
recipepath = self.handler.get_temp_recipe_path(rep.base_image)
|
|
image_name = recipepath.rstrip(".bb")
|
|
path, sep, image_name = image_name.rpartition("/")
|
|
|
|
image = []
|
|
image.append(image_name)
|
|
|
|
rep.writeRecipe(recipepath, self.model)
|
|
# In the case where we saved the file for the purpose of building
|
|
# it we should then delete it so that the users workspace doesn't
|
|
# contain files they haven't explicitly saved there.
|
|
if not self.save_path:
|
|
self.files_to_clean.append(recipepath)
|
|
|
|
self.handler.build_targets(image, self.configurator)
|
|
else:
|
|
self.handler.build_targets(self.model.get_selected_pn(), self.configurator, "packages")
|
|
|
|
# Disable parts of the menu which shouldn't be used whilst building
|
|
self.set_menus_sensitive(False)
|
|
self.nb.set_current_page(1)
|
|
|
|
def set_menus_sensitive(self, sensitive):
|
|
self.add_layers_action.set_sensitive(sensitive)
|
|
self.layers_action.set_sensitive(sensitive)
|
|
self.prefs_action.set_sensitive(sensitive)
|
|
self.open_action.set_sensitive(sensitive)
|
|
|
|
def back_button_clicked_cb(self, button):
|
|
self.toggle_createview()
|
|
|
|
def toggle_createview(self):
|
|
self.set_menus_sensitive(True)
|
|
self.build.reset()
|
|
self.nb.set_current_page(0)
|
|
|
|
def build_complete_cb(self, running_build):
|
|
# Have the handler process BB events again
|
|
self.handler.building = False
|
|
self.stopping = False
|
|
self.back.connect("clicked", self.back_button_clicked_cb)
|
|
self.back.set_sensitive(True)
|
|
self.cancel.set_sensitive(False)
|
|
for f in self.files_to_clean:
|
|
try:
|
|
os.remove(f)
|
|
except OSError:
|
|
pass
|
|
self.files_to_clean.remove(f)
|
|
self.files_to_clean = []
|
|
|
|
lbl = "<b>Build completed</b>\n\nClick 'Edit Image' to start another build or 'View Messages' to view the messages output during the build."
|
|
if self.handler.build_type == "image" and self.build_succeeded:
|
|
deploy = self.handler.get_image_deploy_dir()
|
|
lbl = lbl + "\n<a href=\"file://%s\" title=\"%s\">Browse folder of built images</a>." % (deploy, deploy)
|
|
|
|
dialog = CrumbsDialog(self, lbl)
|
|
dialog.add_button("View Messages", gtk.RESPONSE_CANCEL)
|
|
dialog.add_button("Edit Image", gtk.RESPONSE_OK)
|
|
response = dialog.run()
|
|
dialog.destroy()
|
|
if response == gtk.RESPONSE_OK:
|
|
self.toggle_createview()
|
|
|
|
def build_started_cb(self, running_build):
|
|
self.back.set_sensitive(False)
|
|
self.cancel.set_sensitive(True)
|
|
|
|
def include_gplv3_cb(self, toggle):
|
|
excluded = toggle.get_active()
|
|
self.handler.toggle_gplv3(excluded)
|
|
|
|
def change_bb_threads(self, spinner):
|
|
val = spinner.get_value_as_int()
|
|
self.handler.set_bbthreads(val)
|
|
|
|
def change_make_threads(self, spinner):
|
|
val = spinner.get_value_as_int()
|
|
self.handler.set_pmake(val)
|
|
|
|
def toggle_toolchain(self, check):
|
|
enabled = check.get_active()
|
|
self.handler.toggle_toolchain(enabled)
|
|
|
|
def toggle_headers(self, check):
|
|
enabled = check.get_active()
|
|
self.handler.toggle_toolchain_headers(enabled)
|
|
|
|
def toggle_package_idle_cb(self, opath, image):
|
|
"""
|
|
As the operations which we're calling on the model can take
|
|
a significant amount of time (in the order of seconds) during which
|
|
the GUI is unresponsive as the main loop is blocked perform them in
|
|
an idle function which at least enables us to set the busy cursor
|
|
before the UI is blocked giving the appearance of being responsive.
|
|
"""
|
|
# Whether the item is currently included
|
|
inc = self.model[opath][self.model.COL_INC]
|
|
# FIXME: due to inpredictability of the removal of packages we are
|
|
# temporarily disabling this feature
|
|
# If the item is already included, mark it for removal then
|
|
# the sweep_up() method finds affected items and marks them
|
|
# appropriately
|
|
# if inc:
|
|
# self.model.mark(opath)
|
|
# self.model.sweep_up()
|
|
# # If the item isn't included, mark it for inclusion
|
|
# else:
|
|
if not inc:
|
|
self.model.include_item(item_path=opath,
|
|
binb="User Selected",
|
|
image_contents=image)
|
|
|
|
self.set_busy_cursor(False)
|
|
return False
|
|
|
|
def toggle_package(self, path, model, image=False):
|
|
inc = model[path][self.model.COL_INC]
|
|
# Warn user before removing included packages
|
|
if inc:
|
|
# FIXME: due to inpredictability of the removal of packages we are
|
|
# temporarily disabling this feature
|
|
return
|
|
# pn = model[path][self.model.COL_NAME]
|
|
# revdeps = self.model.find_reverse_depends(pn)
|
|
# if len(revdeps):
|
|
# lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone and all packages which depend on this will be removed\nPackages which depend on %s include %s." % (pn, pn, ", ".join(revdeps).rstrip(","))
|
|
# else:
|
|
# lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone." % pn
|
|
# dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
|
|
# dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
|
# dialog.add_button("Remove", gtk.RESPONSE_OK)
|
|
# response = dialog.run()
|
|
# dialog.destroy()
|
|
# if response == gtk.RESPONSE_CANCEL:
|
|
# return
|
|
|
|
self.set_busy_cursor()
|
|
# Convert path to path in original model
|
|
opath = model.convert_path_to_child_path(path)
|
|
# This is a potentially length call which can block the
|
|
# main loop, therefore do the work in an idle func to keep
|
|
# the UI responsive
|
|
glib.idle_add(self.toggle_package_idle_cb, opath, image)
|
|
|
|
self.dirty = True
|
|
|
|
def toggle_include_cb(self, cell, path, tv):
|
|
model = tv.get_model()
|
|
self.toggle_package(path, model)
|
|
|
|
def toggle_pkg_include_cb(self, cell, path, tv):
|
|
# there's an extra layer of models in the packages case.
|
|
sort_model = tv.get_model()
|
|
cpath = sort_model.convert_path_to_child_path(path)
|
|
self.toggle_package(cpath, sort_model.get_model())
|
|
|
|
def pkgsaz(self):
|
|
vbox = gtk.VBox(False, 6)
|
|
vbox.show()
|
|
self.pkgsaz_tree = gtk.TreeView()
|
|
self.pkgsaz_tree.set_headers_visible(True)
|
|
self.pkgsaz_tree.set_headers_clickable(True)
|
|
self.pkgsaz_tree.set_enable_search(True)
|
|
self.pkgsaz_tree.set_search_column(0)
|
|
self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
|
|
|
|
col = gtk.TreeViewColumn('Package')
|
|
col.set_clickable(True)
|
|
col.set_sort_column_id(self.model.COL_NAME)
|
|
col.set_min_width(220)
|
|
col1 = gtk.TreeViewColumn('Description')
|
|
col1.set_resizable(True)
|
|
col1.set_min_width(360)
|
|
col2 = gtk.TreeViewColumn('License')
|
|
col2.set_resizable(True)
|
|
col2.set_clickable(True)
|
|
col2.set_sort_column_id(self.model.COL_LIC)
|
|
col2.set_min_width(170)
|
|
col3 = gtk.TreeViewColumn('Group')
|
|
col3.set_clickable(True)
|
|
col3.set_sort_column_id(self.model.COL_GROUP)
|
|
col4 = gtk.TreeViewColumn('Included')
|
|
col4.set_min_width(80)
|
|
col4.set_max_width(90)
|
|
col4.set_sort_column_id(self.model.COL_INC)
|
|
|
|
self.pkgsaz_tree.append_column(col)
|
|
self.pkgsaz_tree.append_column(col1)
|
|
self.pkgsaz_tree.append_column(col2)
|
|
self.pkgsaz_tree.append_column(col3)
|
|
self.pkgsaz_tree.append_column(col4)
|
|
|
|
cell = gtk.CellRendererText()
|
|
cell1 = gtk.CellRendererText()
|
|
cell1.set_property('width-chars', 20)
|
|
cell2 = gtk.CellRendererText()
|
|
cell2.set_property('width-chars', 20)
|
|
cell3 = gtk.CellRendererText()
|
|
cell4 = gtk.CellRendererToggle()
|
|
cell4.set_property('activatable', True)
|
|
cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsaz_tree)
|
|
|
|
col.pack_start(cell, True)
|
|
col1.pack_start(cell1, True)
|
|
col2.pack_start(cell2, True)
|
|
col3.pack_start(cell3, True)
|
|
col4.pack_end(cell4, True)
|
|
|
|
col.set_attributes(cell, text=self.model.COL_NAME)
|
|
col1.set_attributes(cell1, text=self.model.COL_DESC)
|
|
col2.set_attributes(cell2, text=self.model.COL_LIC)
|
|
col3.set_attributes(cell3, text=self.model.COL_GROUP)
|
|
col4.set_attributes(cell4, active=self.model.COL_INC)
|
|
|
|
self.pkgsaz_tree.show()
|
|
|
|
scroll = gtk.ScrolledWindow()
|
|
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
|
scroll.set_shadow_type(gtk.SHADOW_IN)
|
|
scroll.add(self.pkgsaz_tree)
|
|
vbox.pack_start(scroll, True, True, 0)
|
|
|
|
hb = gtk.HBox(False, 0)
|
|
hb.show()
|
|
self.search = gtk.Entry()
|
|
self.search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
|
|
self.search.connect("icon-release", self.search_entry_clear_cb)
|
|
self.search.show()
|
|
self.pkgsaz_tree.set_search_entry(self.search)
|
|
hb.pack_end(self.search, False, False, 0)
|
|
label = gtk.Label("Search packages:")
|
|
label.show()
|
|
hb.pack_end(label, False, False, 6)
|
|
vbox.pack_start(hb, False, False, 0)
|
|
|
|
return vbox
|
|
|
|
def search_entry_clear_cb(self, entry, icon_pos, event):
|
|
entry.set_text("")
|
|
|
|
def tasks(self):
|
|
vbox = gtk.VBox(False, 6)
|
|
vbox.show()
|
|
self.tasks_tree = gtk.TreeView()
|
|
self.tasks_tree.set_headers_visible(True)
|
|
self.tasks_tree.set_headers_clickable(False)
|
|
self.tasks_tree.set_enable_search(True)
|
|
self.tasks_tree.set_search_column(0)
|
|
self.tasks_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
|
|
|
|
col = gtk.TreeViewColumn('Package Collection')
|
|
col.set_min_width(430)
|
|
col1 = gtk.TreeViewColumn('Description')
|
|
col1.set_min_width(430)
|
|
col2 = gtk.TreeViewColumn('Include')
|
|
col2.set_min_width(70)
|
|
col2.set_max_width(80)
|
|
|
|
self.tasks_tree.append_column(col)
|
|
self.tasks_tree.append_column(col1)
|
|
self.tasks_tree.append_column(col2)
|
|
|
|
cell = gtk.CellRendererText()
|
|
cell1 = gtk.CellRendererText()
|
|
cell2 = gtk.CellRendererToggle()
|
|
cell2.set_property('activatable', True)
|
|
cell2.connect("toggled", self.toggle_include_cb, self.tasks_tree)
|
|
|
|
col.pack_start(cell, True)
|
|
col1.pack_start(cell1, True)
|
|
col2.pack_end(cell2, True)
|
|
|
|
col.set_attributes(cell, text=self.model.COL_NAME)
|
|
col1.set_attributes(cell1, text=self.model.COL_DESC)
|
|
col2.set_attributes(cell2, active=self.model.COL_INC)
|
|
|
|
self.tasks_tree.show()
|
|
|
|
scroll = gtk.ScrolledWindow()
|
|
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
|
scroll.set_shadow_type(gtk.SHADOW_IN)
|
|
scroll.add(self.tasks_tree)
|
|
vbox.pack_start(scroll, True, True, 0)
|
|
|
|
hb = gtk.HBox(False, 0)
|
|
hb.show()
|
|
search = gtk.Entry()
|
|
search.show()
|
|
self.tasks_tree.set_search_entry(search)
|
|
hb.pack_end(search, False, False, 0)
|
|
label = gtk.Label("Search collections:")
|
|
label.show()
|
|
hb.pack_end(label, False, False, 6)
|
|
vbox.pack_start(hb, False, False, 0)
|
|
|
|
return vbox
|
|
|
|
def cancel_build(self, button):
|
|
if self.stopping:
|
|
lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once,"
|
|
lbl = lbl + " would you like to 'Force Stop' the build?\n\n"
|
|
lbl = lbl + "This will stop the build as quickly as possible but may"
|
|
lbl = lbl + " well leave your build directory in an unusable state"
|
|
lbl = lbl + " that requires manual steps to fix.\n"
|
|
dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
|
|
dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
|
dialog.add_button("Force Stop", gtk.RESPONSE_YES)
|
|
else:
|
|
lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this"
|
|
lbl = lbl + " build?\n\n'Force Stop' will stop the build as quickly as"
|
|
lbl = lbl + " possible but may well leave your build directory in an"
|
|
lbl = lbl + " unusable state that requires manual steps to fix.\n\n"
|
|
lbl = lbl + "'Stop' will stop the build as soon as all in"
|
|
lbl = lbl + " progress build tasks are finished. However if a"
|
|
lbl = lbl + " lengthy compilation phase is in progress this may take"
|
|
lbl = lbl + " some time."
|
|
dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
|
|
dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
|
dialog.add_button("Stop", gtk.RESPONSE_OK)
|
|
dialog.add_button("Force Stop", gtk.RESPONSE_YES)
|
|
response = dialog.run()
|
|
dialog.destroy()
|
|
if response != gtk.RESPONSE_CANCEL:
|
|
self.stopping = True
|
|
if response == gtk.RESPONSE_OK:
|
|
self.handler.cancel_build()
|
|
elif response == gtk.RESPONSE_YES:
|
|
self.handler.cancel_build(True)
|
|
|
|
def view_build_gui(self):
|
|
vbox = gtk.VBox(False, 12)
|
|
vbox.set_border_width(6)
|
|
vbox.show()
|
|
build_tv = RunningBuildTreeView(readonly=True)
|
|
build_tv.show()
|
|
build_tv.set_model(self.build.model)
|
|
self.build.model.connect("row-inserted", self.scroll_tv_cb, build_tv)
|
|
scrolled_view = gtk.ScrolledWindow ()
|
|
scrolled_view.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
|
scrolled_view.add(build_tv)
|
|
scrolled_view.show()
|
|
vbox.pack_start(scrolled_view, expand=True, fill=True)
|
|
hbox = gtk.HBox(False, 12)
|
|
hbox.show()
|
|
vbox.pack_start(hbox, expand=False, fill=False)
|
|
self.back = gtk.Button("Back")
|
|
self.back.show()
|
|
self.back.set_sensitive(False)
|
|
hbox.pack_start(self.back, expand=False, fill=False)
|
|
self.cancel = gtk.Button("Stop Build")
|
|
self.cancel.connect("clicked", self.cancel_build)
|
|
self.cancel.show()
|
|
hbox.pack_end(self.cancel, expand=False, fill=False)
|
|
|
|
return vbox
|
|
|
|
def create_menu(self):
|
|
menu_items = '''<ui>
|
|
<menubar name="MenuBar">
|
|
<menu action="File">
|
|
<menuitem action="Save"/>
|
|
<menuitem action="Save As"/>
|
|
<menuitem action="Open"/>
|
|
<separator/>
|
|
<menuitem action="AddLayer" label="Add Layer"/>
|
|
<separator/>
|
|
<menuitem action="Quit"/>
|
|
</menu>
|
|
<menu action="Edit">
|
|
<menuitem action="Layers" label="Layers"/>
|
|
<menuitem action="Preferences"/>
|
|
</menu>
|
|
<menu action="Help">
|
|
<menuitem action="About"/>
|
|
</menu>
|
|
</menubar>
|
|
</ui>'''
|
|
|
|
uimanager = gtk.UIManager()
|
|
accel = uimanager.get_accel_group()
|
|
self.add_accel_group(accel)
|
|
|
|
actions = gtk.ActionGroup('ImageCreator')
|
|
self.actions = actions
|
|
actions.add_actions([('Quit', gtk.STOCK_QUIT, None, None, None, self.menu_quit,),
|
|
('File', None, '_File'),
|
|
('Save', gtk.STOCK_SAVE, None, None, None, self.save_cb),
|
|
('Save As', gtk.STOCK_SAVE_AS, None, None, None, self.save_as_cb),
|
|
('Edit', None, '_Edit'),
|
|
('Help', None, '_Help'),
|
|
('About', gtk.STOCK_ABOUT, None, None, None, self.about_cb)])
|
|
|
|
self.add_layers_action = gtk.Action('AddLayer', 'Add Layer', None, None)
|
|
self.add_layers_action.connect("activate", self.add_layer_cb)
|
|
self.actions.add_action(self.add_layers_action)
|
|
self.layers_action = gtk.Action('Layers', 'Layers', None, None)
|
|
self.layers_action.connect("activate", self.layers_cb)
|
|
self.actions.add_action(self.layers_action)
|
|
self.prefs_action = gtk.Action('Preferences', 'Preferences', None, None)
|
|
self.prefs_action.connect("activate", self.preferences_cb)
|
|
self.actions.add_action(self.prefs_action)
|
|
self.open_action = gtk.Action('Open', 'Open', None, None)
|
|
self.open_action.connect("activate", self.open_cb)
|
|
self.actions.add_action(self.open_action)
|
|
|
|
uimanager.insert_action_group(actions, 0)
|
|
uimanager.add_ui_from_string(menu_items)
|
|
|
|
menubar = uimanager.get_widget('/MenuBar')
|
|
menubar.show_all()
|
|
|
|
return menubar
|
|
|
|
def info_button_clicked_cb(self, button):
|
|
info = "We cannot accurately predict the image contents before they are built so instead a best"
|
|
info = info + " attempt at estimating what the image will contain is listed."
|
|
dialog = CrumbsDialog(self, info, gtk.STOCK_DIALOG_INFO)
|
|
dialog.add_buttons(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
|
|
resp = dialog.run()
|
|
dialog.destroy()
|
|
|
|
def create_build_gui(self):
|
|
vbox = gtk.VBox(False, 12)
|
|
vbox.set_border_width(6)
|
|
vbox.show()
|
|
|
|
hbox = gtk.HBox(False, 12)
|
|
hbox.show()
|
|
vbox.pack_start(hbox, expand=False, fill=False)
|
|
|
|
label = gtk.Label("Machine:")
|
|
label.show()
|
|
hbox.pack_start(label, expand=False, fill=False, padding=6)
|
|
self.machine_combo = gtk.combo_box_new_text()
|
|
self.machine_combo.show()
|
|
self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.")
|
|
hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6)
|
|
label = gtk.Label("Base image:")
|
|
label.show()
|
|
hbox.pack_start(label, expand=False, fill=False, padding=6)
|
|
self.image_combo = gtk.ComboBox()
|
|
self.image_combo.show()
|
|
self.image_combo.set_tooltip_text("Selects the image on which to base the created image")
|
|
image_combo_cell = gtk.CellRendererText()
|
|
self.image_combo.pack_start(image_combo_cell, True)
|
|
self.image_combo.add_attribute(image_combo_cell, 'text', self.model.COL_NAME)
|
|
hbox.pack_start(self.image_combo, expand=False, fill=False, padding=6)
|
|
self.progress = gtk.ProgressBar()
|
|
self.progress.set_size_request(250, -1)
|
|
hbox.pack_end(self.progress, expand=False, fill=False, padding=6)
|
|
|
|
ins = gtk.Notebook()
|
|
vbox.pack_start(ins, expand=True, fill=True)
|
|
ins.set_show_tabs(True)
|
|
label = gtk.Label("Packages")
|
|
label.show()
|
|
ins.append_page(self.pkgsaz(), tab_label=label)
|
|
label = gtk.Label("Package Collections")
|
|
label.show()
|
|
ins.append_page(self.tasks(), tab_label=label)
|
|
ins.set_current_page(0)
|
|
ins.show_all()
|
|
|
|
hbox = gtk.HBox(False, 1)
|
|
hbox.show()
|
|
label = gtk.Label("Estimated image contents:")
|
|
self.model.connect("contents-changed", self.update_package_count_cb, label)
|
|
label.set_property("xalign", 0.00)
|
|
label.show()
|
|
hbox.pack_start(label, expand=False, fill=False, padding=6)
|
|
info = gtk.Button("?")
|
|
info.set_tooltip_text("What does this mean?")
|
|
info.show()
|
|
info.connect("clicked", self.info_button_clicked_cb)
|
|
hbox.pack_start(info, expand=False, fill=False, padding=6)
|
|
vbox.pack_start(hbox, expand=False, fill=False, padding=6)
|
|
con = self.contents()
|
|
con.show()
|
|
vbox.pack_start(con, expand=True, fill=True)
|
|
|
|
bbox = gtk.HButtonBox()
|
|
bbox.set_spacing(12)
|
|
bbox.set_layout(gtk.BUTTONBOX_END)
|
|
bbox.show()
|
|
vbox.pack_start(bbox, expand=False, fill=False)
|
|
reset = gtk.Button("Reset")
|
|
reset.connect("clicked", self.reset_clicked_cb)
|
|
reset.show()
|
|
bbox.add(reset)
|
|
bake = gtk.Button("Bake")
|
|
bake.connect("clicked", self.bake_clicked_cb)
|
|
bake.show()
|
|
bbox.add(bake)
|
|
|
|
return vbox
|
|
|
|
def update_package_count_cb(self, model, count, label):
|
|
lbl = "Estimated image contents (%s packages):" % count
|
|
label.set_text(lbl)
|
|
|
|
def contents(self):
|
|
self.contents_tree = gtk.TreeView()
|
|
self.contents_tree.set_headers_visible(True)
|
|
self.contents_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
|
|
|
|
# allow searching in the package column
|
|
self.contents_tree.set_search_column(0)
|
|
self.contents_tree.set_enable_search(True)
|
|
|
|
col = gtk.TreeViewColumn('Package')
|
|
col.set_sort_column_id(0)
|
|
col.set_min_width(430)
|
|
col1 = gtk.TreeViewColumn('Brought in by')
|
|
col1.set_resizable(True)
|
|
col1.set_min_width(430)
|
|
|
|
self.contents_tree.append_column(col)
|
|
self.contents_tree.append_column(col1)
|
|
|
|
cell = gtk.CellRendererText()
|
|
cell1 = gtk.CellRendererText()
|
|
cell1.set_property('width-chars', 20)
|
|
|
|
col.pack_start(cell, True)
|
|
col1.pack_start(cell1, True)
|
|
|
|
col.set_attributes(cell, text=self.model.COL_NAME)
|
|
col1.set_attributes(cell1, text=self.model.COL_BINB)
|
|
|
|
self.contents_tree.show()
|
|
|
|
scroll = gtk.ScrolledWindow()
|
|
scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
|
scroll.set_shadow_type(gtk.SHADOW_IN)
|
|
scroll.add(self.contents_tree)
|
|
|
|
return scroll
|
|
|
|
def main (server, eventHandler):
|
|
gobject.threads_init()
|
|
|
|
# NOTE: For now we require that the user run with pre and post files to
|
|
# read and store configuration set in the GUI.
|
|
# We hope to adjust this long term as tracked in Yocto Bugzilla #1441
|
|
# http://bugzilla.pokylinux.org/show_bug.cgi?id=1441
|
|
reqfiles = 0
|
|
dep_files = server.runCommand(["getVariable", "__depends"]) or set()
|
|
dep_files.union(server.runCommand(["getVariable", "__base_depends"]) or set())
|
|
for f in dep_files:
|
|
if f[0].endswith("hob-pre.conf"):
|
|
reqfiles = reqfiles + 1
|
|
elif f[0].endswith("hob-post.conf"):
|
|
reqfiles = reqfiles + 1
|
|
if reqfiles == 2:
|
|
break
|
|
if reqfiles < 2:
|
|
print("""The hob UI requires a pre file named hob-pre.conf and a post
|
|
file named hob-post.conf to store and read its configuration from. Please run
|
|
hob with these files, i.e.\n
|
|
\bitbake -u hob -r conf/hob-pre.conf -R conf/hob-post.conf""")
|
|
return
|
|
|
|
taskmodel = TaskListModel()
|
|
configurator = Configurator()
|
|
handler = HobHandler(taskmodel, server)
|
|
mach = server.runCommand(["getVariable", "MACHINE"])
|
|
sdk_mach = server.runCommand(["getVariable", "SDKMACHINE"])
|
|
# If SDKMACHINE not set the default SDK_ARCH is used so we
|
|
# should represent that in the GUI
|
|
if not sdk_mach:
|
|
sdk_mach = server.runCommand(["getVariable", "SDK_ARCH"])
|
|
distro = server.runCommand(["getVariable", "DISTRO"])
|
|
if not distro:
|
|
distro = "defaultsetup"
|
|
bbthread = server.runCommand(["getVariable", "BB_NUMBER_THREADS"])
|
|
if not bbthread:
|
|
bbthread = 1
|
|
else:
|
|
bbthread = int(bbthread)
|
|
pmake = server.runCommand(["getVariable", "PARALLEL_MAKE"])
|
|
if not pmake:
|
|
pmake = 1
|
|
else:
|
|
# The PARALLEL_MAKE variable will be of the format: "-j 3" and we only
|
|
# want a number for the spinner, so strip everything from the variable
|
|
# up to and including the space
|
|
pmake = int(pmake.lstrip("-j "))
|
|
|
|
selected_image_types = server.runCommand(["getVariable", "IMAGE_FSTYPES"])
|
|
all_image_types = server.runCommand(["getVariable", "IMAGE_TYPES"])
|
|
|
|
pclasses = server.runCommand(["getVariable", "PACKAGE_CLASSES"]).split(" ")
|
|
# NOTE: we're only supporting one value for PACKAGE_CLASSES being set
|
|
# this seems OK because we're using the first package format set in
|
|
# PACKAGE_CLASSES and that's the package manager used for the rootfs
|
|
pkg, sep, pclass = pclasses[0].rpartition("_")
|
|
|
|
incompatible = server.runCommand(["getVariable", "INCOMPATIBLE_LICENSE"])
|
|
gplv3disabled = False
|
|
if incompatible and incompatible.lower().find("gplv3") != -1:
|
|
gplv3disabled = True
|
|
|
|
build_toolchain = bool(server.runCommand(["getVariable", "HOB_BUILD_TOOLCHAIN"]))
|
|
handler.toggle_toolchain(build_toolchain)
|
|
build_headers = bool(server.runCommand(["getVariable", "HOB_BUILD_TOOLCHAIN_HEADERS"]))
|
|
handler.toggle_toolchain_headers(build_headers)
|
|
|
|
prefs = HobPrefs(configurator, handler, sdk_mach, distro, pclass,
|
|
pmake, bbthread, selected_image_types, all_image_types,
|
|
gplv3disabled, build_toolchain, build_headers)
|
|
layers = LayerEditor(configurator, None)
|
|
window = MainWindow(taskmodel, handler, configurator, prefs, layers, mach)
|
|
prefs.set_parent_window(window)
|
|
layers.set_parent_window(window)
|
|
window.show_all ()
|
|
handler.connect("machines-updated", window.update_machines)
|
|
handler.connect("sdk-machines-updated", prefs.update_sdk_machines)
|
|
handler.connect("distros-updated", prefs.update_distros)
|
|
handler.connect("package-formats-found", prefs.update_package_formats)
|
|
handler.connect("generating-data", window.busy)
|
|
handler.connect("data-generated", window.data_generated)
|
|
handler.connect("reload-triggered", window.reload_triggered_cb)
|
|
configurator.connect("layers-loaded", layers.load_current_layers)
|
|
configurator.connect("layers-changed", handler.reload_data)
|
|
handler.connect("config-found", configurator.configFound)
|
|
handler.connect("fatal-error", window.fatal_error_cb)
|
|
|
|
try:
|
|
# kick the while thing off
|
|
handler.current_command = handler.CFG_PATH_LOCAL
|
|
server.runCommand(["findConfigFilePath", "local.conf"])
|
|
except xmlrpclib.Fault:
|
|
print("XMLRPC Fault getting commandline:\n %s" % x)
|
|
return 1
|
|
|
|
# This timeout function regularly probes the event queue to find out if we
|
|
# have any messages waiting for us.
|
|
gobject.timeout_add (100,
|
|
handler.event_handle_idle_func,
|
|
eventHandler,
|
|
window.build,
|
|
window.progress)
|
|
|
|
try:
|
|
gtk.main()
|
|
except EnvironmentError as ioerror:
|
|
# ignore interrupted io
|
|
if ioerror.args[0] == 4:
|
|
pass
|
|
finally:
|
|
server.runCommand(["stateStop"])
|
|
|