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());
Related
I have a tableView that has a column that is using a comboBox. I need to fill the comboBox using the delegate class with the data from the model class. I was using signals and slots for this task but I know there is a method using data.
This is how i create and fill the comboBox. I need to get the file row directly from the model class without already storing it in the delegate.
QWidget *CDelegate :: createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const
{
if(index.column() == COL_ComboBox)
{
QComboBox *editor = new QComboBox(parent);
for(int i=0; i<file.at(index.row()).size(); i++)
editor -> addItem(file.at(index.row()).at(i))
return editor;
}
...
}
As I understand, you want to fill QComboBox with data from model of your QTableView. As you see, const QModelIndex & index parameter in createEditor function provides you access to this model. Look for method model of QModelIndex class. Thats' why, your createEditor function may be like this:
QWidget *CDelegate :: createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const
{
if(index.column() == COL_ComboBox)
{
QComboBox *editor = new QComboBox(parent);
const QAbstractItemModel *model = index.model();
while(/*condition*/)
{
// take data from model
// QVariant dt = model->data(...);
// fill editor with data from dt
// editor->addItem(...)
}
return editor;
}
...
}
Due to lack of any other Qt demo, I am making use of Qt widgets's SimpleTreeModeldemo to implement C++ model for my QML TreeView. I have defined roles so QML can use it but I am having trouble connecting them with actual model data.
What I also find interesting is that widgets (C++) demo works fine but TreeModeldoesn't seem to store the data as its member variable..leaves me scratching my head. I figured this out, every TreeItem store all its child items and TreeModel has only one rootItem which in turn stores all data as its child.
The TreeItem class
class TreeItem
{
public:
explicit TreeItem(const QList<QVariant> &data, TreeItem *parentItem = 0);
~TreeItem();
void appendChild(TreeItem *child);
TreeItem *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
int row() const;
TreeItem *parentItem();
private:
QList<TreeItem*> m_childItems;
QList<QVariant> m_itemData;
TreeItem *m_parentItem;
};
The TreeModel class
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum DisplayRoles {
TitleRole = Qt::UserRole + 1,
SummaryRole
};
explicit TreeModel(const QString &data, QObject *parent = 0);
~TreeModel();
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QHash<int, QByteArray> TreeModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[TitleRole] = "title";
roles[SummaryRole] = "summary";
return roles;
}
private:
void setupModelData(const QStringList &lines, TreeItem *parent);
TreeItem *rootItem;
};
The model loads the data from a default.txt
TreeModel::TreeModel(const QString &data, QObject *parent)
: QAbstractItemModel(parent)
{
QList<QVariant> rootData;
rootData << "Title" << "Summary";
rootItem = new TreeItem(rootData);
setupModelData(data.split(QString("\n")), rootItem);
}
void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
{
QList<TreeItem*> parents;
QList<int> indentations;
parents << parent;
indentations << 0;
int number = 0;
while (number < lines.count()) {
int position = 0;
while (position < lines[number].length()) {
if (lines[number].mid(position, 1) != " ")
break;
position++;
}
QString lineData = lines[number].mid(position).trimmed();
if (!lineData.isEmpty()) {
// Read the column data from the rest of the line.
QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
QList<QVariant> columnData;
for (int column = 0; column < columnStrings.count(); ++column)
columnData << columnStrings[column];
if (position > indentations.last()) {
// The last child of the current parent is now the new parent
// unless the current parent has no children.
if (parents.last()->childCount() > 0) {
parents << parents.last()->child(parents.last()->childCount()-1);
indentations << position;
}
} else {
while (position < indentations.last() && parents.count() > 0) {
parents.pop_back();
indentations.pop_back();
}
}
// Append a new item to the current parent's list of children.
parents.last()->appendChild(new TreeItem(columnData, parents.last()));
}
++number;
}
}
My problem is in this function, how do I connect the roles with the data which is stored in rootItem?. Note titleString and summaryStringare possible proposed functions (if needed) but I don't know what to write in them get access the data!
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.isValid() && role >= TitleRole) {
switch (role) {
case TitleRole:
return QVariant(titleString(rootItem(index))); // get title through rootItem?
case SummaryRole:
return QVariant(summaryString(rootItem(index))); // get summary through rootItem?
}
}
if (role != Qt::DisplayRole)
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->data(index.column());
}
The default.txthas the following data but the same is available in the Qt Creator demo itself as well.
Getting Started How to familiarize yourself with Qt Designer
Launching Designer Running the Qt Designer application
The User Interface How to interact with Qt Designer
Designing a Component Creating a GUI for your application
Creating a Dialog How to create a dialog
Composing the Dialog Putting widgets into the dialog example
Creating a Layout Arranging widgets on a form
Signal and Slot Connections Making widget communicate with each other
Using a Component in Your Application Generating code from forms
The Direct Approach Using a form without any adjustments
The Single Inheritance Approach Subclassing a form's base class
The Multiple Inheritance Approach Subclassing the form itself
Automatic Connections Connecting widgets using a naming scheme
A Dialog Without Auto-Connect How to connect widgets without a naming scheme
A Dialog With Auto-Connect Using automatic connections
Form Editing Mode How to edit a form in Qt Designer
Managing Forms Loading and saving forms
Editing a Form Basic editing techniques
The Property Editor Changing widget properties
The Object Inspector Examining the hierarchy of objects on a form
Layouts Objects that arrange widgets on a form
Applying and Breaking Layouts Managing widgets in layouts
Horizontal and Vertical Layouts Standard row and column layouts
The Grid Layout Arranging widgets in a matrix
Previewing Forms Checking that the design works
Using Containers How to group widgets together
General Features Common container features
Frames QFrame
Group Boxes QGroupBox
Stacked Widgets QStackedWidget
Tab Widgets QTabWidget
Toolbox Widgets QToolBox
Connection Editing Mode Connecting widgets together with signals and slots
Connecting Objects Making connections in Qt Designer
Editing Connections Changing existing connections
My output shows the same number of lines as in widgets demo except that there is no text. It seems like it is just not connected to the roles properly or roles are not connected to day. I am attaching the screen shot of my output.
Just so if anyone is trying to do the same thing, I figured it out and here is the answer. The data method of the model should read the following.
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (index.isValid() && role >= TitleRole) {
switch (role) {
case TitleRole:
return item->data(0);
case SummaryRole:
return item->data(1);
}
}
if (role != Qt::DisplayRole)
return QVariant();
return item->data(index.column());
}
TreeItem could be changed in real application to store specific data in which case the above method will point to that data instead of column number approach.
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 derived a custem model from QAbstractItemModel and implemented drag&drop support by
overloading a flags() method that returns Qt::ItemIsDragEnabled where applicable,
overloading supportedDropActions and
implementing mimeData() to properly return a QMimeData object with some string set for the text/plain format.
When I setup this model in a QTreeView and enable dragging via setDragEnabled(true), I can pick items from the tree view and drag them. For example, I can drag an item and drop it onto a QLineEdit, which in turn will display the string that the model returned via mimeData().
However, when I drag an item and drop it onto another application's widgets (such as notepad or simply a second instance of my application) an empty string gets dropped.
I found another clue using the Drop site example shipped with Qt: When I drag an item over it, it correctly shows that there is text/plain available and it even displays the string associated with it. Yet, when I drop the item I get an empty string again in the dropEvent()...
Sourcecode
ssce.pro:
QT += core gui widgets
TARGET = ssce
TEMPLATE = app
SOURCES += main.cpp
main.cpp:
#include <QtCore>
#include <QtWidgets>
struct Model: public QAbstractItemModel {
int rowCount(const QModelIndex &parent) const {
return parent.isValid() ? 0 : 1;
}
int columnCount(const QModelIndex &parent) const {
return 1;
}
QModelIndex index(int row, int column, const QModelIndex &parent) const {
return parent.isValid() ? QModelIndex() : createIndex(row, column, static_cast<void *>(0));
}
QModelIndex parent(const QModelIndex &child) const {
return QModelIndex();
}
Qt::ItemFlags flags(const QModelIndex &index) const {
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
}
QVariant data(const QModelIndex &index, int role) const {
return (role == Qt::DisplayRole) ? "Test item" : QVariant();
}
QStringList mimeTypes() const {
return QStringList("text/plain");
}
QMimeData *mimeData(const QModelIndexList &indexes) const {
QMimeData *d = new QMimeData();
d->setText("hello world");
return d;
}
Qt::DropActions supportedDropActions() const {
return Qt::CopyAction;
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QDialog d;
QVBoxLayout l;
QLineEdit e;
l.addWidget(&e);
QTreeView v;
Model m;
v.setModel(&m);
v.setDragEnabled(true);
l.addWidget(&v);
d.setLayout(&l);
d.show();
return a.exec();
}
Description:
Run this and you can drag the item from the tree view into the line edit, it will display hello world then. Drop the item anywhere else, including the line edit of a second instance of the sample application, and nothing will happen.
I followed the Spin Box Delegate tutorial, which Qt provides, to try to implement my own QItemDelegate. It would be used to specify a QComboBox to represent data in a QTableView cell but it is not working.
My biggest problem is that I don't know when my QItemDelegate is going to be utilized.
when itemModel->setData() is used or when itemModel->setItem(). I would suspect setItem() because I reimplemented a QItemDelegate (emphasis on the "Item") but the tutorial uses setData() and it works fine.
I know that if the specified QItemDelegate does not work it uses the default one but how do I now that the one I specified did not work?
when should I suspect for QTableView to use my delegate. I would like to specify which delegates to use for each cell. Is this possible or does the QTableView only use one delegate throughout?
How would I specify the items to populate the QComboBox once it gets displayed by the QTableView?
I implemented QItemDelegate here:
the part where I try to add the cell which is suppose to use the QComboBox is under the comment "Enabled" in mainwindow.cpp further down this post.
qcomboboxitemdelegate.h
#ifndef QCOMBOBOXITEMDELEGATE_H
#define QCOMBOBOXITEMDELEGATE_H
#include <QItemDelegate>
#include <QComboBox>
class QComboBoxItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit QComboBoxItemDelegate(QObject *parent = 0);
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index);
void setEditorData(QWidget *editor, const QModelIndex &index);
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index);
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index);
signals:
private:
};
#endif // QCOMBOBOXITEMDELEGATE_H
qcomboboxitemdelegate.cpp
#include "qcomboboxitemdelegate.h"
#include <QDebug>
QComboBoxItemDelegate::QComboBoxItemDelegate(QObject *parent)
: QItemDelegate(parent)
{
}
QWidget* QComboBoxItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) {
// create widget for use
QComboBox* comboBox = new QComboBox(parent);
return comboBox;
}
void QComboBoxItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) {
// update model widget
QString value = index.model()->data(index, Qt::EditRole).toString();
qDebug() << "Value:" << value;
QComboBox* comboBox = static_cast<QComboBox*>(editor);
comboBox->setCurrentIndex(comboBox->findText(value));
}
void QComboBoxItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) {
// store edited model data to model
QComboBox* comboBox = static_cast<QComboBox*>(editor);
QString value = comboBox->currentText();
model->setData(index, value, Qt::EditRole);
}
void QComboBoxItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) {
editor->setGeometry(option.rect);
}
mainwindow.cpp : this is where I initialize the QStandardItemModel
void MainWindow::init() {
itemModel = new QStandardItemModel(this);
}
void MainWindow::setupUi() {
this->setWindowTitle("QAlarmClock");
QStringList labelList;
labelList << "Alarm Name" << "Time" << "Enabled";
itemModel->setHorizontalHeaderLabels(labelList);
ui->tableView->setModel(itemModel);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->tableView->setItemDelegate(comboBoxItemDelegate);
}
void MainWindow::on_actionNew_triggered() {
alarmDialog = new AlarmDialog(this);
connect(alarmDialog, SIGNAL(on_close()), this, SLOT(on_alarmDialog_close()));
alarmDialog->exec();
}
mainwindow.cpp : this is where I update QStandardItemModel
void MainWindow::on_alarmDialog_close() {
QString alarmName = alarmDialog->getAlarmName();
QDateTime alarmDateTime = alarmDialog->getDateTime();
itemModel->insertRow(itemModel->rowCount());
int rowCount = itemModel->rowCount();
// Alarm Name
QStandardItem* alarmItem = new QStandardItem(QIcon("res/alarmclock.ico"), alarmName);
itemModel->setItem(rowCount - 1 , 0, alarmItem);
// Date Time
QStandardItem* dateTimeItem = new QStandardItem();
dateTimeItem->setText(alarmDateTime.toString());
dateTimeItem->setEditable(false);
itemModel->setItem(rowCount - 1, 1, dateTimeItem);
// Enabled
QStandardItem* enabledItem = new QStandardItem();
QList<QStandardItem*> optionList;
optionList << new QStandardItem("Enabled") << new QStandardItem("Disabled");
enabledItem->appendRows(optionList);
itemModel->setItem(rowCount - 1, 2, enabledItem);
}
Edit 1
qcomboboxdelegate.cpp
QWidget* QComboBoxItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) {
// create widget for use
qDebug() << "Column: " << index.column();
if (index.column() == 2) {
QComboBox* comboBox = new QComboBox(parent);
QStringList values;
values << "Enabled" << "Disabled";
comboBox->addItems(values);
return comboBox;
} else {
return QItemDelegate::createEditor(parent, option, index);
}
}
mainwindow.cpp
void MainWindow::on_alarmDialog_close() {
QList<QStandardItem*> row;
QString alarmName = alarmDialog->getAlarmName();
QDateTime alarmDateTime = alarmDialog->getDateTime();
QString status = "Enabled";
// Alarm Name
QStandardItem* alarmItem = new QStandardItem(QIcon("res/alarmclock.ico"), alarmName);
row << alarmItem;
// Date Time
QStandardItem* dateTimeItem = new QStandardItem();
dateTimeItem->setText(alarmDateTime.toString());
dateTimeItem->setEditable(false);
row << dateTimeItem;
// Enabled
QStandardItem* statusItem = new QStandardItem(status);
row << statusItem;
itemModel->appendRow(row);
}
First, you should have a description of your model columns:
enum Columns
{
COL_NAME,
COL_TIME,
COL_STATUS
}
Your delegate should only work for the last column.
Here is an example of how you can populate your model:
for (int i = 0; i < 5; ++i)
{
QStandardItem *itemName = new QStandardItem(QString("name %1").arg(i));
QStandardItem *itemTime = new QStandardItem(QString("time %1").arg(i));
QString status;
if (i % 2 == 0)
{
status = "Enabled";
}
else
{
status = "Disabled";
}
QStandardItem *itemStatus = new QStandardItem(status);
QList<QStandardItem*> row;
row << itemName << itemTime << itemStatus;
model->appendRow(row);
}
As I said, your delegate should only work for the last column. So all methods you have reimplemented should have a column check like this:
QWidget* QComboBoxItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index)
{
if (index.column() == COL_STATUS)
{
QStringList values;
values << "Enabled" << "Disabled";
QComboBox* comboBox = new QComboBox(parent);
comboBox->addItems(values);
return comboBox;
}
else
{
return QItemDelegate::createEditor(parent, option, index);
}
}
You should add this check to the other methods: if the current column is not the status column, the base class (QItemDelegate) implementation should be used.
Then you set your delegate to your view:
ui->tableView->setItemDelegate(new ComboBoxDelegate);
If you do everything right, a combo Box will appear in the last column if you try to edit its values.
Even more simply; I found QTableView::setItemDelegateForColumn() to work admirably for a column. For example, in your MainWindow, you could make a member:
QComboBoxItemDelegate dgtComboDelegate;
Then, in your ctor, or init(), you could have
ui->tableView->setItemDelegateForColumn(2, dgtComboDelegate);
If you wanted that to happen for a single cell, that's when you need to test on the index.column() and index.row().
You know, you don't have to create a QTableView to do this either. E.g., see the ?:
Qt - Centering a checkbox in a QTable
The OP doesn't give the declaration for a table widget or view; but it does have the QTableView tag. It should work equally well for either.
In the former case, you can do ui->tableWidget->setItemDelegateForColumn(2, dgtComboDelegate); and never have to make your own model. Just use setData() on the items you create (or even later, for that matter,) to initialize their values.
So I figured out that I did not override the correct function prototypes..! I forgot that they
had const in the prototype meaning that I was not overriding any functions so it was using the default ones. Here are the correct virtual functions that have to be re-implemented: http://qt-project.org/doc/qt-5.0/qtwidgets/qitemdelegate.html