Embeding PySide/PyQt widget into Qt/C++ application - qt

I have a C++/Qt application which should have its GUI extensible with modules. Extending should be simple and versatile. I am just checking a concept - is it possible to have this main C++/Qt application that would execute a Python/PySide/PyQt script which would create a QWidget (or derived class) instance and embed this widget into the main C++/Qt application?
Is there any working snippet to demonstrate the feasibility of this task? I.e. how to create and embed the widget? How to pass signals between the application and the widget?

This question is a bit old, but in case someone else is face the same issue, I will try to give a useful answer.
I think it's possible. In the following example, I create a QApplication and QMainWindow in c++, embed a python interpreter, and then, on the python side, I create a QPushButton which I add to the main window.
Try this out:
#include <QApplication>
#include <QMainWindow>
#include <iostream>
#include "Python.h"
class PythonQt
{
public:
PythonQt()
{
char name[] = "test";
Py_SetProgramName(name);
Py_Initialize();
std::string code =
"from PySide import QtGui\n"
""
"all_widgets = QtGui.QApplication.instance().allWidgets()\n"
"window = None\n"
"for widget in all_widgets:\n"
" if str(widget.objectName()) == \"main_window\":\n"
" window = widget\n"
" break\n"
""
"def message_box():\n"
" QtGui.QMessageBox.information(None, 'Test', 'Hello!', \
QtGui.QMessageBox.Ok)\n"
" QtGui.QApplication.instance().quit()\n"
""
"button = QtGui.QPushButton('Press Me')\n"
"button.clicked.connect(message_box)\n"
"window.setCentralWidget(button)\n"
"window.move(600, 300)\n"
"window.show()";
PyRun_SimpleString(code.c_str());
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow win;
win.setObjectName("main_window");
PythonQt python_code;
a.exec();
}
The python script is written here in a string to have everything in a single file, but you could of course read it in from a .py file.
Object ownership could be an issue though, as shown in the link given by Trilarion.

I don't think this is possible. PySide/PyQt are wrappers around C++/Qt. That means you create C++ objects and Python wrapper objects and somehow the wrapper objects kind of refer to the C++ objects. This works one way as far as I know.
But you want the other way. Basically a wrapper around Python objects (which themselves are wrappers around C++ objects) to use in C++. I don't think PySide/PyQt are ready for thise. However it is possible to embed Python in other languages.
Also see How to shoot yourself in the foot. about the pitfalls of communication between C++/Qt and Python.

Related

Using QSettings in a global static class

My task is to create a QSettings wrapper class (wrapping is mostly needed by QML) which I can reach everywhere from the model, too.
The obvious choice is a static global instance of this class. However the problem is with this approach is that it's destroyed after main, after QApplication is destroyed, thus giving me the following warning:
QApplication::qAppName: Please instantiate the QApplication object first
Here is a simplified, sample code showing a really simple wrapper, and the destruction phases:
#include <QCoreApplication>
#include <QDebug>
#include <QGlobalStatic>
#include <QSettings>
#include <QTimer>
class Settings: public QObject
{
public:
~Settings() { qDebug() << "~Settings"; }
QSettings settings;
};
Q_GLOBAL_STATIC(Settings, globalSettings)
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QObject::connect(&app, &QCoreApplication::aboutToQuit, [](){qDebug() << "~QCoreApplication aboutToQuit";});
QObject::connect(&app, &QCoreApplication::aboutToQuit, [](){globalSettings->settings.setValue("Hi", 2);});
QObject::connect(&app, &QCoreApplication::destroyed, [](){qDebug() << "~QCoreApplication destroyed";});
QTimer::singleShot(0, &app, SLOT(quit()));
return app.exec();
}
It prints out the following:
~QCoreApplication aboutToQuit
~QCoreApplication destroyed
~Settings
QApplication::qAppName: Please instantiate the QApplication object first
My question is: providing, in my program QSettings is used after QCoreApplication::aboutToQuit is emitted but before QCoreApplication::destroyed, how can I avoid this warning?
Using QSettings
I've used QSettings in pretty much every project I've ever made. Here is the pattern that I tend to use it:
in main.cpp
#include <QSettings>
//...
// Then in main(), after QApplication is instantiated, but before any settings are accessed
QSettings::setDefaultFormat(QSettings::IniFormat);
QApplication::setOrganizationName("MyOrg");
QApplication::setApplicationName("MyApp"):
Then anytime you are about to access QSettings, you just do this:
QSettings s;
s.value(// reading a value
s.setValue(// writing a value
Everything gets saved in the User Scope in an INI text file. It will be located in Windows under C:/Users/<username>/AppData/Roaming/MyOrg/MyApp.ini.
This usage of QSettings (IMHO) is very clean, doesn't require global variables or static references and is very fast and efficient. And it is very readable.
Now to be able to have things load settings at the right times, and not get the errors you mentioned in your question, see the initial example in the links below:
http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application
http://doc.qt.io/qt-5/qtwidgets-mainwindows-application-example.html
It works much better in the timeline of a Qt application and works great. I tend to make a readSettings and writeSettings for most of my GUI classes and for a number of my backend classes. readSettings happens when the widget has its showEvent or constructor happen and the writeSettings happens in the closeEvent. Also if I have a dedicated settings dialog, I emit a signal to have any affected classes to readSettings, right after the settings dialog writes those specific settings.
If you use the QML port of QSettings, it also uses the Organization name and Application name and the default format of QSettings to pick its filename and location.
http://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html
I believe the default functionality of that QML component is just to read the setting when the component is created, and to write the setting whenever QML changes it. So to change it from C++ and have it recognized by QML, you should probably use the standard QML/C++ methods out there such as:
http://doc.qt.io/qt-5/qtqml-cppintegration-topic.html
And lastly if I am planning on installing defaults for a program that are decided for a build and I don't want to hardcode them, I hand write an INI file and have the installer place it in the system scope location: C:/ProgramData/MyOrg/MyApp.ini
And in the case that the settings of your program are more complex than what you want to store in an INI file, I would look into using QJson, and the SaveGame example.
Hope that helps.

How to use QGraphicsScene with QtGui/QGuiApplication?

I created a new Qt 5 project with QtCreator, and added the following lines to it.
#include <QGraphicsScene>
// in main():
QGraphicsScene scene;
In the .pro file I added:
Qt += gui
The object creation of QGraphicsScene is resulting in the segmentation fault.
What point am I missing?
EDIT:
I realized now that instead of QApplication my rest of the program is using QtGui/QGuiApplication.
How to use QGraphicsScene with QtGui/QGuiApplication?
You're correct: with QApplication it works for me in both Qt 4.8 and 5.2, but not with QGuiApplication:
#include <QApplication>
#include <QGraphicsScene>
#include <QTimer>
int main(int argc, char ** argv) {
QApplication app(argc, argv);
// With QGuiApplication, QGraphicsScene constructor crashes.
QGraphicsScene scene;
QTimer::singleShot(1000, &app, SLOT(quit()));
return app.exec();
}
You're incorrect in stating that the code will compile with just the gui module added to the project. Without the widgets module, your code won't compile, and this should have been the first hint that you're trying to do something that's not supported.
QGraphicsScene is not part of the gui module, but of the widgets module! All classes in the widgets module are free to assume (and they do!) that you've instantiated QApplication, not QGuiApplication! I've submitted it as QTBUG-36413, but - unsurprisingly - it got closed as invalid. It simply is not supposed to work.
If you want to use the QGuiApplication, there's a workaround:
The QApplication (but not QGuiApplication) is keeping a list of all graphics scenes. QGraphicsScene assumes that the type of qApp is QApplication. The list is accessed in QGraphicsScenePrivate::init(). The fix is to guard the reference to scene_list with a type check on qApp:
if (qobject_cast<QApplication*>(qApp))
You need this in both QGraphicsScenePrivate::init() and QGraphicsScene::~QGraphicsScene(). I've confirmed that after you recompile Qt, it doesn't crash anymore.
If you're serious about it, you'd have to copy relevant files from Qt's sources to your project, remove the widgets module from the project file, and patch things up until they work. Personally I think it's a waste of effort. It's trivial to render a QGraphicsView into an arbitrary paint device, without having to actually display it on the screen.

How to localize the QFileDialog called from QPrintDialog?

The following works fine for localizing the QPrintDialog:
#include <QtGui/QApplication>
#include <QTranslator>
#include <QPrintDialog>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTranslator translator;
if (translator.load("./translations/qt_de.qm")) {
a.installTranslator(&translator);
}
QPrintDialog printdialog;
printdialog.exec();
}
But when I click on the "Choose file"-Button (with the three dots) in the QPrintDialog, the english version of the file dialog comes up instead of the german one I'd like to have.
Also, there is a warning at the console:
KGlobal::locale::Warning your global KLocale is being recreated with a valid main component instead of a fake component, this usually means you tried to call i18n related functions before your main component was created. You should not do that since it most likely will not work
Where should I look?
As for the workaround mentioned here: Some QDialogs support a ::DontUseNativeDialog flag, but the QPrintDialog doesn't.
(tested on linux, don't know how the outcome is on other platforms)
try to add after translator.load this line:
a::installTranslator(&translator);//or something like that(*)
and read docs about QApplication::installTranslator methods...
(*) sorry I checked and it's a QCoreApplication's method. I used it into a QMainWindow subclass by qApp macro, i don't know how to call that from main.cpp. Please do some test.
I found myself having the same problem and I worked around it by adding to my main window:
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
You can find the enum with the attributes here.

Multithreading in Qt - doubts and problems

nearly searching for hours I became more confused about the Multithreading concept and I need help understanding and implementing it. After searching I have come to the following implementation
main.cpp
#include <QtGui/QApplication>
#include "mainwindow.h"
#include <mythread1.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
mythread abc;
abc.start();
abc.wait();
return a.exec();
}
In above code mythread.h is my header file which I created for multithreading and below is my code of mythread1.h
mythread1.h
#ifndef MYTHREAD1_H
#define MYTHREAD1_H
#include <QtGui>
#include <windows.h>
class mythread : public QThread
{
public:
void run();
};
void mythread::run()
{
}
#endif // MYTHREAD_H
Now my questions are
I have defined only one function run() and when the thread is initialized how compiler determines it has to execute run() function and what it does when it has multiple functions, I got this question because in main.cpp I just typed abc.start(); and didn't specify what to execute?
Inside mythread1.h and in run function I made a infinite loop using while(0<1) and ran the program and to my surprise I was just presented with white screen with 100% CPU usage(not a surprise), but it should run concurrently without interfering with main thread right? then why this is happening?
I then used QFile::copy("pathtomytargetfile","targetpath"); inside the run function inside in mythread1.cpp but that didn't work :O and it didn't copy that file but when i connected it with Push button in main thread then it got successfully copied, why this happened?
Does anyone know a simpler way to implement multithreading?
Thank you
First of all: run method should be protected!
Secondly, what do you mean by "I have defined only one function run()". You defined method, not function. abc.start() means that abc instance of mythread will start and use it's entry point method void run(). You can't declare more than one method with same signature. And if you wonder how it calls your run() instead of QThread::run() then you need to read something about virtual methods in C++
Ad. 3: How can we tell why your file didn't copy? Check what errors QFile provides you. Change your code something like that:
QFile f("pathtomytargetfile");
if( ! f.copy("targetpath") )
{
qDebug() << f.errorString();
}
It will provide you some usefull info
My answer might confuse you even more, but the following links are worth reading in my opinion:
This is an article by the engineer who introduced the QThread class. He apologizes for suggesting that inheriting from QThread is the way to go.
This article shows you, how you should do it (strictly speaking -- inheriting from QThread will work as well, it just is not as nice design-wise).
On this page you can find an overview of multithreading techniques that Qt offers and some help deciding which one you should use for your specific problem.
HTH

Getting font metrics without GUI (console mode)

Let's say some images have to be generated by a Qt console program and that font metrics are needed by internal algorithms (they use the text width/height as input to compute the position where the drawing should occur). This program has to be runable on a Linux without any GUI (runlevel-3, basically a cluster without any display server).
Problem: QFontMetrics are only available when running a Qt application in GUI mode.
Any workaround to get string metrics without any display server ?
Ok after additional comments I think I understand your problem.
Just do it like that:
include <QApplication>
int main(int argv, char **args)
{
QApplication app(argv, args);
QApplication::processEvents(); // this should allow `QApplication` to complete its initialization
// do here whatever you need
return 0; // or some other value to report errors
}
You can also try use QGuiApplication this version doesn't require (doesn't use) widgets.
See also example in documentation how to handle none gui cases.
This code works perfectly on my Ubnutu with Qt 5.3
#include <QGuiApplication>
#include <QFontMetrics>
#include <QDebug>
int main(int argc, char *argv[])
{
QGuiApplication a(argc, argv);
QFont font("Times", 10, QFont::Bold);
qDebug() << font;
QFontMetrics metrics(font);
qDebug() << metrics.boundingRect("test");
return 0;
}
It also works with Qt 4.8 when QApplication is used.
Project file was quite simple
QT += core
TARGET = MetricsNoGui
TEMPLATE = app
SOURCES += main.cpp
Qt 4.8 has such QApplication constructor, whose 3rd parameter can help to solve the issue. Simple provide false as 3rd argument and enjoy using QFontMetrics in Qt console application. There will be no crashes if one starts app on systems without X server.
I didn't find a way to use QFont or QPrinter with QCoreApplication :( However, if you are able to install Xvfb, then your QApplication will be runnable also on a server without any display. I'm using this setup on a headless Raspberry Pi Zero.
Xvfb :1 -screen 0 1024x768x16 &
export DISPLAY=:1.0
./YourQApplication
This isn't the most elegant solution, but after hours of desperate searching, it's the only one I found.

Resources