Qt, get data inside a child widget from another child widget - c++

I create new cad widget in my mainwindow:
glWidget = new MeshViewerWidget(this);
and then my text view widget:
tbl = new tableView( this );
in my mainwindow i can get my mesh with:
glWidget->mesh();
How do i get it in my tableview.cc? ty

You could use the findChildren function of QObject and get a list of all children of a widget that can be casted to the given type. Eg
QList<QTableView*> allTableViews = glWidget->findChildren<QTableView *>();
// Iterate in order to find the table view either by checking the name, the parent etc....
Another approach would be the one Simon suggests, subclassing and adding public access function to the members you want to be reachable from top level widgets.

Related

Qt widget stacking child layouts on top of each other

Qt has a layout system kind of separate from the widgets, which is causing me problems. I know that when I have a QSplitter, I can add stuff to it just by doing
new SomeCustomQWidget(splitter);
However, if I have a QWidget, shouldn't I be able to do something similar? The Qt web pages only explain how to add layoutItems to layouts, only connecting them to widgets by inheriting both in a subclass. So, right no I am doing something like this
item = new QWidget(parentsplitter); //this works
subitem = new customWidget(item); //this too
subitem2 = new QSlider(item); //this isn't laid out with subitem
However this results in the slider being on top of the subitem instead of being next to it (either right or below I'd expect). Calling
//either
new QVBoxLayout(item);
//or
item->setLayout(new QVBoxLayout());
//or
item->setLayout(new QVBoxLayout(item));
after initializing the item doesn't help. (My logic is that the item takes the layout as it's child and will add subsequent children to it).
I went around this problem earlier but now I'm facing the exact same problem when making a widget inside a QGraphicsView. I feel like it shouldn't be necessary to make a custom subclass of every item I could ever want to dynamically add to those widgets, like the Qt website seems to expect. (If I simply want to add some text and a couple buttons, do I really need to make 2 custom subclasses?) I am using a .ui file for basic layout but these items need to be created dynamically.
Solution
Forgot to call item->show(); on the widget used for layout.
Turns out, I had tested otherwise correct code, but since some of my subclasses called show() but others didn't, it just seemed broken to me.
This however isn't the issue with the graphicswidgets, as graphicslayout can only be used to add graphicslayoutitems, which don't include for example graphicstextitems.
Solution 2
Using the QGraphicsProxyWidget instead of QGraphicsWidget fixed the issue!
QWidget *parent = new QWidget();
QVBoxLayout *rootLayout = new QVBoxLayout(parent);
QHBoxLayout *subLayout1 = new QHBoxLayout();
QHBoxLayout *subLayout2 = new QHBoxLayout();
rootLayout->addLayout(subLayout1);
rootLayout->addLayout(subLayout2);
subLayout1->addWidget(new QLabel("Foo"));
subLayout1->addWidget(new QLabel("Bar"));
subLayout2->addWidget(new QPushButton("Foo button"));
subLayout2->addWidget(new QPushButton("Bar button"));
parent->show();
Note once layout is assigned to a widget by setLayout or when widger is passed to constructor fo layout, the are in 1 to 1 relationship which last until widget or layout is destroyed. See documentation:
If there already is a layout manager installed on this widget, QWidget won't let you install another. You must first delete the existing layout manager (returned by layout()) before you can call setLayout() with the new layout.
That is why subitem2 = new QSlider(item) didn't work for you.
I think you're confusing parent/child relationship with managing layout.
Lets start with layouts. When set on a widget the layout becomes a child of that widget. When constructed with a widget parent the widget becomes a parent and the layout is set on the widget. When adding widgets to layout they become children of the widget that layout is set on, not the layout itself. Setting a parent on a widget or constructing widget with a parent does not automatically add the widget to its parent layout. Deleting a layout does not delete widgets governed by it, because they are not its children.
To summarize - layouting and parenting are two distinct mechanisms. first is for governing position and size of a widget, the other is for object hierarchy and resource management (parents delete their children). Layouts have a convenience constructor though, that does both - sets a parent of the layout and sets that layout on the parent widget.
In code:
item = new QWidget()
layout = new QVBoxLayout(item);
is equivalent to:
item = new QWidget();
layout = new QVBoxLayout();
layout->setParent(item);
item->setLayout(layout);
This code adds a child to a layout and item becomes its parent:
item = new QWidget();
layout = new QVBoxLayout(item);
child = new QWidget();
layout->addWidget(child);
This adds a child to a parent item, but does not add child to a layout:
item = new QWidget();
layout = new QVBoxLayout(item);
child = new QWidget(item);
A QSplitter is special in that it is a widget and it does its own layout of children i.e. when you use a splitter as a parent of a widget that widget's geometry is governed by the splitter. Other widgets don't behave like that.
So to comment your code:
item = new QWidget(parentsplitter); //this works because splitter does layout
subitem = new customWidget(item); //item is not splitter so subitem geometry is not managed
subitem2 = new QSlider(item); //item is not splitter so subitem2 geometry is not managed
If you want to lay out subitems in the item then item either has to be a splitter too or have a layout that will manage children, so:
childsplitter = new QWidget(parentsplitter); //note that parentsplitter has only 1 item
subitem = new customWidget(childsplitter);
subitem2 = new customWidget(childsplitter);
or
widget = new QWidget(parentsplitter); //note that parentsplitter has only 1 item
lay = new QHBoxLayout(widget);
subItem = new customWidget();
subItem2 = new customWidget();
lay->addWidget(subItem);
lay->addWidget(subItem2);
If you want parent splitter to have two items then you do:
item = new customWidget(parentsplitter);
item2 = new customWidget(parentsplitter);

Exclusive checkbox in QListView

I'm trying to do exclusive checkboxes as a QListView items. I'm using QStandardItemModel as a model with QStandardItem's.
I'm adding items to the list dynamically and set it checkable:
QStandardItem *item = new QStandardItem(treeView->model()->data(index).toString());
item->setCheckable(true);
m_categoriesModel->appendRow(item);
I tried connect all items to QSignalMapper but QStandardItem doesn't have checked(bool) signal (basically it does not have any).
Is there any way to solve the problem?
You can always make it in the way described below. Firstly connect the clicked signal of ListView to the slot which will handle your items click. Secondly inside of the slot you can get the item from QModelIndex and check the state of the item. Below is pseudo code:
For example, in constructor of ListView:
connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(_handleItemClicked(QModelIndex)));
Slot of ListView:
void ListView::_handleItemClicked(QModelIndex index)
{
QStandardItem* item = _model->itemFromIndex(index);
if( item->checkState() == Qt::Checked) qDebug() << "Checked!";
}
There actually is a class for exactly doing this: QButtonGroup
It's easy to use:
QButtonGroup *group = new QButtonGroup(this);
group->setExclusive(true);//now only one will be checked at a time
//add all buttons
group->addButton(this->ui->myFirstCheckbox);
//...
... at least for manually added buttons. Of course you can use it for the model too, but it would require you to find all the checkbox elements inside the view...

In Qt I want to make my model generation in a separate class for a QTableView

As a C/C# programmer I'm new to Qt and have little experience in C++.
What I want to do is 'making' the model of a QTableView in the class containing my data. In C# I could return a DataSet from a static method in a class containing all my stuff concerning this data, and bind this dataset to my table or list.
public class Books
{
//properties
...
//construtors etc...
...
static DataSet BookData()
{
// fill my dataset
return myDataSet;
}
}
In the main program I then bind my DataSet with the control I wanted to use
Is there a way to do so in Qt / C++ doing the same so that I can write something like:
QSqlTableModel* Books::BookData()
{
// Create an QSqlTableModel
// Fill it with my data
return model; // or whatever is possible
}
in the main program:
...
ui->tvBooks->setModel(BookData());
...
And this with correct garbage cleaning or is this wishful thinking...
tnx
In Qt, this is generally done as follows:
Have a model that contains your data.
Invoke setModel on the view, letting the view display what's in the model.
Qt has two general approaches to models:
Use a concrete model and populate it with data. Such models include QStringListModel, QStandardItemModel and the models that interface databases, such as QSqlTableModel.
Create a class that derives from one of the base model classes, such as QAbstractListModel, QAbstractTableModel or QAbstractItemModel. Reimplement relevant virtual methods to implement your own model.
In your case, you can, for example:
Make the Books class have a QStandardItemModel as a member, and use it to store the book data. Do not store the data outside of that model. That class should expose the model to outside, and you can then set it on a view. This will be the easier approach, as you don't need to reimplement the model yourself.
If you wish to have the flexibility of SQL available to access the data, you can also use an in-memory SQLITE database, and expose it via QSqlTableModel, QSqlQueryModel or QSQLRelationalTableModel. Other than that, the approach would be as above.
(your answer) Have a method that returns a newly created static model.
Let the Books class inherit QAbstractTableModel. This requires you to understand the semantics of Qt's models, and is a bit harder.
If you wish to use static models, such as QSqlQueryModel, created on the fly each time the database contents change, you can easily leverage QObject's compositeness.
To manage the model's lifetime, it can make itself a view's child automatically, and delete any other instances that are also that view's children:
class Books {
...
static QSqlQueryModel * setModelOn(QAbstractItemView * view) {
Q_ASSERT(view);
for (auto child : view->findChildren<QSqlQueryModel>())
delete child;
auto model = new QSqlQueryModel(view);
model->setQuery("SELECT name, pagecount FROM books");
...
view->setModel(model);
return model;
}
};
[SOLVED]
What I did to solve the case:
class Books
{
static void* GetBookData(QTableView *model)
{
model->setTable("books.books");
model->select();
...
}
};
in the library
And:
...
QTableView *view = new QTableView;
...
Books::GetBookData(view);
ui->myCombobox->setModel(view);
...
delete view;
...
in the main program.

QTableView real-time filtering

My situation looks like this: I have QTableView and LineEdit. I'd like to show data which contains value in LineEdit in real time. I guess I should use QSortProxyFilterModel, but I don't know how to do that. I wrote this:
void MainWindow::on_lineFind_textEdited(const QString &arg1)
{
QSortFilterProxyModel proxy;
proxy.setSourceModel(ui->tableView->model());
proxy.setFilterRegExp(arg1);
QModelIndex index=proxy.mapToSource(proxy.index(0,0));
if(index.isValid())
{
ui->tableView->selectionModel()->select(index,QItemSelectionModel::Select | QItemSelectionModel::Rows);
ui->tableView->scrollTo(index,QAbstractItemView::EnsureVisible);
}
}
But it doesn't work (no change visible). Example how it should work: Clementine Player playlist.
You create QSortFilterProxyModel and destroy it immediately in your function. It's incorrect use. You need to create one object of QSortFilterProxyModel (maybe using new), then call QTableView::setModel for attaching proxy model to your view. After that changes will take effect.
In the initialization:
ui->setupUi(this);
my_model = new QStandardItemModel(); // or any other model class
proxy_model = new QSortFilterProxyModel();
ui->table_view->setModel(proxy_model);
proxy_model->setSourceModel(my_model);
In textEdited slot:
proxy_model->setFilterRegExp(arg1);

Modify a tab in a QTabWidget where each tab represents a QTableView

I have a tab widget where every tab is a QTableView. I would like to be able to pass an updated model (QModelIndex) into each tab whenever the contents of that tab need to change.
The alternative (and nastier way) is for me to delete all the tabs, and then recreate them.
I know I can get the widget in the tab by doing something like:
tabWidget->widget(i);
This will return a widget, which is really a QTableView, but I want to update the model that is in that widget without having to delete and recreate the tab.
Thank you!
P.S. This is my current attempt...
for (int i = 0; i < tableView.size(); i++)
{
tabWidget->setCurrentWidget(tableView.at(i));
QTableView* updatedTable = (QTableView*)tabWidget->currentWidget();
updatedTable->setModel(dataModel);
tableView.replace(i, updatedTable);
}
It's not clear why you can't keep the QTableView widget and just change the model, as in your code. Doesn't the view refresh without this tableView.replace thing?
There doesn't appear to be a direct API for replacing the widget you put in with addTab() without going through a tab removal step. But instead of inserting the QTableView directly, you could instead call addTab() on a dummy widget that has a layout in it with a single item. A QStackedLayout, for instance:
QWidget* dummy = new QWidget;
QStackedLayout stackedLayout = new QStackedLayout;
stackedLayout->addWidget(tableView);
dummy->setLayout(stackedLayout);
tabWidget->addTab(dummy);
Then later, when you want to replace the tableView with a new one:
QWidget* dummy = tabWidget->currentWidget();
QStackedLayout newStackedLayout = new QStackedLayout;
newStackedLayout->addWidget(newTableView);
delete dummy->layout();
dummy->setLayout(newStackedLayout);
I still wonder what this is buying you that reusing the old table view couldn't do.