How to update entire window on change in QProxyStyle?
-
When the
QPushButton
has a long text it looks something like this:
and what I wanted it to look like is this:
To achieve this, I subclassed
QProxyStyle
and 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_1
and 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_1
is clickedbtnCount_1
is incremented and the text on the button is changed, same thing happens withbtn_2
. When the value ofbtnBorder_spin
is changed theminBtnBorder
global variable is set to the new value and a call to update is issued. Whenbtn_3
is 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_1
then it changes the size ofbtn_1
accordingly whilebtn_2
andbtn_3
remains fixed at the same previous size:Only if I click on
btn_2
then its size also changes:Clicking on
btn_3
prints the debug output, but its size never changes.I tried using
update()
,repaint()
,hide()
thenshow()
after the value ofminBtnBorder
is 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 ofminBtnBorder
for all buttons? -
Hi
It might be due being in a layout ?
Did you try calling updateGeometry() on the buttons ? -
@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
ProxyStyle
object each time the value ofminBtnBorder
is 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(); -
@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.