Reputation: 4960
I'm using the nice feature in QMessageBox to optionally show detailed text to the user. However, the window after expansion is still fairly small, and one immediately tries to resize the window so more of the details are visible. Even after setting what I think are the proper settings it won't allow resizing.
Here's the relevant snippet of PyQt4 code:
mb = QMessageBox()
mb.setText("Results written to '%s'" % filename)
mb.setDetailedText(str(myData))
mb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
mb.setSizeGripEnabled(True)
Am I missing a step and/or is this at all possible?
Upvotes: 6
Views: 16231
Reputation: 48489
Due to its nature, a QMessageBox should never allow resizing. If you need that, you should probably use a QDialog instead.
That said, the fixed size restraint is caused by the internal layout, which is also replaced any time relevant aspects of the dialog change (buttons, [un]setting detailed text, etc.).
Since the layout management is completely internal, and it always resets the fixed size of the dialog, the solution is to watch for relevant events, specifically LayoutRequest
(which is called whenever the layout needs to be redone) and Resize
and override the maximum size.
With a subclass, the solution is pretty simple:
class ResizableMessageBox(QtWidgets.QMessageBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setSizeGripEnabled(True)
def event(self, event):
if event.type() in (event.LayoutRequest, event.Resize):
if event.type() == event.Resize:
res = super().event(event)
else:
res = False
details = self.findChild(QtWidgets.QTextEdit)
if details:
details.setMaximumSize(16777215, 16777215)
self.setMaximumSize(16777215, 16777215)
return res
return super().event(event)
Alternatively, with an event filter:
class Something(QWidget):
# ...
def showMessageBox(self):
mb = QtWidgets.QMessageBox()
mb.setSizeGripEnabled(True)
mb.setWindowTitle('Hello')
mb.setText('I am a message box')
mb.setDetailedText('very long text ' * 10)
mb.setProperty('resizable', True)
mb.installEventFilter(self)
mb.exec()
def eventFilter(self, obj, event):
if (isinstance(obj, QtWidgets.QMessageBox)
and obj.property('resizable')
and event.type() in (event.LayoutRequest, event.Resize)):
if event.type() == event.Resize:
obj.event(event)
details = obj.findChild(QtWidgets.QTextEdit)
if details:
details.setMaximumSize(16777215, 16777215)
obj.setMaximumSize(16777215, 16777215)
return True
return super().eventFilter(obj, event)
Note: none of the above will work with the static methods of QMessageBox, since they create a private instance.
Upvotes: 3
Reputation: 11
Some time passed but issue is still valid.
I made following implementation, which worked well on OSX and PySide6, but not tested on other systems.
class MyMessageBox(QMessageBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._width = 150 # default
self.setSizeGripEnabled(True)
def setWidth(self, width):
self._width = width
def resizeEvent(self, event):
_result = super().resizeEvent(event)
self.setFixedWidth(self._width)
_text_box = self.findChild(QTextEdit)
if _text_box is not None:
# get width
_width = int(self._width - 50) # - 50 for border area
# get box height depending on content
_font = _text_box.document().defaultFont()
_fontMetrics = QFontMetrics(_font)
_textSize = _fontMetrics.size(0, details_box.toPlainText(), 0)
_height = int(_textSize.height()) + 30 # Need to tweak
# set size
_text_box.setFixedSize(_width, _height)
return _result
Call is done with:
_msg_box = MyMessageBox(parent=self)
_msg_box.setIcon(icon)
_msg_box.setText(title_text)
_msg_box.setInformativeText(message)
_msg_box.setDetailedText(detailed_text)
# define a reference width and apply it
_width = int(self.geometry().width() / 2.5)
_msg_box.setWidth(_width)
_msg_box.exec()
With this, the default width is set to 150, but might be change with the setWidth method. The height is set automatically. In case a detailed Text is provided, the width of the text box is a little smaller than the total width of the message box itself (- 50) and the height is depending on the content.
Note I have not used it with larger content in the text box.
Upvotes: 0
Reputation: 307
This works, but only tested on Linux under Gnome 2. It resizes horizontally only unless the "show details" text is turned on, in which case it resizes in both directions. The "show details" button still resets it to the initial size, this is either a feature or a bug depending on your pov:
bool MyMessageBox::event(QEvent* e)
{
bool result = QMessageBox::event(e);
// force resizing back on, QMessageBox keeps turning it off:
if (maximumWidth() != QWIDGETSIZE_MAX) {
QTextEdit *textEdit = findChild<QTextEdit*>();
if (textEdit && textEdit->isVisible()) {
textEdit->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
} else {
setMaximumWidth(QWIDGETSIZE_MAX);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
}
}
return result;
}
Upvotes: 0
Reputation: 411
This is the solution I would use. This doesn't make the dialog resizable, but it does make the dialog change itself to a sensible size when the details box is visible. I have unashamedly stolen some ideas from serge_gubenko's answer. Even if you'd rather implement his resizing I humbly offer some other improvements below.
# Safe since everything in the namespace begins with 'Q'
from PyQt4.QtGui import *
class MyMessageBox(QMessageBox):
# This is a much better way to extend __init__
def __init__(self, *args, **kwargs):
super(MyMessageBox, self).__init__(*args, **kwargs)
# Anything else you want goes below
# We only need to extend resizeEvent, not every event.
def resizeEvent(self, event):
result = super(MyMessageBox, self).resizeEvent(event)
details_box = self.findChild(QTextEdit)
# 'is not' is better style than '!=' for None
if details_box is not None:
details_box.setFixedSize(details_box.sizeHint())
return result
Upvotes: 7
Reputation: 20492
if you're looking to make a resizable message box, pls, check if code below would work for you:
class MyMessageBox(QtGui.QMessageBox):
def __init__(self):
QtGui.QMessageBox.__init__(self)
self.setSizeGripEnabled(True)
def event(self, e):
result = QtGui.QMessageBox.event(self, e)
self.setMinimumHeight(0)
self.setMaximumHeight(16777215)
self.setMinimumWidth(0)
self.setMaximumWidth(16777215)
self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
textEdit = self.findChild(QtGui.QTextEdit)
if textEdit != None :
textEdit.setMinimumHeight(0)
textEdit.setMaximumHeight(16777215)
textEdit.setMinimumWidth(0)
textEdit.setMaximumWidth(16777215)
textEdit.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
return result
here's how messagebox is called:
mb = MyMessageBox()
mb.setText("Results written to '%s'" % 'some_file_name')
mb.setDetailedText('some text')
mb.exec_()
solution is taken from here
hope this helps, regards
Upvotes: 6