PyQt: как обрабатывать автоматическое изменение размеров виджетов при изменении их содержимого
У меня возникают некоторые проблемы с размером виджетов qt4 при изменении их содержимого.
Я проиллюстрирую свои проблемы двумя простыми сценариями:
Сценарий 1:
У меня есть виджет QLineEdit. Иногда, когда я меняю свой контент с помощью QLineEdit.setText(), однострочная строка больше не вписывается в виджет с текущим размером. Я должен выбрать виджет и использовать клавиши со стрелками для прокрутки строки в обоих направлениях, чтобы увидеть все это.
Сценарий 2:
У меня есть виджет QTextEdit. Иногда, когда я изменяю его содержимое с помощью QTextEdit.setHtml(), отображаемый HTML-контент больше не вписывается в виджет с его текущим размером. Виджет начинает показывать горизонтальные и/или вертикальные полосы прокрутки, и я могу использовать их для прокрутки содержимого HTML.
То, что я хотел бы в таких сценариях, - это иметь некоторую логику, которая решает, будет ли после изменения контента новый контент больше не вписываться в виджет и автоматически увеличить размер виджета, чтобы все соответствовало.
Как обрабатываются эти сценарии?
Я использую PyQt4.
Изменить: после прочтения как комментария, так и первого ответа (который упоминает ввод содержимого в виджет), я снова рассмотрел вопрос. Я был неприятно удивлен, узнав ужасную опечатку. Я имел в виду QTextBrowser, когда писал QTextEdit, извиняясь за то, что вас вводили в заблуждение. То есть: у меня есть виджет, который отображает HTML-код, который я меняю, и я бы хотел, чтобы виджет вырос настолько, чтобы отображать все, не имея полос прокрутки.
Что касается QLineEdit вместо QLabel - я пошел на QLineEdit, так как заметил, что я не могу выбрать текст из QLabel с помощью мыши для его копирования. С QLineEdit это возможно.
Ответы
Ответ 1
Я отвечаю на С++ здесь, так как это то, что я больше всего знаю, и ваша проблема не специфична для PyQt.
Обычно вам нужно вызвать QWidget::updateGeometry()
, когда возможно изменение sizeHint()
, так же, как вам нужно позвонить QWidget::update()
, когда содержимое может быть изменено.
Однако ваша проблема заключается в том, что sizeHint()
не изменяется, когда текст добавляется к QLineEdit
и QTextEdit
. По какой-то причине: люди не ожидают, что их диалоги будут расти как-они-типа:)
Тем не менее, если вы действительно хотите, чтобы в этих виджетах возникало поведение типа "растение по типу", вам нужно унаследовать их и переопределить sizeHint()
и minimumSizeHint()
, чтобы вернуть больший размер и, возможно, setText()
, append()
и т.д., чтобы вызвать updateGeometry()
, поэтому замечено изменение размера.
Расчет размера будет не совсем тривиальным и будет легче для QLineEdit
, чем для QTextEdit
(который тайно a QAbstractScrollArea
), но вы можете посмотреть sizeHint()
и minimumSizeHint()
реализаций для вдохновения (также для QComboBox
, который имеет способ сделать именно то, что вы хотите: QComboBox::AdjustToContents
.
РЕДАКТИРОВАТЬ: Ваши два варианта использования (QTextBrowser без полос прокрутки и QLineEdit вместо QLabel только для выбора текста там) могут быть решены с помощью QLabel и достаточно недавнего Qt. В Qt 4.2 QLabel получил уведомление об активации ссылки и так называемые "флагов текстового взаимодействия" (один из которых - TextSelectableByMouse). Единственное отличие, которое я смог разобрать, это то, что загрузка нового контента не является автоматическим, нет истории, и в QLabel нет намека на микрофокус (т.е. Табуляция из ссылки на ссылку).
Ответ 2
В случае QTextBrowser вы можете получить размер документа, используя
QTextBrowser::document()->size();
после установки html, а затем после этого измените размер QTextBrowser.
Ответ 3
Возможно, посмотрите Python QT Automatic Widget Resizer. Это написано на python, но это может дать вам некоторые идеи о том, как делать то, что вам нужно.
Ответ 4
Я получаю аналогичный эффект, используя следующий класс С++:
textedit.h
#ifndef TEXTEDIT_H
#define TEXTEDIT_H
#include <QTextEdit>
class TextEdit : public QTextEdit
{
Q_DISABLE_COPY( TextEdit )
public:
TextEdit( QWidget* parent = NULL );
TextEdit( const QString& text, QWidget* parent = NULL );
virtual ~TextEdit();
void fitToDocument( Qt::Orientations orientations );
virtual QSize sizeHint() const;
private:
int fittedHeight_;
Qt::Orientations fittedOrientations_;
int fittedWidth_;
};
#include "textedit-inl.h"
#endif // TEXTEDIT_H
TextEdit-inl.h
#ifndef TEXTEDITINL_H
#define TEXTEDITINL_H
#include "textedit.h"
inline TextEdit::TextEdit( QWidget* parent ) :
QTextEdit( parent ), fittedOrientations_( 0 )
{ }
inline TextEdit::TextEdit( const QString& text, QWidget* parent ) :
QTextEdit( text, parent ), fittedOrientations_( 0 )
{ }
inline TextEdit::~TextEdit()
{ }
inline QSize TextEdit::sizeHint() const
{
QSize sizeHint = QTextEdit::sizeHint();
if( fittedOrientations_ & Qt::Horizontal )
sizeHint.setWidth( fittedWidth_ );
if( fittedOrientations_ & Qt::Vertical )
sizeHint.setHeight( fittedHeight_ );
return sizeHint;
}
#endif // TEXTEDITINL_H
textedit.cpp
#include "textedit.h"
void TextEdit::fitToDocument( Qt::Orientations orientations )
{
QSize documentSize( document()->size().toSize() );
QSizePolicy sizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
if( orientations & Qt::Horizontal ) {
fittedWidth_ = documentSize.width() + (width() - viewport()->width());
sizePolicy.setHorizontalPolicy( QSizePolicy::Fixed );
}
if( orientations & Qt::Vertical ) {
fittedHeight_ = documentSize.height() + (width() - viewport()->width());
sizePolicy.setVerticalPolicy( QSizePolicy::Fixed );
}
fittedOrientations_ = orientations;
setSizePolicy( sizePolicy );
updateGeometry();
}
например, вызов TextEdit::fitToDocument( Qt::Horizontal )
будет устанавливать ширину виджета на фиксированную ширину, достаточно большую, чтобы соответствовать документу и его окружению (например, вертикальная полоса прокрутки, если таковая имеется). если ваша цель состоит в том, чтобы это произошло при каждом изменении содержимого, подключите сигнал QTextEdit::textChanged()
к слоту, который вызывает TextEdit::fitToDocument()
.
Что касается вашей проблемы с QLabel
, решение прост: вызов QLabel::setTextInteractionFlags( Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse )
.
Ответ 5
Ok реализует метод sizeHint()
. И каждый раз, когда ваш контент меняет размер, звоните updateGeometry()
При изменении содержимого без изменения размера используйте update()
. (updateGeometry()
автоматически вызывается update()
).