I'm trying to use QtDbus to communicate with interface provided by PowerManager in my system. My goal is very simple. I will be writing code which causes my system to hibernate using DBus interface.
So, I installed d-feet application to see what interfaces DBus is available on my system, and what I saw:
As we see, I have a few interfaces and methods from which I can choose something. My choice is Hibernate(), from interface org.freedesktop.PowerManagment
In this goal I prepared some extremely simple code to only understand mechanism. I of course used Qt library:
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtDBus/QtDBus>
#include <QDBusInterface>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
if (!QDBusConnection::sessionBus().isConnected()) {
fprintf(stderr, "Cannot connect to the D-Bus session bus.\n"
"To start it, run:\n"
"\teval `dbus-launch --auto-syntax`\n");
return 1;
}
QDBusInterface iface("org.freedesktop.PowerManagement" ,"/" , "" , QDBusConnection::sessionBus());
if(iface.isValid())
{
qDebug() << "Is good";
QDBusReply<QString> reply = iface.call("Methods" , "Hibernate");
if(reply.isValid())
{
qDebug() << "Hibernate by by " << qPrintable(reply.value());
}
qDebug() << "some error " << qPrintable(reply.error().message());
}
return 0;
}
Unfortunately I get error in my terminal:
Is good
some error Method "Methods" with signature "s" on interface "(null)" doesn't exist
So please tell me what's wrong with this code? I am sure that I forgot some arguments in function QDBusInterface::call() but what ?
When creating interface you have to specify correct interface, path, service. So that's why your iface object should be created like this:
QDBusInterface iface("org.freedesktop.PowerManagement", // from list on left
"/org/freedesktop/PowerManagement", // from first line of screenshot
"org.freedesktop.PowerManagement", // from above Methods
QDBusConnection::sessionBus());
Moreover, when calling a method you need to use it's name and arguments (if any):
iface.call("Hibernate");
And Hibernate() doesn't have an output argument, so you have to use QDBusReply<void> and you can't check for .value()
QDBusReply<void> reply = iface.call("Hibernate");
if(reply.isValid())
{
// reply.value() is not valid here
}
Related
I have a code as simple as this:
int main() {
QUrl url("http://google.com");
if (!QDesktopServices::openUrl(url) )
qDebug() << "Failed to open url";
return 0;
}
Running the code gives "Failed to open url". Tried on Ubuntu with Qt 5.5.1 and on MS Windows with Qt 5.7. No difference.
Local files also do not open:
int main() {
QString file = "/home/user/testfile.pdf";
if (!QDesktopServices::openUrl( QUrl::fromLocalFile(file) ) )
qDebug() << "Failed to open file";
return 0;
}
Again, "Failed to open file". On both Ubuntu and Windows. I can see some discussion in stackoverflow about openUrl, but they are concerned with specific features, such as failing to open urls with spaces, etc. But here it just doesn't work at all, on two independent platforms. What do I miss?
QDesktopServices is part of the Qt GUI module. Therefore, in order to use any function related to QDesktopServices, you will need to instantiate at least a QGuiApplication :
Since the QGuiApplication object does so much initialization, it must
be created before any other objects related to the user interface are
created.
In fact, you can create a QApplication (as #Alex Spataru suggested), since it inherits QGuiApplication. To make your code work, you just need this :
int main(int argc, char *argv[]) {
QApplication app(argc, argv); // just this line
QUrl url("http://google.com");
if ( !QDesktopServices::openUrl(url) )
qDebug() << "Failed to open url";
return 0;
}
If I do not use connection I can properly exit.
In Pdv.h file
namespace Pdv {
...
extern QSqlDatabase db;
...
}
In LoginDialog.cpp file
QSqlDatabase Pdv::db;
...
Pdv::db= QSqlDatabase::addDatabase("QMYSQL3");
Pdv::db.setHostName(Pdv::DB_URL);
Pdv::db.setUserName(Pdv::DB_USER);
Pdv::db.setPassword(Pdv::DB_PASS);
Pdv::db.setDatabaseName(Pdv::DB_DB);
if(!Pdv::db.open()) {
...
// Checking user login/password and retrieve many variables
...
In mainwindow.cpp file
...
void MainWindow::closeEvent(QCloseEvent *event) {
...
if(Pdv::db.isOpen()) {
qDebug() << "Opened 1";
Pdv::db.close();
qDebug() << Pdv::db.lastError();
if(Pdv::db.isOpen())
qDebug() << "Opened 2";
}
Pdv::app->quit(); // or QApplication::quit();
}
I got this error in QTCreator console
Opened 1
QSqlError("", "", "")
Le programme s'est terminé subitement.
/home/cosmic/src/build-Pdv-Desktop-Debug/Pdv crashed.
A idea?
To make proper exit with usage of QSqlDatabase, you need preferably:
remove all instances of QSqlDatabase objects (because as you copy them, they will keep connection open).
As second condition, you need to use QSqlDatabe::removeDatabase() call. (also this call will make qDebug message if database is still in use occasionally - some QSqlDatabase object is left somewhere - it will help to identify a problem).
If you close and delete your MainWindow, and your program then crashes, then other parts of the program must be trying to use the MainWindow pointer even though it is destroyed.
I think the problem is the line of code Pdv::app->quit(); Try with QApplication::quit(); instead or review the code in Pdv::app->quit();.
I am using the new connect syntax for Qt5. QNetworkReply has a signal called error and also a function called error. This causes problems when attempting to connect to the signal:
connect(reply, &QNetworkReply::error, this, &MyClass::error);
error C2664: 'QMetaObject::Connection QObject::connect(const QObject *,const char *,const QObject *,const char *,Qt::ConnectionType)' : cannot convert parameter 2 from 'overloaded-function' to 'const char *'
Context does not allow for disambiguation of overloaded function
How do I tell the compiler (MSVC) that I want to connect to the signal rather than the function?
You need manually to cast the function pointer to address the correct function:
connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), this, &MyClass::error);
This is the noted consequence of using new syntax for overloaded functions:
cons:
Very complicated syntax in cases of overloads?
P.S. You can find similar example on the above link:
QObject::connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error), [socket] (QAbstractSocket::SocketError) {
qDebug()<< "ERROR " << socket->errorString();
socket->deleteLater();
});
Start from Qt 5.15 QNetworkReply::error is not a signal any more. You can connect to &QNetworkReply::errorOccurred instead, fortunately, without type casting:
connect(reply, &QNetworkReply::errorOccurred, this,
[reply](QNetworkReply::NetworkError) {
qCDebug() << "Error " << reply->errorString();
});
I am having problems with launching Nokia's own video player from my application that I just don't seem to be able to solve.
My first attempt included calling
Qt.openUrlExternally(url)
from QML and that seemed to do the trick just fine, except that it opened the browser every time and used it instead of the video-suite (native player).
Next I tried cuteTube -approach where I start new process like this:
QStringList args;
args << url;
QProcess *player = new QProcess();
connect(player, SIGNAL(finished(int, QProcess::ExitStatus)), player, SLOT(deleteLater()));
player->start("/usr/bin/video-suite", args);
That worked, except that it required video-suite to be closed upon calling player->start, otherwise it did nothing.
My third attempt involved starting the video-suite via QDBus, but that didn't work any better:
QList<QVariant> args;
QStringList urls;
urls << url;
args.append(urls);
QDBusMessage message = QDBusMessage::createMethodCall(
"com.nokia.VideoSuite",
"/",
"com.nokia.maemo.meegotouch.VideoSuiteInterface",
"play");
message.setArguments(args);
message.setAutoStartService(true);
QDBusConnection bus = QDBusConnection::sessionBus();
if (bus.isConnected()) {
bus.send(message);
} else {
qDebug() << "Error, QDBus is not connected";
}
The problem with this is that it requires video-suite to be up and running - autoStartService parameter didn't help either. If video-suite isn't running already, the call opens it just fine but, alas, no video starts to play.
Eventually I tried using also VideoSuiteInterface, but even having the program compile with it seemed to be difficult. When I eventually managed to compile and link all relevant libraries, the results didn't differ from option 3 above.
So, is there a way to use either VideoSuiteInterface directly or via DBus so that it would start video playback regardless of the current state of the application?
The solution was actually simpler than I really thought initially; the VideoSuiteInterface -approach worked after all. All it took was to use it properly. Here are the full sources should anyone want to try it themselves.
player.h:
#ifndef PLAYER_H
#define PLAYER_H
#include <QObject>
#include <maemo-meegotouch-interfaces/videosuiteinterface.h>
class Player : public QObject {
Q_OBJECT
private:
VideoSuiteInterface* videosuite;
public:
Player(QObject *parent = 0);
Q_INVOKABLE void play(QString url);
};
#endif // PLAYER_H
player.cpp:
#include "player.h"
#include <QObject>
#include <QStringList>
#include <QtDeclarative>
Player::Player(QObject *parent) : QObject(parent) {}
void Player::play(QString url) {
QList<QVariant> args;
QStringList urls;
urls << url;
args.append(urls);
videosuite = new VideoSuiteInterface();
videosuite->play(urls);
}
In addition you may want to connect some signals to make the UI more responsive, but basically that should do the trick.
Finally, you need to remember to add following to your .pro file and you are good to go:
CONFIG += videosuiteinterface-maemo-meegotouch
I have a console-based QCoreApplication which has timers and does socket communication and also uses locked mutex.
When I close the application manually, it gives error saying some mutex is locked and it is timed out. Is there any way I can do clean up in a console application when user closes it?
Cleanup should be handled by destructors and child-parent relationship.
Make your master object (the one in the main) a child of QApplication so it is destructed with all its childs before QApplication is.
Are you sure you killed all your threads? If it is a thread with an eventloop be sure to call QThread::quit() to exit the eventloop before you call QThread::wait()
You can also use the void QApplication::qAddPostRoutine ( QtCleanUpFunction ptr )
to do some special cleanup.
For debugging those messages you can use QtMsgHandler qInstallMsgHandler ( QtMsgHandler h ) and write your own message handler to capture those warnings. If you can simulate the problem you can set a breakpoint on the message and see on the stack where the message is coming from.
void debugMessageHandler( QtMsgType type, const char *msg ){
if(QString(msg).contains( "The message you can see in the console" )){
int breakPointOnThisLine(0);
}
switch ( type ) {
case QtDebugMsg:
fprintf( stderr, "Debug: %s\n", msg );
break;
case QtWarningMsg:
fprintf( stderr, "Warning: %s\n", msg );
break;
case QtFatalMsg:
fprintf( stderr, "Fatal: %s\n", msg );
abort();
}
}
In order to clean up with destructor and child-parent relation ship you can catch the console close signal and call QCoreApplication::exit() to the application instance.
#include <csignal>
#include <QtCore/QCoreApplication>
using namespace std;
struct CleanExit{
CleanExit() {
signal(SIGINT, &CleanExit::exitQt);
signal(SIGTERM, &CleanExit::exitQt);
signal(SIGBREAK, &CleanExit::exitQt) ;
}
static void exitQt(int sig) {
QCoreApplication::exit(0);
}
};
int main(int argc, char *argv[])
{
CleanExit cleanExit;
QCoreApplication a(argc, argv);
return a.exec();
}
Turns out that closing command line application (checked on Win7 & VS2010) by pressing 'close' (red x button on title bar) passes the STATUS_CONTROL_C_EXIT signal to the application. All threads are aborted with this code.
The thread 'Main Thread' (0x980) has exited with code -1073741510
(0xc000013a).
The thread 'QThread' (0x2388) has exited with code
-1073741510 (0xc000013a).
That means that there is no way to intercept this with the QCoreApplication::aboutToQuit() signal.
Take a look at winnt.h or ntstatus.h. That is the value assigned to the
manifest constant STATUS_CONTROL_C_EXIT. The runtime is just
choosing to end your program with the code to note the user's cancel
operation.
you can connect to QCoreApplication::aboutToQuit signal and do the necessary clean up there.