How to embed one qApplication's GUI into another qApplication's mainWindow? - c++

There are two qApplications A & B, they can be executed separately with their own main window.
I would like to achieve the following:
1) //Open Application B.
//Inside App B's code
QProcess* proA = new QProcss();
proA->start(A.exe) //Under Windows7
2) //Instead of showing app A in a separate window.
//I would like to show it as a widget of app B's main window.
Sort of like google chrome. A similar post here:QT How to embed an application into QT widget talked about the similar problem. But it involves implement your own window management system. Is there simpler solutions as both my app are Qt's qApp and both uses QWindow.

It's definitely possible if the two QApplications are in different processed.
Create two process each with it's QApplication and QWidget
From one process, find the winId of the other processe's QWidget and reparent it to your own widget.
To manage the widget from the other process reparented to yours, you may use qtwinmigrate. Originally this was meant to embed a MFC widget (with its own CWinApp) in a Qt widget, but it can also help embedding a QWidget from a separate process.
Here is a piece of working code:
Child process:
#include <QLabel>
#include <QWidget>
#include <QVBoxLayout>
#include <QApplication>
#include <sstream>
int main(int argc, char **argv)
{
QApplication app(argc,argv);
QWidget widget;
widget.setWindowTitle( "CHILD WINDOW" );
std::stringstream str;
str << "QWidget ID: " << widget.winId() << std::endl;
str << "Process Name: " << argv[0];
Qt::WindowFlags flags = widget.windowFlags();
flags |= Qt::FramelessWindowHint;
flags |= Qt::MSWindowsFixedSizeDialogHint;
flags |= Qt::SubWindow;
widget.setWindowFlags( flags );
widget.setLayout(new QVBoxLayout(&widget));
QLabel label;
widget.layout()->addWidget(&label);
label.setText(str.str().c_str());
widget.setStyleSheet( "background: red" );
widget.show();
QEvent e(QEvent::EmbeddingControl);
QApplication::sendEvent(&label, &e);
app.processEvents();
return app.exec();
}
Parent process:
#include <QMainWindow>
#include <QApplication>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QLabel>
#include <QProcess>
#include "windows.h"
#include "qtwinmigrate5/qwinhost.h"
#include <iostream>
#include <sstream>
/* The EnumChildProc callback */
static HWND hWndHandle = NULL;
static qint64 childProcessID = 0;
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
{
DWORD dwProcessID = 0;
if (GetWindowThreadProcessId(hwnd, &dwProcessID) &&
dwProcessID == childProcessID )
{
char strTemp[256] = "";
GetWindowText(hwnd, strTemp, 256);
std::string str = strTemp;
if (str == "CHILD WINDOW") // sanity check
hWndHandle = hwnd;
}
return ( hWndHandle == NULL ); // must return TRUE; If return is FALSE it stops the recursion
}
void* GetChildWindowHandle( qint64 pid )
{
hWndHandle = NULL;
childProcessID = pid;
EnumWindows(EnumChildProc, 0);
return hWndHandle;
}
int main(int argc, char **argv)
{
QApplication app(argc,argv);
QMainWindow wnd;
wnd.setWindowTitle("That's the parent window!");
// Create child process:
QProcess process;
process.start("test_3rdparty_inprg_qtwinmigrate_child.exe");
if (process.state() != QProcess::Running)
{
QMessageBox::critical(NULL, "ERROR", "Unable to create child process");
return 1;
}
// Create qtwinmigrate widget container:
QWinHost* host = new QWinHost( &wnd );
// Get child process wiindow handle
HWND hChildWnd = NULL;
int timeout = 20;
while ((hChildWnd = (HWND)GetChildWindowHandle(process.processId())) == NULL)
{
// let child process more time to create and show its widget....
Sleep(200);
--timeout;
if (timeout == 0)
break;
}
int res = 1;
if (hChildWnd != NULL)
{
// attach child window handle to qtwinmigrate widget container
host->setWindow(hChildWnd);
char strTemp[256] = "";
GetWindowText(hChildWnd, strTemp, 256);
QWidget centralWidget(&wnd);
wnd.setCentralWidget(&centralWidget);
QVBoxLayout* layout = new QVBoxLayout(&centralWidget);
std::stringstream str;
str << "Attached data window " << std::showbase << std::hex << hChildWnd << std::endl;
str << "Window title: " << strTemp << std::endl;
str << "Widget below runs in a separate process:" << std::endl;
layout->addWidget( new QLabel( str.str().c_str(), &centralWidget ) );
layout->addWidget(host);
wnd.resize(400, 200);
wnd.show();
res = app.exec();
}
else
{
QMessageBox::critical(NULL, "ERROR", "Unable to find child process widget");
}
// kill child process
process.kill();
return res;
}
1- Compile child process into an executable named test_3rdparty_inprg_qtwinmigrate_child.exe.
2- Compile parent process into an executable
3- Run parent process, this one will start the child process, find it's top level widget window handle and insert it within its own QMainWindow as a child. When done, it will kill the child process.
As a end user, it's hard to tell the widgets are not part of the same process, they are perfectly embedded (the red part of the widget comes from the child process):

well, that's the idea of the whole QWidget approach: Everything that can be put in a container can be part of another application.
However, putting a complete, unmodified Qt application into another one will not be possible: There can only be one QApplication instance, and only one global event loop.

Related

Close main window ffrom messagebox using gtkmm / c++

From a message box i'd like to close the main window if i click on Ok button.
class usb_boot : public Gtk::Window{
public:
usb_boot();
and from message box
i tried this
void usb_boot::creation(){
//Gtk::MessageDialog dialog(*this, dropdownList.get_active_text());
std::string message("Format : " + type);
Gtk::MessageDialog *dialog = new Gtk::MessageDialog("Resume", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
dialog->set_title("Resume");
dialog->set_message(dropdownList.get_active_text());
dialog->set_secondary_text(message);
dialog->set_default_response(Gtk::RESPONSE_YES);
int result = dialog->run();
switch(result){
case(Gtk::RESPONSE_YES):{
std::cout << "next program" << std::endl;
delete dialog;// ok work
usb_boot().close();//compile but doesn't close main window
break;
}
How to close the main window ?
You should avoid using raw new/delete whenever you can (like here, for instance). For message dialogs, you can use simple scopes:
#include <iostream>
#include <gtkmm.h>
class MainWindow : public Gtk::ApplicationWindow
{
public:
MainWindow() = default;
};
int main(int argc, char **argv)
{
auto app = Gtk::Application::create(argc, argv, "so.question.q63872817");
MainWindow w;
w.show_all();
int result;
// Here we put the dialog inside a scope so that it is destroyed
// automatically when the user makes a choice (you could do it
// inside a function instead of a free scope):
{
Gtk::MessageDialog dialog(w, "Message dialog", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
dialog.set_title("Title");
dialog.set_message("Primary message");
dialog.set_secondary_text("Secondary message");
dialog.set_default_response(Gtk::RESPONSE_YES);
result = dialog.run();
} // Here the dialog is destroyed and closed.
if(result == Gtk::RESPONSE_YES)
{
std::cout << "Closing main window..." << std::endl;
//MainWindow().close(); // Will not work!
w.close();
}
return app->run(w);
}
Also, in your code, you call usb_boot().close(), but notice the extra parenthesis after usb_boot. This constructs a new usb_boot object (since you call the constructor) and immediately closes it. In the example above, I called w.close(), instead of MainWindow().close().

How to manage single instance of a Qt app on crash?

I want to execute my application as single instance, currently I am using QSharedMemory and working fine. I am using Qt5.2.1 on Ubuntu 12.04.
Below is my test code:
QApplication a(argc, argv);
a.processEvents();
const char* MEM_KEY = "56";
QSharedMemory sharedMem(MEM_KEY);
if( !sharedMem.create( 512, QSharedMemory::ReadWrite) )
{
QMessageBox msgBox;
msgBox.setText( QObject::tr("Can't start more than one instance of the application.") );
msgBox.setIcon( QMessageBox::Critical );
msgBox.exec();
exit(0);
}
MainWindow w;
w.show();
int p=0;
//p=p/0; // create exception here
return a.exec();
But the if makes the application crash(as shown in above code). If I start the application again, it shows Can't start more than one instance of the application, which means the previous instance is still there even if it has crashed. It should not happen in my case.
How can I restart my application in such a situation?
Edit:
Actually my original project contains lot of source files (the above one just made for testing). I want to implement it on my original project with editing least source file, if possible only by editing main.cpp
You can use a QSetting:
int main(int argc, char *argv[])
{
QSettings settings;
QApplication a(argc, argv);
if(!settings.exitedNormaly()) {
// In case of crash
}
// set the flag to false
settings.setExitedNormaly(false);
MainWindow w(&settings);
w.processArg(argc, argv);
w.show();
int result = a.exec();
settings.setExitedNormaly(result == 0);
return result;
}
Combined with your shared memory, you'll be able to unlock the block in case of application crash.
for linux:
//----------------------------------
QProcess *m_prSystemCall;
m_prSystemCall = new QProcess();
QString Commnd = "pgrep " + qApp->applicationDisplayName();
m_prSystemCall->start(Commnd);
m_prSystemCall->waitForFinished(8000);
QString output(m_prSystemCall->readAllStandardOutput());
QStringList AppList = output.split("\n", QString::SkipEmptyParts);
qDebug() <<"pgrep out:"<<AppList;
for(int i=0;i<AppList.size()-1;i++)
{
Commnd = "kill " + AppList.at(i);
m_prSystemCall->start(Commnd);
m_prSystemCall->waitForFinished(8000);
}
//-------------------------------------------------------
and for Windows:
#include <tlhelp32.h>
#include <comdef.h>
QString pName = qApp->applicationDisplayName();
pName += ".exe";
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == TRUE)
{
DWORD myPID = GetCurrentProcessId();
while (Process32Next(snapshot, &entry) == TRUE)
{
const WCHAR* wc = entry.szExeFile ;
_bstr_t b(wc);
const char* c = b;
if (stricmp(c, pName.toStdString().c_str()) == 0)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
qDebug() <<"myPID: "<< myPID << "entry.th32ProcessID" << entry.th32ProcessID;
if(myPID != entry.th32ProcessID)
TerminateProcess(hProcess,0);
QThread::msleep(10);
CloseHandle(hProcess);
}
}
}
CloseHandle(snapshot);

Trying to render GTK+ window to image

GTK uses cairo for drawing. So I'm trying to create a hello world app that writes to an image (svg, png, ...) instead of X11. I'm facing 2 problems:
- The image is empty
- When starting without X11 running (which is the actual goal) I get the error "** (a.out:9021): WARNING **: Could not open X display"
The code is draft!
#include <string>
#include <iostream>
#include <thread>
#include <chrono>
#include <cmath>
#include <cairo.h>
#include <cairommconfig.h>
#include <cairomm/context.h>
#include <cairomm/surface.h>
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
GtkWidget *window;
GtkWidget *button;
// GtkWidget *main_window = gtk_initialize();
window = gtk_offscreen_window_new();
button = gtk_button_new_with_label ("Hello World");
gtk_container_add (GTK_CONTAINER (window), button);
gtk_widget_show (window);
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
std::cout << "gdk window: " << gdk_window << std::endl;
cairo_surface_t * surfp = gdk_offscreen_window_get_surface(gdk_window);
std::cout << "Created Window will now draw to png" << std::endl;
std::string filename = "image.svg";
double width = 600;
double height = 400;
Cairo::SvgSurface srfobj(surfp);
Cairo::RefPtr<Cairo::SvgSurface> refptr(&srfobj);
Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(refptr);
cr->save(); // save the state of the context
cr->show_page();
std::cout << "Wrote SVG file \"" << filename << "\"" << std::endl;
std::chrono::milliseconds dura( 200 );
std::this_thread::sleep_for(dura);
return 0;
}
Why is this code not working?
Can I run a gtk app without X11 running, or should I just ignore the warning?
Here is my solution based on your example code - keep in mind it is a dirty solution and may not work using newer versions of GTK3. It works to save the UI of a window (only tested with the one button), but still requires (somewhere) a running X-server. It also ignores / don't use your settings for the picture size - you'll have to resize it at your own. I don't know if (and how) it is possible to cut this string (X-Server // X-Framebuffer) too (DirectFB seems not to be really supported anymore), but...
Have fun!
// Default
#include <string>
#include <iostream>
#include <thread>
#include <chrono>
// cairo / cairomm / gtk
#include <cairo.h>
#include <cairomm/context.h> //libcairomm-1.0-dev
#include <gtk/gtk.h>
int main(int argc, char *argv[]) {
// Init
gtk_init(&argc, &argv);
// Create window with a button
GtkWidget *window;
GtkWidget *button;
window = gtk_offscreen_window_new();
button = gtk_button_new_with_label("Hello World");
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
// Make a gdk window out of it, prepare cairo and draw it to it
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
cairo_surface_t* surfp = gdk_offscreen_window_get_surface(gdk_window);
cairo_t* context = cairo_create(surfp);
gtk_widget_draw(GTK_WIDGET(window), context);
// Yay - begin the dump!
Cairo::SvgSurface srfobj(surfp);
std::string filename = "image.png";
srfobj.write_to_png(filename);
std::cout << "Done." << std::endl;
// Aaand a little sleep...
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
return 0;
}
Try to use gtk_widget_draw (widget_ptr, cairo_ctx_ptr); to draw a widget (or a hierarchy of widgets) to a cario context?
The answer to both your questions is that you cannot run GTK+ applications without some sort of output. You're using gtk-x11 which requires an XServer. You might have some luck with the DirectFB backend, but I wouldn't hold your breath as I don't know if it's even maintained anymore.
Because Gtk doesn't run without an XServer the resulting image is empty.

How can I return to main in Qt?

I've been working on a project recently which is a simple game. I've written the following code in my main function (file main.cpp):
ending_note = "Draw.";
End_Page end(ending_note, a);
end.show();
(*a).exec();
if(end.flag == 1)
{
return 1;
} //end if
where a is a Qapplication object. The class End_Page is defined as follows (file end_page.cpp):
End_Page::End_Page(string _winner, QApplication* _a, QWidget *parent):QWidget(parent){
a = _a;
this->setFixedSize(900, 675);
this->move(350, 50);
flag = 0;
//------------------- background label
background = new QLabel(this);
QMovie* movie2 = new QMovie("..\\project\\Data\\pic\\7.jpeg");
movie2->setScaledSize(QSize(this->width(), 600));
background->setMovie(movie2);
background->setGeometry(0, 0, this->width(), 600);
movie2->start();
//-------------------- set label
QString s;
label = new QLabel(s.fromStdString(_winner), this);
label->setStyleSheet("QLabel { color : rgb(200, 0, 30); qproperty-alignment: AlignCenter; }");
QFont f( "MV Boli", 32, QFont::Bold);
label->setFont(f);
label->setGeometry(0,this->height() - 400, this->width(), 160);
question = new QLabel("Do you want to play again?\n", this);
question->setStyleSheet("QLabel { color : black;}");
question->setGeometry(375, 610, 200, 30);
accept = new QPushButton("Yes", this);
accept->setGeometry(300, 630, 80, 40);
decline = new QPushButton("No", this);
decline->setGeometry(500, 630, 80, 40);
//-------------------- connect
connect(this,SIGNAL(closeSignal()), this, SLOT(closeProgram()));
connect(decline, SIGNAL(clicked()), this, SLOT(closeProgram()));
connect(accept, SIGNAL(clicked()), this, SLOT(restartProgram()));
}
End_Page::~End_Page(){}
void End_Page::closeEvent(QCloseEvent* event){
emit closeSignal();
event->accept();
}
void End_Page::EndGame(){
a->exit();
}
void End_Page::closeProgram(){
exit(0);
}
void End_Page::restartProgram(){
flag = 1;
a->exit();
}
My problem is that, after the program executes the statement (*a).exec();, if the user click on the push button labeled Yes, the program executes the function restartProgram to the end, but after that it doesn't continue back in function main (in other words it gets stuck there). How can I solve this problem?
Try calling quit() or exit() as static class members (you don't need to pass your QApplication around):
For any GUI application using Qt, there is precisely one QApplication object, no matter whether the application has 0, 1, 2 or more windows at any given time. For non-GUI Qt applications, use QCoreApplication instead, as it does not depend on the QtGui library.
The QApplication object is accessible through the instance() function that returns a pointer equivalent to the global qApp pointer.
void End_Page::restartProgram(){
flag = 1;
QApplication::quit();
}
But, the main problem in your application is that you are emmiting closeSignal() in your closeEvent() and the slot connected to it will call exit(0); system call, which is, I think, completely unnecessary, and which will "kill" the current process.
Here is a completely working example:
#include <QApplication>
#include <qtimer>
#include <iostream>
/* Move this into h file and moc it! */
class Window:public QObject
{
Q_OBJECT
public slots:
void closeApp(){ QApplication::quit(); flag = 500; }
public:
int flag;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window win;
QTimer::singleShot(5000, &win, SLOT(closeApp()));
a.exec();
std::cout << "Flag: " << win.flag << std::endl;
return 0;
}
Edit
Why are you doing this:
if(end.flag == 1) // flag is set to 1 in restartProgram slot
{
return 1;
} //end if
This will exit your main() function, and it will not restart program.

Launch a QWidget from a QMain window

I'm doing my first C++ - Qt4 application and I'm having some trouble "connecting" my different uis.
I have a main window with several buttons and when I click on one, I want another window to open.
The MyMainWindowClass inherits from QMainWindow and the other from QWidget.
Here is the code I have written so far :
#include <iostream>
#include "MyWidgetClass.hpp"
#include "MyMainWindowClass.hpp"
#include "ui_MyMainWindowClassUi.h"
MyMainWindowClass::MyMainWindowClass(QWidget *parent) :
QMainWindow(parent),
m_ui(new Ui::MyMainWindowClassUi)
{
m_ui->setupUi(this);
initConnect();
}
void MyMainWindowClass::initConnect()
{
QObject::connect(m_ui->SomeBtn,
SIGNAL(clicked()),
this,
SLOT(SomeBtnClicked()));
// Some other QObject::connect calls
return;
}
void MyMainWindowClass::SomeBtnClicked()
{
std::cout << "Some Btn has been clicked" << std::endl;
this->setEnabled(false);
MyWidgetClass mwc(this);
mwc.show();
return;
}
This calls the Ctor and the Dtor from MyWidgetClass, disables the MyMainWindowClassUi, but doesn't show my other GUI. What am I missing to have the window showed when I click on the button ?
Try this instead of your SomeBtnClicked method:
MyWidgetClass *mwc;
void MyMainWindowClass::SomeBtnClicked()
{
std::cout << "Some Btn has been clicked" << std::endl;
this->setEnabled(false);
if (!mwc)
mwc = new MyWidgetClass(this);
mwc->show();
mwc->raise();
mwc->setActiveWindow(); // Qt 4: activateWindow()
return;
}