Commit ead4a56ac9bbec8cfa15e917cbd85b1b416d41fe

Authored by Charles Otto
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
... ...