I have the following problem, I have a QTableWidget to which I have added some QDoubleSpinBox to some cells, all good so far my question is how I can do to select all the content of the spinbox when changing the focus, I mean when pressing the key tab and when they receive the focus, I will appreciate any suggestions.
I leave my code:
void MainClass::setData(double val1, double val2,int _rowCount)
{
for (int i=0;i<= _rowCount; i++) {
int rowCount=ui->tableWidget_4->rowCount();
// ui->tableWidget_4->setRowCount(rowCount);
//primera columna
ui->tableWidget_4->insertRow(rowCount);
// qDebug()<<rowCount;
QTableWidgetItem *item=new QTableWidgetItem(QString("v %1").arg(v));
ui->tableWidget_4->setItem(rowCount,V,item);
//segunda columna columna
// ui->tableWidget_4->insertRow(rowCount);
QTableWidgetItem *item1=new QTableWidgetItem(0);
ui->tableWidget_4->setItem(rowCount,Velocidad,item1);
ui->tableWidget_4->setCellWidget(rowCount,Velocidad,new QDoubleSpinBox);
QDoubleSpinBox *sb=qobject_cast<QDoubleSpinBox *>(
ui->tableWidget_4->cellWidget(rowCount,Velocidad));
sb->setValue(val1);
// qDebug()<<item1->data(Qt::DisplayRole).toString();
//tercera columna columna
// ui->tableWidget_4->insertRow(rowCount);
QTableWidgetItem *item2=new QTableWidgetItem(QString("h %1").arg(h));
ui->tableWidget_4->setItem(rowCount,H,item2);
//cuarta columna columna
// ui->tableWidget_4->insertRow(rowCount);
QTableWidgetItem *item3=new QTableWidgetItem(val2);
ui->tableWidget_4->setItem(rowCount,Profundidad,item3);
ui->tableWidget_4->setCellWidget(rowCount,Profundidad,new QDoubleSpinBox);
QDoubleSpinBox *sb2=qobject_cast<QDoubleSpinBox *>(
ui->tableWidget_4->cellWidget(rowCount,Profundidad));
sb2->setValue(val2);
v++;
h++;
}
here the form
Note that when the spinboxes are in focus, only the cursor blinks but not all the content is selected.
note:
in the assignment of the column number in setItem I use an enumeration instead of the column numbers.
enum ColNames{V,Velocidad,H,Profundidad};
One possible solution is to override the focusInEvent method of the QDoubleSpinBox that is invoked when it receives the focus, and then select all the text.
class DoubleSpinBox: public QDoubleSpinBox{
public:
using QDoubleSpinBox::QDoubleSpinBox;
protected:
void focusInEvent(QFocusEvent *event){
QDoubleSpinBox::focusInEvent(event);
selectAll();
}
};
And then set it using the setCellWidget method.
You can also avoid using the setCellWidget method and use the delegate:
#include <QtWidgets>
#include <random>
class Helper: public QObject{
public:
Helper(QDoubleSpinBox *ds): QObject(ds), m_ds(ds){
m_ds->installEventFilter(this);
}
bool eventFilter(QObject *watched, QEvent *event){
if(watched == m_ds && event->type() == QEvent::FocusIn){
m_ds->selectAll();
}
return QObject::eventFilter(watched, event);
}
private:
QDoubleSpinBox* m_ds;
};
class Delegate: public QStyledItemDelegate{
public:
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
QWidget *editor = QStyledItemDelegate::createEditor(parent, option, index);
if(QDoubleSpinBox *ds = qobject_cast<QDoubleSpinBox *>(editor)){
new Helper(ds);
}
return editor;
}
};
class MainWindow: public QMainWindow{
public:
MainWindow(QWidget *parent=nullptr):
QMainWindow(parent), table(new QTableWidget)
{
table->setColumnCount(4);
table->setItemDelegate(new Delegate);
setCentralWidget(table);
}
void append(double value1, double value2){
int number_of_rows = table->rowCount();
QTableWidgetItem *item1 = new QTableWidgetItem;
item1->setText(QString("v %1").arg(number_of_rows));
QTableWidgetItem *item2 = new QTableWidgetItem;
item2->setData(Qt::DisplayRole, value1);
QTableWidgetItem *item3 = new QTableWidgetItem;
item3->setText(QString("h %1").arg(number_of_rows));
QTableWidgetItem *item4 = new QTableWidgetItem;
item4->setData(Qt::DisplayRole, value2);
table->insertRow(number_of_rows);
table->setItem(number_of_rows, 0, item1);
table->setItem(number_of_rows, 1, item2);
table->setItem(number_of_rows, 2, item3);
table->setItem(number_of_rows, 3, item4);
table->openPersistentEditor(item2);
table->openPersistentEditor(item4);
}
private:
QTableWidget *table;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyle("fusion");
MainWindow w;
std::uniform_real_distribution<> dist(1.0, 2.0);
for(int i=0; i< 4; i++){
double val1 = dist(*QRandomGenerator::global());
double val2 = dist(*QRandomGenerator::global());
w.append(val1, val2);
}
w.resize(640, 480);
w.show();
return a.exec();
}
Related
I want to show custom widget in each QListView cells (3 labels width different fonts and 2 tool buttons). The widget must handle mouse events for correct handling of the hover events and button clicks. (Therefore I cannot just draw it in QStyledItemDelegate::paint()).
Here is what I want each row in a list view looks like:
The main idea: QAbstractItemView::openPersistentEditor().
#include <QApplication>
#include <QWidget>
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QVBoxLayout>
#include <QDateTime>
#include <QListView>
#include <QStringListModel>
#include <QStyledItemDelegate>
class Form : public QWidget
{
//Q_OBJECT
public:
explicit Form(QWidget *parent = nullptr)
:QWidget(parent)
{
verticalLayout = new QVBoxLayout(this);
horizontalLayout = new QHBoxLayout();
labelTitle = new QLabel(this);
labelTitle->setFont(QFont("Calibri", 12, QFont::Bold));
horizontalLayout->addWidget(labelTitle);
toolButtonEdit = new QToolButton(this);
toolButtonEdit->setText("E");
horizontalLayout->addWidget(toolButtonEdit);
toolButtonRemove = new QToolButton(this);
toolButtonRemove->setText("R");
horizontalLayout->addWidget(toolButtonRemove);
verticalLayout->addLayout(horizontalLayout);
labelDate = new QLabel(this);
labelDate->setFont(QFont("Calibri", 8));
verticalLayout->addWidget(labelDate);
labelText = new QLabel(this);
labelText->setFont(QFont("Calibri", 10));
verticalLayout->addWidget(labelText);
verticalLayout->setStretch(2, 1);
setMinimumSize(QSize(300, 50));
}
public:
QVBoxLayout *verticalLayout;
QHBoxLayout *horizontalLayout;
QLabel *labelTitle;
QToolButton *toolButtonEdit;
QToolButton *toolButtonRemove;
QLabel *labelDate;
QLabel *labelText;
};
class MyDelegate : public QStyledItemDelegate
{
public:
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
auto editor = new Form(parent);
return editor;
}
void setEditorData(QWidget *ed, const QModelIndex &index) const override
{
QVariant var = index.model()->data(index, Qt::DisplayRole);
if (Form *editor = dynamic_cast<Form*>(ed))
{
editor->labelTitle->setText("SYMBOL");
editor->labelDate->setText("date-time");
editor->labelText->setText(var.toString());
}
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem& option, const QModelIndex &)const override
{
editor->setGeometry(option.rect);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Form form(nullptr);
form.labelTitle->setText("TITLE");
form.labelDate->setText(QDateTime::currentDateTime().toString());
form.labelText->setText("text body");
form.show();
auto model = new QStringListModel;
model->setStringList(QStringList()
<< "text body 1"
<< "text body 2"
<< "text body 3");
auto view = new QListView(nullptr);
view->setModel(model);
view->setItemDelegate(new MyDelegate);
int rowCount = model->rowCount();
for (int row = 0; row < rowCount; ++row)
{
QModelIndex index = model->index(row, 0);
view->openPersistentEditor(index);
}
view->show();
return a.exec();
}
Here is how the list view actually looks:
What how can one set such a custom widget to show view cells?
Note that while you are defining your own delegate MyDelegate you never actually use it (i.e. by calling QAbstractItemView::setItemDelegate(). Therefore you see the default delegate (a simple QLineEdit for data of type QString) when calling openPersistentEditor().
NOTE: it turned out that the problem was not due to the implementation of QStyledItemDelegate, but it was the fact that in the constructor of MyTreeWidget I was calling setUniformRowHeights(true). The code below and the solution posted by #scopchanov are valid and working
QTreeWidget has a protected method called itemFromIndex() and this is how I am making it accessible:
class MyTreeWidget : public QTreeWidget {
Q_OBJECT
public:
MyTreeWidget(QWidget *parent) : QTreeWidget(parent) {
setItemDelegate(new MyItemDelegate(this));
}
QTreeWidgetItem treeWidgetItemFromIndex(const QModelIndex& index) {
return itemFromIndex(index);
}
}
In my QStyledItemDelegate, I am storing a pointer to MyTreeWidget and then overriding its virtual sizeHint() method and based on the type of the QTreeWidgetItem adding a padding.
class MyItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MyItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {
_myTreeWidget = dynamic_cast<MyTreeWidget*>(parent);
}
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
auto treeWidgetItem = _myTreeWidget->treeWidgetItemFromIndex(index);
QSize padding;
if (dynamic_cast<MyCustomTreeWidgetItem1*>(treeWidgetItem) {
padding = {0, 5};
} else if (dynamic_cast<MyCustomTreeWidgetItem2*>(treeWidgetItem) {
padding = {0, 10};
}
return QStyledItemDelegate::sizeHint(option, index) + padding;
}
}
This doesn't work, since sizeHint() of the delegate doesn't get called for every single QTreeWidgetItem.
So my text options to call setSizeHint() in the constructor of MyCustomTreeWidgetItem1, and that didn't seem to have any effect either. Is Qt ignoring it because there is a delegate?
Another option was to set a minimum height of a QWidget that is contained in MyCustomTreeWidgetItem which is made possible via the QTreeWidget::setItemWidget().
So it looks like the moment I use the delegate, I am confined to only size. Is my option to get rid of the delegate or there's something else I can try?
I know many people would say switch from a QTreeWidget to a QTreeView, but it's not possible at the moment.
Solution
I would approach this problem in a different (simpler) manner:
Define an enumeration for the different item sizes, e.g.:
enum ItemType : int {
IT_ItemWithRegularPadding,
IT_ItemWithBigPadding
};
When creating an item, set the desired size in its user data, depending on its type, e.g.:
switch (type) {
case IT_ItemWithRegularPadding:
item->setData(0, Qt::UserRole, QSize(0, 5));
break;
case IT_ItemWithBigPadding:
item->setData(0, Qt::UserRole, QSize(0, 10));
break;
}
In the reimplementation of sizeHint retrieve the desired size from the index's data, e.g.:
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override {
return QStyledItemDelegate::sizeHint(option, index)
+ index.data(Qt::UserRole).toSize();
}
Example
Here is an example I wrote for you to demonstrate how the proposed solution could be implemented:
#include <QApplication>
#include <QStyledItemDelegate>
#include <QTreeWidget>
#include <QBoxLayout>
class Delegate : public QStyledItemDelegate
{
public:
explicit Delegate(QObject *parent = nullptr) :
QStyledItemDelegate(parent){
}
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override {
return QStyledItemDelegate::sizeHint(option, index)
+ index.data(Qt::UserRole).toSize();
}
};
class MainWindow : public QWidget
{
public:
enum ItemType : int {
IT_ItemWithRegularPadding,
IT_ItemWithBigPadding
};
MainWindow(QWidget *parent = nullptr) :
QWidget(parent) {
auto *l = new QVBoxLayout(this);
auto *treeWidget = new QTreeWidget(this);
QList<QTreeWidgetItem *> items;
for (int i = 0; i < 10; ++i)
items.append(createItem(QString("item: %1").arg(i),
0.5*i == i/2 ? IT_ItemWithRegularPadding
: IT_ItemWithBigPadding));
treeWidget->setColumnCount(1);
treeWidget->setItemDelegate(new Delegate(this));
treeWidget->insertTopLevelItems(0, items);
l->addWidget(treeWidget);
resize(300, 400);
setWindowTitle(tr("Different Sizes"));
}
private:
QTreeWidgetItem *createItem(const QString &text, int type) {
auto *item = new QTreeWidgetItem(QStringList(text));
switch (type) {
case IT_ItemWithRegularPadding:
item->setData(0, Qt::UserRole, QSize(0, 5));
break;
case IT_ItemWithBigPadding:
item->setData(0, Qt::UserRole, QSize(0, 10));
break;
}
return item;
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Note: This example sets the item's size depending on its index - odd or even. Feel free to change this by implementing the logic you need to differentiate between the items.
Result
The given example produces the following result:
The even and odd items are of a different height.
The 'Country' QListwidget is going to be populated with country names items and if the user drags that an item and drops it in capital QListWidget, it should show me the name of its capital , i.e that item text should change in capital QListWidget.
for example - an item named "Russia" in country listwidget , after dragging and dropping , the item should be renamed to "Moscow".
so far to enable drag and drop, i have just written this code,
ui->country_listwidget->setDragEnabled(true);
ui->capital_listwidget->setAcceptDrops(true);
is this possible in QT ?
A simple solution is to create 2 roles that store each information, and then use a delegate to display the text depending on the view:
#include <QtWidgets>
enum CustomRoles {
CounrtyRole = Qt::UserRole,
CapitalRole
};
class DisplayDelegate: public QStyledItemDelegate{
public:
DisplayDelegate(int displayRole=Qt::DisplayRole, QObject *parent=nullptr)
:QStyledItemDelegate(parent), m_displayRole(displayRole){}
int getDisplayRole() const{
return m_displayRole;
}
void setDisplayRole(int value){
m_displayRole = value;
}
protected:
void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const{
QStyledItemDelegate::initStyleOption(option, index);
QVariant value = index.data(m_displayRole);
if (value.isValid() && !value.isNull()) {
option->features |= QStyleOptionViewItem::HasDisplay;
option->text = displayText(value, option->locale);
}
}
private:
int m_displayRole;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListWidget *country_lw = new QListWidget;
country_lw->setItemDelegate(new DisplayDelegate(CounrtyRole));
QListWidget *capital_lw = new QListWidget;
capital_lw->setItemDelegate(new DisplayDelegate(CapitalRole));
country_lw->setDragEnabled(true);
capital_lw->setAcceptDrops(true);
QListWidgetItem *item1 = new QListWidgetItem;
item1->setData(CounrtyRole, "Country1");
item1->setData(CapitalRole, "Capital1");
country_lw->addItem(item1);
QListWidgetItem *item2 = new QListWidgetItem;
item2->setData(CounrtyRole, "Country2");
item2->setData(CapitalRole, "Capital2");
country_lw->addItem(item2);
QListWidgetItem *item3 = new QListWidgetItem;
item3->setData(CounrtyRole, "Country3");
item3->setData(CapitalRole, "Capital3");
country_lw->addItem(item3);
QWidget w;
QHBoxLayout *lay = new QHBoxLayout(&w);
lay->addWidget(country_lw);
lay->addWidget(capital_lw);
w.show();
return a.exec();
}
I want to link two QListWidget, but I don't know how to do with code. Here is what I did :
We can see two QListWidget. With the left QListWidget, I add (by example : "Bonjour", "Hello", "Tag") three QListWidgetItem. I want that if I click on one of three QListWidgetItem of the left QListWidget that I can add QListWidgetItem with the right QListWidget (by example, for "Bonjour" : "Tu", "Vas", "Bien"). If I don't click on one of three QListWidgetItem, I can't add QListWidgetItem with the right QListWidget. If I did for "Bonjour" : "Tu", "Vas", "Bien" and I click on "Hello" (obviously, "Hello" contains nothing), there is nothing in the right QListWidget. That's just an example of what I want to do. Below, I write my code if it's helpful :
- secondwindow.cpp -
#include "secondwindow.h"
#include "ui_secondwindow.h"
#include "thirdwindow.h"
#include "ui_thirdwindow.h"
SecondWindow::SecondWindow(QWidget *parent) :
QWidget(parent),
ui(new Ui::SecondWindow)
{
ui->setupUi(this);
ui->button_1->setIcon(QIcon(":/Images/Images/Haut.png"));
ui->button_2->setIcon(QIcon(":/Images/Images/Bas.png"));
ui->button_5->setIcon(QIcon(":/Images/Images/Haut.png"));
ui->button_6->setIcon(QIcon(":/Images/Images/Bas.png"));
connect(ui->button_1, SIGNAL(clicked()), this, SLOT(UpForLeft()));
connect(ui->button_2, SIGNAL(clicked()), this, SLOT(DownForLeft()));
connect(ui->button_3, SIGNAL(clicked()), this, SLOT(DeleteForLeft()));
connect(ui->button_4, SIGNAL(clicked()), this, SLOT(AddForLeft()));
connect(ui->button_9, SIGNAL(clicked()), this, SLOT(ShowThirdWindow()));
connect(ui->button_10, SIGNAL(clicked()), this, SLOT(close()));
connect(ui->table_1, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
this, SLOT(EditForLeft(QListWidgetItem *)));
}
SecondWindow::~SecondWindow()
{
delete ui;
}
void SecondWindow::ShowThirdWindow()
{
ThirdWindow *window = new ThirdWindow;
window->setWindowTitle("B");
window->setWindowIcon(QIcon(":/Images/Images/Bouclier.png"));
window->setFixedSize(820, 440);
window->show();
}
void SecondWindow::UpForLeft()
{
QListWidgetItem *item;
int i;
i = ui->table_1->currentRow();
item = ui->table_1->takeItem(i);
ui->table_1->insertItem(i - 1, item);
ui->table_1->setCurrentRow(i - 1);
}
void SecondWindow::DownForLeft()
{
QListWidgetItem *item;
int i;
i = ui->table_1->currentRow();
item = ui->table_1->takeItem(i);
ui->table_1->insertItem(i + 1, item);
ui->table_1->setCurrentRow(i + 1);
}
void SecondWindow::UpForRight()
{
QListWidgetItem *item;
int i;
i = ui->table_2->currentRow();
item = ui->table_2->takeItem(i);
ui->table_1->insertItem(i - 1, item);
ui->table_1->setCurrentRow(i - 1);
}
void SecondWindow::DownForRight()
{
QListWidgetItem *item;
int i;
i = ui->table_2->currentRow();
item = ui->table_2->takeItem(i);
ui->table_1->insertItem(i + 1, item);
ui->table_1->setCurrentRow(i + 1);
}
void SecondWindow::AddForLeft()
{
QString string;
string = ui->line_1->text();
ui->table_1->addItem(string);
ui->line_1->clear();
}
void SecondWindow::DeleteForLeft()
{
QListWidgetItem *item;
int i;
i = ui->table_1->currentRow();
item = ui->table_1->takeItem(i);
delete item;
}
void SecondWindow::EditForLeft(QListWidgetItem *item)
{
item->setFlags(item->flags() | Qt::ItemIsEditable);
item = ui->table_1->currentItem();
ui->table_1->editItem(item);
}
- secondwindow.h -
#ifndef SECONDWINDOW_H
#define SECONDWINDOW_H
#include <QListWidgetItem>
#include <QWidget>
#include <QString>
#include <QIcon>
#include "thirdwindow.h"
#include "ui_thirdwindow.h"
namespace Ui {
class SecondWindow;
}
class SecondWindow : public QWidget
{
Q_OBJECT
public:
explicit SecondWindow(QWidget *parent = 0);
~SecondWindow();
public slots:
void ShowThirdWindow();
void UpForLeft();
void DownForLeft();
void UpForRight();
void DownForRight();
void AddForLeft();
void DeleteForLeft();
void EditForLeft(QListWidgetItem *item);
private:
Ui::SecondWindow *ui;
ThirdWindow *window;
};
#endif // SECONDWINDOW_H
Thank you for help.
A QListWidget is a convenience widget that mixes a model with a view. This makes things a bit harder than necessary as you need to keep replenishing the models with data from your top-level model that represents the tree-structured data.
Instead, you could use a QStandardItemModel, and expose it via QListView. The view only shows one column at a given level of the tree, without any children. To view the children, select an appropriate root index.
A key missing feature of QStandardItemModel is moveRows, needed to move items up/down. That's easy enough to remedy with a limited implementation that supports moving a single item within the same parent. Thus the views can be completely model-agnostic by using moveRow. We shall implement moveRows first:
// https://github.com/KubaO/stackoverflown/tree/master/questions/list-widgets-40403640
#include <QtWidgets>
class StandardItemModel : public QStandardItemModel {
bool moveRows(const QModelIndex &srcParent, int srcRow, int count,
const QModelIndex &dstParent, int dstRow) override {
if (count == 0) return true;
if (count != 1 || srcParent != dstParent) return false;
if (srcRow == dstRow) return true;
if (abs(srcRow - dstRow) != 1) return false;
auto root = srcParent.isValid() ? itemFromIndex(srcParent) : invisibleRootItem();
if (!root) return false;
auto srcItem = root->takeChild(srcRow);
auto dstItem = root->takeChild(dstRow);
if (!srcItem || !dstItem) return false;
root->setChild(srcRow, dstItem);
root->setChild(dstRow, srcItem);
return true;
}
public:
using QStandardItemModel::QStandardItemModel;
};
Subsequently, a ListUi widget implements the view, without any knowledge of how the model might work. For the purpose of this example, new items are edited in-place, instead of using a separate control.
class ListUi : public QWidget {
Q_OBJECT
QGridLayout m_layout{this};
QVBoxLayout m_column;
QPushButton m_up{"⬆"}, m_down{"⬇"}, m_remove{"−"}, m_add{"+"};
QLabel m_caption;
QListView m_list;
inline QAbstractItemModel * model() const { return m_list.model(); }
inline QModelIndex root() const { return m_list.rootIndex(); }
inline QModelIndex index(int row) const { return model()->index(row, 0, root()); }
public:
ListUi(const QString & caption, QWidget * parent = nullptr) :
QWidget{parent},
m_caption{caption}
{
m_layout.addWidget(&m_up, 0, 0);
m_layout.addWidget(&m_down, 1, 0, 1, 1, Qt::AlignTop);
m_layout.addLayout(&m_column, 0, 1, 3, 1);
m_column.addWidget(&m_caption);
m_column.addWidget(&m_list);
m_layout.addWidget(&m_remove, 0, 2);
m_layout.addWidget(&m_add, 2, 2);
m_caption.setAlignment(Qt::AlignCenter);
connect(&m_add, &QPushButton::clicked, [this]{
int row = model()->rowCount(root());
if (model()->columnCount(root()) == 0)
model()->insertColumn(0, root());
if (model()->insertRow(row, root())) {
m_list.setCurrentIndex(index(row));
m_list.edit(index(row));
}
});
connect(&m_remove, &QPushButton::clicked, [this]{
if (m_list.currentIndex().isValid())
model()->removeRow(m_list.currentIndex().row(), root());
});
connect(&m_up, &QPushButton::clicked, [this]{
auto row = m_list.currentIndex().row();
if (row > 0 && model()->moveRow(root(), row, root(), row - 1))
m_list.setCurrentIndex(index(row-1));
});
connect(&m_down, &QPushButton::clicked, [this]{
auto row = m_list.currentIndex().row();
if (row >= 0 && row < (model()->rowCount(root()) - 1) &&
model()->moveRow(root(), row, root(), row + 1))
m_list.setCurrentIndex(index(row+1));
});
}
void setModel(QAbstractItemModel * model) {
m_list.setModel(model);
connect(m_list.selectionModel(), &QItemSelectionModel::currentChanged, this, &ListUi::currentIndexChanged);
}
void setRootIndex(const QModelIndex & index) {
m_list.setRootIndex(index);
}
Q_SIGNAL void currentIndexChanged(const QModelIndex &);
};
A simple test harness instantiates two ListUis, a StandardItemModel, populates the model from a JSON source, and links them to obtain the desired functionality.
class Window : public QWidget {
Q_OBJECT
QGridLayout m_layout{this};
ListUi m_programs{"Liste des programmes"};
ListUi m_sessions{"Liste des sessions"};
QPushButton m_generate{"Générer les données"};
StandardItemModel m_model;
public:
explicit Window(QWidget * parent = nullptr) : QWidget{parent}
{
m_layout.addWidget(&m_programs, 0, 0);
m_layout.addWidget(&m_sessions, 0, 1);
m_layout.addWidget(&m_generate, 1, 1, 1, 1, Qt::AlignRight);
m_programs.setModel(&m_model);
m_sessions.setModel(&m_model);
m_sessions.setDisabled(true);
connect(&m_programs, &ListUi::currentIndexChanged, [this](const QModelIndex & root){
m_sessions.setEnabled(true);
m_sessions.setRootIndex(root);
});
connect(&m_generate, &QPushButton::clicked, this, &Window::generateData);
}
Q_SIGNAL void generateData();
void setJson(const QJsonDocument & doc) {
m_model.clear();
auto object = doc.object();
for (auto it = object.begin(); it != object.end(); ++it) {
if (!m_model.columnCount()) m_model.appendColumn({});
auto root = new QStandardItem(it.key());
m_model.appendRow(root);
if (it.value().isArray()) {
auto array = it.value().toArray();
for (auto const & value : array) {
if (!root->columnCount()) root->appendColumn({});
root->appendRow(new QStandardItem{value.toString()});
}
}
}
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
auto json = R"--({
"Foo":["Foo-1", "Foo-2", "Foo-3"],
"Bar":["Bar-1", "Bar-2"]
})--";
Window ui;
ui.connect(&ui, &Window::generateData, [&]{ ui.setJson(QJsonDocument::fromJson(json)); });
ui.show();
return app.exec();
}
#include "main.moc"
It is a simple matter to iterate the standard item model to regenerate the JSON representation.
This concludes the example.
I have created a table using qtablewidget. I would like to selectively merge cells of some rows. Is there any built-in function for that(sorry, I couldn't find one)? Otherwise, please specify how it can be done. Thanks
You could use the setSpan function of QTableView (which QTableWidget inherits) to achieve something like that for rows and columns.
That is, unless you're looking to really just merge individual cells. I'm not sure that can be achieved with setSpan, but have a look at it anyway.
Try QTableWidget::setSpan(int row, int column, int rowSpan, int columnSpan);
But in some versions it doesn't work.
Sample program
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QTableWidget>
#include <QHeaderView>
class TestWidget: public QWidget
{
Q_OBJECT
private:
QTableWidget *testTable;
QVBoxLayout *mainLayout;
QPushButton *button;
public:
TestWidget(QWidget *parent=nullptr) : QWidget(parent)
{
mainLayout = new QVBoxLayout(this);
testTable = new QTableWidget;
testTable = new QTableWidget(this);
testTable->setObjectName("testTable");
testTable->setFixedWidth(400);
testTable->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Minimum);
testTable->setColumnCount(4);
testTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
testTable->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QTableWidgetItem *itemC1 = new QTableWidgetItem("Column 1");
testTable->setHorizontalHeaderItem(0, itemC1);
QTableWidgetItem *itemC2 = new QTableWidgetItem("Column 2");
testTable->setHorizontalHeaderItem(1, itemC2);
QTableWidgetItem *itemC3 = new QTableWidgetItem("Column 3");
testTable->setHorizontalHeaderItem(2, itemC3);
QTableWidgetItem *itemC4 = new QTableWidgetItem("Column 4");
testTable->setHorizontalHeaderItem(3, itemC4);
testTable->setColumnWidth(0,100);
testTable->setColumnWidth(1,100);
testTable->setColumnWidth(2,100);
testTable->setColumnWidth(3,100);
testTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
testTable->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed);
testTable->setProperty("showDropIndicator", QVariant(false));
testTable->setDragDropOverwriteMode(false);
testTable->setSelectionMode(QAbstractItemView::SingleSelection);
testTable->setSelectionBehavior(QAbstractItemView::SelectRows);
testTable->setShowGrid(true);
testTable->setGridStyle(Qt::SolidLine);
testTable->setSortingEnabled(false);
testTable->setCornerButtonEnabled(false);
testTable->horizontalHeader()->setHighlightSections(false);
testTable->horizontalHeader()->setProperty("showSortIndicator", QVariant(false));
testTable->verticalHeader()->setVisible(false);
testTable->setAlternatingRowColors(true);
button = new QPushButton("Group");
connect(button, SIGNAL(clicked(bool)), this, SLOT(buttonClicked(bool)));
mainLayout->addWidget(testTable);
mainLayout->addWidget(button);
this->setLayout(mainLayout);
}
void addRow(QString c1, QString c2, QString c3, QString c4)
{
int row = testTable->rowCount();
testTable->insertRow(row);
QTableWidgetItem *c1Item = new QTableWidgetItem(c1);
QTableWidgetItem *c2Item = new QTableWidgetItem(c2);
QTableWidgetItem *c3Item = new QTableWidgetItem(c3);
QTableWidgetItem *c4Item = new QTableWidgetItem(c4);
c1Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
c2Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
c3Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
c4Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
testTable->setItem(row, 0, c1Item);
testTable->setItem(row, 1, c2Item);
testTable->setItem(row, 2, c3Item);
testTable->setItem(row, 3, c4Item);
}
void updateCell(int rowNumber, int columnNumber, QString textString)
{
if(rowNumber>=testTable->rowCount() || columnNumber>=testTable->columnCount()) return;
testTable->item(rowNumber, columnNumber)->setText(textString);
}
void groupCells(int rowNumber, int columnNumber, int rowSpan, int columnSpan)
{
if(rowNumber>=testTable->rowCount() || columnNumber>=testTable->columnCount()) return;
testTable->setSpan(rowNumber,columnNumber,rowSpan,columnSpan);
}
void ungroupCells(int rowNumber, int columnNumber)
{
if(rowNumber>=testTable->rowCount() || columnNumber>=testTable->columnCount()) return;
testTable->setSpan(rowNumber,columnNumber,1,1);
}
public slots:
void buttonClicked(bool event)
{
Q_UNUSED(event)
if(button->text()=="Group")
{
groupCells(1,1,2,1);
button->setText("Ungroup");
}
else
{
ungroupCells(1,1);
button->setText("Group");
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestWidget wid;
wid.addRow("0,0", "0,1", "0,2", "0,3");
wid.addRow("1,0", "1,1", "1,2", "1,3");
wid.addRow("2,0", "2,1", "2,2", "2,3");
wid.addRow("3,0", "3,1", "3,2", "3,3");
wid.show();
return a.exec();
}
#include "main.moc"
Project file shall be downloaded from github