pythonpygtkgtkentry

pyGTK - gtk.Entry in gtk.Dialog Yields no Text


In pyGTK (2.22 - version is very important), I'm encountering the bug detailed below. I think it's a pyGTK issue, but I could be wrong and don't want to report a non-bug.

Basically, I am extracting text from a gtk.Entry() using .get_text(), and this returns an empty string even with text in the widget. Here is some relevant code (with NOOP definitions to make it runnable):

import gtk

class Item: pass

def tofile(item): pass

# Described issues begin below

class ItemAddDialog:
    "A dialog used when adding a menu item"
    def __init__(self):
        self.dialog = gtk.Dialog(title="Adding menu item...", buttons=btns)
        self.fname, self.name, self.icon, self.exe, self.cats = [gtk.Entry() for i in range(5)]
        self.obs = (self.fname, self.name, self.icon, self.exe, self.cats)
        self._config()

    def _config(self):
        _ = self.dialog.vbox
        map(lambda x: _.pack_start(x, False, False, 0), self.obs)
        map(lambda x: x.show(), self.obs)
        map(lambda x: x[1].set_text(x[0]), zip(("Filename", "Name in Menu", "Icon", "Command", "Categories (; Delimited)"), self.obs))

    def run(self):
        r = self.dialog.run()
        self.dialog.destroy()
        print _quote(str(r))
        if (int(r) == 1): i = Item(self.fname.get_text(), self.name.get_text(), self.icon.get_text(), self.exe.get_text(), self.cats.get_text())
        print str(i)
        tofile(i)

Solution

  • destroy() will among other things cause the widget and its children to be unrealized, which means the entry loses its text. Always read the state of a dialog (or any other widget) before destroying it.

    There are some other minor issues with your code:

    1. For clarity, you should replace the maps with simple loops:

      map(lambda x: _.pack_start(x, False, False, 0), self.obs)

      for x in self.obs: _.pack_start(x, False, False)

      map(lambda x: x[1].set_text(x[0]), zip(("Filename", "Name in Menu", "Icon", "Command", "Categories (; Delimited)"), self.obs))

      for txt, x in zip(("Filename", "Name in Menu", "Icon", "Command", "Categories (; Delimited)"), self.obs)): x.set_text(txt)

    2. Instead of calling show on all the children, just call show_all on the parent (the dialog in this case).

    3. I don't think you have to cast the dialog result to an int. Also, magic numbers are bad. Define a constant, or use a predefined one.