I instantiate GtkInfobars a lot in my GTK+ application in order to communicate with the user. There are various types of infobars, depending on the message. Basically, any infobar could be a combination of the 4 different infobar message types and 5 different icons (which are painted on the left side of the infobar).
Initially, I would call my custom infobar function like this:
infobar("Message. Woo.", type=gtk.MESSAGE_INFO, icon=gtk.STOCK_DIALOG_WARNING, timeout=5)
After a while I decided I wanted to simplify all the creation calls... so I modified my infobar function so that I could do this:
infobar("Message. Woo.", type=(1,3), timeout=3)
I feel like the second way is better.. and that it's worth the code obfuscation, but I suspect not everyone will agree with me. So here's the code of my infobar function as it is now. I'd love to get some feedback. Thanks for reading!
NOTE: Skip to the newer edit below.
def infobar(self, msg=None, type=(1,1), timeout=3, vbox=None):
"""Popup a new auto-hiding InfoBar."""
# List of possible infobar message types
msgtypes = [0,
gtk.MESSAGE_INFO, # 1
gtk.MESSAGE_QUESTION, # 2
gtk.MESSAGE_WARNING, # 3
gtk.MESSAGE_ERROR] # 4
# List of possible images to show in infobar
imgtypes = [gtk.STOCK_APPLY, # 0
gtk.STOCK_DIALOG_INFO, # 1
gtk.STOCK_DIALOG_QUESTION, # 2
gtk.STOCK_DIALOG_WARNING, # 3
gtk.STOCK_DIALOG_ERROR] # 4
ibar = gtk.InfoBar()
ibar.set_message_type (msgtypes[type[0]])
if vbox:
# If specific vbox requested: assume ibar for filemode, add cancel button
ibar.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
ibar.connect ('response', self.cleanup_filemode)
else:
# If no specific vbox requested: do normal ibar at the top of message area
vbox = self.vbox_ibar
ibar.add_button (gtk.STOCK_OK, gtk.RESPONSE_OK)
ibar.connect ('response', lambda *args: ibar.destroy())
vbox.pack_end (ibar, False, False)
content = ibar.get_content_area()
img = gtk.Image()
img.set_from_stock (imgtypes[type[1]], gtk.ICON_SIZE_LARGE_TOOLBAR)
content.pack_start (img, False, False)
img.show ()
if msg:
# If msg was specified, show it, but change the default color
label = gtk.Label()
label.set_markup ("<span foreground='#2E2E2E'>{}</span>".format(msg))
content.pack_start (label, False, False)
label.show ()
# FIXME: Why doesn't Esc trigger this close signal?
ibar.connect ('close', lambda *args: ibar.destroy())
ibar.show()
if timeout:
glib.timeout_add_seconds(timeout, ibar.destroy)
return ibar
----------
EDIT:
So Winston has been giving me some ideas. I'm trying out something new. First, I'm storing all of the messages and associated flags in a separate file (messages.py) as a 2d dictionary. Here's an excerpt:
DEFAULT_TIMEOUT = 5
SUCCESS = 0
INFO = 1
QUESTION = 2
WARNING = 3
ERROR = 4
MESSAGE_DICT = dict(
engine_openssl_missing = dict(
text="<b>Shockingly, your system does not appear to have OpenSSL.</b>",
type=INFO,
icon=WARNING,
timeout=DEFAULT_TIMEOUT
),
txtview_fileopen_error = dict(
text="<b>Error. Could not open file:\n<i><tt><small>{filename}</small></tt></i></b>",
type=WARNING,
icon=ERROR,
timeout=DEFAULT_TIMEOUT
),
msgbuff_empty = dict(
text="<b>{customtext}</b>",
type=INFO,
icon=WARNING,
timeout=2
)
)
Currently I import that like: from messages import MESSAGE_DICT.
And then here's my new (for the moment) infobar function:
def infobar(self, id, filename=None, customtext=None, vbox=None):
"""Popup a new auto-hiding InfoBar."""
# Find the needed dictionary inside our message dict, by id
MSG = MESSAGE_DICT[id]
# Use value from MSG type & icon to lookup Gtk constant, e.g. gtk.MESSAGE_INFO
msgtype = MSGTYPES[ MSG['type'] ]
imgtype = IMGTYPES[ MSG['icon'] ]
# Replace variables in message text & change text color
message = ("<span foreground='#2E2E2E'>" +
MSG['text'].format(filename=filename, customtext=customtext) +
"</span>")
# Now that we have all the data we need, START creating!
ibar = gtk.InfoBar()
ibar.set_message_type(msgtype)
if vbox:
# If specific vbox requested: assume ibar for filemode, add cancel button
ibar.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
ibar.connect ('response', self.cleanup_filemode)
else:
# If no specific vbox requested: do normal ibar at the top of message area
vbox = self.vbox_ibar
ibar.add_button (gtk.STOCK_OK, gtk.RESPONSE_OK)
ibar.connect ('response', lambda *args: ibar.destroy())
vbox.pack_end (ibar, False, False)
content = ibar.get_content_area()
img = gtk.Image()
img.set_from_stock (imgtype, gtk.ICON_SIZE_LARGE_TOOLBAR)
content.pack_start (img, False, False)
img.show ()
label = gtk.Label()
label.set_markup (message)
content.pack_start (label, False, False)
label.show ()
# FIXME: Why doesn't Esc trigger this close signal?
ibar.connect ('close', lambda *args: ibar.destroy())
ibar.show()
if MSG['timeout'] > 0:
glib.timeout_add_seconds(MSG['timeout'], ibar.destroy)
return ibar
Note that MSGTYPES and IMGTYPES are lists that I moved out of the function, but other than the name change, they're the same as in my original post.
Oh and finally, the way I call the infobars now is, e.g.:
self.infobar('engine_openssl_missing')
self.infobar('txtview_fileopen_error', filename)
self.infobar('msgbuff_empty', customtext="No text to save")
By the way, you can see all this in living color on github. The infobar funness is part of a gui encryption application called Pyrite.