3

I was recently coding PyQt with a QComboBox in a QTable. The QComboBox has autocomplete on by default. I was wanting to try and reproduce this in Python3 with Gtk3. I came across this example:

Gtk.Entry in Gtk.TreeView (CellRenderer)

that seems to have successfully added an autocompletion to a ComboBox in a Treeview. The example is not complete and I was hoping someone could give me a complete working example. I don't know how I 'connect' the CellRendererAutoComplete class to one of the columns in a treeview.

What I really want is to have AutoCompletion in an editable TreeView cell (with or without a ComboBox), but I have never come across this in the past in Gtk.

Thank you.

Here is a code example:

# https://stackoverflow.com/questions/13756787/
# https://python-gtk-3-tutorial.readthedocs.io/en/latest/cellrenderers.html
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
#######################################################################
class CellRendererAutoComplete(Gtk.CellRendererText):
    """ Text entry cell which accepts a Gtk.EntryCompletion object """
    __gtype_name__ = 'CellRendererAutoComplete'
    def __init__(self, completion):
        self.completion = completion
        Gtk.CellRendererText.__init__(self)
    def do_start_editing(
               self, event, treeview, path, background_area, cell_area, flags):
        if not self.get_property('editable'):
            return
        entry = Gtk.Entry()
        entry.set_completion(self.completion)
        entry.connect('editing-done', self.editing_done, path)
        entry.show()
        entry.grab_focus()
       return entry

    def editing_done(self, entry, path):
        self.emit('edited', path, entry.get_text())
#######################################################################
class CellRendererTextWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="CellRendererText Example")

        self.set_default_size(200, 200)

        self.liststore = Gtk.ListStore(str, str)
        self.liststore.append(["Fedora", "http://fedoraproject.org/"])
        self.liststore.append(["Slackware", "http://www.slackware.com/"])
        self.liststore.append(["Sidux", "http://sidux.com/"])

        treeview = Gtk.TreeView(model=self.liststore)

        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn("Text", renderer_text, text=0)
        treeview.append_column(column_text)

        renderer_editabletext = Gtk.CellRendererText()
        renderer_editabletext.set_property("editable", True)
########
# This is the problem area, I suppose, but I'm not sure
        x = CellRendererAutoComplete()     
        renderer_editabletext.connect('on_edit',x(renderer_editabletext))
########
        column_editabletext = Gtk.TreeViewColumn("Editable Text",renderer_editabletext, text=1)
        treeview.append_column(column_editabletext)

        renderer_editabletext.connect("edited", self.text_edited)

        self.add(treeview)

    def text_edited(self, widget, path, text):
        self.liststore[path][1] = text

win = CellRendererTextWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
John
  • 178
  • 10
  • Are you looking to have your code working? Or can I redesign your code to provide a simpler example? In other words, is this part of a larger project and I need to maintain the structure to provide you with an example? – theGtknerd Jul 21 '19 at 21:37
  • A simpler example would be just great! I just copied the code from the sources since I'm just at a preliminary stage. Thanks. – John Jul 21 '19 at 21:54

1 Answers1

2

So here is an example showing a CellRendererText and a CellRendererCombo using an EntryCompletion.

Because of the complexity of the Treeview, where one ListStore can be the model behind the Completion, Combo, and Entry, and another model can be behind the Treeview, you should have a very good grasp of Gtk.Treeview to understand this example. Note that this example only uses one Liststore, and only one editable column used by both the CellRendererText and the CellRendererColumn. This makes the example confusing, but it the simplest I can come up with since I do not know the intended use for this Treeview.

#!/usr/bin/python

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


class CellRendererTextWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="CellRendererText Example")

        self.set_default_size(200, 200)

        self.liststore = Gtk.ListStore(str, str)
        self.liststore.append(["Fedora", "http://fedoraproject.org/"])
        self.liststore.append(["Slackware", "http://www.slackware.com/"])
        self.liststore.append(["Sidux", "http://sidux.com/"])

        treeview = Gtk.TreeView(model=self.liststore)

        self.completion = Gtk.EntryCompletion(model = self.liststore)
        self.completion.set_text_column(1)
        self.completion.connect('match-selected', self.renderer_match_selected)

        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn("Text", renderer_text, text=0)
        treeview.append_column(column_text)

######## CellRendererText with EntryCompletion example
        renderer_text = Gtk.CellRendererText()
        renderer_text.connect('editing-started', self.renderer_text_editing_started)
        renderer_text.connect('edited', self.text_edited)
        renderer_text.set_property("editable", True)

        column_text_autocomplete = Gtk.TreeViewColumn("Editable Text", renderer_text, text=1)
        treeview.append_column(column_text_autocomplete)

######## CellRendererCombo with EntryCompletion example
        renderer_combo = Gtk.CellRendererCombo(model = self.liststore)
        renderer_combo.set_property("text-column", 1)
        renderer_combo.connect('editing-started', self.renderer_combo_editing_started)
        renderer_combo.connect('changed', self.combo_changed)
        renderer_combo.set_property("editable", True)

        column_combo_autocomplete = Gtk.TreeViewColumn("Editable Combo", renderer_combo, text=1)
        treeview.append_column(column_combo_autocomplete)


        self.add(treeview)

    def renderer_match_selected (self, completion, model, tree_iter):
        ''' beware ! the model and tree_iter passed in here are the model from the
        EntryCompletion, which may or may not be the same as the model of the Treeview '''
        text_match =  model[tree_iter][1]
        self.liststore[self.path][1] = text_match

    def renderer_text_editing_started (self, renderer, editable, path):
        ''' since the editable widget gets created for every edit, we need to 
        connect the completion to every editable upon creation '''
        editable.set_completion(self.completion)
        self.path = path # save the path for later usage

    def text_edited(self, widget, path, text):
        self.liststore[path][1] = text

    def renderer_combo_editing_started (self, renderer, combo, path):
        ''' since the editable widget gets created for every edit, we need to 
        connect the completion to every editable upon creation '''
        editable = combo.get_child() # get the entry of the combobox
        editable.set_completion(self.completion)
        self.path = path # save the path for later usage

    def combo_changed (self, combo, path, tree_iter):
        ''' the path is from the treeview and the tree_iter is from the model 
        of the combobox which may or may not be the same model as the treeview'''
        combo_model = combo.get_property('model')
        text = combo_model[tree_iter][1]
        self.liststore[path][1] = text

win = CellRendererTextWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

In the official docs, it is explicitly stated that the purpose of editing-started is to add a EntryCompletion and etc. I also subclassed Gtk.CellRendererText before I found that little hint in the docs.

theGtknerd
  • 3,647
  • 1
  • 13
  • 34
  • Thank you so much theGtknerd. I think you should change your handle to theGtkGuru! That is exactly what I wanted and I appreciate that you must have spent some time writing this. It is a lot more complicated than I had expected and beyond my limited abilities, but I've learned a lot from this. Thanks again – John Jul 22 '19 at 00:19
  • This was a kind of a `all the bells and whistles` example. I would have liked to provide a minimal example, but all the other examples I found on the Interweb so far don't have **enough** of info, so I thought I would post a more complete example. Sadly, that makes this way more complex. Just try to break it down into small chunks and digest it that way, or remove all the surplus, and use only what you need. – theGtknerd Jul 22 '19 at 00:24