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,6 +18,109 @@ using namespace cv; | ||
| 18 | 18 | ||
| 19 | namespace br | 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 | QImage toQImage(const Mat &mat) | 124 | QImage toQImage(const Mat &mat) |
| 22 | { | 125 | { |
| 23 | // Convert to 8U depth | 126 | // Convert to 8U depth |
| @@ -280,75 +383,6 @@ private: | @@ -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 | * \ingroup transforms | 387 | * \ingroup transforms |
| 354 | * \brief Displays templates in a GUI pop-up window using QT. | 388 | * \brief Displays templates in a GUI pop-up window using QT. |
| @@ -666,6 +700,11 @@ public: | @@ -666,6 +700,11 @@ public: | ||
| 666 | BR_REGISTER(Transform, SurveyTransform) | 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 | class FPSLimit : public TimeVaryingTransform | 708 | class FPSLimit : public TimeVaryingTransform |
| 670 | { | 709 | { |
| 671 | Q_OBJECT | 710 | Q_OBJECT |
| @@ -716,6 +755,12 @@ protected: | @@ -716,6 +755,12 @@ protected: | ||
| 716 | }; | 755 | }; |
| 717 | BR_REGISTER(Transform, FPSLimit) | 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 | class FPSCalc : public TimeVaryingTransform | 764 | class FPSCalc : public TimeVaryingTransform |
| 720 | { | 765 | { |
| 721 | Q_OBJECT | 766 | Q_OBJECT |