I want to display balloon messages with QSystemTrayIcon in my qt app on mac.
The tray icon appears in the right corner of the menu bar of mac. The problem is the balloon message won't show on El Capitan (10.11.6), but it does on Sierra (10.12) and High Sierra (10.13).
The Qt Tray Icon example works just fine on El Capitan though.
QSystemTrayIcon::isSystemTrayAvailable() and QSystemTrayIcon::supportsMessages() both return true.
I don't know what to do anymore. Here's my code:
custom tray icon class (.h)
class MyTrayIcon
{
public:
MyTrayIcon(QObject* parent = nullptr);
~ MyTrayIcon();
void balloonMsg(const QString& title, const QString& message);
QSystemTrayIcon* sysTrayIcon = nullptr;
};
custom tray icon class (.cpp)
MyTrayIcon::MyTrayIcon(QObject* parent)
{
sysTrayIcon = new QSystemTrayIcon(QIcon("my_icon.png"), parent);
}
MyTrayIcon::~MyTrayIcon()
{
delete sysTrayIcon;
}
void MyTrayIcon::balloonMsg(const QString& title, const QString& message)
{
sysTrayIcon->show(); // the icon does show
sysTrayIcon->showMessage(title, message); // the message doesn't show
}
Main window class (.h)
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
Ui::MainWindow *ui;
MyTrayIcon* mTrayIcon = nullptr;
public slots:
void btnClicked();
}
Main window class (.cpp)
MainWindow::MainWindow() : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
mTrayIcon = new MyTrayIcon(this);
connect(ui->pushButton, SIGNAL(clicked(bool)), this, SLOT(btnClicked()));
}
void MainWindow::btnClicked()
{
mTrayIcon->balloonMsg("my title", "my message");
}
Thanks in advance !
Related
Normally, moving a QDialog using QDialog::move() positions the dialog outside of taskbars.
However, on Ubuntu 20.04 with two monitors it is not the case with frameless Dialogs :
This does not happen if the dialog is not frameless :
This behaviour has been observed on Ubuntu 20.04. It also happens only under some configurations :
Main monitor needs to be on the right side, with task bar on the left (between the two monitors)
Left monitor needs to have a lower resolution than the right one
Fractional scaling needs to be disabled
Here is the code for a minimally reproducible example used in the screenshots:
#ifndef BUGDIALOG_H
#define BUGDIALOG_H
#include <QDialog>
namespace Ui {
class BugDialog;
}
class BugDialog : public QDialog
{
Q_OBJECT
public:
explicit BugDialog(QWidget *parent = nullptr);
~BugDialog();
private slots:
void on_moveButton_clicked();
private:
Ui::BugDialog *ui;
};
#endif // BUGDIALOG_H
#include "bugdialog.h"
#include "ui_bugdialog.h"
BugDialog::BugDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::BugDialog)
{
ui->setupUi(this);
ui->xPosEdit->setText("3200");
ui->yPosEdit->setText("1000");
}
BugDialog::~BugDialog()
{
delete ui;
}
void BugDialog::on_moveButton_clicked()
{
int x = ui->xPosEdit->text().toInt();
int y = ui->yPosEdit->text().toInt();
if (x > -1 && x > -1)
move(x, y);
}
The main window is less interesting, it only creates the child window controlling its WindowFlags property :
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "bugdialog.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
void on_framelessBox_stateChanged(int arg1);
private:
void hideDialog();
Ui::MainWindow *ui;
BugDialog* dialog;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
dialog = new BugDialog(nullptr);
dialog->hide();
}
MainWindow::~MainWindow()
{
delete ui;
delete dialog;
}
void MainWindow::on_pushButton_clicked()
{
if (dialog->isHidden())
{
dialog->show();
ui->pushButton->setText("Hide dialog");
}
else
{
hideDialog();
}
}
void MainWindow::on_framelessBox_stateChanged(int)
{
auto windowType = ui->framelessBox->isChecked() ? Qt::FramelessWindowHint : Qt::Dialog;
dialog->setWindowFlags(windowType);
hideDialog();
}
void MainWindow::hideDialog()
{
dialog->hide();
ui->pushButton->setText("Show dialog");
}
This looks like a bug in Qt. Does anyone know if it is expected behaviour? Or how to get around this?
I didn't find a proper solution or satisfying workaround for this issue, but found a partial solution that is half satisfying :
Before each move() on the dialog, set its flag to Qt::Window (no frameless) and hide it.
Override the moveEvent() handler, set the window flag to Qt::FramelessWindowHint and show it.
Here are the two changes I made on this example :
void BugDialog::on_moveButton_clicked()
{
int x = ui->xPosEdit->text().toInt();
int y = ui->yPosEdit->text().toInt();
if (x > -1 && x > -1)
{
hide();
setWindowFlags(Qt::Window);
move(x, y);
}
}
void BugDialog::moveEvent(QMoveEvent *)
{
QTimer::singleShot(500, this, [this](){
this->setWindowFlags(Qt::FramelessWindowHint);
this->show();
});
}
I also tried changing the dialog painting. The idea was to set window flags as a "framefull" dialog but paint the dialog as if it had the FramelessWindowHint flag. I found no acceptable/affordable solution with this idea.
I'm trying to integrate a QWebEngineView widget that runs as a separate process(QProcess) inside a QTabWidget page. So far the QWebEngineView process is being started properly but its showing the webpage in a separate window instead of showing it inside the QTabWidget in the MainWindow application.
This is the Widget that is being added to the QTabWidget.
BrokersTerminal.h
class BrokersTerminal : public QWidget
{
Q_OBJECT
public:
explicit BrokersTerminal(QWidget *parent = 0);
~BrokersTerminal();
void startTerminal();
public slots:
void brokersTerminalStarted();
private:
Ui::BrokersTerminal *ui;
QProcess *brokers_process;
QString brokers_program_path;
QStringList arguments;
};
BrokersTerminal.cpp
BrokersTerminal::BrokersTerminal(QWidget *parent) :
QWidget(parent),
ui(new Ui::BrokersTerminal)
{
ui->setupUi(this);
brokers_process = new QProcess( this );
brokers_program_path = QApplication::applicationFilePath();
arguments << "--b";
connect( brokers_process, &QProcess::started, this , &BrokersTerminal::brokersTerminalStarted );
}
BrokersTerminal::~BrokersTerminal()
{
delete ui;
}
void BrokersTerminal::startTerminal()
{
brokers_process->start( brokers_program_path, arguments );
brokers_process->waitForStarted();
}
void BrokersTerminal::brokersTerminalStarted()
{
qDebug() << "Brokers terminal started";
}
This is the WebView Widget that is responsible for displaying the brokers website.
BrokersWebWidget.h
class BrokersWebWidget : public QWidget
{
Q_OBJECT
public:
explicit BrokersWebWidget(QWidget *parent = 0);
~BrokersWebWidget();
private:
Ui::BrokersWebWidget *ui;
QUrl brokers_url;
QWebEngineView *web_browser;
};
BrokersWebWidget.cpp
BrokersWebWidget::BrokersWebWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::BrokersWebWidget)
{
ui->setupUi(this);
brokers_url = "https://siteofbrokersapi.com/";
web_browser = new QWebEngineView( this );
web_browser->load( brokers_url );
}
BrokersWebWidget::~BrokersWebWidget()
{
delete ui;
}
Right now this BrokersWebWidget starts properly as a separate process but it opens in a separate window , but how can this be added in the BrokersTerminal Widget ?
Please let me know of any possible solutions. Thanks.
You cannot embed a widget running in one process into a window run in another. QWidgets can only work with widgets run in the GUI thread in the same process.
I am using the QWidget::setCursor method, but after a QMessageBox popup, in some cases it temporarily reverts to the old cursor (until I cause it to "refresh" and load the override cursor again).
The pattern seems to be that if you exit the message box with the cursor outside of its frame (for example, use the Escape key), then the cursor is gone. On the other hand, if you exit the message box with the cursor inside its frame, then the override cursor returns.
I have tried to debug it myself by subclassing QWidget and overriding enterEvent and leaveEvent. Interestingly, when the dialog box appears, a leaveEvent is triggered on the main widget, but the next enterEvent is triggered only if the cursor was inside the message box frame when closing the dialog. Otherwise, no enterEvent is seen.
How can I get that the override cursor is always returned?
Header file:
#include <QtWidgets>
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0);
void enterEvent(QEvent *);
void leaveEvent(QEvent *);
};
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
public slots:
void popup();
};
Source file:
#include <QtWidgets>
#include "main.h"
Widget::Widget(QWidget *parent) : QWidget(parent) { }
void Widget::enterEvent(QEvent * e) {
puts("Widget::enterEvent"); QWidget::enterEvent(e);
}
void Widget::leaveEvent(QEvent * e) {
puts("Widget::leaveEvent"); QWidget::leaveEvent(e);
}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
Widget * widget = new Widget(this);
widget->setMinimumSize(500, 500);
widget->setCursor(Qt::CrossCursor);
setCentralWidget(widget);
connect(new QShortcut(QKeySequence("A"),this),SIGNAL(activated()),
this,SLOT(popup()));
}
void MainWindow::popup() {
QMessageBox::question(
this, "Test", "Test", QMessageBox::Yes|QMessageBox::No);
}
int main(int argc, char *argv[]) {
QApplication app(argc,argv);
MainWindow thewindow;
thewindow.show();
int re = app.exec();
return re;
}
I got two windows(two classes), one window that opens another when i click a button.
then the user inputs something into the newly opened window and then transfer that information to the first window when a button is clicked
The problem is I can't seem to send something to the second window so i can send the user input back to the main window. I read a few places that I should use Q_object but not really sure how that works
I should mention that I am new to Qt and didn't know about the designer there is in qt creator before i was way to ahead with the program.
Hope you have some ideas to how I can do this
edit1:
I should show the relevant code i have
Mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
//alot of stuff not relevant right now creating different stuff
changeProfile= new QPushButton("ændre valgte profil",this);
profileList = new QComboBox(this);
cProfile = new CreateProfile;
connect(changeProfile,SIGNAL(clicked()),this,SLOT(ProfileChange()));
}
void MainWindow::ProfileChange()
{
file pfile(profileList->currentText().toStdString());
string tempName = pfile.read(light);
cProfile->setValue(light,tempName);
cPr
ofile->show();
}
void MainWindow::setProfileList(QString Pname_)
{
bool found = 0;
for (int i = 0;i<5;i++)
{
if (Pname_ ==profileList->itemText(i))
found = 1;
}
if (found !=1)
profileList->addItem(Pname_);
}
createProfile.cpp
CreateProfile::CreateProfile(QWidget *parent)
:QMainWindow(parent)
{
//alot of other irrelevant stuff here
saveP = new QPushButton("Save",this);
connect(saveP,SIGNAL(clicked()),this,SLOT(saveProfile()));
}
void CreateProfile::saveProfile()
{
temp = pName->text();
file pFile(temp.toStdString());
bool lights[2] = {light1->checkState(),light2->checkState()};
if (temp.length() == 0)
{
MessageBox(NULL,"Du har ikke skrevet noget navn ind\n Prov igen","Error",MB_ICONWARNING+MB_SETFOREGROUND);
}
else
{
pFile.save(lights);
//call function setProfileList
this->hide();
}
}
If that makes sence, If you need the .h file too i can show them also
I need to call setProfileList from the mainwindow in the function saveprofile(there is in createprofile window) or if there is a way i can change the combobox in the wainwindow from the createprofile window?
edit 2:
mainwindow.h
#include "createprofile.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public slots:
void ProfileChange();
//some other button clicks
public:
CreateProfile *cProfile;
void setProfileList(QString Pname_);
//other irelevant stuff
private:
// and some private members
};
createProfile.h
class CreateProfile : public QMainWindow
{
Q_OBJECT
public slots:
void saveProfile();
public:
explicit CreateProfile(QWidget *parent = 0);
~CreateProfile();
//and other stuff there isnt relevant
};
You are looking for the Qt signal-slot mechanism, namely:
class SecondWindow : public QMainWindow
{
Q_OBJECT
public:
SecondWindow(QWidget *parent = Q_NULLPTR) : QObject(Q_NULLPTR)
{
// ...
connect(secondWindowButton, SIGNAL(clicked(bool)), SLOT(handleClicked()));
// ...
}
public slots:
void SecondWindow::handleClicked()
{
// Gather information from the UI as you wish
firstWindow->foo();
}
// ...
}
or, if you have a container class for the windows, you could handle it in there, too, as follows:
connect(secondWindow->myButton, SIGNAL(clicked(bool)), SLOT(handleClicked()));
I found a very different way to do this, so I have made a QComboBox in the constructor of createProfile.cpp and then I have access to the profileList this way.
I'm new to Qt C++. I downloaded the latest windows version, did some tutorials and its great.
I saw some styling options that the Qt framework has and its great, but now I need to build my application that its main windows (form) it designed/skinned with image without the rectangle borders (borderless?).
How can I do it with Qt?
If your looking for some advanced styling in the shape of a widget, maybe this example will help you:
Shaped Clock Example
Or maybe you're simply looking for this kind of flag: Qt::CustomizeWindowHint or simply Qt::FramelessWindowHint.
I created a small example, of how to create VS2013 like frameless window in Qt5:
You can get the complete sources here: https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle
Otherwise here a code overview of how to embed the "main" mainwindow into the "frameless" window. Also you can see how to add titlebar, buttons and do maximize, resize and move of the frameless window.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
/*
place your QMainWindow code here
*/
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
/*
this class is to add frameless window supoort and do all the stuff with titlebar and buttons
*/
class BorderlessMainWindow: public QMainWindow
{
Q_OBJECT
public:
explicit BorderlessMainWindow(QWidget *parent = 0);
~BorderlessMainWindow() {}
protected:
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent *event);
private slots:
void slot_minimized();
void slot_restored();
void slot_maximized();
void slot_closed();
private:
MainWindow *mMainWindow;
QWidget *mTitlebarWidget;
QLabel *mWindowTitle;
QPushButton *mMinimizeButton;
QPushButton *mRestoreButton;
QPushButton *mMaximizeButton;
QPushButton *mCloseButton;
QPoint mLastMousePosition;
bool mMoving;
bool mMaximized;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
/*
frameless window class: it adds the MainWindow class inside the centralWidget
*/
BorderlessMainWindow::BorderlessMainWindow(QWidget *parent) : QMainWindow(parent, Qt::CustomizeWindowHint ) {
setObjectName("borderlessMainWindow");
setWindowFlags(Qt::FramelessWindowHint| Qt::WindowSystemMenuHint);
// to fix taskbar minimize feature
setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint);
mMainWindow = new MainWindow(this);
setWindowTitle(mMainWindow->windowTitle());
QVBoxLayout *verticalLayout = new QVBoxLayout();
verticalLayout->setSpacing(0);
verticalLayout->setMargin(1);
QHBoxLayout *horizontalLayout = new QHBoxLayout();
horizontalLayout->setSpacing(0);
horizontalLayout->setMargin(0);
mTitlebarWidget = new QWidget(this);
mTitlebarWidget->setObjectName("titlebarWidget");
mTitlebarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
mTitlebarWidget->setLayout(horizontalLayout);
mMinimizeButton = new QPushButton(mTitlebarWidget);
mMinimizeButton->setObjectName("minimizeButton");
connect(mMinimizeButton, SIGNAL(clicked()), this, SLOT(slot_minimized()));
mRestoreButton = new QPushButton(mTitlebarWidget);
mRestoreButton->setObjectName("restoreButton");
mRestoreButton->setVisible(false);
connect(mRestoreButton, SIGNAL(clicked()), this, SLOT(slot_restored()));
mMaximizeButton = new QPushButton(mTitlebarWidget);
mMaximizeButton->setObjectName("maximizeButton");
connect(mMaximizeButton, SIGNAL(clicked()), this, SLOT(slot_maximized()));
mCloseButton = new QPushButton(mTitlebarWidget);
mCloseButton->setObjectName("closeButton");
connect(mCloseButton, SIGNAL(clicked()), this, SLOT(slot_closed()));
mWindowTitle = new QLabel(mTitlebarWidget);
mWindowTitle->setObjectName("windowTitle");
mWindowTitle->setText(windowTitle());
horizontalLayout->addWidget(mWindowTitle);
horizontalLayout->addStretch(1);
horizontalLayout->addWidget(mMinimizeButton);
horizontalLayout->addWidget(mRestoreButton);
horizontalLayout->addWidget(mMaximizeButton);
horizontalLayout->addWidget(mCloseButton);
verticalLayout->addWidget(mTitlebarWidget);
verticalLayout->addWidget(mMainWindow);
QWidget *centralWidget = new QWidget(this);
centralWidget->setObjectName("centralWidget");
centralWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
centralWidget->setLayout(verticalLayout);
setCentralWidget(centralWidget);
}
void BorderlessMainWindow::mousePressEvent(QMouseEvent* event) {
if (!mTitlebarWidget->underMouse() && !mWindowTitle->underMouse())
return;
if(event->button() == Qt::LeftButton) {
mMoving = true;
mLastMousePosition = event->pos();
}
}
void BorderlessMainWindow::mouseMoveEvent(QMouseEvent* event) {
if (!mTitlebarWidget->underMouse() && !mWindowTitle->underMouse())
return;
if( event->buttons().testFlag(Qt::LeftButton) && mMoving) {
this->move(this->pos() + (event->pos() - mLastMousePosition));
}
}
void BorderlessMainWindow::mouseReleaseEvent(QMouseEvent* event) {
if (!mTitlebarWidget->underMouse() && !mWindowTitle->underMouse())
return;
if(event->button() == Qt::LeftButton) {
mMoving = false;
}
}
void BorderlessMainWindow::mouseDoubleClickEvent(QMouseEvent *event) {
Q_UNUSED(event);
if (!mTitlebarWidget->underMouse() && !mWindowTitle->underMouse())
return;
mMaximized = !mMaximized;
if (mMaximized) {
slot_maximized();
} else {
slot_restored();
}
}
void BorderlessMainWindow::slot_minimized() {
setWindowState(Qt::WindowMinimized);
}
void BorderlessMainWindow::slot_restored() {
mRestoreButton->setVisible(false);
mMaximizeButton->setVisible(true);
setWindowState(Qt::WindowNoState);
setStyleSheet("#borderlessMainWindow{border:1px solid palette(highlight);}");
}
void BorderlessMainWindow::slot_maximized() {
mRestoreButton->setVisible(true);
mMaximizeButton->setVisible(false);
setWindowState(Qt::WindowMaximized);
setStyleSheet("#borderlessMainWindow{border:1px solid palette(base);}");
}
void BorderlessMainWindow::slot_closed() {
close();
}
/*
MainWindow class: put all your code here
*/
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent, Qt::FramelessWindowHint), ui(new Ui::MainWindow) {
ui->setupUi(this);
statusBar()->setSizeGripEnabled(true);
}
MainWindow::~MainWindow() {
delete ui;
}
There is a sample application in your Qt directory: examples/widgets/windowsflags.
I ran into this myself and figured it out after some time. Check out https://github.com/ianbannerman/TrueFramelessWindow for sample code for both Windows & macOS.
Qt::FramelessWindowHint sacrifices resizing and min/max/close, so is probably not what mot people are looking for.