GUI program accepts various kinds of GUI plugin
-
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?