Is it expected behaviour that QDialog::show() does not show the window if a parent has been set?
Background: I want to use QMetaObject::connectSlotsByName() to react to one of the dialogs signals, which means the parent-object needs to own it. Without the line marked as "This is the line in question" I get a runtime-message "QMetaObject::connectSlotsByName: No matching signal for on_child_accepted()". But with the line, the child dialog no longer appears.
#include <QtCore/QDebug>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDialog>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QVBoxLayout>
class Parent : public QDialog
{
Q_OBJECT
public:
Parent(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())
: QDialog{parent, f}
{
b.setText(tr("Show child"));
connect(&b, &QPushButton::clicked, [&]() {
c.show();
});
l.addWidget(&b);
setLayout(&l);
c.setParent(this); // This is the line in question
c.setObjectName("child");
QMetaObject::connectSlotsByName(this);
}
private slots:
void on_child_accepted()
{
qDebug() << "I got called";
}
private:
QPushButton b;
QDialog c;
QVBoxLayout l;
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Parent w;
w.show();
return a.exec();
}
This test fails on Qt 5.11 for Windows, from the MSYS2 64-bit build.
Any suggestions? Thank you in advance.
The dialog did actually appear, but not where you think it did: it is a non-window child widget of your window - it was transparent and yet it obscured most of the "Show Dialog" button, consuming all mouse events, and furthermore it was already shown since all children are shown when the parent is shown - so the button appeared to be non-functional for both of those reasons.
Setting a widget's parent clears its Qt::Window flag. Setting the dialog's background helps visualize the problem. Thus, you need to make the dialog a window after setting its parent.
The following reproduces your bug, and also demonstrates the fix.
// https://github.com/KubaO/stackoverflown/tree/master/questions/dialog-show-parenting-53208641
#include <QtWidgets>
class Parent : public QDialog {
Q_OBJECT
QVBoxLayout layout{this};
QDialog child;
QPushButton cShow{tr("Show child")}, cNonWindow{tr("Renew non-window child")},
cWindow{tr("Renew window child")};
Q_SLOT void on_child_accepted() {}
void reChild(bool makeWindow) {
child.~QDialog();
new (&child) QDialog;
Q_ASSERT(child.isWindow());
child.setParent(this);
child.setObjectName("child");
child.setStyleSheet("QWidget { background: blue }");
if (makeWindow) {
child.setWindowFlag(Qt::Dialog);
Q_ASSERT(child.isWindow());
} else {
Q_ASSERT(!child.isWindow());
child.show(); // The child gets shown when we're shown
}
QMetaObject::invokeMethod(this, &Parent::updateChild, Qt::QueuedConnection);
}
void updateChild() {
if (!child.isWindow()) child.move(50, cWindow.y() + cWindow.height() / 2);
this->update(); // Work around a refresh bug (affects OS X on 5.11 at least)
}
public:
Parent(QWidget *parent = nullptr, Qt::WindowFlags f = {}) : QDialog{parent, f} {
connect(&cShow, &QPushButton::clicked, [&]() { child.show(); });
connect(&cNonWindow, &QPushButton::clicked, [&] { reChild(false); });
connect(&cWindow, &QPushButton::clicked, [&] { reChild(true); });
for (auto *w : {&cShow, &cNonWindow, &cWindow}) layout.addWidget(w);
cNonWindow.click();
QMetaObject::connectSlotsByName(this);
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Parent w;
w.show();
return a.exec();
}
#include "main.moc"
Related
I'm trying to change the background color of a QMenu* object it does not work with setStyleSheet(), nor with setPalette().
I read this article but the guy says that I should add this line:
app.setStyle(QStyleFactory::create("fusion"));
I'm not sure what is app, I tried several combinations but it does not work.
Thanks!
You probably forgot to set QMenu's parent:
#include <QtGui>
class Window : public QWidget
{
public:
Window(QWidget *parent = 0) : QWidget(parent) {}
void contextMenuEvent(QContextMenuEvent *event)
{
QMenu menu(this);
menu.addAction(new QAction("Item 1", this));
menu.addAction(new QAction("Item 2", this));
menu.exec(event->pos());
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window *window = new Window;
window->setStyleSheet("QMenu::item:selected { background-color: green; }");
window->show();
return app.exec();
}
Item.h:
class Item:public QGraphicsRectItem
{
public:
Item(const QRectF & rect, QGraphicsItem * parent = 0);
void mousePressEvent(QGraphicsSceneMouseEvent * event);
void mouseMoveEvent(QGraphicsSceneMouseEvent * event);
~Item();
};
Item.cpp:
Item::Item(const QRectF & rect, QGraphicsItem * parent)
:QGraphicsRectItem(rect, parent)
{
}
void Item::mousePressEvent(QGraphicsSceneMouseEvent * event){
qDebug("press");
QGraphicsRectItem::mousePressEvent(event);
event->accept();
}
void Item::mouseMoveEvent(QGraphicsSceneMouseEvent * event){
qDebug("move");
}
Item::~Item()
{
}
main.cpp:
#include "mainwindow.h"
#include <QApplication>
#include "QGraphicsView"
#include "QGraphicsScene"
#include "item.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// MainWindow w;
// w.show();
auto item = new Item(QRectF(QPointF(),QSize(100,150)));
auto scene = new QGraphicsScene;
scene->addItem(item);
auto view = new QGraphicsView(scene);
view->setMinimumSize(QSize(800,600));
view->scene()->setSceneRect(QRectF(QPointF(),view->size()));
view->show();
return a.exec();
}
I have read the doc about QGraphicsItem::mousePressEvent, which says "If you do reimplement this function, event will by default be accepted (see QEvent::accept()), and this item is then the mouse grabber. This allows the item to receive future move, release and doubleclick events." Now I have reimplemented it. Why doesn't it work?
When the mouse event is created it is set as accepted. Depending on the flags you have set to your item, the default mousePressEvent implementation might reject it.
In your case you are calling the default implementation which might reject the mouse press event. If that is the case, the mouseMoveEvent will never be called.
You should read this interesting article about events.
Enable flags to use the functionalities
item ->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
item ->setAcceptHoverEvents(true);
item ->setAcceptedMouseButtons(Qt::LeftButton);
I have two items of type QGraphicsRectItem. One over the other.
The first one is a custom class called Wall. Inside the wall there are windows and doors.
In fact, i have a list of Doors and Windows inside this custom Wall class.
The Doors are Items too, and are drawn inside the wall.
When i move the mouse over the door, the hover function of the wall is emited, but the hover of the door is not. Both of them correctly copied one from each other as virtual void protected.
Why is that happening? How can i make the door and window items realize about the hover?.
I have tried with a custom QGraphicsItem instead of a custom QGraphicsRectItem. It seems the hover event handler is successfully called for both Wall and Door. This is happening when explicitly setting QGraphicsItem with setAcceptHoverEvents(true). This is not tested with custom QGraphicsRectItem.
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QPainter>
class Item : public QGraphicsItem
{
QBrush m_brush;
public:
explicit Item(bool nested = true, QGraphicsItem* parent = 0) : QGraphicsItem(parent), m_brush(Qt::white)
{
if (nested) {
Item* item = new Item(false, this);
item->setPos(10,10);
m_brush = Qt::red;
}
setAcceptHoverEvents(true);
}
QRectF boundingRect() const
{
return QRectF(0,0,100,100);
}
void hoverEnterEvent(QGraphicsSceneHoverEvent *)
{
m_brush = Qt::red;
update();
}
void hoverLeaveEvent(QGraphicsSceneHoverEvent *)
{
m_brush = Qt::white;
update();
}
void paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
{
p->setBrush(m_brush);
p->drawRoundRect(boundingRect());
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene scene;
scene.addItem(new Item);
QGraphicsView view;
view.setScene(&scene);
view.setMouseTracking(true);
view.show();
return app.exec();
}
Consider a QWidget, normally a child in some Layout.
Supposed I want to make it fullScreen for a while, then have it return to it's old spot.
QWidget::setFullScreen() requires that the widget needs to be an independent window - any ideas how to work it out?
The simplest way I can see is to reparent to 0. Something like this:
#include <QApplication>
#include <QPushButton>
class MyButton : public QPushButton
{
public:
MyButton(QWidget* parent) : QPushButton(parent) {}
void mousePressEvent(QMouseEvent*) {
this->setParent(0);
this->showMaximized();
this->show();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget mainWidget;
MyButton button(&mainWidget);
mainWidget.show();
return a.exec();
}
I have modified the previous example. The previous example never goes back to normal screen.
Just copy paste the code and it will run.
#include <QApplication>
#include <QPushButton>
class MyButton : public QPushButton
{
public:
MyButton(QWidget* parent) : QPushButton(parent) {
m_pParent = parent;
maxMode = false;
}
QWidget * m_pParent;
bool maxMode;
Qt::WindowFlags m_enOrigWindowFlags;
QSize m_pSize;
void mousePressEvent(QMouseEvent*) {
if (maxMode== false)
{
m_enOrigWindowFlags = this->windowFlags();
m_pSize = this->size();
this->setParent(0);
this->setWindowFlags( Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
this->showMaximized();
maxMode = true;
}
else
{
this->setParent(m_pParent);
this ->resize(m_pSize);
this->overrideWindowFlags(m_enOrigWindowFlags);
this->show();
maxMode = false;
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget mainWidget;
MyButton button(&mainWidget);
mainWidget.show();
return a.exec();
}
I want to make the background of a QDialog transparent so that I can see through the window. I'm asking because I want to use a semi-transparent background image that creates a "rounded corner window" illusion. Using setOpacity is not an option for me because I want all widgets to remain at full opacity.
Is there a way to achieve this without resorting to native OS APIs?
Use QWidget::setAttribute(Qt::WA_TranslucentBackground);. Note that this also requires Qt::FramelessWindowHint to be set.
This example works for me:
#include <QtGui>
class Dialog : public QDialog
{
public:
Dialog() : QDialog(0, Qt::FramelessWindowHint) // hint is required on Windows
{
QPushButton *button = new QPushButton("Some Button", this);
setAttribute(Qt::WA_TranslucentBackground);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog d;
d.show();
return a.exec();
}
Regarding rounded corners, QWidget::setMask() will help you.
EDIT: In response to another question below in the comments, here is a working example that uses an image in a resources file, and that overrides QWidget::paintEvent():
#include <QtGui>
class Dialog : public QDialog
{
public:
Dialog() : QDialog(0, Qt::FramelessWindowHint) // hint is required on Windows
{
setFixedSize(500, 500); // size of the background image
QPushButton *button = new QPushButton("Some Button", this);
setAttribute(Qt::WA_TranslucentBackground);
}
protected:
void paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawImage(QRectF(0, 0, 500, 500), QImage(":/resources/image.png"));
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog d;
d.show();
return a.exec();
}