GNU Octave 7.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-2022 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
76namespace octave
77{
78
79 static std::string
80 toolkitObjectProperty (const graphics_object& go)
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 ("octave::qt_graphics_toolkit: no __object__ property known for object "
96 "of type %s", go.type ().c_str ());
97
98 return "";
99 }
100
101 qt_graphics_toolkit::qt_graphics_toolkit (octave::interpreter& interp,
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
117 Qt::BlockingQueuedConnection);
118 }
119
120 bool
121 qt_graphics_toolkit::initialize (const graphics_object& go)
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
138 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
139
140 gh_mgr.unlock ();
141
142 Logger::debug ("qt_graphics_toolkit::initialize %s from thread %p",
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
160 qt_graphics_toolkit::update (const graphics_object& go, int pId)
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 %p",
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
198 qt_graphics_toolkit::finalize (const graphics_object& go)
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
204 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
205
206 gh_mgr.unlock ();
207
208 Logger::debug ("qt_graphics_toolkit::finalize %s from thread %p",
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
225 qt_graphics_toolkit::redraw_figure (const graphics_object& go) const
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
237 qt_graphics_toolkit::show_figure (const graphics_object& go) const
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
249 qt_graphics_toolkit::print_figure (const graphics_object& go,
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),
259 }
260
262 qt_graphics_toolkit::get_pixels (const graphics_object& go) const
263 {
264 uint8NDArray retval;
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
278 qt_graphics_toolkit::get_text_extent (const graphics_object& go) const
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 *
328 qt_graphics_toolkit::toolkitObject (const graphics_object& go)
329 {
330 ObjectProxy *proxy = toolkitObjectProxy (go);
331
332 if (proxy)
333 return proxy->object ();
334
335 return nullptr;
336 }
337
339 qt_graphics_toolkit::toolkitObjectProxy (const graphics_object& go)
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 {
359 octave::event_manager& evmgr = m_interpreter.get_event_manager ();
360
361 evmgr.post_event (fcn);
362 }
363
364 void
366 {
367 octave::event_manager& evmgr = m_interpreter.get_event_manager ();
368
369 evmgr.post_event (meth);
370 }
371
372 void
374 {
375 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
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 %p",
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 {
465 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
466
467 gh_mgr.post_callback (h, nm);
468 }
469
471 const std::string& nm,
472 const octave_value& data)
473 {
474 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
475
476 gh_mgr.post_callback (h, nm, data);
477 }
478
480 const std::string& nm,
481 const octave_value& value)
482 {
483 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
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 {
493 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
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 {
504 gh_manager& gh_mgr = m_interpreter.get_gh_manager ();
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:103
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
void print(const QString &file_cmd, const QString &term)
Definition: ObjectProxy.cc:122
Object * object(void)
Definition: ObjectProxy.h:54
void setObject(Object *obj)
Definition: ObjectProxy.cc:79
uint8NDArray get_pixels(void)
Definition: ObjectProxy.cc:139
void finalize(void)
Definition: ObjectProxy.cc:95
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:439
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
Base class for Octave interfaces that use Qt.
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 create_object_signal(double handle)
static ObjectProxy * toolkitObjectProxy(const graphics_object &go)
bool initialize(const graphics_object &obj)
void interpreter_event(const octave::fcn_callback &fcn)
void finalize(const graphics_object &obj)
void update(const graphics_object &obj, int pId)
void redraw_figure(const graphics_object &h) const
void show_figure(const graphics_object &h) const
octave::interpreter & m_interpreter
static Object * toolkitObject(const graphics_object &go)
qt_graphics_toolkit(octave::interpreter &interp, octave::base_qobject &oct_qobj)
octave::base_qobject & m_octave_qobj
uint8NDArray get_pixels(const graphics_object &go) const
void gh_callback_event(const graphics_handle &h, const std::string &name)
Matrix get_text_extent(const graphics_object &go) const
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
octave_idx_type numel(void) const
Definition: str-vec.h:100
OCTAVE_NAMESPACE_BEGIN typedef std::function< void(void)> fcn_callback
Definition: event-manager.h:47
std::function< void(interpreter &)> meth_callback
Definition: event-manager.h:48
QString fromStdString(const std::string &s)
T::properties & properties(graphics_object obj)
template QFont computeFont< uicontrol >(const uicontrol::properties &props, int height)
static std::string toolkitObjectProperty(const graphics_object &go)
#define OCTAVE_INTPTR_TYPE
#define OCTAVE_PTR_TYPE