QtJambi: update Widget from a test
-
HI,
I'm new to Qt and QtJambi and I am trying to learn about it. As a learning tool I do spikes with small tests that display one or more Widgets and than interact with the widgets programmatically.
This is what I would like to do in Jambi done in Swing:
@ @Test
public void testShowAndUpdateInSwing() throws Exception {
JLabel label = new JLabel();JFrame frame = new JFrame(); frame.setBounds(0, 0, 100, 80); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(label); frame.setVisible(true); Thread.sleep(1000); label.setForeground(Color.red); label.setText("1"); Thread.sleep(1000); label.setForeground(Color.blue); label.setText("2"); Thread.sleep(1000); label.setForeground(Color.orange); label.setText("3"); Thread.sleep(1000); }
@
I'm not using the Event Dispatch Thread here but it works anyway. The result is that i see my label with the content changing every second.
I tried to do the same with QtJambi. My first attempt was:
@ @Test
public void testShowAndUpdateInQtjambiV1() throws Exception {
QApplication.initialize(new String[] {});final QLabel label = new QLabel(); label.setFixedWidth(100); label.setFixedHeight(80); label.show(); Thread.sleep(1000); label.setStyleSheet("QLabel { color : red; }"); label.setText("1"); Thread.sleep(1000); label.setStyleSheet("QLabel { color : blue; }"); label.setText("2"); Thread.sleep(1000); label.setStyleSheet("QLabel { color : orange; }"); label.setText("3"); QApplication.exec(); }
@
As a result I see a label whit an orange 3 and that's not what I want. So I thought that I needed a Thread:
@ @Test
public void testShowAndUpdateInQtjambiV2() throws Exception {
QApplication.initialize(new String[] {});final QLabel label = new QLabel(); label.setFixedWidth(100); label.setFixedHeight(80); label.show(); new Thread() { @Override public void run() { try { Thread.sleep(1000); label.setStyleSheet("QLabel { color : red; }"); label.setText("1"); Thread.sleep(1000); label.setStyleSheet("QLabel { color : blue; }"); label.setText("2"); Thread.sleep(1000); label.setStyleSheet("QLabel { color : orange; }"); label.setText("3"); } catch (InterruptedException e) { } } }.start(); QApplication.exec(); }
@
Which of course gave me an exception (QObject used from outside its own thread).
After a bit of searching I understood the problem and went for a new attempt: signals and slots (a fully new concept for me):@ @Test
public void testShowAndUpdateInQtjambiV3() throws Exception {
QApplication.initialize(new String[] {});QLabel label = new QLabel(); label.setFixedWidth(100); label.setFixedHeight(80); label.show(); final MySignalEmitter updateText = new MySignalEmitter(); updateText.signal.connect(label, "setText(String)"); final MySignalEmitter updateStyleSheet = new MySignalEmitter(); updateStyleSheet.signal.connect(label, "setStyleSheet(String)"); new Thread() { @Override public void run() { try { Thread.sleep(1000); updateStyleSheet.setValue("QLabel { color : red; }"); updateText.setValue("1"); Thread.sleep(1000); updateStyleSheet.setValue("QLabel { color : blue; }"); updateText.setValue("2"); Thread.sleep(1000); updateStyleSheet.setValue("QLabel { color : orange; }"); updateText.setValue("3"); } catch (InterruptedException e) { } } }.start(); QApplication.exec(); } class MySignalEmitter extends QSignalEmitter { public Signal1<String> signal = new Signal1<String>(); public void setValue(String aValue) { signal.emit(aValue); } }
@
This solution works but I don't like it. What I don't like is that if I want to send another messages to my label (e.g. clear(), setPixmap(QPixMap)) I will have to create new SingalEmitters (in the worst case an emitter for each method and if I want to play with more Widgets it will worsen...)
So, it's time for the question :)
Is there another way to accomplish this? Am I completely on the wrong path? Or am I too much Swing infected and I need to approach this issue in a complete different way? Which one?Thanks
Marco
-
call QApplication.processEvents(); before going to sleep() that should get you what you want, but it is not a good idea to ever wait/sleep during the startup sequence. This being the point between calling initialize() and exec().
You want to call exec() as soon as possible to perform all other behaviour in response to eventing. So this means setting up the first state but never calling Thread.sleep() but using eventing instead.
One way to do this maybe to make use of QObject.startTimer(int) to get a callback to the method QObject#timerEvent(QTimerEvent). So you subclass QObject and implement the @Override method and this replaces the Thread.sleep() usage with no need to ever call it.
There are other Qt ways such as using a QTimer object and connecting the slot to a callback method of your choice that cycled through the states and changes UI, sets up QTimer for the next (or stops it) and immediately returns.