12
votes

I am having some issues with the size of qt4 widgets when their content changes.

I will illustrate my problems with two simple scenarios:

Scenario 1:

I have a QLineEdit widget. Sometimes, when I'm changing its content using QLineEdit.setText(), the one-line string doesn't fit into the widget at its current size anymore. I must select the widget and use the arrow keys to scroll the string in both directions in order to see it all.

Scenario 2:

I have a QTextEdit widget. Sometimes, when I'm changing its content using QTextEdit.setHtml(), the rendered HTML content doesn't fit into the widget at its current size anymore. The widget starts displaying horizontal and/or vertical scroll bars and I can use them to scroll the HTML content.

What I would want in such scenarios is to have some logic that decides if after a content change, the new content won't fit anymore into the widget and automatically increase the widget size so everything would fit.

How are these scenarios handled? I'm using PyQt4.

Edit: after reading both the comment and the first answer (which mentions typing content into the widget), I went over the question one more time. I was unpleasantly surprised to find out a horrible typo. I meant QTextBrowser when I wrote QTextEdit, my apologies for misleading you. That is: I have a widget which renders HTML code that I'm changing and I would want the widget to grow enough to display everything without having scrollbars.

As for QLineEdit instead of QLabel - I went for QLineEdit since I've noticed I can't select text from a QLabel with the mouse for copying it. With QLineEdit it is possible.

5
Just wanted to let you know your question hasn't been overlooked... I've been thinking about it on and off for the past day, but I can't think of an easy way to do what you're asking. It's simply not what the items you are using are designed for. They are entry widgets, and designed to allow more data to be entered than can comfortably fit on the screen. If you really want resizing to the current items, try expanding QLabels inside of a layout.Caleb Huitt - cjhuitt

5 Answers

8
votes

I'm answering in C++ here, since that's what I'm most familiar with, and your problem isn't specific to PyQt.

Normally, you just need to call QWidget::updateGeometry() when the sizeHint() may have changed, just like you need to call QWidget::update() when the contents may have changed.

Your problem, however, is that the sizeHint() doesn't change when text is added to QLineEdit and QTextEdit. For a reason: People don't expect their dialogs to grow-as-they-type :)

That said, if you really want grow-as-you-type behaviour in those widgets you need to inherit from them and reimplement sizeHint() and minimumSizeHint() to return the larger size, and potentially setText(), append() etc. to call updateGeometry() so the sizehint change is noticed.

The sizehint calculation won't be entirely trivial, and will be way easier for QLineEdit than for QTextEdit (which is secretly a QAbstractScrollArea), but you can look at the sizeHint() and minimumSizeHint() implementations for inspiration (also the one for QComboBox, which has a mode to do exactly what you want: QComboBox::AdjustToContents.

EDIT: Your two usecases (QTextBrowser w/o scrollbars and QLineEdit instead of QLabel just for selecting the text in there) can be solved by using a QLabel and a recent enough Qt. QLabel has gained both link-clicking notification and so-called "text-interaction flags" (one of which is TextSelectableByMouse) in Qt 4.2. The only difference that I was able to make out is that loading new content isn't automatic, there's no history, and there's no micro focus hinting (ie. tabbing from link to link) in QLabel.

2
votes

For the QTextBrowser case you should be able to get the size of the document using

QTextBrowser::document()->size();

after setting the html, and then resizing it the QTextBrowser afterwards.

1
votes

i achieve a similar effect by using the following C++ class:

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();
}

for example, calling TextEdit::fitToDocument( Qt::Horizontal ) will set the widget's width to a fixed width exactly large enough to fit the document and its surroundings (e.g. a vertical scrollbar, if there is one). if your goal is to have this happen whenever the contents change, connect the QTextEdit::textChanged() signal to a slot which calls TextEdit::fitToDocument().

as for your issue with QLabel, the solution is simple: call QLabel::setTextInteractionFlags( Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse ).

1
votes

Maybe take a look at Python QT Automatic Widget Resizer. It's written in python though but it may give you some ideas on how to go about doing what you need.

0
votes

Ok implement sizeHint() method. And every time your content change size call updateGeometry() When content change without changing size use update(). (updateGeometry() automatically call update()).