How to create a dropdown from a gtk.Entry's icon?

1.9k views Asked by At

I have a gtk.Entry with an icon after the text, intending to be a text search field:

gtk.Entry for search

What I'm trying to do is to display a dropdown (i.e. a gtk.ComboBox) when the user clicks on the icon, to choose the type of search. A mock of that feature would be:

Mock of gtk.Entry with icon ComboBox

I have tried several things without any success. For example, trying to pack an empty gtk.ComboBox only showing an arrow right after the Entry, and stuffing it only on icon-press, which creates the illusion, but it has two drawbacks: a) when I stuff the ComboBox, the toolbar grows, and b) when I clear() the ListStore, the ComboBox retains its width and leaves an ugly grey box.

At this point I guess that I need to create a CellRenderer on icon-press that pops down the icon of the Entry, and I tried without a lot of success to understand the code of gtk.ComboBoxEntry (in gtkcomboboxentry.c), but as far as I understood it uses a vertical Container on the whole piece together with a CellRenderer.

Also GTK+3 doesn't have any ideas on this respect.

Any ideas, or some guidance in how to create this in PyGTK?

2

There are 2 answers

0
jcoppens On BEST ANSWER

I was looking for something similar, so I came up with the code below. I haven't really worried about the aesthetics. I did pass a list of tuples to the MyPopup class, with the idea of passing handlers for each of the menu items in the dropdown. Note that the item.show() is necessary, even though there is a show_all():

from gi.repository import Gtk

class MyPopup(Gtk.MenuButton):
    def __init__(self, btndefs):
        super(MyPopup, self).__init__()

        self.menu = Gtk.Menu()
        self.set_popup(self.menu)
        #self.set_label(">")
        self.set_direction(Gtk.ArrowType.RIGHT)

        for btndef in btndefs:
            item = Gtk.MenuItem()
            item.set_label(btndef[0])
            item.show()
            self.menu.append(item)

class MainWindow(Gtk.Window):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.set_size_request(100, -1)
        self.connect("destroy", lambda x: Gtk.main_quit())

        self.hbox = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL)
        self.entry = Gtk.Entry()

        self.popup = MyPopup( (("String",),
                               ("String no case",),
                               ("Hexadecimal",),
                               ("Regexp",)) )

        self.hbox.pack_start(self.entry, True, True, 0)
        self.hbox.pack_start(self.popup, False, True, 0)

        self.add(self.hbox)

        self.show_all()

    def run(self):
        Gtk.main()


def main():
    mw = MainWindow()
    mw.run()
    return 0

if __name__ == '__main__':
    main()

enter image description here

0
rho On

yup its year late, but lets not make next person stumbled here to be sad like me.

this is the example using Gtk.Menu() popup, you can also similar feat. with Gtk.Popover()

enter image description here

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '3.0')

from gi.repository import Gtk, Gdk

opts = {
    'hex'     : "system-run-symbolic",
    'regex'   : "font-select-symbolic",
    'string'  : "font-x-generic-symbolic",
    'no-case' : "tools-check-spelling-symbolic",
}

def make_menu(entry, opts):
    menu = Gtk.Menu()
    for label, icon in opts.items():
        item = Gtk.MenuItem()
        item.set_label(label)
        item.connect(
            "activate",
            lambda w: entry.set_icon_from_icon_name(0, opts[w.get_label()])
        )
        menu.append(item)

        # NOTE you can use Gtk.ImageMenuItem to add image but its
        # Deprecated since version 3.10

    menu.show_all()
    return menu


def on_icon_release(widget, pos, event):
    menu = make_menu(widget, opts)
    menu.popup(
        parent_menu_shell = None,
        parent_menu_item  = None,
        func              = None,
        data              = None,
        button            = Gdk.BUTTON_PRIMARY,
        activate_time     = event.get_time()
    )

def make_entry():
    entry = Gtk.Entry()
    entry.set_icon_from_icon_name(0, 'action-unavailable-symbolic')
    entry.set_icon_from_icon_name(1, 'fonts')
    entry.set_icon_sensitive(1, True)
    entry.set_icon_activatable(1, True)
    entry.connect("icon-release", on_icon_release)
    return entry


root = Gtk.Window()
root.add(make_entry())
root.show_all()
Gtk.main()