How to adjust QTextEdit to fit it's contents - c++

I'm developing a Qt Application and I'm trying to find a way to use QTextEdit as a label with long text without the scroll bar. In my ui I have a QScrollArea and inside of it I want to place a couple off QTextEdit widgets and I only want use scrolling inside QScrollArea. Problem is that no matter how I try to resize the QTextEdit it seems it has a maximum height and cuts of text, even if I set the size manually and QTextEdit::size returns the correct value.
I did the same thing with QLabel and it works fine, but in this case I need some methods that are only provided in QTextEdit.
I found this post:
Resizing QT's QTextEdit to Match Text Height: maximumViewportSize()
And the answer given was the following:
I have solved this issue. There were 2 things that I had to do to get
it to work:
Walk up the widget hierarchy and make sure all the size policies made
sense to ensure that if any child widget wanted to be big/small, then
the parent widget would want to be the same thing.
This is the main
source of the fix. It turns out that since the QTextEdit is inside a
QFrame that is the main widget in a QScrollArea, the QScrollArea has a
constraint that it will not resize the internal widget unless the
"widgetResizable" property is true. The documentation for that is
here: http://doc.qt.io/qt-4.8/qscrollarea.html#widgetResizable-prop.
The documentation was not clear to me until I played around with this
setting and got it to work. From the docs, it seems that this property
only deals with times where the main scroll area wants to resize a
widget (i.e. from parent to child). It actually means that if the main
widget in the scroll area wants to ever resize (i.e. child to parent),
then this setting has to be set to true. So, the moral of the story is
that the QTextEdit code was correct in overriding sizeHint, but the
QScrollArea was ignoring the value returned from the main frame's
sizeHint.
The problem is that I have no idea how to access the QTextEdit's QScrollArea to enable widgetResizable. Can anyone explain how I can achieve this or suggest a different way of resizing QTextEdit to perfectly fit it's content?

This will allow the height of the text box to change as required. You can edit the code a little to handle the width as well.
connect( m_textField, SIGNAL( textChanged() ), this, SLOT( onTextChanged() ) );
void MyClass::onTextChanged()
{
QSize size = m_textField->document()->size().toSize();
m_textField->setFixedHeight( size.height() + 3 );
}

Try this one :
QTextEdit textEdit;
textEdit.setHtml("<p>test test test test test test</p><p>|||||||||</p>");
textEdit.show();
textEdit.setFixedWidth(textEdit.document()->idealWidth() +
textEdit.contentsMargins().left() +
textEdit.contentsMargins().right());

Without a concrete example it's difficult to judge, but... it sounds as if you simply want a QTextEdit whose sizeHint depends on the current document size.
class text_edit: public QTextEdit {
using super = QTextEdit;
public:
explicit text_edit (QWidget *parent = nullptr)
: super(parent)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
virtual QSize sizeHint () const override
{
QSize s(document()->size().toSize());
/*
* Make sure width and height have `usable' values.
*/
s.rwidth() = std::max(100, s.width());
s.rheight() = std::max(100, s.height());
return(s);
}
protected:
virtual void resizeEvent (QResizeEvent *event) override
{
/*
* If the widget has been resized then the size hint will
* also have changed. Call updateGeometry to make sure
* any layouts are notified of the change.
*/
updateGeometry();
super::resizeEvent(event);
}
};
Then use as...
QScrollArea sa;
sa.setWidgetResizable(true);
text_edit te;
te.setPlainText(...);
sa.setWidget(&te);
sa.show();
It appears to work as expected in the few tests I've done.

In ui i defined QTextEdit *textEdit object. I write it as height scalable-content :
int count = 0;
QString str = "";
// set textEdit text
ui->textEdit->setText("hfdsf\ncsad\nfsc\dajkjkjkjhhkdkca\n925");
str = ui->textEdit->toPlainText();
for(int i = 0;i < str.length();i++)
if(str.at(i).cell() == '\n')
count++;
// resize textEdit (width and height)
ui->textEdit->resize(ui->textEdit->fontMetrics().width("this is the max-length line in qlabel")
, ui->textEdit->fontMetrics().height() * (count + 2));
Notice : this work if you change QTextEdit font face or size! just in height scalable (before every thing set your QTextEdit frameShape to BOX).
if you want do width scalable-content, you should do these steps :
read QTextEdit(textEdit object) text as line to line
calculate every line length
select maximum of line length
use of QTextEdit::fontMetrics().width(QString str) for investigate str size in width
I hope this can help you...

Related

Setting minimum width of QPushButton while keeping default minimumSizeHint behavior

In my application, I need all QPushButtons to have a width of at least 150px, but bigger if needed.
To do so, I am using a global stylesheet (which contains many other properties) with this simple constraint :
QPushButton {
min-width: 150px;
}
The thing is, I also want buttons with a text that doesn't fit inside 150px to be unable to shrink to a width below which the whole text wouldn't be displayed.
This is supposed to be the normal behavior for a QPushButton, but the problem is that, as explained in the documentation for minimumSizeHint() :
QLayout will never resize a widget to a size smaller than the minimum size hint unless minimumSize() is set or the size policy is set to QSizePolicy::Ignore. If minimumSize() is set, the minimum size hint will be ignored.
This leads to some cases where buttons with long texts are displayed at the right size at startup, but when shrinking the window, the button gets too small to display all the text.
I have prepared a simple example that shows this behavior :
#include <QWidget>
#include <QApplication>
#include <QHBoxLayout>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *w = new QWidget();
QLayout* layout = new QHBoxLayout(w);
QPushButton* buttonA = new QPushButton("A", w);
QPushButton* buttonB = new QPushButton("B", w);
buttonB->setStyleSheet("min-width: 150px");
QPushButton* buttonC = new QPushButton("Long long text that doesn't fit in 150px", w);
QPushButton* buttonD = new QPushButton("Very very very long text that doesn't fit in 150px", w);
buttonD->setStyleSheet("min-width: 150px");
layout->addWidget(buttonA);
layout->addWidget(buttonB);
layout->addWidget(buttonC);
layout->addWidget(buttonD);
w->show();
return a.exec();
}
I thought about using dynamic properties to set the minimum width constraint only to buttons that have a base width < 150px, but this doesn't seem to be doable.
Is there a way to do what I want with stylesheets, or do I have to subclass QPushButton and override the minimumSizeHint() method (which I'd like to avoid as I would have to replace a lot of buttons in my app)?
It would be better if you sub-class QPushButton. But there are 2 work around for this problem:
You can strip the long text to specific numbers of characters and use setToolTip(QString) function to show full text when mouse will enter the button.
You can override parent widget resizeEvent and can check the widths and go for approach number 1 if the size is getting really small.
Use Elide Text example to get idea about elide for QPushButton. Not sure if this will work.
I haven't been able to find a way to do this using only stylesheets, but here is the solution I came up with by subclassing QPushButton
MyPushButton.h
#include <QPushButton>
class MyPushButton
: public QPushButton
{
Q_OBJECT
Q_PROPERTY(int minWidth READ minWidth WRITE setMinWidth)
public:
MyPushButton(QWidget* parent = nullptr);
virtual ~MyPushButton();
int minWidth() const;
void setMinWidth(int width);
virtual QSize minimumSizeHint() const override;
private:
int _minWidth;
};
MyPushButton.cpp
#include "MyPushButton.h"
MyPushButton::MyPushButton(QWidget* parent)
: QPushButton(parent)
, _minWidth(0)
{
}
MyPushButton::~MyPushButton()
{
}
int MyPushButton::minWidth() const
{
return _minWidth;
}
void MyPushButton::setMinWidth(int width)
{
_minWidth = width;
}
QSize MyPushButton::minimumSizeHint() const
{
// if the minimum width is less than minWidth, set it to minWidth
// else return the default minimumSizeHint
return QSize(qMax(QPushButton::minimumSizeHint().width(), _minWidth),
QPushButton::minimumSizeHint().height());
}
stylesheet.qss
MyPushButton {
qproperty-minWidth: 150;
}
Buttons with a minimumSizeHint width lower than 150px will be forced to that size, bigger ones will keep the default behavior of QPushButton.

Lock app orientation to landscape in Qt

I would like to lock my app developed with Qt to landscape orientation, even if the screen display is portrait. I have tried adding to my code the resizeEvent method found here: http://qt-project.org/doc/qt-4.8/widgets-orientation.html, but my app still does not display correctly. Here is my code for resizeEvent:
void MainWindow::resizeEvent(QResizeEvent *event)
{
QSize size = event->size();
qDebug() << size;
bool isLandscape = size.width() > size.height();
if (isLandscape == false){
size.transpose();
}
this->setFixedSize(size);
}
Does anyone know how to do this in Qt 4.8.5? I am trying to display an app for a 320x240 display.
Thanks
You can't really follow that example. It shows two different widgets depending on the orientation. Furthermore the doc warns about modifying size properties inside resizeEvent.
One solution would be to set a fix aspect ratio similar to 320x240 by overloading QWidget::heightForWidth. You wouldn't need to overload resizeEvent.
It will look like
int MainWindow::heightForWidth( int w ) {
return (w * 240 )/320;
}
heightForWidth is discussed in detail in https://stackoverflow.com/a/1160476/1122645.
edit:
sizeHint is used by the layout of the parent to compute the size of children widgets. In general, it make sense to implement it as
QSize MainWindow::sizeHint() const
{
int w = //some width you seem fit
return QSize( w, heightForWidth(w) );
}
but if MainWindow is top level then it will not change anything. You can also alter heightForWidth flag of your current size policy
QSizePolicy currPolicy = this->sizePolicy();
currPolicy->setHeightForWidth(true);
this->SetSizePolicy(currPolicy);
But again, if it is a top level widget I doesnt change much.
Anyway you don't need to call updateGeometry.

Resizing a QPixmap inside a QLabel according to the Window

In my window class, inheriting of QMainWindow, I have a QLabel containing a QPixmap, updated every 20ms.
I want the QLabel, and the QPixmap inside it, to be resized according to the resizing of the window.
I want this Central Widget to take as much space as neccessary but also to be able to resize it down. Even smalled than the original size. But always keeping the ratio.
The actual code :
// in the window constructor
this->setWindowFlags(Qt::Window);
this->resize(500, 300);
this->setCentralWidget(this->label);
// in the updating function
QPixmap output;
output = output.fromImage(Mat2QImage(theImage));
this->label->setPixmap(output);
Now I've tried with :
output.scaled(this->label->x(), this->label->y(), Qt::KeepAspectRatio)
but it doesn't work ...
How can I do that ?
EDIT : I'm using Qt 5.3
QPixmap::scaled is const. Next code doesn't work:
// in the window constructor
this->setCentralWidget(this->label);
// in the updating function
QPixmap output;
output = output.fromImage(Mat2QImage(theImage));
output.scaled( this->label->x(), this->label->y(), Qt::KeepAspectRatio );
this->label->setPixmap(output);
Because output doesn't change. Maybe you need something like this:
// in the window constructor
this->setCentralWidget(this->label);
// in the updating function
QPixmap output;
output = output.fromImage(Mat2QImage(theImage));
output = output.scaled( this->label->x(), this->label->y(), Qt::KeepAspectRatio );
this->label->setPixmap(output);
Try puting your QLabel inside a Layout.
hl = new QHBoxLayout;
hl->addWidget(label);
centralWidget()->setLayout(hl);
check this out: http://qt-project.org/doc/qt-4.8/layout.html

QWidget paint even not being called even with non-zero width and height?

I am trying to make a scroll area hold a widget which will serve as a drawing area for a drag-and-drop editor I am trying to build. However, I can't seem to get it to draw.
Here is a picture: http://i.imgur.com/rTBjg.png
On the right the black space is what is supposed to be my scrollarea
Here is the constructor for my the window class (I am using Qt-Creator):
ModelWindow::ModelWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ModelWindow)
{
ui->setupUi(this);
editor = new ModelEditorWidget(this);
ui->scrollArea->setWidget(editor);
}
The model editor widget looks like so:
//header
class ModelEditorWidget : public QWidget
{
Q_OBJECT
public:
explicit ModelEditorWidget(QWidget *parent = 0);
signals:
public slots:
protected:
virtual void paintEvent(QPaintEvent *e);
};
//.cpp file:
ModelEditorWidget::ModelEditorWidget(QWidget *parent) :
QWidget(parent)
{
this->setAcceptDrops(true);
this->resize(1000, 1000);
cout << this->rect().x() << " " << this->rect().width() << endl;
this->update();
}
void ModelEditorWidget::paintEvent(QPaintEvent *e)
{
cout << "painting";
QWidget::paintEvent(e);
QPainter painter(this);
painter.setBrush(QBrush(Qt::green));
painter.setPen(QPen(Qt::red));
painter.drawRect(400, 400, 50, 50);
painter.fillRect(e->rect(), Qt::SolidPattern);
}
I would think that this would set the modeleditorwidget's size to be 1000x1000 and then draw a green or red rectangle on the widget. However, the lack of a "painting" message in the command line from the cout at the beginning of the paintEvent shows that it isn't being executed. I first suspected this is because there was 0 width and 0 height on the widget. However, the cout in the constructor tells me that the widget is positioned at x = 0 and width = 1000 and so I assume that since that matches my resize statement that the height is also 1000 as was specified.
EDIT: By calling cout.flush() I got the "painting" output. However, that only deepens the mystery since the paint event doesn't look like it is actually painting. I am now calling show on both the scroll area and the widget as well.
Does anyone see what I could be doing wrong here? Perhaps I am not adding the ModelEditorWidget to the scrollarea properly?
Btw, I am very new to Qt and this is my first major GUI project using it. Most of my other GUI stuff was done using .NET stuff in C#, but since I want this to be cross platform I decided to stay away from C#.NET and mono and use Qt.
Documentation of QScrollArea::setWidget() says:
If the scroll area is visible when the widget is added, you must
show() it explicitly.
Note that You must add the layout of widget before you call this
function; if you add it later, the widget will not be visible -
regardless of when you show() the scroll area. In this case, you can
also not show() the widget later.
Have you tried that?

Qt: how to make mainWindow automatically resize when centralwidget is resized?

I would like to have my CentralWidget a certain size. What do I need to do to make my mainWindow resize along it's central widget? here the code that doesn't work:
int main (int argc, char **argv) {
QApplication app(argc, argv);
QGLFormat glFormat;
glFormat.setVersion(4,2);
glFormat.setProfile( QGLFormat::CompatibilityProfile);
QGLWidget* render_qglwidget = new MyWidget(glFormat);
QGLContext* glContext = (QGLContext *) render_qglwidget->context();
glContext->makeCurrent();
QMainWindow* mainWindow = new MyMainWindow();
render_qglwidget->resize(720, 486);
mainWindow->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding));
mainWindow->setCentralWidget(render_qglwidget);
render_qglwidget->resize(720, 486);
mainWindow->show();
return app.exec();
}
the window that opens will be very small.
i can set the size of the mainwindow using
mainWindow->resize(720, 486);
and the centralwidget will also change it's size. but the central widget will be slightly squashed because the toolbar of the mainWindow also lies within those 486 pixels.
How to let the mainWindow resize automatically?
You can reimplement QMainWindow::event() to resize the window automatically whenever the size of the central widget changes:
bool MyMainWindow::event(QEvent *ev) {
if(ev->type() == QEvent::LayoutRequest) {
setFixedSize(sizeHint());
}
return result = QMainWindow::event(ev);
}
You also have to use setFixedSize() for the central widget instead of just resize(). The latter is almost useless when the widget is placed inside a layout (which is a QMainWindowLayout here).
Set the size of the central widget. Then get the sizehint for the main window and set it to it's size.
(sizeHint(), setFixedSize()).
It is not possible to set the size of the QMainWindow based up its central widget. It is always the other way around.
As you said, use QMainWindow::resize(), adding the toolbar and statusbar height to your central widget to get the final result.
Be also sure to resize in a "delayed init" (i.e. a QTimer with a timeout of 0), so that the height of the toolbar and statusbar are accurate.
If you want your main window to be unresizable by the user, use QMainWindow::setWindowFlags(Qt::FramelessWindowHint) to disable resize by mouse dragging.
I used the following code to solve a similar problem. I wanted to explicitly resize my main window so that the central widget has a given size.
def centralWidgetResize(self, x, y):
# If the window is not visible, it doesn't keep its layout up to date, so force it.
if not self.isVisible():
self.layout().update()
# Does nothing if the layout is already up to date (and the window is visible).
self.layout().activate()
size = self.size()
childsize = self.centralWidget().size()
dx = size.width() - childsize.width()
dy = size.height() - childsize.height()
self.resize(x + dx, y + dy)
It's Python but the C++ code should be a straightforward translation. It also works with weird toolbar placements or multiple toolbars. Note that the actual updating of the size only happens when the window is shown, if it is needed immediately and the window is hidden do another layout().update(); layout().activate().