GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
qt-graphics-toolkit.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2011-2023 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cstdint>
31 
32 #include <QApplication>
33 #include <QFontMetrics>
34 #include <QThread>
35 
36 #include "ButtonGroup.h"
37 #include "CheckBoxControl.h"
38 #include "ContextMenu.h"
39 #include "EditControl.h"
40 #include "Figure.h"
41 #include "ListBoxControl.h"
42 #include "Logger.h"
43 #include "Menu.h"
44 #include "Object.h"
45 #include "ObjectProxy.h"
46 #include "Panel.h"
47 #include "PopupMenuControl.h"
48 #include "PushButtonControl.h"
49 #include "PushTool.h"
50 #include "QtHandlesUtils.h"
51 #include "RadioButtonControl.h"
52 #include "SliderControl.h"
53 #include "Table.h"
54 #include "TextControl.h"
55 #include "ToggleButtonControl.h"
56 #include "ToggleTool.h"
57 #include "ToolBar.h"
58 #include "qt-graphics-toolkit.h"
59 
60 #include "octave-qobject.h"
61 
62 #include "event-manager.h"
63 #include "graphics.h"
64 #include "interpreter.h"
65 
66 //#if INTPTR_MAX == INT32_MAX
67 //# define OCTAVE_PTR_TYPE octave_uint32
68 //# define OCTAVE_INTPTR_TYPE uint32_t
69 //# define OCTAVE_PTR_SCALAR uint32_scalar_value
70 //#else
71 # define OCTAVE_PTR_TYPE octave_uint64
72 # define OCTAVE_INTPTR_TYPE uint64_t
73 # define OCTAVE_PTR_SCALAR uint64_scalar_value
74 //#endif
75 
77 
78 static std::string
79 toolkitObjectProperty (const graphics_object& go)
80 {
81  if (go.isa ("figure"))
82  return "__plot_stream__";
83  else if (go.isa ("uicontrol")
84  || go.isa ("uipanel")
85  || go.isa ("uibuttongroup")
86  || go.isa ("uimenu")
87  || go.isa ("uicontextmenu")
88  || go.isa ("uitable")
89  || go.isa ("uitoolbar")
90  || go.isa ("uipushtool")
91  || go.isa ("uitoggletool"))
92  return "__object__";
93  else
94  qCritical ("octave::qt_graphics_toolkit: no __object__ property known for object "
95  "of type %s", go.type ().c_str ());
96 
97  return "";
98 }
99 
100 qt_graphics_toolkit::qt_graphics_toolkit (octave::interpreter& interp,
101  octave::base_qobject& oct_qobj)
102  : QObject (), base_graphics_toolkit ("qt"), m_interpreter (interp),
103  m_octave_qobj (oct_qobj)
104 {
105  // Implemented with a signal/slot connection in order to properly
106  // cross from the interpreter thread (where requests to create
107  // graphics object are initiated) to the GUI application thread
108  // (where they are actually created and displayed).
109  // We need to make sure the GUI Object and its proxy are properly
110  // created before the initialize method returns, so we use a
111  // BlockingQueuedConnection. After the signal is emitted, the interpreter
112  // thread is locked until the slot has returned.
113 
116  Qt::BlockingQueuedConnection);
117 }
118 
119 bool
120 qt_graphics_toolkit::initialize (const graphics_object& go)
121 {
122  if (go.isa ("figure")
123  || (go.isa ("uicontrol") && go.get ("style").string_value () != "frame")
124  || go.isa ("uipanel")
125  || go.isa ("uibuttongroup")
126  || go.isa ("uimenu")
127  || go.isa ("uicontextmenu")
128  || go.isa ("uitable")
129  || go.isa ("uitoolbar")
130  || go.isa ("uipushtool")
131  || go.isa ("uitoggletool"))
132  {
133  // FIXME: We need to unlock the mutex here but we have no way to know
134  // if it was previously locked by this thread, and thus if we should
135  // re-lock it.
136 
137  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
138 
139  gh_mgr.unlock ();
140 
141  Logger::debug ("qt_graphics_toolkit::initialize %s from thread %p",
142  go.type ().c_str (), QThread::currentThreadId ());
143 
144  ObjectProxy *proxy = new ObjectProxy ();
145  graphics_object gObj (go);
146 
147  OCTAVE_PTR_TYPE tmp (reinterpret_cast<OCTAVE_INTPTR_TYPE> (proxy));
148  gObj.get_properties ().set (toolkitObjectProperty (go), tmp);
149 
150  emit create_object_signal (go.get_handle ().value ());
151 
152  return true;
153  }
154 
155  return false;
156 }
157 
158 void
159 qt_graphics_toolkit::update (const graphics_object& go, int pId)
160 {
161  // Rule out obvious properties we want to ignore.
162  if (pId == figure::properties::ID___PLOT_STREAM__
163  || pId == uicontrol::properties::ID___OBJECT__
164  || pId == uipanel::properties::ID___OBJECT__
165  || pId == uibuttongroup::properties::ID___OBJECT__
166  || pId == uimenu::properties::ID___OBJECT__
167  || pId == uicontextmenu::properties::ID___OBJECT__
168  || pId == uitable::properties::ID___OBJECT__
169  || pId == uitoolbar::properties::ID___OBJECT__
170  || pId == uipushtool::properties::ID___OBJECT__
171  || pId == uitoggletool::properties::ID___OBJECT__
172  || pId == base_properties::ID___MODIFIED__)
173  return;
174 
175  Logger::debug ("qt_graphics_toolkit::update %s(%d) from thread %p",
176  go.type ().c_str (), pId, QThread::currentThreadId ());
177 
178  ObjectProxy *proxy = toolkitObjectProxy (go);
179 
180  if (proxy)
181  {
182  if ((go.isa ("uicontrol")
183  && pId == uicontrol::properties::ID_STYLE)
184  || (go.isa ("uitable")
185  && pId == uitable::properties::ID_DATA))
186  {
187  // Special case: we need to recreate the control widget
188  // associated with the octave graphics_object
189  // FIXME: For uitable, it would only be necessary to recreate
190  // the table widget if the type of the displayed values changes
191  // between Boolean and non-Boolean (bug #63388).
192 
193  finalize (go);
194  initialize (go);
195  }
196  else
197  proxy->update (pId);
198  }
199 }
200 
201 void
202 qt_graphics_toolkit::finalize (const graphics_object& go)
203 {
204  // FIXME: We need to unlock the mutex here but we have no way to know if
205  // if it was previously locked by this thread, and thus if we should
206  // re-lock it.
207 
208  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
209 
210  gh_mgr.unlock ();
211 
212  Logger::debug ("qt_graphics_toolkit::finalize %s from thread %p",
213  go.type ().c_str (), QThread::currentThreadId ());
214 
215  ObjectProxy *proxy = toolkitObjectProxy (go);
216 
217  if (proxy)
218  {
219  proxy->finalize ();
220  delete proxy;
221 
222  graphics_object gObj (go);
223 
224  gObj.get_properties ().set (toolkitObjectProperty (go), Matrix ());
225  }
226 }
227 
228 void
229 qt_graphics_toolkit::redraw_figure (const graphics_object& go) const
230 {
231  if (go.get_properties ().is_visible ())
232  {
233  ObjectProxy *proxy = toolkitObjectProxy (go);
234 
235  if (proxy)
236  proxy->redraw ();
237  }
238 }
239 
240 void
241 qt_graphics_toolkit::show_figure (const graphics_object& go) const
242 {
243  if (go.get_properties ().is_visible ())
244  {
245  ObjectProxy *proxy = toolkitObjectProxy (go);
246 
247  if (proxy)
248  proxy->show ();
249  }
250 }
251 
252 void
253 qt_graphics_toolkit::print_figure (const graphics_object& go,
254  const std::string& term,
255  const std::string& file_cmd,
256  const std::string& /*debug_file*/) const
257 {
258  ObjectProxy *proxy = toolkitObjectProxy (go);
259 
260  if (proxy)
261  proxy->print (QString::fromStdString (file_cmd),
262  QString::fromStdString (term));
263 }
264 
266 qt_graphics_toolkit::get_pixels (const graphics_object& go) const
267 {
268  uint8NDArray retval;
269 
270  if (go.isa ("figure"))
271  {
272  ObjectProxy *proxy = toolkitObjectProxy (go);
273 
274  if (proxy)
275  retval = proxy->get_pixels ();
276  }
277 
278  return retval;
279 }
280 
281 Matrix
282 qt_graphics_toolkit::get_text_extent (const graphics_object& go) const
283 {
284  Matrix ext (1, 4, 0.0);
285 
286  if (go.isa ("uicontrol"))
287  {
288  octave_value str = go.get ("string");
289  if (! str.isempty ())
290  {
291  const uicontrol::properties& up =
292  dynamic_cast<const uicontrol::properties&> (go.get_properties ());
293  Matrix bb = up.get_boundingbox (false);
294  QFont font = Utils::computeFont<uicontrol> (up, bb(3));
295  QFontMetrics fm (font);
296 
297  QString s;
298  QSize sz;
299 
300  if (str.is_string ())
301  {
303  sz = fm.size (Qt::TextSingleLine, s);
304  ext(2) = sz.width ();
305  ext(3) = sz.height ();
306  }
307  else if (str.iscellstr ())
308  {
310  double wd = 0.0;
311  double hg = 0.0;
312  for (octave_idx_type ii = 0; ii < sv.numel (); ii++)
313  {
314  s = QString::fromStdString (sv(ii));
315  sz = fm.size (Qt::TextSingleLine, s);
316  wd = std::max (wd, static_cast<double> (sz.width ()));
317  hg = std::max (hg, static_cast<double> (sz.height ()));
318  }
319 
320  ext(2) = wd;
321  // FIXME: Find a better way to determine the height of e.g.
322  // listbox uicontrol objects
323  ext(3) = hg * sv.numel ();
324  }
325  }
326  }
327 
328  return ext;
329 }
330 
331 Object *
332 qt_graphics_toolkit::toolkitObject (const graphics_object& go)
333 {
334  ObjectProxy *proxy = toolkitObjectProxy (go);
335 
336  if (proxy)
337  return proxy->object ();
338 
339  return nullptr;
340 }
341 
342 ObjectProxy *
343 qt_graphics_toolkit::toolkitObjectProxy (const graphics_object& go)
344 {
345  if (go)
346  {
347  octave_value ov = go.get (toolkitObjectProperty (go));
348 
349  if (ov.is_defined () && ! ov.isempty ())
350  {
351  OCTAVE_INTPTR_TYPE ptr = ov.OCTAVE_PTR_SCALAR ().value ();
352 
353  return reinterpret_cast<ObjectProxy *> (ptr);
354  }
355  }
356 
357  return nullptr;
358 }
359 
360 void
362 {
363  octave::event_manager& evmgr = m_interpreter.get_event_manager ();
364 
365  evmgr.post_event (fcn);
366 }
367 
368 void
370 {
371  octave::event_manager& evmgr = m_interpreter.get_event_manager ();
372 
373  evmgr.post_event (meth);
374 }
375 
376 void
378 {
379  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
380 
381  octave::autolock guard (gh_mgr.graphics_lock ());
382 
383  graphics_object go (gh_mgr.get_object (graphics_handle (handle)));
384 
385  if (! go.valid_object ())
386  {
387  qWarning ("qt_graphics_toolkit::create_object: invalid object for handle %g",
388  handle);
389  return;
390  }
391 
392  if (go.get_properties ().is_beingdeleted ())
393  {
394  qWarning ("qt_graphics_toolkit::create_object: object is being deleted");
395  return;
396  }
397 
399 
400  if (! proxy)
401  {
402  qWarning ("qt_graphics_toolkit::create_object: no proxy for handle %g",
403  handle);
404  return;
405  }
406 
407  Logger::debug ("qt_graphics_toolkit::create_object: "
408  "create %s from thread %p",
409  go.type ().c_str (), QThread::currentThreadId ());
410 
411  Object *obj = nullptr;
412 
413  if (go.isa ("figure"))
415  else if (go.isa ("uicontrol"))
416  {
418  Utils::properties<uicontrol> (go);
419 
420  if (up.style_is ("pushbutton"))
422  else if (up.style_is ("edit"))
424  else if (up.style_is ("checkbox"))
426  else if (up.style_is ("radiobutton"))
428  else if (up.style_is ("togglebutton"))
430  else if (up.style_is ("text"))
432  else if (up.style_is ("popupmenu"))
434  else if (up.style_is ("slider"))
436  else if (up.style_is ("listbox"))
438  }
439  else if (go.isa ("uibuttongroup"))
441  else if (go.isa ("uipanel"))
443  else if (go.isa ("uimenu"))
445  else if (go.isa ("uicontextmenu"))
447  else if (go.isa ("uitable"))
449  else if (go.isa ("uitoolbar"))
451  else if (go.isa ("uipushtool"))
453  else if (go.isa ("uitoggletool"))
455  else
456  qWarning ("qt_graphics_toolkit::create_object: unsupported type '%s'",
457  go.type ().c_str ());
458 
459  if (obj)
460  {
461  proxy->setObject (obj);
462  obj->do_connections (this);
463  }
464 }
465 
467  const std::string& nm)
468 {
469  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
470 
471  gh_mgr.post_callback (h, nm);
472 }
473 
475  const std::string& nm,
476  const octave_value& data)
477 {
478  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
479 
480  gh_mgr.post_callback (h, nm, data);
481 }
482 
484  const std::string& nm,
485  const octave_value& value)
486 {
487  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
488 
489  gh_mgr.post_set (h, nm, value);
490 }
491 
493  const std::string& nm,
494  const octave_value& value,
495  bool notify_toolkit)
496 {
497  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
498 
499  gh_mgr.post_set (h, nm, value, notify_toolkit);
500 }
501 
503  const std::string& nm,
504  const octave_value& value,
505  bool notify_toolkit,
506  bool redraw_figure)
507 {
508  gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
509 
510  gh_mgr.post_set (h, nm, value, notify_toolkit, redraw_figure);
511 }
512 
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
static ButtonGroup * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: ButtonGroup.cc:102
static CheckBoxControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
static ContextMenu * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: ContextMenu.cc:43
static EditControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: EditControl.cc:42
static Figure * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: Figure.cc:112
static ListBoxControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
static void debug(const char *fmt,...)
Definition: Logger.cc:77
Definition: dMatrix.h:42
static Menu * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: Menu.cc:65
Object * object(void)
Definition: ObjectProxy.h:53
void print(const QString &file_cmd, const QString &term)
Definition: ObjectProxy.cc:121
void show(void)
Definition: ObjectProxy.cc:115
void redraw(void)
Definition: ObjectProxy.cc:109
void update(int pId)
Definition: ObjectProxy.cc:88
void finalize(void)
Definition: ObjectProxy.cc:94
void setObject(Object *obj)
Definition: ObjectProxy.cc:78
uint8NDArray get_pixels(void)
Definition: ObjectProxy.cc:138
Definition: Object.h:47
virtual void do_connections(const QObject *receiver, const QObject *emitter=nullptr)
Definition: Object.cc:224
static Panel * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: Panel.cc:95
static PopupMenuControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
static PushButtonControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
static PushTool * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: PushTool.cc:39
static RadioButtonControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
static SliderControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
static Table * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: Table.cc:435
static TextControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: TextControl.cc:41
static ToggleButtonControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
static ToggleTool * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: ToggleTool.cc:39
static ToolBar * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: ToolBar.cc:72
bool iscellstr(void) const
Definition: ov.h:652
bool is_string(void) const
Definition: ov.h:682
bool is_defined(void) const
Definition: ov.h:637
std::string string_value(bool force=false) const
Definition: ov.h:1019
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:1022
bool isempty(void) const
Definition: ov.h:646
qt_graphics_toolkit(octave::interpreter &interp, octave::base_qobject &oct_qobj)
void gh_set_event(const graphics_handle &h, const std::string &name, const octave_value &value)
void print_figure(const graphics_object &go, const std::string &term, const std::string &file_cmd, const std::string &) const
void show_figure(const graphics_object &h) const
void interpreter_event(const octave::fcn_callback &fcn)
void gh_callback_event(const graphics_handle &h, const std::string &name)
void create_object_signal(double handle)
void redraw_figure(const graphics_object &h) const
uint8NDArray get_pixels(const graphics_object &go) const
void finalize(const graphics_object &obj)
octave::interpreter & m_interpreter
octave::base_qobject & m_octave_qobj
void create_object(double handle)
static Object * toolkitObject(const graphics_object &go)
void update(const graphics_object &obj, int pId)
bool initialize(const graphics_object &obj)
static ObjectProxy * toolkitObjectProxy(const graphics_object &go)
Matrix get_text_extent(const graphics_object &go) const
octave_idx_type numel(void) const
Definition: str-vec.h:100
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::function< void(void)> fcn_callback
Definition: event-manager.h:43
std::function< void(interpreter &)> meth_callback
Definition: event-manager.h:48
QString fromStdString(const std::string &s)
template QFont computeFont< uicontrol >(const uicontrol::properties &props, int height)
T::properties & properties(graphics_object obj)
OCTAVE_END_NAMESPACE(octave)
static std::string toolkitObjectProperty(const graphics_object &go)
#define OCTAVE_INTPTR_TYPE
#define OCTAVE_PTR_TYPE