equal row heights for QFormLayout - qt

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

Related

Best way to make grid with custom amount of square items in QT

I want to create a custom size grid of squares where user can choose how many columns and rows are in the gird. The sqaures in the grid must be clickable so that they can change color when user clicks them. The grid is ment for pathfinding visalization.
I thought about using QRect that are stored in some kind of map with coordinates or QGraphicsItem in QGraphicsScene. What is the best QT class for this considering performance because i'm not sure if getting the postion of click in mouse event and then looping through huge grid is the best way?
Thank you in advance.
If I were you, I would just put QPushButtons into a QGridLayout. They are clickable and then you can change the colour accordingly.
QGridLayout *buttonLayout = new QGridLayout(this);
size_t rows = 5, columns = 5; // You can make this configurable.
for (size_t row = 0; row < rows; ++row) {
for(size column = 0; column < column; ++column) {
QPushButton *button = new QPushButton;
connect(button, &QPushButton::clicked, this, [this](){/* change colours */});
buttonLayout->addWidget(button, row, column);
}
}
centralWidget->setLayout(buttonLayout);

How do I clear certain lines/paths from a QWidget but not others, then redraw separate lines/paths on top?

I'm trying to optimize line drawings on a QWidget. I basically have a grid in the background and drawings on top of the grid. Currently I'm redrawing the background and grid lines every time the paint event is called. This works fine if the grid lines are far enough apart so I don't have to draw that many lines, but if the scale gets changed, the lines must be redrawn at that new scale. Also, if the window is resized, then more of the grid is displayed, hurting the performance even more.
Here is the code for drawing the grid:
// draw grid
painter.fillRect(0,0,areaWidth, areaHeight, QColor(255,255,255));
painter.setPen(QPen(QBrush(QColor(240,240,255)), 1, Qt::SolidLine, Qt::FlatCap));
int numXLines = areaWidth/mSIToPixelScale + 1;
int numYLines = areaHeight/mSIToPixelScale + 1;
double width = areaWidth;
double height = areaHeight;
for (int x=0; x<numXLines;x++)
{
for (int y=0; y<numYLines; y++)
{
painter.drawLine(0,y*mSIToPixelScale,width, y*mSIToPixelScale);
painter.drawLine(x*mSIToPixelScale,0,x*mSIToPixelScale,height);
}
}
So when numXLines and numYLines in the above code reach higher values, the performance drops very hard, which makes sense. The grid will always have to be redrawn if the scale changes, but if the scale does not change, then only the drawing on top of the grid should change. How can I accomplish this?
The QWidget::paintEvent( QPaintEvent* aEvent ) is called by the framework not just when you want it. So if you want to remove some lines from the widget than you need draw them conditionally in your function.
For example:
if ( numXLines < 25 && numYLines < 25 )
{
// Draw only every second lines for example.
}
else
{
// Draw all lines.
}
But this is not the best way. Maybe you shall use larger steps between the grid lines if there too many of them.
I found where I went wrong, I was redrawing the y lines every x iteration. I fixed it by creating two separate for loops:
// add grid lines to a painter path
QPainterPath grid;
for (int x=0; x<numXLines;x++)
{
grid.moveTo(x*mSIToPixelScale, 0);
grid.lineTo(x*mSIToPixelScale, height);
}
for (int y=0; y<numYLines; y++)
{
grid.moveTo(0, y*mSIToPixelScale);
grid.lineTo(width,y*mSIToPixelScale);
}
painter.drawPath(grid);
painter.end();
Also, I think drawing onto a QImage first then drawing that image inside the paintEvent would make code more organized, so all your doing in the paintEvent is drawing from a high level.

Don't repaint instantly after setVisble in Qt

I'm using QPushButton in my mineSweeping game.
After changing from easy mode to hard mode, the number of QPushButton is supposed to change from 9x9 to 30x16.
So, I add QPushButton with the largest number(which is of hard mode) to GridLayout in constructor of MainWindow.
btnArr = new QPushButton[HARD_WIDTH * HARD_HEIGHT]; // member element
int index = 0;
for (int i = 0; i < HARD_HEIGHT; ++i) {
for (int j = 0; j < HARD_WIDTH; ++j) {
ui->mainGrid->addWidget(&btnArr[index], i, j, 1, 1,
Qt::AlignCenter);
++index;
}
}
Then if the user change mode(e.g.: easy mode to hard mode), resetBtn(HARD_WIDTH, HARD_HEIGHT); will be called.
void MainWindow::resetBtn(const int width, const int height)
{
int index = 0;
for (int i = 0; i < HARD_HEIGHT; ++i) {
for (int j = 0; j < HARD_WIDTH; ++j) {
if (j < width && i < height) {
btnArr[index].setVisible(true);
} else {
btnArr[index].setVisible(false);
}
++index;
}
}
}
The problem is that it seems the widget repaints each time setVisible is called. So in the hard mode case, it will be called 30x16 times, which caused strange effect like this:
So how can I set the widget not repaint during this loop?
Thanks in advance.
I think that you are trying to solve the wrong problem. You shouldn't be updating the widgets like this. If you necessarily want to, then hiding the parent widget of the layout before the change and showing it again afterwards should work.
A better approach is to use QStackedWidget and have all the boards prepared initially. Switching to a different board is then simply a matter of switching the active widget.
The total number of QPushButton is really big: 30x16 = 480!!! I don't use to make people change their programming logic, but in this case I think that using QPushButtons is not the better approach. The layout must have a really bad time trying to move the objects as they are added, and perhaps you are reaching some internal limit in refresh time for the layout to be repainted.
What I would have done is a custom widget with a custom paintEvent method. There you can divide its width and height in the number of columns and rows that you wish and paint the cells with pixmaps as the game is played.
For the mouse interaction, the best would have been to override the mousePressEvent with a custom logic that calculates the mouse position in the grid and calls the corresponding methods or emits signals indicating the position of the event. Not very hard to code. You can also use the event->buttons() method to know which mouse button was pressed and emit different signals if you wish.
I don't use to answer telling that it is better to change your whole program, but in this case I think you are going "the hard way". I know this is not the kind of answer you are looking for, but consider this possibility.
You could try calling setUpdatesEnabled(false) on the parent widget before doing those "massive" changes, and re-enable it once all is done.
Maybe I'm wrong but as far as I know Qt doesn't render the widget right after setVisible() is called. Rendering happens as a result of a 'render' event, except if you call render() manually.
From the official Qt doc (http://qt-project.org/doc/qt-4.8/qwidget.html#paintEvent):
Qt also tries to speed up painting by merging multiple paint events
into one. When update() is called several times or the window system
sends several paint events, Qt merges these events into one event with
a larger region (see QRegion::united()). The repaint() function does
not permit this optimization, so we suggest using update() whenever
possible.
My instincts tell me that it's not a painting problem rather a layouting (not enough space to present every button in 'hard mode').
Also I think you shouldn't use Qt::AlignCenter when you add your buttons to the layout, it will try to centerize every button in the layout. You should rather centerize the parent widget of the layout (if you don't have one create one and centerize it) and set size-policies correctly (QWidget setSizePolicy).
But as #Mat suggested if this really is a painting problem you can use setUpdatesEnabled(false/true) (if setUpdatesEnabled solves your problem please accept #Mat 's solution)
Try to enabling/disabling instead of visible/invisible:
void MainWindow::resetBtn(const int width, const int height)
{
int index = 0;
for (int i = 0; i < HARD_HEIGHT; i++)
for (int j = 0; j < HARD_WIDTH; j++)
btnArr[index++].setEnabled(j < width && i < height);
}

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

Get width of the clipped displayed column

I have a DataGrid in my project. It doesn't fit's the width of the form, so the scrollBar appears. The DataGrid, on it's initial state, displays a few columns and a part of the next column (which would appear after scrolling).
Is there any way, I can get the width of this, displaying part?
You should get the width of the parent object.
let's say the datagrid is in your application, then you should get the width of the stage.(stage.stageWidth)
If your datagrid is on a certain x location in your application (or any other parent object) then you should take the width of the parent object - the x value of your datagrid. (stage.stageWidth - dataGrid.x)
Ok, I had some time to dig more deeply in this problem. I've searched a few classes to see, how the Adobe was implementing those grid behavior (displaying columns partly). So, for those, who'll need to deal with this, the needed part is in DataGrid.as file, method configureScrollBars .. actually, this part of it:
// if the last column is visible and partially offscreen (but it isn't the only
// column) then adjust the column count so we can scroll to see it
if (collectionHasRows && rowCount > 0 && colCount > 1 &&
listItems[0][colCount - 1].x +
visibleColumns[colCount - 1].width > (displayWidth - listContent.x + viewMetrics.left))
colCount--;
else if (colCount > 1 && !collectionHasRows)
{
// the slower computation requires adding up the previous columns
var colX:int = 0;
for (var i:int = 0; i < visibleColumns.length; i++)
{
colX += visibleColumns[i].width;
}
if (colX > (displayWidth - listContent.x + viewMetrics.left))
colCount--;
}
That's pretty much all code, that needed to catch and measure all those tricky divided cols in a grid :)

Resources