[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 {
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 {
155 
156  // fire up the thread
157  QThread::start(priority);
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 {
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 {
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 {
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 {
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 
Specific input to KLFBackend::getLatexFormula()
Definition: klfbackend.h:306
bool setSettings(const KLFBackend::klfSettings &settings, bool disableExtraFormats=true)
#define klfDbg(streamableItems)
#define KLF_DEBUG_BLOCK(msg)
static klfOutput getLatexFormula(const klfInput &in, const klfSettings &settings, bool isMainThread=true)
The function that processes everything.
Definition: klfbackend.cpp:487
int status
A code describing the status of the request.
Definition: klfbackend.h:381
int type(const char *typeName)
void start(Priority priority)
#define KLF_DELETE_PRIVATE
virtual void latexPreviewError(const QString &errorString, int errorCode)
int width() const
virtual void latexPreviewImageAvailable(const QImage &preview)
#define KLF_DEBUG_TIME_BLOCK(msg)
virtual void run()
#define KLF_FUNC_NAME
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)
Definition of class KLFBackend.
QImage result
The actual resulting image.
Definition: klfbackend.h:393
virtual void latexPreviewFullImageAvailable(const QImage &fullPreview)
QString errorstr
An explicit error string.
Definition: klfbackend.h:390
KLFBackend::getLatexFormula() result.
Definition: klfbackend.h:370
virtual void latexPreviewLargeImageAvailable(const QImage &largePreview)
int height() const
bool setPreviewSize(const QSize &previewSize)
virtual void latexOutputAvailable(const KLFBackend::klfOutput &output)
bool setLargePreviewSize(const QSize &largePreviewSize)
#define KLF_INIT_PRIVATE(ClassName)
bool setInput(const KLFBackend::klfInput &input)
virtual void latexPreviewAvailable(const QImage &preview, const QImage &largePreview, const QImage &fullPreview)
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:218

Generated by doxygen 1.8.13