I created my custom class (CustomObject), that inherit from QGraphicsItem. I use those object to draw on scene (rectangles, polygons and stuff) and I store pointers to them in QList. Now I need to display all my CustomOBjects in tableView, but there are 2 conditions:
When I select and interact with object in tableview - I must be able to interact with "real" CustomObject represented by it (example: I selected obj1 in tableview and click button "delete" or "edit" - and I want to be able to interact with acctaul object (delet or edit it).
When I add new or change it - i wish to see change in tableView.
I'm not sure if i can achieve that with jsut table view and soem custom mdoel - or shoud I make my own QAbstractItemModel class, but if i do - how do i do it? Shoud i make class inherit from QAbstractItemModel and add pointer to my CustomObject, or just force my CustomObjects into soem specific model?
Little bits of my code:
Here is my CustomObject.h
//I removed some code that was stricly related to "personal" functions related with specific features of my app
class CustomObject : public QGraphicsItem
{
public:
CustomObject();
CustomObject(int _x, int _y, int _w, int _h);
virtual QRectF boundingRect() const;
void set_Name(QString name);
QString get_Name();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
private:
QString Name;
I store them in list, in my "Overseer" class:
class ObjOverseer
public:
void drawingCustomObject_Do(int x, int y); //This function creates new "CustomObject" and adds it to the list (below)
QList<CustomObject*> ObjectsList_CustomObjects;
In my mainwindow - I simply create that ObjOverseer and keep its pointer.
EDIT 1
I used this example:
https://doc.qt.io/archives/4.6/itemviews-addressbook.html
and created this class:
CustomModelOfCustomObject::CustomModelOfCustomObject()
{
}
CustomModelOfCustomObject::CustomModelOfCustomObject(QObject *parent)
: QAbstractTableModel(parent)
{
}
CustomModelOfCustomObject::CustomModelOfCustomObject(QList<CustomObject*> objects, QObject *parent)
: QAbstractTableModel(parent)
{
ListOfObjects=objects;
}
int CustomModelOfCustomObject::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return ListOfObjects.size();
}
int CustomModelOfCustomObject::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 2;//TODO - ZMIENIC ILOSC KOLUMN
}
QVariant CustomModelOfCustomObject::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= ListOfObjects.size() || index.row() < 0)
return QVariant();
if (role == Qt::DisplayRole) {
CustomObject* obj = ListOfObjects.at(index.row());
if (index.column() == 0)
return obj->get_Name();
else if (index.column() == 1)
return obj->get_Address();
}
return QVariant();
}
QVariant CustomModelOfCustomObject::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
switch (section) {
case 0:
return tr("Name");
case 1:
return tr("Address");
default:
return QVariant();
}
}
return QVariant();
}
bool CustomModelOfCustomObject::insertRows(int position, int rows, const QModelIndex &index)
{
Q_UNUSED(index);
beginInsertRows(QModelIndex(), position, position+rows-1);
for (int row=0; row < rows; row++) {
CustomObject* obj;
ListOfObjects.insert(position, obj);
}
endInsertRows();
return true;
}
bool CustomModelOfCustomObject::removeRows(int position, int rows, const QModelIndex &index)
{
Q_UNUSED(index);
beginRemoveRows(QModelIndex(), position, position+rows-1);
for (int row=0; row < rows; ++row) {
ListOfObjects.removeAt(position);
}
endRemoveRows();
return true;
}
bool CustomModelOfCustomObject::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
int row = index.row();
CustomObject* p = ListOfObjects.value(row);
if (index.column() == 0)
p->set_Name(value.toString());
else if (index.column() == 1)
p->set_Address(value.toString());
else
return false;
ListOfObjects.replace(row, p);
emit(dataChanged(index, index));
return true;
}
return false;
}
Qt::ItemFlags CustomModelOfCustomObject::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}
QList<CustomObject*> CustomModelOfCustomObject::getList()
{
return ListOfObjects;
}
But still, when i reach point in my function where i shoud use this model- i dont know hwo i shoud add it or even if i will be able to use it as intended.
EDIT 2
When i chaned ListOfObject to public and tried:
MyModel->ListOfObjects.append(newObj);
all crashed
First thing to note is, you list in ObjOverseer and CustomModelOfCustomObject are not connected, you need to save the pointer of ObjOverseer list instead of copy it to CustomModelOfCustomObject. This one :
CustomModelOfCustomObject::CustomModelOfCustomObject(QList<CustomObject*> objects, QObject *parent)
: QAbstractTableModel(parent)
{
ListOfObjects=objects;
}
You need to add function to your CustomModel that will handle adding new customobject :
bool CustomModelOfCustomObject::insertNewData(CustomObject *obj, int rowposition = -1)
{
int row = rowposition < 0 ? ListOfObjects.size : row;
beginInserRows(QModelIndex(), row, row);
ListOfObjects.insert(row, obj);
endInsertRow();
}
And when you want to add new object, simply call those function. If you list on model is connected to ObjOverseer (pointer type), you does not need to add new object to your ObjOverseer.
Solved it. Now i only add "representation" of object to model and whenever i need to update any object (ew. change name or color) i just do it via my overseer, passing soem kind of guid/identifier/id/anything that woudl let me tell objects apart form eachother.
Related
I want to update the active tool tip of a view widget of an model/view scenario. The tool tip is determined by calling data of the model with the Qt::ToolTipRole role.
However, I don't find any solution to update a displayed/active tool tip due to a change of data. Scenarios are, for instance, some time-based tool tips or image tool tips with a preview while the image is loading.
I went through some time researching for a solution and I want to share a possible answer to the above question.
EDIT:
Emitting dataChanged with the Qt::ToolTipRole alone doesn't seem to update an active tool tip, here's my minimal example for a QTableViewModel model (tested with Qt5):
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
MyModel(QObject *parent = 0) : QAbstractTableModel(parent), i(0) {
QTimer * timer = new QTimer(this);
timer->setInterval(500);
connect(timer, &QTimer::timeout, this, &MyModel::onNewTime);
timer->start();
}
int rowCount(const QModelIndex &) const { return 1; }
int columnCount(const QModelIndex &) const { return 1; }
QVariant data(const QModelIndex &, int role) const {
switch(role){
case Qt::DisplayRole:
return "Hund";
case Qt::ToolTipRole:
return i;
default:
return QVariant();
}
}
private slots:
void onNewTime() {
i+=1;
QModelIndex model = index(0,0);
emit dataChanged(model, model, QVector<int>()<<Qt::ToolTipRole);
}
private:
int i;
};
The first idea to subclass, for instance, QTableView fails, since neither eventFilter(...) event(...) nor helpEvent(...) are called with a QEvent::ToolTip-event.
I ended up re-implementing viewportEvent(...) to catch a QEvent::ToolTip event. Also I had to make sure the tool-tip popup would not be updated once the mouse had been moved.
This is my solution. It will basically close and re-open the tool tip on a data change. To update a tool tip while it is running, just call or connect to the slot showOrUpdateToolTip. First the subclassing of QTableView:
class TableViewToolTipModifier : public QTableView
{
Q_OBJECT
public:
TableViewToolTipModifier(QWidget *parent=0);
public slots:
void showOrUpdateToolTip();
protected:
bool viewportEvent(QEvent *event);
void mouseMoveEvent(QMouseEvent *);
private:
QPoint _lastPosition;
bool _isActive = false;
};
The implementation:
TableViewToolTipModifier::TableViewToolTipModifier(QWidget* parent)
: QTableView(parent)
{
this->setMouseTracking(true);
}
void TableViewToolTipModifier::showOrUpdateToolTip()
{
if (QTableView::underMouse() && _isActive)
{
const QModelIndex index = QTableView::indexAt(
this->mapFromGlobal(_lastPosition));
if (index.isValid())
{
const QString toolTip = index.data(Qt::ToolTipRole).toString();
QToolTip::showText(_lastPosition, toolTip, this, QRect());
}
}
}
void TableViewToolTipModifier::mouseMoveEvent(QMouseEvent * event)
{
_isActive = false;
QToolTip::hideText();
QTableView::mouseMoveEvent(event);
}
bool TableViewToolTipModifier::viewportEvent(QEvent *event)
{
if (event->type() == QEvent::ToolTip)
{
_lastPosition = static_cast<QHelpEvent*>(event)->globalPos();
_isActive = true;
showOrUpdateToolTip();
return true;
}
return QTableView::viewportEvent(event);
}
If you use model/view approach the only thing you need to do is writing an appropriate code in your model's data() function, i.e. QAbstactItemModel::data(). The code should look like:
QVariant MyModel::data(const QModelIndex &item, int role) const
{
if (role == Qt::DisplayRole)
{
return "This is view item text";
}
else if (role == Qt::ToolTipRole)
{
// Here you should decide what to return.
if (someCondition)
return "This is the actual tool tip";
else
return "Something else";
}
return QAbstractItemModel::data(item, role);
}
Thus, your model has to return the actual tool tip text.
I have the model MyModel : public QSqlTableModel.
I want to forbid editing certain columns of the table. I did this using the method flags(), but faced with a new problem. I need to insert a row in the database, and when I cause methods insertRow () and setData (), the method setData () returns 0 for not editable columns. Therefore, I can not fill data to inserted row.
I want to forbid editing certain columns for view, but not for the model.
Some code:
Qt::ItemFlags ApplicantTableModel::flags(const QModelIndex &index) const
{
if(index.column() == 9 || index.column() == 10)
if(index.column() == 9 && index.data() == 0)
return Qt::ItemIsEnabled|Qt::ItemIsSelectable;
else
return Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable;
else return Qt::ItemIsEnabled|Qt::ItemIsSelectable;
}
I fix it in next way. I created delegate.
QWidget *StatusDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.column() == 9){
QComboBox* editor = new QComboBox(parent);
editor->addItem(tr("Відраховано"));
editor->addItem(tr("Допущено"));
editor->addItem(tr("Бюджет"));
editor->addItem(tr("Контракт"));
editor->setAutoFillBackground(true);
return editor;
}
else return 0;
}
Given the information you left, I created an example widget, which shows that setData() even works on non editable indexes.
Model
class MyModel : public QStandardItemModel
{
public:
MyModel(QObject* parent = 0)
: QStandardItemModel(parent)
{}
Qt::ItemFlags flags(const QModelIndex &index) const {
if(index.column() < 5)
return Qt::ItemIsEnabled|Qt::ItemIsSelectable;
else
return Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable;
}
};
Table Widget
// .h
class MyTableWidget : public QWidget
{
Q_OBJECT
public:
MyTableWidget(QWidget* parent = 0);
private slots:
void onEditTextChanged(QString const&);
private:
void initWidgets();
void initLayout();
QTableView* table_view_;
MyModel* table_model_;
QLineEdit* table_edit_;
};
// .cc
MyTableWidget::MyTableWidget(QWidget* parent)
: QWidget(parent)
, table_view_(0)
, table_model_(0)
, table_edit_(0)
{
initWidgets();
initLayout();
}
void MyTableWidget::onEditTextChanged(QString const& text)
{
foreach(QModelIndex index, table_view_->selectionModel()->selectedIndexes())
table_model_->setData(index, text);
}
void MyTableWidget::initWidgets()
{
table_view_ = new QTableView(this);
table_edit_ = new QLineEdit(this);
table_model_ = new MyModel;
table_model_->setColumnCount(10);
table_model_->setRowCount(10);
for(int c = 0; c < table_model_->columnCount(); ++c) {
for(int r = 0; r < table_model_->rowCount(); ++r) {
table_model_->setData(table_model_->index(r,c), QString("foo"));
}
}
table_view_->setModel(table_model_);
connect(table_edit_, SIGNAL(textChanged(QString const&)),
this, SLOT(onEditTextChanged(QString)));
}
void MyTableWidget::initLayout()
{
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(table_view_);
layout->addWidget(table_edit_);
setLayout(layout);
}
Leaving out the Qt::ItemIsEditable flag will only prevent editing by any caller that handles this flag. For instance, Qt delegates check flags() before they create the editor for modifying cell values. This prevents from ever calling setData(). If you call setData() yourself there should be no problem in editing the data.
I am assuming your error lies somewhere else. Did you overwrite setData() to handle flags()? Did you check if the ModelIndex you are handing in is correct?
I implemented a derived class from QAbstractItemModel, and it seems to work fine.
Then I created a QItemSelectionModel and assigned it to the mentioned QAbstractItemModel.
My QAbstractItemModel is not a widget and is not displayed, only manages a hierarchy.
When the selection is changed, and the QItemSelection model emits the selection changed signal the QItemSelections selected and deselected seem to contain the right data.
The problem arises when i call their ::indexes() function to get the indexes of the selected items, it returns no items, even so I know items are selected and the ::width() and ::height() functions return correct values.
Basic example code:
(A working example and files that demonstrates the problem follows)
class DerivedModel : public QAbstractItemModel {
DerivedModel(QObject* parent) : QAbstractItemModel(parent)
,m_selectionModel(nullptr)
{
//create the selection model and assign this model to it
m_selectionModel = new QItemSelectionModel(this, this);
}
...
//all needed overload functions
//the DerivedModel works great
...
private:
QItemSelectionModel* m_selectionModel;
}
//in a different object called SceneModel (a QGraphicsScene which shows graphical items based on the DerivedModel) which is connected to the selection models selectionChanged() signal I query the new selection
SceneModel::setSelectedItems(const QItemSelection& selected, const QItemSelection& deselected){
int selectionSize_A = selected.size(); //this returns correct number of selected items
int selectionSize_B = selected.indexes().size(); //this returns 0 -> WRONG
int selectionSize_C = selected.value(0).indexes().size(); //this returns 0 -> WRONG
int selectionSize_CA = selected.value(0).width(); //this returns correct
int selectionSize_CB = selected.value(0).height(); //this returns correct
//if I purposefully try to access the 1st selected index via QItemSelectionRange::topLeft() all is good and I get the index:
QItemSelectionRange range = selected.value(0);
QModelIndex topLeft = range.topLeft(); //cool, i get the 1st selected index
//it seems there is a problem with the ::indexes function, so dived into the Qt5 source and basically implemented again whats done there and it works.
}
A link to the files including cmake build:
https://drive.google.com/file/d/0Bz03DnXr46WXYXRCeExtaHZadUU/view?usp=sharing
Whats happening there:
A DerivedModel is created and holds 2 items (A and B) under root item (ROOT).
Pushing a button signals the QItemSelectionModel to select/deselect A or B.
If the item is found in the model "Found Item :)" is printed, showing the item exists and is available to the model.
The QGraphicsView holds a Scene (derived from QGraphicsScene).
That scene is empty, and only represents an object receiving the selectionChange signal from the selection model.
When it receives that signal it prints "Scene received item selection change" so we can see the signal has passed.
then comes the real stuff:
we get a count of how many QItemRanges are in the passed "selected" variable, which is correct.
we get a count of how many indexes are inside all the ranges in the passed "selected" variable (selected.indexes()) that returns 0 which is wrong as we can soon see
we manually access the 1st index in the 1st range in the "selected" variable (selected.value(0).topLeft()) and see that it indeed holds an index pointing to the right item, showing the problem.
If anybody knows about something, or can see an error in my approach please let me know.
Thanks!
Linux Manjaro Gcc 4.9.1 Qt5.3
DerivedModel.h:
#ifndef DERIVEDMODEL_H
#define DERIVEDMODEL_H
#include <QAbstractItemModel>
//fwd declaration
QT_FORWARD_DECLARE_CLASS(QItemSelectionModel)
class Item;
class DerivedModel : public QAbstractItemModel{
Q_OBJECT
public:
//model is a singleton, function to get instance
static DerivedModel& instance();
explicit DerivedModel(QObject* parent);
virtual ~DerivedModel();
/////////////////model overloads//////////////////////////////
QVariant data(const QModelIndex& index, int role) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex& index) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const {Q_UNUSED(parent); return 1;}
//////////////////////////////////////////////////////////////
//get the item from an index
Item* item(const QModelIndex& index) const { return static_cast<Item*>(index.internalPointer());}
//get the index from an item name
const QModelIndex indexFromName(const QString& name);
//add an item
void addItem(const QString& name, Item* parent=nullptr);
//get the selection model
QItemSelectionModel* selectionModel() const {return m_selectionModel;}
private:
//the instance of the singleton to return
static DerivedModel* m_instance;
//the root object for the model
//never actually used
Item* m_rootItem;
//selection model for handeling selection
QItemSelectionModel* m_selectionModel;
};
#endif
DerivedModel.cpp
#include "DerivedModel.h"
#include "Item.h"
#include <QItemSelectionModel>
#include <QDebug>
//init static member
DerivedModel* DerivedModel::m_instance = nullptr;
DerivedModel& DerivedModel::instance(){
//check if set
if(!m_instance){
qDebug() << "ERROR model instance not set";
std::abort();
}
return *m_instance;
}
DerivedModel::DerivedModel(QObject* parent):
QAbstractItemModel(parent)
,m_rootItem(nullptr)
,m_selectionModel(nullptr)
{
//set the instance
m_instance = this;
//creae root item
m_rootItem = new Item("ROOT");
//init selection model
m_selectionModel = new QItemSelectionModel(this, this);
}
DerivedModel::~DerivedModel(){
//selection model is child so gets deleted
}
QVariant DerivedModel::data(const QModelIndex& index, int role) const {
//if the index is valid
if(!index.isValid()) {
qDebug() << "Index not valid!";
return QVariant();
}
//switch role
switch(role){
case Qt::DisplayRole:{
QString name = static_cast<Item*>(index.internalPointer())->name();
return name;
break;
}
default:
return QVariant();
}
}
Qt::ItemFlags DerivedModel::flags(const QModelIndex& index) const {
//check valid
if(!index.isValid()) return 0;
return static_cast<Item*>(index.internalPointer())->flags();
}
QVariant DerivedModel::headerData(int section, Qt::Orientation orientation, int role) const {
//unused for now
Q_UNUSED(section);
Q_UNUSED(orientation);
if(role==Qt::DisplayRole) return QVariant("HeaderData");
else return QVariant();
}
QModelIndex DerivedModel::index(int row, int column, const QModelIndex& parent) const {
Item* parentItem(nullptr);
//is valid?
if(!parent.isValid()) {
parentItem = m_rootItem;
}
else {
parentItem = item(parent);
}
//child pointer holder
Item* childItem = parentItem->children().value(row);
//is null?
if(childItem){
return createIndex(row, column, childItem);
}
else {
return QModelIndex();
}
}
QModelIndex DerivedModel::parent(const QModelIndex& index) const {
//check valid
if(!index.isValid()) return QModelIndex();
//get child
Item* childItem = static_cast<Item*>(index.internalPointer());
//find parent
Item* parentItem = childItem->parent();
//is null?
if(parentItem == m_rootItem) return QModelIndex();
return createIndex(parentItem->parent()->children().indexOf(parentItem), 0, parentItem);
}
int DerivedModel::rowCount(const QModelIndex& parent) const {
//parent holder
Item* parentItem;
//check 0 column (not sure why, but is in example, maybe the model iterates also through different columns)
if(parent.column()>0) return 0;
//check valid
if(!parent.isValid()) parentItem = m_rootItem;
else parentItem = static_cast<Item*>(parent.internalPointer());
return parentItem->children().length();
}
const QModelIndex DerivedModel::indexFromName(const QString& name){
//make a match based on the name
//and return 1st match
QModelIndex index = match(DerivedModel::index(0,0,QModelIndex()),
Qt::DisplayRole, name, 1,
Qt::MatchFlags(Qt::MatchExactly|Qt::MatchRecursive))
.value(0);
return index;
}
void DerivedModel::addItem(const QString& name, Item* parent){
//check parent
if(!parent) parent = m_rootItem;
//create the item
//will be deleted once parent is deleted
new Item(name, parent);
}
Item.h:
#ifndef ITEM_H
#define ITEM_H
#include <QString>
#include <QList>
#include <QDebug>
class Item {
public:
Item(const QString& name, Item* parent=nullptr) :
m_name(name), m_parent(parent){
//add as child to parent
if(parent) parent->addChild(this);
//set the flag to enable selection
m_flags = Qt::ItemIsSelectable;
qDebug() << "Created Item "+name;
};
~Item(){
//delete children
for (auto& child : m_children){
delete child;
}
};
//get the name
const QString& name() const {return m_name;}
//get the flags
const Qt::ItemFlags& flags() const {return m_flags;}
//gte the parent
Item* parent() const {return m_parent;}
//get the children
const QList<Item*>& children() {return m_children;}
//add a child
void addChild(Item* item) {m_children.append(item);}
private:
//name
QString m_name;
//flags
Qt::ItemFlags m_flags;
//parent
Item* m_parent;
//list og children
QList<Item*> m_children;
};
#endif
Scene.h:
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
//fwd dec
QT_FORWARD_DECLARE_CLASS(QItemSelection)
QT_FORWARD_DECLARE_CLASS(QGraphicsRectItem)
class Scene : public QGraphicsScene {
public:
Scene(QObject* parent);
virtual ~Scene(){}
public slots:
//pass the selection to the squares
void setSelection(const QItemSelection& selected, const QItemSelection& deselected);
};
#endif
Scene.cpp:
#include "Scene.h"
#include "DerivedModel.h"
#include "Item.h"
#include <QItemSelectionModel>
#include <QGraphicsRectItem>
#include <QRect>
#include <QDebug>
Scene::Scene(QObject* parent) : QGraphicsScene(parent)
{
//connect to the models selection change
connect(DerivedModel::instance().selectionModel(), &QItemSelectionModel::selectionChanged,
this, &Scene::setSelection);
}
void Scene::setSelection(const QItemSelection& selected, const QItemSelection& deselected){
Q_UNUSED(deselected);
//testing changes
int A = selected.size();
int B = selected.indexes().size();
QModelIndex index = selected.value(0).topLeft();
QString name = "";
if(index.isValid()) name = static_cast<Item*>(index.internalPointer())->name();
qDebug() << "Scene recieved item selection change";
qDebug() << "Number of selected QItemRanges from source = "+QString::number(A);
qDebug() << "Number of selected INDEXES from source = "+QString::number(B);
qDebug() << "Manually accessd 1st index in 1st range returns item named: "+name;
}
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
//fwd dec
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
QT_FORWARD_DECLARE_CLASS(QGraphicsView)
class DerivedModel;
class Scene;
class Widget : public QWidget{
public:
Widget(QWidget* parent=nullptr);
virtual ~Widget(){}
private slots:
//button toggle alot
void toggle(bool state);
private:
//layout
QVBoxLayout* m_mainVBLayout;
//horizontal layout for the buttons
QHBoxLayout* m_HBButtonsLayout;
//GraphicsView widget for the scene
QGraphicsView* m_graphicsView;
//the graphics scene recieving the change event where the problem is
Scene* m_scene;
//push buttons
QPushButton* m_button1;
QPushButton* m_button2;
//the DerivedModel
DerivedModel* m_model;
};
#endif
Widget.cpp:
#include "Widget.h"
#include "DerivedModel.h"
#include "Scene.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGraphicsView>
#include <QDebug>
#include <QItemSelection>
Widget::Widget(QWidget* parent) : QWidget(parent)
,m_mainVBLayout(nullptr)
,m_HBButtonsLayout(nullptr)
,m_graphicsView(nullptr)
,m_scene(nullptr)
,m_button1(nullptr)
,m_button2(nullptr)
,m_model(nullptr)
{
//init the DerivedModel
m_model = new DerivedModel(this);
//add two items to the model
m_model->addItem("A");
m_model->addItem("B");
//create the main layout
m_mainVBLayout = new QVBoxLayout(this);
//create the buttons layout
m_HBButtonsLayout = new QHBoxLayout;
//add it to the main layout
m_mainVBLayout->addLayout(m_HBButtonsLayout);
//create the buttons
m_button1 = new QPushButton("A", this);
m_button2 = new QPushButton("B", this);
//set them to be checkable
m_button1->setCheckable(true);
m_button2->setCheckable(true);
//connect their signals
connect(m_button1, &QPushButton::toggled, this, &Widget::toggle);
connect(m_button2, &QPushButton::toggled, this, &Widget::toggle);
//add them to the layout
m_HBButtonsLayout->addWidget(m_button1);
m_HBButtonsLayout->addWidget(m_button2);
//create the graphics view
m_graphicsView = new QGraphicsView(this);
//create the scene
m_scene = new Scene(this);
m_scene->setSceneRect(QRect(0,0,50,25));
//set its scene
m_graphicsView->setScene(m_scene);
//add the graphics view to the layout
m_mainVBLayout->addWidget(m_graphicsView);
}
void Widget::toggle(bool state){
//get the sender
QPushButton* button(nullptr);
if(sender()==m_button1) button = m_button1;
else button = m_button2;
//get the name of the item related to the button to change
QString name = button->text();
//get the index based on the name
//im using the instance of the DerivedModel because this is how I implement it in my project;
QModelIndex itemIndex = DerivedModel::instance().indexFromName(name);
//check if index is valid
if(!itemIndex.isValid()){
qDebug() << "Index for item "+name+" not valid!";
return;
}
else
qDebug() << "Found Item :)";
//create a QItemSelection as how it is in my project
QItemSelection selection;
//add the index to the selection
selection.select(itemIndex, itemIndex);
//check the state
if(state){
//add to the selection
DerivedModel::instance().selectionModel()->select(selection, QItemSelectionModel::Select);
}
else{
//remove from selection
DerivedModel::instance().selectionModel()->select(selection, QItemSelectionModel::Deselect);
}
}
Ok, so I found the culprit :)
In case anybody else has the same problem, my error was in the ::flags() function of my derived QAbstractItemModel class:
Inside the definition I didnt call the base class QAbstractItemModel::flags(index) function, once I called that instead of returning the flags myself all went well.
So I think that as long as your item has a Qt::flags flags() function that the model can call, you dont have to reimplement the QAbstractItemModel::flags() function.
It seems that the model is quering the flags through the QModelIndex::flags() function anyway.
Thanks you "Ezee" and "Kuba Ober" for willing to help.
My issue was that I was not calling the flags function of the inherited class.
def flags(self, index):
super_flags = super().flags(index)
flags = Qt.ItemIsEnabled
node = index.internalPointer()
if index.column() == 0:
flags |= Qt.ItemIsUserCheckable
if node.parent == self._rootNode:
flags |= Qt.ItemIsAutoTristate
return flags|super_flags
How can I build the Layout used by QtCreator in Tools->Options for my own application? It should look like this, but with another content:
How can I do this using C++/Qt using the QtDesigner integrated in QtCreator?
I just found the solution! I needed to use a QListView with a QStringListModel in it. To set the Item Size and Icons manually, I needed to subclass QStringListModel and QStyledItemDelegate:
// ItemDelegate to set item size manually
class MyItemDelegate : public QStyledItemDelegate
{
public:
virtual QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QSize suggestion = QStyledItemDelegate::sizeHint(option, index);
return QSize(suggestion.width(), 30);
}
};
// StringListModel to allow setting images
class MyStringListModel : public QStringListModel
{
public:
virtual QVariant data (const QModelIndex &index, int role) const
{
if (role == Qt::DecorationRole)
{
QString value = stringList().at(index.row());
if (value == "new")
return add;
else
return db;
}
else
return QStringListModel::data(index, role);
}
private:
QIcon db = QIcon::fromTheme("server-database");
QIcon add = QIcon::fromTheme("list-add");
};
Then, I could update my QListView (that was created by Qt):
MyStringListModel * model = new MyStringListModel();
model->setStringList(QStringList() << "test" << "new");
ui->databases->setModel(model);
ui->databases->setItemDelegate(new MyItemDelegate());
I'm using QTableView and QStandardItemModel to show some data.
For each row, there is a column which has a check Box, this check box is inserted by setItem, the code is as follows:
int rowNum;
QStandardItemModel *tableModel;
QStandardItem* __tmpItem = new QStandardItem();
__tmpItem->setCheckable(true);
__tmpItem->setCheckState(Qt::Unchecked);
tableModel->setItem(rowNum,0,__tmpItem);
Now I want to interact with the check box. If a check box changes its state by user (from checked to unchecked or vice versa), I want to do something on the corresponding data row.
I know I can use signal-slot to catch the change of checkbox, but since there are lots of data row, I don't want to connect each row one by one.
Is there anyway to interact with the check action more effectively? Thanks :)
I don't deal with QTableView+QStandardItemModel, but may be example below will help you:
1). table.h file:
#ifndef TABLE__H
#define TABLE__H
#include <QtGui>
class ItemDelegate : public QItemDelegate
{
public:
ItemDelegate(QObject *parent = 0)
: QItemDelegate(parent)
{
}
virtual void drawCheck(QPainter *painter, const QStyleOptionViewItem &option,
const QRect &, Qt::CheckState state) const
{
const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
QRect checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
check(option, option.rect, Qt::Checked).size(),
QRect(option.rect.x() + textMargin, option.rect.y(),
option.rect.width() - (textMargin * 2), option.rect.height()));
QItemDelegate::drawCheck(painter, option, checkRect, state);
}
virtual bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
const QModelIndex &index)
{
Q_ASSERT(event);
Q_ASSERT(model);
// make sure that the item is checkable
Qt::ItemFlags flags = model->flags(index);
if (!(flags & Qt::ItemIsUserCheckable) || !(flags & Qt::ItemIsEnabled))
return false;
// make sure that we have a check state
QVariant value = index.data(Qt::CheckStateRole);
if (!value.isValid())
return false;
// make sure that we have the right event type
if (event->type() == QEvent::MouseButtonRelease) {
const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
QRect checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
check(option, option.rect, Qt::Checked).size(),
QRect(option.rect.x() + textMargin, option.rect.y(),
option.rect.width() - (2 * textMargin), option.rect.height()));
if (!checkRect.contains(static_cast<QMouseEvent*>(event)->pos()))
return false;
} else if (event->type() == QEvent::KeyPress) {
if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
&& static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
return false;
} else {
return false;
}
Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
? Qt::Unchecked : Qt::Checked);
QMessageBox::information(0,
QString((state == Qt::Checked) ? "Qt::Checked" : "Qt::Unchecked"),
QString("[%1/%2]").arg(index.row()).arg(index.column()));
return model->setData(index, state, Qt::CheckStateRole);
}
virtual void drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const
{
QItemDelegate::drawFocus(painter, option, option.rect);
}
};
static int ROWS = 3;
static int COLS = 1;
class Table : public QTableWidget
{
Q_OBJECT
public:
Table(QWidget *parent = 0)
: QTableWidget(ROWS, COLS, parent)
{
setItemDelegate(new ItemDelegate(this));
QTableWidgetItem *item = 0;
for (int i=0; i<rowCount(); ++i) {
for (int j=0; j<columnCount(); ++j) {
setItem(i, j, item = new QTableWidgetItem);
item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsUserCheckable);
item->setCheckState((i+j) % 2 == 0 ? Qt::Checked : Qt::Unchecked);
}
}
}
};
#endif
2). main.cpp file:
#include <QApplication>
#include "table.h"
int main(int argc, char **argv)
{
QApplication a(argc, argv);
Table w;
w.show();
return a.exec();
}
Good luck.
PS: here is the original text.
handle the click event, there you will get the modelindex, get the data and modify the same
if you are going to insert more than one text or icon, then you need to set the delegate for your listview
Here is another similar idea of the example suggested by mosg using a QStyleItemDelegate instead. http://developer.qt.nokia.com/faq/answer/how_can_i_align_the_checkboxes_in_a_view
Use on clicked slot and index.data(Qt::CheckStateRole) :
void MainWindow::on_tableView_clicked(const QModelIndex &index)
{
if(index.column() == 2){
if(index.data(Qt::CheckStateRole) == Qt::Checked){
//your code
}else if(index.data(Qt::CheckStateRole) == Qt::Unchecked){
//your code
}
}
//get other infos
QVariant value = index.sibling(index.row(),0).data();
QString selectedMessageName = value.toString();
}