Is it possible to add QTreeWidgetItem to QTableWidgetItem? - c++

I have QTableWidget and I interested in how I can add QTreeWidget item into 3rd column in table? Is it possible?

I do it:
ui->tableWidget->insertRow(ui->tableWidget->rowCount());
ui->tableWidget->setCellWidget(ui->tableWidget->rowCount() - 1, 3, new QTreeWidget());
auto widget = dynamic_cast<QTreeWidget*>(ui->tableWidget->cellWidget(ui->tableWidget->rowCount() - 1, 3));
widget->setHeaderHidden(true);
QTreeWidgetItem * topLevel = new QTreeWidgetItem();
topLevel->setText(0, "This is top level");
widget->addTopLevelItem(topLevel);

Related

QFileDialog custom layout

I'm developing a file dialog to import file in my application and I want to have an additional QComboBox with a list of formats between File names edit and File of types filter combo box, like this:
I've managed to add QComboBox under filters like this:
using this code:
QGridLayout * layout = qobject_cast <QGridLayout *>(dialog->layout());
QLabel * labelFormat = new QLabel(tr("Format"), dialog);
layout->addWidget(labelFormat, 4, 0);
QComboBox * comboBoxFormat = new QComboBox(dialog);
layout->addWidget(comboBoxFormat, 4, 1);
But I need to swap the last two rows of this grid layout. I've tried something like this to swap rows:
QWidget * w0 = layout->itemAtPosition(3, 0)->widget();
QWidget * w1 = layout->itemAtPosition(3, 1)->widget();
QWidget * w2 = layout->itemAtPosition(3, 2)->widget();
QLabel * labelFormat = new QLabel(tr("Format"), dialog);
layout->addWidget(labelFormat, 3, 0);
QComboBox * comboBoxFormat = new QComboBox(dialog);
layout->addWidget(comboBoxFormat, 3, 1);
layout->replaceWidget(w0, labelFormat);
layout->replaceWidget(w1, comboBoxFormat);
layout->addWidget(w0, 4, 0);
layout->addWidget(w1, 4, 1);
layout->addWidget(w2, 4, 2);
But I got the wrong widgets position:
How can I achieve the widget positioning from the first screenshot?
In your case the problem is caused by the fact that you are incorrectly locating QDialogButtonBox, this must be in position 3, 2 occupying 2 rows and 1 column:
QGridLayout *layout = qobject_cast<QGridLayout *>(dialog->layout());
QWidget * w0 = layout->itemAtPosition(3, 0)->widget();
QWidget * w1 = layout->itemAtPosition(3, 1)->widget();
QWidget * w2 = layout->itemAtPosition(3, 2)->widget();
QLabel * labelFormat = new QLabel("Format", dialog);
layout->addWidget(labelFormat, 3, 0);
QComboBox * comboBoxFormat = new QComboBox(dialog);
layout->addWidget(comboBoxFormat, 3, 1);
layout->replaceWidget(w0, labelFormat);
layout->replaceWidget(w1, comboBoxFormat);
layout->addWidget(w0, 4, 0);
layout->addWidget(w1, 4, 1);
layout->addWidget(w2, 3, 2, 2, 1);

How do I align a widget?

I cannot move the widget into the layout (QGridLayout). You need to move the "label shift" to the other widgets. That they were close. How can I do that?
QLabel * label_answer = new QLabel(this);
label_answer->setText("label_shift");
label_answer->setFont(params_font);
label_answer->setObjectName(QStringLiteral("label_answer_%1").arg(count-1));
lay->addWidget(main_label, i,0, Qt::AlignLeft);
lay->addWidget(box_input_value, i, 1, Qt::AlignLeft);
lay->addWidget(label_answer, i, 2, Qt::AlignLeft); // label_shift
You could add a extra column to the gridlayout, have the spinbox span 2 columns, which would mean the space needed in the label row is after the label, instead of before it

QTableWidget filled with QLineEdits does not fire signals

I'm relatively new to QT. In my code, I create a QTableWidget, iterate through the rows and set the cells to QLineEdits and QCheckBoxes. I want to make it so that changing the text within any of the QLineEdits or checking/unchecking the QCheckBoxes causes my table to fire a signal passing either the item in question, or the row/column that it's within.
I build the table here:
for(int row=0; row < conditionTable->rowCount(); row++)
{
QLineEdit *condition = new QLineEdit;
conditionTable->setCellWidget(row, 0, condition);
QLineEdit *minBoundField = new QLineEdit;
conditionTable->setCellWidget(row, 1, minBoundField);
QLineEdit *maxBoundField = new QLineEdit;
conditionTable->setCellWidget(row, 2, maxBoundField);
QCheckBox *checkbox = new QCheckBox;
conditionTable->setCellWidget(row, 3, checkbox);
if(row > 0)
{
condition->setReadOnly(true);
minBoundField->setReadOnly(true);
maxBoundField->setReadOnly(true);
checkbox->setCheckable(false);
}
}
I then try to make it so that changes to the table can be handled by one of the slot methods:
connect(conditionTable, SIGNAL(itemChanged(QTableWidgetItem*)),
this, SLOT(handleConditionTableChange(QTableWidgetItem*)));
However, this doesn't seem to work, and I'm not sure where to go from here. Any help would be appreciated.
You shouldn't be using QLineEdit and QCheckBox here.
To add a check box to your QTableWidget do the following:
QTableWidgetItem* item = new QTableWidgetItem("check box");
item->setFlags(Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
tableWidget->setItem(row, column, item);
To add an line edit:
QTableWidgetItem* item = new QTableWidgetItem("line edit");
tableWidget->setItem(row, column, item);
With this setup, the signal will be emitted when an item is changed.
Edit:
For your example, try something like:
for(int row=0; row < conditionTable->rowCount(); row++)
{
QTableWidgetItem* condition = new QTableWidgetItem("");
conditionTable->setItem(row, 0, condition);
QTableWidgetItem *minBoundField = new QTableWidgetItem("");
conditionTable->setItem(row, 1, minBoundField);
QTableWidgetItem *maxBoundField = new QTableWidgetItem("");
conditionTable->setItem(row, 2, maxBoundField);
QTableWidgetItem *checkbox = new QTableWidgetItem("");
checkbox->setFlags(Qt::ItemIsUserCheckable);
checkbox->setCheckState(Qt::Unchecked);
conditionTable->setItem(row, 3, checkbox);
if(row > 0)
{
condition->setFlags(Qt::NoItemFlags);
minBoundField->setFlags(Qt::NoItemFlags);
maxBoundField->setFlags(Qt::NoItemFlags);
checkbox->setFlags(Qt::NoItemFlags);
}
}
If you still want to use QLineEdit and QCheckBox for some reason, you will need to connect each line edit and each check box to a slot.

Add widgets to QFileDialog

I need to add a widget (QTableWidget) into QFileDialog's layout. I know that it is QGridLayout with sizes (3,4). The table must be in 3-rd row and span all columns.
QTableWidget* tableWidget = new QTableWidget(this);
QGridLayout *layout = static_cast<QGridLayout*>(QFileDialog::layout());
layout->addWidget(tableWidget, 2, 0, 1, 4);
With this code the original 3-rd row which contains lineEdit and save/open pushButton disappears. How can I add widgets between already existing widgets of QGridLayout so that original widgets remain in the layout.
I strongly recommend you not to rely on QFileDialog's implementation. The layout can be different on different platforms or different versions of Qt. It may be more correct to place your table under the dialog or to the right of it. This can be done easily without altering the layout of the QFileDialog itself. Just create a QVBoxLayout and put QFileDialog and QTableWidget inside it.
However, the question has been asked, and the solution exists. QGridLayout has no functionality such as QBoxLayout::insertItem. So we need to implement this behavior manually. The plan is:
Obtain the list of layout items placed in 3rd and 4th rows.
Calculate new positions of items.
Take elements out of item and add them back at new positions.
Working code:
QFileDialog* f = new QFileDialog();
f->setOption(QFileDialog::DontUseNativeDialog, true); //we need qt layout
QGridLayout *layout = static_cast<QGridLayout*>(f->layout());
QList< QPair<QLayoutItem*, QList<int> > > moved_items;
f->show();
for(int i = 0; i < layout->count(); i++) {
int row, column, rowSpan, columnSpan;
layout->getItemPosition(i, &row, &column, &rowSpan, &columnSpan);
if (row >= 2) {
QList<int> list;
list << (row + 1) << column << rowSpan << columnSpan;
moved_items << qMakePair(layout->takeAt(i), list);
i--; // takeAt has shifted the rest items
}
}
for(int i = 0; i < moved_items.count(); i++) {
layout->addItem(moved_items[i].first,
moved_items[i].second[0],
moved_items[i].second[1],
moved_items[i].second[2],
moved_items[i].second[3]);
}
QTableWidget* tableWidget = new QTableWidget();
layout->addWidget(tableWidget, 2, 0, 1, 4);

QGridLayout issue with inserting widgets

(Using Qt 4.6.3, x64, linux)
I'm testing how to properly insert widgets into a existing a QGridLayout filled with various widgets. A broken down contrived case is the following:
QApplication app(argc,argv);
QWidget w;
QGridLayout* gl = new QGridLayout(&w);
QLabel* label = new QLabel("Image Size:");
QLineEdit* wedit = new QLineEdit("100");
QLabel* xlabel = new QLabel("x");
wedit->setAlignment(Qt::AlignRight);
gl->addWidget(label);
gl->addWidget(xlabel, 0, 1, 1, 1);
gl->addWidget(wedit, 0, gl->columnCount());
Which creates the following widget:
.
Assuming that have an existing QGridLayout as above, but without the "x" label, and I wished to insert this into the layout, switching the latter two addWidget lines might seem valid, i.e.:
\\ same as above
gl->addWidget(label);
gl->addWidget(wedit, 0, gl->columnCount());
gl->addWidget(xlabel, 0, 1, 1, 1);
This however, creates the following:
The gl->columnCount() after this is still 2, as both the x-label and the QLineEdit are filling the same cell. Based on this knowledge, the following code produces the initial desired result:
gl->addWidget(label);
gl->addWidget(wedit, 0, 2); // note: specified column 2, columnCount() is now 3
gl->addWidget(xlabel, 0, 1, 1, 1);
Though this is not particularly useful, as the original layout in question isn't built with later layouts in mind.
Since addWidget allows for specifying cell position, as well as row/column span, it seems odd that Qt wouldn't automatically replace the existing widgets in the layout. Does anyone have a suggestion as to how I might overcome this? I assume it would be possible to recreate a QGridLayout and copy into it the children of the original, taking care to insert the additional widget in the right location. This however is ugly, and susceptible to Qt version issues (as I want to modify a built in widget).
Edit:
I realize that I'm making the assumption of thinking in a QHBoxLayout way, where inserting a widget is uniquely understood, whereas in a QGridLayout this isn't the case (?).
I can clarify that I ultimately would like to modify QFileDialog::getSaveFileName, by inserting a widget (similar to the widget shown above) right above the two lower rows (i.e. above "File &Name:").
Thanks
Switching the latter two addWidget lines is not valid. For the following code:
gl->addWidget(label);
gl->addWidget(wedit, 0, gl->columnCount());
gl->addWidget(xlabel, 0, 1, 1, 1);
The arguments for the addWidget() calls are evaluated prior to adding the widget. Therefore, gl->columnCount() evaluates to one instead of two for the second call, since the column still has to be created. You are effectively adding two widgets to column one.
A possible solution is to re-add the widgets that should be relocated. I.e.
QLayoutItem* x01 = gl->itemAtPosition(0,1);
gl->addWidget(x01->widget(), 0, 2);
gl->addWidget(xlabel, 0, 1, 1, 1);
Now, this isn't particularly pretty, or easy to maintain, as a new version of Qt might change the original widget, and blindly handpicking and relocating children isn't that clever. The following real example (the one I actually wanted to solve) was to alter the Qt's "Save As" dialog window, that shows up using QFileDialog::getSaveFileName.
class ImageFileDialog : public QFileDialog {
public:
ImageFileDialog(QWidget* parent);
~ImageFileDialog();
QString getFileName() const;
QSize getImageSize() const;
QDialog::DialogCode exec(); // Overriden
protected:
void showEvent(QShowEvent* event); // Overriden
private:
QString fileName_;
QSize imageSize_;
QLineEdit* widthLineEdit_;
QLineEdit* heightLineEdit_;
};
And in the source (showing just constructor, focus handling and exec):
ImageFileDialog::ImageFileDialog(QWidget* parent)
: fileName_(""),
imageSize_(0,0),
widthLineEdit_(0),
heightLineEdit_(0)
{
setAcceptMode(QFileDialog::AcceptSave);
setFileMode(QFileDialog::AnyFile);
setConfirmOverwrite(true);
QGridLayout* mainLayout = dynamic_cast<QGridLayout*>(layout());
assert(mainLayout->columnCount() == 3);
assert(mainLayout->rowCount() == 4);
QWidget* container = new QWidget();
QGridLayout* glayout = new QGridLayout();
QLabel* imageSizeLabel = new QLabel("Image Size:");
widthLineEdit_ = new QLineEdit("400");
heightLineEdit_ = new QLineEdit("300");
widthLineEdit_->setAlignment(Qt::AlignRight);
heightLineEdit_->setAlignment(Qt::AlignRight);
container->setLayout(glayout);
glayout->setAlignment(Qt::AlignLeft);
glayout->addWidget(widthLineEdit_);
glayout->addWidget(new QLabel("x"), 0, 1);
glayout->addWidget(heightLineEdit_, 0, 2);
glayout->addWidget(new QLabel("[pixels]"), 0, 3);
glayout->addItem(new QSpacerItem(250, 0), 0, 4);
glayout->setContentsMargins(0,0,0,0); // Removes unwanted spacing
// Shifting relevant child widgets one row down.
int rowCount = mainLayout->rowCount();
QLayoutItem* x00 = mainLayout->itemAtPosition(mainLayout->rowCount()-2,0);
QLayoutItem* x10 = mainLayout->itemAtPosition(mainLayout->rowCount()-1,0);
QLayoutItem* x01 = mainLayout->itemAtPosition(mainLayout->rowCount()-2,1);
QLayoutItem* x11 = mainLayout->itemAtPosition(mainLayout->rowCount()-1,1);
QLayoutItem* x02 = mainLayout->itemAtPosition(mainLayout->rowCount()-1,2);
assert(x00); assert(x01); assert(x10); assert(x11); assert(x02);
mainLayout->addWidget(x00->widget(), rowCount-1, 0, 1, 1);
mainLayout->addWidget(x10->widget(), rowCount, 0, 1, 1);
mainLayout->addWidget(x01->widget(), rowCount-1, 1, 1, 1);
mainLayout->addWidget(x11->widget(), rowCount, 1, 1, 1);
mainLayout->addWidget(x02->widget(), rowCount-1, 2, 2, 1);
// Adding the widgets in the now empty row.
rowCount = mainLayout->rowCount();
mainLayout->addWidget(imageSizeLabel, rowCount-3, 0, 1, 1 );
mainLayout->addWidget(container, rowCount-3, 1, 1, 1);
// Setting the proper tab-order
QLayoutItem* tmp = mainLayout->itemAtPosition(mainLayout->rowCount()-2,1);
QLayoutItem* tmp2 = mainLayout->itemAtPosition(mainLayout->rowCount()-1,1);
assert(tmp); assert(tmp2);
QWidget::setTabOrder(heightLineEdit_ , tmp->widget());
QWidget::setTabOrder(tmp->widget(), tmp2->widget());
}
// Makes sure the right widget is in focus
void ImageFileDialog::showEvent(QShowEvent* event)
{
widthLineEdit_->setFocus(Qt::OtherFocusReason);
}
// Called to create the widget
QDialog::DialogCode ImageFileDialog::exec()
{
if (QFileDialog::exec() == QDialog::Rejected)
return QDialog::Rejected;
// The code that processes the widget form and stores results for later calls to
// getImageSize()
return QDialog:Accepted;
}
Which, using for instance
ImageFileDialog* dialog = new ImageFileDialog(&w);
dialog->exec();
Creates the following widget:
Comments and ways to do this better, or why this is just plain wrong are most welcome :)