So I'm trying to use QPropertyAnimation on a QWidget that has been added to a QGraphicsScene and it's not working. I can't really copy and paste the code as it's intertwined with a somewhat complicated system but the summary is basically this:
A custom widget is created
CustomWidget widget;
Widget is added to a graphics scene
graphicsScene,addWidget(widget);
at some later point, one of the widget's member functions tries to create and start a QPropertyAnimation
QPropertyAnimation *anim = new QPropertyAnimation(this, "_opacity");
anim->setDuration(5000);
anim->setKeyValueAt(0, 0);
anim->setKeyValueAt(1, 1);
anim->start();
instead of a smooth animation, the property changes to the second value and stays there.
I've looked online at some related problems and their solutions, but none seem to match my situation. Does anyone know how to accomplish this?
EDIT: I discovered I just needed to make the WRITE function for _opacity call update()
I guess you are allocating QPropertyAnimation and friends on the stack instead of the heap.
Note that the QPropertyAnimation object will be destructed at the end of the scope, hence no properties are changed later on.
You most likely wanted to create that object on the heap. Also see the example at: http://doc.qt.digia.com/4.7/qpropertyanimation.html
Related
I'm developping a Qt application in which the user can add QML objects inside a QGraphicsScene. The available QML objects are listed and the user can add as many as he wants.
Until now, I used QQuickWidgets. The QGraphicsScene contains a top-level widget which is the parent of all the QQuickWidgets I create. It works fine, but I have a performance problem. With a high number of objects, the application starts to slow down, and takes too much space in RAM (more than 1.5 GB with the first example I created, containing 400 objects).
I thought it comes from the way QQuickWidgets are handled by Qt, and wanted to try another way, with QQuickViews. To do so I created a root view, converted in a QWidget so I can embed it in my view, which is a QWidget. Then I add a new QQuickView in the root view for each created object.
The creation of the root view, its container and the engine:
_rootView = new QQuickWindow();
_rootView->resize(1024, 720);
_rootView->show();
QWidget *container = QWidget::createWindowContainer(_rootView, this);
container->resize(_rootView->size());
container->setObjectName("TopLevelQmlViewWidget");
_layout->addWidget(container);
_engine = new QQmlEngine(_rootView);
The creation of the QQuickViews representing the objects:
QQuickView *view = new QQuickView(_engine, _rootView);
view->setSource(url);
view->setResizeMode(QQuickView::SizeViewToRootObject);
view->show();
It works, but the problem is that each QQuickView creates its own thread, which doesn't change the way I handle it but takes place in memory. I don't understand why, because I reparent them to the root view.
So my questions are the following :
1 - Is there a way to prevent the QQuickViews to create their own threads ?
2 - Is using QQuickViews, indeed, less memory-consuming than using QQuickWidgets ?
3 - If no, how can I handle adding a big number of QML objects in a QWidget view without consuming too much memory ?
I think using multiple QQuickViews is a bad idea. An application usually only needs one. I would take a look at QQmlComponent instead. Here is an example:
QQmlComponent component(_engine, QUrl::fromLocalFile("MyItem.qml"));
QQuickItem *childItem = qobject_cast<QQuickItem*>(component.create());
childItem->setParentItem(_rootView);
I'm by no means a QML expert. However, here are some pointers I can think of
Avoid mixing and matching QQuick: widget/View.
Consider creating objects dynamically
You can also make use of Loaders, but they have a small amount of extra overhead.
Consider using something like a stack/swipe view to minimize amount of loaded objects.
For best ROI, I'd first try implementing something like stack view and see how much it may help with the RAM. Then go on to create other objects dynamically as needed.
Finally I think QT has a tool that lets you see during runtime the amount of memory of the QML tree. You can look at that and see where your biggest memory hogs are.
I'm starting to program in Qt and am looking for best practices on where in the code to write the connect statements so that the code is more readable.
From the current perspective, it seems that defining connect on arbitrary locations (provided that the arbitrary locations don't mean a functional difference) can lead to very hard code to read.
Currently, I find the most intuitive way to define the connect statements in the constructor of the class that contains the SLOTS.
Is there a standard or a recommended best practice?
I like to have a function for each "main" part of my layout for initialization. Below is an example of a constructor. Each of the create functions returns a group box widget.
MissionConfiguration::MissionConfiguration(QWidget* parent) : QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(createMissionConfigurationGroupBox());
layout->addWidget(createNetAcquireGroupBox());
layout->addWidget(createSystemStatus());
layout->addWidget(createStatusButtons());
setLayout(layout);
}
In each of these "create" functions, the appropriate signals/slots are connected. It helps me keep things organized if the widget is complicated.
Here is an example of the createStatusButtons function:
QGroupBox* MissionConfiguration::createStatusButtons() {
// on the qbutton status
QGroupBox *runModes = new QGroupBox(tr("Run Modes"));
QHBoxLayout *runModeLayout = new QHBoxLayout;
live = new QRadioButton(tr("Live"));
playback = new QRadioButton(tr("Playback"));
simulation = new QRadioButton(tr("Simulation"));
QPushButton *logout = new QPushButton("Logout");
simulation->setChecked(true);
connect(recorder, SIGNAL(isConnected(bool)), live, SLOT(setEnabled(bool)));
connect(recorder, SIGNAL(isConnected(bool)), playback, SLOT(setEnabled(bool)));
connect(logout, SIGNAL(clicked()), this, SLOT(logout()));
runModeLayout->addWidget(live);
runModeLayout->addWidget(playback);
runModeLayout->addWidget(simulation);
runModeLayout->addWidget(logout);
runModes->setLayout(runModeLayout);
return runModes;
}
Really, the "best" way to do is is how you like it and what is easy for you to remember. I recommend coming up with an idiom that you can understand, so in the future if you have to edit your code, you'll know exactly where to look.
What is the best way (place) to initialize QWidgets, when you add them programmatically?
I can think of ways to do it:
1) Create instances in the definition file and then initialize them in the initializer list. This seems to be the right way, but it becomes kind of sloppy the more QWidgets you need to add to the Window.
class SomeWindow : QDialog{
...
private:
QLabel label1;
QLabel label2;
...
}
SomeWindow::SomeWindow(...) : QDialog(...),
label('label1 text'), label('label2 text')
{
...
layout.addWidget(&label1);
layout.addWidget(&label2);
...
}
2) Coming from C# I tend to like this, but it seem to generate memory leak...
SomeWindow::SomeWindow(...) : QDialog(...)
{
QLabel* label1 = new QLabel('label1 text');
QLabel* label2 = new QLabel('label2 text');
...
layout.addWidget(label1);
layout.addWidget(label2);
...
}
Is there a better way to do this that I'm missing?
I appologize for the newbie question.
Qt use his own system to delete parent-children QObject derived classes. When you delete object, all children are deleted too.
With the first code, you have 2 destructions (in destructor of SomeWindow and with QObject system), then this code is illegal (only with Qt ; with C++ standard code, it's good)
With the second code, labels are delete by QObject system, you don't have memory leak. No need to keep pointer on objects.
#Jairo
Setting parent in constructor is not the only way to add children to objects. In particular, here, QLayout::addWidget reparent objets (if layout is correctly child of object)
#msgmaxim
Be carefull, layout must not be local variable in constructor
An advantage to having pointers to widgets in the headers, rather than the actual objects, is that you don't need to include all the headers for the widgets, but just forward declare them. This increases compilation time, which can be considerably noticeable in large projects.
In addition, if you have a dialog and are just adding a lot of widgets, such as a QLabel, you can make the code neater by doing this in the implementation: -
layout.addWidget(new QLabel('label1 text'));
layout.addWidget(new QLabel('label2 text'));
layout.addWidget(new QLabel('label3 text'));
layout.addWidget(new QLabel('label4 text'));
As has been mentioned, Qt's parent system will take care of cleaning up the widgets when their parent is deleted.
Of-course, if you want to change a property, such as the text, you'd then need to find the widget from the layout's children, but often QLabel, as its name implies, just labels an item and its properties aren't changed.
Two ways are good in order to initialize a new widget.
First case you have labels as objects. So when SomeWindow is destroyed, they will be destroyed automatically too. Remember, if you have pointer to widgets instead objects, you will need (and can) to delete the labels into destructor of dialog.
SomeWindow::~SomeWindow()
{
delete label1;
label2.deleteLater(); // A safer way to delete a widget. Thread-safe.
}
And second case there will be a memory leak because you have no way to delete the widget into destructor. But if you define a parent for labels, they will be delete when parent is delete too. Take a look to QWidget documentation.
If parent is 0, the new widget becomes a window. If parent is another widget, this widget becomes a child window inside parent. The new widget is deleted when its parent is deleted.
Moreover, anytime a object constructor ask you for a parent QWidget or QObject, you can think that Qt will delete the object when the parent is deleted.
I am a newbey as well, but I hope this help you.
I have a few QGraphicsScene subclasses "CustomScene" all deriving from a common interface that contains the virtual functions cut(), copy(), paste(), and delete(). QGraphicsScene is the superclass, which is inherited by CustomSceneInterface, which is inherited by CustomScene. Each of the CustomScenes are shown in separate QGraphicsViews in the main window. I also have QActions for cut, copy, paste, and delete.
I'm having trouble figuring out how to send the QAction signals to whichever CustomScene was clicked on last (or whichever has "focus").
How can I do this?
I realized I can just send the QAction signals to slots that check which QGraphicsView has focus and then call its scene's appropriate method. I'll need to call
QWidget::setFocusPolicy(Qt::ClickFocus)
on the QGraphicsViews to get this to work properly. If someone can think of a better solution, please let me know.
EDIT:
With Qt5 and being able to use lambda expressions as slots, I can now employ a pretty spiffy approach. First, I make a function lastClickedScene(), which returns whichever scene was last clicked on. Then I do connect(actionCut, &QAction::triggered, [=]{lastClickedScene->cut();}).
i have a problem with QPropertyAnimation in Qt
my code:
QString my_text = "Hello Animation";
ui->textBrowser->setText((quote_text));
ui->textBrowser->show();
QPropertyAnimation animation2(ui->textBrowser,"geometry");
animation2.setDuration(1000);
animation2.setStartValue(QRect(10,220/4,1,1));
animation2.setEndValue(QRect(10,220,201,71));
animation2.setEasingCurve(QEasingCurve::OutBounce);
animation2.start();
till now it seems very good , but the problem is that i can see this animation only when i show a message box after it .
QMessageBox m;
m.setGeometry(QRect(100,180,100,50));
m.setText("close quote");
m.show();
m.exec();
when i remove the code of this message box , i can't see the animation anymore.
the functionality of the program doesn't require showing this MessageBox at all.
Can anybody help?
Maybe it is an update problem. Could you try to connect the valueChanged() signal of QPropertyAnimation to an update() call in the GUI?
My guess is that the code for the animation that you present is inside a larger chunk of code where the control doesn't get back to the event loop (or the event loop hasn't started yet). This means that when the MessageBox's exec function is called, an event loop starts operating again, and the animation starts. If you were to dismiss the message box in the middle of the animation, it would probably freeze at that point, as well.
animation2 is declared as a local variable. When the enclosing function
exits, it is no longer in scope and is deleted. The animation never runs as
it does not exist when Qt returns to the event loop and, as noted in the QAbstractAnimation
documentation
(QPropertyAnimation inherits QAbstractAnimation), for QPropertyAnmiation to execute, it must exist when Qt returns to the event loop.
When control reaches the event loop, the animation will run by itself,
periodically calling updateCurrentTime() as the animation progresses.
The solution is to dynamically allocate animation2 rather than declare it as
a local variable.
QPropertyAnimation *animation2 = new QPropertyAnimation(ui->textBrowser,"geometry");
animation2->setDuration(1000);
animation2->setStartValue(QRect(10,220/4,1,1));
animation2->setEndValue(QRect(10,220,201,71));
animation2->setEasingCurve(QEasingCurve::OutBounce);
animation2->start();
Note that this it the same technique is the same as that used in the C++
example provided in the QPropertyAnmiation
documentation:
QPropertyAnimation *animation = new QPropertyAnimation(myWidget, "geometry");
animation->setDuration(10000);
animation->setStartValue(QRect(0, 0, 100, 30));
animation->setEndValue(QRect(250, 250, 100, 30));
animation->start();
The original question notes:
i can see this animation only when i show a message box after it
This is an interesting side affect of how QMessageBox works. The exec()
method executes an event loop. Since the event loop executes within the scope
of the function enclosing animation2, animation2 still exists and the
desired animation executes.
By default, animation2 will be deleted when the parent, ui->textBrowser in
the original question, is deleted. If you wish for the animation to be
deleted when it completes executing, QAbstractAnimation provides a property
that controls when the animation is deleted. To automatically delete
animation2 when it finishes executing, change the start() method to:
animation2->start(QAbstractAnimation::DeleteWhenStopped);