Search code examples
pythontreeviewgtk3pygobject

Button for row deletion in GTK 3


I trying to do my first desktop app in Python and GTK3, but I quickly run into a problem. I want to display a TreeView with columns URL, Title, and delete icon, but I'm having problem in displaying and icon that can be clicked, and that deletes the row.

I found that question, but the solution didn't work for me.

Is there any way to do what I want? Or did I design it wrong?

Code:

    # List
    self.store = Gtk.ListStore(str, str, str)
    self.store.append(['https://www.youtube.com/watch?v=dQw4w9WgXcQ',  # URL
                       'Rick Astley - Never Gonna Give You Up',  # Title
                       'edit-delete'])  # Action icon
    tree = Gtk.TreeView(self.store)
    tree.set_size_request(600, 400)

    # Editable URL
    url = Gtk.CellRendererText()
    url.set_property("editable", True)
    url.connect("edited", self.text_edited)
    column_url = Gtk.TreeViewColumn("YouTube URL", url, text=0)
    column_url.set_min_width(300)
    tree.append_column(column_url)

    # Title
    title = Gtk.CellRendererText()
    column_title = Gtk.TreeViewColumn("Title", title, text=1)
    tree.append_column(column_title)

    # Action icon
    action_icon = Gtk.CellRendererPixbuf()
    # action_icon.connect("clicked", self.action_icon_clicked)
    column_action_icon = Gtk.TreeViewColumn("", action_icon, icon_name=2)
    tree.append_column(column_action_icon)

Thanks for help


Solution

  • The trick is to exploit the row activation in a Treeview to capture whether the button was clicked. As the row_activated tells you which column and row was clicked, such that we can delete the clicked row.

    The default behaviour of a Treeview is double click to activate but it is possible to change this to single click using tree.set_activate_on_single_click(True). By now connecting to the listener to the signal like this tree.connect("row_activated", self.action_icon_clicked) we can use the function below to delete the clicked row.

    def action_icon_clicked(self, treeview, path, column):
        # If the column clicked is the action column remove the clicked row
        if column is self.column_action_icon:
    
            # Get the iter that points to the clicked row
            iter = self.store.get_iter(path)
    
            # Remove it from the ListStore
            self.store.remove(iter)
    

    So the complete code will become:

        # List
        self.store = Gtk.ListStore(str, str, str)
        self.store.append(['https://www.youtube.com/watch?v=dQw4w9WgXcQ',  # URL
                           'Rick Astley - Never Gonna Give You Up',  # Title
                           'edit-delete'])  # Action icon
        tree = Gtk.TreeView(self.store)
        tree.set_size_request(600, 400)
    
        # Editable URL
        url = Gtk.CellRendererText()
        url.set_property("editable", True)
        column_url = Gtk.TreeViewColumn("YouTube URL", url, text=0)
        column_url.set_min_width(300)
        tree.append_column(column_url)
    
        # Title
        title = Gtk.CellRendererText()
        column_title = Gtk.TreeViewColumn("Title", title, text=1)
        tree.append_column(column_title)
    
        # Action icon
        action_icon = Gtk.CellRendererPixbuf()
        self.column_action_icon = Gtk.TreeViewColumn("", action_icon, icon_name=2)
        tree.append_column(self.column_action_icon)
    
        # Make a click activate a row such that we get the row_activated signal when it is clicked
        tree.set_activate_on_single_click(True)
    
        # Connect a listener to the row_activated signal to check whether the correct column was clicked
        tree.connect("row_activated", self.action_icon_clicked)
    
    def action_icon_clicked(self, treeview, path, column):
        # If the column clicked is the action column remove the clicked row
        if column is self.column_action_icon:
    
            # Get the iter that points to the clicked row
            iter = self.store.get_iter(path)
    
            # Remove it from the ListStore
            self.store.remove(iter)