How do I resize QTableView so that the area is not scrolled anymore - qt

I want the size of the QTableView to be the same as the table it contains (and fixed) so that it does not have a scrollbar

What you could do is calculate your tableview columns widths according to the data they have (or you can just call resizeColumnToContents for each column to size it to its content). Then change the tableview width to be equal or more then total width of columns + vertical header if shown. You would also need to track model changes and adjust your tableview width + if horizontal header is shown you can track columns resize events and adjust them again. Below is some sample code for this:
initialization:
// add 3 columns to the tableview control
tableModel->insertColumn(0, QModelIndex());
tableModel->insertColumn(1, QModelIndex());
tableModel->insertColumn(2, QModelIndex());
...
// switch off horizonatal scrollbar; though this is not really needed here
ui->tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// adjust size; see code below
adjustTableSize();
// connect to the horizontal header resize event (non needed if header is not shown)
connect(ui->tableView->horizontalHeader(),SIGNAL(sectionResized(int,int,int)), this,
SLOT(updateSectionWidth(int,int,int)));
// connect to the model's datachange event
connect(ui->tableView->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(dataChanged(QModelIndex,QModelIndex)));
adjust tableview size:
void MainWindow::adjustTableSize()
{
ui->tableView->resizeColumnToContents(0);
ui->tableView->resizeColumnToContents(1);
ui->tableView->resizeColumnToContents(2);
QRect rect = ui->tableView->geometry();
rect.setWidth(2 + ui->tableView->verticalHeader()->width() +
ui->tableView->columnWidth(0) + ui->tableView->columnWidth(1) + ui->tableView->columnWidth(2));
ui->tableView->setGeometry(rect);
}
process model change
void MainWindow::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
adjustTableSize();
}
process horizontal header resize
void MainWindow::updateSectionWidth(int logicalIndex, int, int newSize)
{
adjustTableSize();
}
hope this helps, regards

sum(item.sizeHint()+headeroffset+border) doesn't work well for me, there's probably spacing between the items, even if grid is off. So I made adjustment this way:
view->resizeRowsToContents();
view->resizeColumnsToContents();
QAbstractItemModel* model = view->model();
QHeaderView* horHeader = view->horizontalHeader();
QHeaderView* verHeader = view->verticalHeader();
int rows = model->rowCount();
int cols = model->columnCount();
int x = horHeader->sectionViewportPosition(cols-1) + horHeader->offset()
+ horHeader->sectionSize(cols-1) + 1;
int y = verHeader->sectionViewportPosition(rows-1) + verHeader->offset()
+ verHeader->sectionSize(rows-1) + 1;
QPoint p = view->viewport()->mapToParent(QPoint(x,y));
QRect g = view->geometry();
g.setSize(QSize(p.x(),p.y()));
view->setGeometry(g);
Should work if the last column and last row is visible.

I tried serge_gubenko answer but I didn't work for me (Partly because I wanted to rezise both Height and Width)... so I altered it; To avoid the table being resized by layouts or parent widgets you will need this:
ui->tableView->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
Then:
ui->tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->tableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QRect rect = ui->tableView->geometry();
int width = 2,length = 2;
for(int col = 0;col<proxySortModel->columnCount();++col){
if(!ui->tableView->isColumnHidden(col))
width += ui->tableView->columnWidth(col);
}
for(int row =0;row<proxySortModel->rowCount();++row)
length += ui->tableView->rowHeight(row);
rect.setWidth(width);
rect.setHeight(length);
ui->tableView->setGeometry(rect);
I hope this helps someone.

Related

Qt connect QScrollBar with QLineEdit

I would like to "activate" the scrollbar when the lineedit text is too long to display on the window. I have already done it.
I want to move the cursor with the scrollbar. I also want to modify the scroll bar slider length with the increment/decrement of the text length.
.h
private:
Ui::MainWindow *ui;
QLineEdit* LineEdit;
QScrollBar* hScrollBar;
void HDScrollBar();
constructor:
resize(400,100);
LineEdit = new QLineEdit(this);
LineEdit->resize(400,100);
LineEdit->setFont(QFont("Times",20));
hScrollBar = new QScrollBar(Qt::Horizontal, LineEdit);
hScrollBar->resize(400,20);
hScrollBar->move(0,80);
hScrollBar->hide();
connect(LineEdit, &QLineEdit::textChanged, this, &MainWindow::HDScrollBar);
hide/display scrollbar
void MainWindow::HDScrollBar() {
QFont myFont(QFont("Times",20));;
QString str = LineEdit->text();
QFontMetrics fm(myFont);
int width = fm.horizontalAdvance(str);
(width >= 400) ? hScrollBar->show() : hScrollBar->hide();
}
As for the first part, you use can scrollbar's valueChanged signal, e.g. this way:
connect(ui->horizontalScrollBar, &QScrollBar::valueChanged, ui->lineEdit, [&](int v) {
auto scrollbarAt = static_cast<double>(v) / ui->horizontalScrollBar->maximum();
auto cursorTo = std::round(ui->lineEdit->text().length() * scrollbarAt);
ui->lineEdit->setCursorPosition(cursorTo);
});
EDIT:
as for the latter part:
you can either alter the PageStep of the scrollbar, or you can set it to 1 and alter its maximum value, then also the first part should simplify:
ui->horizontalScrollBar->setPageStep(1);
ui->horizontalScrollBar->setMaximum(ui->lineEdit.text().length());
connect(ui->horizontalScrollBar, &QScrollBar::valueChanged,
ui->lineEdit, &QLineEdit::setCursorPosition);
//this can also ben done on textChanged, however for the price
//of more frequent execution...
connect(ui->lineEdit, &QLineEdit::cursorPositionChanged,
ui->horizontalScrollBar, [&](int, int n) {
ui->horizontalScrollBar->setMaximum(ui->lineEdit->text().length());
//...one gets the easy way to track the cursor
//with the slider
ui->horizontalScrollBar->setValue(n);
});

Fit QDialog window to size of text

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

equal row heights for QFormLayout

I am using QFormLayout with QLabels in the left column and various widgets in the right column. On the right, there are either labels, check boxes, combos or line edits. Unfortunately each of there controls has different natural height. But I would like to have each row in the form layout to have equal heights determined by the biggest one (I know in which row it is). Is there any simple way to achieve this? I cannot find anything like QFormLayout::setRowHeight().
One solution, just assign equal size to all widgets at runtime using the following function:
void setEqualRowHeight(QFormLayout *formLayout, int height)
{
QWidget *w;
for(int i = 0; i < formLayout->rowCount(); i++) {
QLayoutItem *item = formLayout->itemAt(i, QFormLayout::FieldRole);
if (item && (w = item->widget())) {
w->setFixedHeight(height);
}
}
}

Maintaining relative child position after applying QGraphicsItem::ItemIgnoresTransformations

I have a QGraphicsTextItem parented to a QGraphicsItem. I want the QGraphicsTextItem to always reside directly above the QGraphicsItem, but I also want the text to remain the same size when the scale factor goes below 1, i.e. the text remains the size it is at a scale factor of 1 even when the parent graphics item is scaled smaller. I have found that setting the QGraphicsItem::ItemIgnoresTransformations flag to true when the scale factor is below 1 does the trick for retaining the size.
But I can’t seem to find a way to get the position of the text to always remain above the QGraphicsItem. Is there a way to do this? I tried using deviceTransform () function, but the text still moved off of the QGraphicsItem as I scrolled out. What was worse is that some of the text items started “jiggling”, i.e. they started continuously changing their position ever so slightly, so that it looked like they were shaking. If this is the function I need to use, I guess I don’t know how to use it properly.
In the constructor of my QGraphicsItem I’ve added a QGraphicsTextItem:
fTextItem = new QGraphicsTextItem(getName(), this);
fTextItem->setFlag(QGraphicsItem::ItemIgnoresTransformations);
Here is code snippet from paint function of QGraphicsItem
qreal lod = painter->worldTransform().m22();
if(lod <= 1.0) {
fTextItem-setFlag(QGraphicsItem::ItemIgnoresTransformations);
fTextItem->setPos(fTextItem->deviceTransform(view-viewportTransform()).inverted().map(view->mapFromScene(mapToScene(0,0))));
} else {
fTextItem->setFlag(QGraphicsItem::ItemIgnoresTransformations, false);
fTextItem->setPos(0, 0);
}
My suggestion is to subclass QGraphicsSimpleTextItem in this manner:
class TextItem
: public QGraphicsSimpleTextItem
{
public:
TextItem(const QString &text)
: QGraphicsSimpleTextItem(text)
{
}
void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->translate(boundingRect().topLeft());
QGraphicsSimpleTextItem::paint(painter, option, widget);
painter->translate(-boundingRect().topLeft());
}
QRectF boundingRect() const
{
QRectF b = QGraphicsSimpleTextItem::boundingRect();
return QRectF(b.x()-b.width()/2.0, b.y()-b.height()/2.0,
b.width(), b.height());
}
};
QGraphicsSimpleTextItem *mText = new TextItem("Item");
scene()->addItem(mText);
mText->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
mText->setPos(itemToFollow->pos());
Disclaimer: this may be overkill for what you are trying to do. We had some additional restrictions in our project that made this solution the easiest for us.
We had to do something similar in a project, and it ended up being easiest for us to not use ItemIgnoresTransformations and instead roll our own transform. Here is the main function we use to create a translation-only (no scaling) transform for drawing an item at a specific location. You might be able to modify it for your usage.
static QTransform GenerateTranslationOnlyTransform(
const QTransform &original_transform,
const QPointF &target_point) {
// To draw the unscaled icons, we desire a transform with scaling factors
// of 1 and shearing factors of 0 and the appropriate translation such that
// our icon center ends up at the same point. According to the
// documentation, QTransform transforms a point in the plane to another
// point using the following formulas:
// x' = m11*x + m21*y + dx
// y' = m22*y + m12*x + dy
//
// For our new transform, m11 and m22 (scaling) are 1, and m21 and m12
// (shearing) are 0. Since we want x' and y' to be the same, we have the
// following equations:
// m11*x + m21*y + dx = x + dx[new]
// m22*y + m12*x + dy = y + dy[new]
//
// Thus,
// dx[new] = m11*x - x + m21*y + dx
// dy[new] = m22*y - y + m12*x + dy
qreal dx = original_transform.m11() * target_point.x()
- target_point.x()
+ original_transform.m21() * target_point.y()
+ original_transform.m31();
qreal dy = original_transform.m22() * target_point.y()
- target_point.y()
+ original_transform.m12() * target_point.x()
+ original_transform.m32();
return QTransform::fromTranslate(dx, dy);
}
To use, take the QPainter transform that is passed to the paint method and do something like:
painter->save();
painter->setTransform(GenerateTranslationOnlyTransform(painter->transform(),
some_point));
// Draw your item.
painter->restore();
I've found another solution, which does not involve messing with any transformations or by hand scaling/positioning. There is a hint in QGraphicsItem::ItemIgnoresTransformations flag description:
QGraphicsItem::ItemIgnoresTransformations
The item ignores inherited transformations (i.e., its position is
still anchored to its parent, but the parent or view rotation, zoom or
shear transformations are ignored). [...]
And that's the key! We need two items: a parent that will keep the relative position (without any flags set) and a child item that will do the drawing at parent's (0,0) point (with QGraphicsItem::ItemIgnoresTransformations flag set). Simple as that!
I've encapsulated this functionality into a single class - here is some code:
#include <QGraphicsItem>
#include <QPainter>
class SampleShape : public QGraphicsItem
{
private:
/* This class implements shape drawing */
class SampleShapeImpl : public QGraphicsItem
{
public:
SampleShapeImpl (qreal len, QGraphicsItem *parent = nullptr)
: QGraphicsItem(parent), m_len(len)
{
/* ignore transformations (!) */
setFlag(QGraphicsItem::ItemIgnoresTransformations);
}
QRectF boundingRect (void) const override
{
/* sample bounding rectangle */
return QRectF(-m_len, -m_len, m_len*2, m_len*2);
}
void paint (QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
{
/* draw a shape, (0,0) is an anchor */
painter->drawLine(0, -m_len, 0, m_len);
painter->drawLine(-m_len, 0, m_len, 0);
// ...
}
private:
qreal m_len; // sample shape parameter
};
public:
/* This is actually almost an empty class, you only need to set
* a position and pass any parameters to a SampleShapeImpl class.
*/
SampleShape (qreal x, qreal y, qreal len, QGraphicsItem *parent = nullptr)
: QGraphicsItem(parent), m_impl(len, this) // <-- IMPORTANT!!!
{
/* set position at (x, y), view transformations will apply */
setPos(x, y);
}
QRectF boundingRect (void) const override
{
return QRectF(); // it's just a point, no size
}
void paint (QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override
{
// empty, drawing is done in SampleShapeImpl
}
private:
SampleShapeImpl m_impl;
};
Great answer by Dave Mateer! I had the problem that I wanted to define a different scale factor at different zoom levels. This is how I did it:
void MyGraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
//save painter for later operations
painter->save();
QTransform originalTransform = painter->transform();
QPointF originalCenter = rect().center();
qreal dx = originalTransform.m11() * originalCenter.x() + originalTransform.m21() * originalCenter.y() + originalTransform.m31();
qreal dy = originalTransform.m22() * originalCenter.y() + originalTransform.m12() * originalCenter.x() + originalTransform.m32();
//normally our target scale factor is 1, meaning the item has keeps its size, regardless of zoom
//we adjust the scale factor though when the item is smaller than one pixel in comparison to the background image
qreal factor = 1.0;
//check if scale factor if bigger that the item size, and thus it occupies less that a pixel in comparision to the background image
if (rect().width() < originalTransform.m11()) {
//calculate adjusted scale factor
factor = originalTransform.m11() / rect().width();
}
//adjust position according to scale factor
dx -= factor * originalCenter.x();
dy -= factor * originalCenter.y();
//set the new transform for painting
painter->setTransform(QTransform::fromScale(factor, factor) * QTransform::fromTranslate(dx, dy));
//now paint...
QGraphicsXYZItem::paint(painter, option, widget);
//restore original painter
painter->restore();
}
You do need to adjust the bounding rectangle too in that case:
QRectF MyGraphicsItem::boundingRect() const
{
QRectF rect = QGraphicsEllipseItem::boundingRect();
//this is a bit hackish, let me know if you know another way...
if (scene() != NULL && scene()->views().at(0) != NULL)
{
//get viewport transform
QTransform itemTransform = scene()->views().at(0)->transform();
QPointF originalCenter = rect.center();
//calculate back-projected original size of item
qreal realSizeX = rect.width() / itemTransform.m11();
qreal realSizeY = rect.height() / itemTransform.m11();
//check if scale factor is bigger that the item size, and thus it occupies less that a pixel in comparison
//to the background image and adjust size back to equivalent of 1 pixel
realSizeX = realSizeX < 1.0 ? 1.0 : realSizeX;
realSizeY = realSizeY < 1.0 ? 1.0 : realSizeY;
//set adjusted position and size according to scale factor
rect = QRectF(rect.center().x() - realSizeX / 2.0, rect.center().y() - realSizeY / 2.0, realSizeX, realSizeY);
}
return rect;
}
With this solution the item work very well in my case.
Adding to Dave Mateer's answer, I think it'd be helpful to add that in some scenario, you should also maintain proper bounding rectangle (as well as shape) of the object. For me, I need to modify boundingRect() a little too for proper object selection behavior. Remember that the bounding rect of the object will be scaled and transformed as usual if we do NOT use ItemIgnoresTransformations flag. So we also need to rescale the boundingRect to maintain the view independence effect.
To maintain the view-independent bounding rectangle turns out to be quite easy: just grab the scaling factor from deviceTransform(m_view->viewportTransform()).inverted().m11() and multiply this constant to your local coordinate bounding rectangle. For example:
qreal m = this->deviceTransform(m_view->viewportTransform()).inverted().m11();
return QRectF(m*(m_shapeX), m*(m_shapeY),
m*(m_shapeR), m*(m_shapeR));
here is a solution I devised of very moderate complexity :
1) Get the boundingRect() of the parent and map it to scene
2) take the minimum X and Y of this list of points, this is the real origin of your item, in scene coordinates
3) set the position of the child
In Pyside :
br = parent.mapToScene(parent.boundingRect())
realX = min([item.x() for item in br])
realY = min([item.y() for item in br])
child.setPos(parent.mapFromScene(realX, realY)) #modify according to need

How to set minimum height of QListWidgetItem?

How can I set the minimum height of a QListWidgetItem? I'm using QListWidget::setItemWidget() with a customized widget, and although I explicitly declared minimum height of my customized widget, those QListWidgetItems still have a pretty low height attribute.
To set minimum height of each individual QListWidgetItem you can use sizeHint() function. For example, following code will set minimum height of all the QListWidgetItem to 30px..
int count = ui->listWidget->count();
for(int i = 0; i < count; i++)
{
QListWidgetItem *item = ui->listWidget->item(i);
item->setSizeHint(QSize(item->sizeHint().width(), 30));
}
Hope this helps..
Use setSizeHint on the items.
void QListWidgetItem::setSizeHint ( const QSize & size )
This is the right method for telling the delegate how much screen it must preserve for the item.
Look at http://qt-project.org/doc/qt-4.8/qlistwidgetitem.html#setSizeHint

Resources