Source code for kiwi.ui.dialogs

#
# Kiwi: a Framework and Enhanced Widgets for Python
#
# Copyright (C) 2005-2007 Async Open Source
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Author(s): Johan Dahlin <jdahlin@async.com.br>
#

import os
import gettext

import atk
import gtk

__all__ = ['error', 'info', 'messagedialog', 'warning', 'yesno', 'save',
           'open', 'HIGAlertDialog', 'BaseDialog']

_ = lambda m: gettext.dgettext('kiwi', m)

_IMAGE_TYPES = {
    gtk.MESSAGE_INFO: gtk.STOCK_DIALOG_INFO,
    gtk.MESSAGE_WARNING : gtk.STOCK_DIALOG_WARNING,
    gtk.MESSAGE_QUESTION : gtk.STOCK_DIALOG_QUESTION,
    gtk.MESSAGE_ERROR : gtk.STOCK_DIALOG_ERROR,
}

_BUTTON_TYPES = {
    gtk.BUTTONS_NONE: (),
    gtk.BUTTONS_OK: (gtk.STOCK_OK, gtk.RESPONSE_OK,),
    gtk.BUTTONS_CLOSE: (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE,),
    gtk.BUTTONS_CANCEL: (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,),
    gtk.BUTTONS_YES_NO: (gtk.STOCK_NO, gtk.RESPONSE_NO,
                         gtk.STOCK_YES, gtk.RESPONSE_YES),
    gtk.BUTTONS_OK_CANCEL: (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                            gtk.STOCK_OK, gtk.RESPONSE_OK)
    }

[docs]class HIGAlertDialog(gtk.Dialog): def __init__(self, parent, flags, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_NONE): if not type in _IMAGE_TYPES: raise TypeError( "type must be one of: %s", ', '.join(_IMAGE_TYPES.keys())) if not buttons in _BUTTON_TYPES: raise TypeError( "buttons be one of: %s", ', '.join(_BUTTON_TYPES.keys())) gtk.Dialog.__init__(self, '', parent, flags) self.set_deletable(False) self.set_border_width(5) self.set_resizable(False) # Some window managers (ION) displays a default title (???) if # the specified one is empty, workaround this by setting it # to a single space instead self.set_title(" ") self.set_skip_taskbar_hint(True) # It seems like get_accessible is not available on windows, go figure if hasattr(self, 'get_accessible'): self.get_accessible().set_role(atk.ROLE_ALERT) self._primary_label = gtk.Label() self._secondary_label = gtk.Label() self._details_label = gtk.Label() self._image = gtk.image_new_from_stock(_IMAGE_TYPES[type], gtk.ICON_SIZE_DIALOG) self._image.set_alignment(0.5, 0.0) self._primary_label.set_use_markup(True) for label in (self._primary_label, self._secondary_label, self._details_label): label.set_line_wrap(True) label.set_selectable(True) label.set_alignment(0.0, 0.5) hbox = gtk.HBox(False, 12) hbox.set_border_width(5) hbox.pack_start(self._image, False, False) vbox = gtk.VBox(False, 0) hbox.pack_start(vbox, False, False) vbox.pack_start(self._primary_label, False, False) vbox.pack_start(self._secondary_label, False, False) self._expander = gtk.expander_new_with_mnemonic( _("Show more _details")) self._expander.set_spacing(6) self._expander.add(self._details_label) vbox.pack_start(self._expander, False, False) self.get_content_area().pack_start(hbox, False, False) hbox.show_all() self._expander.hide() self.add_buttons(*_BUTTON_TYPES[buttons]) self.label_vbox = vbox
[docs] def set_primary(self, text, bold=True): if bold: text = "<span weight=\"bold\" size=\"larger\">%s</span>" % text self._primary_label.set_markup(text)
[docs] def set_secondary(self, text): self._secondary_label.set_markup(text)
[docs] def set_details(self, text, use_markup=False): if use_markup: self._details_label.set_markup(text) else: self._details_label.set_text(text) self._expander.show()
[docs] def set_details_widget(self, widget): self._expander.remove(self._details_label) self._expander.add(widget) widget.show() self._expander.show()
[docs]class BaseDialog(gtk.Dialog): def __init__(self, parent=None, title='', flags=0, buttons=()): if parent and not isinstance(parent, gtk.Window): raise TypeError("parent needs to be None or a gtk.Window subclass") if not flags and parent: flags &= (gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT) gtk.Dialog.__init__(self, title=title, parent=parent, flags=flags, buttons=buttons) self.set_border_width(6) self.set_has_separator(False) self.vbox.set_spacing(6)
[docs]def messagedialog(dialog_type, short, long=None, parent=None, buttons=gtk.BUTTONS_OK, default=-1): """Create and show a MessageDialog. :param dialog_type: one of constants - gtk.MESSAGE_INFO - gtk.MESSAGE_WARNING - gtk.MESSAGE_QUESTION - gtk.MESSAGE_ERROR :param short: A header text to be inserted in the dialog. :param long: A long description of message. :param parent: The parent widget of this dialog :type parent: a gtk.Window subclass :param buttons: The button type that the dialog will be display, one of the constants: - gtk.BUTTONS_NONE - gtk.BUTTONS_OK - gtk.BUTTONS_CLOSE - gtk.BUTTONS_CANCEL - gtk.BUTTONS_YES_NO - gtk.BUTTONS_OK_CANCEL or a tuple or 2-sized tuples representing label and response. If label is a stock-id a stock icon will be displayed. :param default: optional default response id """ if buttons in (gtk.BUTTONS_NONE, gtk.BUTTONS_OK, gtk.BUTTONS_CLOSE, gtk.BUTTONS_CANCEL, gtk.BUTTONS_YES_NO, gtk.BUTTONS_OK_CANCEL): dialog_buttons = buttons buttons = [] else: if buttons is not None and type(buttons) != tuple: raise TypeError( "buttons must be a GtkButtonsTypes constant or a tuple") dialog_buttons = gtk.BUTTONS_NONE if parent and not isinstance(parent, gtk.Window): raise TypeError("parent must be a gtk.Window subclass") d = HIGAlertDialog(parent=parent, flags=gtk.DIALOG_MODAL, type=dialog_type, buttons=dialog_buttons) if buttons: for text, response in buttons: d.add_buttons(text, response) d.set_primary(short) if long: if isinstance(long, gtk.Widget): d.set_details_widget(long) elif isinstance(long, basestring): d.set_details(long) else: raise TypeError( "long must be a gtk.Widget or a string, not %r" % long) if default != -1: d.set_default_response(default) if parent: d.set_transient_for(parent) d.set_modal(True) response = d.run() d.destroy() return response
def _simple(type, short, long=None, parent=None, buttons=gtk.BUTTONS_OK, default=-1): if buttons == gtk.BUTTONS_OK: default = gtk.RESPONSE_OK return messagedialog(type, short, long, parent=parent, buttons=buttons, default=default)
[docs]def error(short, long=None, parent=None, buttons=gtk.BUTTONS_OK, default=-1): return _simple(gtk.MESSAGE_ERROR, short, long, parent=parent, buttons=buttons, default=default)
[docs]def info(short, long=None, parent=None, buttons=gtk.BUTTONS_OK, default=-1): return _simple(gtk.MESSAGE_INFO, short, long, parent=parent, buttons=buttons, default=default)
[docs]def warning(short, long=None, parent=None, buttons=gtk.BUTTONS_OK, default=-1): return _simple(gtk.MESSAGE_WARNING, short, long, parent=parent, buttons=buttons, default=default)
[docs]def yesno(text, parent=None, default=gtk.RESPONSE_YES, buttons=gtk.BUTTONS_YES_NO): return messagedialog(gtk.MESSAGE_WARNING, text, None, parent, buttons=buttons, default=default)
[docs]def open(title='', parent=None, patterns=None, folder=None, filter=None, with_file_chooser=False): """Displays an open dialog. :param title: the title of the folder, defaults to 'Select folder' :param parent: parent gtk.Window or None :param patterns: a list of pattern strings ['*.py', '*.pl'] or None :param folder: initial folder or None :param filter: a filter to use or None, is incompatible with patterns """ ffilter = filter if patterns and ffilter: raise TypeError("Can't use patterns and filter at the same time") filechooser = gtk.FileChooserDialog(title or _('Open'), parent, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) if patterns or ffilter: if not ffilter: ffilter = gtk.FileFilter() for pattern in patterns: ffilter.add_pattern(pattern) if type(ffilter) != list: ffilter = [ffilter] for f in ffilter: filechooser.add_filter(f) filechooser.set_default_response(gtk.RESPONSE_OK) if folder: filechooser.set_current_folder(folder) response = filechooser.run() if response != gtk.RESPONSE_OK: if with_file_chooser: return None, filechooser filechooser.destroy() return path = filechooser.get_filename() if path and os.access(path, os.R_OK): if with_file_chooser: return path, filechooser filechooser.destroy() return path abspath = os.path.abspath(path) error(_('Could not open file "%s"') % abspath, _('The file "%s" could not be opened. ' 'Permission denied.') % abspath) if with_file_chooser: return None, filechooser filechooser.destroy() return
def selectfolder(title='', parent=None, folder=None): """Displays a select folder dialog. :param title: the title of the folder, defaults to 'Select folder' :param parent: parent gtk.Window or None :param folder: initial folder or None """ filechooser = gtk.FileChooserDialog( title or _('Select folder'), parent, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) if folder: filechooser.set_current_folder(folder) filechooser.set_default_response(gtk.RESPONSE_OK) response = filechooser.run() if response != gtk.RESPONSE_OK: filechooser.destroy() return path = filechooser.get_filename() if path and os.access(path, os.R_OK | os.X_OK): filechooser.destroy() return path abspath = os.path.abspath(path) error(_('Could not select folder "%s"') % abspath, _('The folder "%s" could not be selected. ' 'Permission denied.') % abspath) filechooser.destroy() return def ask_overwrite(filename, parent=None): submsg1 = _('A file named "%s" already exists') % os.path.abspath(filename) submsg2 = _('Do you wish to replace it with the current one?') text = ('<span weight="bold" size="larger">%s</span>\n\n%s\n' % (submsg1, submsg2)) result = messagedialog(gtk.MESSAGE_ERROR, text, parent=parent, buttons=((gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL), (_("Replace"), gtk.RESPONSE_YES))) return result == gtk.RESPONSE_YES
[docs]def save(title='', parent=None, current_name='', folder=None): """Displays a save dialog.""" filechooser = gtk.FileChooserDialog(title or _('Save'), parent, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) if current_name: filechooser.set_current_name(current_name) filechooser.set_default_response(gtk.RESPONSE_OK) if folder: filechooser.set_current_folder(folder) path = None while True: response = filechooser.run() if response != gtk.RESPONSE_OK: path = None break path = filechooser.get_filename() if not os.path.exists(path): break if ask_overwrite(path, parent): break filechooser.destroy() return path
def password(primary='', secondary='', parent=None): """ Shows a password dialog and returns the password entered in the dialog :param primary: primary text :param secondary: secondary text :param parent: a gtk.Window subclass or None :returns: the password or None if none specified :rtype: string or None """ if not primary: raise ValueError("primary cannot be empty") d = HIGAlertDialog(parent=parent, flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_OK_CANCEL) d.set_default_response(gtk.RESPONSE_OK) d.set_primary(primary + '\n') if secondary: secondary += '\n' d.set_secondary(secondary) hbox = gtk.HBox() hbox.set_border_width(6) hbox.show() d.label_vbox.pack_start(hbox) label = gtk.Label(_('Password:')) label.show() hbox.pack_start(label, False, False) entry = gtk.Entry() entry.set_invisible_char(u'\u2022') entry.set_visibility(False) entry.show() d.add_action_widget(entry, gtk.RESPONSE_OK) # FIXME: Is there another way of connecting widget::activate to a response? d.action_area.remove(entry) hbox.pack_start(entry, True, True, 12) response = d.run() if response == gtk.RESPONSE_OK: password = entry.get_text() else: password = None d.destroy() return password def _test(): yesno('Kill?', default=gtk.RESPONSE_NO) info('Some information displayed not too long\nbut not too short', long=('foobar ba asdjaiosjd oiadjoisjaoi aksjdasdasd kajsdhakjsdh\n' 'askdjhaskjdha skjdhasdasdjkasldj alksdjalksjda lksdjalksdj\n' 'asdjaslkdj alksdj lkasjdlkjasldkj alksjdlkasjd jklsdjakls\n' 'ask;ldjaklsjdlkasjd alksdj laksjdlkasjd lkajs kjaslk jkl\n'), default=gtk.RESPONSE_OK, ) error('An error occurred', gtk.Button('Woho')) error('Unable to mount the selected volume.', 'mount: can\'t find /media/cdrom0 in /etc/fstab or /etc/mtab') print open(title='Open a file', patterns=['*.py']) print save(title='Save a file', current_name='foobar.py') print password('Administrator password', 'To be able to continue the wizard you need to enter the ' 'administrator password for the database on host anthem') print selectfolder() if __name__ == '__main__': _test()