In the Qt State Machine Framework documentation there is an example how to set properties on state activation:
s1->assignProperty(label, "text", "In state s1");
s2->assignProperty(label, "text", "In state s2");
s3->assignProperty(label, "text", "In state s3");
Is there a way to connect slots on state activation? like s1_buttonclick will only be connected when s1 is active and s2_buttonclick will only be connected when s2 is active?
You want the connections to be different based on which state the state machine is currently in?
I think you will have to manage this yourself using other slots and the entered() and exited() signals. Just create a slot for the each entrance and exit of the states.
QObject::connect(s1, SIGNAL(entered()), connectionManager, SLOT(connectS1()));
QObject::connect(s1, SIGNAL(exited()), connectionManager, SLOT(disconnectS1()));
//continue for each state
Filtering signal-slot connections can be done using a helper class that represents a connection and provides an active property of the connection. Note that Qt 5's QMetaObject::Connection is not sufficient for this.
#include <QMetaMethod>
#include <QPointer>
class Connection : public QObject
{
Q_OBJECT
Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged USER true)
Q_PROPERTY(bool valid READ isValid)
QMetaMethod m_signal, m_slot;
QPointer<QObject> m_source, m_target;
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
QMetaObject::Connection m_connection;
#else
bool m_connection;
#endif
bool m_active;
void release() {
if (!m_source || !m_target) return;
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
disconnect(m_connection);
#else
disconnect(m_source, m_signal, m_target, m_slot);
#endif
}
public:
Connection(QObject * source, const char * signal, QObject * target, const char * slot, QObject * parent = 0) :
QObject(parent),
m_signal(source->metaObject()->method(source->metaObject()->indexOfSignal(signal))),
m_slot(target->metaObject()->method(target->metaObject()->indexOfSlot(slot))),
m_source(source), m_target(target),
m_connection(connect(m_source, m_signal, m_target, m_slot)),
m_active(m_connection)
{}
~Connection() { release(); }
QObject* source() const { return m_source; }
QObject* target() const { return m_target; }
QMetaMethod signal() const { return m_signal; }
QMetaMethod slot() const { return m_slot; }
bool isActive() const { return m_active && m_source && m_target; }
bool isValid() const { return m_connection && m_source && m_target; }
Q_SIGNAL void activeChanged(bool);
Q_SLOT void setActive(bool active) {
if (active == m_active || !m_source || !m_target) return;
m_active = active;
if (m_active) {
m_connection = connect(m_source, m_signal, m_target, m_slot);
} else {
release();
}
emit activeChanged(m_active);
}
};
Related
I'm working at a socket class for my application that will introduce me in QT framework. When I try to build I get this error: 'this' is unavailable for static member functions.
This is my class .h and .cpp
#pragma once
#include <QObject>
class QTcpSocket;
namespace Ps{
class InstSocket : public QObject
{
Q_OBJECT
public:
InstSocket(QObject *parent=0);
bool Connect();
bool isOpen();
void Disconnect();
//Geters
QString GetHostName() const {return m_hostName;}
quint16 GetPort() const {return m_port;}
//seters
void SetHostName(const QString& value);
void SetPort(quint16 value);
void SetLongWaitMs(int value){m_longWaitMs = value;}
void SetShortWaitMs(int value){m_shortWaitMs = value;}
void WriteData(const QString &data) const;
~InstSocket();
QString ReadData() const;
signals:
static void NotifyConnected();
static void NotifyDisconnected();
private slots:
void onConnected();
void onDisconnected();
private:
//this holds a reference to QtcpSocket
QTcpSocket& m_socket;
QString m_hostName;
quint16 m_port;
int m_shortWaitMs;
int m_longWaitMs;
explicit InstSocket(const InstSocket& rhs) = delete;
InstSocket& operator= (const InstSocket& rhs) = delete;
};
}
and the cpp:
#include "instsocket.h"
#include "QTcpSocket"
#include "QDebug"
#include "utils.h"
namespace Ps
{
InstSocket::InstSocket(QObject *parent) :
QObject(parent),
m_socket(*new QTcpSocket(this)),
m_hostName(""),
m_port(0),
m_shortWaitMs(0),
m_longWaitMs(0)
{
/* my signals are wired to the undelying socket signals, the signal connected is triggered, when a conection
* is established. This will be wired to onConnected and Disconnected slots*/
connect(&m_socket, &QTcpSocket::connected, this, &InstSocket::onConnected);
connect(&m_socket, &QTcpSocket::disconnected, this, &InstSocket::onDisconnected);
}
bool InstSocket::Connect()
{
qDebug() << "attempting to connect to "<< m_hostName << "on port" << m_port << "with wait time: "<<m_longWaitMs;
m_socket.connectToHost(m_hostName, m_port, QTcpSocket::ReadWrite);
return m_socket.waitForConnected(m_longWaitMs);
}
bool InstSocket::isOpen()
{
return m_socket.isOpen();
}
void InstSocket::Disconnect()
{
if(!isOpen()) return;
m_socket.disconnectFromHost();
}
void InstSocket::onConnected()
{
emit NotifyConnected();
}
void InstSocket::onDisconnected()
{
emit NotifyDisconnected();
}
void InstSocket::SetHostName(const QString &value)
{
m_hostName = value;
}
void InstSocket::SetPort(quint16 value)
{
m_port = value;
}
void InstSocket::WriteData(const QString &data) const
{
/*support for writeing to socket. The write metod of the socket will return the number of bites writen*/
int bytes_written = m_socket.write(qPrintable(data));
qDebug() << "Bytes written: "<<bytes_written;
}
QString InstSocket::ReadData() const
{
if(!m_socket.isReadable())
{
return "ERROR: Socket is unreadable.";
}
QString result;
//until the socket reports there is no data available
while(!m_socket.atEnd())
{
result.append(m_socket.readAll());
/*since typically a PC would be much faster at reading than an instrument might be at writing
* instrument must have a chance to queue up more data in case the message it's sending us is long.*/
m_socket.waitForReadyRead(m_shortWaitMs);
}
return result;
}
InstSocket::~InstSocket()
{
Utils::DestructorMsg(this);
}
}
and this is the error:
Qt Projects\build-Vfp-Desktop_Qt_5_7_0_MSVC2015_64bit-Debug\debug\moc_instsocket.cpp:-1: In static member function 'static void Ps::InstSocket::NotifyConnected()':
error: 'this' is unavailable for static member functions
QMetaObject::activate(this, &staticMetaObject, 0, Q_NULLPTR); In static member function 'static void Ps::InstSocket::NotifyDisconnected()':
error: 'this' is unavailable for static member functions
QMetaObject::activate(this, &staticMetaObject, 1, Q_NULLPTR);
When I clicked on them, QT creator took me to moc_instsocket.cpp (that is in build folder and poit to this:
// SIGNAL 0
void Ps::InstSocket::NotifyConnected()
{
QMetaObject::activate(this, &staticMetaObject, 0, Q_NULLPTR);
}
// SIGNAL 1
void Ps::InstSocket::NotifyDisconnected()
{
QMetaObject::activate(this, &staticMetaObject, 1, Q_NULLPTR);
}
I can't figure out what to do althought I checked all the code several times. There is no need to know about utils class since there are just some debug messages. Did anyone know how to fix it?
What is the meaning of static signal? In Qt signals and slots are used on object level.
signals:
static void NotifyConnected();
static void NotifyDisconnected();
All classes that inherit from QObject or one of its subclasses (e.g., QWidget) can contain signals and slots. Signals are emitted by objects when they change their state in a way that may be interesting to other objects. This is all the object does to communicate. It does not know or care whether anything is receiving the signals it emits. This is true information encapsulation, and ensures that the object can be used as a software component. Signal Slots documentation
I'm developing a multithreaded application, I need to instantiate n devices by modbus.
So I created a controller (ServiceSM) that instantiates N threads (ServiceSlots).
The devices are varied so I had to create "drivers" for each type of device, one of the drivers uses the QModbusClient class, so I created a controller to manage the device type.
schema
To test the operation of the state machine and connection to the device, I made an example code to run in a graphical interface.
I deleted some snippets of code that do not matter to make it easier to understand
In the MD4040driver class
When my code runs this section, the following messages appear.
If I instantiate the DeviceDriver class in the graphical interface, it works perfectly, the problem occurs when I instantiate it inside a thread.
when calls
modbusDevice->connectDevice()
MD4040drive::sm_conn() - try connect - this my message
Error:
QObject::connect: Cannot queue arguments of type
'QModbusDevice::State' (Make sure 'QModbusDevice::State' is registered
using qRegisterMetaType().)
QObject: Cannot create children for a parent that is in a different
thread. (Parent is QTcpSocket(0x24a6ce8), parent's thread is
ServiceSlots(0xea66488), current thread is QThread(0x2418a78)
QObject: Cannot create children for a parent that is in a different
thread. (Parent is QTcpSocket(0x24a6ce8), parent's thread is
ServiceSlots(0xea66488), current thread is QThread(0x2418a78)
void MD4040drive::sm_conn()
{
if (modbusDevice->state() != QModbusDevice::ConnectedState) {
modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, this->cfg.modbus.porta );
modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, this->cfg.modbus.ip);
modbusDevice->setTimeout( this->cfg.modbus.timeout );
modbusDevice->setNumberOfRetries(this->cfg.modbus.retries);
qDebug() << "MD4040drive::sm_conn() - try connect";
if (!modbusDevice->connectDevice()) {
qDebug() << "Erro: " << modbusDevice->errorString();
} else {
qDebug() << "Aguardando conexão...";
}
}
else{
//already connected...
this->getDados_NO_TH();
}
}
rest my code(parts)
devicedriverviewgui.h
devicedriverviewgui.cpp
class DeviceDriverViewGUI : public QDialog
{
Q_OBJECT
public:
explicit DeviceDriverViewGUI(QWidget *parent = 0);
~DeviceDriverViewGUI();
private slots:
void on_pbTry_clicked();
private:
Ui::DeviceDriverViewGUI *ui;
ServiceSlots *serviceSlot;
};
void DeviceDriverViewGUI::on_pbTry_clicked()
{
Equip equip_try = Equip();
serviceSlot = new ServiceSlots();
serviceSlot->setEquipamento(equip_try);
serviceSlot->start();
}
serviceslots.h
serviceslots.cpp
class ServiceSlots : public QThread
{
Q_OBJECT
public:
ServiceSlots();
void run();
private:
QTimer *timer;
DeviceDriver *device;
private slots:
void sm_getData();
void device_response(bool boEnd);
};
void ServiceSlots::run()
{
int e;
eventLoop = new QEventLoop();
timer = new QTimer();
connect(timer, SIGNAL(timeout()),this, SLOT(sm_controler()));
timer->start(TICK_SM_SLOT);
this->device = new DeviceDriver();
e = eventLoop->exec();
qDebug() << "Exit loop"<< e;
}
void ServiceSlots::sm_controler()
{
if(this->idleState){;}
else{
this->sm_getData();
this->idleState = true;
}
}
void ServiceSlots::sm_getData()
{
connect(device,SIGNAL(end(bool)),this,SLOT(device_response(bool)));
device->requestDeviceDriver(&this->equipamento,&this->next_date);
}
devicedriver.h
devicedriver.cpp
class DeviceDriver : public QObject
{
Q_OBJECT
public:
DeviceDriver();
void requestDeviceDriver(Equip *equip,QDateTime *date);
private:
//Drivers de dispositivos..
MD4040drive *md4040;
private slots:
//Request data to driver...
void request();
signals:
void end(bool boEnd);
};
void DeviceDriver::request()
{
connect(md4040,SIGNAL(end(bool)),this,SLOT(md4040_end(bool)));
this->md4040->requestMD4040drive(&this->equip,&this->date);
}
DeviceDriver::DeviceDriver(){
----
md4040 = new MD4040drive();
---
}
void DeviceDriver::requestDeviceDriver(Equip *equip, QDateTime *date){
this->equip = *equip;
this->date = *date;
this->request();
}
md4040drive.h
md4040drive.cpp
class MD4040drive : public QObject // QObject//public QObject QRunnable QThread
{
Q_OBJECT
public:
explicit MD4040drive(QObject *parent = 0);
~MD4040drive();
void requestMD4040drive(Equip *equip,QDateTime *date);
private:
void run();
QModbusClient *modbusDevice;
private slots:
void m_conn();
signals:
void end(bool boRun);
};
MD4040drive::MD4040drive(QObject *parent): QObject(parent)
{
modbusDevice = new QModbusTcpClient();
connect(modbusDevice, &QModbusClient::stateChanged,this, &MD4040drive::onStateChanged);
}
void MD4040drive::requestMD4040drive(Equip *equip, QDateTime *date)
{
this->equip = *equip;
this->date = *date;
this->run();
}
void MD4040drive::run()
{
this->sm_conn();
}
void MD4040drive::sm_conn()
{
if (modbusDevice->state() != QModbusDevice::ConnectedState) {
modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, this->cfg.modbus.porta );
modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, this->cfg.modbus.ip);
modbusDevice->setTimeout( this->cfg.modbus.timeout );
modbusDevice->setNumberOfRetries(this->cfg.modbus.retries);
qDebug() << "MD4040drive::sm_conn() - try connect";
if (!modbusDevice->connectDevice()) {
qDebug() << "Erro: " << modbusDevice->errorString();
} else {
qDebug() << "Aguardando conexão...";
}
}
else{
//already connected...
this->getDados_NO_TH();
}
}
There are a few problems:
You need to call qRegisterMetaType<QModbusDevice::State>() in main().
You need to maintain parent-child relationships between all objects that are potentially moved to other threads as a group.
The ServiceSlots and DeviceDriver classes seem to be unnecessary.
The ubiquitous this-> is non-idiomatic C++. Don't write this-> unless you need to disambiguate a member from a local variable.
Prefer to hold objects by value if they have the same lifetime as the parent object. Let the compiler generate memory management code for you!
Leverage C++11.
Fist of all, let's have a helper SafeThread class that provides us with a thread that is safely destructible at any time:
class SafeThread : public QThread {
Q_OBJECT
using QThread::run;
public:
using QThread::QThread;
~SafeThread() { quit(); wait(); }
};
The DeviceDriverViewGUI class can hold the drive and its thread by value:
class DeviceDriverViewGUI : public QDialog
{
Q_OBJECT
public:
explicit DeviceDriverViewGUI(QWidget *parent = nullptr);
private:
Ui::DeviceDriverViewGUI ui;
MD4040drive drive;
SafeThread driveThread{this};
Equip equipamento;
QDateTime nextDate;
Q_SLOT void on_pbTry_clicked();
};
Then, the pushbutton can be connected directly to the drive's thread context, and run the requestMD4040drive in the proper thread:
DeviceDriverViewGUI::DeviceDriverViewGUI(QWidget *parent) : QDialog(parent)
{
ui.setupUi(this);
// vvvvvv -- gives the thread context
connect(ui.pbTry, &QPushButton::clicked, &drive, [this]{
Q_ASSERT(QThread::currentThread() == drive.thread()); // ensured by the thread context
drive.requestMD4040drive(&equipamento, nextDate);
});
connect(&drive, &MD4040drive::end, this, [this](bool end){
//...
});
drive.moveToThread(&driveThread);
driveThread.start();
}
When done this way, you don't need any extraneous helper objects nor timers to queue requests. Qt handles all of it.
When passing Qt value classes to functions, pass them by const reference, not by pointer. The MD4040drive should look roughly as follows:
class MD4040drive : public QObject
{
Q_OBJECT
public:
explicit MD4040drive(QObject *parent = nullptr);
void requestMD4040drive(Equip *equip, const QDateTime &date);
Q_SIGNAL void end(bool boRun);
private:
Equip *equip = nullptr;
QDateTime date;
QModbusTcpClient modbusDevice{this};
Cfg cfg;
Q_SLOT void onStateChanged();
Q_SLOT void m_conn();
void sm_conn();
void getDados_NO_TH() {}
};
The implementation:
MD4040drive::MD4040drive(QObject *parent): QObject(parent)
{
connect(&modbusDevice, &QModbusClient::stateChanged,this, &MD4040drive::onStateChanged);
}
void MD4040drive::requestMD4040drive(Equip *equip, const QDateTime &date)
{
this->equip = equip;
this->date = date;
sm_conn();
}
void MD4040drive::sm_conn()
{
if (modbusDevice.state() != QModbusDevice::ConnectedState) {
modbusDevice.setConnectionParameter(QModbusDevice::NetworkPortParameter, cfg.modbus.porta );
modbusDevice.setConnectionParameter(QModbusDevice::NetworkAddressParameter, cfg.modbus.ip);
modbusDevice.setTimeout( this->cfg.modbus.timeout );
modbusDevice.setNumberOfRetries(this->cfg.modbus.retries);
qDebug() << "MD4040drive::sm_conn() - try connect";
if (!modbusDevice.connectDevice()) {
qDebug() << "Erro: " << modbusDevice.errorString();
} else {
qDebug() << "Aguardando conexão...";
}
}
else{
//already connected...
getDados_NO_TH();
}
}
The configuration class might look as follows - notice that the compiler will generate the necessary constructors and destructors for you:
struct Cfg {
struct Modbus {
int porta = 0;
QString ip = QStringLiteral("127.0.0.1");
int timeout = 1000;
int retries = 2;
} modbus;
};
Make sure 'QModbusDevice::State' is registered using qRegisterMetaType()
Means you need to call qRegisterMetaType<QModbusDevice::State>(); before connecting signal/slot that would pass this type of parameter between threads.
And/or add Q_DECLARE_METATYPE(QModbusDevice::State) macro at global scope (never understood clearly which one is actually needed, putting both works for sure...).
See this post for more details: Emitting signals with custom types does not work
I'm trying to persist some application specific info in a settings file. Info like form size and position, the state of some check boxes on the main form etc. I trying to extend the QSettings class to do this.
This is what I have so far:
appsettings.h
class AppSettings : public QSettings
{
Q_OBJECT
public:
explicit AppSettings(QObject *parent = 0);
void load();
void save();
bool getLinkState();
bool getFullState();
void setLinkState(bool state);
void setFullState(bool state);
signals:
public slots:
private:
int mainTop;
int mainLeft;
int mainWidth;
int mainHeight;
bool cbFullState;
bool cbLinkState;
}
appsettings.cpp
#include "appsettings.h"
#include <QDebug>>
AppSettings::AppSettings(QObject *parent) :
QSettings(parent)
{
load();
}
void load(){
qDebug() << "appSettings load()"" ;
}
void save() {
qDebug() << "appSettings save()"" ;
}
bool getLinkState() {
return cbLinkState;
}
bool getFullState() {
return cbFullState;
}
void setLinkState(bool state)
{
if (state == cbLinkState) return;
cbLinkState = state;
save();
}
void setFullState(bool state)
{
if (state == cbFullState) return;
cbFullState = state;
save();
}
mainwindow.cpp
AppSettings *appSettings = AppSettings("MyOrgName", "MyAppName);
cbLink->setChecked( appSettings->getLinkState() );
I'm unsure how to:
1) create the object in the mainwindow such that the organsiation name and application name get passed through to QSettings that I've inherited from. I get a "no matching function" compile error on the first line in the mainwindow code above.
2) if I use the AppSettings class to store (cache) the info, how to create it only once and make it available from other windows and classes. For example, I may want to store the location of other windows as well as the main window.
I am trying to create an app that holds a list of tasks and for each time a deadline, now i want to execute a function (show a popup) once a deadline is met.
i have this:
#ifndef TIMER_H
#define TIMER_H
#include <QWidget>
#include <QTimer>
#include <QtGui>
#include <QObject>
class Timer : public QWidget
{
Q_OBJECT
public:
Timer(QWidget * parent = 0);
void setTimer(QString title, QString description, QDate date, QTime reminderTime);
public slots:
void showWarning() {QString show = tit;
QPushButton * thanks = new QPushButton(QObject::tr("Thank you for reminding me!"));
show.append("\n");
show.append(des);
QMessageBox popup;
popup.setText(show);
popup.setWindowTitle("Calendar : Reminder");
popup.setDefaultButton(thanks);
popup.exec();
}
private:
QString tit;
QString des;
QDateTime now;
QDateTime timeoftheaction;
QTimer *timer;
};
cpp file:
#endif // TIMER_H
#include "timer.h"
#include <iostream>
using namespace std;
Timer::Timer(QWidget * parent)
: QWidget(parent)
{
}
void Timer::setTimer(QString title, QString description, QDate date, QTime reminderTime)
{
now.currentDateTime();
timer = new QTimer;
tit = title;
des = description;
timeoftheaction.setDate(date);
timeoftheaction.setTime(reminderTime);
connect(timer, SIGNAL(timeout()),this,SLOT(showWarning()));
timer->start(now.secsTo(timeoftheaction)*1000);
}
Yet function showWarning is never being called...
no compilation errors, function showWarning works perfectly (tested)
I think the error is in the connect but i am not sure...
Short answer:
Change:
now.currentDateTime();
to
now = QDateTime::currentDateTime();
Longish answer:
currentDateTime() is a static function which instead of changing your existing object, actually returns a new QDataTime object. Although you are calling it as a member function, it's still called as a static one and leaves your object intact, which is still invalid.
Your later call to secsTo() on an invalid data time probably gets you an negative or really large number that either has passed (never going to trigger) or really late in the future.
Here is something that might be a more generic solution.
#include <QThread>
#include <QTimer>
#include <QObject>
#include <map>
/**
* Singleton to implement simple 'relative' timer.
* Implements busy wait and also timeout-notifications (useful to monitor operations that could hang, etc).
*
* If the whole application is stalled (e.g. when a new device is connected), and we only want to
* wait for a period during which application was 'really' working (not just hanging waiting for OS)
* - then ticks will be missed too. This way - it's should be possible to avoid unnecessary timeouts
* that could happen if global time was measured (especially annoying on WINdows platforms)
*/
class RelativeTimer : public QObject
{
Q_OBJECT
typedef std::multimap <unsigned int, std::pair <QObject*, QString> > Notifications;
public:
/**
* Call to busy-wait for number of ticks.
*/
static void wait_num_of_ticks(unsigned int num_of_ticks_to_wait)
{
if(self.timer_id == 0)
{
qDebug("timer not initialised, call 'RelativeTimer::Init()'");
return;
}
if(num_of_ticks_to_wait > 0)
{
unsigned long until = self.tick_counter + num_of_ticks_to_wait; // it's ok if it wraps around..
while(self.tick_counter != until)
{
QCoreApplication::processEvents(); // let others to their job..
// or comment above out and just busy wait..
}
}
}
/**
* Call to busy-wait until ms_to_wait have elapsed.
* If ms_to_wait is < tick period
* Interval will define 'tick' frequency (and accuracy).
*/
static void wait_ms(unsigned int ms_to_wait)
{
wait_num_of_ticks(num_of_ticks_to_wait(ms_to_wait));
}
/**
* Call to schedule a notification after a given timeout.
* returns notification_id that can be used to cancel this notification.
*/
static unsigned long notify_timeout_ms(unsigned int ms_to_wait,
QObject *receiver,
const char* method_name)
{
unsigned long ticks_to_wait = 0;
if(receiver && method_name)
{
ticks_to_wait = num_of_ticks_to_wait(ms_to_wait);
if(ticks_to_wait > 1)
{
ticks_to_wait += self.tick_counter;
if(ticks_to_wait == 0) // avoid 0 - make it one tick more (to alow to see if successfully added this notif)
{
ticks_to_wait = 1;
}
self.notifications.insert(std::make_pair(ticks_to_wait,
std::make_pair(receiver, method_name)));
qDebug("added delayed call..");
}
else
{
QMetaObject::invokeMethod(receiver, method_name, Qt::QueuedConnection);
ticks_to_wait = 0;
}
}
return ticks_to_wait;
}
/**
* Call to cancel a notification with a given id.
* Specify name if there were more notification with the same id (scheduled for the same tick).
* returns true on successfull cancellation, false otherwise.
*/
static bool cancel_timeout_notification(unsigned long notification_id, QString notification_name="")
{
bool cancelled = false;
if(self.notifications.size())
{
std::pair<Notifications::iterator, Notifications::iterator> to_cancel = self.notifications.equal_range(notification_id);
Notifications::iterator n = to_cancel.first;
for( ;n != to_cancel.second; ++n)
{
if(notification_name.size()== 0 || n->second.second == notification_name)
{
self.notifications.erase(n);
cancelled = true;
break;
}
}
}
return cancelled;
}
static const unsigned int default_tick_period_ms = 100;
/**
* Call this method after event loop is created- to initiate (re-start) timer.
* tick period defines 'tick' frequency (and accuracy of the timer)
* (note on Windows that there's no point to go down below 100ms).
*/
static void Init(unsigned int tick_period_ms = default_tick_period_ms)
{
self.moveToThread(&self.thread);
self.thread.start();
while(!self.thread.isRunning());
self.current_interval = tick_period_ms;
// InitMe() should execute in the thread context..
QMetaObject::invokeMethod(&self, "InitMe", Qt::QueuedConnection);
}
private:
/**
* Internal method to convert ms to number of ticks.
*/
static unsigned int num_of_ticks_to_wait(unsigned int ms_to_wait)
{
if(ms_to_wait > self.current_interval)
{
if(ms_to_wait % self.current_interval)
{
// average it..
ms_to_wait = ms_to_wait + self.current_interval / 2;
}
ms_to_wait /= self.current_interval;
}
else
{
ms_to_wait = 0;
}
return ms_to_wait;
}
/**
* Internal method to handle tick. Increments counter and invokes notifications.
*/
void timerEvent ( QTimerEvent* /*event*/ )
{
tick_counter++;
if(notifications.size())
{
std::pair<Notifications::iterator, Notifications::iterator> to_notify = notifications.equal_range(tick_counter);
Notifications::iterator n = to_notify.first;
for( ;n != to_notify.second; ++n)
{
QMetaObject::invokeMethod(n->second.first,
n->second.second.toStdString().c_str(),
Qt::QueuedConnection);
}
notifications.erase(to_notify.first, to_notify.second);
}
}
private slots:
/**
* Internal slot to initialize the timer. Should be called in this->timer context.
*/
void InitMe()
{
if(timer_id != 0)
{
killTimer(timer_id);
timer_id = 0;
}
tick_counter = 0;
timer_id = self.startTimer(self.current_interval);
}
private:
RelativeTimer()
{
}
~RelativeTimer()
{
thread.quit();
thread.wait();
}
QThread thread;
Notifications notifications;
int timer_id;
unsigned int current_interval;
unsigned long tick_counter;
static RelativeTimer self; // implement it as a signleton.. Define it in your C file, e.g.:
// RelativeTimer RelativeTimer::self;
};
Can be used like:
CurrQObjectClass::OnTimeout()
{
// ...
}
CurrQObjectClass::SomeMethod()
{
RelativeTimer::notify_timeout_ms(5000, this, "OnTimeout");
}
but also for busy-waiting:
RelativeTimer::wait_ms(2000);
Enjoy.
I am working with Qt application in which I want to create a QListWidget with names of all application installed on device and its icon.
So I was unable get all application names and UID of each application from the code in this LINK.
But I was also unable to get the application icons. I tried both this LINK1 & LINK2 but here I came across few more issues like how to convert CGulIcon into QIcon & CApaMaskedBitmap into QIcon.
How can I do this?
If you are unable to get the solution or have any more doubts click here for disscussion.
main.cpp
#include "GetInstalledApps.h"
#include <QtGui>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GetInstalledApps w;
w.showMaximized();
return a.exec();
}
.pro
TEMPLATE = app
TARGET = GetInstalledApps
QT += core \
gui
HEADERS += GetInstalledApps.h
SOURCES += main.cpp \
GetInstalledApps.cpp
FORMS += GetInstalledApps.ui
RESOURCES +=
HEADERS += xqinstaller_p.h \
xqinstaller.h \
SOURCES += xqinstaller_p.cpp \
xqinstaller.cpp
symbian:LIBS += -lswinstcli \
-lcommdb \
-lapparc \
-lefsrv \
-lapgrfx \
symbian:TARGET.CAPABILITY += TrustedUI
symbian:TARGET.UID3 = 0xEF3055F4
GetInstalledApps.h
#ifndef GETINSTALLEDAPPS_H
#define GETINSTALLEDAPPS_H
#include <QtGui/QMainWindow>
#include "ui_GetInstalledApps.h"
#include "xqinstaller.h"
class GetInstalledApps : public QMainWindow
{
Q_OBJECT
public:
GetInstalledApps(QWidget *parent = 0);
~GetInstalledApps();
private:
void GetApps();
private:
Ui::GetInstalledAppsClass ui;
XQInstaller* m_installer;
QMap<QString, uint> m_applications;
QList<QString> m_appNames;
QList<uint> m_appUID;
};
#endif // GETINSTALLEDAPPS_H
GetInstalledApps.cpp
#include "GetInstalledApps.h"
#include <QScrollArea>
GetInstalledApps::GetInstalledApps(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
setWindowTitle("GetAllInstalledApps");
m_installer = new XQInstaller(this);
GetApps();
}
void GetInstalledApps::GetApps()
{
/* Get List of applications.
* type of m_applications is QMap<QString, uint>.
* type of m_installer is XQInstaller
*/
m_applications = m_installer->applications();
/* Get List of application names
type of m_appNames is QList<QString> */
m_appNames = m_applications.keys();
/* Get List of application UID in decimal.
type of m_appUID is QList<uint> */
m_appUID = m_applications.values();
ui.listWidget->clear();
for (int i = 0; i < m_appNames.count(); i++)
{
QString str;
/* convert UID from decimal to hex and then string. */
str.setNum(m_appUID.at(i),16);
/* append name and UID to string */
QString string(m_appNames.at(i)+" UID:"+ str);
/* append string to list widget to display on screen */
ui.listWidget->addItem(string);
}
/* Let's make the UI scale so that we can scroll it. */
QScrollArea* scrollArea = new QScrollArea;
scrollArea->setWidget(ui.listWidget);
scrollArea->setWidgetResizable(true);
setCentralWidget(scrollArea);
}
GetInstalledApps::~GetInstalledApps()
{
}
xqinstaller.h
#ifndef XQINSTALLER_H
#define XQINSTALLER_H
// INCLUDES
#include <QObject>
#include <QMap>
class XQInstallerPrivate;
// CLASS DECLARATION
class XQInstaller : public QObject
{
Q_OBJECT
public:
enum Error {
NoError = 0,
OutOfMemoryError,
AlreadyInUseError,
UserCancelError,
PackageNotSupportedError,
SecurityFailureError,
MissingDependencyError,
NoRightsError,
BusyError,
AccessDeniedError,
UpgradeError,
UnknownError = -1
};
enum Drive {
DriveA, DriveB, DriveC, DriveD, DriveE,
DriveF, DriveG, DriveH, DriveI, DriveJ,
DriveK, DriveL, DriveM, DriveN, DriveO,
DriveP, DriveQ, DriveR, DriveS, DriveT,
DriveU, DriveV, DriveW, DriveX, DriveY,
DriveZ
};
XQInstaller(QObject *parent = 0);
~XQInstaller();
bool install(const QString& file, XQInstaller::Drive drive = XQInstaller::DriveC);
QMap<QString, uint> applications() const;
bool remove(uint uid);
XQInstaller::Error error() const;
Q_SIGNALS:
void applicationInstalled();
void applicationRemoved();
void error(XQInstaller::Error);
private:
friend class XQInstallerPrivate;
XQInstallerPrivate *d;
};
#endif // XQINSTALLER_H
xqinstaller.cpp
#include "xqinstaller.h"
#include "xqinstaller_p.h"
/*!
\class XQInstaller
\brief The XQInstaller class is used to install sis-packages silently.
This extension can be used for example to install back-end applications.
Example:
\code
XQInstaller *installer = new XQInstaller(this);
QMap<QString, uint> applications = installer->applications();
QListWidget *applicationList = new QListWidget(this);
QList<QString> appNames = applications.keys();
for (int i = 0; i < appNames.count(); i++) {
applicationList->addItem(appNames.at(i));
}
\endcode
*/
/*!
Constructs a XQInstaller object with the given parent.
\sa install(), remove()
*/
XQInstaller::XQInstaller(QObject *parent)
: QObject(parent), d(new XQInstallerPrivate(this))
{
}
/*!
Destroys the XQInstaller object.
*/
XQInstaller::~XQInstaller()
{
delete d;
}
/*!
Installs a sis package silently given as parameter.
\param file Sis package
\param drive Drive letter where the sis is installed to. Default value is 'C'.
\return If false is returned, an error has occurred. Call error() to get a value of
XQInstaller::Error that indicates which error occurred
\sa error()
*/
bool XQInstaller::install(const QString& file, XQInstaller::Drive drive)
{
return d->install(file, drive);
}
/*!
Get list of installed applications
If an empty QMap is returned, an error has possibly occurred. Call error() to get a value of
XQInstaller::Error that indicates which error occurred if any
\return List of installed applications
\sa error()
*/
QMap<QString, uint> XQInstaller::applications() const
{
return d->applications();
}
/*!
Removes application specified by the uid
\param uid of the application
\return True if removing was successfully started, otherwise false
\sa error()
*/
bool XQInstaller::remove(uint uid)
{
return d->remove(uid);
}
/*!
\enum XQInstaller::Error
This enum defines the possible errors for a XQInstaller object.
*/
/*! \var XQInstaller::Error XQInstaller::NoError
No error occured.
*/
/*! \var XQInstaller::Error XQInstaller::OutOfMemoryError
Not enough memory.
*/
/*! \var XQInstaller::Error XQInstaller::AlreadyInUseError
Installer is already in used.
*/
/*! \var XQInstaller::Error XQInstaller::UserCancelError
Installer cancelled by the user.
*/
/*! \var XQInstaller::Error XQInstaller::PackageNotSupportedError
Package not supported
*/
/*! \var XQInstaller::Error XQInstaller::SecurityFailureError
Security failure
*/
/*! \var XQInstaller::Error XQInstaller::MissingDependencyError
Missing dependency
*/
/*! \var XQInstaller::Error XQInstaller::NoRightsError
No rights
*/
/*! \var XQInstaller::Error XQInstaller::BusyError
Installer is busy
*/
/*! \var XQInstaller::Error XQInstaller::AccessDeniedError
Access denied
*/
/*! \var XQInstaller::Error XQInstaller::UpgradeError
Error while upgrading
*/
/*! \var XQInstaller::Error XQInstaller::UnknownError
Unknown error.
*/
/*!
Returns the type of error that occurred if the latest function call failed; otherwise returns NoError
\return Error code
*/
XQInstaller::Error XQInstaller::error() const
{
return d->error();
}
/*!
\fn void XQInstaller::applicationInstalled()
This signal is emitted when the application has been installed.
\sa install()
*/
/*!
\fn void XQInstaller::error(XQInstaller::Error)
This signal is emitted if error occured during the asynchronous operation
\sa install()
*/
/*!
\fn void XQInstaller::applicationRemoved()
This signal is emitted when the application has been removed.
\sa remove()
*/
// End of file
xqinstaller_h.h
#ifndef XQINSTALLER_P_H
#define XQINSTALLER_P_H
// INCLUDES
#include "xqinstaller.h"
#include <SWInstApi.h>
#include <SWInstDefs.h>
// FORWARD DECLARATIONS
class QString;
class QFile;
// CLASS DECLARATION
class XQInstallerPrivate: public CActive
{
public:
enum State {
ERemove,
EInstall
};
XQInstallerPrivate(XQInstaller *installer);
~XQInstallerPrivate();
bool install(const QString& file, XQInstaller::Drive drive);
bool remove(uint uid);
QMap<QString, uint> applications() const;
public:
XQInstaller::Error error();
protected:
void DoCancel();
void RunL();
private:
XQInstaller *q;
mutable int iError;
SwiUI::RSWInstSilentLauncher iLauncher;
SwiUI::TInstallOptions iOptions;
SwiUI::TInstallOptionsPckg iOptionsPckg;
SwiUI::TUninstallOptions iUninstallOptions;
SwiUI::TUninstallOptionsPckg iUninstallOptionsPckg;
XQInstallerPrivate::State iState;
TFileName iFileName;
bool iLauncherConnected;
};
#endif /*XQINSTALLER_P_H*/
// End of file
xqinstaller_h.cpp
#include "xqinstaller.h"
#include "xqinstaller_p.h"
#include <f32file.h>
#include <apgcli.h>
XQInstallerPrivate::XQInstallerPrivate(XQInstaller *installer)
: CActive(EPriorityNormal), q(installer), iOptionsPckg(iOptions),
iUninstallOptionsPckg(iUninstallOptions), iLauncherConnected(false)
{
CActiveScheduler::Add(this);
}
XQInstallerPrivate::~XQInstallerPrivate()
{
Cancel();
if (iLauncherConnected) {
iLauncher.Close();
}
}
bool XQInstallerPrivate::install(const QString& file, XQInstaller::Drive drive)
{
int asciiValue = 10; // = 'A'
TRAP(iError,
if (!iLauncherConnected) {
User::LeaveIfError(iLauncher.Connect());
iLauncherConnected = true;
}
if (IsActive()) {
User::Leave(KErrInUse);
}
iState = XQInstallerPrivate::EInstall;
iOptions.iUpgrade = SwiUI::EPolicyAllowed;
iOptions.iOCSP = SwiUI::EPolicyNotAllowed;
iOptions.iDrive = TChar(asciiValue+drive);
iOptions.iUntrusted = SwiUI::EPolicyAllowed;
iOptions.iCapabilities = SwiUI::EPolicyAllowed;
iOptions.iOptionalItems = SwiUI::EPolicyAllowed;
iOptions.iOverwrite = SwiUI::EPolicyAllowed;
TPtrC16 fileName(reinterpret_cast<const TUint16*>(file.utf16()));
iFileName = fileName;
iLauncher.SilentInstall(iStatus, iFileName, iOptionsPckg);
SetActive();
)
return (iError == KErrNone);
}
bool XQInstallerPrivate::remove(uint uid)
{
TRAP(iError,
if (!iLauncherConnected) {
User::LeaveIfError(iLauncher.Connect());
iLauncherConnected = true;
}
if (IsActive()) {
User::Leave(KErrInUse);
}
iState = XQInstallerPrivate::ERemove;
iLauncher.SilentUninstall(iStatus,TUid::Uid(uid),
iUninstallOptionsPckg, SwiUI::KSisxMimeType);
SetActive();
)
return (iError == KErrNone);
}
QMap<QString, uint> XQInstallerPrivate::applications() const
{
RApaLsSession lsSession;
QMap<QString, uint> applications;
// Connect to application architecture server
TRAP(iError,
User::LeaveIfError(lsSession.Connect());
CleanupClosePushL(lsSession);
TApaAppInfo appInfo;
lsSession.GetAllApps();
while (lsSession.GetNextApp(appInfo) == KErrNone) {
TApaAppCapabilityBuf capability;
User::LeaveIfError(lsSession.GetAppCapability(capability,
appInfo.iUid));
if (appInfo.iCaption.Length() > 0 && !capability().iAppIsHidden) {
QString fullName = QString::fromUtf16(
appInfo.iCaption.Ptr(), appInfo.iCaption.Length());
applications.insert(fullName, (TUint)appInfo.iUid.iUid);
}
}
CleanupStack::PopAndDestroy(&lsSession);
)
return applications;
}
void XQInstallerPrivate::DoCancel()
{
if (iState == XQInstallerPrivate::EInstall) {
iLauncher.CancelAsyncRequest(SwiUI::ERequestSilentInstall);
} else if (iState == XQInstallerPrivate::ERemove) {
iLauncher.CancelAsyncRequest(SwiUI::ERequestSilentUninstall);
}
}
void XQInstallerPrivate::RunL()
{
if (iStatus.Int() == KErrNone) {
if (iState == XQInstallerPrivate::EInstall) {
emit q->applicationInstalled();
} else if (iState == XQInstallerPrivate::ERemove) {
emit q->applicationRemoved();
}
} else {
iError = iStatus.Int();
emit q->error(error());
}
}
XQInstaller::Error XQInstallerPrivate::error()
{
switch (iError) {
case KErrNone:
return XQInstaller::NoError;
case SwiUI::KSWInstErrInsufficientMemory:
case KErrNoMemory:
return XQInstaller::OutOfMemoryError;
case SwiUI::KSWInstErrFileInUse:
case KErrInUse:
return XQInstaller::AlreadyInUseError;
case SwiUI::KSWInstErrUserCancel:
return XQInstaller::UserCancelError;
case SwiUI::KSWInstErrPackageNotSupported:
return XQInstaller::PackageNotSupportedError;
case SwiUI::KSWInstErrSecurityFailure:
return XQInstaller::SecurityFailureError;
case SwiUI::KSWInstErrMissingDependency:
return XQInstaller::MissingDependencyError;
case SwiUI::KSWInstErrNoRights:
return XQInstaller::NoRightsError;
case SwiUI::KSWInstErrBusy:
return XQInstaller::BusyError;
case SwiUI::KSWInstErrAccessDenied:
return XQInstaller::AccessDeniedError;
case SwiUI::KSWInstUpgradeError:
return XQInstaller::UpgradeError;
case SwiUI::KSWInstErrGeneralError:
default:
return XQInstaller::UnknownError;
}
}
// End of file
And for getting QIcon::
Hearders Required:
#include <fbs.h> //CFbsBitmap
#include <aknsskininstance.h> //MAknsSkinInstance
#include <aknsutils.h> //AknsUtils
Library required:
LIBRARY fbscli.lib ///CFbsBitmap
LIBRARY aknskins.lib aknskinsrv.lib aknswallpaperutils.lib //MAknsSkinInstance ,AknsUtils
Source Code:
CGulIcon* CMyClass::GetApplicationIconL(const TUid& aAppUID)
{
CFbsBitmap* AppIcon(NULL);
CFbsBitmap* AppIconMsk(NULL);
MAknsSkinInstance* skin = AknsUtils::SkinInstance();
AknsUtils::CreateAppIconLC(skin,aAppUID, EAknsAppIconTypeContext,AppIcon,AppIconMsk);
CleanupStack::Pop(2);
return CGulIcon::NewL(AppIcon,AppIconMsk);
}
Well, for the icon thing look at this: CGullIcon there's a function that returns the Bitmap, a CFbsBitmap. Once you've done that look at this: http://qt-project.org/doc/qt-4.8/qpixmap.html#fromSymbianCFbsBitmap then you create a new QIcon(QPixmap) (QPixmap = the icon you've transformed). So most likely you're first passing it to a CFbsBitmap and then with QPixmap you use fromSymibnaCFbsBitmap().
CApaMaskedBitmap is a sub-class of CFbsBitmap if i'm not wrong, so it should work the same.
And for getting the applications and their UID try to look at this: http://www.developer.nokia.com/Community/Wiki/Get_list_of_installed_applications_and_its_UID_in_Qt