Unable to detect why QDialog is memory leaking - qt

I'm doing a project using Qt with some customized QDialogs for user input. Due to hardware constraint of my development box, I want to monitor the memory usage of my app. How I exec the dialog.
1 void MainWindow::callDialog() {
2 DlgPopConfig dialog(&theApp->cfgPop, m_fnPopCfg, this);
3 dialog.exec();
4 m_fnPopCfg = dialog.fileName();
5 lbl_fnPopCfg->setText(m_fnPopCfg);
6 }
As the dialog is a local variable, I expect it to be created on stack and destroy immediately once the function ends (after line 5). When the app repeatedly open and close the dialog, its mem usage goes up, and it never return to initial values ['Memory (Working Set)' and 'Memory (Private Working Set)' columns of Task Manager]. I used Application Verifier, enabling all the basic tests, and it shows no error'. The memory pattern looks like follow (numeric values are made-up of illustration only):
Application start (working set = 12000K, private set = 6000K)
Open Dialog-1 (working set = 14000K, private set = 7000K)
Close Dialog (working set = 12010K, private set = 6005K)
Open Dialog-2 (working set = 14020K, private set = 7000K)
Close Dialog (working set = 12010K, private set = 6008K)
Open Dialog-3 (working set = 14080K, private set = 7010K)
Close Dialog (working set = 12040K, private set = 6008K)
...
So, any idea to trace the root cause of the problem? (Actually, I'm also facing similar issue when usage static methods of QFileDialog getOpenFileName, getSaveFileName, and found some discussion here, but it seems not solved)
Edit I use QFormLayout in my dialogs, and I add widgets by layout->addRow("label text", mywidget);, I doubt if the object destruction fail to remove the labels cleanly.
Edit I created a test program with the QDialog have ten QLineEdits, using same add-widget strategy. The problem still exists. (The problem will happen for this test program if I create and close the dialog frequently, says 10 times in a second)
mainwindow.h
#include <QMainWindow>
#include <QPushButton>
#include <QDialog>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
private:
QPushButton * button;
private slots:
void button_click();
};
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
};
mainwindow.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QFormLayout>
#include <QLineEdit>
#include <QLabel>
MainWindow::MainWindow(QWidget *parent):QMainWindow(parent)
{
button=new QPushButton(this);setCentralWidget(button);
connect(button,SIGNAL(clicked()),SLOT(button_click()));
}
void MainWindow::button_click()
{
Dialog d(this);
d.exec();
}
Dialog::Dialog(QWidget *parent):QDialog(parent)
{
QFormLayout*layout=new QFormLayout(this);
setLayout(layout);
for (int i = 0; i < 10; i++)
{
layout->addRow(QString("%1").arg(i+1), new QLineEdit(this));
}
}
int main(int c,char *argv[])
{
QApplication a(c,argv);
MainWindow w;
w.show();
return a.exec();
}
Platform
Win 7 x64, MinGW 4.7.2 x64 (rubenvb-build), 4GB ram
Qt 4.8.5 (built natively using above tool-chain)
Qt-Creator 2.6.1 (built natively using above tool-chain)

A few months late, but this might help the next person who comes across this problem. I'm using PySide, but had the same memory leak. There turned out to be two options, depending on what information you need to get back from the dialog:
1) Schedule the dialog for deletion when you're done with it.
In Python, this looks like:
dialog = MyDialog(self)
dialog.exec_()
# Do other things with dialog
dialog.deleteLater()
And it should look similar in your C++ code:
void MainWindow::button_click()
{
Dialog d(this);
d.exec();
// Do other things with d
d.deleteLater()
}
2) Set the WA_DeleteOnClose attribute.
I ended up including this in the custom dialog's constructor:
self.setAttribute(PySide.QtCore.Qt.WA_DeleteOnClose)
Which should look something like this in your C++ code:
Dialog::Dialog(QWidget *parent):QDialog(parent)
{
QFormLayout*layout=new QFormLayout(this);
setLayout(layout);
setAttribute(Qt::WA_DeleteOnClose);
for (int i = 0; i < 10; i++)
{
layout->addRow(QString("%1").arg(i+1), new QLineEdit(this));
}
}
Both of those fixed the memory leak for me, except that it will occasionally leak 4kb if I open/close the dialog very quickly a bunch of times in a row. Sorry for the Python-centric answer -- hopefully this points people in the right direction.

Related

Qt: using QWidgets in non GUI threads

I'm trying to understand what is and isn't allowed when it comes to QWidget and Qt concurrency. I've created a Widget which has a slow_function and I'm considering three cases:
Run the slow_function on the GUI thread. This results in the expected behaviour; the GUI becomes unresponsive while waiting for the function to return.
Use QtConcurrent::run(this, &Widget::slow_function). I was surprised to see that this didn't block the GUI. I've confirmed that the thread affinity of my instance is still the GUI thread, nevertheless, the function seems to be executing on a separate thread. Is such an approach allowed and is this the expected behaviour (documentation link would be really helpful)? Is such an approach safe if I can guarantee that slow_function is thread-safe?
Create a subclass of QThread which holds a pointer to my widget. Override the run method to call slow_function. The behaviour is the same as Case 2. This is also surprising as the thread affinity is still the GUI thread (besides, we are not even allowed to use moveToThread on a QWidget). Why is this running on a separate thread? Is moveToThread meant to be useful only when we are interested in calling slots via signals sent from another thread?
Thank you for reading. Here is the relevant code starting with my the header file:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QDebug>
#include <QPushButton>
#include <QLayout>
#include <windows.h>
#include <QtConcurrent/QtConcurrent>
#include <QThread>
#include <QApplication>
class Widget;
class Thread: public QThread
{
public:
Thread(Widget* widget)
: m_widget(widget){}
protected:
void run() override;
private:
Widget* m_widget;
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr)
: QWidget(parent)
, m_thread(this){
auto layout = new QVBoxLayout(this);
auto button = new QPushButton("Case 1: Run on gui thread");
auto button2 = new QPushButton("Case 2: Run with qtconcurrent");
auto button3 = new QPushButton("Case 3: Run with qthread");
connect(button, &QPushButton::clicked, this, &Widget::slow_function);
connect(button2, &QPushButton::clicked, this, &Widget::use_concurrent);
connect(button3, &QPushButton::clicked, this, &Widget::use_qthread);
layout->addWidget(button);
layout->addWidget(button2);
layout->addWidget(button3);
}
~Widget()
{
m_thread.quit();
m_thread.wait();
}
public slots:
void slow_function()
{
qDebug() << "Starting";
auto gui_thread = QApplication::instance()->thread();
auto this_thread = thread();
qDebug() << "Thread affinity is" << (gui_thread == this_thread ? "gui_thread" : "non_gui_thread");
Sleep(5000);
qDebug() << "Finished";
}
void use_concurrent()
{
QtConcurrent::run(this, &Widget::slow_function);
}
void use_qthread()
{
m_thread.start();
}
private:
Thread m_thread;
};
#endif // WIDGET_H
and the main.cpp file:
#include "widget.h"
#include <QApplication>
void Thread::run()
{
m_widget->slow_function();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
You should not do any UI things in nonmain threads. The UI means
widget interaction
model
Run the slow_function on the GUI thread
That's not allowed, nothing should block the GUI thread.
Is such an approach safe if I can guarantee that slow_function is thread-safe?
It's okay to run slow function in a different thread. But...
what happens when the user closes the application, but the function still executes?
I have done this, but I prefer to encapsulate it in a separate class.
Create a subclass of QThread which holds a pointer to my widget.
You should only subclass QThread if the subclass is a thread. Like, subclassing an animal class will give you an animal, not a chair with four legs.
Is moveToThread meant to be useful only when we are interested in calling slots via signals sent from another thread?
moveToThread changes the affinity of the object. So, slots are always executed in the correct thread, when you call invokeMethod, the method is executed in the correct thread. When an event is delivered, the event handler is always called in the appropriate thread.
The whole purpose of QtConcurrent::run() is to make running heavy workloads in a thread easier. If your use-case allows for it, great!
Pair it with a QFutureWatcher to retrieve the result after your slow_function is finished.
Using QThread is another option, but it makes more sense when you have a long-lived object. Instead of subclassing, I find it easier to use the worker model: create a worker class with signals/slots, call moveToThread on it with a vanilla QThread object, connect/subscribe, start the thread
QWidget is a way to create GUI functionality in Qt. While it may be possible to use such objects in non-gui threads, it's best you separate your compute workloads from GUI (don't put your slow_function into widget classes).

Qt: Track mouse position while QDrag is running

I am developing a Qt application with multiple windows and want to implement cross-window drag&drop functionality for some elements in my program.
To do so, I attach an event filter to the to-be-dragged QML elements and listen for the MousePress/MouseMove events to start the drag procedure as follows:
QDrag *drag = new QDrag(quickItem);
QMimeData* mimeData = new QMimeData();
mimeData->setText("Test");
drag->setHotSpot(QPoint(0, 0));
drag->setMimeData(mimeData);
drag->exec();
This works fine, but now I would like to show a little tooltip (being a QWidget) while dragging, following the mouse cursor and displaying a short text depending on the element the mouse is currently over (similar to the "Copy to ..." or "Move to..." labels appearing when you drag files around in Windows Explorer).
However, while dragging the element, I don't receive any MouseMove events neither on the QDrag object nor on the quickItem itself, which makes it impossible to track the mouse position. Since the mouse is grabbed during dragging, there should be some event in Qt that frequently reports the mouse position, no matter where on the screen the mouse is.
I am aware of the QDrag::setPixmap method, however this won't allow me to change my tooltip text during dragging and has some other limitations I would like to avoid.
Is there some way to listen to mouse move events while QDrag is running, without using platform-specific system APIs?
Update:
I don't think that there is way of doing this without using OS libraries nor getting the mouse position every X miliseconds. It looks like a really specific problem that Qt framework does not contemplate. You will need to write your own class to control this using win32 for windows, x11 for linux and the equivalent of Mac.
If you want to get the mouse position when your window is active and you are dragging something, check this:
Searching a bit I've found a solution for getting it when your window has the focus using QObject::eventFilter.
Create a class (for example EventListener) that inherits from QObject and overrides eventFilter and a method to set this as your qml window (which inherits from QObject) event filter with installEventFilter.
eventslistener.h:
#include <QEvent>
#include <QObject>
#include <QDebug>
#include <QDropEvent>
class EventsListener : public QObject
{
Q_OBJECT
public:
EventsListener(QObject * ptr) : QObject (ptr) {
}
Q_INVOKABLE void handleEventsOf(QObject *object) {
if (object)
object->installEventFilter(this);
}
bool eventFilter(QObject *object, QEvent *event) override {
if(event->type() == QEvent::DragMove) {
QDragMoveEvent *mouseEvent = static_cast<QDragMoveEvent*>(event);
qDebug() << "Mouse position dragging (x, y): (" << mouseEvent->pos().x() << ", " << mouseEvent->pos().y() << ")";
return false; //this is must return false or drop event will be handled by this method and drag&drop won't work correctly
}
return false;
}
};
Now we need to access to an instance (singleton in this case) of this class with qmlRegisterSingletonType. You may wish to use qmlRegisterType instead to register this eventlistener as a type (instead of a singleton) and use signals to notify directly qml the mouse position.
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "eventlistener.h"
static QObject *eventsListenerInstance(QQmlEngine *qmlEngine, QJSEngine *engine)
{
return new EventsListener(engine);
}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterSingletonType<EventsListener>("AppEventListener", 1, 0, "EventsListener", eventsListenerInstance);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml:
import ...
import AppEventListener 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
id: item
property string display
property alias dropEnabled: acceptDropCB.checked
color: dropArea.containsDrag ? "#CFC" : "#EEE"
Component.onCompleted: EventsListener.handleEventsOf(item)
...
}

How to achieve exclusive (e.g. Radio Button) type behavior with QGraphicObject?

I'm working with Qt (v 5.3) again after sometime away. As a learning exercise I am prototyping a module using QGraphicView in order to understand better how to use it.
The functionality is simple: I have several QGraphicObjects which function like buttons with states and behaviors:
OFF - image panel hidden; default button art
ON - image panel
displayed; highlight button art
At startup all buttons are in an OFF state
A clicked button will toggle it's state (non-Exclusive)
If a different button is already in an ON state, it must be turned off (Exclusive)
Everything is data driven and created dynamically at runtime.
So that is my little learning exercise. What I am trying to sort out is an efficient messaging mechanism to handle the "radio button sometimes" exclusive behavior and sending a message to a group of objects without strongly coupling them.
I've looked at signals and slots but that gets tedious if there are many connections to make.
QSignalMapper which seems somewhat better
QEvent is probably the most solid approach but I've had trouble finding good learning examples.
I am making this more complicated than need be but as I say, it's a learning exercise to get used to Qt again.
So my question (finally): is there an approach I am overlooking or one of these mentioned which would be better (e.g most flexible, maintainable, scalable). Not looking for code per se, but an understanding of how to go about coding something like this using the Qt framework.
Here's an image of the thing:
Signal and slots is the method I would use here. However, rather than connect each button to every other button, you can create an intermediate, Controller class.
The Controller class can either aggregate, or be a parent to all of the buttons.
When a button is created, it connects signals to the controller to inform it of being pressed. The receiving slot in the Controller class is then responsible for turning off any other buttons.
Skeleton code: -
class ButtonController : public QGraphicsObject
{
public slots:
void ButtonPressed();
private:
QList<MyButton*> m_buttonList;
};
void ButtonController::ButtonPressed()
{
foreach(MyButton* pButton, m_buttonList)
{
if(pButton != sender())
{
pButton->Off();
}
}
}
In the constructor of MyButton, assuming the controller is the parent: -
MyButton::MyButton(ButtonController* parent)
: QGraphicsObject(parent);
{
connect(this, &MyButton::pressed(), parent, &ButtonController::ButtonPressed());
...
}
Alternatively, the controller may just hold a list of the buttons, in which case you can do this: -
MyButton::MyButton(QObject* parent, ButtonController* pButtonController)
: QGraphicsObject(parent);
{
connect(this, &MyButton::pressed(), pButtonController, &ButtonController::ButtonPressed());
...
}
In the case of not being able to change the constructor of the buttons, the controller may simply have an AddButton function, in which it creates the connection when the button is added under its control.
Subclass QGraphicsScene
Reimplement mousePressEvent and check is left mouse button pressed.
If so, then get position of mouse by pos() method and get current graphics object under arrow by itemAt() method.
Try to cast it into your needed object, if it was successfully, then change background and other.
Store your graphics objects in vector, in this case you can check every object and decide which object should be turned off.
Fully working example:
header:
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QPoint>
#include <QMouseEvent>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
signals:
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
public slots:
private:
QVector<QGraphicsEllipseItem * > vec;
};
#endif // GRAPHICSSCENE_H
cpp:
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsItem>
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
//set circles in different positions.
vec.push_back(addEllipse(0,0,50,50,QPen(Qt::red),QBrush(Qt::blue)));
vec.push_back(addEllipse(0+100,0+100,50,50,QPen(Qt::red),QBrush(Qt::blue)));
vec.push_back(addEllipse(0+150,0+150,50,50,QPen(Qt::red),QBrush(Qt::blue)));
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
//qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
QGraphicsEllipseItem *ell = qgraphicsitem_cast<QGraphicsEllipseItem *>(item);
if(ell)
{
for(int i = 0; i < vec.size(); i++)
{
if(vec.at(i) != ell)
{
vec.at(i)->setBrush(QBrush(Qt::blue));//disable all other bettons
}
}
ell->setBrush(QBrush(Qt::black));//to chosen circle
qDebug() << "good";
}
else
qDebug() << "not ell" << mouseEvent->scenePos();
}
}
Usage:
GraphicsScene *scene = new GraphicsScene(this);
ui->graphicsView->setScene(scene);
With this case, your circles will be similar to radioButtons. If you want save state of all buttons you can use:
for(int i = 0; i < vec.size(); i++)
vec.at(i)->setData(0,false);
And get state with
if(!ell->data(0).toBool())
//do

App does not close if I set QQuickView-WindowContainer as central widget

My application is designed in that way that different plugins can set the central widget of the main windows to show the desired content.
This works so far.
But if I set a QQuickView-WindowContainer as central widget, the app does not quit when I close the main window.
If I set a "normal" widget like QPushButton as central widget the appliation quits just fine. Why is that?
This is the code of a minimal example which shows this behaviour (MainWindow is a class created from the QtCreator wizard):
class AppCore : public QObject
{
Q_OBJECT
public:
explicit AppCore(QObject *parent = 0);
signals:
public slots:
void showMainWindow();
private:
MainWindow *m_mainWindow;
};
AppCore::AppCore(QObject *parent) :
QObject(parent)
{
}
void AppCore::showMainWindow()
{
QQuickView *view;
QWidget *container;
view = new QQuickView();
container = QWidget::createWindowContainer(view);
view->setSource(QUrl("qrc:/main.qml"));
m_mainWindow = new MainWindow();
//m_mainWindow->setCentralWidget(new QPushButton("Button"));
m_mainWindow->setCentralWidget(container);
m_mainWindow->show();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
AppCore appCore;
appCore.showMainWindow();;
return a.exec();
}
This looks like a bug. I see a dead lock in debugger: v8::internal::RuntimeProfiler::WaitForSomeIsolateToEnterJS and QQmlDataLoader::shutdownThread wait for each other. I can't find a good workaround for this issue.
I found a dirty hack that solved the issue. If container is deleted a bit earlier, all works ok:
void MainWindow::closeEvent(QCloseEvent *e) {
QMainWindow::closeEvent(e);
if (e->isAccepted() && centralWidget()) {
delete centralWidget();
}
}
You probably should send a bug report. Note that m_mainWindow is not needed to reproduce the issue. Using container->show(); gives the same result.

Taking screenshot from inside fullscreen Qt Quick Application

This was my original question:
I just want to take a screenshot (using the Print key) of my fullscreen QtQuick 2 application. But all I get is a black or sometimes white screenshot. When the application is not started in fullscreen it works.
SOLUTION
I thought I post a really nice solution here,
even though it does not solve the original problem of taking the screenshot with an external tool.
Starting with the suggestion from the accepted answer I did the following:
First I added a signal to my QML main class (in main.qml)
signal takeScreenShot()
This signal is emmited by pressing a certain button.
Then I wrote a C++ / QT class autside the QML code to handle this signal:
class QMLSupplement : public QObject
{
Q_OBJECT
public:
QQuickView* view;
public:
QMLSupplement(QObject* parent = 0);
public slots:
void takeScreenShot();
};
The reference to QQuickView is used to take the screenshot.
void QMLSupplement::takeScreenShot()
{
QString file;
file = QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss");
file += ".png";
qDebug() << "taking screenshot, saving here:" << file;
view->grabWindow().save(file);
}
Finally I connect the signal and the slot in main.cpp:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl::fromLocalFile("./qml/main.qml"));
view.setResizeMode(QQuickView::SizeRootObjectToView);
QObject* rootObject = (QObject*) view.rootObject();
QMLSupplement supplement;
supplement.view = &view;
QObject::connect(rootObject, SIGNAL(takeScreenShot()),
&supplement, SLOT(takeScreenShot()));
view.show();
// view.showFullScreen();
return app.exec();
}
That's a limitation of the platform where you're running. If you care about this working, you need to implement the functionality yourself. Qt provides you with enough to get the contents of the Qt Quick 2 window and post it to the clipboard as an image.
In your print key handler, if you detect that the window is full-screen, you need to pass the QQuickWindow instance to a helper function:
void grabAndCopy(QQuickWindow * window) {
QApplication::clipboard()->setImage(window->grabWindow());
}
...
if (window->windowState() == Qt::WindowFullScreen) grabAndCopy(window);

Resources