'PySide2: QMessageBox resizes when informatve text is changed obscuring progress bar

I have a custom QMessageBox with a progress bar. When I change the info text of the message box (using method setInformativeText), the window resizes automatically and hides the progress bar.

See below for a working example. The version I use is: PySide2==5.12.0.

How can I prevent the message box from resizing?

When the info text hasn't been changed the message box looks like:

before changing info text

After I change the info text, I get:

after changing info text

Working example:

#!/usr/bin/env python

import time
from PySide2.QtWidgets import (
    QAction, QMessageBox, QAbstractButton, QProgressBar, qApp, QPlainTextEdit, QDialog,
    QPushButton, QVBoxLayout, QApplication,
)
from PySide2.QtCore import QThread, Qt, Signal as Signal, QStandardPaths
from PySide2.QtGui import QIcon

class ShowMessageBox(QThread):
    SLEEPTIME = 0.2
    started = Signal()
    finished = Signal()

    def __init__(
            self, text, parent=None, minimum_time=1.0, maximum_time=120.0,
            cancel=False, callback=None):
        super().__init__(parent=parent)
        self.message_box = QMessageBox(parent=parent)
        self.message_box.setText(str(text))
        if cancel:
            self.message_box.setStandardButtons(QMessageBox.Cancel)
        else:
            self.message_box.setStandardButtons(QMessageBox.NoButton)
        self.callback = callback
        self.progress_bar = None
        self.started.connect(self.message_box.show)
        self.finished.connect(self.message_box.accept)
        if cancel and callback:
            self.message_box.buttonClicked['QAbstractButton *'].connect(callback)
            self.message_box.buttonClicked['QAbstractButton *'].connect(
                self.button_clicked)
        self.must_run = False
        self.time_left = minimum_time
        self.time_stop = maximum_time
        self.start()
        time.sleep(0.01)
        qApp.processEvents()

    def set_running(self, must_run):
        self.must_run = must_run
        time.sleep(0.01)
        qApp.processEvents()

    def run(self):
        self.must_run = True
        self.started.emit()
        while (self.must_run or self.time_left) and self.time_stop:
            time.sleep(self.SLEEPTIME)
            if self.time_left > 0:
                self.time_left -= self.SLEEPTIME
                if self.time_left < 0.0:
                    self.time_left = 0.0
            if self.time_stop > 0:
                self.time_stop -= self.SLEEPTIME
                if self.time_stop < 0.0:
                    self.time_stop = 0.0
        self.finished.emit()

    def button_clicked(self, button_or_id):
        if isinstance(button_or_id, QAbstractButton):
            self.set_running(False)
        time.sleep(0.01)
        qApp.processEvents()

    def add_progress_bar(self, progress_max):
        # add a progress bar if we have none
        if progress_max and not self.progress_bar:
            self.progress_bar = QProgressBar()
            self.progress_bar.setMinimum(0)
            self.progress_bar.setMaximum(progress_max)
            layout = self.message_box.layout()
            layout.addWidget(self.progress_bar, 5, 1)
            self.progress_bar.setValue(0)

        # remove progress bar if we have one
        if progress_max == 0 and self.progress_bar:
            self.progress_bar.deleteLater()
            self.progress_bar.setParent(None)
            self.progress_bar = None
        time.sleep(0.01)
        qApp.processEvents()

    def set_progress_bar(self, progress):
        if self.progress_bar:
            self.progress_bar.setValue(progress)
        time.sleep(0.01)
        qApp.processEvents()

    def set_text(self, text):
        self.message_box.setText(str(text))
        time.sleep(0.01)
        qApp.processEvents()

    def set_info_text(self, text):
        self.message_box.setInformativeText(str(text))
        time.sleep(0.01)
        qApp.processEvents()

def process_events(process_time):
    val = 0.0
    while val < process_time:
        qApp.processEvents()  # todo:test
        time.sleep(0.01)  # todo:test
        val += 0.01

def main():
    app = QApplication()
    mbox = ShowMessageBox('some normal text...', cancel=True, maximum_time=10)
    process_events(1)
    mbox.set_info_text('some info text...')
    process_events(1)
    # add a progress bar with a maximum value of 20; corresponds to 100%
    mbox.add_progress_bar(20)
    val = 0
    while val <= 20:
        process_events(0.2)
        mbox.set_progress_bar(val)
        val += 1
        # change info text to show effect on progress bar
        if val == 10:
            mbox.set_info_text('changed info text...')
    app.exec_()

if __name__ == "__main__":
    main()


Solution 1:[1]

I couldn't get it to work using method setInformativeText without resizing issues.

I managed to work around the issue by appending the additional text to the main text, as suggested in Qt5 - Resizing a QMessageBox

My updated (working) code is:

#!/usr/bin/env python

import time
from PySide2.QtWidgets import (
    QAction, QMessageBox, QAbstractButton, QProgressBar, qApp, QPlainTextEdit, QDialog,
    QPushButton, QVBoxLayout, QApplication,
)
from PySide2.QtCore import QThread, Qt, Signal as Signal, QStandardPaths
from PySide2.QtGui import QIcon

class ShowMessageBox(QThread):
    SLEEPTIME = 0.2
    started = Signal()
    finished = Signal()

    def __init__(
            self, text, parent=None, minimum_time=1.0, maximum_time=120.0,
            cancel=False, callback=None):
        super().__init__(parent=parent)
        # trac:970:I couldn't get self.message_box.setInformativeText to work
        # so used '\n' to delimit main text and info text, see:
        # https://web.archive.org/web/20190910202435/http://apocalyptech.com/linux/qt/qmessagebox/
        self.message_box = QMessageBox(parent=parent)
        self.set_text(text)
        if cancel:
            self.message_box.setStandardButtons(QMessageBox.Cancel)
        else:
            self.message_box.setStandardButtons(QMessageBox.NoButton)
        self.callback = callback
        self.progress_bar = None
        self.started.connect(self.message_box.show)
        self.finished.connect(self.message_box.accept)
        if cancel and callback:
            self.message_box.buttonClicked['QAbstractButton *'].connect(callback)
            self.message_box.buttonClicked['QAbstractButton *'].connect(
                self.button_clicked)
        self.must_run = False
        self.time_left = minimum_time
        self.time_stop = maximum_time
        self.start()

    def set_running(self, must_run):
        self.must_run = must_run

    def run(self):
        self.must_run = True
        self.started.emit()
        while (self.must_run or self.time_left) and self.time_stop:
            time.sleep(self.SLEEPTIME)
            if self.time_left > 0:
                self.time_left -= self.SLEEPTIME
                if self.time_left < 0.0:
                    self.time_left = 0.0
            if self.time_stop > 0:
                self.time_stop -= self.SLEEPTIME
                if self.time_stop < 0.0:
                    self.time_stop = 0.0
        self.finished.emit()

    def button_clicked(self, button_or_id):
        if isinstance(button_or_id, QAbstractButton):
            self.set_running(False)

    def add_progress_bar(self, progress_max):
        # add a progress bar if we have none
        if progress_max and not self.progress_bar:
            self.progress_bar = QProgressBar()
            self.progress_bar.setMinimum(0)
            self.progress_bar.setMaximum(progress_max)
            layout = self.message_box.layout()
            # line up progress bar with main text in column 1
            layout.addWidget(self.progress_bar, 5, 1)
            # set initial value
            self.set_progress_bar(0)
        # remove progress bar if we have one
        if progress_max == 0 and self.progress_bar:
            self.progress_bar.deleteLater()
            self.progress_bar.setParent(None)
            self.progress_bar = None

    def set_progress_bar(self, progress):
        if self.progress_bar:
            self.progress_bar.setValue(progress)
            # force update of message box in GUI
            time.sleep(0.01)
            qApp.processEvents()

    def set_text(self, text):
        self.main_text = str(text)
        self.output_text()

    def set_info_text(self, text):
        self.info_text = str(text)
        self.output_text()

    def output_text(self):
        try:
            text = self.main_text
        except AttributeError:
            self.main_text = ''
            text = self.main_text
        try:
            if self.info_text:
                text = text + '\n' + self.info_text
        except AttributeError:
            self.info_text = ''
        self.message_box.setText(text)
        # force update of message box in GUI
        time.sleep(0.01)
        qApp.processEvents()

def main():
    app = QApplication()
    mbox = ShowMessageBox('some normal text...', cancel=True, maximum_time=10)
    mbox.set_info_text('some info text...')
    time.sleep(1.5)
    # add a progress bar with a maximum value of 20; corresponds to 100%
    mbox.add_progress_bar(20)
    val = 0
    while val <= 20:
        # sleeping mimics application processing
        time.sleep(0.2)
        mbox.set_progress_bar(val)
        val += 1
        # change informative text while progress meter is active
        if val == 10:
            mbox.set_info_text('changed info text...')
    app.exec_()

if __name__ == "__main__":
    main()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 NZD