embed a qdialog into qwidget (Qt c++)
-
HI Guys!
I would like to ask you how I could make a QWidget-based application where the QDialogs are not opened as separate windows but within the main QWidget? I'd like to keep mouse moveability as default for QDialogs. I would like the contents behind it to be grayed out as long as the QDialog is open, and when I say "ok" I can get back what was pressed in the same way as with a QDialog.In short: As with games, a message window does not open as a separate window, but within the game's main window.
I tried the checked answer here (https://stackoverflow.com/questions/17163585/simple-way-to-embed-a-qdialog-into-a-qwidget), but somehow I must have messed it up because it didn't want to work for me :(
Thank for your help!
-
@Kaguro
Hi,could you explain a bit more about the specific use case?
main QWidget?
What's this? A central widget, or a
QMainWindow
, or something else?The documentation is quite clear about
QDialog
being a toplevel window (i.e. a popup) for brief user interactions, e.g. asking a question, require confirmation, or - as one of many special cases - choose a file. Embedding it into another widget sounds like the opposite of such genuine use cases. I don't see an easy way to shoehorn aQDialog
into a non-toplevel behavior.If the bare requirement is to say "OK" or "cancel", I'd probably inherit a custom class from
QWidget
and make it behave as desired, including disabling/enabling its siblings.Cheers
Axel -
@Kaguro said in embed a qdialog into qwidget (Qt c++):
In short: As with games, a message window does not open as a separate window, but within the game's main window.
Because it's rendered as content of the same window / in the same context.
QDialog
is probably the wrong class for you and a customQWidget
based design works better. -
Well, there are two different parts to this problem. First, you need to gray out the existing widget. The only way I see is to put a slightly transparent widget on top. This widget would not sit inside any layout, but just would be resized to cover the whole parent widget. To make it perfect you would need to register a event filter to the parent widget to catch all resize events and resize the transparent widget accordingly. Though the event filter is advanced and not necessary to try this approach. Secondly, as mentioned already you cannot really force QDialog to behave differently. Maybe the additional transparent widget is all you really want and you are fine with the title bar of the dialog. You could also try to change the windowFlags of the dialog to drop the title bar. I'm not sure if this works. Otherwise you need to resort to @Pl45m4 suggestion to use a plain QWidget instead and implement the dialog-behavior yourself.
-
@SimonSchroeder @Pl45m4 @Axel-Spoerl sorry for the very late reply!
So I started to try make a widget like the suggestion, here is my code (sorry it a little bit big):
fxmessagebox.h
#include <QLabel> #include <QPushButton> #include <QHBoxLayout> #include <QDialogButtonBox> //------------------------------------------------------------------------ enum Type { Error = 1, Information , Warning }; class MessageHeader: public QLabel { public: QPushButton *closeB; MessageHeader(const QString &Text,Type type) { this->closeB = new QPushButton(this); closeB->setFocusPolicy(Qt::NoFocus); closeB->setCursor(Qt::PointingHandCursor); Set_Type(type); this->setContentsMargins(5,0,40,0); this->setText(Text); } void Set_Text(const QString &text) { this->setText(text); } void Set_Type(Type t) { if(t==Type::Error) { this->setObjectName("lineerror"); closeB->setObjectName("closebuttonerror"); } else if(t==Type::Information) { this->setObjectName("lineinfo"); closeB->setObjectName("closebuttoninfo"); } else if(t==Type::Warning) { this->setObjectName("linewarning"); closeB->setObjectName("closebuttonwarning"); } } private: QPoint m_pCursor; protected: void resizeEvent(QResizeEvent *) { closeB->setGeometry((this->width()-35),0,35,35); } }; //------------------------------------------------------------------------ class FXMessageBox:public QWidget { private: QLabel *cs; QLabel *bot; QLabel *ic; QPushButton *bClose; int defButtonIndex = -1; QString defButtonName =""; bool isModifiedHeaderText = false; QPoint oldPos; public: MessageHeader *header; QWidget *m_parent; QPoint position; QDialogButtonBox *buttonBox; QStringList buttonList; QHBoxLayout *h; QGridLayout *gl; explicit FXMessageBox(QWidget *parent = nullptr); void Set_HeaderText(const QString &text); void Set_Text(const QString &text); void Set_Buttons(const QStringList &buttonList); void Set_DefaultButton(int index); void Set_DefaultButton(const QString &name); void Set_Type(Type type); protected: void showEvent(QShowEvent *) override; bool eventFilter(QObject *obj, QEvent *e) override; private slots: void dialogButtonClicked(QAbstractButton *button); void exitClick(); };
fxmessagebox.cpp
#include <QMouseEvent> #include <QFile> #include <QProxyStyle> #include <QApplication> #include "fxmessagebox.h" //------------------------------------------------------------------------ class Style_tweaks : public QProxyStyle { public: void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { if (element == QStyle::PE_FrameFocusRect) return; QProxyStyle::drawPrimitive(element, option, painter, widget); } }; //------------------------------------------------------------------------ FXMessageBox::FXMessageBox(QWidget *parent) { this->setContentsMargins(25,25,25,25); QFile file("messagebox.qss"); file.open(QFile::ReadOnly); QString styleSheet = QLatin1String(file.readAll()); setStyleSheet(styleSheet); this->setFixedSize(380,200); this->h = new QHBoxLayout(this); h->setSpacing(0); h->setContentsMargins(0,0,0,0); this-> gl = new QGridLayout; gl->setSpacing(0); gl->setContentsMargins(0,0,0,0); this->m_parent = parent; this->buttonBox = new QDialogButtonBox(); buttonBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); buttonBox->setContentsMargins(4,4,4,4); h->addWidget(buttonBox); this->cs = new QLabel(this); cs->setWordWrap(true); cs->setObjectName("body"); cs->setAlignment(Qt::AlignCenter); this->ic = new QLabel; this->bot = new QLabel; bot->setLayout(h); ic->setObjectName("headerinfo"); bot->setObjectName("botinfo"); header = new MessageHeader("INFORMÁCIÓ",Type::Information); header->setWordWrap(true); ic->setFixedSize(35,35); bot->setFixedHeight(45); header->setFixedHeight(35); header->installEventFilter(this); connect(header->closeB, &QPushButton::clicked,this, [=] { exitClick();}); gl->addWidget(ic,0,0,1,1); gl->addWidget(header,0,1,1,9); gl->addWidget(bot,4,1,1,9); gl->addWidget(cs,1,1,3,9); this->setParent(m_parent); } void FXMessageBox::Set_HeaderText(const QString &text) { header->Set_Text(text); isModifiedHeaderText = true; } void FXMessageBox::Set_Text(const QString &text) { cs->setText(text); } void FXMessageBox::Set_Buttons(const QStringList &buttonList) { this->buttonList = buttonList; for (int i=0; i<buttonList.count() ;i++ ) { QPushButton *button = new QPushButton(buttonList.at(i)); button->setObjectName("choicebutton"); button->setProperty("ActionRole",i+1); button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); button->setStyle(new Style_tweaks); buttonBox->addButton(button,QDialogButtonBox::ActionRole); connect(button, &QPushButton::clicked,this, [=] { dialogButtonClicked(button);}); button->setCursor(Qt::PointingHandCursor); } } void FXMessageBox::Set_DefaultButton(int index) { defButtonIndex = index; } void FXMessageBox::Set_DefaultButton(const QString &name) { defButtonName = name; } void FXMessageBox::Set_Type(Type type) { if(type==Type::Error) { ic->setObjectName("headererror"); bot->setObjectName("boterror"); if(!isModifiedHeaderText) header->Set_Text("ERROR"); } if(type==Type::Information) { ic->setObjectName("headerinfo"); bot->setObjectName("botinfo"); if(!isModifiedHeaderText) header->Set_Text("INFORMATION"); } if(type==Type::Warning) { ic->setObjectName("headerwarning"); bot->setObjectName("botwarning"); if(!isModifiedHeaderText) header->Set_Text("WARNING"); } header->Set_Type(type); } void FXMessageBox::showEvent(QShowEvent *) { if(buttonList.count()==0) { QPushButton *button = new QPushButton("Ok"); button->setObjectName("choicebutton"); button->setProperty("ActionRole",1); button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); button->setMaximumWidth(100); button->setCursor(Qt::PointingHandCursor); buttonBox->addButton(button,QDialogButtonBox::ActionRole); connect(button, &QPushButton::clicked,this, [=] { dialogButtonClicked(button);}); } setLayout(gl); QList <QPushButton *> tmp = buttonBox->findChildren<QPushButton *>(); if(defButtonIndex == -1 && defButtonName=="") { tmp.at(0)->setFocus(); } else if(defButtonIndex!=-1) { tmp.at(defButtonIndex)->setFocus(); } else { for (int i=0;i<tmp.count();i++) { if(tmp.at(i)->text() == defButtonName) { tmp.at(i)->setFocus(); break; } } } } bool FXMessageBox::eventFilter(QObject *obj, QEvent *e) { if((e->type() == QEvent::MouseButtonPress)) { position = QPoint(static_cast<QMouseEvent*>(e)->globalPosition().x()-geometry().x(), static_cast<QMouseEvent*>(e)->globalPosition().y()-geometry().y()); } if((e->type() == QEvent::MouseMove )) { if(static_cast<QMouseEvent*>(e)->buttons() & Qt::LeftButton ) { QPoint toMove = static_cast<QMouseEvent*>(e)->globalPosition().toPoint() - position; move(toMove); } } return QObject::eventFilter(obj, e); } void FXMessageBox::dialogButtonClicked(QAbstractButton *button) { int actionRole = button->property("ActionRole").toInt(); qDebug()<<actionRole; } void FXMessageBox::exitClick() { qDebug()<<"Exit clicked"; }
messagebox.qss
#FxMessageBox { margin: 0px; } QLabel#body { background-color : rgb(81, 81, 81); text-align: center; font-family: Source Sans Pro; color:rgb(233, 237, 241); font-size: 10pt; padding: 20px 10px 20px 10px; } QLabel#boterror,#botwarning,#botinfo { border-bottom: 0px; border-right: 0px; border-left: 0px; padding:4px; } QLabel#boterror { background-color: rgb(232,48,65); } QLabel#botwarning { background-color: rgb(233,163,60); } QLabel#botinfo { background-color: rgb(107,171,224); } QLabel#headererror { image: url(RES/error.svg); background-color: rgb(232, 48, 65); } QLabel#headerinfo { image: url(:/RES/information.svg); background-color: rgb(107, 171, 224); } QLabel#headerwarning { image: url(:/RES/warning.svg); background-color: rgb(233, 163, 60); } QLabel#lineerror,#lineinfo,#linewarning { border-top: 0px; border-right: 0px; border-left: 0px; text-align: center; font: bold; background-color: rgb(51, 51, 51); font-family: Source Sans Pro; font-size: 10pt; } QLabel#lineerror { border-bottom: 2px solid rgb(232, 48, 65); color: rgb(232,48,65); } QLabel#linewarning { border-bottom: 2px solid rgb(233, 163, 60); color: rgb(233,163,60); } QLabel#lineinfo { border-bottom: 2px solid rgb(107, 171, 224); color: rgb(107,171,224); } QPushButton#choicebutton { background-color: rgb(233,237,241); color: rgb(102,124,146); padding-left: 10px; padding-right: 10px; border: 0px solid rgb(255, 213, 74); } QPushButton#choicebutton:focus { background-color: rgb(212,220,227); color: rgb(37,59,79); } QPushButton#choicebutton:hover { background-color: rgb(212,220,227); color: rgb(37,59,79); } QPushButton#choicebutton:pressed { background-color: rgb(182,195,206); color: rgb(37,59,79); } QPushButton#closebuttonerror,#closebuttoninfo,#closebuttonwarning { image: url(:/RES/close.svg); border: 0px; background-color: rgb(51, 51, 51); padding: 7px 7px 7px 7px; } QPushButton#closebuttonerror { border-bottom: 2px solid rgb(232,48,65); } QPushButton#closebuttoninfo { border-bottom: 2px solid rgb(107, 171, 224); } QPushButton#closebuttonwarning { border-bottom: 2px solid rgb(233,163,60); } QPushButton#closebuttonerror:hover,#closebuttoninfo:hover,#closebuttonwarning:hover { background-color:rgb(232,48,65); }
And the useage:
int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); FXMessageBox *kate = new FXMessageBox(&w); kate->Set_Text("Lorem Ipsum Dolor Sit Amet"); kate->Set_Type(Type::Warning); kate->show(); kate->move(0,0); return a.exec(); }
Everything works fine, except that when I move my fxmessage widget, the edges start to creep while moving, so they don't stay rigid. After moving everything back to normal. I don't know where the error could be, that's why I wrote the entire code (sorry for that). Thank you for your answers!
-
@Kaguro said in embed a qdialog into qwidget (Qt c++):
the edges start to creep while moving, so they don't stay rigid
Do they leave a trace on the screen? Or is it some blur effect? Can you show it?
Could be an issue with your stylesheet. Is it the same, when you don't set the stylesheet? -
-
@Kaguro
The video looks like a rendering glitch to me, which becomes more obvious on a widget without frame margins.
But since everything gets rendered nicely when the drag stops, I have the impression that the glitch is not Qt related.
In essence, there's no difference in how a widget is painted during or after a drag.The fact having a style sheet or not doesn't influence the glitch, would fit into this picture.
Have you tried the reproducer on different computers with fast/slow graphics?
-
@Axel-Spoerl
hmm interesting. Would it depend on the computer? I've only tested it on my work machine, but I'll test it at home too, where I have a faster computer. But it needs faster graphics card? Does this render require that much? What is interesting too: If i set the widget's setContentMargins, higher the number I set, the less the glitch occurs. In the video the contentmargins are 0. -
@Kaguro
I am speculating now. I can't see any glitch on my PC, but it's a fast one with a native NVIDIA driver.
The glitch on the video (watched in slomo) looks like rendering on the "new" position starts, before rendering on the "old" position stops. And as said before, technically there is no difference between rendering "on the move" and "at the end of the move". Wouldn't make sense - we can't predict, when a user stops moving. -
@Axel-Spoerl
I understand. I will try it at home and report back!
it's just not so nice, just that the edges don't glitch when i do it with QDialog (Of course, then they are not in the same window). -
@Kaguro
I completely fail to reproduce that.
If you are able to make a video, you could certainly file a bug report at https://bugreports.qt.io -
@Axel-Spoerl It's very weird to me that it works differently at you. Maybe can you also show in a video what you see? (I will try make a bug report but for some reason I think this is not a bug, especially if you don't see this behavior)
-
@Kaguro
Well, even if the bug will be closed without a fix, we should have a look at it.