Results 1 to 7 of 7

Thread: QtConcurrent::mapped() and member functions

  1. #1
    Join Date
    May 2016
    Posts
    3
    Qt products
    Qt5
    Platforms
    Windows

    Default QtConcurrent::mapped() and member functions

    Hi,

    I'm trying to convert the "imagescaling" example http://doc.qt.io/qt-5/qtconcurrent-i...g-example.html to work with a call to a member function instead of the global one.

    Here is the modified code:

    imagescaling.cpp
    Qt Code:
    1. #include "imagescaling.h"
    2. #include <qmath.h>
    3.  
    4. const int imageSize = 100;
    5.  
    6. QImage* scale(const QString &imageFileName)
    7. {
    8. QImage* image = new QImage(imageFileName);
    9. image->scaled(QSize(imageSize, imageSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    10. return image;
    11. }
    12.  
    13. Images::Images(QWidget *parent)
    14. : QWidget(parent)
    15. {
    16. setWindowTitle(tr("Image loading and scaling example"));
    17. resize(800, 600);
    18.  
    19. imageScaling = new QFutureWatcher<QImage*>(this);
    20. connect(imageScaling, SIGNAL(resultReadyAt(int)), SLOT(showImage(int)));
    21. connect(imageScaling, SIGNAL(finished()), SLOT(finished()));
    22.  
    23. openButton = new QPushButton(tr("Open Images"));
    24. connect(openButton, SIGNAL(clicked()), SLOT(open()));
    25.  
    26. cancelButton = new QPushButton(tr("Cancel"));
    27. cancelButton->setEnabled(false);
    28. connect(cancelButton, SIGNAL(clicked()), imageScaling, SLOT(cancel()));
    29.  
    30. pauseButton = new QPushButton(tr("Pause/Resume"));
    31. pauseButton->setEnabled(false);
    32. connect(pauseButton, SIGNAL(clicked()), imageScaling, SLOT(togglePaused()));
    33.  
    34. QHBoxLayout *buttonLayout = new QHBoxLayout();
    35. buttonLayout->addWidget(openButton);
    36. buttonLayout->addWidget(cancelButton);
    37. buttonLayout->addWidget(pauseButton);
    38. buttonLayout->addStretch();
    39.  
    40. imagesLayout = new QGridLayout();
    41.  
    42. mainLayout = new QVBoxLayout();
    43. mainLayout->addLayout(buttonLayout);
    44. mainLayout->addLayout(imagesLayout);
    45. mainLayout->addStretch();
    46. setLayout(mainLayout);
    47. }
    48.  
    49. Images::~Images()
    50. {
    51. imageScaling->cancel();
    52. imageScaling->waitForFinished();
    53. }
    54.  
    55. QImage* Images::scale_(const QString &imageFileName)
    56. {
    57. QImage* image = new QImage(imageFileName);
    58. image->scaled(QSize(imageSize, imageSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    59. return image;
    60. }
    61.  
    62. void Images::open()
    63. {
    64. // Cancel and wait if we are already loading images.
    65. if (imageScaling->isRunning()) {
    66. imageScaling->cancel();
    67. imageScaling->waitForFinished();
    68. }
    69.  
    70. // Show a file open dialog at QStandardPaths::PicturesLocation.
    71. QStringList files = QFileDialog::getOpenFileNames(this, tr("Select Images"),
    72. QStandardPaths::writableLocation(QStandardPaths::PicturesLocation),
    73. "*.jpg *.png");
    74.  
    75. if (files.count() == 0)
    76. return;
    77.  
    78. // Do a simple layout.
    79. qDeleteAll(labels);
    80. labels.clear();
    81.  
    82. int dim = qSqrt(qreal(files.count())) + 1;
    83. for (int i = 0; i < dim; ++i) {
    84. for (int j = 0; j < dim; ++j) {
    85. QLabel *imageLabel = new QLabel;
    86. imageLabel->setFixedSize(imageSize,imageSize);
    87. imagesLayout->addWidget(imageLabel,i,j);
    88. labels.append(imageLabel);
    89. }
    90. }
    91.  
    92. // Use mapped to run the thread safe scale function on the files.
    93. //imageScaling->setFuture(QtConcurrent::mapped(files, scale));
    94. imageScaling->setFuture(QtConcurrent::mapped(files, [this](const QString &file){this->scale_(file);}));
    95.  
    96. openButton->setEnabled(false);
    97. cancelButton->setEnabled(true);
    98. pauseButton->setEnabled(true);
    99. }
    100.  
    101. void Images::showImage(int num)
    102. {
    103. labels[num]->setPixmap(QPixmap::fromImage(*(imageScaling->resultAt(num))));
    104. }
    105.  
    106. void Images::finished()
    107. {
    108. openButton->setEnabled(true);
    109. cancelButton->setEnabled(false);
    110. pauseButton->setEnabled(false);
    111. }
    To copy to clipboard, switch view to plain text mode 

    imagescaling.h

    Qt Code:
    1. #ifndef IMAGESCALING_H
    2. #define IMAGESCALING_H
    3.  
    4. #include <QtWidgets>
    5. #include <QtConcurrent>
    6.  
    7. class Images : public QWidget
    8. {
    9. Q_OBJECT
    10. public:
    11. Images(QWidget *parent = 0);
    12. ~Images();
    13. QImage *scale_(const QString &imageFileName);
    14. public Q_SLOTS:
    15. void open();
    16. void showImage(int num);
    17. void finished();
    18. private:
    19. QPushButton *openButton;
    20. QPushButton *cancelButton;
    21. QPushButton *pauseButton;
    22. QVBoxLayout *mainLayout;
    23. QList<QLabel *> labels;
    24. QGridLayout *imagesLayout;
    25. QFutureWatcher<QImage*> *imageScaling;
    26. };
    27.  
    28. #endif // IMAGESCALING_H
    To copy to clipboard, switch view to plain text mode 

    If I map the global function everything works:

    Qt Code:
    1. imageScaling->setFuture(QtConcurrent::mapped(files, scale));
    To copy to clipboard, switch view to plain text mode 

    If I map to the member function it won't compile:

    Qt Code:
    1. imageScaling->setFuture(QtConcurrent::mapped(files, &Images::scale_));
    2. or
    3. imageScaling->setFuture(QtConcurrent::mapped(files, [this](const QString &file){this->scale_(file);}));
    To copy to clipboard, switch view to plain text mode 

    ERROR:

    Qt Code:
    1. ...\imagescaling.cpp:133: Fehler: C2664: "void QFutureWatcher<QImage *>::setFuture(const QFuture<T> &)" : Converting of Argument 1 of "QFuture<void>" in "const QFuture<T> &" not possible
    2. with
    3. [
    4. T=QImage *
    5. ]
    To copy to clipboard, switch view to plain text mode 

    Is using QtConcurrent::mapped() the wrong approach to bind with member functions?

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: QtConcurrent::mapped() and member functions

    Any specific reason you want this to be a member function?
    Seems to just "leak" internal implementation details to the API of your class.

    Anyway:
    - Your first approach could work with a static member function.
    - The lambda probably didn't work because it has the wrong signature, i.e. it as "void" as its return type, you could try a lambda that returns T, in your case QImage*.

    Which leads to the question, why not stay with the type used in the Qt example? I.e. why allocate QImage on the head and get into the hassle of having to manually delete it again?
    Also you should probably revisit the example to look on how the scaling is done.
    Your functions (both standalone and member) do not returned scaled images while still needing to calculate the scaling but immediately discarding its result.

    Cheers,
    _

  3. #3
    Join Date
    May 2016
    Posts
    3
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QtConcurrent::mapped() and member functions

    Thanks for your answer!

    I used the imagescale example as a simplyfied demonstration to show what I am trying to achieve in my own implementation of QFutureWatcher, QFuture, QtConcurrent.

    In my application I have the MainWindow (QMainWindow) which has a ImageManager member.
    The MainWindow handles all the GUI Input/Output, the ImageManager handles the data (i.e. image processing and holds all the references to the processed images and the image data).

    i.e.: The MainWindow triggers, via a connection, the slot function of the ImageManager member, which will load the image files and process them.
    The ImageManager member function, I want to map to the QFutureWatcher->setFuture(QtConcurrent::mapped(files, &ImageManager::createImage)); does:

    1. Loads the image
    2. Processes the image
    3. Puts a reference (pointer) into a QList<QImage*> list
    4. Emittes a signal, which is connected to a SLOT in the MainWindow, to add the image to a QListWidget
    5. Returns the reference (pointer)

    Member function I want to map and call concurrent:

    Qt Code:
    1. QImage* ImageManager::createImage(const QString &file)
    2. {
    3. cv::Mat image = cv::imread(file.toLocal8Bit().constData());
    4. ...
    5.  
    6. QImage* qImage = ImageUtils::cvMatToQImage(image);
    7. m_imageList.append(qImage);
    8. emit imageAdded(qImage);
    9.  
    10. return qImage;
    11. }
    To copy to clipboard, switch view to plain text mode 

    Connection / SLOT in MainWindow:
    Qt Code:
    1. connect(m_imageManager, &ImageManager::imageAdded, this, &MainWindow::addImage);
    2.  
    3. void MainWindow::addImage(QImage* image)
    4. {
    5. label->setPixmap(QPixmap::fromImage(*(image)));
    6. }
    To copy to clipboard, switch view to plain text mode 

    I've changed the lambda to:
    Qt Code:
    1. imageScaling->setFuture(QtConcurrent::mapped(files, [this](const QString &file)->QImage*{return this->scale_(file);}));
    To copy to clipboard, switch view to plain text mode 
    But the compiler message remains the same.

    Maybe I'm doing it the wrong way all along and need to approach this problem an other way?

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: QtConcurrent::mapped() and member functions

    Quote Originally Posted by h.a.n.d View Post
    1. Loads the image
    2. Processes the image
    3. Puts a reference (pointer) into a QList<QImage*> list
    4. Emittes a signal, which is connected to a SLOT in the MainWindow, to add the image to a QListWidget
    5. Returns the reference (pointer)
    That sounds like you want to the the first two steps with QtConcurrent and then, when the future watchers signals availability, just handle the rest in the slot connected to the future watchers.
    So a stand-alone function that does 1+2 with QtConcurrent and handling the things that need the image manager instance in the slot of the image manager that handles the future watcher.

    Btw, it is very uncommon to use QImage as a pointer, as this only requires manual pointer ownership handling.

    Cheers,
    _

  5. #5
    Join Date
    May 2016
    Posts
    3
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QtConcurrent::mapped() and member functions

    I refactored the code to use stand-alone functions for the mapping.

    Returning the pointer of an object, created in a separate thread, was my first attempt not to copy the whole data again. But then I coudn't make use of the QObject->parent utility which deletes the object if the parent gets deleted, because the object was created in an other thread as the parent object.
    I guess returning it by value would be the better way.

    Is this best-practice?

  6. #6
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: QtConcurrent::mapped() and member functions

    Quote Originally Posted by h.a.n.d View Post
    Returning the pointer of an object, created in a separate thread, was my first attempt not to copy the whole data again.
    QImage is reference counted, copying it does not copy the data.

    Quote Originally Posted by h.a.n.d View Post
    But then I coudn't make use of the QObject->parent utility which deletes the object if the parent gets deleted
    QImage is not a QObject derived class

    Cheers,
    _

  7. #7
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: QtConcurrent::mapped() and member functions

    The only reason I can see for allocating QImage on the heap rather than simply relying on its implicit sharing semantics is a limitation in QtConcurrent. QFuture only allows read-only access to the results; if we are not interested in keeping the QImages around until all of them are computed (for instance if we just make QPixmaps of them and never need them again), then we can save memory by finding a way to mutably access them. This can be done by storing pointers (so that we can delete each QImage as soon as it has been processed), but a better alternative would simply be to wrap QImage in a structure with a mutable field, whose value can be reset to an empty QImage as needed. Or use a more sophisticated concurrency library instead of hacking around QtConcurrent.

Similar Threads

  1. how to document classes and member functions?
    By jackal in forum Qt Programming
    Replies: 15
    Last Post: 9th April 2011, 22:02
  2. QtConcurrent::run() with function member?
    By wookoon in forum Newbie
    Replies: 3
    Last Post: 8th July 2010, 13:12
  3. Replies: 4
    Last Post: 14th January 2010, 16:30
  4. Pointers to Member Functions
    By Doug Broadwell in forum General Programming
    Replies: 8
    Last Post: 15th May 2007, 23:08
  5. emiting signals from const member functions !?
    By sunil.thaha in forum Qt Programming
    Replies: 2
    Last Post: 25th March 2006, 11:29

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.