I am designing a user interface with several functions. On the .ui I have a QTableView. After right click on the table view I open a dialog with the same QTableView so in this way is easier to visualize data.
After Trying to copy and paste some of the values say, from a cell to another or from a column to another column I have a weird behavior that I don't know how to solve. Below I try to copy ctrl+C the selected cell:
And paste it ctrl+V I obtain a weird behavior:
For some reasons I am copiyng the header "longitude" on the second row instead of the number 24. Additionally the number 200 is pushed below.
This code also allows to copy and paste from the QTableView to an Excel file, this functions works well but I decided to include it in the explanation so you know how I handle all the loops.
editdialog.h
private:
Ui::editLeftTableDialog *ui;
QSqlTableModel *mModel;
QAction *mActionAppendRow;
QAction *mActionDeleteRow;
protected:
void keyPressEvent(QKeyEvent *event);
editdialog.cpp
void editLeftTableDialog::keyPressEvent(QKeyEvent *event)
{
{
QAbstractItemModel * modelRemoveAdd = ui->tableViewLeft->model();
QItemSelectionModel *selectionModel = ui->tableViewLeft->selectionModel();
QModelIndexList selectedRows = selectionModel->selectedRows();
QAbstractItemModel * modelNew = ui->tableViewLeft->model();
QItemSelectionModel *selectedIndexes = ui->tableViewLeft->selectionModel();
QModelIndexList selectedInd = selectedIndexes->selectedIndexes();
// Make sure one row is selected
if(!selectedRows.isEmpty())
{
if(event->key() == Qt::Key_Insert)
mModel->insertRows(selectedRows.at(0).row(),
selectedRows.size());
else if(event->key() == Qt::Key_Delete)
mModel->removeRows(selectedRows.at(0).row(),
selectedRows.size());
}
// Make sure one cell selected
if(!selectedInd.isEmpty())
{
if(event->key() == Qt::Key_Delete)
foreach(QModelIndex index, selectedInd)
mModel->setData(index, QString());
else if(event->matches(QKeySequence::Copy))
{
QString text;
QItemSelectionRange range = ui->tableViewLeft->selectionModel()->selection().first();
for(auto i = range.top(); i <= range.bottom(); ++i)
{
QStringList rowContents;
for(auto j = range.left(); j <= range.right(); ++j)
rowContents << mModel->index(i,j).data().toString();
text += rowContents.join("\t");
text += "\n";
}
QApplication::clipboard()->setText(text);
}
else if(event->matches(QKeySequence::Paste))
{
QString text = QApplication::clipboard()->text();
QStringList rowContents = text.split("\n", QString::SkipEmptyParts);
QModelIndex initIndex = ui->tableViewLeft->selectionModel()->selectedIndexes().at(0);
auto initRow = initIndex.row();
auto initCol = initIndex.column();
for(auto i = 0; i < rowContents.size(); i++)
{
QStringList columnContents = rowContents.at(i).split("\t");
for(auto j = 0; j < columnContents.size(); j++)
{
mModel->setData(mModel->index(initRow + i, initCol + j), columnContents.at(j));
}
}
}
}
}
// Copy from QTableView to an Excel or CSV file happens here and works well
QAbstractItemModel * model = ui->tableViewLeft->model();
QItemSelectionModel *selection = ui->tableViewLeft->selectionModel();
QModelIndexList indices = selection->selectedIndexes();
if(indices.isEmpty())
return;
QMap<int, bool> selectedColumnsMap;
Q_FOREACH (QModelIndex current, indices)
{
selectedColumnsMap[current.column()] = true;
}
QList<int> selectedColumns = selectedColumnsMap.uniqueKeys();
int minCol = selectedColumns.first();
// prepend headers for selected columns
QString selectedText;
Q_FOREACH (int column, selectedColumns)
{
selectedText += ui->tableViewLeft->model()->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString();
if(column != selectedColumns.last())
selectedText += QLatin1Char('\t');
}
selectedText += QLatin1Char('\n');
qSort(indices);
int lastRow = indices.first().row();
int lastColumn = minCol;
Q_FOREACH(QModelIndex current, indices)
{
if(current.row() != lastRow)
{
selectedText += QLatin1Char('\n');
lastColumn = minCol;
lastRow = current.row();
}
if(current.column() != lastColumn)
{
for(int i = 0; i < current.column() - lastColumn; ++i)
selectedText += QLatin1Char('\t');
lastColumn = current.column();
}
selectedText += ui->tableViewLeft->model()->data(current).toString();
}
selectedText += QLatin1Char('\n');
QApplication::clipboard()->setText(selectedText);
}
A messy code always brings problems, so I'm not going to analyze it but I'm going to propose an appropriate method where we create a function for each task, and instead of using keyPressEvent you can use a QShorcut:
editlefttabledialog.h
#ifndef EDITLEFTTABLEDIALOG_H
#define EDITLEFTTABLEDIALOG_H
#include <QDialog>
class QSqlTableModel;
namespace Ui {
class EditLeftTableDialog;
}
class EditLeftTableDialog : public QDialog
{
Q_OBJECT
public:
explicit EditLeftTableDialog(QWidget *parent = nullptr);
~EditLeftTableDialog();
private slots:
void copy();
void paste();
private:
Ui::EditLeftTableDialog *ui;
QSqlTableModel *mModel;
};
#endif // EDITLEFTTABLEDIALOG_H
editlefttabledialog.cpp
#include "editlefttabledialog.h"
#include "ui_editlefttabledialog.h"
#include <QShortcut>
#include <QSqlTableModel>
#include <QClipboard>
EditLeftTableDialog::EditLeftTableDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::EditLeftTableDialog)
{
ui->setupUi(this);
mModel = new QSqlTableModel(this);
mModel->setTable("test_table");
mModel->select();
ui->tableViewLeft->setModel(mModel);
QShortcut *shortcut_copy = new QShortcut(QKeySequence::Copy, ui->tableViewLeft);
connect(shortcut_copy, &QShortcut::activated, this, &EditLeftTableDialog::copy);
QShortcut *shortcut_paste = new QShortcut(QKeySequence::Paste, ui->tableViewLeft);
connect(shortcut_paste, &QShortcut::activated, this, &EditLeftTableDialog::paste);
}
EditLeftTableDialog::~EditLeftTableDialog()
{
delete ui;
}
void EditLeftTableDialog::copy()
{
QString text;
QItemSelectionRange selection_range = ui->tableViewLeft->selectionModel()->selection().first();
for(int i=selection_range.top(); i <= selection_range.bottom(); i++){
QStringList row_content;
for(int j= selection_range.left(); j <= selection_range.right(); j++){
row_content << mModel->index(i,j).data().toString();
}
text += row_content.join(QChar('\t')) + QChar('\n');
}
QApplication::clipboard()->setText(text);
}
void EditLeftTableDialog::paste()
{
QModelIndex start = ui->tableViewLeft->selectionModel()->selectedIndexes().first();
QString text = QApplication::clipboard()->text();
int i=0, j=0;
for(const QString & row_string: text.split("\n", QString::SkipEmptyParts)){
j=0;
for(const QString & col: row_string.split("\t", QString::SkipEmptyParts)){
QModelIndex ix = mModel->index(start.row() + i , start.column()+ j);
if(ix.isValid()){
ui->tableViewLeft->model()->setData(ix, col);
mModel->submit();
}
j++;
}
i++;
}
}
Related
I have a very simple tree data structure:
A, B and C are some vectors, whose elements are respectively A0-A3, B0-B1 and C0-C2.
Now, I'm trying to show in a QListView (let's call it listview1) the elements A B and C, and in another QFileView (listview2) the child elements of the element selected in listview1.
What I have:
What I want:
Here there is my code so far:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
model = new QStandardItemModel(this);
QStandardItem *collection1 = new QStandardItem(QString("A"));
model->invisibleRootItem()->appendRow(collection1);
for (int i = 0; i < 4; ++i) {
QStandardItem *item = new QStandardItem(QString("A %0").arg(i));
collection1->appendRow(item);
}
QStandardItem *collection2 = new QStandardItem(QString("B"));
model->invisibleRootItem()->appendRow(collection2);
for (int i = 0; i < 2; ++i) {
QStandardItem *item = new QStandardItem(QString("B %0").arg(i));
collection2->appendRow(item);
}
QStandardItem *collection3 = new QStandardItem(QString("C"));
model->invisibleRootItem()->appendRow(collection3);
for (int i = 0; i < 3; ++i) {
QStandardItem *item = new QStandardItem(QString("C %0").arg(i));
collection3->appendRow(item);
}
ui->listView->setModel(model);
ui->listView_2->setModel(model);
ui->treeView->setModel(model);
}
void MainWindow::on_listView_activated(const QModelIndex &index) {
QStandardItem* item = model->itemFromIndex(index);
QStandardItem* childItem = item->child(0);
qDebug() << item << "index" << index << childItem;
if (childItem != nullptr) {
ui->treeView->setCurrentIndex(childItem->index());
}
//ui->listView->setModelColumn(1);
}
Does someone have some ideas?
You must use the setRootIndex() method:
void MainWindow::on_listView_activated(const QModelIndex &index) {
ui->listView_2->setRootIndex(index);
}
How do i set the checkbox for the selected Treeview items in my QTreeview C++ project?
My goal is to iterate over the selected Treeview items and set their checkbox state to true or false. I'll be doing this through a righclick menu. I just don't know how to collect the selected items.
I wrote a function that loops through all the items and sets the checkboxes to true or false, but I'm struggling to figure out how to set only the selected items.
void ShotsEditor::checkAll()
{
for (int i = 0; i < d->sourceModel->rowCount(); i++){
QStandardItem* item = d->sourceModel->item(i);
if (item->isCheckable())
{
item->setCheckState(Qt::Checked);
}
else{
if (item->hasChildren())
{
for (int j = 0; j < item->rowCount(); j++){
QStandardItem* item1 = item->child(j);
if (item1->isCheckable())
{
item1->setCheckState(Qt::Checked);
}
}
}
}
}
}
This is where I'm stuck...
void ShotsEditor::checkSelected()
{
QModelIndexList selected = d->treeView->selectionModel()->selectedIndexes();
qDebug() << "selected indexes" << selected;
foreach (QModelIndex index, selected)
{
if (index.column()==0)
{
int row = index.row();
qDebug() << "row" << row;
}
}
}
This is how i would do it in pyside. I don't know how to do it in c++
def set_checked(self):
indexes = self.treeview.selectedIndexes()
for i in indexes:
model = i.model()
item = i.model().itemFromIndex(i)
print i, model, item
if item.isSelectable():
item.setCheckState(QtCore.Qt.Checked)
The proper way to get the items selected is overwriting the selectionChanged slot, it returns the selected and deselected items, then we get the items through the indexes and check them.
class TreeView : public QTreeView
{
Q_OBJECT
protected slots:
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected){
Q_UNUSED(deselected)
const auto *m = qobject_cast<QStandardItemModel *>(model());
if(m){
for(const auto index: selected.indexes()){
m->itemFromIndex(index)->setCheckState(Qt::Checked);
}
}
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TreeView w;
QStandardItemModel *model = new QStandardItemModel;
for (int i = 0; i < 4; ++i){
QStandardItem *parent =new QStandardItem(QString("Family %1").arg(i));
for (int j = 0; j < 4; ++j){
QStandardItem *child = new QStandardItem(QString("item %1").arg(j));
parent->appendRow(child);
}
model->appendRow(parent);
}
w.setModel(model);
w.show();
return a.exec();
}
The complete example you find in the following link
For those who have this same problem:
QModelIndexList selected = d->treeView->selectionModel()->selectedIndexes();
qDebug() << "selected" << selected;
foreach (QModelIndex index, selected)
{
QStandardItem* item = d->sourceModel->itemFromIndex(index);
qDebug() << "item" << item;
if (item->isSelectable() && item->isCheckable())
{
item->setCheckState(Qt::Unchecked);
}
}
I have a QStandardItemModel, which I display in q QTreeView. Works fine.
To highlight relevant rows I want to highlight some of them: Therefore I have a QStringList with the names of the QStandItem* s to be highlighted.
QStringList namesToBeHighlighted = getNames();
QModelIndex in = myModel->index(0, 0);
if ( in.isValid() ) {
for (int curIndex = 0; curIndex < myModel->rowCount(in); ++curIndex) {
QModelIndex si = myModel->index(curIndex, 0, in);
QStandardItem *curItem = myModel->itemFromIndex(si);
if (curItem) {
QString curItemName = curItem->text();
if ( namesToBeHighlighted.contains(curItem->text()) ) {
curItem->setFont(highlightFont);
}
else curItem->setFont(unHighlightFont);
}
}
}
My Model has following structure:
Level_1
+--> Level_11
+--> Level_12
+--> Level_13
Level_2
+--> Level_21
+--> Level_22
+--> Level_23
...
Here, it iterates trough Levels 11, 12 and 13 then it stops.
I hope it helps you:
void forEach(QAbstractItemModel* model, QModelIndex parent = QModelIndex()) {
for(int r = 0; r < model->rowCount(parent); ++r) {
QModelIndex index = model->index(r, 0, parent);
QVariant name = model->data(index);
qDebug() << name;
// here is your applicable code
if( model->hasChildren(index) ) {
forEach(model, index);
}
}
}
QStandardItemModel model;
QStandardItem* parentItem = model.invisibleRootItem();
for (int i = 0; i < 4; ++i) {
QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
for (int j = 0; j < 5; ++j) {
item->appendRow(new QStandardItem(QString("item %0%1").arg(i).arg(j)));
}
parentItem->appendRow(item);
parentItem = item;
}
forEach(&model);
{
...
nrow = 10;
ncol = 1;
/*create QListView */
m_listView = new QListView(this);
m_listView->setGeometry(QRect(QPoint(0,100), QSize(100, 150)));
QStandardItemModel *model = new QStandardItemModel( nrow, 1, this );
//fill model value
for( int r=0; r<nrow; r++ )
{
QString sstr = "[ " + QString::number(r) + " ]";
QStandardItem *item = new QStandardItem(QString("Idx ") + sstr);
model->setItem(r, 0, item);
}
//set model
m_listView->setModel(model);
m_listView->setSelectionMode( QAbstractItemView::ExtendedSelection );
connect(m_listView, SIGNAL(pressed(QModelIndex)), this, SLOT(hItem(QModelIndex)));
}
void MainWindow::hItem(QModelIndex m)
{
QItemSelectionModel *selectionModel = m_listView->selectionModel();
m_txt2->setText(QString::number(selectionModel->selectedIndexes().at(0),'d',0));//???
//not sure how to get the items selected: index and string per selection
}
I just tested this for my own needs and it works in Qt 5.1.
I'm pretty new to C++ so in this line:
foreach(const QModelIndex &index, list){
I don't know if the const and the dereferencing (&) is needed - it works with or without. I cobbled this together from various examples I've seen.
Perhaps someone who understands C++ better can comment.
void MainWindow::on_keywordsList_clicked(const QModelIndex &index)
{
QModelIndexList list =keywordListView->selectionModel()->selectedIndexes();
QStringList slist;
foreach(const QModelIndex &index, list){
slist.append( index.data(Qt::DisplayRole ).toString());
}
qDebug() << slist.join(",");
}
I am developing a Qt application using QTreeView and QFileSystemModel.
I am able to get the parent's child till one level but I am not able to get the parent's child's child.
For eg:
C is child of B,
B is child of A
I am able to get B as A's child but I also want C as A's Child.
I want like this C->B->A.
Can someone give some input regarding this and help me out.
Thanks in advance.
//QItemSelectionModel *sel = ui->dir_tree->selectionModel();
QStringList strings = extractStringsFromModel(ui->dir_tree->model(), ui->dir_tree->rootIndex());
QFileSystemModel* model = (QFileSystemModel*)ui->dir_tree->model();
QModelIndexList indexlist = ui->dir_tree->selectionModel()->selectedIndexes();
QVariant data;
//QList<QModelIndex> modelindex(indexlist);
int row = -1;
for(int i=0; i<indexlist.size();i=i+4)
{
QModelIndex mi=indexlist.at(i);
info1 = model->fileInfo(mi);
QString childstr = info1.filePath();
QString childname = info1.fileName();
QModelIndex mi2= indexlist.at(i).parent();
info = model->fileInfo(mi2);
QString parentstr = info.filePath();
QString parentname = info.fileName();
QStringList childlist;
for(int j=0;j<model->rowCount(indexlist.at(i));j++)
{
QModelIndex mi3 = indexlist.at(i).child(j, 0);
info2 = model->fileInfo(mi3);
QString childrenstr = info2.filePath();
childlist << childrenstr;
qDebug()<<"parents' children"<<childrenstr<<j;
}
}
I like to use recursion for this kind of thing:
void MyTreeView::GetAllChildren(QModelIndex &index, QModelIndexList &indicies)
{
indicies.push_back(index);
for (int i = 0; i != model()->rowCount(index); ++i)
GetAllChildren(index.child(i,0), indicies);
}
Usage (from somewhere inside the tree view):
QModelIndexList list;
GetAllChildren(model()->index(0,0), list);
Getting all children breadth-first:
QModelIndexList children;
// Get top-level first.
for ( int i = 0; i < model->rowCount(); ++i ) {
children << model->index( i, 0 ); // Use whatever column you are interested in.
}
// Now descend through the generations.
for ( int i = 0; i < children.size(); ++i ) {
for ( int j = 0; j < model->rowCount( children[i] ); ++j ) {
children << children[i].child( j, 0 );
}
}
Getting all parents from a child:
QModelIndexList parents;
parents << child.parent();
while ( parents.last().isValid() ) {
parents << parents.last().parent();
}
Please note, I haven't tried these samples!
//Delete
QList<QTreeWidgetItem *> selected = ui->twg->selectedItems();
QTreeWidgetItem *item = selected.first();
QTreeWidgetItem *parent = item->parent();
if(parent) {
int index = parent->indexOfChild(item);
delete parent->takeChild(index);
}
//Add
QList<QTreeWidgetItem *> selected = ui->twg->selectedItems();
QTreeWidgetItem *item = selected.first();
QTreeWidgetItem *parent = item->parent();
if(parent) {
QTreeWidgetItem *tmp = new QTreeWidgetItem(parent);
tmp->setText(0, "Dude");
parent->addChild(tmp);
}