GNU Octave  6.2.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-2021 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 
76 namespace QtHandles
77 {
78 
79  static std::string
81  {
82  if (go.isa ("figure"))
83  return "__plot_stream__";
84  else if (go.isa ("uicontrol")
85  || go.isa ("uipanel")
86  || go.isa ("uibuttongroup")
87  || go.isa ("uimenu")
88  || go.isa ("uicontextmenu")
89  || go.isa ("uitable")
90  || go.isa ("uitoolbar")
91  || go.isa ("uipushtool")
92  || go.isa ("uitoggletool"))
93  return "__object__";
94  else
95  qCritical ("QtHandles::qt_graphics_toolkit: no __object__ property known for object "
96  "of type %s", go.type ().c_str ());
97 
98  return "";
99  }
100 
102  octave::base_qobject& oct_qobj)
103  : QObject (), base_graphics_toolkit ("qt"), m_interpreter (interp),
104  m_octave_qobj (oct_qobj)
105  {
106  // Implemented with a signal/slot connection in order to properly
107  // cross from the interpreter thread (where requests to create
108  // graphics object are initiated) to the GUI application thread
109  // (where they are actually created and displayed).
110  // We need to make sure the GUI Object and its proxy are properly
111  // created before the initialize method returns, so we use a
112  // BlockingQueuedConnection. After the signal is emitted, the interpreter
113  // thread is locked until the slot has returned.
114 
115  connect (this, SIGNAL (create_object_signal (double)),
116  this, SLOT (create_object (double)),
117  Qt::BlockingQueuedConnection);
118  }
119 
120  bool
122  {
123  if (go.isa ("figure")
124  || (go.isa ("uicontrol") && go.get ("style").string_value () != "frame")
125  || go.isa ("uipanel")
126  || go.isa ("uibuttongroup")
127  || go.isa ("uimenu")
128  || go.isa ("uicontextmenu")
129  || go.isa ("uitable")
130  || go.isa ("uitoolbar")
131  || go.isa ("uipushtool")
132  || go.isa ("uitoggletool"))
133  {
134  // FIXME: We need to unlock the mutex here but we have no way to know
135  // if it was previously locked by this thread, and thus if we should
136  // re-lock it.
137 
139 
140  gh_mgr.unlock ();
141 
142  Logger::debug ("qt_graphics_toolkit::initialize %s from thread %08x",
143  go.type ().c_str (), QThread::currentThreadId ());
144 
145  ObjectProxy *proxy = new ObjectProxy ();
146  graphics_object gObj (go);
147 
148  OCTAVE_PTR_TYPE tmp (reinterpret_cast<OCTAVE_INTPTR_TYPE> (proxy));
149  gObj.get_properties ().set (toolkitObjectProperty (go), tmp);
150 
151  emit create_object_signal (go.get_handle ().value ());
152 
153  return true;
154  }
155 
156  return false;
157  }
158 
159  void
161  {
162  // Rule out obvious properties we want to ignore.
163  if (pId == figure::properties::ID___PLOT_STREAM__
164  || pId == uicontrol::properties::ID___OBJECT__
165  || pId == uipanel::properties::ID___OBJECT__
166  || pId == uibuttongroup::properties::ID___OBJECT__
167  || pId == uimenu::properties::ID___OBJECT__
168  || pId == uicontextmenu::properties::ID___OBJECT__
169  || pId == uitable::properties::ID___OBJECT__
170  || pId == uitoolbar::properties::ID___OBJECT__
171  || pId == uipushtool::properties::ID___OBJECT__
172  || pId == uitoggletool::properties::ID___OBJECT__
173  || pId == base_properties::ID___MODIFIED__)
174  return;
175 
176  Logger::debug ("qt_graphics_toolkit::update %s(%d) from thread %08x",
177  go.type ().c_str (), pId, QThread::currentThreadId ());
178 
179  ObjectProxy *proxy = toolkitObjectProxy (go);
180 
181  if (proxy)
182  {
183  if (go.isa ("uicontrol")
184  && pId == uicontrol::properties::ID_STYLE)
185  {
186  // Special case: we need to recreate the control widget
187  // associated with the octave graphics_object
188 
189  finalize (go);
190  initialize (go);
191  }
192  else
193  proxy->update (pId);
194  }
195  }
196 
197  void
199  {
200  // FIXME: We need to unlock the mutex here but we have no way to know if
201  // if it was previously locked by this thread, and thus if we should
202  // re-lock it.
203 
205 
206  gh_mgr.unlock ();
207 
208  Logger::debug ("qt_graphics_toolkit::finalize %s from thread %08x",
209  go.type ().c_str (), QThread::currentThreadId ());
210 
211  ObjectProxy *proxy = toolkitObjectProxy (go);
212 
213  if (proxy)
214  {
215  proxy->finalize ();
216  delete proxy;
217 
218  graphics_object gObj (go);
219 
220  gObj.get_properties ().set (toolkitObjectProperty (go), Matrix ());
221  }
222  }
223 
224  void
226  {
227  if (go.get_properties ().is_visible ())
228  {
229  ObjectProxy *proxy = toolkitObjectProxy (go);
230 
231  if (proxy)
232  proxy->redraw ();
233  }
234  }
235 
236  void
238  {
239  if (go.get_properties ().is_visible ())
240  {
241  ObjectProxy *proxy = toolkitObjectProxy (go);
242 
243  if (proxy)
244  proxy->show ();
245  }
246  }
247 
248  void
250  const std::string& term,
251  const std::string& file_cmd,
252  const std::string& /*debug_file*/) const
253  {
254  ObjectProxy *proxy = toolkitObjectProxy (go);
255 
256  if (proxy)
257  proxy->print (QString::fromStdString (file_cmd),
258  QString::fromStdString (term));
259  }
260 
263  {
265 
266  if (go.isa ("figure"))
267  {
268  ObjectProxy *proxy = toolkitObjectProxy (go);
269 
270  if (proxy)
271  retval = proxy->get_pixels ();
272  }
273 
274  return retval;
275  }
276 
277  Matrix
279  {
280  Matrix ext (1, 4, 0.0);
281 
282  if (go.isa ("uicontrol"))
283  {
284  octave_value str = go.get ("string");
285  if (! str.isempty ())
286  {
287  const uicontrol::properties& up =
288  dynamic_cast<const uicontrol::properties&> (go.get_properties ());
289  Matrix bb = up.get_boundingbox (false);
290  QFont font = Utils::computeFont<uicontrol> (up, bb(3));
291  QFontMetrics fm (font);
292 
293  QString s;
294  QSize sz;
295 
296  if (str.is_string ())
297  {
299  sz = fm.size (Qt::TextSingleLine, s);
300  ext(2) = sz.width ();
301  ext(3) = sz.height ();
302  }
303  else if (str.iscellstr ())
304  {
306  double wd = 0.0;
307  double hg = 0.0;
308  for (octave_idx_type ii = 0; ii < sv.numel (); ii++)
309  {
310  s = QString::fromStdString (sv(ii));
311  sz = fm.size (Qt::TextSingleLine, s);
312  wd = std::max (wd, static_cast<double> (sz.width ()));
313  hg = std::max (hg, static_cast<double> (sz.height ()));
314  }
315 
316  ext(2) = wd;
317  // FIXME: Find a better way to determine the height of e.g.
318  // listbox uicontrol objects
319  ext(3) = hg * sv.numel ();
320  }
321  }
322  }
323 
324  return ext;
325  }
326 
327  Object*
329  {
330  ObjectProxy *proxy = toolkitObjectProxy (go);
331 
332  if (proxy)
333  return proxy->object ();
334 
335  return nullptr;
336  }
337 
338  ObjectProxy*
340  {
341  if (go)
342  {
343  octave_value ov = go.get (toolkitObjectProperty (go));
344 
345  if (ov.is_defined () && ! ov.isempty ())
346  {
347  OCTAVE_INTPTR_TYPE ptr = ov.OCTAVE_PTR_SCALAR ().value ();
348 
349  return reinterpret_cast<ObjectProxy *> (ptr);
350  }
351  }
352 
353  return nullptr;
354  }
355 
356  void
358  {
360 
361  evmgr.post_event (fcn);
362  }
363 
364  void
366  {
368 
369  evmgr.post_event (meth);
370  }
371 
372  void
374  {
376 
377  octave::autolock guard (gh_mgr.graphics_lock ());
378 
379  graphics_object go (gh_mgr.get_object (graphics_handle (handle)));
380 
381  if (! go.valid_object ())
382  {
383  qWarning ("qt_graphics_toolkit::create_object: invalid object for handle %g",
384  handle);
385  return;
386  }
387 
388  if (go.get_properties ().is_beingdeleted ())
389  {
390  qWarning ("qt_graphics_toolkit::create_object: object is being deleted");
391  return;
392  }
393 
395 
396  if (! proxy)
397  {
398  qWarning ("qt_graphics_toolkit::create_object: no proxy for handle %g",
399  handle);
400  return;
401  }
402 
403  Logger::debug ("qt_graphics_toolkit::create_object: "
404  "create %s from thread %08x",
405  go.type ().c_str (), QThread::currentThreadId ());
406 
407  Object *obj = nullptr;
408 
409  if (go.isa ("figure"))
411  else if (go.isa ("uicontrol"))
412  {
414  Utils::properties<uicontrol> (go);
415 
416  if (up.style_is ("pushbutton"))
418  else if (up.style_is ("edit"))
420  else if (up.style_is ("checkbox"))
422  else if (up.style_is ("radiobutton"))
424  else if (up.style_is ("togglebutton"))
426  else if (up.style_is ("text"))
428  else if (up.style_is ("popupmenu"))
430  else if (up.style_is ("slider"))
432  else if (up.style_is ("listbox"))
434  }
435  else if (go.isa ("uibuttongroup"))
437  else if (go.isa ("uipanel"))
439  else if (go.isa ("uimenu"))
441  else if (go.isa ("uicontextmenu"))
443  else if (go.isa ("uitable"))
445  else if (go.isa ("uitoolbar"))
447  else if (go.isa ("uipushtool"))
449  else if (go.isa ("uitoggletool"))
451  else
452  qWarning ("qt_graphics_toolkit::create_object: unsupported type '%s'",
453  go.type ().c_str ());
454 
455  if (obj)
456  {
457  proxy->setObject (obj);
458  obj->do_connections (this);
459  }
460  }
461 
463  const std::string& nm)
464  {
466 
467  gh_mgr.post_callback (h, nm);
468  }
469 
471  const std::string& nm,
472  const octave_value& data)
473  {
475 
476  gh_mgr.post_callback (h, nm, data);
477  }
478 
480  const std::string& nm,
481  const octave_value& value)
482  {
484 
485  gh_mgr.post_set (h, nm, value);
486  }
487 
489  const std::string& nm,
490  const octave_value& value,
491  bool notify_toolkit)
492  {
494 
495  gh_mgr.post_set (h, nm, value, notify_toolkit);
496  }
497 
499  const std::string& nm,
500  const octave_value& value,
501  bool notify_toolkit,
502  bool redraw_figure)
503  {
505 
506  gh_mgr.post_set (h, nm, value, notify_toolkit, redraw_figure);
507  }
508 };
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
Definition: dMatrix.h:42
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:44
static EditControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: EditControl.cc:43
static Figure * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: Figure.cc:113
static ListBoxControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
static void debug(const char *fmt,...)
Definition: Logger.cc:78
static Menu * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: Menu.cc:66
Object * object(void)
Definition: ObjectProxy.h:54
uint8NDArray get_pixels(void)
Definition: ObjectProxy.cc:139
void setObject(Object *obj)
Definition: ObjectProxy.cc:79
void print(const QString &file_cmd, const QString &term)
Definition: ObjectProxy.cc:122
void update(int pId)
Definition: ObjectProxy.cc:89
virtual void do_connections(const QObject *receiver, const QObject *emitter=nullptr)
Definition: Object.cc:225
static Panel * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: Panel.cc:96
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:40
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:432
static TextControl * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: TextControl.cc:42
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:40
static ToolBar * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: ToolBar.cc:72
void print_figure(const graphics_object &go, const std::string &term, const std::string &file_cmd, const std::string &) const
void create_object_signal(double handle)
void gh_set_event(const graphics_handle &h, const std::string &name, const octave_value &value)
void show_figure(const graphics_object &h) const
static ObjectProxy * toolkitObjectProxy(const graphics_object &go)
octave::interpreter & m_interpreter
octave::base_qobject & m_octave_qobj
void gh_callback_event(const graphics_handle &h, const std::string &name)
static Object * toolkitObject(const graphics_object &go)
void finalize(const graphics_object &obj)
void update(const graphics_object &obj, int pId)
void redraw_figure(const graphics_object &h) const
void interpreter_event(const octave::fcn_callback &fcn)
Matrix get_text_extent(const graphics_object &go) const
uint8NDArray get_pixels(const graphics_object &go) const
bool initialize(const graphics_object &obj)
qt_graphics_toolkit(octave::interpreter &interp, octave::base_qobject &oct_qobj)
virtual void set(const caseless_str &, const octave_value &)
void unlock(void)
Definition: graphics.in.h:6320
void post_set(const graphics_handle &h, const std::string &name, const octave_value &value, bool notify_toolkit=true, bool redraw_figure=false)
Definition: graphics.cc:12257
graphics_object get_object(double val) const
Definition: graphics.in.h:6260
void post_callback(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix())
Definition: graphics.cc:12214
octave::mutex graphics_lock(void)
Definition: graphics.in.h:6393
octave_value get(bool all=false) const
Definition: graphics.in.h:2746
bool isa(const std::string &go_name) const
Definition: graphics.in.h:2827
base_properties & get_properties(void)
Definition: graphics.in.h:2829
std::string type(void) const
Definition: graphics.in.h:2849
graphics_handle get_handle(void) const
Definition: graphics.in.h:2815
bool valid_object(void) const
Definition: graphics.in.h:2847
Base class for Octave interfaces that use Qt.
Provides threadsafe access to octave.
void post_event(const fcn_callback &fcn)
gh_manager & get_gh_manager(void)
Definition: interpreter.h:295
event_manager & get_event_manager(void)
Definition: interpreter.h:290
double value(void) const
Definition: oct-handle.h:78
bool iscellstr(void) const
Definition: ov.h:563
bool is_string(void) const
Definition: ov.h:593
bool is_defined(void) const
Definition: ov.h:551
std::string string_value(bool force=false) const
Definition: ov.h:927
string_vector string_vector_value(bool pad=false) const
Definition: ov.h:930
bool isempty(void) const
Definition: ov.h:557
octave_idx_type numel(void) const
Definition: str-vec.h:100
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:10933
template QFont computeFont< uicontrol >(const uicontrol::properties &props, int height)
QString fromStdString(const std::string &s)
static std::string toolkitObjectProperty(const graphics_object &go)
std::function< void(octave::interpreter &)> meth_callback
Definition: event-manager.h:47
std::function< void(void)> fcn_callback
Definition: event-manager.h:46
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
#define OCTAVE_INTPTR_TYPE
#define OCTAVE_PTR_TYPE