ProcessInterface.hh 13.7 KB
 Jan Möbius committed Oct 01, 2009 1 /*===========================================================================*\  Jan Möbius committed Nov 25, 2010 2 3 * * * OpenFlipper *  Martin Schultz committed Jul 16, 2015 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38  * Copyright (c) 2001-2015, RWTH-Aachen University * * Department of Computer Graphics and Multimedia * * All rights reserved. * * www.openflipper.org * * * *---------------------------------------------------------------------------* * This file is part of OpenFlipper. * *---------------------------------------------------------------------------* * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * * * 1. Redistributions of source code must retain the above copyright notice, * * this list of conditions and the following disclaimer. * * * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * * * 3. Neither the name of the copyright holder nor the names of its * * contributors may be used to endorse or promote products derived from * * this software without specific prior written permission. * * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *  Jan Möbius committed Nov 25, 2010 39 * *  Jan Möbius committed Oct 01, 2009 40 41 \*===========================================================================*/  Jan Möbius committed Jan 08, 2019 42 #pragma once  Jan Möbius committed Oct 01, 2009 43   Matthias Möller committed Aug 25, 2014 44 #include  Matthias Möller committed Jul 19, 2013 45   Jan Möbius committed Oct 01, 2009 46   Jan Möbius committed Mar 16, 2011 47 48 49 50 51 52 53 54 55 56 57 58 /** \file ProcessInterface.hh * * Interface class for Thread and Process handling.\ref processInterfacePage */ /** \brief Interface class for Thread handling. * * \ref processInterfacePage "Detailed description" * \n * * This Interface is used to handle threads in OpenFlipper. You can run arbitrary slots within * separate threads.  Jan Möbius committed Oct 01, 2009 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73  */ class ProcessInterface { public : /// Destructor virtual ~ProcessInterface() {}; signals : /** \brief announce a new job * * Use this function to announce a new job. The Job Id should be a unique id for your job. * Prepend your PluginName to your id to make it unique across plugins. *  Jan Möbius committed Mar 17, 2011 74 75 76  * The range between _min and _max will be used to compute the progress percentage based on * the ProcessInterface::setJobState() values provided by the thread. You could for example * set it to the number of steps performed in your algorithm.  Jan Möbius committed Oct 02, 2009 77  *  Jan Möbius committed Mar 17, 2011 78 79 80 81 82  * @param _jobId String which is used as the id of the thread * @param _description The description will be the string visible to the user. * @param _min minimum of the status value range * @param _max maximum of the status range * @param _blocking If the thread is blocking, no other interaction will be allowed  Jan Möbius committed Oct 01, 2009 83  */  Jan Möbius committed Mar 17, 2011 84  virtual void startJob( QString _jobId, QString _description , int _min , int _max , bool _blocking = false) {};  Jan Möbius committed Oct 01, 2009 85 86 87 88 89  /** \brief update job state * * Emit this signal to tell the core about your job status. *  Jan Möbius committed Mar 17, 2011 90 91  * @param _jobId String which is used as the id of the thread * @param _value The status value has to be in the range you defined in ProcessInterface::startJob()  Jan Möbius committed Oct 01, 2009 92  */  Jan Möbius committed Mar 17, 2011 93  virtual void setJobState(QString _jobId, int _value ) {};  Jan Möbius committed Oct 01, 2009 94   Mike Kremer committed Feb 22, 2010 95 96 97 98  /** \brief update job's name * * Emit this signal to tell the core to change a job's name. *  Jan Möbius committed Mar 17, 2011 99 100  * @param _jobId String which is used as the id of the thread * @param _name The new name of the job  Mike Kremer committed Feb 22, 2010 101  */  Jan Möbius committed Mar 17, 2011 102  virtual void setJobName(QString _jobId, QString _name ) {};  Mike Kremer committed Feb 22, 2010 103 104 105 106 107  /** \brief update job's description text * * Emit this signal to tell the core to change a job's widget's description. *  Jan Möbius committed Mar 17, 2011 108 109  * @param _jobId String which is used as the id of the thread * @param _text The text of the job's description  Mike Kremer committed Feb 22, 2010 110  */  Jan Möbius committed Mar 20, 2012 111  virtual void setJobDescription(QString _jobId, QString _text ) {};  Mike Kremer committed Feb 22, 2010 112   Jan Möbius committed Oct 01, 2009 113  /** \brief Cancel your job  Jan Möbius committed Mar 17, 2011 114 115  * * @param _jobId String which is used as the id of the thread  Jan Möbius committed Oct 01, 2009 116  */  Jan Möbius committed Mar 17, 2011 117  virtual void cancelJob(QString _jobId ) {};  Jan Möbius committed Oct 01, 2009 118 119  /** \brief Finish your job  Jan Möbius committed Mar 17, 2011 120 121 122 123 124  * * This signal has to be emitted when your job has finished. You can * directly connect the threads finished signal to this signal. * * @param _jobId String which is used as the id of the thread  Jan Möbius committed Oct 01, 2009 125  */  Jan Möbius committed Mar 17, 2011 126  virtual void finishJob(QString _jobId ) {};  Matthias Möller committed Feb 10, 2015 127   Jan Möbius committed Oct 01, 2009 128  private slots :  Jan Möbius committed Mar 20, 2012 129   Jan Möbius committed Oct 01, 2009 130 131 132 133  /** \brief A job has been canceled * * This function is called when the user cancels a job. * The returned name is the name of the job which has been canceled  Jan Möbius committed Mar 17, 2011 134  *  Jan Möbius committed Mar 20, 2012 135  * @param _job String which is used as the id of the thread  Jan Möbius committed Oct 01, 2009 136  */  Jan Möbius committed Mar 17, 2011 137  virtual void canceledJob (QString _job ) {};  Jan Möbius committed Oct 01, 2009 138 139 140  };  Jan Möbius committed Mar 16, 2011 141 142 143 144 145 146 147 148 149 150  /** \page processInterfacePage Process Interface * \n \image html ProcessInterface.png \n The Process interface can be used to run arbitrary slots of your plugin within a separate thread of OpenFlipper. OpenFlipper supports a process manager, visualizing currently running operations to the user. Process dialogs and user information can be provided via special signals.  Jan Möbius committed Mar 17, 2011 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 As in qt user interface operations are only allowed from within the main thread, you can not interact with the ui in your threads. Feedback like the progress or other messages need to be passed through the ProcessInterface. Additionally you have to make sure, that your operations can actually run in parallel to others. If not, you have to mark your thread as blocking, which would prevent the user from starting additional operations until your job gets finished. Using threads in OpenFlipper is quite simple. All necessary class definitions are already available to plugins. A tutorial for the thread interface is available: \ref tutorial_thread \section processInterface_setup Process Setup So the first thing we have to do is instantiate an OpenFlipperThread object and tell the core that we just created a thread via ProcessInterface::startJob(). \code // Create job named "My Thread" .. Name will e visible in OpenFlippers process manager. OpenFlipperThread* thread = new OpenFlipperThread(name() + "My thread"); // Tell OpenFlipper about the newly created job. In this case, it gets a description and // a span for its steps. This job has 100 steps from 0 to 100. These values are used // to display a progress bar. // The last parameter means that the job will be non-blocking, so the ui will still be available. emit startJob( name() + "My thread", "My thread's description" , 0 , 100 , false); \endcode Next thing to do is connecting the thread's signal to local signals/slots in order to keep track of updates concerning the processing of our thread. \code // Slot connection required to tell the core, that the thread has finished connect(thread, SIGNAL(finished(QString)), this, SIGNAL(finishJob(QString))); // Slot connection required setting the function in your plugin that should run inside the thread. // It will get the job name which can be used to control the ui elements like progress. // If you don't want to show progress you can skip the jobId QString. connect(thread, SIGNAL(function(QString)), this, SLOT(testFunctionThread(QString)), Qt::DirectConnection); \endcode \section processInterface_start Process Startup The function() signal needs to be connected to the function that will actually be processed in the thread. Once having connected these signals, we're about to start our thread: \code // Launch the thread thread->start(); // Start processing of the function code (connected via signal OpenFlipperThread::function() ) // This function is non-blocking and will return immediately. thread->startProcessing(); \endcode \section processInterface_status Status Reports There are two signals that you can use to inform the user about your threads state. The first is ProcessInterface::setJobState(). It gets the jobId and an int. The int is the process status. It has to be in the values that have been given with ProcessInterface::startJob() before. OpenFlipper will calculate the percentage on its own and display a progress bar at the specific position.  Jan Möbius committed Mar 20, 2012 208 The second function is ProcessInterface::setJobDescription() getting a description, that can be changed  Jan Möbius committed Mar 17, 2011 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 wile the thread is running. The following example shows a simple thread slot,using these signals. \code // Useless example function which runs in a thread and updates the ui state. // The QString parameter is only necessary if you want to provide feedback. void testFunctionThread(QString _jobId) { for (int i = 0 ; i < 1000000; ++i) { // Emit new progress state if(i % 10000 == 0) emit setJobState(_jobId, (int)(i / 10000)); if(i == 500000) { // When half of the processing is finished // change the job's description emit setJobDescription(_jobId, "Half way done"); } } } \endcode  Jan Möbius committed Mar 16, 2011 230   Jan Möbius committed Mar 17, 2011 231 \section processInterface_usage Usage  Jan Möbius committed Mar 16, 2011 232   Jan Möbius committed Mar 17, 2011 233 234 235 236 237 238 239 To use the ProcessInterface:
 Jan Möbius committed Mar 16, 2011 240   241 242 243 244 \note In general, OpenFlipper signals should not be emitted from other threads than the main thread. However, there are some signals, which are save to call outside of another thread. Some will block, until the main thread executed the function, some run asnyc. If a function is guaranteed to be threadsafe, you will find a note in the documentation e.g. \c LoggingInterface::log()  Jan Möbius committed Mar 17, 2011 245 246 \section processInterface_quickstart Quick example A quick example for stating a thread:  Jan Möbius committed Mar 16, 2011 247   Jan Möbius committed Mar 17, 2011 248 249 250 251 252 253 254 255 \code OpenFlipperThread* thread = new OpenFlipperThread(name() + "unique id"); // Create your thread containing a unique id connect(thread,SIGNAL( finished(QString)), this,SIGNAL(finishJob(QString))); // connect your threads finish info to the global one ( you can do the same for a local one ) connect(thread,SIGNAL( function(QString)), this,SLOT(testFunctionThread(QString)),Qt::DirectConnection); // You can directly give a slot of your app that gets called emit startJob( name() + "unique id", "Description" , 0 , 100 , false); // Tell the core about your thread thread->start(); // start thread thread->startProcessing(); // start processing \endcode  256   Matthias Möller committed Feb 10, 2015 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 Note: If your class is derived from OpenFlipperThread, do not reimplement the \a run() function. Instead, use the \a function(QString) to connect the your running function with \a Qt::DirectConnection. Short example: Be \a MyThread the derived thread class \code{.cpp} class MyThread : public OpenFlipperThread \endcode and \a parallelFunction(QString) the function which should run by different threads. \code void MyThread::parallelFunction(QString) // function which will run in parallel { std::cout << "I am running in another thread." << currentThreadId() << std::endl; } \endcode Instead of reimplementing the run function, connect the \a parallelFunction(QString) with the \a function(QString) signal in e.g. the constructor: \code MyThread::MyThread(QString _jobId):OpenFlipperThread(_jobId) { // connect signal function(QString) with the self defined slot function connect(this,SIGNAL(function(QString)),this,SLOT(parallelFunction(QString)), Qt::DirectConnection); } \endcode Note: The OpenFlipperThread will not be notified about job cancelling. You plugin has to be aware of it. Note: You might also want to connect the signal OpenFlipperThread::state()  287 288 289 290 291 292 to the plugin's signal setJobState(): \code connect(thread, SIGNAL(state(QString, int)), this, SIGNAL(setJobState(QString, int))); \endcode  Matthias Möller committed Feb 10, 2015 293   294 295 Use signal OpenFlipperThread::state() within your run() function in order to correctly update the job's state.  Jan Möbius committed Mar 16, 2011 296 297 */  Jan Möbius committed Oct 01, 2009 298 299 Q_DECLARE_INTERFACE(ProcessInterface,"OpenFlipper.ProcessInterface/1.0")