I have a QMainWindow opening a QDialog (not modal). When I minimize the main window, the dialog is closed as well. Can I somehow keep it open? (the other way round as in Keep QMainWindow minimized when QDialogs show() ).
One thing I have tried is to ignore the event, but to my surprise I never see such a state. Actually I only see ActivationChange (0n99) there.
void CMyDialog::changeEvent(QEvent *evt)
{
QEvent::Type t = evt->type();
if (t == QEvent::WindowStateChange)
{
evt->ignore();
hide();
}
else
{
QDialog::changeEvent(evt);
}
}
Question in Qt center dealing with a similar topic:
http://www.qtcentre.org/threads/24765-Intercept-minimize-window-event
Here I create it as member:
QScopedPointer<MyDialog> m_navigator{new MyDialog(this)}; // this here is the main application window
It is displayed by a public slot:
void MyDialog::toogleNavigator()
{
this->setVisible(!this->isVisible());
}
and is a QDialog derived class:
class MyDialog : public QDialog { ...
---- Edit 2 ------
First Wouter has mentioned it , then Alexander. It is like you guys say, if I pass no parent (as in Alexander`s minimal example), the dialog stays open - with parent it is minimized along with its parent. My apologizes Wouter.
However, in my case it does not work like that. So I did not turn Wouter`s comment without checking or in bad intention. Now it is my duty to find out why. I suspect some utility classes to alter the dialog. I will report back here when I have found the root cause.
Ok, it is the windows flags. If the dialog is a tool window, it is always minimized, as normal window it depends on the parent.
Try to create MyDialog without this(MainApplication) like parent
and may be play with a second parameter of the constructor.
new MyDialog(0/*, ?*/);
Addition It is working code
MainWindow.cpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QScopedPointer<Dialog> dialog;
};
MainWindow.hpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
dialog(new Dialog(0))
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
dialog->setVisible(!dialog->isVisible());
}
Related
Minimal code example:
class Boo : public QPushButton{
public:
Boo(QWidget* w) : QPushButton(w){}
virtual void paintEvent(QPaintEvent* ev){
qDebug()<<__FUNCTION__<<this->objectName();
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
virtual void paintEvent(QPaintEvent* ev){
qDebug()<<__FUNCTION__;
}
private:
QTimer t;
Ui::MainWindow *ui;
Boo *b1, *b2;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
b1 = new Boo(this);
b1->setObjectName("O1");
b2 = new Boo(this);
b2->setObjectName("O2");
connect(&t, &QTimer::timeout, this, [this](){
b2->repaint();
});
t.start();
t.setInterval(10);
}
Outputs infinitely: MainWindow::paintEvent Boo::paintEvent "O1" Boo::paintEvent "O2"
but i call repaint only for button b2. Using "QWidget::repaint(int x, int y, int w, int h)" or :QWidget::update()" also invoke repaint for main window.
Problem exists on Qt5.12/5.15 and windows11, but looks like general qt bug.
This issue cause high GPU consumption in our more complex GUI application.
Ok. I found the reason myself, after add:
...
b1->setGeometry(15,15, 12, 12);
...
b2->setGeometry(35,35, 14, 14);
...
virtual void paintEvent(QPaintEvent* ev){
qDebug()<<__FUNCTION__<<ev->rect();
}
ouput says, that actual repaint is done only for button region:
MainWindow::paintEvent QRect(35,35 14x14)
Boo::paintEvent "O2" QRect(0,0 14x14)
Important thing: ivoke setGeometry for button, not just add as child. In other case (usable only for toy example, ofcourse) repaint button with unset geometry will lead to repaint every button with unset geometry.
Qt repaints by default all parents too, to allow widgets being partially transparent. If Qt doesn't repaint the parent on an update, some pixels of the previous paintEvent may still be visible.
Qt provides two methods to optimise this behaviour when no transparent background is needed:
Set autoFillBackground. This option is preferred in case of an opaque background color.
Set Qt::WA_OpaquePaintEvent to indicate that you will paint the whole widget (with opaque colors).
More information
Qt documentation: QWidget: Transparency and Double Buffering
I have been trying to add a QToolBar inside a QTabWidget in order to achieve something like the image below, so that everytime I add a new QTabWidget I have also a related QToolBar inside it.
Everything seems to work fine, I create a QAction to link it to the QTabWidget and according to this post it seems to be possible to do that but the problem is that when I compile nothing shows up as shows below:
Below is what I have done so far:
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void onChangeTab(int index);
void newTab();
void closeTab(const int &index);
private slots:
void on_addTabBtn_clicked();
void on_tabWidget_tabCloseRequested(int index);
private:
Ui::MainWindow *ui;
QAction *addTab1;
QToolBar *mToolBar1;
QAction *addIconToolBar1;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->tabWidget->clear();
ui->tabWidget->setTabsClosable(true);
ui->tabWidget->addTab(new QLabel("Add"), QString("Add"));
ui->toolBar->setContextMenuPolicy(Qt::ActionsContextMenu);
mToolBar1 = new QToolBar;
addIconToolBar1 = new QAction;
addIconToolBar1->setIcon(QIcon("qrc:/cardio.png"));
ui->toolBar->addWidget(mToolBar1);
ui->toolBar->addAction(addIconToolBar1);
connect(ui->addTabBtn, &QPushButton::clicked, this, [&] { ui->tabWidget->addTab(new QLabel("Add"), QString("Add")); });
connect(ui->tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_addTabBtn_clicked()
{
int index = 0;
if(index == this->ui->tabWidget->count() - 1) {
newTab();
}
}
void MainWindow::on_tabWidget_tabCloseRequested(int index)
{
ui->tabWidget->removeTab(index);
}
I tried to solve the problem in many ways and researched what the cause might be. I came across several references such as this one, which is the most important I found as the user seems to be doing it but there is no reference to documentation or no example to code to understand/study through.
Thanks for pointing to the right direction to solve this issue.
You can simply do something like this, and it really works.
QToolBar *toolbar=new QToolBar("toolbar",ui->tab);
toolbar->addAction("action1");
toolbar->addAction("action2");
enter image description here
I don't see where you are trying to add Toolbar to your TabWidget...
You must define Layout, add your toolbar to that layout and finally set layout to your tabWidget.
Try to do something like this, in your mainwindow constructor.
QHBoxLayout* tabWidgetLayout = new QHBoxLayout;
tabWidgetLayout->addWidget( your toolbar);
tabwidget->setLayout(tabWidgetLayout);
Also don't forget to include QHBoxLayout's header.
Even if other answers may seem to work, this is actually the right way to do what you asked for.
EDIT and Some Self Critisicm
I tried both given solutions, which both solved my problem, and thus I thank you both! I marked the transparent solution as accepted because I thought it is was the easiest implementation when I only had one child widget, but I wish to share some insight for other beginners:
I first used QLabel, which apperently has enabled Qt::WA_TransparentForMouseEvents by default and thus obviously worked, but I also wanted the text to be selectable, by using QPlainTextEdit instead. Laughably, this is not possible in any way, because if you try to select the text (by clicking) you will close the window! I ended up keeping the transparancy, and neglecting the select-text-feature.
I'm guessing my following question has been answered somewhere before, but after an hour of searching I now post the question myself. I'm grateful if someone can point me to an already answered question that solves my problem.
Anyhow, I'm creating a popup window, using C++ and Qt. I've created the following PopupDialog class which works well and fine for all its purposes. However, I've removed its frame (including the bar containing the close button and window title) to make it look minimalistic, and now I want it to close if the user presses/releases the mouse button anywhere inside the popup window (dialog).
The below code works, however in such a way that I have to click and release the mouse exactly at the QDialog-window itself. It will not close when i click if the mouse hovers over the child widget(s) inside the QDialog, e.g. a QPlainTextEdit, which is displaying text.
Hence, I'm in need of a solution for QDialog to recognize QMouseEvents inside its child widgets. Please, don't hesitate to ask if something is unclear. I have not included my mainwindow.h/.cpp files, or popupdialog.ui file since I believe it would be a little too much to post here, but the .ui extremely simple: Just the QDialog window holding a QBoxLayout, containing a single widget, a QPlainTextEdit. I may posts these on request if it helps.
// popupdialog.h
#ifndef POPUPDIALOG_H
#define POPUPDIALOG_H
#include <QDialog>
#include <QString>
namespace Ui {class PopupDialog;}
class PopupDialog : public QDialog
{
Q_OBJECT
public:
explicit PopupDialog(QWidget *parent = 0, QString msgTxt="");
~PopupDialog();
private:
Ui::PopupDialog *ui;
QString messageText;
void mouseReleaseEvent(QMouseEvent*);
};
#endif //POPUPDIALOG_H
...
// popupdialog.cpp
#include "popupdialog.h"
#include "ui_popupdialog.h"
PopupDialog::PopupDialog(QWidget *parent, QString msgTxt) :
QDialog(parent),
ui(new Ui::PopupDialog),
messageText(msgTxt)
{
ui->setupUi(this);
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
setModal(true);
ui->message_text_display->setText(messageText);
// The message_text_display is an instance of the class,
// "PlainTextEdit". Using "QLabel" partly solves my
// problem, but does not allow text selection.
}
PopupDialog::~PopupDialog()
{
delete ui;
}
void PopupDialog::mouseReleaseEvent(QMouseEvent *e)
{
this->close();
}
As you already noticed mouse events are handled from child widgets and propagated to parents if not accepted. You can read more about it here
To close your popup window when the click is done inside a child widget you can do two things. You could try looking into installEventFilter and set it up on each child widget to call close().
Another option would require you to have a kind of centralWidget (like the MainWindow usually has) - just to group all your child widgets. This way you could call setAttribute() on it to set Qt::WA_TransparentForMouseEvents property to simply skip handling mouse events on the widget and all of its children.
groupWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
According to Qt docs:
When enabled, this attribute disables the delivery of mouse events to
the widget and its children. Mouse events are delivered to other
widgets as if the widget and its children were not present in the
widget hierarchy; mouse clicks and other events effectively "pass
through" them. This attribute is disabled by default.
Which basically means the event would be passed up the chain to the first widget which can handle the event. In your case it would be the PopupDialog and the already overriden mouseReleaseEvent slot.
in header file
class PopupDialog : public QDialog
{
Q_OBJECT
public:
explicit PopupDialog(QWidget *parent = 0, QString msgTxt="");
~PopupDialog();
//////////////////////////////////
protected:
bool eventFilter(QObject *obj, QEvent *event);
//////////////////////////////////////
private:
Ui::PopupDialog *ui;
QString messageText;
void mouseReleaseEvent(QMouseEvent*);
};
in cpp
PopupDialog::PopupDialog(QWidget *parent, QString msgTxt) :
QDialog(parent),
ui(new Ui::PopupDialog),
messageText(msgTxt)
{
ui->setupUi(this);
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
setModal(true);
ui->message_text_display->setText(messageText);
// The message_text_display is an instance of the class,
// "PlainTextEdit". Using "QLabel" partly solves my
// problem, but does not allow text selection.
///////////////////////////////////////
foreach (QObject *child, children())
{
child->installEventFilter(this);
}
///////////////////////////////////////
}
///////////////////////////////////////
bool PopupDialog::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonRelease)
{
this->close();
}
}
i want to build a QT application for windows/android.
I have one mainwindow in which i have different buttons to call submenus, as you can see on the pictures. What do you think is the best implementation for that?
I thought about replacing the central widget
Using QStackwidget
Or open a new widget on the same position and size and close after.
What do you think? Do you have some favourite implementation or do i miss some important?
Edit
My Mainwindow constructor
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_stacked = new QStackedWidget;
HomeScreen *homescreen=new HomeScreen(m_stacked);
Control *manual=new Control(m_stacked);
m_stacked->addWidget(homescreen);
m_stacked->addWidget(manual);
this->setCentralWidget(m_stacked);
}
Implementation of every Screen
class HomeScreen : public QWidget
{
Q_OBJECT
public:
explicit HomeScreen(QStackedWidget* stack, QWidget *parent = 0);
~HomeScreen();
private slots:
void on_pushButton_clicked();
private:
Ui::HomeScreen *ui;
QStackedWidget *m_stacks;
};
HomeScreen::HomeScreen(QStackedWidget* stack,QWidget *parent) :
m_stacks(stack),QWidget(parent),
ui(new Ui::HomeScreen)
{
ui->setupUi(this);
}
HomeScreen::~HomeScreen()
{
delete ui;
}
void HomeScreen::on_pushButton_clicked()
{
m_stacks->setCurrentIndex(1);
}
What is your solution to change the current Widget, inside a widget of the QStackwidget? I used the solution above what do you think about it?
QStackedWidget was made right for that, I don't see any reason why you should incline to other options. You can always removeWidget(widget) and delete widget; if you want to free the memory of some rarely used menu.
Edit:
Your code is fine, there can be only a few enhancements made. You can create enum for your indexes in some separate header file.
Option 1:
If you use only QStackedWidget as a parent menu, you can adjust the constructor:
HomeScreen(QStackedWidget* parent) :
QWidget(parent),
ui(new Ui::HomeScreen)
{
ui->setupUi(this);
}
If you'd want to access QStackedWidget and change an index, you spare m_stacks pointer and use the parent: dynamic_cast<QStackedWidget*>(parent())->setCurrentIndex(1 /* or enum */);.
Option 2:
Leave index changing to the parent MainWindow. Each menu will have a request signal:
signals:
void goToMenuRequest(int index /* or enum */);
which you connect to m_stacked's setCurrentIndex in the constructor of MainWindow.
Not a big things, but It'll make coding easier.
In Qt Creator, I have a couple of widgets declared like so:
Header File:
class MapViewer : public QGraphicsView
{
Q_OBJECT
public:
explicit MapViewer(QGraphicsScene *scene, QWidget *parent = 0);
~MapViewer();
public slots:
void mousePressEvent(QMouseEvent *event);
};
// Declaration for the map editor window.
class MapEditor : public QMainWindow
{
Q_OBJECT
public:
explicit MapEditor(QWidget *parent = 0);
~MapEditor();
public:
QLayout *editorLayout;
QPushButton *btn;
QGraphicsScene *mapScene;
MapViewer *mapView;
private:
Ui::MapEditor *ui;
};
CPP File:
MapEditor::MapEditor(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MapEditor)
{
ui->setupUi(this);
this->setWindowTitle("2DXY :: Map Editor");
this->setGeometry(10,10,1170,750);
editorLayout = new QVBoxLayout; // Create a new layout
this->setLayout(editorLayout); // Set the widget's layout to our newly created layout.
mapScene = new QGraphicsScene(); // Create a new graphics scene to draw upon.
mapView = new MapViewer(mapScene,this); // Create a new graphics view to display our scene - set its parent to 'this' so that it doesn't open in a new window.
mapView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mapView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mapView->setGeometry(20,20,1178,546); // Width first, then height.
AND:
void MapViewer::mousePressEvent(QMouseEvent *event)
{
// Show an empty message box, just to check that the event handler works!
QMessageBox *notification = new QMessageBox();
notification->show();
notification->exec();
// Some how access the same QGraphicsScene and View (mapScene, mapView) as above, so
// I can update their contents on the open form / window.
}
And as you can see, I wish to access the Graphics Scene again to update it, then redraw it (or whatever). But I'm not able to access the graphics scene at all, despite a few hours of trial and error with declaring widgets in different ways.
I know that the listener itself works, because if it's set to open a new message box, or output to the debug window, then it works, it's just that I can't access the widgets I've already defined.
I feel that there is a (relatively) easy solution to this problem, and that I'm just missing something obvious.
You passed the QGraphicsScene to your MapRender object's constructor. What do you do with the scene within its constructor? Ideally, you should be storing it as a data member of MapRender. For example:
class MapRender {
public:
MapRender(QGraphicsScene* scene)
: scene_(scene)
{
}
public slots:
void mousePressEvent(QMouseEvent *event);
private:
QGraphicsScene* scene_;
}
Now in your implementation of mousePressEvent, you can access to the scene member:
void MapRender::mousePressEvent(QMouseEvent *event) {
int CursorX = event->globalX();
int CursorY = event->globalY();
QGraphicsRectItem *clickedBox = new QGraphicsRectItem(40,40,32,32);
clickedBox->setBrush(QBrush(Qt::blue));
scene_->addItem(clickedBox);
}
Keep in mind you should ideally be putting the implementation of the constructor in your cpp file, but my example does it in the declaration for brevity.
void MapViewer::mousePressEvent(QMouseEvent *event)
{
// Show an empty message box, just to check that the event handler works!
QMessageBox *notification = new QMessageBox();
notification->show();
notification->exec();
// To add something whenever the user clicks, you don't need the view,
// just the scene.
scene()->addItem( new MyItem() );
}
Remember MapViewer derives from QGraphicsView and the view must know about the scene it belongs to - so it has a scene() method to return it, which you inherited.