I'm developing an application, where I have a system tray icon. I'm trying to catch QSystemTrayIcon::DoubleClick in the system tray. For some reason I do not understand, I have been unable to catch. In its stead, I just get two QSystemTrayIcon::Trigger events. I have tried this using both Qt4 (v4.8.7) and Qt5 (v5.5.1). My platform is KDE/Plasma 5(v5.4.3), on Debian Testing. I have tested this even on LXDE available on Debian Testing.
So my question here is: is this a bug in Qt or some other issue else where?
/* My Header File */
class MyTrayIcon : public QSystemTrayIcon {
Q_OBJECT
public :
NBTrayIcon();
public slots:
void handleActivation( QSystemTrayIcon::ActivationReason reason );
private slots:
void toggleVisible();
void showInfo();
void quit();
Q_SIGNALS:
void newWindow();
};
/* My Cpp File */
MyTrayIcon::MyTrayIcon() : QSystemTrayIcon() {
setIcon( QIcon( ":/icons/newbreeze.png" ) );
connect( this, SIGNAL( activated( QSystemTrayIcon::ActivationReason ) ), this, SLOT( handleActivation( QSystemTrayIcon::ActivationReason ) ) );
QMenu *menu = new QMenu( "TrayMenu" );
menu->addAction( "&Toggle Visible Windows", this, SLOT( toggleVisible() ) );
menu->addAction( QIcon::fromTheme( "application-exit", QIcon( ":/icons/delete.png" ) ), "&Quit NewBreeze", this, SLOT( quit() ) );
setContextMenu( menu );
};
void MyTrayIcon::handleActivation( QSystemTrayIcon::ActivationReason reason ) {
qDebug() << reason;
switch( reason ) {
case MyTrayIcon::Context: {
qDebug() << "Context";
break;
};
case MyTrayIcon::MiddleClick: {
qDebug() << "Middle Click";
break;
};
case MyTrayIcon::Trigger: {
qDebug() << "Trigger";
break;
}
case MyTrayIcon::DoubleClick: {
qDebug() << "DoubleClick";
break;
};
default:{
qDebug() << reason;
break;
};
};
};
PS: I have added the code as listed above.
Related
I have a service: GWT client calls QT QTcpSocket function that make a request to a device and gets responses(it cannot be only the one response. I should waiting for all of them).
According to the QT documentation I can't use waitForReadyRead() function because I use Windows platform.
Note: This function may fail randomly on Windows. Consider using the
event loop and the readyRead() signal if your software will run on
Windows. http://doc.qt.io/qt-5/qabstractsocket.html#waitForReadyRead
I have only one decision now:
pseudo code:
QString MainQTFunc() {
create new thread;
while (!thread.isStopped()) {
sleep(x);
}
return QString variable from thread to GWT client;
}
New Thread {
run() {
make a TcpRequest to the device...
}
boolean isStopped() {
if(we got the response!!!) {
return true;
}
}
}
Does it the best solution to do so? I can't understand how to send simply QString variable after I get the result. Is it really impossible to the powerful QT?
Now I have(without any threads):
// The function should to return QString to the GWT client
QString MainWindow::TcpConnect(QByteArray data) {
_pSocket = new QTcpSocket( this );
connect( _pSocket, SIGNAL(readyRead()), SLOT(readTcpData()) );
connect( _pSocket, SIGNAL(connected()), SLOT(connected()) );
connect( _pSocket, SIGNAL(disconnected()), SLOT(disconnected()) );
dataGlobal = data;
_pSocket->connectToHost("IP", port);
//waiting here for all responses and sendinig the last response
return responseHexGlobal;
}
void MainWindow::connected() {
qDebug() << "connected. " << QDateTime::currentDateTime();
_pSocket->write( dataGlobal );
}
void MainWindow::disconnected() {
qDebug() << "disconnected. " << QDateTime::currentDateTime();
}
void MainWindow::readTcpData()
{
QByteArray data = _pSocket->readAll();
QByteArray as_hex_string = data.toHex();
QString response = QString(as_hex_string);
if(some condition here...) {
responseHexGlobal = response;
_pSocket->disconnectFromHost();
}
}
It's the best solution I've found. It works, but I don't like it
QString JSPrinter::connectTcp(QByteArray data) {
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
_pSocket = new QTcpSocket( this ); // <-- needs to be a member variable: QTcpSocket * _pSocket;
connect( _pSocket, SIGNAL(readyRead()), SLOT(readTcpData()) );
connect( _pSocket, SIGNAL(connected()), SLOT(connected()) );
connect( _pSocket, SIGNAL(disconnected()), SLOT(disconnected()) );
connect( &timer, SIGNAL(timeout()), &loop, SLOT(quit()) );
loop.connect( this, SIGNAL(exitLoop()), SLOT(quit()) );
dataGlobal = data;
_pSocket->connectToHost(ip_, port_);
timer.start(75000);
loop.exec();
if(timer.isActive())
logger()->info("ok");
else
logger()->info("timeout");
return responseHexGlobal;
}
void JSPrinter::connected() {
qDebug() << "connected. " << QDateTime::currentDateTime();
_pSocket->write( dataGlobal );
}
void JSPrinter::disconnected() {
qDebug() << "disconnected. " << QDateTime::currentDateTime();
}
void JSPrinter::readTcpData() {
QByteArray data = _pSocket->readAll();
QByteArray as_hex_string = data.toHex();
std::string stdString(as_hex_string.constData(), as_hex_string.length());
qDebug() << "readTcpData response " << QDateTime::currentDateTime() << QString::fromStdString(stdString);
// doing something...
if(condition) {
responseHexGlobal = received;
qDebug() << "responseHexGlobal " << responseHexGlobal;
emit exitLoop();
_pSocket->disconnectFromHost();
} else {
emit exitLoop();
_pSocket->disconnectFromHost();
return;
}
}
I have a dialog AlarmSetup derived from QDialog with following button arrangement:
// button box
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel |ButtonBox::Help, Qt::Horizontal, this);
buttonBox->button(QDialogButtonBox::Ok)->setText("übernehmen");
buttonBox->button(QDialogButtonBox::Cancel)->setText("abbrechen");
buttonBox->button(QDialogButtonBox::Help)->setText("Hilfe");
connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(slotCancel()));
connect(buttonBox, SIGNAL(helpRequested()), this, SLOT(slotHelp()));
From a second dialog AlarmWindow, I have a slot AlarmWindow::slotOpen() in which I am creating a new instance of AlarmSetup and evaluating the return code of AlarmSetup::exec():
void AlarmWindow::slotOpen() // we create a new instance of AlarmSetup
{
if ( DBG_ALARM ) qDebug() << "AlarmWindow::slotOpen() triggered";
int alarmId = mAlarm.getAlarmIdFromAlarmMap( objectName() );
AlarmData alarmData = mAlarm.mAlarmMap.value( alarmId );
//qDebug() << "alarmData:" << alarmData << "| alarmId:" << alarmId;
AlarmSetup* alarmSetup = new AlarmSetup( mAlarm, alarmData, alarmId );
int res = alarmSetup->exec();
qDebug() << "AlarmWindow -> AlarmSetup() returned:" << (res==QDialog::Accepted? "QDialog::Accepted":"QDialog::Rejected");
...
}
res is always QDialog::Rejected, independly which button I clicked in AlarmSetup!
The corresponding buttons are standard button QDialogButtonBox::Ok and QDialogButtonBox::Cancel respectively, the corresponding signals SIGNAL(accepted()) and SIGNAL(rejected()) respectively, so I do not understand why the return value is wrong!
Note that the dialog AlarmSetup is working as expected.
Any solution to get return value res working?
Thank you for your time.
here is the code of slotOK()
void AlarmSetup::slotOk()
{
if (DBG_ALARM) qDebug() << "AlarmSetup::slotOk() triggered";
QTime time = timeBox->time();
time.addSecs(60); // next full minute
time.setHMS( time.hour(), time.minute(), 0 );
AlarmData alarmData( alarmActiveBox->isChecked()
, QDateTime( calendar->selectedDate(), time )
, titleBox->text()
, textBox->document()->toPlainText()
, alarmSound->isChecked()
, alarmSoundBox->text()
, alarmRepeatActive->isChecked()
, numBox->text().toInt()
, unitBox->currentText()
, mFileName );
//qDebug() << "data from Setup:" << alarmData;
emit signalSetAlarm( alarmData, mAlarmId );
close();
}
Call accept(); or reject(); instead of close(). Rejected is just the default value (as by pressing ESC key).
Change your slots to return the desired value.
Update:
This works for me:
Mainwindow (removed irrelevant methods):
void MainWindow::openDialog()
{
Dialog* dialog = new Dialog();
dialog->setModal(true);
int result = dialog->exec();
qDebug()<<"Result:"<<result;
}
Dialog (removed irrelevant methods):
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
buttonBox->button(QDialogButtonBox::Ok)->setText("übernehmen");
buttonBox->button(QDialogButtonBox::Cancel)->setText("abbrechen");
connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(slotCancel()));
}
void Dialog::slotOk()
{
accept();
//close();
}
void Dialog::slotCancel()
{
reject();
}
With close() instead of accept() I receive 0 for result too. This may be due to being in the button box and not gaining the correct role, not sure though.
Did you override QDialog::exec() or QDialog::accept()?
I'm relatively new to Qt, but I have done a little searching around. I have a base class that handles UDP broadcasting, and does the connect statements in the constructor of the class like this:
NetworkConnection::NetworkConnection(QObject *parent)
: QObject(parent) // Based on QObject
, m_server_search( new QUdpSocket ) // Our UDP Broadcast socket
, m_waiting_for_server( false )
, m_found_server( false )
{
qDebug() << "NetworkConnection::constructor";
connect( m_server_search, SIGNAL(readyRead()), this, SLOT(serverResponse()), Qt::UniqueConnection );
if ( m_server_search->bind( QHostAddress::AnyIPv4, (quint16)PORT_MULTICAST, QUdpSocket::ShareAddress ) )
{
if ( m_server_search->joinMulticastGroup( QHostAddress( MULTICAST_GROUP ) ) )
{
connect( this, SIGNAL(broadcast(NetworkMessage)), this, SLOT(broadcast_message(NetworkMessage)), Qt::UniqueConnection );
this->m_ping_timer = this->startTimer(2000);
qDebug() << "Ping timer id=" << this->m_ping_timer;
} else qDebug() << "Couldn't start multicast listener";
} else qDebug() << "Couldn't bind multicast to port" << PORT_MULTICAST;
}
I set up a signal/slot interface for broadcasting:
signals:
void serverFound();
void serverNotFound();
void broadcast(NetworkMessage);
private slots:
void serverResponse();
void broadcast_message( NetworkMessage msg );
And broadcast_message looks like this:
void NetworkConnection::broadcast_message( NetworkMessage msg )
{
QByteArray raw = msg.toString();
qDebug() << "NetworkConnection::broadcast_message>" << raw;
if ( m_server_search->writeDatagram( raw.data(), raw.size(), QHostAddress(MULTICAST_GROUP), (quint16)PORT_MULTICAST ) < 1 ) qDebug() << "Failed broadcast last message";
}
My timer works well, and here is the code:
void NetworkConnection::timerEvent(QTimerEvent *event)
{
qDebug() << "NetworkConnection::timerEvent with id" << event->timerId() << "(ping timer=" << this->m_ping_timer << ")";
if ( event->timerId() == this->m_ping_timer )
{
qDebug() << "NetworkConnection::pingForServer";
if ( m_waiting_for_server && !m_found_server )
{
qDebug() << "Server not found!";
emit this->serverNotFound();
return;
}
if ( !m_found_server )
{
qDebug() << "Sending a ping to the server";
NetworkMessage msg( m_software_guid, get_microseconds(), QString("whoisaserver") );
emit this->broadcast( msg );
m_waiting_for_server = true;
m_found_server = false;
}
}
}
I only get the text "Sending a pint to the server" once, but my broadcast_message outputs it's qDebug() multiple times.
I'm not explicitly using multiple threads, and as you can see I'm using Qt::UniqueConnection, which is apparently having no affect?
SO why would the slot be called multiple times? I've even tried debugging it a little and just calling this->broadcast( ... ) without using emit, and it still gets called multiple times.
Edit: I just added a counter to the broadcast_message slot, and it gets called 340 times. Is there any significance to that?
I'm aware I need to derive from QObject in order to connect to a slot if I am using QGraphicsPixmapItem, but I am struggling to do this. I have tried alternative ways to achieve what I want, I have tried onMousePress and isSelectable i.e.
run->setFlag(QGraphicsPixmapItem::ItemIsSelectable);
if (run->isSelected())
{
qDebug() << "selected";
}
else if (!run->isSelected())
{
qDebug() << "not selected";
}
although run is selectable, the first argument is never true, it is always "not selected"
This is my code, I am working on the slot method;
mainwindow.cpp
int MainWindow::sim()
{
...
QGraphicsPixmapItem* run = new QGraphicsPixmapItem(QPixmap::fromImage(image6));
run->scale(0.3,0.3);
run->setPos(-200,-200);
run->setFlag(QGraphicsPixmapItem::ItemIsSelectable);
run->setCursor(Qt::PointingHandCursor);
connect(run, SIGNAL(selectionChanged()), this, SLOT(runClicked()));
scene->addItem(run);
//pause
QGraphicsPixmapItem* pause = new QGraphicsPixmapItem(QPixmap::fromImage(image7));
pause->scale(0.3,0.3);
pause->setPos(-160,-197);
pause->setFlag(QGraphicsPixmapItem::ItemIsSelectable);
pause->setCursor(Qt::PointingHandCursor);
connect(pause, SIGNAL(selectionChanged()), this, SLOT(pauseClicked()));
scene->addItem(pause);
...
}
void MainWindow::runClicked()
{
qDebug() << "run Clicked";
}
void MainWindow::pauseClicked()
{
qDebug() << "pause Clicked";
}
mainwindow.h
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
int sim();
...
public slots:
void runClicked();
void pauseClicked();
...
So obviously I get the error when connecting to the slots. Could anyone help please? Thank you.
To find out if your item is selected, do this:
QVariant MyItem::itemChange( GraphicsItemChange change, const QVariant& value )
{
if ( change == QGraphicsItem::ItemSelectedHasChanged ) {
qDebug() << ( isSelected() ? "selected" : "not selected" );
}
return QGraphicsItem::itemChange( change, value );
}
If you want to use signals and slots, you need to subclass both QObject and QGraphicsPixmapItem.
Because QObject doesn't contain clicked() signal, you need to implement that, too, by re-implementing
void mousePressEvent ( QGraphicsSceneMouseEvent *e ) and void mouseReleaseEvent ( QGraphicsSceneMouseEvent *e ).
MyItem:
#pragma once
#include <QGraphicsPixmapItem>
#include <qobject.h>
#include <QMouseEvent>
#include <QGraphicsSceneMouseEvent>
class MyItem: public QObject, public QGraphicsPixmapItem
/* moc.exe requires to derive from QObject first! */
{
Q_OBJECT
public:
MyItem(QGraphicsItem *parent = 0): QObject(), QGraphicsPixmapItem(parent)
{
}
MyItem(const QPixmap & pixmap, QGraphicsItem * parent = 0 ): QObject(),
QGraphicsPixmapItem(pixmap, parent)
{
}
signals:
void clicked();
protected:
// re-implement processing of mouse events
void mouseReleaseEvent ( QGraphicsSceneMouseEvent *e )
{
// check if cursor not moved since click beginning
if ((m_mouseClick) && (e->pos() == m_lastPoint))
{
// do something: for example emit Click signal
emit clicked();
}
}
void mousePressEvent ( QGraphicsSceneMouseEvent *e )
{
// store click position
m_lastPoint = e->pos();
// set the flag meaning "click begin"
m_mouseClick = true;
}
private:
bool m_mouseClick;
QPointF m_lastPoint;
};
And simple example of usage:
#include <qgraphicsview.h>
#include <qgraphicsscene.h>
#include "reader.h"
#include <qdebug.h>
class MainAppClass: public QObject
{
Q_OBJECT
public:
MainAppClass()
{
QGraphicsScene *scene = new QGraphicsScene();;
scene->setSceneRect( -100.0, -100.0, 200.0, 200.0 );
MyItem *item = new MyItem(QPixmap("about.png"));
connect(item, SIGNAL(clicked()), this, SLOT(pixmapClicked()));
scene->addItem(item);
QGraphicsView * view = new QGraphicsView( scene );
view->setRenderHints( QPainter::Antialiasing );
view->show();
}
public slots:
void pixmapClicked()
{
qDebug() << "item clicked!" ;
}
};
I have an object that is derived from QThread and the class definition includes the Q_OBJECT macro. I created a timer within the thread so I can do some occasional checks while the thread is running; however, the timeout event is never occurring.
I've tried making the timer a singleshot as well, but no events are emitted.
Are events processed in a thread by default or do I need to do something else to have them processed?
Here's the code for how I set up the thread and timers:
void MyClass::run( void )
{
checkTimer_chA = new QTimer( this );
qDebug() << connect( checkTimer_chA, SIGNAL( timeout() ), this, SLOT( timerExpiry_chA() ) );
checkTimer_chA->start( 1000 );
// prevent multiple, simultaneous starts
if( !isRunning )
{
qDebug() << "Thread: MyClass::run";
isRunning = true;
while( isRunning )
{
getData();
processData();
yieldCurrentThread();
}
}
checkTimer_chA->stop();
delete checkTimer_chA;
}
void DAQ::timerExpiry_chA( void )
{
qDebug() << "timerExpiry_chA";
checkTimer_chA->stop();
}
If I add QApplication::processEvents(); right before the call to yieldCurrentThread(); the timer works as expected. However, this seems wrong to me.
Working with threads in Qt can sometimes be a bit of a hassle. This blog post was a real eye opener for me. In case we adopt the style proposed in the blog post to your problem we end up with a solution like below.
Consumer::Consumer():
checkTimer_(new QTimer(this))
{
QObject::connect(checkTimer_, SIGNAL(timeout()), this, SLOT(onTimerExpiration());
QObject::connect(this, SIGNAL(ready()), this, SLOT(consume());
}
bool Consumer::event(QEvent *e)
{
if (e->type() == QEvent::ThreadChange)
{
QTimer::singleShot(0, this, SLOT(start());
}
return QObject::event(e);
}
void Consumer::consume()
{
getData();
processData();
emit ready();
}
void Consumer::start()
{
checkTimer_->start(1000);
emit ready();
}
void Consumer::onTimerExpiration()
{
qDebug() << "timeout";
}
Then run it in a separate thread as follows:
...
Consumer *consumer = new Consumer();
...
QThread *thread = new QThread(this);
thread->start();
consumer->moveToThread(thread);
All child objects of Consumer will run in the context of the thread Consumer was moved to. It is possible to create a timeout signal for the Consumer class and connect it with an object that is not running in thread. Qt will ensure that the proper signal/slot types are applied to the connection once the object is moved to the thread.
I left out the whole isRunning part as I doubt you will still need it as long as you only create one Consumer.
Threads do not have their own event loops unless you explicitly create event loops in them. See http://doc.qt.io/qt-5/threads-qobject.html#per-thread-event-loop for details.
Perhaps you need an event loop running on the thread. What about changing the code to the following?
void MyClass::run( void )
{
checkTimer_chA = new QTimer( this );
qDebug() << connect( checkTimer_chA, SIGNAL( timeout() ), this, SLOT( timerExpiry_chA() ) );
checkTimer_chA->start( 1000 );
// prevent multiple, simultaneous starts
if( !isRunning )
{
qDebug() << "Thread: MyClass::run";
isRunning = true;
QTimer::singleShot(0, this, SLOT(process()));
exec();
}
checkTimer_chA->stop();
delete checkTimer_chA;
}
void MyClass::process()
{
if( isRunning )
{
getData();
processData();
yieldCurrentThread();
QTimer::singleShot(0, this, SLOT(process()));
}
else
QThread::exit();
}
void MyClass::timerExpiry_chA( void )
{
qDebug() << "timerExpiry_chA";
checkTimer_chA->stop();
}