QListView to display created Shapes
-
Hello,
I am making a paint program. The user can create geometric shapes. Currently I store these shapes inside a QList<Shape*> and a QPainter iterates over the list, drawing them.
I want to have a QListView or something similar that will display all created shapes so you can select them by clicking on their name (like in Photoshop the Layer/Shape selection panel). Each shape gets their own name like Shape1, Shape2, etc. and then are displayed in a list.
I am trying to use QListView for this but realised I need to make a model and subclass QAbstractListModel. I did that, and moved the QList as a private member of the subclassed class. Now I have to implement the rowCount() and data() functions to return the data from the QList, but the data() function wants to return a QVariant, which doesn't have a constructor for QList, only for QStringList, so I get an error message.
What would be the best way to store a list of Shapes ( I need polymorphism so it needs to store Shape* since specific shape types inherit from the base Shape class) while being to able to display these objects inside a list so they can easily be selected?
Thanks
-
Hi,
maybe, the most easiest way is to use QTableWidget. It has a method "setCellWidget" or something similar. Is already tested with native controls, like QComboBox, so as I can imagene the solution for you:
Create a 2 columns QTableWidget item, where the first one will displays the name of shapes, and the second one could be a QLabel instance that can display bitmaps or any other images.
By the way: I looked for such a solution preferring MVC, and I got a tip that I can implement these solution with QListView or QTableView (both of them are model based grids), because they are inherent from QTableWidget. I didn't have to have a chance to test it.For your current implementation: I don't understand, why did you sublcassed from the QAbstractListView, I think QStandardListModel would be suitable for you.
Regards,
Norbert -
Hi,
You should not return your list when data is called, only the element corresponding to the row of the index passed to the method. The you can use a QStyledItemDelegate to render your shape in QListView unless you already return something suitable for e.g. the decoration role.
-
@SGaist Yes, I tried like:
QVariant ShapeListModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
{
return shapeList.at(index.row());
}
else
{
return QVariant();
}
}But I get:
\untitled1\paintarea.cpp:12: error: C2248: 'QVariant::QVariant' : cannot access private member declared in class 'QVariant'Oh btw I do not need to render the shape inside the ListView, I just need there a name and maybe a toggle to hide/show the shape on the main canvas.
-
You are using a custom type so you must use QVariant::fromValue
-
OK, developing an early working prototype, but still further issues. I created my model:
class ShapeListModel : public QAbstractListModelIt has a member which stores the Shapes that the user can create (the shapes get created by clicking mouse on canvas, this is important):
QList<Shape*>& shapeList;I implemented data() like this (so far it only gets the name of the shape, like Shape1, etc.):
QVariant ShapeListModel::data(const QModelIndex &index, int role) const
{
return QVariant::fromValue(shapeList.at(index.row())->getName());
}And setData() like this:
bool ShapeListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
Shape* shape = value.value<Shape*>();
shapeList.insert(shapeList.count(), shape);
return true;
}This adds a Shape* to the shapeList and the shapes are drawn. Now I want to update the ListView that displays the list of shapes, so I need to emit a dataChanged() signal (which takes QModelIndex as parameter). My problem now is that I am not adding Shapes through the ListView, but rather the shapes are added when the user draws them on the canvas. So I do not know how to use a QModelIndex, to tell the ListView, which row to refresh?
I know that classical usage is to use setData() through the View widget to edit the data but in my program that's not the usage case as the Shapes get added as they are drawn on canvas and not added in the widget they are displayed. Not sure how to tell the widget to update the proper cell.
-
When using a custom model, the usage is to add specialized methods to your model to add/remove your custom objects. So when you add/remove a Shape you know exactly where they are so you can use beginInsertRow/endInsertRow and the update will happen automatically for you.