Qt Model for non-table custom widget - c++

I created a custom widget composed of QPushButtons placed on a broken layout since I need to place them on specific places with no geometric pattern. I've previously used QAbstractTableModel for QTableView with success and I would like to have the same ability of letting a model be in charge of the front-end updating. The buttons will mostly provide visual feedback changing text and color based on backend data changes. I included a link (end of post) that has something of use for me but I'm unable to see how I would achieve what you get by, for example, reemplementing Model::data() when role is Qt::DisplayRole. How would QModelIndex apply in something like this?
The first thing that comes to mind with a rough pseudocode:
class MyModel : QAbstractItemModel
{
QMap<int, BtnData> m_btnDataMap;
QVariant data(QModelIndex mIdx, int role) {
if (role == DisplayRole) {
return m_btnDataMap[mIdx.row()]; // returns a BtnData item
}
}
void updateButtonData(int btnKey, BtnData d) {
m_btnDataMap[btnKey] = d;
Q_EMIT dataChanged(btnKey);
}
};
class RandomlyPlacedButtonsWidget : public QWidget
{
QMap<int, QPushButton*> m_btnMap;
MyModel m_myModel;
RandomlyPlacedButtonsWidget() {
connect(m_myModel, dataChanged(int btnKey), this, updateButtonVisual(int btnKey));
}
void updateButtonVisual(int btnKey) {
BtnData data = m_myModel->data(index, DisplayRole);
m_btnMap[index]->text = data.text;
m_btnMap[index]->color = data.color;
}
};
Is this a proper way of using the dataChanged() signal on the widget side? Would something like this make sense to do? It would be ignoring columns, would that conflict with it? Would QAbstractListModel be better because of that?
I don't need specific code, I'm more in need of a way to properly approach this while keeping things clean and easy to maintain.
Using Qt Model/View with non-table like data and non-table/list UI?

Related

Qt set a custom widget inside a QTableView

I need to put a custom widget inside a QTableView cell from a subclassed QAbstractTableModel.
I searched for already given solutions but no one catch my needs.
The custom widget must stay here all the time and not only in editing mode like with the QItemDelegate::createEditor.
The custom widget may be everything, i'm searching for a general solutions for all the widget not only QPushButtons or QCheckBox.
Sorry for my english.
You can use QAbstractItemView::setIndexWidget and overriding QAbstractItemView::dataChanged to achieve what you want as follows:
class MyTableView : public QTableView
{
protected:
void dataChanged(const QModelIndex &topLeft, const QModelIndex & bottomRight, const QVector<int> & roles)
{
QPushButton *pPushButton = qobject_cast<QPushButton*>(indexWidget(topLeft));
if (pPushButton)
pPushButton->setText(model()->data(topLeft, Qt::DisplayRole).toString());
else
QTableView::dataChanged(topLeft, bottomRight, roles);
}
};
void main ()
{
MyTableView table;
table.setIndexWidget(table.model()->index(0, 0, QModelIndex()),new QPushButton());
}
Note that it is an incomplete implementation, but it should show you how you can solve your problem. A real implementation should update all QPushButton between topLeft and bottomRight.

Implementing a shift-click modifier to a specific form widget?

If I use Qt Designer for my application's main window -- and the application contains many different widgets inside of it -- how do I further customize those widgets sepcifically? For example, I have a QTableView widget inside of my main application. Upon debugging the application, a ui_myapplication.h file gets created from the Qt Designer .ui form. Suppose I wanted to add some extra things to the widgets defined in that file. How would I if it's created at runtime?
Example:
In myApplication.cpp, I have this block of code:
void myApplication::mousePressEvent(QMouseEvent* event) {
if(event->modifiers() & Qt::ShiftModifier) {
if(event->button() == Qt::LeftButton) {
qDebug() << "shift modifier";
ui->tableView->setSortingEnabled(false);
}
}
}
This is similar to what I want, but not exactly. The idea is to have the QTableView widget named tableView (which is contained within the main application that I created in Qt Designer) to disable the table's sorting functionality when I hold Shift and left-click a column header. (the end goal is to make it so that Shift+clicking will disable column sorting for a short while so I can select all items in the column instead of having it sort the column).
The code above will only work if I Shift+Click the very bottom of the main application (in the space where there are no other widgets). That makes sense. But how do I make it so that doing a Shift+click inside the tableView widget will trigger the qDebug() << "shift modifier"; line?
I'd want something similar to this: (pseudocode):
void myApplication::mousePressEvent(QMouseEvent* event) {
if(target == ui->tableView->horizontalheader() && event->modifiers() & Qt::ShiftModifier) {
if(event->button() == Qt::LeftButton) {
qDebug() << "shift modifier";
ui->tableView->setSortingEnabled(false);
}
}
}
How can I do this?
EDIT: research had led me to believe I can do this with an event filter that could target a specific widget. Am I on the right track?
EDIT 2: Thanks to goug's answer below, I was able to accomplish what I needed by subclassing QTableView and then promoting my existing form's QTableView to the new class. See below:
mytableview.h
#ifndef MYTABLEVIEW_H
#define MYTABLEVIEW_H
#include "mytableview.h"
#include <QTableView>
class MyTableView : public QTableView
{
Q_OBJECT
public:
explicit MyTableView(QWidget * parent = 0);
~MyTableView();
protected:
void mousePressEvent(QMouseEvent *event);
};
#endif // MYTABLEVIEW_H
mytableview.cpp
#include "mytableview.h"
#include <QDebug>
MyTableView::MyTableView(QWidget* parent)
{
}
MyTableView::~MyTableView()
{
}
void MyTableView::mousePressEvent(QMouseEvent* event) {
if(event->modifiers() & Qt::ShiftModifier) {
if(event->button() == Qt::LeftButton) {
qDebug() << "shift modifier";
setSortingEnabled(false);
}
}
}
There's a couple of different approaches you could use here. You could subclass QTableView, and then in Qt Designer, you place a QTableView as normal, but then promote it to your derived class. The generated code creates an instance of your class rather than QTableView. In Qt Designer, right-click on the table view and select from the Promote options. You'll have to enter your class details the first time. I'd be inclined to go this route especially if there's other custom behavior you want to implement on the table view.
Another option is to create a new class and install it as an event filter on your QTableView. Your new class then gets the events before they go to the QTableView and you can act accordingly on them. Look up installEventFilter in Qt Assistant, and that'll get you to the details of how to do this.

QT - QTreeView with different colors for subgroups of the QTreeView items

Does anybody know how to implement/realize a QTreeView with different colors for subgroups of the QTreeView items?
Something like:
Does anybody has done something like that and could give me a link to an tutorial or how to, or sample code would also be good. Currently I have absolutely no idea how to build this.
I'm working with Qt 5.1.1 and using the QTreeView with the QFileSystemModel and the QItemSelectionModel.
I also thought of :
m_TreeView->setStyleSheet(...)
but this only sets the style for the whole treeView or only for the selected ones.
Any suggestions? Thanks a lot for your help!
There's Qt::BackgroundRole which can be used to return a QColor to be used by the view to paint an index' background.
As you use an existing item model class (QFileSystemModel), it would be easiest to put a proxy model on top of the file system model just doing the colorization.
Using QIdentityProxyModel:
class ColorizeProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit ColorizeProxyModel(QObject *parent = 0) : QIdentityProxyModel(parent) {}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const {
if (role != Qt::BackgroundRole)
return QIdentityProxyModel::data(index, role);
... find out color for index
return color;
}
};
To use it:
QFileSystemModel *fsModel = new QFileSystemModel(this);
ColorizeProxyModel *colorProxy = new ColorizeProxyModel(this);
colorProxy->setSourceModel(fsModel);
treeView->setModel(colorProxy);
If you need anything more fancy, (like special shapes etc.), you'd need your own item delegate with custom painting (see QStyledItemDelegate).

Changing QAbstractTableModel headerData using the role

I have a subclass
class TableModel : public QAbstractTableModel
I override the headerData method as follow:
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {...}
if (role == TableModel::CurrencyRole && orientation == Qt::Horizontal) {...}
return QVariant();
}
I have a method that set a QTableView as follow using TableModel* table
void A::SetDisplay(QTableView* table_view, QString filter, int role, int sort_role)
{
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(table);
proxyModel->setDynamicSortFilter(true);
proxyModel->setSortRole(sort_role);
table_view->setModel(proxyModel);
table_view->setSortingEnabled(true);
table_view->setSelectionBehavior(QAbstractItemView::SelectRows);
table_view->horizontalHeader()->setStretchLastSection(true);
table_view->verticalHeader()->hide();
table_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
table_view->setSelectionMode(QAbstractItemView::SingleSelection);
proxyModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive));
proxyModel->setFilterKeyColumn(1);
proxyModel->sort(0, Qt::AscendingOrder);
connect( table_view->selectionModel(),
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SIGNAL(selectionChanged(QItemSelection)));
}
I have two QTableView objects ViewA and viewB. I need ViewA to have a header with role == Qt::DisplayRole and viewB to have a header with role == TableModel::CurrencyRole. How can I get the headerData to change for each view using the role.
Thanks, and please let me know if I left any detail out or or something is unclear in my question.
First of all, it looks like to do exactly what you're trying to to is going to be a bit tricky.
After a quick perusal of the Qt source code it looks like there's no way to change what role is passed to your model's headerData() function just using the API.
You do, however, have the ability to subclass QHeaderView and override the virtual paintSection() function and then do whatever you want. You'll probably need to look over Qt's implementation of this function for reference of how to implement it properly.
At this point you can set the header view on your views to your new custom one, and then set some internal flag from your view that tells it how to properly call headerData() with the role you want.

Is there a way to display icons in QListView without text?

Using a QListView, and QStandardItemModel, is it possible to display icons in the list view without displaying the associated text? QStandardItem is defined as so:
QStandardItem ( const QIcon & icon, const QString & text )
So it seems to require a text string of some sort - I only want the icon displayed. If I use the following code, I get the icons as requested, but I also get a blank text element underneath them. I don't want this.
ImageListView->setViewMode( QListView::IconMode );
{
QStandardItemModel *iStandardModel = new QStandardItemModel(this);
QStandardItem* item1 = new QStandardItem(QIcon("images/shield-280x280.png"),"");
QStandardItem* item2 = new QStandardItem(QIcon("images/shield-280x280.png"),"");
iStandardModel->appendRow(item1);
iStandardModel->appendRow(item2);
ImageListView->setIconSize(QSize(100,100));
ImageListView->setUniformItemSizes(true);
ImageListView->setDragDropMode(QAbstractItemView::DropOnly);
ImageListView->setModel(iStandardModel);
}
If I go to the trouble of building a custom model, can I resolve this issue?
To expand on the accepted answer, here's the simplest delegate which can optionally hide the text (display role) of items, but otherwise acts like the default delegate. This works with any QAbstractItemView subclass (and QComboBox) and any QAbstractItemModel subclass as well. And is a better solution if one would rather keep the display role non-null for other views (or whatever reason).
class ItemDelegate : public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
// simple public member to toggle the display role (create getter/setter if you prefer)
bool displayRoleEnabled = false;
protected:
void initStyleOption(QStyleOptionViewItem *o, const QModelIndex &idx) const override
{
QStyledItemDelegate::initStyleOption(o, idx);
// to hide the display role all we need to do is remove the HasDisplay feature
if (!displayRoleEnabled)
o->features &= ~QStyleOptionViewItem::HasDisplay;
}
};
Yes, you can do.
first you create a delegate associated with the list-view.Then,
While inserting the elements to the listview, use set-data function to insert the icon and in the paint event of delegate you handle the drawing icon. i hope its clear.