Qt: How to have QDialog follow parent - c++

I hope this has not been asked before, but I have not found any thread that answers my specific question.
I have created my own QDialog class(TagPopup), which accurately displays the layout I have specified in its Constructor. The QDialog is created in the center of the parent - as expected - but when I move the parent the Dialog stays at its original position. I want it to always stay at the center of the parent widget regardless of where it moves.
The parent is a custom class derived from QMainWindow (PlayerWindow).
WindowFlags of the parent class have not been altered, but the child has to be modal, has to have Qt::WA_TranslucentBackground as an attribute and the Qt::FramelessWindowHint has to be set.
Is this only solvable by overriding the parents move event and then have the child move with it or is there a more elegant solution?
void PlayerWindow::open_tag_dialog(int id) {
TagPopup *dialog = new QDialog(this, GameTag::int_to_tag_type(id), (int) 1000000 * elapsed_seconds(), {this->clip->game->home_team, this->clip->game->away_team});
dialog->show();}
This is the method on the parent Widget which creates and shows the Dialog.
explicit TagPopup(QWidget *parent = 0, TagType tag_type = TagType::CUSTOM, int time = 0, vector<Team*> teams = {}, int min_height = 360) : QDialog(parent, Qt::FramelessWindowHint), BasePlayer(min_height, this){
setModal(true);
setAttribute(Qt::WA_TranslucentBackground);
Qt::WindowFlags flags = windowFlags();
setWindowFlags(flags | Qt::WindowStaysOnTopHint);
These are the first lines of the child constructor, which deal with setting the flags and attributes.
I have definitely tried removing all flags, but that also does not help.
Is QDialog for creating something like a popup? It seemed like the obvious pick. Adding the Qt::Popup flag didn't change the behaviour. It instead broke the whole layout when I passed it to the constructor and didn't add any other flags. Therefore didn't meddle with that idea for too long.

Related

What are `(QWidget* pobj=0)` and `(QWidget* pwgt/*=0/)` for?

I have just started to learn QT. Can't understand how do theese constructors work. For example:
//Progress.h
#include<QtWidgets>
class QProgressBar;
class Progress:public QWidget{
Q_OBJECT
private:
QProgressBar* m_pprb;
int step;
public:
Progress(QWidget* pobj=0);
public slots:
void slotStep();
void slotReset();
//
//Progress.cpp
#include<QtWidgets>
#include"Progress.h"
Progress::Progress(QWidget* pwgt/*=0*/):QWidget(pwgt)
{
//some buttons
}
So, the question is, what happens in constructors?
What you are creating is a Progressclass which inherits from QWidget.
The QWidget class can take an parentargument, if you look at the documentation:
Constructs a widget which is a child of parent, with widget flags set to f. If parent is nullptr, the new widget becomes a window. If parent is another widget, this widget becomes a child window inside parent. The new widget is deleted when its parent is deleted.
This parentin your code is called pwgt (I would think it stands for parentWidget)
What you do, is creating a default argument for your constructor to be set automatic to 0:
Progress(QWidget* pobj=0);
Cleaner maybe would be (for convenience with Qt standard):
Progress(QWidget* parent=nullptr);
So, why do do you need the constructor to look like this?
It is the same reason, which stands for QWidget:
You can set a parent widget, but you don't have to!
The QWidget class will deal for you with this, either you set a parent or not.

why isVisible does not work in QWidget child

I try to create a custom widget like this:
device.h
class Device : public QWidget
{
Q_OBJECT
public:
explicit Device(QWidget *parent = 0);
};
device.cpp
Device::Device(QWidget *parent) :
QWidget(parent)
{
setGeometry(QRect(0, 0, 100, 100));
setStyleSheet(QString::fromUtf8("background: black;"));
raise();
setVisible(true);
qDebug() << "is visible: " << isVisible();
}
The constructor tries to create the square widget with black background. But I see nothing on my MainWindow and have output like:
is visible: false
It seems I create the device object correctly (ui->centralWidget is parent):
// MainWindow constructor
// device and button pointers defined in mainwindow.h
device = new Device(ui->centralWidget);
button = new QPushButton("Push me!", ui->centralWidget);
I think I could see black square with button or only black square overlaped the button. But I see only the button without any square.
It is not clear for me even because I call setVisible(true) and get isVisible() as false in the next line. But the button (the same child of QWidget) is visible. Where is difference?
The parent widget is likely invisible. Order of events matter, if the parent becomes visible after the constructor of Device, then the isVisible will work as intended when called from a different function after it is displayed. Otherwise, if the parent widget is hidden, all of it's children widgets are also hidden (even if you explicitly state otherwise for the children widgets). When do you call show() on the parent widget? Without a Minimal, Complete, and Verifiable Example
we can only speculate.

QDockWidgets merging incorrectly

I have a QDockWidget class and a QMainWindow:
// docker.hpp
class Docker : public QDockWidget
{
Q_OBJECT
public:
Docker(QString title, QWidget* parent = 0);
}
// docker.cpp
Docker::Docker(QString title, QWidget* parent): QDockWidget(title, parent)
{
QWidget* widget = new QWidget(this);
widget.setMinimumSize(200, 200);
setWidget(widget);
widget->setStyleSheet("border:5px solid gray;");
setAllowedAreas(Qt::AllDockWidgetAreas);
}
// mainwindow.hpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent);
private slots:
void createDockers();
};
// mainwindow.cpp
MainWindow::MainWindow(QWidget* parent): QMainWindow(parent)
{
setDockOptions(dockOptions() |
QMainWindow::AllowTabbedDocks |
QMainWindow::GroupedDragging);
// The following line of code does not change the situation.
// setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
// There are some other codes which connect a button to the void createDockers() method
}
void createDockers()
{
Docker* dock = new Docker("Docker", this);
dock->setFloating(true);
dock->show();
}
I am able to create two Dockers with clicks of the button mentioned above.
However, when I drag one QDockWidget onto the other, the border disappears and no tabs show up:
I am expecting the following to happen: (Achieved by spawning several QDockWidgets)
I am also noticing that one of the QDockWidgets did not vanish. Instead, it merged back to the MainWindow. This only happens if they are the "first two" QDockWidgets.
What caused this problem and how to solve it? I am trying to mimic this project.
I guess it's linked to the QMainWindow::GroupedDragging option. I'm pretty sure it should work well without it (I mean for the not showing tab issue). Do you have restrictions on dock position somewhere else? The documentation implies it could create issues: http://doc.qt.io/qt-5/qmainwindow.html#DockOption-enum
For the style issue, you may need to redefine it on tab event, because once tabbed, the widget may inherit the tab style instead of the dock widget style you defined (not certified at all ^^)
Last guess/thing you can try, is to start with the dock tabbed and not floating to see if you have any new bahaviour, it was what I was doing in a previous project and it was working pretty well.
Sorry but no other ideas for the moment.

QDialog not positioned correctly

I have a problem with QDialog which is not displayed centered on the parent window.
The following snippet explains it:
void MyWidget::showDialog() {
QObject* p = parent();
while (p!=0) {
qDebug() << p;
p = p->parent();
}
qDebug() << QApplication::activeWindow();
MyClassDerivedFromQDialog dlg( this );
if ( dlg.exec() != dlg.Accepted ) {
return;
}
... do something
}
The output on qDebug is the following
QSplitter(0x2d89930, name = "splitter")
MyWidget(0x2d89670, name = "widget")
MainWindow(0x27ef20, name = "application")
MainWindow(0x27ef20, name = "application")
Executing my example opens the dialog somewhere on the screen. Passing QApplication::activeWindow() as a parent to the dialogs constructor results in a dialog centered on the main window. So why is that and how to track down the problem?
I found that this behavior is related to the timing of the dialog creation.
If you create a QDialog (or a derived class) before the dialog parent is shown (e.g., in the parent constructor), the dialog is displayed at a non-predictable location (or at least, not where you'd expect it to show).
However, if the dialog is created after the parent is displayed, then you get the expected behavior.
For example, if you have a button invoking your dialog. Both the button and the dialog are children of the same widget, so the dialog parent is the same as the button parent. In this case, it is advised to delay the dialog creation until the button is clicked, not before that.
This way, you ensure the dialog is created only after the parent is shown.
I am not sure if understand your problem.
QDialogs are always centered on the widget you pass as parent. This is by design. So if you pass "activeWindow()" as parent it is centered on the active window. If you pass "this" as a parent the dialog is centered above MyWidget.
In which way does your dialog not respect these rules?
The Dialog class is instantiated via
MyClassDerivedFromQDialog::MyClassDerivedFromQDialog(QWidget *parent)
: QDialog(parent),
ui(new Ui::MyClassDerivedFromQDialog)
{
ui->setupUi(this);
//remove the ? button in titlebar
Qt::WindowFlags flags = windowFlags();
Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint;
flags = flags & (~helpFlag);
setWindowFlags(flags);
}
And i always use it as in the showDialog function in the initial post. sometimes it works... And no, i do not have two MyWigets which are parents of each other.

QGraphicsView size in a GridLayout

I have found this: Getting the size of a QGraphicsView
But I can't figure out what does it mean to "move my initialization code to showEvent" and I can't comment on that answer.
I am want to resize a QPixmap so it could fit my QGraphicsView. I've placed my graphicsview in Designer and set GridLayout for my main window. In a MainWindow constructor I have written the following code:
ui->setupUi(this);
// Get GView size
g_sizeX = ui->mapView->width();
g_sizeY = ui->mapView->height();
// Init scene
scene = new QGraphicsScene(this);
// Init MAP pixmap and add it to scene
mapImage = new QPixmap(":/Map/europe.jpg");
QPixmap newmapImage = mapImage->scaled(g_sizeX, g_sizeY);
scene->addPixmap(newmapImage);
// Display scene in gview.
ui->mapView->setScene(scene);
But I always get size of 100x30. If I break the gridLayout, I get the correct size.
So, how should I deal with this?
Thank you.
The QGraphicsView will be resized by the QGridLayout after the widget is shown, and can be also resized later when the window is itself resized.
So you should change the size of the pixmap as a result of a QResizeEvent, either by subclassing QGraphicsView to redefine resizeEvent(), and then promoting your view object to your new class in the designer to use it instead of QGraphicsView, or by installing your MainWindow object as an event filter for the view to handle to the resize event from the MainWindow::eventFilter function.
You probably don't want to change the pixmap size in the scene, but rather adjust the view matrix so that your QGraphicsPixmapItem fits perfectly inside the view, with QGraphicsView::fitInView.
For example:
/* QGraphicsPixmapItem *pixmapItem; as a MainWindow member */
pixmapItem = scene->addPixmap(newmapImage);
/* Either always disable or enable the scrollbars (see fitInView doc) */
ui->mapView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->mapView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->mapView->installEventFilter(this);
...
bool MainWindow::eventFilter(QObject *obj, QEvent *evt) {
if(obj == ui->mapView && evt->type() == QEvent::Resize) {
ui->mapView->fitInView(pixmapItem, Qt::KeepAspectRatioByExpanding);
}
// Call the base class implementation
return QMainWindow::eventFilter(obj, evt);
}
I believe that what is happening is that Qt only applies layouts and sets widget sizes when the widget is first displayed.
One way to work that is to override QWidget::showEvent(), and put your sizing code in there.
However, one simpler way, that often works in constructors, is to ask the widget for its sizeHint(), rather than for its not-yet-layed-out size.
In your case, that would mean changing two lines of code to:
g_sizeX = ui->mapView->sizeHint().width();
g_sizeY = ui->mapView->sizeHint().height();
If your layout isn't too complicated, and if you haven't overridden the default size policies, this may well fix things for you.