Example #1
0
 def __init__(self):
     self.jobservice = RemoteJobService()
     self.active_job = None
     self.active_index = 0
     self.waiting = False
     self.builder = gtk.Builder()
     
     # https://bugzilla.gnome.org/show_bug.cgi?id=574520
     self.builder.set_translation_domain('jobs-admin')
     
     try:
         self.builder.add_from_file('jobs-admin.ui')
     except:
         self.builder.add_from_file('/usr/share/jobs-admin/jobs-admin.ui')
     
     objects = [
         'act_job_start',
         'act_job_stop',
         'act_protected',
         'act_refresh',
         'cr_auto',
         'frm_settings',
         'lbl_job_mode',
         'lbl_job_starts',
         'lbl_job_status',
         'lbl_job_stops',
         'lbl_job_type',
         'lst_jobs',
         'menu_edit',
         'menu_help',
         'menu_jobs',
         'mi_about',
         'mi_quit',
         'tv_jobs',
         'vbox_right',
         'win_main',
     ]
     for obj in objects:
         self.__dict__[obj] = self.builder.get_object(obj)
     
     self.win_main.connect('destroy', gtk.main_quit)
     self.mi_quit.connect('activate', gtk.main_quit)
     
     self.tv_jobs_sel = self.tv_jobs.get_selection()
     self.tv_jobs.connect('cursor-changed', self.show_job_info)
     self.cr_auto.connect('toggled', self.auto_toggle)
     
     self.act_job_start.connect('activate', self.job_start)
     self.act_job_stop.connect('activate', self.job_stop)
     self.act_refresh.connect('activate', self.load_jobs)
     self.act_protected.connect('activate', self.set_protected)
     self.mi_about.connect('activate', self.show_about)
     
     self.lbl_job_starts.connect('activate-link', self.link_clicked)
     self.lbl_job_stops.connect('activate-link', self.link_clicked)
     
     self.infomanager = InfoManager(self.vbox_right)
     self.context = ContextMenu(self.act_job_start, self.act_job_stop)
     self.tv_jobs.connect('button-press-event', self.context.popup)
Example #2
0
class JobsAdminUI:

    def __init__(self):
        self.jobservice = RemoteJobService()
        self.active_job = None
        self.active_index = 0
        self.waiting = False
        self.builder = gtk.Builder()
        
        # https://bugzilla.gnome.org/show_bug.cgi?id=574520
        self.builder.set_translation_domain('jobs-admin')
        
        try:
            self.builder.add_from_file('jobs-admin.ui')
        except:
            self.builder.add_from_file('/usr/share/jobs-admin/jobs-admin.ui')
        
        objects = [
            'act_job_start',
            'act_job_stop',
            'act_protected',
            'act_refresh',
            'cr_auto',
            'frm_settings',
            'lbl_job_mode',
            'lbl_job_starts',
            'lbl_job_status',
            'lbl_job_stops',
            'lbl_job_type',
            'lst_jobs',
            'menu_edit',
            'menu_help',
            'menu_jobs',
            'mi_about',
            'mi_quit',
            'tv_jobs',
            'vbox_right',
            'win_main',
        ]
        for obj in objects:
            self.__dict__[obj] = self.builder.get_object(obj)
        
        self.win_main.connect('destroy', gtk.main_quit)
        self.mi_quit.connect('activate', gtk.main_quit)
        
        self.tv_jobs_sel = self.tv_jobs.get_selection()
        self.tv_jobs.connect('cursor-changed', self.show_job_info)
        self.cr_auto.connect('toggled', self.auto_toggle)
        
        self.act_job_start.connect('activate', self.job_start)
        self.act_job_stop.connect('activate', self.job_stop)
        self.act_refresh.connect('activate', self.load_jobs)
        self.act_protected.connect('activate', self.set_protected)
        self.mi_about.connect('activate', self.show_about)
        
        self.lbl_job_starts.connect('activate-link', self.link_clicked)
        self.lbl_job_stops.connect('activate-link', self.link_clicked)
        
        self.infomanager = InfoManager(self.vbox_right)
        self.context = ContextMenu(self.act_job_start, self.act_job_stop)
        self.tv_jobs.connect('button-press-event', self.context.popup)
        
    def load_jobs(self, *args):
        """Load the job listing and populate the liststore."""
        self.lst_jobs.clear()
        protect = not self.act_protected.props.active
        for jobname, job in self.jobservice.get_all_jobs(protect).iteritems():
            weight = 700 if job.running else 400
            if job.running:
                markup = "<b>{1}</b>\n<small>{0}</small>"
            else:
                markup = "{1}\n<small>{0}</small>"
            if job.description:
                markup = markup.format(jobname, job.description)
            else:
                markup = markup.format('', jobname)
            # update these if the liststore changes structure
            self.lst_jobs.append((jobname, markup, job.running,
                                  job.automatic, not job.protected))
        self.lst_jobs.set_sort_column_id(0, gtk.SORT_ASCENDING)
        # put the cursor on something to trigger show_job_info
        self.tv_jobs.set_cursor(self.active_index)
    
    def show_job_info(self, *args):
        """Update the UI with the currently selected job's info."""
        # we can't call on dbus while an async call is active, otherwise
        # this will block. also don't want to change active_index while
        # async calls are depending on it
        if self.waiting:
            return
        model, treeiter = self.tv_jobs_sel.get_selected()
        self.active_index = self.tv_jobs_sel.get_selected_rows()[1][0][0]
        jobname = self.lst_jobs.get_value(treeiter, 0)
        self.active_job = job = self.jobservice.jobs[jobname]
        # enable/disable some actions
        self.act_job_start.props.sensitive = not job.running and not job.protected
        self.act_job_stop.props.sensitive = job.running and not job.protected
        # backend type
        self.lbl_job_type.props.label = BACKEND_NAMES[job.backend] \
                if job.backend in BACKEND_NAMES else _("Unknown")
        # starton/stopon vary slightly by backend
        starton = []
        stopon = []
        if job.backend == 'sysv':
            if job.starton:
                starton.append(_("on runlevels {list}").format(
                        list=", ".join(job.starton)))
            if job.stopon:
                stopon.append(_("on runlevels {list}").format(
                        list=", ".join(job.stopon)))
        elif job.backend == 'upstart_0_6':
            for mode, field in ((job.starton, starton), (job.stopon, stopon)):
                for item in mode:
                    if 'runlevel' in item:
                        field.append(item)
                    else:
                        action, jobname = item.split()
                        if jobname in self.jobservice.jobs:
                            jobname = "<a href='{0}'>{0}</a>".format(jobname)
                        field.append("on {0} {1}".format(action, jobname))
        self.lbl_job_starts.props.label = _("Unknown")
        self.lbl_job_stops.props.label = _("Unknown")
        if starton: self.lbl_job_starts.props.label = ", ".join(starton)
        if stopon: self.lbl_job_stops.props.label = ", ".join(stopon)
        self.lbl_job_status.props.label = _("Running") if job.running else _("Not running")
        self.lbl_job_mode.props.label = _("Automatic") if job.automatic else _("Manual")
        # load settings
        self.show_settings()
        # clear the infobar
        if self.active_index != self.infomanager.active_index:
            self.infomanager.hide()
    
    def auto_toggle(self, cr, path):
        self.active_index = int(path)
        row = self.lst_jobs[int(path)]
        job = self.jobservice.jobs[row[0]]
        if job.protected:
            return
        self.set_waiting()
        # handlers
        def reply():
            self.set_waiting(False)
            self.load_jobs()
            self.show_job_info()
            # infobar
            self.infomanager.hide()
            if job.automatic and not job.running:
                lbl = _("The job has been placed into automatic mode.\nWould you like to start it?")
                self.infomanager.show(self.active_index, lbl, _("_Start"), self.job_start)
            elif not job.automatic and job.running:
                lbl = _("The job has been placed into manual mode.\nWould you like to stop it?")
                self.infomanager.show(self.active_index, lbl, _("S_top"), self.job_stop)
        def error(e):
            if not 'DeniedByPolicy' in e._dbus_error_name:
                lbl = _("Could not save state:") + "\n\t" + e.get_dbus_message()
                lbl += "\n" + _("You can start or stop the job anyway.")
                if job.running:
                    self.infomanager.show(self.active_index, lbl, _("S_top"), self.job_stop, msgtype=gtk.MESSAGE_WARNING)
                else:
                    self.infomanager.show(self.active_index, lbl, _("_Start"), self.job_start, msgtype=gtk.MESSAGE_WARNING)
            self.set_waiting(False)
        # async call
        if job.automatic:
            job.disable(reply_handler=reply, error_handler=error)
        else:
            job.enable(reply_handler=reply, error_handler=error)

    def job_start(self, *args):
        """Start a job."""
        self.set_waiting()
        def reply():
            self.set_waiting(False)
            self.infomanager.hide()
            self.load_jobs()
        def error(e):
            if not 'DeniedByPolicy' in e._dbus_error_name:
                lbl = _("A problem has occurred:") + "\n\t" + e.get_dbus_message()
                if self.active_job.settings:
                    lbl += "\n" + _("Try changing the job settings and try again.")
                self.infomanager.show(self.active_index, lbl, msgtype=gtk.MESSAGE_WARNING)
            self.set_waiting(False)
        self.active_job.start(reply_handler=reply, error_handler=error)
    
    def job_stop(self, *args):
        """Stop a job."""
        self.set_waiting()
        def reply():
            self.set_waiting(False)
            self.infomanager.hide()
            self.load_jobs()
        def error(e):
            self.set_waiting(False)
            if not 'DeniedByPolicy' in e._dbus_error_name:
                raise e
        self.active_job.stop(reply_handler=reply, error_handler=error)
    
    def job_restart(self, *args):
        """Stop a job, then start it again."""
        self.job_stop()
        self.job_start()
    
    def show_settings(self):
        """Load the settings table for this job."""
        # only show for non-protected jobs with published settings
        if not self.active_job.settings or self.active_job.protected:
            self.frm_settings.hide_all()
            return
        tbl = SettingsTable(self.active_job, self.active_index, self.infomanager)
        # apply button
        hbb = gtk.HButtonBox()
        hbb.props.layout_style = gtk.BUTTONBOX_END
        save = gtk.Button(stock='gtk-apply')
        save.connect('clicked', self.apply_settings, tbl)
        hbb.pack_start(save)
        row = tbl.props.n_rows
        tbl.attach(hbb, 0, 2, row, row + 1)
        # only enable apply button when settings have been activated
        save.props.sensitive = False
        for w in tbl:
            # for some reason a combobox doesn't emit focus-in-event...
            if w.__class__.__name__ == 'ComboBox':
                w.connect('changed', self.enable_settings_apply, None, save)
            w.connect('focus-in-event', self.enable_settings_apply, save)
        # remove the old table if present
        child = self.frm_settings.get_child()
        if child:
            self.frm_settings.remove(child)
            child.destroy()
        self.frm_settings.add(tbl)
        self.frm_settings.show_all()
    
    def enable_settings_apply(self, widget, event, save):
        """Enable the Apply button. Signal callback for 'focus'."""
        save.props.sensitive = True
        save.props.can_default = True
        save.props.has_default = True
    
    def apply_settings(self, button, tbl):
        """Call on the supplied SettingsTable to save settings and reload."""
        self.set_waiting()
        def reply():
            self.set_waiting(False)
            self.load_jobs()
            # infobar
            self.infomanager.hide()
            if self.active_job.running and not '/' in self.active_job.name:
                lbl = _("The settings have been saved.\nRestart the job to apply these changes.")
                self.infomanager.show(self.active_index, lbl, _("_Restart"), self.job_restart)
        def error(e):
            self.set_waiting(False)
            if not 'DeniedByPolicy' in e._dbus_error_name:
                raise e
        tbl.apply_settings(reply, error)
        
    def set_waiting(self, waiting=True):
        """Disable the UI or re-enable it for an action."""
        self.waiting = waiting
        cursor = gtk.gdk.Cursor(gtk.gdk.WATCH if waiting else gtk.gdk.ARROW)
        self.win_main.get_window().set_cursor(cursor)
        for obj in self.win_main.get_children():
            obj.props.sensitive = not waiting
        
    def show_about(self, *args):
        dlg = gtk.AboutDialog()
        dlg.props.name = "jobs-admin"
        dlg.props.website = "https://launchpad.net/jobsadmin"
        try:
            from JobsAdmin.info import version, description
            dlg.props.version = version
            dlg.props.comments = description
        except: pass
        dlg.run()
        dlg.destroy()
    
    def set_protected(self, action):
        self.active_index = 0
        self.load_jobs()
    
    def link_clicked(self, label, uri):
        """Action for any link clicked to change to that job."""
        self.active_index = 0
        for job in self.lst_jobs:
            if uri == job[0]:
                break
            self.active_index += 1
        self.tv_jobs.set_cursor(self.active_index)
        return True