I cannot figure out how to handle the notify::active
signal of a Gtk.Switch
. I'm working with the MVC architecture (pattern) suggested here.
The error I got is this:
TypeError: _on_switch_serial_toggled() missing 1 required positional argument: 'state'
Here's my minimal working example (without a model):
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio, GObject
class Application(Gtk.Application):
def __init__(self):
app_id = "org.iea.etc"
flags = Gio.ApplicationFlags.FLAGS_NONE
super(Application, self).__init__(application_id=app_id, flags=flags)
def do_activate(self):
# c.Controller(m.Model(), v.View(application=self))
Controller(None, View(application=self))
def do_startup(self):
Gtk.Application.do_startup(self)
class Controller(object):
def __init__(self, model, view):
self._model = model
self._view = view
self._view.connect('switch_serial_toggled',
self._on_switch_serial_toggled)
self._view.show_all()
def _on_switch_serial_toggled(self, switch, state):
if switch.get_active():
print('Switch ON')
else:
print('Switch OFF')
class View(Gtk.ApplicationWindow):
__gsignals__ = {
'switch_serial_toggled': (GObject.SIGNAL_RUN_FIRST, None, ())
}
def __init__(self, **kw):
super(View, self).__init__(**kw)
self._switch_serial = Gtk.Switch()
self._switch_serial.connect("notify::active",
self.on_switch_serial_toggled)
self.add(self._switch_serial)
def on_switch_serial_toggled(self, switch, state):
self.emit('switch_serial_toggled')
if __name__ == '__main__':
app = Application()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
First of all, you are handling the notify
signal for the active
property of Gtk.Switch
almost properly. The problem resides in the handling of the custom signal, that you've added to your View.
It's important to understand what you are doing: You have created a View class that has a property, which is a Gtk.Switch. You've also created a signal, called switch_serial_toggled
associated with this class. Inside the class, you want that, when the internal Gtk.Switch changes state, the View triggers its own "toggled" signal.
This being said, lets fix your code:
1 - Lets add the toggle state to the View switch_serial_toggled
as a boolean
class View(Gtk.ApplicationWindow):
__gsignals__ = {
'switch_serial_toggled': (GObject.SIGNAL_RUN_FIRST, None, (bool,))
}
2 - Lets give proper names to the arguments of the View internal signal handler and add the state to the emitted signal:
def on_switch_serial_toggled(self, obj, pspec):
self.emit('switch_serial_toggled', self._switch_serial.get_active ())
GObject.Object notify signal handler is described here
3 - Now, let's go to the Controller and handle the View signal correctly:
def _on_switch_serial_toggled(self, widget, active):
if active is True:
print('Switch ON')
else:
print('Switch OFF')
widget
argument is the View instance inside the Controller instance and state
is the boolean that gets passed with the signal emission.
The full code, with the fixes above, should look like this:
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio, GObject
class Application(Gtk.Application):
def __init__(self):
app_id = "org.iea.etc"
flags = Gio.ApplicationFlags.FLAGS_NONE
super(Application, self).__init__(application_id=app_id, flags=flags)
def do_activate(self):
# c.Controller(m.Model(), v.View(application=self))
Controller(None, View(application=self))
def do_startup(self):
Gtk.Application.do_startup(self)
class Controller(object):
def __init__(self, model, view):
self._model = model
self._view = view
self._view.connect('switch_serial_toggled',
self._on_switch_serial_toggled)
self._view.show_all()
def _on_switch_serial_toggled(self, widget, active):
if active is True:
print('Switch ON')
else:
print('Switch OFF')
class View(Gtk.ApplicationWindow):
__gsignals__ = {
'switch_serial_toggled': (GObject.SIGNAL_RUN_FIRST, None, (bool,))
}
def __init__(self, **kw):
super(View, self).__init__(**kw)
self._switch_serial = Gtk.Switch()
self._switch_serial.connect("notify::active", self.on_switch_serial_toggled)
self.add(self._switch_serial)
def on_switch_serial_toggled(self, obj, pspec):
self.emit('switch_serial_toggled', self._switch_serial.get_active ())
if __name__ == '__main__':
app = Application()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
Resulting in something like this:
PS: Notice that in step 2, obj
is the object to which you have connected the signal handler which is similar to self._switch_serial
. This means that you could use obj.get_active ()
instead:
def on_switch_serial_toggled(self, obj, pspec):
self.emit('switch_serial_toggled', obj.get_active ())