Commit ead4a56ac9bbec8cfa15e917cbd85b1b416d41fe
1 parent
d4d04907
Slight reorganization and comment update in gui.cpp
Clearly obtuse solutions to obtuse problems deserve top billing.
Showing
1 changed file
with
114 additions
and
69 deletions
openbr/plugins/gui.cpp
| ... | ... | @@ -18,6 +18,109 @@ using namespace cv; |
| 18 | 18 | |
| 19 | 19 | namespace br |
| 20 | 20 | { |
| 21 | +// Generally speaking, Qt wants GUI objects to be on the main thread, and | |
| 22 | +// for the main thread to be in an event loop. We don't restrict transform | |
| 23 | +// creation to just the main thread, but in br we do compromise and put | |
| 24 | +// the main thread in an event loop (and so should any applications wanting to | |
| 25 | +// use GUI transforms). This does mean we need a way to make our QWidget subclasses | |
| 26 | +// on the main thread. We can't create them from an arbitrary thread, then move them | |
| 27 | +// (you know, since that would be crazy), so we need some tricks to get the main | |
| 28 | +// thread to make these objects. | |
| 29 | + | |
| 30 | +// Part 1. A generic interface for creating objects, the type of object | |
| 31 | +// created is not exposed in the interface. | |
| 32 | +class NominalCreation | |
| 33 | +{ | |
| 34 | +public: | |
| 35 | + virtual ~NominalCreation() {} | |
| 36 | + virtual void creation()=0; | |
| 37 | +}; | |
| 38 | + | |
| 39 | +// Part 2. A template class that creates an object of the specified type | |
| 40 | +// through the interface defined in part 1. The point of this is that the | |
| 41 | +// type of object created can be hidden by using a NominalCreation *. | |
| 42 | +template<typename T> | |
| 43 | +class ActualCreation : public NominalCreation | |
| 44 | +{ | |
| 45 | +public: | |
| 46 | + T * basis; | |
| 47 | + | |
| 48 | + void creation() | |
| 49 | + { | |
| 50 | + basis = new T(); | |
| 51 | + } | |
| 52 | +}; | |
| 53 | + | |
| 54 | +// Part 3. A class that inherits from QObject, but not QWidget. This means | |
| 55 | +// we are free to move it to the main thread. | |
| 56 | +// If this object is on the main thread, and we signal one of its slots, then | |
| 57 | +// the slot will be executed by the main thread, which is what we need. | |
| 58 | +// Unfortunately, since it uses Q_OBJECT we cannot make it a template class, but | |
| 59 | +// we still want to be able to make objects of arbitrary type on the main thread, | |
| 60 | +// so that we don't need a different adaptor for every type of QWidget subclass we use. | |
| 61 | +class MainThreadCreator : public QObject | |
| 62 | +{ | |
| 63 | + Q_OBJECT | |
| 64 | +public: | |
| 65 | + | |
| 66 | + MainThreadCreator() | |
| 67 | + { | |
| 68 | + this->moveToThread(QApplication::instance()->thread()); | |
| 69 | + // We actually bind a signal on this object to one of its own slots. | |
| 70 | + // the signal will be emitted by a call to getItem from an abitrary | |
| 71 | + // thread. | |
| 72 | + connect(this, SIGNAL(needCreation()), this, SLOT(createThing()), Qt::BlockingQueuedConnection); | |
| 73 | + } | |
| 74 | + | |
| 75 | + // While this cannot be a template class, it can still have a template | |
| 76 | + // method. Which is useful, but the slot which will actually be executed | |
| 77 | + // by the main thread cannot be a template. So, we use the template method | |
| 78 | + // here (called from an arbitrary thread, to create an object of arbitrary type) | |
| 79 | + // to instantiate an ActualCreation object with matching type, then we hide | |
| 80 | + // the template with the NominalCreation interface, and call worker->creation | |
| 81 | + // in the slot. | |
| 82 | + template<typename T> | |
| 83 | + T * getItem() | |
| 84 | + { | |
| 85 | + // If this is called by the main thread, we can just create the object | |
| 86 | + // it's important to check, otherwise we will have problems trying to | |
| 87 | + // wait for a blocking connection that is supposed to be processed by | |
| 88 | + // the thread that is waiting. | |
| 89 | + if (QThread::currentThread() == QApplication::instance()->thread()) | |
| 90 | + return new T(); | |
| 91 | + | |
| 92 | + // Create the object creation interface | |
| 93 | + ActualCreation<T> * actualWorker; | |
| 94 | + actualWorker = new ActualCreation<T> (); | |
| 95 | + // hide it | |
| 96 | + worker = actualWorker; | |
| 97 | + | |
| 98 | + // emit the signal, we set up a blocking queued connection, so | |
| 99 | + // this is a blocking wait for the slot to finish being run. | |
| 100 | + emit needCreation(); | |
| 101 | + | |
| 102 | + // collect the results, and return. | |
| 103 | + T * output = actualWorker->basis; | |
| 104 | + delete actualWorker; | |
| 105 | + return output; | |
| 106 | + } | |
| 107 | + | |
| 108 | + NominalCreation * worker; | |
| 109 | + | |
| 110 | +signals: | |
| 111 | + void needCreation(); | |
| 112 | + | |
| 113 | +public slots: | |
| 114 | + // The actual slot, to be run by the main thread. The type | |
| 115 | + // of object being created is not, and indeed cannot, be exposed here | |
| 116 | + // since this cannot be a template method, and the class cannot be a | |
| 117 | + // template class. | |
| 118 | + void createThing() | |
| 119 | + { | |
| 120 | + worker->creation(); | |
| 121 | + } | |
| 122 | +}; | |
| 123 | + | |
| 21 | 124 | QImage toQImage(const Mat &mat) |
| 22 | 125 | { |
| 23 | 126 | // Convert to 8U depth |
| ... | ... | @@ -280,75 +383,6 @@ private: |
| 280 | 383 | |
| 281 | 384 | }; |
| 282 | 385 | |
| 283 | -// I want a template class that doesn't look like a template class | |
| 284 | -class NominalCreation | |
| 285 | -{ | |
| 286 | -public: | |
| 287 | - virtual ~NominalCreation() {} | |
| 288 | - virtual void creation()=0; | |
| 289 | -}; | |
| 290 | - | |
| 291 | -// Putting the template on a subclass means we can maintain a pointer that | |
| 292 | -// doesn't include T in its type. | |
| 293 | -template<typename T> | |
| 294 | -class ActualCreation : public NominalCreation | |
| 295 | -{ | |
| 296 | -public: | |
| 297 | - T * basis; | |
| 298 | - | |
| 299 | - void creation() | |
| 300 | - { | |
| 301 | - basis = new T(); | |
| 302 | - } | |
| 303 | -}; | |
| 304 | - | |
| 305 | -// We want to create a QLabel subclass on the main thread, but are running in another thread. | |
| 306 | -// We cannot move QWidget subclasses to a different thread (obviously that would be crazy), but | |
| 307 | -// we can create one of these, and move it to the main thread, and then use it to create the object | |
| 308 | -// we want. | |
| 309 | -// Additional fact: QObject subclasses cannot be template classes. | |
| 310 | -class MainThreadCreator : public QObject | |
| 311 | -{ | |
| 312 | - Q_OBJECT | |
| 313 | -public: | |
| 314 | - | |
| 315 | - MainThreadCreator() | |
| 316 | - { | |
| 317 | - this->moveToThread(QApplication::instance()->thread()); | |
| 318 | - | |
| 319 | - connect(this, SIGNAL(needCreation()), this, SLOT(createThing()), Qt::BlockingQueuedConnection); | |
| 320 | - } | |
| 321 | - | |
| 322 | - // While this cannot be a template class, it can still have a template method. | |
| 323 | - template<typename T> | |
| 324 | - T * getItem() | |
| 325 | - { | |
| 326 | - if (QThread::currentThread() == QApplication::instance()->thread()) | |
| 327 | - return new T(); | |
| 328 | - | |
| 329 | - ActualCreation<T> * actualWorker; | |
| 330 | - actualWorker = new ActualCreation<T> (); | |
| 331 | - worker = actualWorker; | |
| 332 | - | |
| 333 | - emit needCreation(); | |
| 334 | - | |
| 335 | - T * output = actualWorker->basis; | |
| 336 | - delete actualWorker; | |
| 337 | - return output; | |
| 338 | - } | |
| 339 | - | |
| 340 | - NominalCreation * worker; | |
| 341 | - | |
| 342 | -signals: | |
| 343 | - void needCreation(); | |
| 344 | - | |
| 345 | -public slots: | |
| 346 | - void createThing() | |
| 347 | - { | |
| 348 | - worker->creation(); | |
| 349 | - } | |
| 350 | -}; | |
| 351 | - | |
| 352 | 386 | /*! |
| 353 | 387 | * \ingroup transforms |
| 354 | 388 | * \brief Displays templates in a GUI pop-up window using QT. |
| ... | ... | @@ -666,6 +700,11 @@ public: |
| 666 | 700 | BR_REGISTER(Transform, SurveyTransform) |
| 667 | 701 | |
| 668 | 702 | |
| 703 | +/*! | |
| 704 | + * \ingroup transforms | |
| 705 | + * \brief Limits the frequency of projects going through this transform to the input targetFPS | |
| 706 | + * \author Charles Otto \cite caotto | |
| 707 | + */ | |
| 669 | 708 | class FPSLimit : public TimeVaryingTransform |
| 670 | 709 | { |
| 671 | 710 | Q_OBJECT |
| ... | ... | @@ -716,6 +755,12 @@ protected: |
| 716 | 755 | }; |
| 717 | 756 | BR_REGISTER(Transform, FPSLimit) |
| 718 | 757 | |
| 758 | +/*! | |
| 759 | + * \ingroup transforms | |
| 760 | + * \brief Calculates the average FPS of projects going through this transform, stores the result in AvgFPS | |
| 761 | + * Reports an average FPS from the initialization of this transform onwards. | |
| 762 | + * \author Charles Otto \cite caotto | |
| 763 | + */ | |
| 719 | 764 | class FPSCalc : public TimeVaryingTransform |
| 720 | 765 | { |
| 721 | 766 | Q_OBJECT | ... | ... |