Garbage pixels in transparent background of QSystemTrayIcon - qt

I'm trying to make a system tray icon using Qt 5.10.0. The icon would be non-rectangular (text actually). My current code works normally on a KDE Plasma panel, but on XFCE4-panel it appears to have garbage in the background, whenever icon's pixel is transparent. The garbage typically represents pieces of some of the already present icons in the system tray, but sometimes has some pieces of other windows.
Almost all other applications' icons look clean, including Qt-based apps like QBittorrent, Klipper, KTorrent, as well as GTK-based (Pidgin). The only exception is Dropbox and my code. Notable difference is that my code, as well as Dropbox, are both Qt5-based, while the above mentioned correctly-looking Qt applications are Qt4-based. Compiling my code for Qt4 indeed doesn't show the problem.
Below is the code. What am I doing wrong here?
#include <QTimer>
#include <QPixmap>
#include <QPainter>
#include <QApplication>
#include <QSystemTrayIcon>
class MyTrayIcon : public QSystemTrayIcon
{
Q_OBJECT
QTimer updateTimer;
public:
MyTrayIcon()
{
connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateIcon()));
updateTimer.start(2*1000);
updateIcon();
}
private:
Q_SLOT void updateIcon()
{
const int size=22;
QPixmap pixmap(size,size);
// Make sure there's no garbage in the pixmap
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setPen(QColor(0,96,192));
painter.drawText(pixmap.rect(), Qt::AlignCenter, "5");
setIcon(pixmap);
}
};
int main(int argc, char** argv)
{
QApplication app(argc,argv);
MyTrayIcon trayIcon;
trayIcon.show();
return app.exec();
}
#include "temps.moc"

Related

Qt - How to create Image that scale with window, and keeps aspect ratio?

I'm trying to create image in QT (inside label) that would change size according to changes in window size, but also would keep aspect ratio.
What's the best way to do it?
You tagged this question with linux. I develop on Windows 10 - the closest to Linux I have at hand is cygwin. Thus, I solved it in VS2013 but, hey, this is C++ with Qt. It should be portable...
Actually, QPixmap::scaled() has everything built-in what's necessary for scaling by keeping the aspect ratio. Thus, my solution is rather short plugging the QLabel and QPixmap together.
// standard C++ header:
#include <iostream>
#include <string>
// Qt header:
#include <QApplication>
#include <QResizeEvent>
#include <QLabel>
#include <QMainWindow>
#include <QPixmap>
#include <QTimer>
using namespace std;
class LabelImage: public QLabel {
private:
QPixmap _qPixmap, _qPixmapScaled;
public:
void setPixmap(const QPixmap &qPixmap) { setPixmap(qPixmap, size()); }
protected:
virtual void resizeEvent(QResizeEvent *pQEvent);
private:
void setPixmap(const QPixmap &qPixmap, const QSize &size);
};
void LabelImage::resizeEvent(QResizeEvent *pQEvent)
{
QLabel::resizeEvent(pQEvent);
setPixmap(_qPixmap, pQEvent->size());
}
void LabelImage::setPixmap(const QPixmap &qPixmap, const QSize &size)
{
_qPixmap = qPixmap;
_qPixmapScaled = _qPixmap.scaled(size, Qt::KeepAspectRatio);
QLabel::setPixmap(_qPixmapScaled);
}
int main(int argc, char **argv)
{
cout << QT_VERSION_STR << endl;
// main application
#undef qApp // undef macro qApp out of the way
QApplication qApp(argc, argv);
// setup GUI
QMainWindow qWin;
#if 0 // does not consider aspect ratio
QLabel qLblImg;
qLblImg.setScaledContents(true);
#else // (not) 0
LabelImage qLblImg;
#endif // 0
qLblImg.setAlignment(Qt::AlignCenter);
qLblImg.setSizePolicy(
QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
QPixmap qPM;
if (qPM.load("cats.jpg")) qLblImg.setPixmap(qPM);
else {
qLblImg.setText(
QString::fromLatin1("Sorry. Cannot find file 'cats.jpg'."));
}
qWin.setCentralWidget(&qLblImg);
qWin.show();
// run application
return qApp.exec();
}
Notes:
I overloaded the QLabel::setPixmap() method and store actually two versions of the pixmap - the original and the scaled. I'm not sure if this is necessary - it's the first time I used QPixmap.
While reading the Qt docs I found QLabel::setScaledContents(). I gave it a try but it does not consider the aspect ratio of the pixmap. I couldn't find a way to set this as extra option. (May be, I did not search enough. I disabled this code but left it in to remember this as "wrong direction".)
In an intermediate version, I was able to enlarge the application (and scaling was fine) but I could not shrink it. Googling a little bit I found SO: Enable QLabel to shrink even if it truncates text. This solved the issue.
To keep the sample short, I hardcoded the image file name. It is actually unnecessary to say that the current directory of the application must be the one where the file is located. (This is probably no issue in Linux but I had to adjust the debug settings in VS2013 appropriately.)
Below is a snapshot of my test appl.:
This should work (of course) with any image file which can be loaded into Qt. However, to make the sample complete (and because Internet and pictures of cats belong really together) I provide the sample image also (for download).
The left is Max, the right is Moritz. (Or vice versa?)
Edit:
According to the feedback of Shefy Gur-ary, this didn't work properly in a QLayout. Thus, I modified the original version and added a QGridLayout to my sample code to examine this topic:
// standard C++ header:
#include <iostream>
#include <string>
// Qt header:
#include <QApplication>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QMainWindow>
#include <QPixmap>
#include <QResizeEvent>
#include <QTimer>
using namespace std;
class LabelImage: public QLabel {
private:
QPixmap _qPixmap, _qPixmapScaled;
public:
void setPixmap(const QPixmap &qPixmap) { setPixmap(qPixmap, size()); }
protected:
virtual void resizeEvent(QResizeEvent *pQEvent);
private:
void setPixmap(const QPixmap &qPixmap, const QSize &size);
};
void LabelImage::resizeEvent(QResizeEvent *pQEvent)
{
QLabel::resizeEvent(pQEvent);
setPixmap(_qPixmap, pQEvent->size());
}
void LabelImage::setPixmap(const QPixmap &qPixmap, const QSize &size)
{
_qPixmap = qPixmap;
_qPixmapScaled = _qPixmap.scaled(size, Qt::KeepAspectRatio);
QLabel::setPixmap(_qPixmapScaled);
}
int main(int argc, char **argv)
{
cout << QT_VERSION_STR << endl;
// main application
#undef qApp // undef macro qApp out of the way
QApplication qApp(argc, argv);
// setup GUI
QMainWindow qWin;
QGroupBox qBox;
QGridLayout qGrid;
// a macro for the keyboard lazy:
#define Q_LBL_WITH_POS(ROW, COL) \
QLabel qLbl##ROW##COL(QString::fromLatin1(#ROW", "#COL)); \
/*qLbl##ROW##COL.setFrameStyle(QLabel::Raised | QLabel::Box);*/ \
qGrid.addWidget(&qLbl##ROW##COL, ROW, COL, Qt::AlignCenter)
Q_LBL_WITH_POS(0, 0);
Q_LBL_WITH_POS(0, 1);
Q_LBL_WITH_POS(0, 2);
Q_LBL_WITH_POS(1, 0);
LabelImage qLblImg;
qLblImg.setFrameStyle(QLabel::Raised | QLabel::Box);
qLblImg.setAlignment(Qt::AlignCenter);
//qLblImg.setMinimumSize(QSize(1, 1)); // seems to be not necessary
qLblImg.setSizePolicy(
QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
QPixmap qPM;
if (qPM.load("cats.jpg")) qLblImg.setPixmap(qPM);
else {
qLblImg.setText(
QString::fromLatin1("Sorry. Cannot find file 'cats.jpg'."));
}
qGrid.addWidget(&qLblImg, 1, 1, Qt::AlignCenter);
qGrid.setRowStretch(1, 1); // tell QGridLayout to stretch this cell...
qGrid.setColumnStretch(1, 1); // ...prior to other cells (w/ stretch 0)
Q_LBL_WITH_POS(1, 2);
Q_LBL_WITH_POS(2, 0);
Q_LBL_WITH_POS(2, 1);
Q_LBL_WITH_POS(2, 2);
qBox.setLayout(&qGrid);
qWin.setCentralWidget(&qBox);
qWin.show();
// run application
return qApp.exec();
}
Notes:
The aspect ratio of image was still correct but the resizing didn't work anymore. Thus, I added QGrid::setRowStretch() and QGrid::setColumnStretch(). Unfortunately, this didn't change much.
I googled this topic and found SO: Change resize behavior in Qt layouts. Actually, This didn't help really also but made me suspective that the QGridLayout could be the actual source of this layout issue.
For better visualization of this layout issue, I added frames to all my widgets. To my surprise, it worked suddenly.
I assume that the layout in the QGridLayout works somehow not like expected (although I wouldn't dare to call it a bug). However, the workaround to make a frame around the image is something I could live with. (Actually, it looks not that bad.)
A snapshot of the updated code sample:

Qt 5 with USB video grabber

My demo code is to choose a camera from the integrated camera of my laptop, and a USB video grabber (STK1160). My code is attached.
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QCamera>
#include <QCameraInfo>
#include <QCameraImageCapture>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QList <QCameraInfo> camList;
QCamera *camera;
private slots:
void onCameraChanged(int);
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
camera = NULL;
connect(ui->cameraComboBox,static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),this,
&MainWindow::onCameraChanged);
// find all available cameras and put them in the combo box
camList = QCameraInfo::availableCameras();
for(QList <QCameraInfo>::iterator it = camList.begin();it!=camList.end();++it) {
ui->cameraComboBox->addItem(it->description());
}
}
MainWindow::~MainWindow() {
delete ui;
}
void MainWindow::onCameraChanged(int idx) {
if(camera != NULL) {
camera->stop();
}
camera = new QCamera(camList.at(idx),this);
camera->setViewfinder(ui->viewfinder);
camera->setCaptureMode(QCamera::CaptureStillImage);
camera->start();
}
My problem is that when I choose the USB grabber from the combo box, I got the following error message:
libv4l2: error turning on stream: Message too long
CameraBin error: "Error starting streaming on device '/dev/video1'."
CameraBin error: "Could not negotiate format"
and the camera view is all black. Anyone has any idea? I have tested my video input on an AV screen, it works well.
I don't have a very concrete answer, but a more general one about camera & media APIs inside Qt5. In my experience some features only work on some platforms while other work on other platforms. For example I am currently struggling with having QVideoProbe work on Ubuntu even if it works fine on Android.
And development focus by Qt developers seems to be 80% on the mobile platforms recently. Also on Linux platforms the "backend" for video is gstreamer, which means that most errors originate there. My best tip is to upgrade to Qt 5.6 which relies on gstreamer1.0 instead of the ancient gstreamer0.1. Also make sure to install all the gstreamer plugins etc for your platform, as this may have a big impact on how well media works.
Also if you can reproduce the error directly in gstreamer, you might be able to find a fix for it there and this fix will then be available from Qt as well. For example if you are missing a codec or driver, using the gstreamer tools to add the support you need might solve the problem
I find the media APIs in Qt to be solid and I know that work is continuously being made to fill in the massing features in each platform's media back-end, so for each update of Qt more features should be added and bugs should be fixed.
I hope this helps, even if it does not address your question directly (which probably is because very few have the exact experience that you had).

QGraphicsProxyWidget has clipped context menu in QGraphicsScene

The following code is based on the documentation of Graphics View Framework. I embed a QLineEdit in a QGraphicsScene and run the program. When I right click the line edit in the scene I get a clipped context menu. The context menu of a QGraphicsProxyWidget is drawn by the scene as a child QGraphicsProxyWidget so it get's clipped if the window is too small. I want all embedded widgets to show their context menus as top-level windows like they do when not being embedded in a QGraphicsScene. I have tried the BypassGraphicsProxyWidget flag in two ways but it doesn't work as I want. Tested on Qt 4.8 / 5.0 on Linux and Windows. Same issue on all platforms.
How can I make the embedded widgets display normal, top-level context menus with native look? Overloading QGraphicsView's contextMenuEvent gives a native top-level context menu - could I do some sort of delegation and make QGraphicsView display the context menu of embedded widgets in the scene?
#include <QApplication>
#include <QLineEdit>
#include <QGraphicsScene>
#include <QGraphicsProxyWidget>
#include <QGraphicsView>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QGraphicsScene scene;
QGraphicsProxyWidget *proxy = scene.addWidget(new QLineEdit(), Qt::BypassGraphicsProxyWidget);
QGraphicsView view(&scene);
view.setWindowFlags(Qt::BypassGraphicsProxyWidget);
view.show();
return app.exec();
}
Unfortunately, this is a known bug QTBUG-10683. A workaround is suggested in the last comment to the bug report.
You get native context menus by adding a QWidget that has the Qt::BypassGraphicsProxyWidget set. Children will render it's context menus as pop-ups native style.
#ifndef QGLPARENT_H
#define QGLPARENT_H
#include <QGLWidget>
#include <QGraphicsScene>
#include <QGraphicsProxyWidget>
#include <QGraphicsView>
class QGLParent : public QGraphicsView
{
private:
QGraphicsProxyWidget *child;
public:
QGLParent(QWidget *parent, QWidget *child) : QGraphicsView(parent)
{
setFrameShape(QFrame::NoFrame);
QGLFormat format(QGL::SampleBuffers);
format.setSwapInterval(1);
setScene(new QGraphicsScene());
setViewport(new QGLWidget(format));
//setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
child->setWindowFlags(Qt::BypassGraphicsProxyWidget);
this->child = scene()->addWidget(child);
}
protected:
void resizeEvent(QResizeEvent *event)
{
scene()->setSceneRect(QRect(QPoint(0, 0), event->size()));
child->resize(event->size().width(), event->size().height());
QGraphicsView::resizeEvent(event);
}
};
#endif

Loading and unloading textures crashes the system

While debugging an application written in Qt using OpenGL ES 2.0 on a Linux embedded ARM platform, I noticed that when repeatedly loading and unloading textures, the embedded platform I'm working on started to behave strangely. I therefore wrote a simple test code to stress the OpenGL library:
main.cpp
#include <QApplication>
#include "mainwidget.h"
int main(int argc, char** argv)
{
QApplication a(argc, argv);
MainWidget widget;
widget.showFullScreen();
return a.exec();
}
and mainwidget.h:
#include <QElapsedTimer>
#include <QTimer>
#include "mainwidget.h"
MainWidget::MainWidget(QWidget *parent) :
QGLWidget(parent)
{
makeCurrent();
QTimer* t = new QTimer();
t->setSingleShot(false);
t->setInterval(10);
connect(t, SIGNAL(timeout()), this, SLOT(textureLoadingTest()));
t->start();
}
void MainWidget::textureLoadingTest()
{
QImage image(256, 512, QImage::Format_ARGB32);
image.fill(Qt::red);
QElapsedTimer timer;
timer.start();
GLuint code = bindTexture(image);
qDebug("Texture loaded in %lldms to code = %u.", timer.elapsed(), code);
deleteTexture(code);
}
This code seems to completely crash the system after 20/30 thousands textures.
Is this a bug in the test code or a system problem?

How to click a button in a out-of-focus widget without changing the current focus to it

I am trying to implement a input method with Qt Embedded.
There is a lookup table for choosing the candidate words for typing. "text input area" to the "lookup table" and the selected word cannot be sent to the "text input area".
Dose anyone have any idea to solve this problem? Thanks~
Here I give a simple example:
main.cpp
#include "InputWidget.h"
#include "ButtonWidget.h"
#include <QApplication>
int main(int argc,char *argv[])
{
QApplication app(argc,argv);
InputWidget *inputWidget=new InputWidget();
ButtonWidget *buttonWidget=new ButtonWidget();
inputWidget->show();
buttonWidget->show();
int ref=app.exec();
inputWidget->deleteLater();
buttonWidget->deleteLater();
return ref;
}
InputWidget.h
#include <QWidget>
#include <QPlainTextEdit>
#ifndef _InputWidget_H_
#define _InputWidget_H_
class InputWidget:public QWidget
{
Q_OBJECT
public:
InputWidget(QWidget *parent=0);
private:
QPlainTextEdit *inputArea;
};
#endif
InputWidget.cpp
#include "InputWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
InputWidget::InputWidget(QWidget *parent):QWidget(parent)
{
//input area setup
inputArea=new QPlainTextEdit(this);
//main layout
QVBoxLayout *mainLayout=new QVBoxLayout(this);
mainLayout->setContentsMargins(1,4,1,1);
mainLayout->addWidget(inputArea);
setLayout(mainLayout);
}
ButtonWidget.h
#include <QWidget>
#include <QPushButton>
#ifndef _ButtonWidget_H_
#define _ButtonWidget_H_
class ButtonWidget:public QWidget
{
Q_OBJECT
public:
ButtonWidget(QWidget *parent=0);
private:
QPushButton *selectedBtn;
public slots:
void changeBtnText();
};
#endif
ButtonWidget.cpp
#include "ButtonWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
ButtonWidget::ButtonWidget(QWidget *parent):QWidget(parent)
{
//selectedBtn setup
selectedBtn=new QPushButton(tr("Click Me!!"),this);
connect(selectedBtn,SIGNAL(clicked()),this,SLOT(changeBtnText()));
//main layout
QVBoxLayout *mainLayout=new QVBoxLayout(this);
mainLayout->setContentsMargins(1,4,1,1);
mainLayout->addWidget(selectedBtn);
setLayout(mainLayout);
}
void
ButtonWidget::changeBtnText()
{
selectedBtn->setText("I am clicked :)");
}
Those codes would generate a widget which has a PlainTextEdit "inputArea" and a widget which has a PushButton "selectedBtn".
First, I input some words in the "inputArea". The current foucs is on "inputArea" in the InputWidget.
But when I move mouse to ButtonWidget and click the "selectedBtn", the foucs is changed to "selectedBtn" in the ButtonWidget.
How do I click the "selectedBtn" but still keep the foucs on "inputArea"? Thanks~
Just like my comment described in laura's answer, InputWidget and ButtonWidget may have no identical parent and I cannot use QWidget's "setFocus" slot to change the current focus between them.
First of all, you will need to make the two widgets know about each other. Once you have done that (by setting the text widget into the button widget or by adding them both to the same parent widget), you can try to see if QWidget's setFocus slot can help you (look at the other slots too, some might be useful for this).
Perhaps implement something like this, in the ButtonWidget:
(in the header) declare a signal foo()
(in the constructor) connect button widget's foo signal to InputWidget's setFocus slot
(in the changeBtnText) after you've done everything you wanted, emit foo()
Note though that setFocus works if the window is active.
You might be able to get what you want by playing with the focusPolicy for your widget. Pay attention to the Qt::NoFocus option. I don't think it prevents mouse clicks on your widget, but you'll want to test to be sure.
Thank laura and cjhuitt, your responses give me a big hint to solve my question.
Because InputWidget and ButtonWidget are two independent widgets, if we want to deal with the focus issue between them, we need a "global" control, i.e., use QApplication to do the focus job.
The key point is using QApplication's "focusChanged" slot and QWidget's "activateWindow". The following is my modification.
main.cpp
#include "InputWidget.h"
#include "ButtonWidget.h"
#include <QApplication>
int main(int argc,char *argv[])
{
QApplication app(argc,argv);
InputWidget *inputWidget=new InputWidget();
ButtonWidget *buttonWidget=new ButtonWidget(0,&app);
inputWidget->show();
buttonWidget->show();
int ref=app.exec();
inputWidget->deleteLater();
buttonWidget->deleteLater();
return ref;
}
ButtonWidget.h
#include <QWidget>
#include <QPushButton>
#include <QApplication>
#ifndef _ButtonWidget_H_
#define _ButtonWidget_H_
class ButtonWidget:public QWidget
{
Q_OBJECT
public:
ButtonWidget(QWidget *parent=0,QApplication *app=0);
private:
QPushButton *selectedBtn;
public slots:
void changeBtnText();
void changeFocus(QWidget *oldWidget,QWidget *curWidget);
};
#endif
ButtonWidget.cpp
#include "ButtonWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
ButtonWidget::ButtonWidget(QWidget *parent,QApplication *app):QWidget(parent)
{
//selectedBtn setup
selectedBtn=new QPushButton(tr("Click Me!!"),this);
connect(selectedBtn,SIGNAL(clicked()),this,SLOT(changeBtnText()));
//main layout
QVBoxLayout *mainLayout=new QVBoxLayout(this);
mainLayout->setContentsMargins(1,4,1,1);
mainLayout->addWidget(selectedBtn);
setLayout(mainLayout);
//deal with focus
connect(app,SIGNAL(focusChanged(QWidget*,QWidget*)),this,SLOT(changeFocus(QWidget*,QWidget*)));
}
void
ButtonWidget::changeBtnText()
{
selectedBtn->setText("I am clicked :)");
}
void
ButtonWidget::changeFocus(QWidget *oldWidget,QWidget *curWidget)
{
if(oldWidget && curWidget==this)
oldWidget->activateWindow();
}
InputWidget.cpp and InputWidget are not modified.
This solution can work in this example, but I am not sure that it can work in two independent Qt programs(they have their own QApplication), especially in Qt Embedded environment. I would continue doing some tests on it. If there are any results, I would also post them here.
Thank for this discussion, I have learned a lot and thank you all again!!

Resources