Embedding another Window as a QWidget - c++

I'm currently working on a project that uses Allegro for rendering, input, etc. However I would like to add a GUI to my project using something like Qt. The problem is that Allegro does not support using a Window not created by allegro for rendering/input, it needs to create the window itself. I was thinking of using Qt to make the UI, and then creating a window normally using allegro, and then somehow embedding the allegro window into the Qt application.
Allegro provides the HWND handle to the window its using. Is there anyway to embed the allegro window into a Qt ui using its HWND handle?

You need the QWinHost class from the Qt/MFC Migration Framework. The code is 3-clause BSD licensed. You only need two files: qwinhost.h and qwinhost.cpp, available here.
It does exactly what you need, and works on both Qt 4 and 5.

Related

How to embed a native window into QML component?

I'm developing a cross-platform SIP application based on PJSUA2 for the core and QtQuick for the GUI.
PJSUA2 provides an API for displaying the user's capture devices as well as the remote party video stream. Such an API exposes a native window handler for a given video. The native window handler is platform-specific (HWND on Windows, NSView* on Mac, etc).
I'd like to embed this native window into a specific QML component, let's say a Rectangle.
Is that possible?
FYI: I'm using MacOS Sierra 10.12.6, PJSIP 2.7 with SDL backend and Qt 5.9.2. But I'd like to make it work on both Windows and MacOS.
You cannot. You can't even include a QWidget inside a Qt Quick Item.
To give you somehting to chew, you can take a look at https://github.com/vlc-qt/vlc-qt .
They offer QWidgets and QtQuick Items that allow to use VLC to play videos.
They use a window handle (HWND) for the widget (https://github.com/vlc-qt/vlc-qt/blob/master/src/core/MediaPlayer.cpp#L217).
But for Qt Quick they copy each video frame into a QSGNode (https://github.com/vlc-qt/vlc-qt/blob/master/src/core/VideoStream.cpp#L111 and https://github.com/vlc-qt/vlc-qt/blob/master/src/qml/rendering/VideoNode.cpp#L32).

Qt5 QWidget::create() with Win32 HWND embedding not longer working after port from Qt4

the following code tries to embed a native Win32 HWND of a custom created OpenGL windows into a QWidget using the create method:
viewer_widget::viewer_widget(
QWidget* parent,
const viewer::viewer_attributes& view_attrib,
const wm::context::attribute_desc& ctx_attrib,
const wm::surface::format_desc& win_fmt)
: QWidget(parent)
{
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_NativeWindow, true);
setAttribute(Qt::WA_PaintOnScreen, true); // disables qt double buffering (seems X11 only since qt4.5, ...)
setAttribute(Qt::WA_NoSystemBackground, true);
setAutoFillBackground(false);
_viewer = make_shared<viewer>(math::vec2ui(100, 100), parent->winId(), view_attrib, ctx_attrib, win_fmt);
// ok set the native window as this widgets window...and hold thumbs
QWidget::create(_viewer->window()->window_handle(), true, true);
}
The viewer creates a native Win32 (or X11) window wit the parent of the QWidget as a parent. It also creates and initializes an OpenGL context. This was/is done for more control over the context creation and live time (I know that Qt5 is much improved in that regard). The QWidget::create() method now takes the HWND of the native window and embeds it into the current QWidget, so that event handling is completely done through Qt.
This works perfectly on Qt4 (latest used is Qt 4.8.6 x64 on Windows 7/8.1 x64 on Visual Studio 2013).
Now when porting to Qt5 the same code should still work according to the Qt5 documentation. It required minor changes to account for the change in the WId type. The QWidget::winId() methods still return the native HWND handles of the widgets, which I verified using spyxx.exe (Visual Studio Tools).
However, the code does not work anymore (using Qt 5.4.0 x64 on Windows 7/8.1 x64 on Visual Studio 2013). The native window is just not embedded. In Qt4 when inspecting the created QWidget its native handle (winId) after the create call was identical to the native HWND, which meant the embedding worked. Now using Qt5 the QWidget contains its own HWND which I could again confirm using spyxx.exe. Now there is the parent widget/window and two child widgets/windows where there should only be one child (the native one). I looked at the source code of the create() method for both Qt versions and I do not understand why it is not working anymore.
Ok, after the first night trying to figure this out I tried several other methods I could find on forums or the documentation:
QWindow::fromWinId(HWND) and QWidget::createWindowContainer(QWindow): This one seems to be the way Qt-Devs want me to do it:
_viewer = make_shared<viewer>(math::vec2ui(100, 100), par_hndl, view_attrib, ctx_attrib, win_fmt);
QWindow* native_wnd = QWindow::fromWinId((WId)_viewer->window()->window_handle());
QWidget* native_wdgt = QWidget::createWindowContainer(native_wnd);
QHBoxLayout* lo = new QHBoxLayout(this);
lo->setContentsMargins(0, 0, 0, 0);
lo->addWidget(native_wdgt);
This at least at least behaves almost the way I expect. I see my window and in the newly created widget the native window is embedded. BUT: I have found no way to get any mouse events/input from that newly created widget. I added an eventFilter without luck. I tried a QStackedLayout with a transparent top-level widget to catch the input, but this does not work as the widget created through createWindowContainer() is always on top of the windows stack. I tries creating a QWindow-derived class to intercept the nativeEvent() invocation, without luck...
I am at the end of my ideas to get this working as with Qt4. Is there anything I can to differently to get the old behavior back? The keyboard input I can track through the parent widget using a Qt::StrongFocus policy but the mouse input in complete swallows by the native window. I cannot move the the Qt5 OpenGL window code and need to use our custom OpenGL context tools as we are doing things with the contexts that Qt5 still does not fully support.
I could not yet try Qt5 on Linux, but the Qt4 code as seen above did work there.
I have largely solved communication from Qt to the window by subclassing QWidget, using create() as you originally did, and reimplementing the QWidget event functions to make changes to the native window directly. The ones I've found so far (the big ones) are the focus events, resizeEvent, moveEvent, and the contents rect and enable changeEvents. Font, palette, tooltip, etc. changeEvents are probably a lower priority.
The above won't solve the reverse issue, that messages from the native window never arrive in Qt's event dispatcher. You will need to post messages from your WndProc to the widget's HWND (i.e., the return from calling winId()). This also nearly steps around trying to translate virtual key codes to Qt::Keys and back.
Some notes about why this doesn't work gracefully, as it did in Qt4:
QWindow only works on X11 with native child widgets right now. Try testing on Linux as you mentioned.
QWidget::create() and QWindow have been problems since 5.1 at the least. The problems you're having in both implementations seem to fit.
Here is a consolidated bug report about QWindow intended usage and issues
Other notes:
QWidget::effectiveWinId() is preferred even though you are creating an immediate child window. This, unfortunately, breaks in 5.4.1.
Update to 5.4.1. There are binary compatibility issues with 5.4.0 and all other versions of Qt5.

QT How to embed an application into QT widget

In our project we have three independent applications, and we have to develop a QT control application that controls these three applications. The main window will be seperated to three sub windows - each one display another one application.
I thought to use QX11EmbedWidget and QX11EmbedContainer widgets, but two problems with that:
The QX11Embed* is based on X11 protocol and I dont know if it's supported on non-x11 systems like Windows OS.
Since QT 5 these classes are not existing, and the QT documentation doesn't mention why.
So that I dont know whether to use it or not - I'll be happy to get an answers.
In addition, I see that the QT 5.1 contains QWidget::createWindowContainer(); function that in some posts it looks like this should be the replacement to the X11Embed. Can anyone please explian me more how can I use this function to create a QT widget that will run another application (a Calculator for example) inside its?
I have searched a lot in Google, and didn't find answers to my Qs.
Can anyone please help me? Am I on the right way?
Thanks!
If all three independent applications are written with Qt, and you have their source, you should be able to unify them just through the parenting of GUI objects in Qt.
http://qt-project.org/doc/qt-4.8/objecttrees.html
http://qt-project.org/doc/qt-4.8/widgets-and-layouts.html
http://qt-project.org/doc/qt-4.8/mainwindows-mdi.html
If you don't have access to them in that way, what you are talking about is like 3rd party window management. It is kind of like writing a shell, like Windows Explorer, that manipulates the state and the size of other window applications.
Use a program like Spy++ or AutoIt Spy for Windows and the similar ones for other OS's, and learn the identifying markings of your windows you want to control, like the class, the window title, etc. Or you can launch the exe yourself in a QProcess::startDetached() sort of thing.
http://qt-project.org/doc/qt-5.1/qtcore/qprocess.html#startDetached
Then using the OS dependent calls control the windows. The Qt library doesn't have this stuff built in for third party windows, only for ones under the QApplication that you launched. There are a lot of examples of doing things like this by AutoHotKey, or AHK. It is a scripting language that is made for automating a lot of things in the windows environment, and there is port for Mac as well (though I haven't tried the mac port myself).
So in the end you are looking at finding your window probably with a call like this:
#include <windows.h>
HWND hwnd_1 = ::FindWindow("Window_Class", "Window Name");
LONG retVal = GetWindowLongA(hwnd_1, GWL_STYLE); // to query the state of the window
Then manipulate the position and state of the window like so:
::MoveWindow(hwnd_1, x, y, width, height, TRUE);
::ShowWindow(hwnd_1, SW_SHOWMAXIMIZED);
You can even draw widgets on top of the windows you are controlling if you set your window flags correctly for the windows you are manipulating.
transparent QLabel with a pixmap
Cannot get QSystemTrayIcon to work correctly with activation reason
Some gotchas that come up in Windows when doing all of this, is finding out the quirks of the Windows UI when they set the Display scaling different from what you expect, and if you want to play nice with the Task bar, and handling all the modal windows of your programs you are manipulating.
So overall, it is do-able. Qt will make a nice interface for performing these commands, but in the end you are looking at a lot of work and debugging to get it in a beautiful, reliable, window manager.
Hope that helps.
I never tried it myself, but from the docs in Qt 5.1 I would try QWindow::fromId(WId id), which gives you a QWindow, which should be embeddable with createWindowContainer:
QWindow * QWindow::fromWinId(WId id) [static] Creates a local
representation of a window created by another process or by using
native libraries below Qt.
Given the handle id to a native window, this method creates a QWindow
object which can be used to represent the window when invoking methods
like setParent() and setTransientParent(). This can be used, on
platforms which support it, to embed a window inside a container or to
make a window stick on top of a window created by another process.
But no guarantee. :-)

How to integrate Qt into an existing application development workflow?

I have been playing with Qt for a week or so, with the aim of integrating Qt dialogs into an existing application. I've figured out use a basic Qt Message box from my MFC application, eg:
extern "C" __declspec(dllexport) bool showDialog( HWND parent )
{
QWinWidget win( parent );
win.showCentered();
QMessageBox::about( &win, "About QtMfc", "QtMfc Version 1.0\nCopyright (C) 2003" );
return TRUE;
}
I've used Qt Designer to create dialog boxes. What I haven't figured out is how to bring all of these pieces together. Qt Designer leaves me with a .ui file. I've seen that I can use uic to compile that to a .h file, but where are the other parts, like .cpp files? Further, if I want to compile the Qt parts to a dll, which are loaded on demand, what is the process for that? To put it into context, the existing application has 1000's of dialogs that the user may want to use, hence they all live in dll's that are loaded as needed and the dialogs come as additional downloads, kind of a plugin if you will, separately from the main program.
This is probably missing a lot, so please bring on the questions.
thanks,
Daniel
Get the VS plugin, it makes Qt work seamlessly with VS.
It complies ui files into classes which you then call from your code. You can also write dialogs, menus, etc in your C code. Since it uses sizers for layout there isn't a lot of X-Y coords to manage to place every component.
Although if you can I would go for a big-bang approach of replacing the MFC main with a Qt main and moving the functionality over. AFAIK MFC dialogs should work perfectly well in a Qt app.

Controls on main window using Visual C++ designer?

Is is possible to draw controls using Visual C++ designer on the main window, in the same way you can design dialogs? I'd preferably like to be able to design the main window controls this way without using MFC, rather than creating them on WM_CREATE.
EDIT: I don't want a dialog-based app, just to be able to design the main window graphically similar to what can be done using Windows Forms Designer in .NET?
Your options are:
Use MFC and create a main window that has a dialog view (based on the CFormView class).
Use WinForms/.NET
Use Qt.
If you're starting a new project and you want to stick with C++, then I highly recommend Qt. Not only is it an excellent framework, but it's cross-platform so your app could be built on Linux and the Mac.
http://www.qtsoftware.com/products/
A Visual C++ plugin is available and you can design your main window visually using a tool called Qt Designer.
I'm not sure if I understand what you want your app to look like. If you want your application to be a dialog, then make it a dialog app.
Just create a new MFC Application, and set it to "Dialog based". Now your application will start at that dialog.
If you want to use a native win32 app, just create the dialog in your InitInstance, using CreateDialog (instead of CreateWindow).
In both cases, you use the resource editor to create the dialog.