How can I program a custom popup Window in Qt? - c++

My Qt application consists of several screens added on a QStackedLayout(). Now after some useraction I would like a little popup window that confirms the action and disappears after a few seconds. What I would like is a gray rectangle with black border and some text in it. No buttons, no titlebar.
I tried to do it with QMessage Box (see code below) but in general it doesnt seem to be possible to adjust border styles for QMessageBox(). Also the size can't be adjusted.
QMessageBox* tempbox = new QMessageBox;
tempbox->setWindowFlags(Qt::FramelessWindowHint); //removes titlebar
tempbox->setStandardButtons(0); //removes button
tempbox->setText("Some text");
tempbox->setFixedSize(800,300); //has no effect
tempbox->show();
QTimer::singleShot(2000, tempbox, SLOT(close())); //closes box after 2 seconds
So, how can I program a custom popup Window in Qt?

First of all, I'd like to recommend the Windows Flags Example of the Qt docs. It provides a nice sample to play around with this topic. In this sample, a QWidget is derived to show the effect of the distinct flags. This brought me to the idea that probably any QWidget can be used for this if the appropriate Qt::WindowFlags are set. I choosed QLabel because
it can display text
it inherits from QFrame and, thus, can have a frame.
Source code testQPopup.cc:
// standard C++ header:
#include <iostream>
#include <string>
// Qt header:
#include <QApplication>
#include <QLabel>
#include <QMainWindow>
#include <QTimer>
using namespace std;
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;
qWin.setFixedSize(640, 400);
qWin.show();
// setup popup
QLabel qPopup(QString::fromLatin1("Some text"),
&qWin,
Qt::SplashScreen | Qt::WindowStaysOnTopHint);
QPalette qPalette = qPopup.palette();
qPalette.setBrush(QPalette::Background, QColor(0xff, 0xe0, 0xc0));
qPopup.setPalette(qPalette);
qPopup.setFrameStyle(QLabel::Raised | QLabel::Panel);
qPopup.setAlignment(Qt::AlignCenter);
qPopup.setFixedSize(320, 200);
qPopup.show();
// setup timer
QTimer::singleShot(1000,
[&qPopup]() {
qPopup.setText(QString::fromLatin1("Closing in 3 s"));
});
QTimer::singleShot(2000,
[&qPopup]() {
qPopup.setText(QString::fromLatin1("Closing in 2 s"));
});
QTimer::singleShot(3000,
[&qPopup]() {
qPopup.setText(QString::fromLatin1("Closing in 1 s"));
});
QTimer::singleShot(4000, &qPopup, &QLabel::hide);
// run application
return qApp.exec();
}
I compiled with VS2013 and Qt 5.6 on Windows 10 (64 bit). The image below shows a snaphot:
To make the popup better visible (and because I liked it), I changed the background color of the QLabel for popup. And, I couldn't resist to add a little countdown.

Related

Draw inside a QGraphicsScene inside a QWidget

I am trying to have a window (in the form of a QWidget) consisting of both a menu on the right and a graphics area on the left.
Despite the numerous websites explaining the many ways to use QGraphicsScene and QGraphicsView, I just couldn't figure out how to do it.
Here's main.cpp modified to work on its own :
#include <QApplication>
#include <QRectF>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QPushButton>
#include <QVBoxLayout>
#include <QGraphicsRectItem>
#include <QPalette>
int main (int argc, char * argv []) {
QApplication app (argc, argv) ;
// Main window
QWidget frame ;
frame.setFixedSize(750, 550) ;
frame.show() ;
// Right side, works ok
QWidget menu (&frame) ;
menu.setFixedSize(200, 500) ;
menu.move(550, 10) ;
QPalette pal = menu.palette() ;
pal.setColor(QPalette::Background, Qt::red) ;
menu.setPalette(pal) ; // I expected this to color the whole area
// to show the extent of the zone devoted to the menu,
// but it only outlines the button
menu.show() ;
QPushButton button ("Menu", &menu) ;
button.show() ; // I didn't think this was necessary,
// but the button didn't appear without this line
// Left side, nothing displayed
QVBoxLayout layout ;
QGraphicsScene * scene = new QGraphicsScene () ;
QGraphicsView view (scene) ;
layout.addWidget(&view) ;
QWidget canvas (&frame) ;
canvas.setLayout(&layout) ;
// I found this trick to include a QGraphicsScene inside a QWidget,
// I haven't had the opportunity to see whether it really works.
scene->addItem(new QGraphicsRectItem(10, 10, 20, 20)) ;
// The above line has no visible effect
view.show() ;
return app.exec() ;
}
I would expect this to create a window, put a bunch of buttons on the right side (or in the case of the rediced code I provided, just a single button), and draw a rectangle on the left, but it leaves the whole left area blank.
Does the problem come from how I put the QGraphicsView inside the QWidget ? Or is it failing to draw because of something else ? Do I have to update the QGraphicsView to reflect the change ? Is it just out of visible range ?
Finally, is the failure to draw in any way related to the fact that the whole application crashes on line QWidget canvas (&frame) ; when closed ?
I just repaired you program a bit, so that it illustrates, what you can do with Qt and how you should likely use the framework.
I just moved the QPushButton to a QAction residing in a QMenuBar. The QMenuBar can be added to a QMainWindow, which is reasonable for a normal app.
The central widget of the QMainWindow contains the QGraphicsView. Now, you just forgot to connect the QGraphicsScene with the QGraphicsView. That was the reasons for not seeeing anything in your view.
QGraphicsView and QGraphicsScene are just a typical example for a MVC pattern. You can also add another QGraphicsView and connect it to the same QGraphicsScene.
You should also create all you objects with new, as Qt automatically disposes all its children of a QObject, if it is either deleted or leaves scope.
If you are realyl interesting into seriously learning Qt I suggest, that you are creating plenty small example programs like these. It really helped me a lot.
#include <QApplication>
#include <QMenuBar>
#include <QGraphicsView>
#include <QVBoxLayout>
#include <QGraphicsRectItem>
#include <QMainWindow>
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
auto mainWindow = new QMainWindow;
auto menuBar = new QMenuBar;
auto menu = new QMenu("Menu");
auto action = new QAction("Action");
menu->addAction(action);
menuBar->addMenu(menu);
mainWindow->setMenuBar(menuBar);
auto frame = new QFrame;
frame->setLayout(new QVBoxLayout);
mainWindow->setCentralWidget(frame);
auto scene = new QGraphicsScene();
auto view=new QGraphicsView(scene);
view->setScene(scene); // That connects the view with the scene
frame->layout()->addWidget(view);
QObject::connect(action, &QAction::triggered, [&]() {
scene->addItem(new QGraphicsRectItem(10, 10, 20, 20));
});
mainWindow->show();
return app.exec();
}

How to retrieve the icon FROM a QPushButton?

QT 5.8.0, OSX 10.12.6
I need to retrieve the image on a QButton in a window. Here's where I'm at:
I have a number of QPushButtons in a window. These all have been set to have images in QtDesigner by selecting the images from resources assigned to the project. Which works fine.
My (admittedly vague) understanding is that when the window initializes the ui...
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
...the images are applied to the button. When the window opens, they are there, and I didn't do anything else, so that's my guess.
What I need to do now is access the images in the buttons, by which I mean I need to make local copies of them for modification and use later. QAbstractButton has an icon() function, which says it returns the "icon property", so within MainWindow(), I attempted to get the icon from the entire button as follows...
{
QIcon xx;
xx = ui->entire->icon();
}
...which promptly crashes with:
Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes:
KERN_INVALID_ADDRESS at 0x00000023000004b8 Exception Note:
EXC_CORPSE_NOTIFY
So I'm doing it wrong, or perhaps it can't be done. TIA for any insight on this one.
There is likely an error somewhere else in your code. I'm using Qt 5.13.0 and the following code works without problems. I suggest, that you might try it in your environment.
#include <QFrame>
#include <QHBoxLayout>
#include <QPushButton>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
auto frame = new QFrame;
frame->setLayout(new QHBoxLayout);
auto btn = new QPushButton("Test");
btn->setIcon(QIcon("./data/icon.png"));
frame->layout()->addWidget(btn);
QObject::connect(btn, &QPushButton::clicked, [btn]() {
auto icon = btn->icon();
auto pixmap=icon.pixmap(16,16,QIcon::Mode::Normal, QIcon::State::On);
pixmap.save("savedIcon.png");
});
frame->show();
return a.exec();
}

QWidget UI freezes when using a QQuickWidget

I am working on a software that uses both QQuickWidget and QWidget.
The main window is a QWidget that contains a QQuickWidget used to render stuff with OpenGL.
The QQuickWidget is in a QFrame that is sometimes displayed and sometimes hidden.
The problem is that when the QFrame is hidden, all the UI base on QWidget stops being updated
The weird thing is that only the UI freezes. If you know where to click, the software still work, only the UI is no longer updated. I think it might be that QML steals the rendering loop that is no longer accessible when it is hidden ?
I tested this on :
Linux, Qt 5.11.2 : No problem observed
Windows 10 64bit, Qt 5.9 : Problem as described above
Windows 10 64bit, Qt 5.11.2 : Problem as described above
Below is a small code example that can reproduce the problem. it works this way :
Homescreen is a QWidget. UI is updated on mouse hover (colors change)
on click, it switches to QQuickWidget
QML UI is updated on mouse hover (color change)
on click, it switches back to QWidget
The mouse hover no longer updates the UI (no color change)
main.cpp
#include <QApplication>
#include "myMainWindow.hpp"
int main(int argc, char **argv) {
QApplication application(argc, argv);
MyMainWindow window;
window.show();
window.raise();
return application.exec();
}
myMainWindow.hpp
#include <QMainWindow>
#include <QStackedLayout>
class MyMainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MyMainWindow(QWidget *parent = nullptr);
~MyMainWindow(void){}
private:
QStackedLayout *layout;
public slots:
void switchToWidget(void);
};
myMainWindow.cpp
#include <QApplication>
#include <QDebug>
#include <QFrame>
#include <QMainWindow>
#include <QPushButton>
#include <QtQuickWidgets/QQuickWidget>
#include <QQuickItem>
#include <QQmlProperty>
#include "myMainWindow.h"
MyMainWindow::MyMainWindow(QWidget *parent) : QMainWindow(parent) {
// Central Widget
QFrame *central = new QFrame;
central->setFixedSize(300,300);
this->setCentralWidget(central);
// BUTTON - QWidget
QPushButton *buttonWidget = new QPushButton("WIDGET", central);
buttonWidget->setStyleSheet("QPushButton{"
" background-color: red;"
"}"
"QPushButton:hover{"
" background-color: yellow;"
"}");
// BUTTON - QQuickWidget
QQuickWidget *buttonQML = new QQuickWidget(central);
buttonQML->setResizeMode(QQuickWidget::SizeRootObjectToView);
buttonQML->setSource(QUrl("qrc:/qml/ButtonQML.qml"));
buttonQML->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// Main Stack Layout
this->layout = new QStackedLayout(central);
this->layout->addWidget(buttonWidget);
this->layout->addWidget(buttonQML);
this->layout->setCurrentIndex(0);
// Stack siwtch
QObject::connect(buttonWidget, &QPushButton::clicked, [=](){
this->layout->setCurrentIndex(1);
});
QQuickItem *rootObject = buttonQML->rootObject();
QObject::connect(rootObject, SIGNAL(clicked()), this, SLOT(switchToWidget()));
}
void MyMainWindow::switchToWidget(void) {
this->layout->setCurrentIndex(0);
}
ButtonQML.qml
import QtQuick 2.0
import QtQuick.Controls 2.1
Button {
text: "ButtonQML"
id: root
background: Rectangle {
color: root.hovered ? 'green' : 'white'
}
}
Any idea what could cause this ?
I found the problem. Seems that I had not read the QQuickWidget Documentation well enough.
Note: Using QQuickWidget disables the threaded render loop on all platforms. This means that some of the benefits of threaded rendering, for example Animator classes and vsync driven animations, will not be available.
I changed my QQuickWidget to a QQuickView and everything is working.

Maximize window maintaining taskbar limits

I want to create a Qt application without the windows title bar (I want to create a customized one).
I've created three buttons for minimizing, maximizing and closing the window. Everything works except for considering that when I maximize the window, application doesn't take into account the taskbar, and the maximized window takes the entire screen, going under the taskbar. A normal maximize command from windows instead maximizes the application window avoiding to go under the taskbar.
If I don't use the Qt::CustomizeWindowHint the window title bar appears, and maximizing behaviour is correct; but if I use this flag, the title bar disappears and the application goes under the window: here you can find two screenshots explaning the behaviour:
With Windows title:
Without Windows title:
As you can see in latter case che "Close" button goes inside the taskbar because the application takes the entire screen.
How can I avoid this behaviour without using windows title bar? I want to recreate the same behaviour as with the window title bar.
SampleWindow.h
#ifndef SAMPLEWINDOW_H_
#define SAMPLEWINDOW_H_
#include <QMainWindow>
#include <QPushButton>
#include <QHBoxLayout>
class SampleWindow : public QMainWindow {
Q_OBJECT
public:
SampleWindow();
virtual ~SampleWindow() = default;
};
#endif // !SAMPLEWINDOW_H_
SampleWindow.cpp
#include "SampleWindow.h"
#include <QCoreApplication>
SampleWindow::SampleWindow() :
QMainWindow() {
// With uncommenting this line the title bar disappears
// but application goes under the taskbar when maximized
//
//setWindowFlags(Qt::CustomizeWindowHint);
auto centralWidget = new QWidget(this);
auto layout = new QHBoxLayout(this);
auto minimizeButton = new QPushButton("Minimize", this);
auto maximizeButton = new QPushButton("Maximize", this);
auto closeButton = new QPushButton("Close", this);
layout->addWidget(minimizeButton);
layout->addWidget(maximizeButton);
layout->addWidget(closeButton);
centralWidget->setLayout(layout);
setCentralWidget(centralWidget);
connect(closeButton, &QPushButton::clicked, [=]() {QCoreApplication::quit();});
connect(minimizeButton, &QPushButton::clicked, this, [=]() {setWindowState(Qt::WindowMinimized);});
connect(maximizeButton, &QPushButton::clicked, this, [=]() {setWindowState(Qt::WindowMaximized);});
}
Main.cpp
#include <QApplication>
#include "SampleWindow.h"
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
SampleWindow mainWindow;
mainWindow.show();
return app.exec();
}
This behavior depends on system. I tested your code on Windows 7 and Linux Mint KDE and behavior was different. In Windows 7 taskbar has hidden and window has filled area of taskbar. In KDE I have noticed that window maximizes correctly (avoids widget panels and not hides them).
However when I try to run code in Windows 10 with compatibility mode, I was able to repeat behavior of Win7 only in compatibility with Windows Vista and older versions.
For Windows 10 I found another solution: you can maximize your window in fullscreen if that suits you:
mainWindow.showFullScreen();
or
setWindowState(Qt::WindowFullScreen);
UPD:
In addition to your solution I found another one:
setGeometry(QApplication::desktop()->availableGeometry().x(),
QApplication::desktop()->availableGeometry().y(),
QApplication::desktop()->availableGeometry().width(),
QApplication::desktop()->availableGeometry().height());
I think that I've found a solution by using this slot when maximize button is clicked:
void SampleWindow::maximize() {
//setWindowState(Qt::WindowFullScreen);
QDesktopWidget *desktop = QApplication::desktop();
QRect desktopGeometry = desktop->availableGeometry();
int desktopHeight = desktopGeometry.height();
int desktopWidth = desktopGeometry.width();
int padx = (frameGeometry().width() - geometry().width()) / 2;
setFixedSize(desktopWidth, desktopHeight);
move(-padx,0);
}
I need to test it more but at the moment the area seems correct.

Cannot press QPushButton in a simple program

Basically, I want a simple pushButton with a colorful text which when pressed exits the application.
Why cant I press PushButton in this simple program. I am using QT 4.6 on Arch x86_64.
#include <QtGui/QApplication>
#include <QLabel>
#include <QPushButton>
#include<QtGui>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow *Main=new QMainWindow;
QPushButton *button = new QPushButton(Main);
QLabel *label = new QLabel(Main);
label->setText("<h2><i>Hello</i> ""<font color=red>Qt!</font></h2>");
label->setVisible(true);
QObject::connect(button, SIGNAL(clicked()),label, SLOT(close()));
label->setAlignment(Qt::AlignCenter|Qt::AlignVCenter);
label->setWindowTitle("HelloWorld Test Program");
Main->show();
return a.exec();
}
Beside the use of a button class that will allow you to display rich text, you also need to make sure your connections are correct.
In your example, you're connecting the clicked signal of the button to the clear() slot of the label, which is non-sense.
To exit your app when the button is clicked, you need to close the main window. Here is the code to get the right connection :
QObject::connect(button, SIGNAL(clicked()),Main, SLOT(close()));
Changing this single line of code in your example is not enough, because your label is drawn on top of your button, so it's not possible to graphically click on it. You need to hide your label and put some text into your button :
button->setText("Hello");
label->setVisible(false);
Regarding the rich text feature in a QPushButton, AFAIK it is not possible to do it with a QPushButton.
UPDATE :
Here is a way to put some richtext on a QPushButton. It uses the solution described by my comment : painting a QTextDocument onto a pixmap and setting this pixmap as the button's icon.
#include <QtGui/QApplication>
#include <QLabel>
#include <QPushButton>
#include <QtGui>
#include <QTextDocument>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow *Main=new QMainWindow;
QPushButton *button = new QPushButton(Main);
QTextDocument Text;
Text.setHtml("<h2><i>Hello</i> ""<font color=red>Qt!</font></h2>");
QPixmap pixmap(Text.size().width(), Text.size().height());
pixmap.fill( Qt::transparent );
QPainter painter( &pixmap );
Text.drawContents(&painter, pixmap.rect());
QIcon ButtonIcon(pixmap);
button->setIcon(ButtonIcon);
button->setIconSize(pixmap.rect().size());
QObject::connect(button, SIGNAL(clicked()),Main, SLOT(close()));
Main->show();
return a.exec();
}
Take a look here. Widget called QwwRichTextButton.
The QwwRichTextButton widget provides a button that can display rich text.