How to update entire window on change in QProxyStyle?
-
When the
QPushButtonhas a long text it looks something like this:
and what I wanted it to look like is this:
To achieve this, I subclassed
QProxyStyleand reimplemented thesizeFromContents()function. To test this new method I created a new project, with the UI in preview mode:
Where the spinbox name isbtnBorder_spin, button with text "Button 1: 0" has an object namebtn_1and button with text "Button 2: 0" has object namebtn_2, button with text "Button 3: Demo" has object namebtn_3.the mainwindow.h
inline int minBtnBorder{10}; class ProxyStyle : public QProxyStyle { public: QSize sizeFromContents(ContentsType ct, const QStyleOption* opt, const QSize& csz, const QWidget* widget = 0) const { QSize sz = QProxyStyle::sizeFromContents(ct, opt, csz, widget); if(ct == CT_PushButton) { const auto optionPtr = qstyleoption_cast<const QStyleOptionButton*>(opt); QStyleOptionButton customOption = *optionPtr; auto btn = qobject_cast<QPushButton*>(customOption.styleObject); if(!btn->text().isEmpty()) { QFontMetrics fm = opt->fontMetrics; int strWidth = fm.boundingRect(btn->text()).width(); int diffBtnWidth = sz.rwidth() - strWidth; if(diffBtnWidth < (2 * minBtnBorder)) sz.rwidth() = strWidth + (2 * minBtnBorder); } } return sz; } }; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = Q_NULLPTR); private: Ui::MainWindowClass UI; int btnCount_1{0}; int btnCount_2{0}; protected: void showEvent(QShowEvent* event) override; private slots: void on_btnBorder_spin_valueChanged(int val); void on_btn_1_clicked(); void on_btn_2_clicked(); void on_btn_3_clicked(); };and mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { auto proxyStyle = new ProxyStyle; proxyStyle->setBaseStyle(QStyleFactory::create("Fusion")); qApp->setStyle(proxyStyle); qApp->setFont(QFont("Verdana", 10)); ui.setupUi(this); } void MainWindow::showEvent(QShowEvent* event) { setFixedSize(this->layout()->minimumSize()); } void MainWindow::on_btnBorder_spin_valueChanged(int val) { minBtnBorder = val; update(); } void MainWindow::on_btn_1_clicked() { ++btnCount_1; ui.btn_1->setText(QString("Button 1: ").append(QString::number(btnCount_1))); } void MainWindow::on_btn_2_clicked() { ++btnCount_2; ui.btn_2->setText(QString("Button 2: ").append(QString::number(btnCount_2))); } void MainWindow::on_btn_3_clicked() { qDebug() << "Button 3 Clicked"; }Each time
btn_1is clickedbtnCount_1is incremented and the text on the button is changed, same thing happens withbtn_2. When the value ofbtnBorder_spinis changed theminBtnBorderglobal variable is set to the new value and a call to update is issued. Whenbtn_3is clicked only a debug message saying "Button 3 Clicked" is generated.When I run my code, the window appears normally and I see a border of around 10 px on the sides of the button:
If I change the value in the spinbox to 100 px then nothing changes:
Now if I click on
btn_1then it changes the size ofbtn_1accordingly whilebtn_2andbtn_3remains fixed at the same previous size:Only if I click on
btn_2then its size also changes:Clicking on
btn_3prints the debug output, but its size never changes.I tried using
update(),repaint(),hide()thenshow()after the value ofminBtnBorderis changed but the new value is never visible immediately. It is only visible after I change the text on a button, and the change is only applied to the button whose text has changed. How can I make the changes appear immediately after I change the value ofminBtnBorderfor all buttons? -
Hi
It might be due being in a layout ?
Did you try calling updateGeometry() on the buttons ? -
@mrjj Hi, I did just now but it doesn't work.
void MainWindow::on_btnBorder_spin_valueChanged(int val) { minBtnBorder = val; ui.btn_1->updateGeometry(); ui.btn_2->updateGeometry(); ui.btn_3->updateGeometry(); update(); } -
Hi
Ok. that is odd since it will resize when clicked. updateGeometry() should make layout recalc.Is the proxy complete so I can just add 3 buttons to a layout and see myself ?
-
@mrjj Hi, that is strange. I used a normal layout in Qt Designer. Here is an image:
I didn't change anything about the layout, I just put it there and that is all.
-
@mrjj Yes, the sizePolicy, min and max size, etc is all default. All I did was change the text on the buttons.
Here is the UI file content:
/******************************************************************************** ** Form generated from reading UI file 'mainwindowggYFGS.ui' ** ** Created by: Qt User Interface Compiler version 5.15.2 ** ** WARNING! All changes made in this file will be lost when recompiling UI file! ********************************************************************************/ #ifndef MAINWINDOWGGYFGS_H #define MAINWINDOWGGYFGS_H #include <QtCore/QVariant> #include <QtWidgets/QApplication> #include <QtWidgets/QHBoxLayout> #include <QtWidgets/QLabel> #include <QtWidgets/QMainWindow> #include <QtWidgets/QPushButton> #include <QtWidgets/QSpacerItem> #include <QtWidgets/QSpinBox> #include <QtWidgets/QVBoxLayout> #include <QtWidgets/QWidget> QT_BEGIN_NAMESPACE class Ui_MainWindowClass { public: QWidget *centralWidget; QVBoxLayout *verticalLayout; QHBoxLayout *horizontalLayout; QLabel *label; QSpinBox *btnBorder_spin; QSpacerItem *horizontalSpacer; QHBoxLayout *horizontalLayout_2; QPushButton *btn_1; QSpacerItem *horizontalSpacer_2; QPushButton *btn_2; QSpacerItem *horizontalSpacer_3; QPushButton *btn_3; void setupUi(QMainWindow *MainWindowClass) { if (MainWindowClass->objectName().isEmpty()) MainWindowClass->setObjectName(QString::fromUtf8("MainWindowClass")); MainWindowClass->resize(311, 73); centralWidget = new QWidget(MainWindowClass); centralWidget->setObjectName(QString::fromUtf8("centralWidget")); verticalLayout = new QVBoxLayout(centralWidget); verticalLayout->setSpacing(6); verticalLayout->setContentsMargins(11, 11, 11, 11); verticalLayout->setObjectName(QString::fromUtf8("verticalLayout")); horizontalLayout = new QHBoxLayout(); horizontalLayout->setSpacing(6); horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); label = new QLabel(centralWidget); label->setObjectName(QString::fromUtf8("label")); horizontalLayout->addWidget(label); btnBorder_spin = new QSpinBox(centralWidget); btnBorder_spin->setObjectName(QString::fromUtf8("btnBorder_spin")); btnBorder_spin->setMaximum(999); btnBorder_spin->setValue(10); horizontalLayout->addWidget(btnBorder_spin); horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); horizontalLayout->addItem(horizontalSpacer); verticalLayout->addLayout(horizontalLayout); horizontalLayout_2 = new QHBoxLayout(); horizontalLayout_2->setSpacing(6); horizontalLayout_2->setObjectName(QString::fromUtf8("horizontalLayout_2")); btn_1 = new QPushButton(centralWidget); btn_1->setObjectName(QString::fromUtf8("btn_1")); horizontalLayout_2->addWidget(btn_1); horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); horizontalLayout_2->addItem(horizontalSpacer_2); btn_2 = new QPushButton(centralWidget); btn_2->setObjectName(QString::fromUtf8("btn_2")); horizontalLayout_2->addWidget(btn_2); horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); horizontalLayout_2->addItem(horizontalSpacer_3); btn_3 = new QPushButton(centralWidget); btn_3->setObjectName(QString::fromUtf8("btn_3")); horizontalLayout_2->addWidget(btn_3); verticalLayout->addLayout(horizontalLayout_2); MainWindowClass->setCentralWidget(centralWidget); retranslateUi(MainWindowClass); QMetaObject::connectSlotsByName(MainWindowClass); } // setupUi void retranslateUi(QMainWindow *MainWindowClass) { MainWindowClass->setWindowTitle(QCoreApplication::translate("MainWindowClass", "MainWindow", nullptr)); label->setText(QCoreApplication::translate("MainWindowClass", "Button Side Borders:", nullptr)); btnBorder_spin->setSuffix(QCoreApplication::translate("MainWindowClass", " px", nullptr)); btn_1->setText(QCoreApplication::translate("MainWindowClass", "Button 1: 0", nullptr)); btn_2->setText(QCoreApplication::translate("MainWindowClass", "Button 2: 0", nullptr)); btn_3->setText(QCoreApplication::translate("MainWindowClass", "Button 3: Demo", nullptr)); } // retranslateUi }; namespace Ui { class MainWindowClass: public Ui_MainWindowClass {}; } // namespace Ui QT_END_NAMESPACE #endif // MAINWINDOWGGYFGS_H -
@mrjj I found a way around, by recreating a new
ProxyStyleobject each time the value ofminBtnBorderis changed and then applying this to theqApp.void MainWindow::on_btnBorder_spin_valueChanged(int val) { minBtnBorder = val; auto proxyStyle = new ProxyStyle; proxyStyle->setBaseStyle(QStyleFactory::create("Fusion")); qApp->setStyle(proxyStyle); hide(); show(); }It is a workaround at best though.
-
@mrjj I found a way around, by recreating a new
ProxyStyleobject each time the value ofminBtnBorderis changed and then applying this to theqApp.void MainWindow::on_btnBorder_spin_valueChanged(int val) { minBtnBorder = val; auto proxyStyle = new ProxyStyle; proxyStyle->setBaseStyle(QStyleFactory::create("Fusion")); qApp->setStyle(proxyStyle); hide(); show(); }It is a workaround at best though.
@CJha
Good. at least we have a "fix"The odd thing is that setText
void QAbstractButton::setText(const QString &text) { Q_D(QAbstractButton); if (d->text == text) return; d->text = text; #ifndef QT_NO_SHORTCUT QKeySequence newMnemonic = QKeySequence::mnemonic(text); setShortcut(newMnemonic); #endif d->sizeHint = QSize(); update(); updateGeometry(); #ifndef QT_NO_ACCESSIBILITY QAccessibleEvent event(this, QAccessible::NameChanged); QAccessible::updateAccessibility(&event); #endif }just calls
update();
updateGeometry(); -
@CJha
Good. at least we have a "fix"The odd thing is that setText
void QAbstractButton::setText(const QString &text) { Q_D(QAbstractButton); if (d->text == text) return; d->text = text; #ifndef QT_NO_SHORTCUT QKeySequence newMnemonic = QKeySequence::mnemonic(text); setShortcut(newMnemonic); #endif d->sizeHint = QSize(); update(); updateGeometry(); #ifndef QT_NO_ACCESSIBILITY QAccessibleEvent event(this, QAccessible::NameChanged); QAccessible::updateAccessibility(&event); #endif }just calls
update();
updateGeometry(); -
@mrjj Yeah, that is very odd. Maybe something else is going on in the background that we are unable to get to!? Although, the code for
setText()seems complete. -
@CJha
Good. at least we have a "fix"The odd thing is that setText
void QAbstractButton::setText(const QString &text) { Q_D(QAbstractButton); if (d->text == text) return; d->text = text; #ifndef QT_NO_SHORTCUT QKeySequence newMnemonic = QKeySequence::mnemonic(text); setShortcut(newMnemonic); #endif d->sizeHint = QSize(); update(); updateGeometry(); #ifndef QT_NO_ACCESSIBILITY QAccessibleEvent event(this, QAccessible::NameChanged); QAccessible::updateAccessibility(&event); #endif }just calls
update();
updateGeometry();@mrjj said in How to update entire window on change in QProxyStyle?:
d->sizeHint = QSize();
I think this may be the reason. I would guess sizeFromContents() is only called when sizeHint is invalid. Simply set a breakpoint at ProxyStyle ::sizeFromContents() and take a look from where it is called (and why).
-
@mrjj said in How to update entire window on change in QProxyStyle?:
d->sizeHint = QSize();
I think this may be the reason. I would guess sizeFromContents() is only called when sizeHint is invalid. Simply set a breakpoint at ProxyStyle ::sizeFromContents() and take a look from where it is called (and why).
@Christian-Ehrlicher
Hi
Seems to call it from QPushButton::sizeHint() but I failed
to find way to trigger it without altering text.
I guess for full disclosure the source is needed and single step :9
I use wobog for it.