I have a treeModel. Which i am able to access in the following way.
void SumTreeProject::insertRow()
{
QModelIndex index = treeView->selectionModel()->currentIndex();
QAbstractItemModel *model = treeView->model();
TreeModel *myModel = qobject_cast<TreeModel*>(model);
if (!model->insertRow(index.row() + 1, index.parent()))
return;
Container cont;
QModelIndex child = model->index(index.row() + 1, 0,
index.parent());
model->setData(child, QVariant("Container"), Qt::EditRole);
myModel->SetContainer(child, cont);
QModelIndex indexRoot = treeView->rootIndex();
iterate(indexRoot, model);
}
I want to access the same Model from some other Cpp file.
I tried to move in all the headers but it creates a new model rather than me accessing the already existing model.
This is my Constructor defination.
WavefrontRenderer::WavefrontRenderer(QWidget *parent)
: QMainWindow(parent)
{
setupUi(this);
Container c;
TreeModel *model = new TreeModel("RootElement", c);
treeView->setModel(model);
treeView->setDragEnabled(true);
treeView->setAcceptDrops(true);
}
I tried to access the Model by making a object of the WavefrontRenderer class but then realised that it is making a new Model Structure rather than accesing the already built Model structure.
Creating a new instance of the model will create a new model.
One way to share your model is to pass it to the other class
for example
WavefrontRenderer::WavefrontRenderer(QWidget *parent, TreeModel * poOtherTreeModel )
: QMainWindow(parent)
{
setupUi(this);
Container c;
//TreeModel *model = new TreeModel("RootElement", c);
treeView->setModel(poOtherTreeModel); // TBD: for safety test for nullptr
treeView->setDragEnabled(true);
treeView->setAcceptDrops(true);
}
Then use the tree model in the SumTreeProject class and pass it to the WavefrontRenderer constractor
Related
I have subclassed QAbstractListModel in order to have a model on the qml side. I can easily use this model in ListViews and other similar components that deal with models, however, I can't access it directly. This is what I am trying without success:
myModel[0].name // TypeError: Cannot read property 'name' of undefined
Is this possible? Am I using the wrong syntax?
You can access generic model (based on QAbstractListModel) easily when you use DelegateModel model as a mediator.
import QtQuick 2.2
import QtQml.Models 2.2
DelegateModel {
id: delegateModel
}
MyModel {
id: myModel
onDataLoaded: {
delegateModel.model = myModel;
for (var row = 0; row < myModel.rowCount(); row++) {
var item = delegateModel.items.get(row).model;
console.log(" name " + row + ":" + item.name);
}
}
}
(1) You're mixing up roles and properties.
Your model implements roles which are used to feed a delegate in a view. In your code, you try to access a property.
(2) A model is not an array, where you can access rows by the [] operator.
But you can write a getter function to achieve exactly that:
class ConversationListModel : public QAbstractListModel
{
Q_OBJECT
public:
enum ConversationRoles {
IdRole = Qt::UserRole, // 256
NameRole,
};
explicit ConversationListModel(QObject *parent = 0);
QHash<int, QByteArray> roleNames() const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
Q_INVOKABLE ConversationModel* get(quint32 conversationId) const;
signals:
// ...
Than you need a data type that represents a row, e.g. ConversationModel. Now you can access a row using
myModel.get(0).name
As stated in (1), you now need to give ConversationModel a property called name
public:
explicit ConversationModel(QObject *parent = 0);
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
QString name() const;
To make the (correct) solution of Simon Warta work, you have to register ConversationModel as QML type. For example in your main you have to state
qmlRegisterType<ConversationModel>("ConversationModel", 1, 0, "ConversationModel");
Then in your qml file you have to import this type with
import ConversationModel 1.0
Afterwards everything works as expected and you can use
myModel.get(0).name
in your qml code, given that myModel is of type ConversationListModel and was registered using QDeclarativeContext::setContextProperty.
In one of my projects on Qt, I have this requirement of selecting multiple items from a folder like view (tree-view), and populate the selected items in another widget. To display available items for multi-selection, I'm using a QTreeView, and populating element hierarchy like the following
m_StandardModel = new QStandardItemModel ;
QStandardItem *rootNode = m_StandardModel->invisibleRootItem();
//defining a couple of items
QStandardItem *item1 = new QStandardItem(tr("ITEM1"));
QStandardItem *item2 = new QStandardItem(tr("ITEM2"));
QStandardItem *item3 = new QStandardItem(tr("ITEM3"));
QStandardItem *item4 = new QStandardItem(tr("ITEM4"));
rootNode->appendRow(item1 );
rootNode->appendRow(item2 );
rootNode->appendRow(item3 );
rootNode->appendRow(item4 );
//register the model
ui->treeView->setModel(m_StandardModel);
ui->treeView->expandAll();
//enabling multiselection behaviour
QItemSelectionModel *selectionModel= ui->treeView->selectionModel();
ui->treeView->setSelectionMode(QAbstractItemView::MultiSelection);
Everything's fine till here. I'm able to display my items in tree-view and also able to multiselect items. The problem occurs when I'm trying to use these multiple selected items from tree view.
In my UI, I have connected a button's clicked() signal to my slot, which handles the iteration and manipulation of selected items. Here's the function that's being called:
//User selects a number of features listed on the left pane and clicks this button to disable them
void MainWindow::on_pushButton_2_clicked()
{
QModelIndexList selectedItems = ui->treeView->selectionModel()->selectedIndexes();
QStringList items;
foreach(QModelIndex index, selectedItems)
{
QStandardItemModel* itemModel = dynamic_cast<QStandardItemModel*>(ui->treeView->model());
if(itemModel)
{
QStandardItem* item = itemModel->itemFromIndex(index);
items<< item->data().toString();
}
}
}
Debugging till the function end is perfect. But as soon as I exit this function (shown above), I get a DEBUG ASSERTION !! like the following
HEAP[myprog.exe]: Invalid address specified to RtlValidateHeap( 00390000, 01946798 )
The call stack is showing that this assertion is reached because of destruction of local QModelIndexList that I've created in the function.
Following is the call stack at the time of debug assertion:
Any idea, what I might be missing? I have tried a number of times, but yet unable to figure out the real problem. Is there a better way of doing what is being done here?
I'm using QT 4.8.4, and am building/debugging my application in DEBUG configuration on Windows 7.
I tried your code in a Windows application and I don't see your behaviour, maybe you have extra code in other part of the program that generates that behaviour. The code I tested is the following (I only modified the way to get the data, ot get the display role item->data(Qt::DisplayRole).toString()):
The cpp file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_StandardModel = new QStandardItemModel ;
QStandardItem *rootNode = m_StandardModel->invisibleRootItem();
//defining a couple of items
QStandardItem *item1 = new QStandardItem(tr("ITEM1"));
QStandardItem *item2 = new QStandardItem(tr("ITEM2"));
QStandardItem *item3 = new QStandardItem(tr("ITEM3"));
QStandardItem *item4 = new QStandardItem(tr("ITEM4"));
rootNode->appendRow(item1 );
rootNode->appendRow(item2 );
rootNode->appendRow(item3 );
rootNode->appendRow(item4 );
//register the model
ui->treeView->setModel(m_StandardModel);
ui->treeView->expandAll();
//enabling multiselection behaviour
QItemSelectionModel *selectionModel= ui->treeView->selectionModel();
ui->treeView->setSelectionMode(QAbstractItemView::MultiSelection);
connect(ui->pushButton, SIGNAL(clicked()),
this, SLOT(on_pushButton_clicked()) );
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked(){
QModelIndexList selectedItems = ui->treeView->selectionModel()->selectedIndexes();
QStringList items;
foreach(QModelIndex index, selectedItems)
{
QStandardItemModel* itemModel = dynamic_cast<QStandardItemModel*>(ui->treeView->model());
if(itemModel)
{
QStandardItem* item = itemModel->itemFromIndex(index);
items << item->data(Qt::DisplayRole).toString();
}
}
}
The header file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QStandardItemModel>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QStandardItemModel * m_StandardModel ;
};
#endif // MAINWINDOW_H
Maybe you can publish the hole source code to see the behaviour you are having.
I have a custom table model that extends QAbstractTableModel. I've implemented data(), rowCount() and columnCount().
This is my model:
point_data_view_model::point_data_view_model(QObject *parent) :
QAbstractTableModel(parent)
{
}
int point_data_view_model::rowCount(const QModelIndex & /*parent*/) const
{
return 2;
}
int point_data_view_model::columnCount(const QModelIndex & /*parent*/) const
{
return 3;
}
QVariant point_data_view_model::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
{
return QString("Row%1, Column%2")
.arg(index.row() + 1)
.arg(index.column() +1);
}
return QVariant();
}
But when I want to show the table view in my form, nothing is displayed.
This is the form's constructor:
SelectWindow::SelectWindow(QWidget *parent) :
QMainWindow(parent)
,ui(new Ui::SelectWindow)
{
ui->setupUi(this);
point_data_view_model myModel;
ui->tableView->setModel(&myModel);
myModel.timerHit();
ui->tableView->show();
}
In other words, my data function in the model is not called.
What am I missing?
point_data_view_model myModel;
That's a big problem. myModel is local to that constructor and will get destroyed when the function exits. You need to store that model somewhere that persists as long as you need to use it - a member variable of your SelectWindow should be appropriate.
You're creating myModel on stack. It gets deleted immediately after calling ui->tableView->show();. So the view loses the model and doesn't have a chance to call data. You should create myModel using new:
point_data_view_model* myModel = new point_data_view_model();
I've created a QAbstractListModel derived model based on an underlying QHash. Since I need to use the model in QML, I cannot make use of the sorting functionality Qt widgets and views have integrated.
I tried using a QSortFilterProxyModel but it doesn't seem to work with my model. Getting the model to properly work in QML wasn't tedious enough, and now I am stuck on sorting.
Any suggestions are appreciated.
Here is the model source:
typedef QHash<QString, uint> Data;
class NewModel : public QAbstractListModel {
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
public:
NewModel(QObject * parent = 0) : QAbstractListModel(parent) {}
enum Roles {WordRole = Qt::UserRole, CountRole};
QHash<int, QByteArray> roleNames() const {
QHash<int, QByteArray> roles;
roles[WordRole] = "word";
roles[CountRole] = "count";
return roles;
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const {
if (index.row() < 0 || index.row() >= m_data.size()) return QVariant();
Data::const_iterator iter = m_data.constBegin() + index.row();
switch (role) {
case WordRole:
return iter.key();
case CountRole:
return iter.value();
} return QVariant();
}
int rowCount(const QModelIndex &parent) const {
Q_UNUSED(parent)
return m_data.size();
}
int count() const { return m_data.size(); }
public slots:
void append(const QString &word) {
bool alreadyThere = m_data.contains(word);
if (alreadyThere) m_data[word]++;
else m_data.insert(word, 1);
Data::const_iterator iter = m_data.find(word);
uint position = delta(iter);
if (alreadyThere) {
QModelIndex index = createIndex(position, 0);
emit dataChanged(index, index);
} else {
beginInsertRows(QModelIndex(), position, position);
endInsertRows();
emit countChanged();
}
}
void prepend(const QString &word) {
if (m_data.contains(word)) m_data[word]++;
else m_data.insert(word, 1);
}
signals:
void countChanged();
private:
uint delta(Data::const_iterator i) {
uint d = 0;
while (i != m_data.constBegin()) { ++d; --i; }
return d;
}
Data m_data;
};
Here is "trying" to sort it:
NewModel model;
QAbstractItemModel * pm = qobject_cast<QAbstractItemModel *>(&model);
QSortFilterProxyModel proxy;
proxy.setSourceModel(pm);
proxy.setSortRole(NewModel::WordRole);
proxy.setDynamicSortFilter(true);
Alas, the proxy works as a model, but it doesn't sort the entries.
If you enable QSortFilterProxyModel::setDynamicSortFilter(true), you need to call QSortFilterProxyModel::sort(...) function once to let the proxy know which way to sort.
With that, any time the model is updated the proxy will sort everything again just automatically.
proxy.setDynamicSortFilter(true);
proxy.sort(0);
First of all, There's no need for qobject_cast<QAbstractItemModel *> downcasting -- the NewModel is a derived class of the QAbstractItemModel and the polymorphism principle says that you can use a subclass everywhere where a parent class is applicable.
Second, your prepend method does not use beginInsertRows and endInsertRows. That's a violation of the MVC API. You'll get data corruption in the attached views and proxy models if you use it this way.
Third, you haven't mentioned whether you're actually using your proxy model as the model for the attached view :).
Finally, you are using QHash as a backing store of your data with QHash::iterator for insertion. That's an itneresting solution, but something which just cannot work -- an insertion or removal can cause the hash table to grow/shrink, which means changing all data you publish via your model indexes. This is just not going to work. Don't use QHash when you need a stable order. The O(n) complexity of your delta method should be interpreted as a warning; this is a wrong approach.
Have a Look at https://github.com/oKcerG/SortFilterProxyModel. This project exposes the functionality of QSortFilterProxyModel nicely to QML. I used it in different projects and it junst worked. However if you want to implement your own solution it's something to get your ideas.
I have a QSqlQueryModel table. The user can see and access this. I want to know which row/rows that the user selects.
I have looked through a lot of other posts and documentations from qt-centre and I think the closest I believe is to somehow use QModelIndex, as shown here:
// for QSqlQueryModel or subclasses:
QSqlQueryModel *qmod = qobject_cast<QSqlQueryModel*>(md);
if(!qmod) return false;
QSqlRecord rec = qmod->record(curr.row()); // you have data inside the "rec" object
taken from http://www.qtcentre.org/archive/index.php/t-3104.html.
However this doesn't work for me. I don't want to use Qtableview as I want to work with only sqlQueryModel.
How do I detect user selections?
Thanks!
QTableView has a selection model. You can use the currentRowChanged signal of that selection model:
YourWidget : public QWidget
{
Q_OBJECT
public:
YourWidget(QWidget *parent = 0);
private slots:
void onRowChanged(QModelIndex index);
}
YourWidget::YourWidget (QWidget *parent) :
QWidget(paret)
{
...
QTableView *view = new QTableView;
view->setModel(yourModel);
connect(view->selectionModel(),
SIGNAL(currentRowChanged(QModelIndex, QModelIndex)),
this,
SLOT(onRowChanged(QModelIndex)));
...
}
void YourWidget::onRowChanged(QModelIndex index)
{
int row = index.row();
QSqlRecord rec = yourModel->record(row);
...
}