Advanced Control Panel#
Control panel can be used to do many things. It is mainly a single class that creates a PyQt widget. Some additional features may be needed.
Making a shared Control Panel#

Here we will focus on the example availible in the ``implementation/example/panel’’.
Here is a snippet of code, which is responsible for the mechanism shown in the gif above:
from titania.common.singleton import TitaniaSingleton
class VeloPanelSingleton(QtCore.QObject, metaclass=TitaniaSingleton):
sensor_group_signal = pyqtSignal(str)
sensor_id_signal = pyqtSignal(int)
class SharedVeloPanel(ControlPanel):
def __init__(self, widget):
ControlPanel.__init__(self, widget)
self.singleton = VeloPanelSingleton()
self.sensor_group_id_combo_box.currentTextChanged.connect(self.singleton.sensor_group_signal.emit)
self.sensor_id_combo_box.currentIndexChanged.connect(self.singleton.sensor_id_signal.emit)
self.singleton.sensor_group_signal.connect(self.on_singleton_group_changed)
self.singleton.sensor_id_signal.connect(self.on_singleton_id_changed)
def on_singleton_group_changed(self, new_group):
if self.sensor_group_id_combo_box.currentText() != new_group:
self.sensor_group_id_combo_box.setCurrentText(new_group)
def on_singleton_id_changed(self, new_id):
if self.sensor_id_combo_box.currentId() != new_id:
self.sensor_id_combo_box.setCurrentId(new_id)
Let’s break it down.
The following class:
class VeloPanelSingleton(QtCore.QObject, metaclass=TitaniaSingleton):
sensor_group_signal = pyqtSignal(str)
sensor_id_signal = pyqtSignal(int)
Is a singleton class. What it means that when we will create the class, as a member of another class like this:
class SharedVeloPanel(ControlPanel):
def __init__(self, widget):
...
self.singleton = VeloPanelSingleton()
...
Then, this object will be exactly the same in all of the instances of the SharedVeloPanel class. In other words; this object will be shared allong all SharedVeloPanel objects. We can use the signals created in the singleton object by accesing them like normal PyQt signals:
class SharedVeloPanel(ControlPanel):
def __init__(self, widget):
...
self.singleton = VeloPanelSingleton()
self.sensor_group_id_combo_box.currentTextChanged.connect(self.singleton.sensor_group_signal.emit)
self.singleton.sensor_group_signal.connect(self.on_singleton_group_changed)
What happens in the two last lines is that the object self.sensor_group_id_combo_box is connected to the singleton signal. Whenever the self.sensor_group_id_combo_box is changed, it will forse self.singleton.sensor_group_signal to emit a signal, with the changed text value of the combo box. In the last line, we connect the emition of the signal from that singleton to the self.on_singleton_group_changed function.
class SharedVeloPanel(ControlPanel):
...
def on_singleton_group_changed(self, new_group):
if self.sensor_group_id_combo_box.currentText() != new_group:
self.sensor_group_id_combo_box.setCurrentText(new_group)
...
So that whenever the self.singleton.sensor_group_signal is emited, this function will be runned. This function checks whether the value in the combo box is the same as the new text value emited by the signal. If it’s not it changes the value in the combo box. The if clause is very important because it prohibis looping of the signal. the looping may occur because when we use the setCurrentText() method, the change signal is emmited from the self.sensor_group_id_combo_box. Remember that this signal is connected to the singleton signal, which is exactly the one that is triggering this function. To prevent this from looping, again and again, we use the if clause to tell whether the change is neccesarry.
This concludes this example, the same procedure for the integer (instead of string text) value of the other combo box is repeated with the self.sensor_id_combo_box object.