Qt - QPushButtons in place of QTreeView Items - c++

Is it possible to add QPushButtons for every item in a QTreeView? For instance, when you click on a TreeItem (that is a button), it's children get displayed as buttons as well? I just have a standard QTreeView.
_layout = new QVBoxLayout(this);
treeView = new QTreeView(this);
QStandardItemModel* standardModel = new QStandardItemModel();
QStandardItem* rootMenu = standardModel->invisibleRootItem();
//populate TreeView
treeView->setModel(standardModel);
treeView->setWordWrap(true);
treeView->setHeaderHidden(true);
//treeView->expandAll();
_layout->addWidget(treeView);
this->setLayout(_layout);

I have not personally done this (yet), but you could try using QAbstractItemView::setIndexWidget(). The widgets won't aren't connected in any way to the data model, so it is up to your code to update them if necessary. Also, you need to call it for each QModelIndex separately.

Here is the answer. You must create your own delegate and applay it for your QTreeView.
To create delegate you must subclass QStyledItemDelegate and re-implement its QStyledItemDelegate::paint(...) method in that way what you want, also, don't forget about re-implementing QStyledItemDelegate::sizeHint(...) method if needed, of course.
Also, you may need to re-implement QStyledItemDelegate::createEditor(...) method.
To apply created delegate to your view (QTreeView) you must create delegate and call QTreeView's method setItemDelegate (or setItemDelegateForColumn, or setItemDelegateForRow).
Good luck!

Related

Accessing widgets inside QStackedWidget

I am developing a Qt app with QtDesigner.
Previously it was quite easy to access specific widgets to do something with them like connecting signals. After I added QStackedWidget I can no longer access specific widgets with something like ui->stack->page1->widget.
Is there a way to do it somehow? Or should I always call findChild method? Or maybe it is possible to at least assign some of the nested widgets in stack widget to properties of the main windwo class?
QStackedWidget provides a method to get child widgets by index, as well as the current widget.
A quick example is as follows:
MOCed Header
class MyWidget: QWidget
{
Q_OBJECT
public:
using QWidget::QWidget
QWidget *ptr;
};
Source File
QStackedWidget *stackedWidget = new QStackedWidget;
stackedWidget->addWidget(new MyWidget); // index 0
stackedWidget->addWidget(new QWidget); // index 1
stackedWidget->addWidget(new MyWidget); // index 2
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(stackedWidget);
setLayout(layout);
// do something specific with the first widget's ptr element
auto* widget = stackedWidget->widget(0);
auto* mywidget = qobject_cast<MyWidget*>(widget);
if (mywidget) {
mywidget->ptr->setObjectName("FirstPage");
}
Now, Qt uses virtual interfaces by default, so if you have a custom subwidget you need to extract, you can use qobject_cast. qobject_cast is basically a fast dynamic_cast, and works even without RTTI. In template-driven code, dynamic_cast is a bit of a code-smell: it means you lost useful type information too early. With virtual interfaces, the exact opposite is true: you should use qobject_cast as needed.
Why you get the widget layer by layer, if your widgets are added in Qt designer, you can get it by ui->widget directly.

Retrieving parent QTableWidget from its cell widget item

I am working on a Project using Qt 5.7 with C++. I am stuck in a weird problem.
I have a QTableWidget which contains a QComboBox on its one and only cell. Simplified code is as follows.
QTableWidget *tab = new QTableWidget();
tab->insertColumn(0);
tab->insertRow(0);
QComboBox *cb = new QComboBox(tab);
cb->addItem("A");
cb->addItem("B");
tab->setCellWidget(0, 0, cb);
Now on currentIndexChanged(int) signal of the QComboBox, I am connecting to a SLOT where I am trying to retrieve the pointer of the QTableWidget as follows,
QComboBox* cb = qobject_cast<QComboBox*>(sender());
QWidget* par = cb->parentWidget();
But, I am not getting the same pointer as the actual QTableWidget.
I have also tried as follows, but still failed.
QComboBox* cb = qobject_cast<QComboBox*>(sender());
QObject *par = cb->parent();
QTableWidget *tab = qobject_cast<QTableWidget *>(par);
Can anyone suggest some other way to do it or point out the error in those code segment ?
Possible Solution Found
Parent Widget can be retrieved using cb->parent()->parent(). Although, this process is not documented, so, not reliable.
The reason is that the QTableWidget is not a direct parent of QComboBox, but a grand parent. But even this is an implementation detail and cannot be relied upon, because it is not documented.
A way out could be storing the QTableWidget pointer somewhere else.

Replace a widget in Qt

I have a base class which has some gui items that i have set positions of using the designer in Qt creator. Those items are:
QWidget* w1;
QWidget* w2;
QWidget* w3;
Now in a class that inherits that base class, I would like to "transform" those widgets into lineEdit items, that would keep all the geometrical parameters of that widgets. So I do something like this:
QLineEdit* leAmplitude;
leAmplitude = new QLineEdit(ui->w1);
leAmplitude->setGeometry(ui->w1->geometry());
ui->glControls->addWidget(leAmplitude);
But the added QLineEdit item doesn't appear in the exact same place as w1 item. Its just added at the bottom of other controls in the QGridLayout glControls. How to make the lineEdit to take all geometric parameters from w1?
Layout takes care of the widgets placed in the layout, according to the hints given by the widget, so calling setGeometry, then doing addLayout is not useful. Also, adding widget to layout resets it parent, so you setting new widget's parent to ui->w1 is not useful either.
Fortunately, there is QLayout::replaceWidget method! Just use that. Example:
QLineEdit* leAmplitude;
leAmplitude = new QLineEdit;
QLayoutItem *previous = ui->glControls->replaceWidget(ui->w1, leAmplitude);
// possibly assert that previous is ui->w1, or just delete it, or whatever
This method was added as late as in Qt 5.2 it seems, so if you need to support older versions, I can expand this answer to cover how to (try to) do the same manually. But in short, you have to use the right QGridLayout::addWidget overload and make sure relevant properties (including at least sizeHint and sizePolicy) match.
try this, it is works:
QLineEdit* leAmplitude;
leAmplitude = new QLineEdit(ui->w1->parentWidget());
ui->w1->parentWidget()->layout()->replaceWidget(ui->w1, leAmplitude);
ui->w1 = leAmplitude;

How to logically group widgets in QT for easy show/hide?

I'm grouping a set of widgets in a parent and then I control the visibility/flow of these widgets by hiding/showing the parent. Is this a good way to achieve what I'm trying to do? Here is the code:
QVBoxLayout* l = new QVBoxLayout(this);
// .....
QWidget* toolset_frame = new QWidget(this);
{
QVBoxLayout* l = new QVBoxLayout(toolset_frame);
l->addWidget(new QLabel(tr("Stuff")));
this->Toolset = new QLineEdit(toolset_frame);
l->addWidget(this->Toolset);
}
l->addWidget(toolset_frame);
// Call toolset_frame->hide() and this hides everything inside the parent
The problem with this solution is that the children shrink in size slightly, I think this is due to some padding or border in the parent. Ideally the children should appear as if they are not contained in an intermediate object, but rather flow with the parent. In this case the horizontal size of the children should not be affected.
http://doc.qt.io/qt-5/qtwidgets-dialogs-extension-example.html
This example shows that your approach is correct. Using a widget to contain the elements you want to hide, and so on.
If you want the margins/content margins/padding to be less, then change it.
// in finddialog.cpp
extensionLayout->setMargin(0);
To quickly prototype what properties to change to get it to look right, try laying it out in the Qt Designer, and modify the property editor to get the look and feel you want.
Hope that helps.

How to draw a custom control in QTableView?

I have to draw a custom control in QTableView. This control must looks like FileChooser.
FileChooser http://www.vision.ee.ethz.ch/computing/sepp-irix/qt-3.0-mo/filechooser.png
QStyleOptionButton button_option;
button_option.state |= QStyle::State_Enabled | QStyle::State_Off;
button_option.rect = PushButtonRect(option); //calculate button rect
button_option.text = "...";
QApplication::style()->drawControl(
QStyle::CE_PushButton,
&button_option,
painter);
The code above draws QStyle::CE_PushButton - that looks like QButton, - but there is no QStyle::CE_LineEdit in Qt library. How can I draw QLineEdit?
In order to draw custom widgets in a Table View, you need to create a custom QItemDelegate subclass and override at least the createEditor method, where you can create any kind of widget which is displayed when double-clicking into the table cell. This item delegate can be assigned to the respective column in your table view.
You would then need to create a separate class e.g. CustomFileChooser which inherits from QWidget and consists of a Line Edit and Button.
Your createEditor method would then return such an object.
You may also have to override setEditorData (which shall assign the current model value to the editor widget which was created) and setModelData (which is called when the changes are committed).
This way, the line edit and button would only be visible after double-clicking into the table cell. If you want it to be always visible, you will have to override drawDisplay() as well.
I found an answer by myself. You may display a custom editor (ordinary widget) permanently using:
void QAbstractItemView::openPersistentEditor ( const QModelIndex & index )
First you need to understand that a button is a control Element and thus you can find it under CE but when you need a lineEdit it is not a control element.
In order to paint a lineEdit, I shall quote from the qt documentation,
"QStyleOptionFrameV2 inherits QStyleOptionFrame which is used for drawing several built-in Qt widgets, including QFrame, QGroupBox, QLineEdit, and QMenu."
Yes, only a sample code that might work will help you understand it clearly!
The code should somehow look like this
QStyleOptionFrameV2 *panelFrame = new QStyleOptionFrameV2;
QLineEdit *search = new QLineEdit;
panelFrame->initFrom(search);
panelFrame->rect = QRect(x,y,w,h);//Indeed the location and the size
panelFrame->lineWidth = QApplication::style->pixelMetric(QStyle::PM_DefaultFrameWidth, panelFrame, search);
panelFrame->state |= QStyle::State_Sunken;
QApplication::style()->drawPrimitive(QStyle::PE_PanelLineEdit, panelFrame, painter);