pythongtkgnomegladepygobject

gi.repository.GLib.Error: gtk-builder-error-quark: No function named `btnProcess_clicked_cb`. (14)


I have .glade file written in glade ver. 3.38 and PyGObject 3.38. Right now I'm on Ubuntu 21.10 with PyGObject 3.40. So, I converting my glade from gtk3 to gtk4 with gtk4-builder-tool but i got an error gi.repository.GLib.Error: gtk-builder-error-quark: No function named btnProcess_clicked_cb. (14)

Glade file

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk" version="4.0"/>
  <object class="GtkWindow" id="graywindow">
    <signal name="destroy" handler="graywindow_destroy_cb" swapped="no"/>
    <property name="child">
      <object class="GtkBox">
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkLabel">
            <property name="label" translatable="yes">1811
Jq
IC</property>
            <property name="width-chars">0</property>
            <attributes>
              <attribute name="font-desc" value="Sans Bold 14"></attribute>
              <attribute name="style" value="normal"></attribute>
            </attributes>
          </object>
        </child>
        <child>
          <object class="GtkBox">
            <property name="homogeneous">1</property>
            <child>
              <object class="GtkLabel">
                <property name="label" translatable="yes">Image Ori</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox">
            <property name="homogeneous">1</property>
            <child>
              <object class="GtkImage" id="imgOri">
                <property name="icon-name">image</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox">
            <property name="homogeneous">1</property>
            <child>
              <object class="GtkLabel">
                <property name="label" translatable="yes">Prewitt</property>
              </object>
            </child>
            <child>
              <object class="GtkLabel">
                <property name="label" translatable="yes">Global Threshold
</property>
              </object>
            </child>
            <child>
              <object class="GtkLabel">
                <property name="label" translatable="yes">Otsu</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox">
            <property name="homogeneous">1</property>
            <child>
              <object class="GtkImage" id="imgGray">
                <property name="icon-name">image</property>
              </object>
            </child>
            <child>
              <object class="GtkImage" id="imgGT">
                <property name="icon-name">image</property>
              </object>
            </child>
            <child>
              <object class="GtkImage" id="imgO">
                <property name="icon-name">image</property>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox">
            <property name="homogeneous">1</property>
            <child>
              <object class="GtkButton" id="btnProcess">
                <property name="label" translatable="yes">Process</property>
                <property name="focusable">1</property>
                <property name="receives-default">1</property>
                <signal name="clicked" handler="btnProcess_clicked_cb" swapped="no"/>
              </object>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkBox">
            <property name="homogeneous">1</property>
            <child>
              <object class="GtkEntry">
                <property name="focusable">1</property>
              </object>
            </child>
            <child>
              <object class="GtkButton" id="btnBrowse">
                <property name="label" translatable="yes">browse</property>
                <property name="focusable">1</property>
                <property name="receives-default">1</property>
                <signal name="clicked" handler="btnBrowse_clicked_cb" swapped="no"/>
              </object>
            </child>
          </object>
        </child>
      </object>
    </property>
  </object>
</interface>

Python file

from gi.repository import Gtk
import numpy as np
import cv2
import gi
from matplotlib import pyplot as plt
import sys

gi.require_version('Gtk', '4.0')


class grayProcess:
    def graywindow_destroy_cb(self, object, data=None):
        Gtk.main_quit()

    def btnBrowse_clicked_cb(self, object, data=None):
        self.dialog = Gtk.FileChooserDialog(
            'Please Choose a File',
            None,
            Gtk.FileChooserAction.OPEN,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))

        self.response = self.dialog.run()

        global url
        if self.response == Gtk.ResponseType.OK:
            self.search = self.builder.get_object('txtSearch')
            imgOri = self.builder.get_object('imgOri')
            url = self.dialog.get_filename()
            self.search.set_text(str(url))
            imgOri.set_from_file(url)

        elif self.response == Gtk.ResponseType.cancel:
            print('Cancel Clicked')

        self.dialog.destroy()

    def btnProcess_clicked_cb(self, object, data=None):
        self.img = cv2.imread(url)
        imgGrey = self.builder.get_object('imgGray')


# Prewitt Start
        self.gray = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
        img_gaussian = cv2.GaussianBlur(self.gray, (3, 3), 0)

        kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]])
        kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])
        img_prewittx = cv2.filter2D(img_gaussian, -1, kernelx)
        img_prewitty = cv2.filter2D(img_gaussian, -1, kernely)

        cv2.imwrite('image/prewitt.jpg', img_prewittx + img_prewitty)
        imgGrey.set_from_file('image/prewitt.jpg')

# Prewitt End

# Global Threshold Start
        self.img = cv2.imread(url, 0)
        imgGt = self.builder.get_object('imgGT')
        dimensao = self.img.shape
        img = cv2.medianBlur(self.img, 5)

        ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
        value = cv2.countNonZero(th1)
        titles = ['Original Image', 'Global Thresholding (v = 127)']
        images = [img, th1]

        for i in range(len(images)):
            plt.subplot(2, 2, i+1), plt.imshow(images[i], 'gray')
            plt.title(titles[i])
            plt.xticks([]), plt.yticks([])

            cv2.imwrite('image/Global Threshold.jpg', th1)
            imgGt.set_from_file('image/Global Threshold.jpg')
# Global Threshold End

# Start Otsu
        self.img = cv2.imread(url, 0)
        imgo = self.builder.get_object('imgO')
        ret, thresh2 = cv2.threshold(self.img, 127, 255, cv2.THRESH_BINARY_INV)

        titles = ['Original Image', 'BINARY',
                  'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
        images = [self.img, thresh2]

        for i in range(0, 6):
            plt.subplot(2, 3, i+1), plt.imshow(images[i], 'gray')
            plt.title(titles[i])
            plt.xticks([]), plt.yticks([])

            cv2.imwrite('image/otsu.jpg', thresh2)
            imgo.set_from_file('image/otsu.jpg')

# Otsu End

    def __init__(self):
        self.gladefile = ('uts_cv.glade')
        self.builder = Gtk.Builder()
        self.builder.add_from_file(self.gladefile)
        self.builder.connect_signals(self)
        self.window = self.builder.get_object('graywindow')

        self.window.show()


if __name__ == '__main__':
    main = grayProcess()
    Gtk.main()

Solution

  • According to gtk4 docs, https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkbuilder-api-changes

    gtk_builder_connect_signals() no longer exists. Instead, signals are always connected automatically. If you need to add user data to your signals, gtk_builder_set_current_object() must be called. An important caveat is that you have to do this before loading any XML. This means if you need to use gtk_builder_set_current_object(), you can no longer use gtk_builder_new_from_file(), gtk_builder_new_from_resource(), or gtk_builder_new_from_string(). Instead, you must use vanilla gtk_builder_new(), then call gtk_builder_set_current_object(), then load the XML using gtk_builder_add_from_file(), gtk_builder_add_from_resource(), or gtk_builder_add_from_string(). You must check the return value for failure and manually abort with g_error() if something went wrong.

    The easy way to use signals now, imo is Gtk.Template, you can look at the documentation here, https://pygobject.readthedocs.io/en/latest/guide/gtk_template.html