[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
klflatexpreviewthread.cpp
1 /***************************************************************************
2  * file klflatexpreviewthread.cpp
3  * This file is part of the KLatexFormula Project.
4  * Copyright (C) 2011 by Philippe Faist
5  * philippe.faist at bluewin.ch
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program; if not, write to the *
19  * Free Software Foundation, Inc., *
20  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21  ***************************************************************************/
22 /* $Id: klflatexpreviewthread.cpp 998 2017-01-19 23:54:56Z phfaist $ */
23 
24 #include <QImage>
25 #include <QThread>
26 #include <QMutex>
27 #include <QWaitCondition>
28 #include <QQueue>
29 
30 #include <klfbackend.h>
31 
32 #include "klflatexpreviewthread.h"
33 #include "klflatexpreviewthread_p.h"
34 
35 
36 
37 KLFLatexPreviewHandler::KLFLatexPreviewHandler(QObject * parent)
38  : QObject(parent)
39 {
40 }
41 KLFLatexPreviewHandler::~KLFLatexPreviewHandler()
42 {
43 }
44 
46 {
47 }
49 {
50  Q_UNUSED(output);
51 }
52 void KLFLatexPreviewHandler::latexPreviewAvailable(const QImage& preview, const QImage& largePreview,
53  const QImage& fullPreview)
54 {
55  Q_UNUSED(preview); Q_UNUSED(largePreview); Q_UNUSED(fullPreview);
56 }
58 {
59  Q_UNUSED(preview);
60 }
62 {
63  Q_UNUSED(largePreview);
64 }
66 {
67  Q_UNUSED(fullPreview);
68 }
69 void KLFLatexPreviewHandler::latexPreviewError(const QString& errorString, int errorCode)
70 {
71  Q_UNUSED(errorString); Q_UNUSED(errorCode);
72 }
73 
74 
75 
76 // ---
77 
78 
79 KLFLatexPreviewThread::KLFLatexPreviewThread(QObject * parent)
80  : QThread(parent)
81 {
82  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
83 
85 
86  // we need to register meta-type KLFBackend::klfOutput/klfInput/klfSettings for our
87  // signal/meta-object call, so register it if not yet done
88  if (QMetaType::type("KLFBackend::klfOutput") == 0) {
89  qRegisterMetaType<KLFBackend::klfOutput>("KLFBackend::klfOutput") ;
90  }
91  if (QMetaType::type("KLFBackend::klfInput") == 0) {
92  qRegisterMetaType<KLFBackend::klfInput>("KLFBackend::klfInput") ;
93  }
94  if (QMetaType::type("KLFBackend::klfSettings") == 0) {
95  qRegisterMetaType<KLFBackend::klfSettings>("KLFBackend::klfSettings") ;
96  }
97  if (QMetaType::type("KLFLatexPreviewThreadWorker::Task") == 0) {
98  qRegisterMetaType<KLFLatexPreviewThreadWorker::Task>("KLFLatexPreviewThreadWorker::Task") ;
99  }
100  if (QMetaType::type("KLFLatexPreviewThread::TaskId") == 0) {
101  qRegisterMetaType<KLFLatexPreviewThread::TaskId>("KLFLatexPreviewThread::TaskId") ;
102  }
103 
104 
105  //
106  // create a worker that will do all the job for us
107  //
108 
109  d->worker = new KLFLatexPreviewThreadWorker;
110  d->worker->moveToThread(this);
111 
112  // create a direct-connection abort signal; this is fine because worker.abort() is thread-safe.
113  connect(d, SIGNAL(internalRequestAbort()), d->worker, SLOT(abort()), Qt::DirectConnection);
114 
115  // connect the signal that submits a new job.
116  connect(d, SIGNAL(internalRequestSubmitNewTask(KLFLatexPreviewThreadWorker::Task, bool,
117  KLFLatexPreviewThread::TaskId)),
118  d->worker, SLOT(threadSubmitTask(KLFLatexPreviewThreadWorker::Task, bool,
119  KLFLatexPreviewThread::TaskId)),
120  Qt::QueuedConnection);
121  // signal to clear all pending jobs
122  connect(d, SIGNAL(internalRequestClearPendingTasks()), d->worker, SLOT(threadClearPendingTasks()),
123  Qt::QueuedConnection);
124  // signal to cancel a specific task
125  connect(d, SIGNAL(internalRequestCancelTask(KLFLatexPreviewThread::TaskId)),
126  d->worker, SLOT(threadCancelTask(KLFLatexPreviewThread::TaskId)),
127  Qt::QueuedConnection);
128 }
129 
130 KLFLatexPreviewThread::~KLFLatexPreviewThread()
131 {
132  stop();
133 
134  if (d->worker) {
135  delete d->worker;
136  }
137 
139 }
140 
141 QSize KLFLatexPreviewThread::previewSize() const
142 { return d->previewSize; }
143 QSize KLFLatexPreviewThread::largePreviewSize() const
144 { return d->largePreviewSize; }
145 
146 void KLFLatexPreviewThread::setPreviewSize(const QSize& previewSize)
147 { d->previewSize = previewSize; }
148 void KLFLatexPreviewThread::setLargePreviewSize(const QSize& largePreviewSize)
149 { d->largePreviewSize = largePreviewSize; }
150 
151 
152 void KLFLatexPreviewThread::start(Priority priority)
153 {
154  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
155 
156  // fire up the thread
158 }
159 
160 void KLFLatexPreviewThread::stop()
161 {
162  // tell thread to stop, and wait for it
163  emit d->internalRequestAbort();
164  quit();
165  wait();
166 }
167 
168 
169 void KLFLatexPreviewThread::run()
170 {
171  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
172 
173  // fire up and enter the main loop.
174  QThread::run();
175 }
176 
177 KLFLatexPreviewThread::TaskId
178 /* */ KLFLatexPreviewThread::submitPreviewTask(const KLFBackend::klfInput& input,
179  const KLFBackend::klfSettings& settings,
180  KLFLatexPreviewHandler * outputhandler,
181  const QSize& previewSize,
182  const QSize& largePreviewSize)
183 {
184  KLFLatexPreviewThreadWorker::Task t;
185  t.input = input;
186  t.settings = settings;
187  t.handler = outputhandler;
188  t.previewSize = previewSize;
189  t.largePreviewSize = largePreviewSize;
190 
191  return d->submitTask(t, false, -1);
192 }
193 
194 KLFLatexPreviewThread::TaskId
195 /* */ KLFLatexPreviewThread::submitPreviewTask(const KLFBackend::klfInput& input,
196  const KLFBackend::klfSettings& settings,
197  KLFLatexPreviewHandler * outputhandler)
198 {
199  KLFLatexPreviewThreadWorker::Task t;
200  t.input = input;
201  t.settings = settings;
202  t.handler = outputhandler;
203  t.previewSize = d->previewSize;
204  t.largePreviewSize = d->largePreviewSize;
205 
206  return d->submitTask(t, false, -1);
207 }
208 
209 KLFLatexPreviewThread::TaskId
210 /* */ KLFLatexPreviewThread::clearAndSubmitPreviewTask(const KLFBackend::klfInput& input,
211  const KLFBackend::klfSettings& settings,
212  KLFLatexPreviewHandler * outputhandler,
213  const QSize& previewSize,
214  const QSize& largePreviewSize)
215 {
216  KLFLatexPreviewThreadWorker::Task t;
217  t.input = input;
218  t.settings = settings;
219  t.handler = outputhandler;
220  t.previewSize = previewSize;
221  t.largePreviewSize = largePreviewSize;
222 
223  return d->submitTask(t, true, -1);
224 }
225 
226 KLFLatexPreviewThread::TaskId
227 /* */ KLFLatexPreviewThread::clearAndSubmitPreviewTask(const KLFBackend::klfInput& input,
228  const KLFBackend::klfSettings& settings,
229  KLFLatexPreviewHandler * outputhandler)
230 {
231  KLFLatexPreviewThreadWorker::Task t;
232  t.input = input;
233  t.settings = settings;
234  t.handler = outputhandler;
235  t.previewSize = d->previewSize;
236  t.largePreviewSize = d->largePreviewSize;
237 
238  return d->submitTask(t, true, -1);
239 }
240 
241 KLFLatexPreviewThread::TaskId
242 /* */ KLFLatexPreviewThread::replaceSubmitPreviewTask(KLFLatexPreviewThread::TaskId replaceId,
243  const KLFBackend::klfInput& input,
244  const KLFBackend::klfSettings& settings,
245  KLFLatexPreviewHandler * outputhandler,
246  const QSize& previewSize,
247  const QSize& largePreviewSize)
248 {
249  KLFLatexPreviewThreadWorker::Task t;
250  t.input = input;
251  t.settings = settings;
252  t.handler = outputhandler;
253  t.previewSize = previewSize;
254  t.largePreviewSize = largePreviewSize;
255 
256  return d->submitTask(t, false, replaceId);
257 }
258 
259 KLFLatexPreviewThread::TaskId
260 /* */ KLFLatexPreviewThread::replaceSubmitPreviewTask(KLFLatexPreviewThread::TaskId replaceId,
261  const KLFBackend::klfInput& input,
262  const KLFBackend::klfSettings& settings,
263  KLFLatexPreviewHandler * outputhandler)
264 {
265  KLFLatexPreviewThreadWorker::Task t;
266  t.input = input;
267  t.settings = settings;
268  t.handler = outputhandler;
269  t.previewSize = d->previewSize;
270  t.largePreviewSize = d->largePreviewSize;
271 
272  return d->submitTask(t, false, replaceId);
273 }
274 
275 
276 
277 void KLFLatexPreviewThread::cancelTask(TaskId task)
278 {
279  emit d->internalRequestCancelTask(task);
280 }
281 void KLFLatexPreviewThread::clearPendingTasks()
282 {
283  emit d->internalRequestClearPendingTasks();
284 }
285 
286 
287 
288 
289 // -----
290 
291 
292 
293 void KLFLatexPreviewThreadWorker::threadSubmitTask(Task task, bool clearOtherJobs, TaskId replaceTaskId)
294 {
295  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
296 
297  if (clearOtherJobs) {
298  threadClearPendingTasks();
299  }
300  if (replaceTaskId) {
301  threadCancelTask(replaceTaskId);
302  }
303 
304  // enqueue the new task
305  newTasks.enqueue(task);
306 
307  klfDbg("enqueued task id="<<task.taskid) ;
308 
309  // and notify ourself in the event loop that there are more jobs to process
310  QMetaObject::invokeMethod(this, "threadProcessJobs", Qt::QueuedConnection);
311 }
312 
313 bool KLFLatexPreviewThreadWorker::threadCancelTask(TaskId taskid)
314 {
315  int k;
316  for (k = 0; k < newTasks.size(); ++k) {
317  if (newTasks.at(k).taskid == taskid) {
318  newTasks.removeAt(k);
319  return true;
320  }
321  }
322 
323  // this might not be an error, it could be that the task completed before we had
324  // a chance to cancel it
325  klfDbg("No such task ID: "<<taskid) ;
326  return false;
327 }
328 
329 void KLFLatexPreviewThreadWorker::threadClearPendingTasks()
330 {
331  newTasks.clear();
332 }
333 
334 void KLFLatexPreviewThreadWorker::threadProcessJobs()
335 {
336  KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
337 
338  Task task;
339  KLFBackend::klfOutput ouroutput;
340 
341  if (!newTasks.size()) {
342  return;
343  }
344 
345  if (_abort) {
346  return;
347  }
348 
349  // fetch task info
350  task = newTasks.dequeue();
351 
352  klfDbg("processing job ID="<<task.taskid) ;
353 
354  QImage img, prev, lprev;
355  if ( task.input.latex.trimmed().isEmpty() ) {
356  QMetaObject::invokeMethod(task.handler, "latexPreviewReset", Qt::QueuedConnection);
357  } else {
358  // and GO!
359  klfDbg("worker: running KLFBackend::getLatexFormula()") ;
360  ouroutput = KLFBackend::getLatexFormula(task.input, task.settings, false);
361  img = ouroutput.result;
362 
363  klfDbg("got result: status="<<ouroutput.status) ;
364 
365  if (ouroutput.status != 0) {
366  // error...
367  QMetaObject::invokeMethod(task.handler, "latexPreviewError", Qt::QueuedConnection,
368  Q_ARG(QString, ouroutput.errorstr),
369  Q_ARG(int, ouroutput.status));
370  } else {
371  // this method must be called first (by API design)
372  QMetaObject::invokeMethod(task.handler, "latexOutputAvailable", Qt::QueuedConnection,
373  Q_ARG(KLFBackend::klfOutput, ouroutput));
374  if (task.previewSize.isValid()) {
375  prev = img;
376  if (prev.width() > task.previewSize.width() || prev.height() > task.previewSize.height()) {
377  prev = img.scaled(task.previewSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
378  }
379  }
380  if (task.largePreviewSize.isValid()) {
381  lprev = img;
382  if (lprev.width() > task.largePreviewSize.width() || lprev.height() > task.largePreviewSize.height()) {
383  lprev = img.scaled(task.largePreviewSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
384  }
385  }
386 
387  QMetaObject::invokeMethod(task.handler, "latexPreviewAvailable", Qt::QueuedConnection,
388  Q_ARG(QImage, prev),
389  Q_ARG(QImage, lprev),
390  Q_ARG(QImage, img));
391  if (task.previewSize.isValid()) {
392  QMetaObject::invokeMethod(task.handler, "latexPreviewImageAvailable", Qt::QueuedConnection,
393  Q_ARG(QImage, prev));
394  }
395  if (task.largePreviewSize.isValid()) {
396  QMetaObject::invokeMethod(task.handler, "latexPreviewLargeImageAvailable", Qt::QueuedConnection,
397  Q_ARG(QImage, lprev));
398  }
399  QMetaObject::invokeMethod(task.handler, "latexPreviewFullImageAvailable", Qt::QueuedConnection,
400  Q_ARG(QImage, img));
401  }
402  }
403 
404  klfDbg("about to invoke delayed threadProcessJobs.") ;
405 
406  // continue processing jobs, but let the event loop have a chance to run a bit too.
407  QMetaObject::invokeMethod(this, "threadProcessJobs", Qt::QueuedConnection);
408 
409  klfDbg("threadProcessJobs: end") ;
410 }
411 
412 
413 
414 
415 
416 
417 
418 // ------------
419 
420 
421 KLFContLatexPreview::KLFContLatexPreview(KLFLatexPreviewThread *thread)
422  : QObject(thread)
423 {
424  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
425 
427 
428  setThread(thread);
429 }
430 
431 KLFContLatexPreview::~KLFContLatexPreview()
432 {
434 }
435 
436 KLF_DEFINE_PROPERTY_GET(KLFContLatexPreview, QSize, previewSize) ;
437 
438 KLF_DEFINE_PROPERTY_GET(KLFContLatexPreview, QSize, largePreviewSize) ;
439 
440 
441 void KLFContLatexPreview::setThread(KLFLatexPreviewThread * thread)
442 {
443  d->thread = thread;
444 }
445 
447 {
449 
450  if (d->input == input)
451  return false;
452 
453  d->input = input;
454  d->refreshPreview();
455  return true;
456 }
457 bool KLFContLatexPreview::setSettings(const KLFBackend::klfSettings& settings, bool disableExtraFormats)
458 {
459  KLFBackend::klfSettings s = settings;
460  if (disableExtraFormats) {
461  s.wantRaw = false;
462  s.wantPDF = false;
463  s.wantSVG = false;
464  }
465 
466  if (d->settings == s)
467  return false;
468 
469  d->settings = s;
470  d->refreshPreview();
471  return true;
472 }
473 
475 {
476  if (d->previewSize == previewSize)
477  return false;
478  d->previewSize = previewSize;
479  d->refreshPreview();
480  return true;
481 }
482 bool KLFContLatexPreview::setLargePreviewSize(const QSize& largePreviewSize)
483 {
484  if (d->largePreviewSize == largePreviewSize)
485  return false;
486  d->largePreviewSize = largePreviewSize;
487  d->refreshPreview();
488  return true;
489 }
490 
491 
492 
KLFBackend::klfSettings::wantSVG
bool wantSVG
Definition: klfbackend.h:285
KLFLatexPreviewHandler::latexOutputAvailable
virtual void latexOutputAvailable(const KLFBackend::klfOutput &output)
Definition: klflatexpreviewthread.cpp:48
QImage::height
int height() const
KLFBackend::klfSettings::wantRaw
bool wantRaw
Definition: klfbackend.h:277
QThread
QMetaType::type
int type(const char *typeName)
KLFContLatexPreview::setInput
bool setInput(const KLFBackend::klfInput &input)
Definition: klflatexpreviewthread.cpp:446
QThread::wait
bool wait(unsigned long time)
KLFBackend::klfSettings::wantPDF
bool wantPDF
Definition: klfbackend.h:281
QImage::scaled
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
QSize
KLFLatexPreviewHandler
Definition: klflatexpreviewthread.h:37
KLFBackend::klfInput
Specific input to KLFBackend::getLatexFormula()
Definition: klfbackend.h:306
KLF_FUNC_NAME
#define KLF_FUNC_NAME
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::thread
QThread * thread() const
QThread::quit
void quit()
KLFBackend::klfOutput::status
int status
A code describing the status of the request.
Definition: klfbackend.h:381
KLFLatexPreviewThread
Definition: klflatexpreviewthread.h:73
klfDbg
#define klfDbg(streamableItems)
KLFBackend::getLatexFormula
static klfOutput getLatexFormula(const klfInput &in, const klfSettings &settings, bool isMainThread=true)
The function that processes everything.
Definition: klfbackend.cpp:487
QObject
QThread::run
virtual void run()
KLF_DEBUG_BLOCK
#define KLF_DEBUG_BLOCK(msg)
QString
QThread::start
void start(Priority priority)
KLF_DELETE_PRIVATE
#define KLF_DELETE_PRIVATE
KLFLatexPreviewHandler::latexPreviewError
virtual void latexPreviewError(const QString &errorString, int errorCode)
Definition: klflatexpreviewthread.cpp:69
KLFContLatexPreview::setPreviewSize
bool setPreviewSize(const QSize &previewSize)
Definition: klflatexpreviewthread.cpp:474
KLFLatexPreviewHandler::latexPreviewLargeImageAvailable
virtual void latexPreviewLargeImageAvailable(const QImage &largePreview)
Definition: klflatexpreviewthread.cpp:61
klfbackend.h
Definition of class KLFBackend.
KLFLatexPreviewHandler::latexPreviewImageAvailable
virtual void latexPreviewImageAvailable(const QImage &preview)
Definition: klflatexpreviewthread.cpp:57
QMetaObject::invokeMethod
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
KLFLatexPreviewHandler::latexPreviewReset
virtual void latexPreviewReset()
Definition: klflatexpreviewthread.cpp:45
KLFBackend::klfOutput
KLFBackend::getLatexFormula() result.
Definition: klfbackend.h:370
KLFBackend::klfSettings
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:218
KLFContLatexPreview::setLargePreviewSize
bool setLargePreviewSize(const QSize &largePreviewSize)
Definition: klflatexpreviewthread.cpp:482
KLFBackend::klfOutput::result
QImage result
The actual resulting image.
Definition: klfbackend.h:393
KLF_INIT_PRIVATE
#define KLF_INIT_PRIVATE(ClassName)
KLFLatexPreviewHandler::latexPreviewAvailable
virtual void latexPreviewAvailable(const QImage &preview, const QImage &largePreview, const QImage &fullPreview)
Definition: klflatexpreviewthread.cpp:52
KLFBackend::klfOutput::errorstr
QString errorstr
An explicit error string.
Definition: klfbackend.h:390
KLFContLatexPreview
Definition: klflatexpreviewthread.h:216
QThread::priority
Priority priority() const
KLF_DEBUG_TIME_BLOCK
#define KLF_DEBUG_TIME_BLOCK(msg)
QImage
KLFLatexPreviewHandler::latexPreviewFullImageAvailable
virtual void latexPreviewFullImageAvailable(const QImage &fullPreview)
Definition: klflatexpreviewthread.cpp:65
KLFContLatexPreview::setSettings
bool setSettings(const KLFBackend::klfSettings &settings, bool disableExtraFormats=true)
Definition: klflatexpreviewthread.cpp:457
QImage::width
int width() const

Generated by doxygen 1.8.17