# -*- Mode: Python; coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
##
## Copyright (C) 2013 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## 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., or visit: http://www.gnu.org/.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
import collections
import glib
import gobject
import gtk
from stoqlib.api import api
from stoqlib.gui.base.dialogs import BasicDialog, run_dialog
from stoqlib.gui.dialogs.feedbackdialog import FeedbackDialog
from stoqlib.gui.dialogs.progressdialog import ProgressDialog
from stoqlib.gui.stockicons import (STOQ_FEEDBACK,
STOQ_STATUS_NA,
STOQ_STATUS_OK,
STOQ_STATUS_WARNING,
STOQ_STATUS_ERROR)
from stoqlib.lib.message import warning
from stoqlib.lib.translation import stoqlib_gettext as _
from stoqlib.lib.threadutils import terminate_thread
from stoq.lib.status import ResourceStatus, ResourceStatusManager
# FIXME: Improve those strings
_status_mapper = {
None: (
gtk.STOCK_REFRESH,
_("Checking status...")),
ResourceStatus.STATUS_NA: (
STOQ_STATUS_NA,
_("Status not available")),
ResourceStatus.STATUS_OK: (
STOQ_STATUS_OK,
_("Everything is running fine")),
ResourceStatus.STATUS_WARNING: (
STOQ_STATUS_WARNING,
_("Some services are in a warning state")),
ResourceStatus.STATUS_ERROR: (
STOQ_STATUS_ERROR,
_("Some services are in an error state")),
}
[docs]class StatusDialog(BasicDialog):
size = (700, 400)
title = _("System Status")
def __init__(self, *args, **kwargs):
kwargs.setdefault('size', self.size)
kwargs.setdefault('title', self.title)
super(StatusDialog, self).__init__(*args, **kwargs)
self._manager = ResourceStatusManager.get_instance()
self._manager.connect('status-changed',
self._on_manager__status_changed)
self._manager.connect('action-finished',
self._on_manager__action_finished)
with api.new_store() as store:
user = api.get_current_user(store)
self._is_admin = user.profile.check_app_permission(u'admin')
self._widgets = {}
self._setup_ui()
#
# Private
#
def _setup_ui(self):
self.cancel_button.hide()
for child in self.vbox.get_children():
self.vbox.remove(child)
self._refresh_btn = gtk.Button(stock=gtk.STOCK_REFRESH)
action_area = self.toplevel.get_action_area()
action_area.pack_start(self._refresh_btn, True, True, 6)
action_area.set_child_secondary(self._refresh_btn, True)
self._refresh_btn.connect('clicked', self._on_refresh_btn__clicked)
self._refresh_btn.show()
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_AUTOMATIC)
self.vbox.add(sw)
viewport = gtk.Viewport()
viewport.set_shadow_type(gtk.SHADOW_NONE)
sw.add(viewport)
alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
alignment.set_padding(6, 6, 6, 6)
viewport.add(alignment)
vbox = gtk.VBox(spacing=12)
alignment.add(vbox)
resources = self._manager.resources.items()
for i, (name, resource) in enumerate(resources):
hbox = gtk.HBox(spacing=6)
img = gtk.Image()
hbox.pack_start(img, False, True)
lbl = gtk.Label()
hbox.pack_start(lbl, False, True)
buttonbox = gtk.HButtonBox()
hbox.pack_end(buttonbox, False, True)
self._widgets[name] = (img, lbl, buttonbox)
vbox.pack_start(hbox, False, True, 6)
if i < len(resources) - 1:
vbox.pack_start(gtk.HSeparator(), False, True, 0)
self.vbox.show_all()
self._update_ui()
def _update_ui(self):
running_action = self._manager.running_action
self._refresh_btn.set_sensitive(running_action is None)
for name, resource in self._manager.resources.iteritems():
img, lbl, buttonbox = self._widgets[name]
status_stock, _ignored = _status_mapper[resource.status]
img.set_from_stock(status_stock, gtk.ICON_SIZE_LARGE_TOOLBAR)
if resource.reason and resource.reason_long:
text = "<b>%s</b>: %s\n<i>%s</i>" % (
api.escape(resource.label),
api.escape(resource.reason),
api.escape(resource.reason_long))
elif resource.reason:
text = "<b>%s</b>: %s" % (
api.escape(resource.label),
api.escape(resource.reason))
else:
text = _("Status not available...")
for child in buttonbox.get_children():
buttonbox.remove(child)
for action in resource.get_actions():
btn = gtk.Button(action.label)
if running_action is not None:
btn.set_sensitive(False)
if action.admin_only and not self._is_admin:
btn.set_sensitive(False)
btn.set_tooltip_text(
_("Only admins can execute this action"))
# If the action is the running action, add a spinner together
# with the label to indicate that it is running
if action == running_action:
spinner = gtk.Spinner()
hbox = gtk.HBox(spacing=6)
child = btn.get_child()
btn.remove(child)
hbox.add(child)
hbox.add(spinner)
btn.add(hbox)
spinner.start()
hbox.show_all()
btn.show()
btn.connect('clicked', self._on_action_btn__clicked, action)
buttonbox.add(btn)
lbl.set_markup(text)
def _handle_action(self, action):
retval = self._manager.handle_action(action)
if action.threaded:
thread = retval
msg = _('Executing "%s". This might take a while...') % (
action.label, )
progress_dialog = ProgressDialog(msg, pulse=True)
progress_dialog.start(wait=100)
progress_dialog.set_transient_for(self.get_toplevel())
progress_dialog.set_title(action.resource.label)
progress_dialog.connect(
'cancel', lambda d: terminate_thread(thread))
while thread.is_alive():
if gtk.events_pending():
gtk.main_iteration(False)
progress_dialog.stop()
self._update_ui()
#
# Callbacks
#
def _on_manager__status_changed(self, manager, status):
self._update_ui()
def _on_manager__action_finished(self, manager, action, retval):
if isinstance(retval, Exception):
warning(_('An error happened when executing "%s"') % (action.label, ),
str(retval))
self._update_ui()
def _on_action_btn__clicked(self, btn, action):
self._handle_action(action)
def _on_refresh_btn__clicked(self, btn):
self._manager.refresh_and_notify()
[docs]class ShellStatusbar(gtk.Statusbar):
__gtype_name__ = 'ShellStatusbar'
def __init__(self, window):
gtk.Statusbar.__init__(self)
self._disable_border()
self.message_area = self._create_message_area()
self._create_default_widgets()
self.shell_window = window
def _disable_border(self):
# Disable border on statusbar
children = self.get_children()
if children and isinstance(children[0], gtk.Frame):
frame = children[0]
frame.set_shadow_type(gtk.SHADOW_NONE)
def _create_message_area(self):
for child in self.get_children():
child.hide()
area = gtk.HBox(False, 4)
self.add(area)
area.show()
return area
def _create_default_widgets(self):
alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
# FIXME: These looks good on Mac, might need to tweak
# on Linux to look good
alignment.set_padding(2, 3, 5, 0)
self.message_area.pack_start(alignment, True, True)
alignment.show()
widget_area = gtk.HBox(False, 0)
alignment.add(widget_area)
widget_area.show()
self._text_label = gtk.Label()
self._text_label.set_alignment(0.0, 0.5)
widget_area.pack_start(self._text_label, True, True)
self._text_label.show()
vsep = gtk.VSeparator()
widget_area.pack_start(vsep, False, False, 0)
vsep.show()
self._feedback_button = gtk.Button(_('Feedback'))
image = gtk.Image()
image.set_from_stock(STOQ_FEEDBACK, gtk.ICON_SIZE_MENU)
self._feedback_button.set_image(image)
image.show()
self._feedback_button.set_can_focus(False)
self._feedback_button.connect('clicked',
self._on_feedback__clicked)
self._feedback_button.set_relief(gtk.RELIEF_NONE)
widget_area.pack_start(self._feedback_button, False, False, 0)
self._feedback_button.show()
vsep = gtk.VSeparator()
widget_area.pack_start(vsep, False, False, 0)
vsep.show()
self._status_button = StatusButton()
self._status_button.connect('clicked',
self._on_status_button__clicked)
self.message_area.pack_end(self._status_button, False, False, 0)
self._status_button.show()
[docs] def do_text_popped(self, ctx, text):
self._text_label.set_label(text)
[docs] def do_text_pushed(self, ctx, text):
self._text_label.set_label(text)
#
# Callbacks
#
def _on_feedback__clicked(self, button):
if self.shell_window.current_app:
screen = self.shell_window.current_app.app_name + ' application'
else:
screen = 'launcher'
run_dialog(FeedbackDialog, self.get_toplevel(), screen)
def _on_status_button__clicked(self, button):
run_dialog(StatusDialog, self.get_toplevel())
gobject.type_register(ShellStatusbar)