PyQt: 如何处理内容变化时控件的自动调整大小
我在使用qt4的控件时遇到了一些关于大小的问题,特别是当它们的内容发生变化时。
我用两个简单的例子来说明我的问题:
例子1:
我有一个QLineEdit控件。有时候,当我用QLineEdit.setText()来改变它的内容时,原本的一行文字就不再适合这个控件的大小了。这时我必须选中这个控件,然后用箭头键来左右滚动文字,才能看到全部内容。
例子2:
我有一个QTextEdit控件。有时候,当我用QTextEdit.setHtml()来改变它的内容时,显示的HTML内容也不再适合当前的控件大小。这时控件会出现水平和/或垂直的滚动条,我可以用这些滚动条来查看HTML内容。
在这种情况下,我希望能有一些逻辑来判断内容变化后,新的内容是否还适合控件,如果不适合的话,自动增大控件的大小,这样所有内容就都能显示出来了。
这些情况是怎么处理的呢?我使用的是PyQt4。
补充说明:在阅读了评论和第一个回答(提到在控件中输入内容)后,我又仔细看了一遍我的问题。让我感到不快的是,我发现了一个严重的拼写错误。我本来想说的是QTextBrowser,而不是QTextEdit,抱歉让你们误解了。也就是说,我有一个控件用来显示我正在更改的HTML代码,我希望这个控件能够自动增大到足够显示所有内容,而不需要出现滚动条。
至于为什么用QLineEdit而不是QLabel——我选择QLineEdit是因为我发现无法用鼠标从QLabel中选中文本来复制。而在QLineEdit中是可以的。
5 个回答
我通过使用以下的C++类实现了类似的效果:
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 )
。
对于QTextBrowser的情况,你可以通过以下方式获取文档的大小:
QTextBrowser::document()->size();
在设置好HTML内容后,再调整QTextBrowser的大小。
我这里用C++来回答,因为这是我最熟悉的,而且你的问题并不是特定于PyQt的。
通常情况下,当你觉得控件的大小提示可能发生变化时,只需要调用 QWidget::updateGeometry()
,就像当控件内容可能改变时需要调用 QWidget::update()
一样。
不过,你的问题在于,当在 QLineEdit
和 QTextEdit
中添加文本时,sizeHint()
并不会改变。原因是:人们不希望他们的对话框在输入时自动变大 :)
也就是说,如果你真的想要在这些控件中实现“输入时自动变大”的效果,你需要从它们继承,并重新实现 sizeHint()
和 minimumSizeHint()
,让它们返回更大的尺寸。此外,可能还需要在 setText()
、append()
等方法中调用 updateGeometry()
,这样才能让尺寸提示的变化被注意到。
计算尺寸提示并不是一件简单的事,对于 QLineEdit
来说会容易一些,但对于 QTextEdit
就复杂多了,因为它实际上是一个 QAbstractScrollArea
。不过你可以参考 sizeHint()
和 minimumSizeHint()
的实现来获取灵感(还有 QComboBox
的实现,它有一个模式正好可以做到你想要的: QComboBox::AdjustToContents
)。
补充:你的两个使用场景(没有滚动条的QTextBrowser和用QLineEdit代替QLabel来选择文本)可以通过使用QLabel和一个足够新的Qt来解决。QLabel在Qt 4.2中增加了链接点击通知和所谓的“文本交互标志”(其中一个是TextSelectableByMouse)。我能分辨出的唯一区别是,加载新内容不是自动的,没有历史记录,也没有微焦点提示(比如从一个链接切换到另一个链接)在QLabel中。