GUI program accepts various kinds of GUI plugin
-
@Frederick-Virchanza-Gotham said in GUI program accepts various kinds of GUI plugin:
I understand that wxWidgets is pretty much a rival to Qt, and vice versa
In most cases wxwidgets can't compete with the whole Qt Framework. And I don't say that because I'm a Qt user, but because it is like that ;-)
if a Qt program could accept GUI plugins written in wxWidgets
You can embed native Windows/content via
and
I think it would be even cooler to be able to load a Qt plugin that draws widgets into a wxWidgets program
I don't think this will work. At least not directly.
I would appreciate some help with this, and if someone wants to write code to load wxWidgets plugins into a Qt program, then I'll assist as best I can (I've been making wxWidgets GUI programs for about 15 - 20 years). I think both the core code that handles window messages will need code added to forward messages between Qt and wxWidgets
So you want the Qt event loop and underlaying system send events and messages to wxWidgets handlers?!
But why?!
(⊙_⊙)? -
First thing I need to do is pick a very simple Qt plugin to try load into my wxWidgets program. What about the following?
Or is there a more suitable candidate?
-
I still don't see the reason to mix Qt (Widgets) and wxWidgets... or even this:
@Frederick-Virchanza-Gotham said in GUI program accepts various kinds of GUI plugin:
I think it would be even cooler to be able to load a Qt plugin that draws widgets into a wxWidgets program
but feel free to do it :)
-
Glad I have your support @Pl45m4 :-)
Okay so I'm looking at the plugin example here: https://doc.qt.io/qt-6.2/qtwidgets-tools-echoplugin-example.html
It shows how to load the plugins into the main app:
bool EchoWindow::loadPlugin() { QDir pluginsDir(QCoreApplication::applicationDirPath()); QStringList const entries = pluginsDir.entryList(QDir::Files); for ( QString const &fileName : entries ) { QPluginLoader pluginLoader( pluginsDir.absoluteFilePath(fileName) ); QObject *plugin = pluginLoader.instance(); if ( nullptr == plugin ) continue; echoInterface = qobject_cast<EchoInterface *>(plugin); if ( echoInterface ) return true; pluginLoader.unload(); } return false; }
The only thing missing is that this plugin doesn't draw any widgets. Does anyone have a link to a Qt plugin example that draws widgets (sort of like how you'd load an ActiveX control into a Win32 program)?
Or shall we edit this example to draw widgets? Here's how the plugin interface looks so far:
class EchoInterface { public: virtual ~EchoInterface() = default; virtual QString echo(const QString &message) = 0; }; #define EchoInterface_iid "org.qt-project.Qt.Examples.EchoInterface" Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)
So would I need to add a method that draws the widgets and connects their handlers? Something like the following:
class EchoInterface { public: virtual ~EchoInterface() = default; virtual QString echo(const QString &message) = 0; virtual bool DrawWidgets( QWidget &parent, QGridLayout &layout ) = 0; }; #define EchoInterface_iid "org.qt-project.Qt.Examples.EchoInterface" Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)
And how would the implementation of this method look? Would it be something like the following?
bool EchoInterface::DrawWidgets( QWidget &parent, QGridLayout &layout ) { auto *const button = new QPushButton( tr("Click me!") ); connect( button, &QPushButton::clicked, &parent, &EchoInterface::ButtonClickHandler ); layout.addWidget(button, 2, 1, Qt::AlignRight); layout.setSizeConstraint(QLayout::SetFixedSize); }
Am I on the right track so far? The main program has a tab control, and when you click on a different tab, it shows a different plugin. Note that the plugin draws the widgets on a pre-existing window -- i.e. on the tab control, and that it does not actually create a new dialog box on the screen.
Here's how the main program looks, it has a tab for each plugin:
-
Guys it's like pulling teeth trying to get Qt Creator to run on Ubuntu. I installed it from the Ubuntu Software app and now I'm looking through guides on the web to try figure out how to fix it. It doesn't work out of the box. I'm running Ubuntu 24.04 and Qt Creator 13.0.2. I've already fixed the 'qmake' problem , and now I've moved onto solving the 'No QML Utility installed' problem.
-
Also I'm looking here at the implementation of QCoreApplication::exec which I've simplified to:
int QCoreApplication::exec() { QEventLoop eventLoop; return eventLoop.exec(); }
Inside the implementation of QEventLoop::exec, it calls loadAcquire and also processEvents.
I'm not sure yet but I might have to edit the source code for the wxWidgets library so that when it gets a message which it doesn't recognise, it forwards it on to something along the lines of QEventLoop::processEvents. Although, I'm not entirely sure if this will be necessary (because it wasn't necessary to do so in order to accommodate DotNet plugins written in C# and Visual Basic, nor was it necessary to accommodate ActiveX controls from OCX files).
In the meantime I'm still trying to install Qt Creator but I'm busy resizing my virtual machine because the Qt installer is telling me it needs tens of gigs of freespace.
If anyone could please tell me if I'm barking up the tree with the method I've written called DrawWidgets:
bool EchoInterface::DrawWidgets( QWidget &parent, QGridLayout &layout ) { auto *const button = new QPushButton( tr("Click me!") ); connect( button, &QPushButton::clicked, &parent, &EchoInterface::ButtonClickHandler ); layout.addWidget(button, 2, 1, Qt::AlignRight); layout.setSizeConstraint(QLayout::SetFixedSize); }
Is this how a Qt plugin show draw widgets on a pre-exisiting window (e.g. in a tab control)?
-
For simplicity at the beginning, here's how I'll code the 'DrawWidgets' method:
bool EchoInterface::DrawWidgets(void *const parent_native) { auto *const parent = QWindow::fromWinId(parent_native); auto *const button = new QPushButton( tr("Click me!") ); connect( button, &QPushButton::clicked, parent, &EchoInterface::ButtonClickHandler ); auto *const layout = new QGridLayout; layout->addWidget(button, 2, 1, Qt::AlignRight); layout->setSizeConstraint(QLayout::SetFixedSize); parent->setLayout(layout); }
This might actually be a lot easier than I first thought -- I mean 2 or 3 days works instead of 2 or 3 weeks work.
For the timebeing I'm still downloading Qt Creator (I had to move my virtual machine to a terrabyte hard disk to resize it).
-
@Frederick-Virchanza-Gotham
FYI, the Qt libraries and Qt Creator compiled and supplied for Ubuntu 24.04 viaapt
(I never go to Qt site) work, are readily available, take a few minutes to download, don't occupy terrabytes. I have always installed Qt like this through every Ubuntu version. No compiling and no source code. -
@Frederick-Virchanza-Gotham said in GUI program accepts various kinds of GUI plugin:
auto *const parent = QWindow::fromWinId(parent_native);
Hi,
note that
QWindow::fromWinId
creates aQWindow
which most likely does't match your usage here:
parent, &EchoInterface::ButtonClickHandler
Also when grabbing a non-Qt window, the window should't be modified via the obtained window handle.
As:
Note: The resulting QWindow should not be used to manipulate the underlying native window (besides re-parenting), or to observe state changes of the native window. Any support for these kind of operations is incidental, highly platform dependent and untested.
( https://doc.qt.io/qt-6/qwindow.html#fromWinId ) -
I'm having great difficulty getting Qt to draw widgets on a native window (hereafter referred to as the 'top-level window').
If the top-level window is a QWidget with a QVBoxLayout, then of course I can just get the shared library to create widgets with the QWidget as the parent, and add them to the layout. No problem there.
If the shared library only has a QWindow to deal with (instead of a QWidget) then I can get the shared library to draw widgets, but only if the QWindow is actually a top-level window. If the QWindow is for some sort of 'panel' on the main window, it won't draw controls.
And as for using 'fromWndId' with 'createWindowContainer' -- well when I try to draw the widgets, it invariably creates another top-level window.It's looking like I'm gonna need to look very closely at how the QWidget is implemented, and I'm going to have to manually manipulate the Qt window/widget record system to fool it into thinking that the native window handle it has been given was actually created by Qt.
-
Yeah I'm looking at manually filling out one of these to try fool Qt into thinking a native window is a Qt window:
class QWidgetData { public: WId winid; uint widget_attributes; Qt::WindowFlags window_flags; uint window_state : 4; uint focus_policy : 4; uint sizehint_forced :1; uint is_closing :1; uint in_show : 1; uint in_set_window_state : 1; mutable uint fstrut_dirty : 1; uint context_menu_policy : 3; uint window_modality : 2; uint in_destructor : 1; uint unused : 13; QRect crect; mutable QPalette pal; QFont fnt; QRect wrect; };
-
Actually now I'm considering drawing one top-level window on top of another top-level window:
-
The tactic of drawing one top-level window on top of another is working for me here on Linux X11 . . . but now I need to find a way to have two event loops running together (I need my main event loop to somehow pick out the Qt events and forward them to the Qt event handlers).
-
Look what I got working today. The Qt plugin puts the button on the dialog box, and when you click the button, the Qt code shows a message box. Right now I don't know how the wxWidgets event handler system is forwarding events to the Qt library . . . but of course I'm not complaining.
-
Could someone please help me with something?
I achieved the above screenshot by getting Qt to create another top-level window without a frame and with a transparent background. So what I've got is a top-level frame with another top-level frame sitting on top of it. But of course really I just want Qt to draw the button on a pre-existing native window -- but this doesn't seem possible (I've tried all sorts of things). Perhaps could I do something like the following?
Step 1: Maintain a global map of QWidget objects to native handles ( e.g. std::map<QWidget*, WId> g_mywidgets; )
Step 2: Alter the source code of QWidget::show, make it check the aforementioned map, and if it finds the QWidget in the map, then make sure to show it as a child window placed upon the parent WId.Do you reckon this could be a good strategy? Hopefully it would mean minimal alteration to the Qt library.
Or here's another idea:
Could I get the Qt code to tell me the actual pixels of the widgets it will draw? If I can get these pixels written to a simple 2-dimensional array, e.g. int pixels[1024][1024], then I could send the contents of this array to my wxWidgets main program and get the wxWidgets program to draw the pixels. -
Okay I might be making a bit of progress here. I'm not sure how this will turn out but it's worth a shot. First of all, in the Qt plugin, I export a function which renders the widgets:
extern "C" void RenderWidgets(PixelArray_t &pixelData, int const w, int const h) { assert( nullptr != &pixelData ); // just for debug parentWidget->setGeometry(0, 0, w, h); QPixmap pixmap( parentWidget->size() ); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); parentWidget->render(&painter); painter.end(); QImage image = pixmap.toImage(); int const width = image.width(); int const height = image.height(); pixelData.resize( width, PixelArray_t::value_type(height) ); // 2D array of QColor for ( int x = 0; x < width; ++x ) { for ( int y = 0; y < height; ++y ) { pixelData[x][y] = (unsigned)image.pixel(x, y); } } }
In my main program which runs wxWidgets, I have a handler for the Paint event for the panel, implemented as follows:
void OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); // Create a paint DC for the panel int const w = dc.GetSize().GetWidth (); int const h = dc.GetSize().GetHeight(); pfnRender(pixels, w, h); // Call the function exported by the Qt plugin assert( this->pixels.size() > 0 ); assert( this->pixels.front().size() > 0 ); wxBitmap bitmap(w, h); // Create a bitmap that matches the panel size wxMemoryDC memDC(bitmap); // Create a memory DC to work with the bitmap for ( int x = 0; x < w; ++x ) { for ( int y = 0; y < h; ++y ) { unsigned const pixel = pixels[x][y]; unsigned char r = (pixel >> 24) & 0xFF; unsigned char g = (pixel >> 16) & 0xFF; unsigned char b = (pixel >> 8) & 0xFF; unsigned char a = (pixel >> 0) & 0xFF; memDC.SetPen( wxPen(wxColour(r, g, b), 1) ); memDC.SetBrush(wxBrush(wxColour(r, g, b), wxSOLID)); //memDC.SetAlpha(a); memDC.DrawRectangle(x, y, 1, 1); // Draw 1x1 pixel } } dc.DrawBitmap(bitmap, 0, 0); }
I've tested this out and it works. So I'm able to display widgets.
But next . . . here's what I need to do:
In the wxWidgets main program, record mouse movements and mouse clicks, and send them to the Qt plugin. Then inside the Qt plugin, I need to take these movements and clicks and somehow send them to the widgets (even though the widgets aren't on screen in the canonical sense). Can anyone help me with this part?