Adding Rows in a QTreeWidget in runtime messes up widgetItems visuals - qt

I have a QDialog show a QTreeWidget that has the parameters of some objects.
I have added a button to add extra fields in the vectors of the objects visually like this.
void on_Create_Surface_Pressed()
{
QPushButton * const button = qobject_cast<QPushButton*>(sender());
QTreeWidgetItem* branch = treeButtons[button];
if(branch)
{
QTreeWidgetItem* leaf = new QTreeWidgetItem(branch);
QString str("Point ");
str+=QString::number(branch->childCount()-1);
QLabel* label = new QLabel(str);
QString surfType[3] = {QString("x") , QString("y") ,QString("z")};
QTableWidget* surfTable = sampleTree( surfType);<--(Create and add a
Table under the
qtreewidgetItem)
leaf->treeWidget()->setItemWidget(leaf , 0 , label);
leaf->treeWidget()->setItemWidget(leaf , 1 , surfTable);
update();
}
}
My problem is that the new rows are rendered above the next QTreeWidgetItem until the user collapses the tree(which is the gimmick i m using).
Is there a workaround for this? Or isn't QTreeWidget purposed for this use and a QTreeView would serve better?

Related

QVBoxLayout push to the top with spacer

I would like to add push buttons to the layout. The newest item would be on the top of the layout.
I also would like position the buttons to the top, thus I am using QSpacerItem.
Here is what I have tried so far.
Constructor:
//frame is a QFrame
lVertical = new QVBoxLayout(frame); //private variable
lVertical->setMargin(0);
lVertical->setSpacing(0);
auto verticalSpacer = new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
lVertical->addItem(verticalSpacer);
connect(b, &QPushButton::clicked, this, &MainWindow::addToLayout);
Function:
void MainWindow::addToLayout() {
QPushButton* button = new QPushButton(frameSlider);
button->setText(QString::number(i));
++i; //private variable
layoutVertical->addWidget(button);
}
Currently I add like this:
But I would like to add like this:
The problem is that you have placed a spacer at the beginning so it will stretch all the widgets down.
One possible solution is to add a stretch and then insert an element before, You should not use QSpacerItem:
// constructor
layoutVertical = new QVBoxLayout(frame);
layoutVertical->setMargin(0);
layoutVertical->setSpacing(0);
layoutVertical->addStretch();
void MainWindow::addToLayout() {
QPushButton* button = new QPushButton();
layoutVertical->insertWidget(0, button);
}

Finding QGridLayout elements in Qt

I created labels and added them to the layout. How to get the elements from the layout? I tried to use method children(), but it gives empty list... Is there a way to get them? Some sample code below.
QGridLayout* layout = new QGridLayout();
QLabel* test = new QLabel();
test->setPixmap(m_staticStorage->getFirstImg());
test->setScaledContents(true);
QLabel* test2 = new QLabel();
test2->setMaximumSize(50,50);
test2->setPixmap(m_staticStorage->getSecondImg());
tes2->setScaledContents(true);
layout->addWidget(worker, 0, 0);
layout->addWidget(farmer, 0, 1);
ui->verticalLayout->addLayout(layout);
//layout->children() ->>>> empty
This will iterate over any QLayout subclass to find the items which have been added so far:
for (int i=0; i < layout->count(); ++i) {
QLayoutItem *item = layout->itemAt(i);
if (!item || !item->widget())
continue;
QLabel *label = qobject_cast<QLabel*>(item->widget());
if (label) {
// .... do stuff with label
}
}
One can also iterate in a similar fashion over each row or column of a QGridLayout using QGridLayout::columnCount() or QGridLayout::rowCount() and then QGridLayout::itemAtPosition() to get the actual QLayoutItem.
If you need to uniquely identify the QLabel after finding it, you could for example give each label a unique objectName or do a setProperty() on them with a unique ID when creating them.
QLabel *test1 = new QLabel(this);
test1->setObjectName(QStringLiteral("test1"));
....
if (label) {
if (!label->objectName().compare(QLatin1String("test1")))
// this is "test1" label
}
QLabel *test1 = new QLabel(this);
test1->setProperty("id", 1);
....
if (label) {
if (label->property("id").toInt() == 1)
// this is "test1" label
}
Better to use the function QObject::findChild
Qt is returning all children of a given Type and Objectname. You can decide to get only direct children or also all recursively.
this->findChild<QLabel*>(QString(), Qt::FindDirectChildrenOnly);
This will return all direct children of this (where this is your parent widget, not your layout) with any name and of type QLabel*
Your approach do not work because the layout do not take the ownership of the labels:
From Layout Management:
Tips for Using Layouts
When you use a layout, you do not need to pass a parent when
constructing the child widgets. The layout will automatically
reparent the widgets (using QWidget::setParent()) so that they are
children of the widget on which the layout is installed.
Note: Widgets in a layout are children of the widget on which the
layout is installed, not of the layout itself. Widgets can only have
other widgets as parent, not layouts.
You can nest layouts using addLayout() on a layout; the inner layout
then becomes a child of the layout it is inserted into.
BTW: Don't forget to set a parent for your layout
QGridLayout* layout = new QGridLayout(this);
and for your labels too
QLabel* test2 = new QLabel(this);

Small panel at the bottom of a QTreeWidget

I'm using QT 5.4.2 and trying to create a small panel at the bottom
of a subclassed QTreeWidget.
Here is the code:
void HmiScenarioAutoscriptPanel::searchEmitter() {
QWidget *child = new QWidget(ui->emitterTreeWidget);
//QMainWindow* child = new QMainWindow;
QLabel *labelSearch = new QLabel("Search");
QLineEdit *lineSearch = new QLineEdit();
lineSearch->setFixedSize(100, 20);
QHBoxLayout* layout = new QHBoxLayout(ui->emitterTreeWidget);
layout->setAlignment(Qt::AlignBottom);
layout->addWidget(child);
layout->addWidget(labelSearch);
layout->addWidget(lineSearch);
}
The label and search field correctly appear at the bottom of the tree,
however the fields overlap with the tree nodes (see image below).
Any idea why this behavior?
Ciao
Alf
enter image description here
It is not recommended to set layout on the tree widget. It is like other controls like a button, label etc..
I see that you are using designer. Add a blank widget (searchWidget) under the tree widget and then
void HmiScenarioAutoscriptPanel::searchEmitter() {
QWidget *child = new QWidget(ui->searchWidget);
//QMainWindow* child = new QMainWindow;
QLabel *labelSearch = new QLabel("Search", searchWidget);
QLineEdit *lineSearch = new QLineEdit(searchWidget);
lineSearch->setFixedSize(100, 20);
QHBoxLayout* layout = new QHBoxLayout(ui->searchWidget);
layout->setAlignment(Qt::AlignBottom);
layout->addWidget(child);
layout->addWidget(labelSearch);
layout->addWidget(lineSearch);
}
Just out of curiosity, why don't you add these using the designer as well?

(Qt) Rendering scene, different items in the same relative positions

I have a QSqlTableModel model that contains my data.
I have made a QGraphicsScene scene and a QGraphicsView view so the user can move around same myQGraphicsTextItem text items until the desired position.
Something like this:
myQWidget::myQWidget()
{
//these are member of my class
chequeScene = new QGraphicsScene();
chequeView = new QGraphicsView();
model = new QSQLTableModel();
//populate model, inialize things here...
//add predefined items to the scene
setScene();
}
there's a button to show the view and move the textitems of scene. It works well.
there's a button that calls the slot print that belongs to the class. It configures a QPrinter and then calls the following paint method myQWidget::paint(), after that scene->render() is called.
The porpoise of the method below is to print data on a paper that is configured to have the same size than the scene while printing the data in the same relative position the textItem had on the scene. Can't do it with QList it doesn't order the items in the same way I added them to the scene.
Here is my code below, it prints with overlapping of some fields doe to QList order items as they appear on the scene.
void myQWidget::paint()
{
qreal dx = 0;
qreal dy = 0;
QList<QGraphicsItem*> L = chequeScene->items();
for (int j=0; j<model->columnCount(); j++) {
if(!L.isEmpty())
{
//Saves the position on dx, dy
dx = L.first()->scenePos().x();
dy = L.first()->scenePos().y();
chequeScene->removeItem( L.first() );
delete L.first();
L.removeFirst();
}
QString txt("");
//selecting printing formar for each column
switch(j)
{
case COLUMNADEFECHA:
txt = QDate::fromString(model->data(model->index(chequenum,j)).toString(), "yyyy/MM/dd").toString("dd/MM/yyyy");
break;
case COLUMNADECHEQUES:
break;
default:
txt = model->data(model->index(chequenum,j)).toString();
break;
}
//filtering not important columns
if(j!=COLUMNADECHEQUES)
{
//Supposubly item with the desired information is added to the scene
//on the same position it had before. Not working.
GraphicsTextItem *item=new GraphicsTextItem();
item->setPlainText(txt);
item->setPos(dx,dy);
chequeScene->addItem(item);
}
}
}
Any idea on how to get this working?
I think as you are getting the scenePos in dx and dy but are setting it using setPos function.
Also as you are using your GraphicsTextItem and not QGraphicsTextItem maybe a look at your paint method will help in understanding the problem.
Try using item->mapFromScene(dx, dy) and then use those coordinates to set the item position by item->setPos(..).
Hope This Helps..

Drawing lines and checkbox on same layer in Qt

I want to design a GUI using Qt. That contains lines and check-boxs, which are connected to each other like this :
----------[ ]-----------
------[ ]---------[ ]-------------
(where dash represents line and [] is for check-box)
Lines are created dynamically. And selecting the check-box will disable the corresponding line. So basically the lines and check-box should be on same layer.
Any hint/link about the implementation is appreciated.
You'll need a combination of QFrame, QCheckBox, and QHBoxLayout. For something a little fancier, you could sub-class your own QWidget for each section and add them incrementally to a QVBoxLayout. Something like this...
class CheckLine : public QWidget
{
Q_OBJECT
public:
CheckLine(int numboxes = 1, QObject* parent = 0) :
QWidget(parent)
{
m_layout = new QHBoxLayout;
m_layout->setSpacing(0); //you can also set the margins to zero if need be
setLayout(m_layout);
QFrame* firstline = new QFrame();
firstline->setFrameShape(QFrame::HLine);
m_layout->addWidget(firstline);
m_lines.append(firstline);
for(int i = 0; i < numboxes; ++i)
addBox();
}
void addBox()
{
QCheckBox* newbox = new QCheckBox(""); //add text here - or leave it blank if you want no label
m_newbox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_layout->addWidget(newbox);
m_boxes.append(newbox);
QFrame* newline = new QFrame();
newline->setFrameShape(QFrame::HLine);
m_layout->addWidget(newline);
m_lines.append(newline);
/* link each checkbox to disable the line after it */
connect(newbox, SIGNAL(toggled(bool)), newline, SLOT(setEnabled(bool)));
// connect(newbox, SIGNAL(toggled(bool)), this, SLOT(setEnabled(bool))); //use this instead if you want it to disable the entire row
}
private:
QHBoxLayout* m_layout;
QList<QCheckBox*> m_boxes;
QList<QFrame*> m_lines;
};
Then, create a widget with a QVBoxLayout and new a CheckLine, incrementing numboxes by 1 each time. Tweak the code if you want any checkbox to disable the entire line.

Resources