Fit QDialog window to size of text - qt

I have a QDialog class that takes in a QString. I am calling setFixedSize with a set width and height but I want the QDialog to be more dynamic and fit to the size of the text.
I have tried adjustSize() but all that did was shrink the window to the point where the text was cut off.
ConfirmDialog::ConfirmDialog(const QString& message, QWidget* parent)
: QDialog(parent)
{
setFixedSize(WIDTH, HEIGHT);
statusLabel->setText(tr("Confirmation"));
statusDetailsLabel->setText(message);
statusDetailsLabel->setWordWrap(true);
}
I always see a Window with size of dimensions WIDTH and HEIGHT. I want it to fit the test.

One way would be to use Font Metrics to get the bounding rects of each label, and then set the window size to the sum of both rects + some padding to make it look nice.
One problem you will run into is having wordwrap on. How do you determine the width of the window, if you are word wrapping? So I've added a "MAXWIDTH" for the window. If your text is shorter and does not require word wrap - the window will shrink to fit it. If it does require word wrap, it will not go over your set size.
ConfirmDialog::ConfirmDialog(const QString& message, QWidget* parent)
: QDialog(parent)
{
const int MAXWIDTH = 400;
const int VERTICALPADDING = 50;
// Create Layout
QLabel *statusLabel = new QLabel(this);
QLabel *statusDetailsLabel = new QLabel(this);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(statusLabel);
layout->addWidget(statusDetailsLabel);
setLayout(layout);
// Populate Text
statusLabel->setText(tr("Confirmation"));
statusDetailsLabel->setText(message);
statusDetailsLabel->setWordWrap(false); // Start w/ word wrap off.
// Font metrics to get the sizes of our text.
QFontMetrics fontMetricsLabel(statusLabel->font());
QFontMetrics fontMetricsDetail(statusDetailsLabel->font());
// Get max width - label or detail lable, whichever is longer.
int width = std::max(fontMetricsLabel.boundingRect("Confirmation").width(),
fontMetricsDetail.boundingRect(message).width());
// Check that we do not go over our MAXWIDTH.
if(width > MAXWIDTH) width = MAXWIDTH;
// Enable word wrapping.
statusDetailsLabel->setWordWrap(true);
// Get the heigts of both boxes.
int height = std::max(fontMetricsLabel.boundingRect("Confirmation").height(),
fontMetricsDetail.boundingRect(message).height());
// Set window size.
this->setFixedSize(width, height + VERTICALPADDING);
}

Related

How to calculate QTextEdit height accurately by its contents to get rid of vertical scrollbars?

I need a QTextEdit widget which height grows or shrinks to the size of the inner content. I didn't find built-in solution and decided to make my own with connections to &QTextEdit::textChanged signals that will update size of the widget at runtime. In the code snippet I flattened my custom component to raw QTextEdit for simplicity. The problem is that calculation via QFontMetrics is inaccurate, a vertical scrollbar appears with only some pixels to scroll
I want to get rid of any vertical scrollbars in these text widgets.
However, I don't need to get rid of horizontal scrollbars and here is the second problem arises. Seemingly, horizontal scrolls occupy space in the viewport of the text widget and its internal height shrinks by the height of the scrollbar itself
void TestWindow::setupUi(QWidget *parent)
{
QVBoxLayout *v_box0 = new QVBoxLayout(parent);
v_box0->setContentsMargins(0, 0, 0, 0);
QVBoxLayout *v_scroll_layout = new QVBoxLayout();
v_scroll_layout->setContentsMargins(0, 0, 0, 0);
v_scroll_layout->setSpacing(0);
v_scroll_layout->setAlignment(::Qt::AlignTop);
QWidget *scroll_content = new QWidget();
scroll_content->setLayout(v_scroll_layout);
QScrollArea *v_scroll = new QScrollArea();
v_scroll->setWidgetResizable(true);
v_scroll->setWidget(scroll_content);
v_box0->addWidget(v_scroll);
QSize size;
QTextEdit *edit0 = new QTextEdit();
edit0->setLineWrapMode(QTextEdit::NoWrap);
edit0->setText("Hello World 1\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11");
edit0->setFont(viewer_font);
size = edit0->fontMetrics().size(0, edit0->document()->toPlainText());
edit0->document()->setDocumentMargin(0);
edit0->setMinimumHeight(size.height());
QTextEdit *edit1 = new QTextEdit();
edit1->setLineWrapMode(QTextEdit::NoWrap);
edit1->setText("New item");
edit1->setFont(viewer_font);
size = edit1->fontMetrics().size(0, edit1->document()->toPlainText());
edit1->document()->setDocumentMargin(0);
edit1->setMinimumHeight(size.height());
v_scroll_layout->addWidget(edit0);
v_scroll_layout->addWidget(edit1);
setFixedHeight(300);
}

manual new line in QGraphicsText Item

i created a editable text class inheriting QGraphicsTextitem and with setTextIntraction flag im allowing the user to enter the text in the QGraphicsTextItem.
now i want to add a new line manually when the graphicsTextitem width exceeds the viewport width . but i cant find the relevant way to manually add a new line when the textWidth exceeds the width of the viewport.
i managed to shift the text left and up as
QGraphicsView *view = scene()->views()[0];
QRect viewport_rect(0, 0, view->viewport()->width(), view->viewport()->height());
QRectF visible_scene_rect = view->mapToScene(viewport_rect).boundingRect();
qreal sceneRightEnd = visible_scene_rect.right();
qreal sceneBottom = visible_scene_rect.bottom();
QPointF textItemRight = mapToScene( now.bottomRight() ) ;
if(textItemRight.x() >= sceneRightEnd)
{
this->moveBy(sceneRightEnd - textItemRight.x(), 0);
}
if(textItemRight.y() >= sceneBottom )
{
this->moveBy( 0, ( sceneBottom - textItemRight.y() ));
}
You can set the width of your text item by calling:
QGraphicsTextItem::setTextWidth(qreal width)
The default width is -1 which causes the text not to be broken into multiple lines.
If you set a width and the actual text is wider than the specified width then it will be broken into multiple lines.

QT paintEvent for a QWidget

I've a class that inherits QPushButton widget. I want to have custom look of that button so i've overrided paintEvent method. All buttons that I want to paint are childs of QFrame object.
And there I have a problem. I can't repaint those objects.
My paintEvent function:
void Machine::paintEvent(QPaintEvent*) {
QPainter painter(this);
QRect geo = this->geometry();
int x, y, width, height;
x = geo.x()-10;
y = geo.y()-10;
width = geo.width()-3;
height = geo.height()-5;
painter.fillRect(x, y, width, height, QColor(220,220,220));
painter.drawText(x+10, y+10, "Machine " + QString::number(id));
}
When a widget is in top left corner of QFrame, desired effect is ok. But when I move button somewhere else, widget starts to disappear. On images you can see whats going on:
Button is just moved some px down-left. Why it works like this? QFrame which is a container for that button is big enough.
Thanks in advance ;)
The reason is in coordinate system: geometry method returns position relatively to parent, but QPainter::drawRect accepts rectangle in local coordinates. Try this code:
void Machine::paintEvent(QPaintEvent*) {
QPainter painter(this);
int width = size().width() - 3;
int height = size().height() - 5;
painter.fillRect(0, 0, width, height, QColor(220,220,220));
painter.drawText(10, 10, "Machine " + QString::number(id));
}

Setting initial size of QTabWidget

I am creating a QTabWidget that has a number of tabs and each tab contains a different number of push buttons. Because some of these tabs may include many push buttons, I would like to make each tab scrollable.
I have an implementation that sort of works except no matter what I try I can't get the darn QTabWidget to be of a particular vertical size - it always wants to size itself based on the maximum height used by one of its pages. I've tried changing size policies, layout strategies and nothing works.
Here is exact widget structure I am using:
QFrame
QSplitter
QTabWidget
QFrame
I populate the QTabWidget with QWidget instances that serve as pages. These instances are wrapped around with a QScrollArea and use a QGridLayout so that I can populate the tab with push buttons in a grid.
Here is the actual code that populates the tab widget:
for (const std::string &tabName : tabs) {
// Grid layout for each tab
QGridLayout *gridLayout = new QGridLayout();
// The tab widget itself
QWidget *page = new QWidget(tabWidget);
page->setLayout(gridLayout);
// Wrap tab with a scroll area so that it's scrollable when the tab widget
// is resized to be smaller than the page size
QScrollArea *scrollArea = new QScrollArea(tabWidget);
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(page);
tabWidget->addTab(scrollArea, tabName.c_str());
// Populate tab with push buttons
const std::vector<std::string> &buttons = GetButtons(tabName);
for (const std::string &buttonName : buttons) {
QPushButton *button = new QPushButton(buttonName.c_str());
button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
QSize buttonSize(130, 20);
button->setFixedSize(buttonSize); // <=== this does work
int row = i / numColumns;
int col = i % numColumns;
gridLayout->addWidget(button, row, col, Qt::AlignLeft | Qt::AlignTop);
}
}
// This does not work
const int fixedVerticalSize = 120;
tabWidget->resize(tabWidget->size().width(), fixedVerticalSize);
BTW, I am using Qt 5.0.2 on Mac OS X 10.8.2.

Layout with two tableviews problem

I have a widget containing two QTableViews in a horizontal layout. I would like them to resize to their preferred size (so that they don't have to show autoscrolls). I also don't want them to grow more than their preferred size and leave empty space to the right if there is any left. It should look like this:
I've tried to achieve it with a spacer on the right, but the views don't grow because of autoscrolls.
Can you propose any solution?
If I understood your question, you want the two QTableViews to resize to their PreferredSize if there is enough space (but no more than thier PreferredSize) and to shrink if there is not enough space. If there is too much space, it should be left empty. Here's an example that does it:
#include <QtGui>
int main(int argc, char **argv) {
QApplication app(argc, argv);
QMainWindow w;
QHBoxLayout *hbox = new QHBoxLayout;
QWidget *centralw = new QWidget;
centralw->setLayout(hbox);
w.setCentralWidget(centralw);
QTableView *t1 = new QTableView;
QTableView *t2 = new QTableView;
// Version one: the preferred size is the maximum size
// t1->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
// t2->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred));
// Version two: the preferred size is the only accepted size
// if you want the widgets not to change their size, change the two previous lines with
// t1->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
// t2->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
// hbox->addWidget(t1);
// hbox->addWidget(t2);
// add a stretch that will fill empty space
// hbox->addStretch(1);
// Version three: tables have a minimum and maximum width. They can be shrunk
// but they try to expand to take the maximum available space up to their maximum
// width.
t1->setMinimumWidth(150);
t1->setMaximumWidth(400);
t2->setMinimumWidth(100);
t2->setMaximumWidth(200);
t1->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
t2->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
hbox->addWidget(t1);
hbox->addWidget(t2);
hbox->setStretchFactor(t1, 1);
hbox->setStretchFactor(t2, 1); // tableviews have the same stretch factor
hbox->addStretch(0); // lowest stretch factor
w.show();
return app.exec();
}
You can reimplement parent's widget resizeEvent function and not to use a layout at all. So when the parent widget is resized you can manually set the sizes of your tables.
The other way you can try is to reimplement sizeHint or/and minimumSizeHint functions of the QTableView so they return a good for you size.
Also take a look at QSizePolicy - it may be usefull

Resources