PDA

View Full Version : State machine inside a QWidget



leoalvesmachado
3rd August 2010, 19:15
Hi everyone,

I have a project that is entirely "based on QWidget". Now, I need to add a simple animation to change automatically (based on an user interaction) the styleSheet of a sub-component (a small toolbar with 3 buttons, built in the QT Creator designer).
Since I have only 2 possible states, I though about creating a state machine to do the switching. However, on the Demo "states" (Animation Framework->States), it uses a QGraphicsView. If I would use QGraphicsView, I'd have to change almost all of my classes (yeah, bad code design, I know...).
So, my question is, can I have a QStateMachine animation in a QWidget?

thanks for any help

Lykurg
3rd August 2010, 19:30
For such a simple task I would use animation direct without states, but you can use a state machine with widgets. They do not depend on the graphics view. In the detailed description of QStateMachine you have a simple example with a button.

leoalvesmachado
3rd August 2010, 21:01
Thanks a lot, Lykurg
As you may notice, I'm just starting with qt. How would you do it using animation direct? Do you have any example?
I've tryed before using QPropertyAnimation, but I got compiler error - seems that an ui component created by QT creator design is not a QObject... That's why I though of using stateMachines.

I got this:

QPropertyAnimation::QPropertyAnimation(QObject *, const QByteArray &, QObject *) : cannot convert parameter 1 from 'Ui::TopBar *' to 'QObject *"

Lykurg
4th August 2010, 05:13
seems that an ui component created by QT creator design is not a QObject... Well they are QObjects, but it seems you used your own "TopBar". Make sure this class has the Q_OBJECT macro. Also please show us the line, which causes the error.

leoalvesmachado
4th August 2010, 14:54
The thing is, the definition of the TopBar was automatically generated by the QT Creator UI design. It does not have a single line in cpp. The component was supposed to be a QWidget (at least that was my intention). Here is its xml:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TopBar</class>
<widget class="QWidget" name="TopBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>360</width>
<height>70</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>360</width>
<height>70</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>360</width>
<height>70</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">#TopBar{
background-image: url(:/tbskin.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
}</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>15</y>
<width>371</width>
<height>61</height>
</rect>
</property>
<layout class="QVBoxLayout" name="vertical_layout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<layout class="QHBoxLayout" name="tb_layout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>23</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="tb_bEtc">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>42</horstretch>
<verstretch>43</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>42</width>
<height>43</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>42</width>
<height>43</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#tb_bEtc:!pressed:!hover {
background-image: url(:/betc_normal.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
border:0px;
}

#tb_bEtc:pressed {
background-image: url(:/betc_press.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
border:0px;
}

#tb_bEtc:hover:!pressed {
background-image: url(:/betc_hover.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
border:0px;
}</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="hspcr_etc_pin">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>60</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="tb_bPin">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>42</horstretch>
<verstretch>43</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>42</width>
<height>43</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>42</width>
<height>43</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#tb_bPin:!pressed:!hover {
background-image: url(:/bpin_normal.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
border:0px;
}

#tb_bPin:pressed {
background-image: url(:/bpin_press.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
border:0px;
}

#tb_bPin:hover:!pressed {
background-image: url(:/bpin_hover.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
border:0px;
}</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="hspcr_pin_cls">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>60</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="tb_bCls">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>42</horstretch>
<verstretch>43</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>42</width>
<height>43</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>42</width>
<height>43</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#tb_bCls:!pressed:!hover {
background-image: url(:/bcls_normal.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
border:0px;
}

#tb_bCls:pressed {
background-image: url(:/bcls_press.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
border:0px;
}

#tb_bCls:hover:!pressed {
background-image: url(:/bcls_hover.png);
background-repeat: no-repeat;
background-color: rgba(255, 255, 255, 0);
border:0px;
}</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

Is there anything I can do to make it a "Q_OBJECT"? Or should I rewrite the component in cpp, so I could easily "make it a Q_OBJECT"?

Lykurg
4th August 2010, 15:16
Your ui file is converted to a QObject, so still valid:

Also please show us the line, which causes the error.
Means the part where you set up your state machine (= the lines which throw the error)

leoalvesmachado
4th August 2010, 16:46
Sorry, I forgot to add the line in the last code.
I'm not trying to use the state machine right know. Since you said you would use animation direct, I'm trying to use the QPropertyAnimation class.
I define my TopBar as a private property of a class that inherits QWidget, using the code below:

namespace Ui {
class TopBar;
}
/**...class code...*/
private:
Ui::TopBar *chrome;

I initialize it in the constructor doing this:


QWidget* activeChromeWidget = new QWidget();
chrome->setupUi (activeChromeWidget);

I did not connect the buttons' signals to slots yet, so that's pretty much everything I do to initialize the TopBar. I get the compiler error in the animation code, at this method:


void WebWidget::setChromeStyleSheet(bool active) {
QString activeStyle = "#TopBar{\n background-image: url(:/tbskin.png);\n background-repeat: no-repeat;\n background-color: rgba(255, 255, 255, 0);\n}";
QString inactiveStyle = "#InactiveTopBar{\n background-image: url(:/tbskin_inactive.png);\n background-repeat: no-repeat;\n background-color: rgba(255, 255, 255, 0);\n}";
if (!active) {
QPropertyAnimation *anim = new QPropertyAnimation(chrome, "styleSheets"); //HERE I GET ONE COMPILER ERROR
anim->setDuration(2);
anim->setStartValue(activeStyle); //could not reach chrome->styleSheets value here, seems to be not accessible, using the actual value
anim->setEndValue(inactiveStyle);
anim->start();
} else {
QPropertyAnimation *anim = new QPropertyAnimation(chrome, "styleSheets"); //HERE I GET ANOTHER ONE COMPILER ERROR
anim->setDuration(2);
anim->setStartValue(inactiveStyle); //could not reach chrome->styleSheets value here, seems to be not accessible, using the actual value
anim->setEndValue(activeStyle);
anim->start();
}
}


Thanks for your attention :)

Lykurg
4th August 2010, 17:02
Ok, I see. You have to use activeChromeWidget, the widget you initialized with your ui file. The ui handler is only to set up widgets.

Also, there is a huge page about using ui files in the docs: Using a Designer UI File in Your Application. I prefer the "The Single Inheritance Approach" (with ui on heap, like the creator does). Have a look on it. It saves you doing such mistakes you did.

EDIT: Also consider QAbstractAnimation::DeleteWhenStopped for starting your animation. The code right now creates memory leaks!

leoalvesmachado
4th August 2010, 17:49
Thanks a lot, Lykurg, you've been very helpful. I don't have compiler errors anymore on this part.
However, now the animation is not doing what I expect, that is change the background image from the component. Is it not allowed? Do I need to do something else here?

by the way, I've changed the string with the name of the property styleSheets to styleSheet. There was a warning about this....

Lykurg
4th August 2010, 18:17
Ok, I haven't looked closely on what you are doing with the animation. But a animation for changing a stylesheet is nonsense. What do you expect. It only can change from one state into the other. Therefore you don't need a animation, because there is nothing to animate. In that particular case just do something like:
// m_currentState is a private member
void Foo::changeTabBar() {
if (1 == m_currentState)
{
activeChromeWidget->setStyleSheet(...);
m_currentState = 0;
}
else
{
activeChromeWidget->setStyleSheet(...);
m_currentState = 1;
}
}

Animation is for changing the geometry, fading color etc.

leoalvesmachado
4th August 2010, 18:36
I worked...
As usual, I was making simple things complicated :o
Thanks