In Qt4, we could use the embedInto method of the QX11EmbedWidget class to embed any QWidget into another application. But in Qt5, the QX11EmbedWidget class was dropped. I've been searching Google for many hours, but all I found indicated to use the QWidget::createWindowContainer method instead. However, as far as I understand, this method is rather a replacement for QX11EmbedContainer.
So here's the question: How do we embed a Qt5-Widget into a non-Qt window? -
And in case it metters: The non-Qt window is Gtk3.
I've been asked for the use-case, so let me illustrate briefly: Consider a Gtk-based application, which you are willing to extend - and the UI component, which you need, already exists, but is written in Qt. The goal is to avoid porting the application to Qt or the component to Gtk.
My initial approach was to request the embedding from Qt side. I had no luck with this, as I couldn't find any equivalent for QX11EmbedWidget::embedInto in Qt5. After giving up on this, I decided to give XReparentWindow a try, which was reportedly used with success. However, due to lack of documentation, I wasn't able to reproduce this. My third attempt was to initiate the embedding from server side, which is the Gtk window in my case. I'm glad to report, that it worked :-)
For the Record: How to embed an arbitrary Qt5 Widget into a Gtk window
TL;DR:
Make sure that you write your Gtk code in a unit which is separate from the Qt part. This is necessary in order to avoid name conflicts between Qt5 and Gtk3.
Simply use gtk_socket_add_id to embed any X11 window into a GtkSocket.
Also see the documentation for reference, but note that the example given their doesn't even compile, because they forgot the GTK_SOCKET macro. On the contrary, the following code works.
Details
The QGtkWindow class provides an interface to the Gtk window.
class QGtkWindow : public QObject
{
public:
QGtkWindow();
virtual ~QGtkWindow();
void setCentralWidget( QWidget* const widget );
void show();
private:
GtkWindowAdapter gtkWindow;
QWidget* const container;
}; // QGtkWindow
The GtkWindowAdapter class wraps the Gtk calls and isolates them from the Qt part of the application. An object of this class represents the Gtk window.
class GtkWindowAdapter
{
public:
GtkWindowAdapter();
~GtkWindowAdapter();
void show();
void embed( unsigned long clientWinId );
private:
struct Details;
const std::auto_ptr< Details > pimpl;
}; // GtkWindowAdapter
When instantiated, an GtkWindowAdapter object first initializes Gtk,
static bool gtkInitialized = false;
struct GtkWindowAdapter::Details
{
GtkWidget* widget;
GtkWidget* socket;
void setupUi();
};
GtkWindowAdapter::GtkWindowAdapter()
: pimpl( new Details() )
{
if( !gtkInitialized )
{
int argc = 0;
gtk_init( &argc, NULL );
gtkInitialized = true;
}
pimpl->setupUi();
}
and then setups the Gtk window:
void GtkWindowAdapter::Details::setupUi()
{
widget = gtk_window_new( GTK_WINDOW_TOPLEVEL );
socket = gtk_socket_new();
gtk_widget_show( socket );
gtk_container_add( GTK_CONTAINER ( widget ), socket );
gtk_widget_realize( socket );
}
You might already have noted the embed method which this class provides. This method initiates the embedding of any X11 window. The show method turns the encapsulated Gtk window visible.
void GtkWindowAdapter::embed( unsigned long clientWinId )
{
gtk_socket_add_id( GTK_SOCKET( pimpl->socket ), clientWinId );
}
void GtkWindowAdapter::show()
{
gtk_widget_show( pimpl->widget );
}
Now, the implementation of the QGtkWindow class is fairly simple. When created, it initializes a Gtk window by using the GtkWindowApdater and puts an empty QWidget into that window:
QGtkWindow::QGtkWindow()
: container( new QWidget() )
{
container->setLayout( new QVBoxLayout() );
gtkWindow.embed( container->winId() );
container->show();
}
When the user of the QGtkWindow class decides to put some widget into the window, the setCentralWidget is the way to go. It simply clears the parent widget, which was embedded into the Gtk window originally, then inserts the user's widget instead:
void QGtkWindow::setCentralWidget( QWidget* const widget )
{
qDeleteAll( pimpl->container->layout()->children() );
pimpl->container->layout()->addWidget( widget );
}
void QGtkWindow::show()
{
pimpl->gtkWindow.show();
}
Hope this might spare somebody many hours.
To quote from this link:
https://forum.qt.io/topic/32785/qwindow-qwidget-qt5-x11embedding-how/2
All the x11 stuff has moved to the "extras" department found on
Gitorious. (QX11EmbedWidgets and QX11EmbedContainer and the likes are
not in 5.x)
Try this:
http://qt-project.org/doc/qt-5.1/qtx11extras/qx11info.html
Related
Maybe my title is unclear, so I will tell here a more precise explanation:
I am just learning WxWidgets, and I am now trying to make two files: main.cpp and Quit.h. Main.cpp will have the core of the application, and Quit.h will have the class for the quit dialog: Do you really want to quit this application (Yes / No).
Now this is my Quit.h file (without include part):
class Quit : public wxFrame
{
public:
Quit(const wxString& tekst);
};
Quit::Quit(const wxString& tekst)
{
wxMessageDialog* dial = new wxMessageDialog(NULL, _("Do you really want to quit?"), _("Quit"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
dial->ShowModal();
}
And here I am stuck. I tried with wxDECLARE_EVENT_TABLE(), but I don't know which event stands for this: "on the press of yes button (in wxYES_NO system of buttons)". I can't say: on the press of wxYES_NO because these are two buttons (both YES and NO).
So how can I execute the function when the button YES is pressed?
Thank you!
P.S.
I really apologize for this unclear question, but I hope, that you'll understand. Note that I am just a beginner, so please don't use so many "technical" words and expressions in the answer. I read the documentation, but it uses so many technical expressions and explanation. Also, I read this book.
P.P.S.
Have you noticed that there are a lot of questions on SE now, while there is COVID-19 on its way?
EDIT: When I was making the program on, I came to the other error. Minimal code:
Quit.h
class Quit : public wxFrame
{
public:
Quit(const wxWindow* parent, const wxString& text);
};
Quit::Quit(const wxWindow* parent, const wxString& text)
{
int dialog_return_value = wxNO;
wxMessageDialog* dial = new wxMessageDialog(NULL, text, _("Exit"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
dialog_return_value = dial->ShowModal();
switch (dialog_return_value)
{
case wxYES:
Close(true);
break;
case wxNO:
Close(false);
break;
default:
Close(false);
};
}
and then I have this line in main.cpp:
void MyFrame::CloseWindow(wxCommandEvent& event)
{
Quit* quit = new Quit(this, _("Do you really want to close the App?"));
}
And then it doesn't work. I can't find the solution, so, if you have some time, please help.
Thank you again!
I would suggest using the wxEvtHandler::Bind<>() function as detailed in the wxWidgets documentaton at https://docs.wxwidgets.org/3.0/overview_events.html. The Bind() function allows dynamic binding of events and the syntax is one line of code as compared to setting up tables to link events to objects.
Additionally see this wxWidgets user forum thread which has detailed explanation for calling member and nonmember methods https://forums.wxwidgets.org/viewtopic.php?t=39817
wxYES_NO is a style flag that tells wxWidgets framework that you want both yes and no buttons in your dialog. Check if the return value of ShowModal() is equal to one of the builtin macros defined as wxYES and wxNO.
See here for the macro definitions https://docs.wxwidgets.org/trunk/defs_8h.html
And you should read up on wxDiaglog. Start here https://docs.wxwidgets.org/trunk/classwx_dialog.html
Do you want to return the value to the caller of Quit::Quit()? Constructors do not return values, you can set a member variable to the value but remember that if the object is destroyed then your member variable is gone too. You have not provided enough information to know what needs to be done for cleanup when you Quit() so I will provide you with the code to check the return value, just fill in what you need in the case body.
Here is how you would check the return value:
class Quit : public wxFrame
{
public:
Quit(const wxString& tekst);
};
Quit::Quit(const wxString& tekst)
{
int dialog_return_value = wxNO; // initialize to a sane default value
wxMessageDialog* dial = new wxMessageDialog(NULL, _("Do you really want to quit?"), _("Quit"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
dialog_return_value = dial->ShowModal();
// You do not have cancel button so only check wxYES and wxNO
switch( dialog_return_value) // Use switch, scales to more buttons later
{
case wxYES :
/* do something */
break;
case wxNO :
/* do something */
break;
default : /* ignore or handle error */ ;
};
}
You are doing a technical task, it is reasonable to expect that learning "technical" words will be involved.
I checked the code return values are not the same as the input style values, we need to check the return value with wxID_YES instead of wxYES!
I was trying to stick to using as much as possible your code but it makes no sense to me using a plain class to close the application. In that case with wxWidgets you still need to reference your main frame to accomplish the closure. There are easier ways as shown in the example below.
Following is a full working example of an application which simply has a quit button on a frame. You click the button and get the quit message dialog. wxWidgets allows creating dialogs on the stack as opposed to the heap and that is what you need here because the dialog is trivial and will not be reused.
You can copy/paste/compile/run the following code as long as you are using wxWidgets 3+ (I am pretty sure Bind() was added then, may have been slightly earlier)
#include <wx/wx.h>
// toolkit requires defining a wxApp class, OnInit() will be called automatically
// when the wxIMPLEMENT_APP macro is invoked below
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
class MyFrame : public wxFrame
{
public:
MyFrame();
~MyFrame();
private:
void OnExit( wxCommandEvent& event );
// these pointer are owned by the wxWidgets toolkit, do not delete them
// but you need them in a "real" app to add items to the sizer or change
// button properties
wxSizer* m_frame_sizer;
wxButton* m_quit_button;
};
// toolkit requires calling this macro with a wxApp object to bootstrap the GUI framework
wxIMPLEMENT_APP( MyApp );
// OnInit is loosely synonymous with main(), it is where the GUI thread is started
bool MyApp::OnInit()
{
// Create a frame with button
MyFrame* frame = new MyFrame();
// Show the frame with its button
frame->Show( true );
// If return value is false, the wxWidgets framework will kill the app
return true;
}
MyFrame::MyFrame() : wxFrame( NULL, wxID_ANY, "Test Exit" )
{
// wxWidgets requires all controls to be placed in a sizer
m_frame_sizer = new wxBoxSizer( wxVERTICAL );
// Assign the sizer to the frame
this->SetSizer( m_frame_sizer );
m_quit_button = new wxButton( this, wxID_EXIT, "Quit" );
// Put the button into the sizer
m_frame_sizer->Add( m_quit_button, wxSizerFlags().Center() );
// Here we bind the button click event to the OnExit method of MyFrame
// keep in mind that this technique will bind all buttons with id wxID_EXIT to the method
// m_quit_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnExit, this) will also work
// to handle the event for just the m_quit_button (notice the lack of wxID_EXIT, it is not needed in this case)
Bind( wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnExit, this, wxID_EXIT );
}
MyFrame::~MyFrame()
{
// for illustration, not strictly needed here becasue the entire app is shutting down
Unbind( wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnExit, this, wxID_EXIT );
// OR m_quit_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnExit, this) for the alternative form
}
void MyFrame::OnExit( wxCommandEvent& event )
{
// Setup a message box with (in order below) the user query text, a title, and style which puts yes/no button and a question mark icon
// Create the message box on the stack as opposed to the heap becasue we only need it here
int answer = wxMessageBox( "Do you rally want to quit?", "Exit App", wxYES_NO | wxICON_QUESTION );
if( answer == wxYES )
{
this->Close( true );
}
// else just return
}
wxYES is a different value than wxID_YES (the codes are 2 vs. 5103). wxMessageDialog::ShowModal returns "one of wxID_OK, wxID_CANCEL, wxID_YES, wxID_NO or wxID_HELP". So the switch statement as written will always trigger the default case. This applies for wx3.1- hopefully those variables will be consolidated in the future as the redundant values do lend themselves to mistakes.
For completeness, the provided switch statement should be:
switch (dialog_return_value)
{
case wxID_YES: //Subtly different from wxYES
Close(true);
break;
case wxID_NO: //Not wxNO
Close(false);
break;
default:
Close(false);
};
I have a Custom Container Widget (we’ll call it WidgetA) which consists of some number of widgets, as well as a container that is exposed to Qt Designer using a QDesignerContainerExtension. I then have another widget (WidgetB) which extends WidgetA, adding a number of widgets to the container in WidgetA. Both widgets have a .ui form file associated with them.
This configuration works well for my purposes with one exception. Qt’s connectSlotsByName function, which runs within the setupUI function of both classes, is causing the widgets of Widget A to be connected to their slots twice. My code looks something like this:
// WidgetA.h
namespace Ui {
class WidgetA;
}
class WidgetA : public QWidget
{
Q_OBJECT
public:
explicit WidgetA( QWidget* parent = 0 ) :
QWidget( parent ),
ui( new Ui::WidgetA )
{
ui->setupUi( this ); // <-- Connects widgets in WidgetA.
}
// ...
private:
Ui::WidgetA* ui;
}
// ...
// WidgetB.h
namespace Ui {
class WidgetB;
}
class WidgetB : public WidgetA
{
Q_OBJECT
public:
explicit WidgetB( QWidget* parent = 0 ) :
WidgetA( parent ), // <-- WidgetA’s constructor, calls setupUi resulting in WidgetA’s widgets being connected.
ui( new Ui::WidgetB )
{
ui->setupUi( this ); // <-- Connects widgets in WidgetA and WidgetB, resulting in duplicate connection of WidgetA’s widgets.
}
// ...
private:
ui::WidgetB* ui;
}
I’m aware of the Qt:: UniqueConnection flag, but because the ui’s header class is generated automatically I have no control over the nature of the connections. How can avoid this double connection?
The answer is simple:
Do not use the connectSignalsByName feature, do not create methods like on_object_signal(), this is just asking for trouble in any software.
You can use Qt Designer drag'n drop signal connectors if the widget in question should be connected via it's UI, but in my personal experience dealing with huge codebases written in Qt is that you should not use that and instead use the connect() function manually as it's much less errorprone.
The Situation
My company has a QML-based application which displays some content using a custom OpenGL-based render plugin (MyGame). This plugin has a few critical needs:
To be able to effect changes in the renderer in response to QML-based signals.
(e.g. change the position of an object rendered by the game)
To only process these changes at a specific spot in MyGame's redraw loop.
(This is very important; MyGame is very sensitive about when changes are allowed.)
To have the plugin redraw at 60Hz (at least).
The Problem
The code we have right now honors (1) and (2), but fails (3); the plugin does not get visually updated consistently. (The updates are erratic, at an estimated 5-10Hz.) I believe that the plugin we have created—based on QQuickFramebufferObject—is not taking proper advantage of how Qt/QML intended the scene graph to be updated.
How can I re-structure my plugin so that I get all three of the above?
The Code
Overview:
The plugin creates a QQuickFramebufferObject (MyPlugin) and a QQuickFramebufferObject::Renderer (MyRenderer).
When MyRenderer::render() is called it calls MyGame::Redraw() itself, and then calls update().
MyGame::Redraw() does what it needs to, and at the right spot where changes can be accepted, emits a timeToMakeChanges QML signal on MyPlugin.
QML listens for the onTimeToMakeChanges signal and invokes methods on the plugin that affect MyGame.
To workaround the problem of low-frequency visual updates, I've found that if I overlay a QML Canvas over my plugin and redraw the canvas frequently using a Timer, my plugin starts to get visually updated at what appears to be around 60Hz. Clearly this is a gross hack.
Following is a summary of the code setup. Please forgive missing/incorrect code; I'm trying to distill thousands of lines of glue code down to the essentials for this question.
MyPlugin.h
#include <QOpenGLFramebufferObject>
#include <QQuickFramebufferObject>
class MyPlugin : public QQuickFramebufferObject {
Q_OBJECT
public:
MyPlugin();
virtual ~MyPlugin();
virtual QQuickFramebufferObject::Renderer* createRenderer() const;
signals:
void timeToMakeChanges();
public slots:
void makeChanges(QVariant inValue);
void HandleWindowChanged(QQuickWindow *inWindow);
private:
MyGame* GetGame() { ... }
};
MyPlugin.cpp
#include "MyPlugin.h"
#include <MyGame.h>
// ******************************************************************
class MyRenderer:
public QObject,
public QQuickFramebufferObject::Renderer,
protected QOpenGLFunctions
{
Q_OBJECT
public:
virtual void render();
private:
static void RequestGameChanges();
};
void MyRenderer::render() {
if ( !m_Initialized ) {
QOpenGLFramebufferObject *theFbo = this->framebufferObject();
InitializeGl( theFbo ); // Not shown
m_MyGame = &MyGame::Create();
m_MyGame->RegisterCallback(
reinterpret_cast<qml_Function>(MyRenderer::RequestGameChanges)
);
m_Initialized = true;
}
m_MyGame->RestoreState();
m_MyGame->Redraw();
m_MyGame->SaveState();
m_PluginItem->window()->resetOpenGLState();
// Tell QML that we want to render again as soon as possible
update();
}
// This gets invoked in the middle of m_MyGame->Redraw()
void MyRenderer::RequestGameChanges() {
emit m_PluginItem->timeToMakeChanges();
}
// ******************************************************************
MyPlugin::MyPlugin() {
setMirrorVertically(true);
connect(
this, SIGNAL(windowChanged(QQuickWindow*)),
this, SLOT(HandleWindowChanged(QQuickWindow*))
);
}
void MyPlugin::HandleWindowChanged(QQuickWindow *inWindow) {
inWindow->setClearBeforeRendering(false);
}
void MyPlugin::makeChanges(QVariant inValue) {
MyGame *theGame = GetGame();
// Send the requested changes to theGame
}
QQuickFramebufferObject::Renderer* MyPlugin::createRenderer() const {
m_Renderer = new MyRenderer( *this );
}
MyApp.qml
import MyPlugin 1.0
Window {
MyPlugin {
property var queuedUpChanges: ([])
onSomeOtherSignal: queueUpChangesToMake();
onTimeToMakeChanges: makeChanges( queuedUpChanges );
}
Canvas { id:hack }
Timer {
interval:10; running:true; repeat:true
onTriggered: hack.changeWhatYouShow();
}
}
Bonus Points
The main question is "How do I modify my code so that I get 60Hz updates?" However, as seen in the QML, the setup above requires me to queue up all changes in QML so that they are able to be applied during the right spot in the MyGame::Render().
Ideally, I'd prefer to write QML without timeToMakeChanges, like:
MyPlugin {
onSomeOtherSignal: makeChanges( ... );
}
If there's a way to accomplish this (other than queuing up the changes in C++ instead)—perhaps something related to synchronize() I'd love to know about it.
I'd make a timer in QML that calls the makeChanges regularly. But store all the state in MyPlugin. Then, in Renderer::synchronize(), copy from MyPlugin to MyRenderer, so it can be used by the MyGame.
(although, I wouldn't do any gamelogic-related calculations in QML ever in the first place)
I have written a basic image viewing application using Qt and C++, i.e. I have a class
ImageViewApp : public QMainWindow, private Ui::ImageViewer {
Q_OBJECT
public:
ImageViewApp ( const char * InputFilename = NULL ) {
setupUi ( this );
}
};
with a .ui file created with Qt Designer, that generates a header file defining
class Ui_ImageViewer {
public:
void setupUi(QMainWindow *ImageViewer) { … }
};
namespace Ui {
class ImageViewer: public Ui_ImageViewer {};
} // namespace Ui
Now I would like to write another application ImageRegistrationApp, where I extend the QMenuBar of ImageViewApp by additional QMenus containing QActions for a certain purpose, say image registration. Additionally, I would like to change some other things, such as the windowTitle and add QActions to existing QMenus of ImageViewApp.
I am looking for a way where I don't need to touch or copy the the .ui file of ImageViewApp. I would like to do something like inheritance, where changes to the UI of ImageViewApp affect the UI of ImageRegistrationApp. Also I would like to be able to create and edit the additional QMenus and QActions for ImageRegistrationApp via Qt Designer.
Is this possible?
UPDATE:
I tried changing the title of ImageRegistrationApp and adding a QAction to an existing QMenu in ImageViewApp through inheritance within C++ as follows:
ImageRegistrationApp : public ImageViewApp {
Q_OBJECT
public:
QAction *actionTest;
ImageRegistrationApp ( const char * InputFilename = NULL )
: ImageViewApp ( InputFilename ) {
this->setWindowTitle(QApplication::translate("ImageViewer", "ImReg", 0, QApplication::UnicodeUTF8));
actionTest = new QAction(this);
actionTest->setObjectName(QString::fromUtf8("actionTest"));
actionTest->setText(QApplication::translate("ImageViewer", "Test", 0, QApplication::UnicodeUTF8));
this->menuTools->addAction(actionTest);
}
protected slots:
void on_actionTest_triggered() {
QMessageBox::information(NULL, "Test", "Hello world!" );
}
};
I also changed the inheritance in ImageViewApp to protected Ui::ImageViewer in order to be able to access the Ui elements.
The title of my ImageRegistrationApp changes as intended and also the QAction Test shows up in the menu Tools, but when I click it, nothing happens though I expect it to display the QMessageBox as defined in the slot on_actionTest_triggered.
Is there anything else I have to do, to connect the QAction with the slot?
I tried QObject::connect(actionTest, SIGNAL(triggered()), this, SLOT(actionTest)); but this did not change anything.
The problem with the slot not being executed when the QAction actionTest was triggered, was a misspelling. I had also tried QObject::connect(actionTest, SIGNAL(on_triggered()), this, SLOT(on_actionTest_triggered)); before, but there were the () missing at the end.
Adding the following line at the end of the constructor of ImageRegistrationApp solved the problem:
QObject::connect(actionTest, SIGNAL(on_triggered()), this, SLOT(on_actionTest_triggered()));
Instantiating ImageRegistrationApp now instantiates the entire GUI of ImageViewApp and adds the additionally defined QActions to the respective QMenus. This method should be capable of everything I asked for in the question, except for designing additional QMenus with Qt Designer. Instead, everything has to be coded in C++ source.
I'm using the QMdiArea in Qt 4.4.
If a new project is created, I add a number of sub windows to a QMdiArea. I'd like to disallow the user to close a sub window during runtime. The sub windows should only be closed if the whole application is closed or if a new project is created.
How can I do this?
You need to define your own subWindow. create a subclass of QMdiSubWindow and override the closeEvent(QCloseEvent *closeEvent). you can control it by argument. for example:
void ChildWindow::closeEvent(QCloseEvent *closeEvent)
{
if(/*condition C*/)
closeEvent->accept();
else
closeEvent->ignore(); // you can do something else, like
// writing a string in status bar ...
}
then subclass the QMdiArea and override QMdiArea::closeAllSubWindows () like this:
class MainWindowArea : public QMdiArea
{
Q_OBJECT
public:
explicit MainWindowArea(QWidget *parent = 0);
signals:
void closeAllSubWindows();
public slots:
};
// Implementation:
MainWindowArea::closeAllSubWindows()
{
// set close condition (new project is creating, C = true)
foreach(QMdiSubWindow* sub,this->subWindowList())
{
(qobject_cast<ChildWindow*>(sub))->close();
}
}
you may also need to override close slot of your mdi area.
You'd do this the same as for a top-level window: process and ignore the QCloseEvent it sent. QMdiArea::closeActiveSubWindow/QMdiArea::closeAllSubWindows just call QWidget::close, which sends a closeEvent and confirms that it was accepted before proceeding.
You can process this event by subclassing QMdiSubWindow and reimplementing QWidget::closeEvent, or by using an event filter to intercept it..