How to remove an Item from a List View? - qt

I have a QLineEdit, 2 QPushButtons (Add & Remove Buttons) and a QListView.
I want to add the QLineEdit text to the QListView when the add button is clicked. Same way, I have to delete an item from the QListView if the remove button is clicked.
I'm using a QStringListModel to add the QLineEdit text to the QListView. But I don't know how to delete the QListView item. How can I do this? Plz Help.. Thanks in Advance.
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include <QWidget>
#include <QStringList>
#include <QStringListModel>
class EXAMPLE : public QWidget
{
Q_OBJECT
public:
explicit EXAMPLE(QWidget *parent = 0);
~EXAMPLE();
private slots:
void on_addButton_released();
void on_removeButon_released();
private:
Ui::EXAMPLE *ui;
QStringList stringList;
};
#endif // EXAMPLE_H
EXAMPLE.CPP
#include "EXAMPLE.h"
#include <QStringListModel>
EXAMPLE::EXAMPLE(QWidget *parent) :
QWidget(parent),
ui(new Ui::EXAMPLE)
{
ui->setupUi(this);
ui->listView->setModel(new QStringListModel(stringList));
}
EXAMPLE::~EXAMPLE()
{
delete ui;
}
void EXAMPLE::on_addButton_released()
{
stringList.append(ui->lineEdit->text());
((QStringListModel*) ui->listView->model())->setStringList(stringList);
ui->lineEdit->clear();
}
void EXAMPLE::on_removeButon_released()
{
}

If your list view is only using single selection, the following will work:
void EXAMPLE::on_removeButton_released()
{
QModelIndexList selected = ui->listView->selectionModel()->selectedIndexes();
if (!selected.isEmpty())
{
stringList.removeAt(selected.first().row()-i);
((QStringListModel*) ui->listView->model())->setStringList(stringList);
}
}
If your list view allows for multiple items to be selected, then you'll need something slightly more complex:
void EXAMPLE::on_removeButton_released()
{
QModelIndexList selected = ui->listView->selectionModel()->selectedIndexes();
if (!selected.isEmpty())
{
qSort(selected);
for (int i=0; i<selected.count(); ++i)
{
stringList.removeAt(selected.at(i).row()-i);
}
((QStringListModel*) ui->listView->model())->setStringList(stringList);
}
}
There is also a means of removing the items directly from the item model (as opposed to removing it from the string list and then setting the string list on the model). Refer to the documentation for QAbstractItemModel::removeRow. If you go down this road, your method of adding items will also need to change.

Related

How to simulate a drag and drop action using QTest

In order to create a test case for a drag and drop bug in the QTreeView widget I tried to simulate a drag and drop mouse movement behavior.
I basically select the first element in the QTreeView and want it to drag and drop on the third element. I did this by using a combination of QTest::mousePress and QTest::mouseMove. At the end there should be of course a QTest::mouseRelease, but I already failed in reproducing the mousePress and mouseMove.
These are steps necessary to reproduce:
User moves mouse on center of first item
User presses the left mouse button
User holds the left mouse button pressed and moves the mouse to the center of the third item
User releases the left mouse button
If one does these action as described I can see, that the QTreeView Widgets reacts appropriately, and indicating special highlights and vertical lines, in case the item will be move between to items.
Unfortunately, my automatized test fails to reproduce this behavior. It seems that calling QTest::mousePress in sequence does something different. Also using a pair of QTest::mousePress and QTest::mouseMove is something differently.
This is my code:
main.cpp
#include "TestObject.h"
#include <QTest>
QTEST_MAIN(TestObject)
TestObject.h
#include <QtTest/QtTest>
class TestObject : public QObject
{
Q_OBJECT
private slots:
void dragAndDrop();
};
TestObject.cpp
#include "TestObject.h"
#include "TestObject.moc"
#include <QTest>
#include <QTreeView>
#include <QStandardItemModel>
#include <QPropertyAnimation>
#include "MouseMover.h"
void TestObject::dragAndDrop() {
qDebug() << "Hello";
QStandardItemModel model;
QTreeView view;
view.setModel(&model);
view.show();
view.setHeaderHidden(true);
view.setDragDropMode(QAbstractItemView::DragDropMode::DragDrop);
view.setDefaultDropAction(Qt::DropAction::MoveAction);
view.setColumnHidden(1, true);
for (auto rowIter = 0; rowIter < 3; rowIter++) {
QList<QStandardItem*> items;
for (auto colIter = 0; colIter < 2; colIter++) {
items << new QStandardItem(QString("%1-%2").arg(rowIter).arg(colIter));
}
model.appendRow(items);
}
MouseMover mover;
mover.setWidget(view.viewport());
QPropertyAnimation anim(&mover, "mousePosition");
QTimer::singleShot(0, [&]() {
auto startValue = view.visualRect(model.index(0, 0)).center();
auto endValue = view.visualRect(model.index(2, 0)).center();
QTest::mousePress(view.viewport(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, startValue);
anim.setStartValue(startValue);
anim.setEndValue(endValue);
anim.setDuration(500);
anim.start();
});
qApp->exec();
}
MouseMover.h
#pragma once
#include <QObject>
#include <QTest>
class MouseMover : public QObject {
Q_OBJECT
public:
Q_PROPERTY(QPoint mousePosition READ mousePosition WRITE setMousePosition MEMBER mMousePosition)
void setWidget(QWidget* widget) {
mWidget = widget;
}
QPoint mousePosition() const {
return mMousePosition;
}
void setMousePosition(const QPoint& pos) {
mMousePosition = pos;
if (mWidget) {
QTest::mousePress(mWidget, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, mMousePosition);
QTest::mouseMove(mWidget, mMousePosition);
}
}
private:
QPoint mMousePosition;
QWidget* mWidget{ nullptr };
};
MouseMover.cpp
#include "MouseMover.h"
mouseMoveEvent() handler starts and blocks at QDrag.exec(). In order to simulate the drop, you have to schedule it e.g. with QTimer:
def complete_qdrag_exec():
QTest.mouseMove(drop_target)
QTest.qWait(50)
QTest.mouseClick(drop_target, Qt.MouseButton.LeftButton)
QTimer.singleShot(1000, complete_qdrag_exec)
QTest.mousePress(drag_source, Qt.MouseButton.LeftButton, Qt.KeyboardModifier.NoModifier, QPoint(10, 10))
QTest.mouseMove(drag_source, QPoint(50, 50)) # Ensure distance sufficient for DND start threshold
Above is plain Python (PyQt6/PySide6). I assume it's similar for C++ ...

Qt QTreeView indexBelow does not work

As far as I understood, indexBelow function is used for navigating to the next item on a tree in Qt's QTreeView. I want to write two functions - one to move to next item, one to move to previous item in a DirModel based TreeView. However, indexBelow does only work once.
Considering ui is the UI variable, the following is my function to move to the next item
void MainWindow::moveDown (void)
{
QModelIndex index_it = ui->treeView->indexBelow(ui->treeView->currentIndex());
qDebug() << index_it.row();
if (index_it.isValid())
{
qDebug() << "Valid";
ui->treeView->setCurrentIndex(index_it);
ui->treeView->selectionModel()->select(index_it, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
}
When a button is pressed, this will be triggered and the tree will be navigated using buttons. This however, works only once, and afterwards stops working. Note that I want to be able to move to next item even if there is an expanded child. Any pointers and help is greately appreciated.
Just tried your code and it work fine.
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QStandardItemModel>
#include <QFileSystemModel>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
model->setReadOnly(false);
model->setSorting(QDir::DirsFirst |
QDir::IgnoreCase |
QDir::Name);
ui->treeView->setModel(model);
index = model->index("C:/");
// Set initial view of directory
// for the selected drive as expanded
ui->treeView->expand(index);
// Make it scroll to the selected
ui->treeView->scrollTo(index);
// Highlight the selected
ui->treeView->setCurrentIndex(ui->treeView->indexBelow(index));
// Resizing the column - first column
ui->treeView->resizeColumnToContents(0);
}
void MainWindow::on_pushButton_3_clicked()//<------Your function MoveDown
{
QModelIndex index_it = ui->treeView->indexBelow(ui->treeView
->currentIndex());
qDebug() << index_it.row();
if (index_it.isValid())
{
qDebug() << "Valid";
ui->treeView->setCurrentIndex(index_it);
ui->treeView->selectionModel()->select(index_it, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
}
And mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QModelIndex>
#include <QDirModel>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
emitProgress(int per)
{
emit signalProgress(per);
}
void on_pushButton_clicked();
void on_pushButton_3_clicked();
signals:
private:
Ui::MainWindow *ui;
QModelIndex index;
QDirModel* model = new QDirModel(this);
};

connecting three comboboxes to control the options avaliable in qt

I have three comboboxes with three similar options, 'one', 'two', 'three', and I want to prevent the same option in different combobox.
Example:
combobox1 : 'one',
so when I go choose in combobox2 and combobox3 there's only 'two' and 'three' to choose from.
I know I could do this in a combination of for loop and if, but can anyone help me with some trick I could use here?
Write your own class MyComboBox which is derived from QComboBox.
Implement a slot in MyComboBox that would do something like this:
void excludeIndex(const QString & text)
{
// Get the list of all the options for the ComboBox
QStringList list = getIndices();
// Remove one (the only) item 'text' from the list
list.removeOne( text );
// Clear the ComboBox and fill with the new values
this->clear();
this->addItems( list );
}
In your parent widget/application, connect the signal void currentIndexChanged(const QString & text) from each 'sending' MyComboBox or QComboBox to this slot of the 'receiving' MyComboBox.
If you need to exclude the values from multiple other ComboBoxes, then it might be better to implement the slot in the parent Widget. That way, you can loop over all the 'receiving' ComboBoxes. Per ComboBox you will read all the current values of the other ComboBoxes and remove those values from list. Your slot in the parent Widget will not need an input QString anymore.
How about using only one combobox? There are only six possible options:
one two three
one three two
two one three
two three one
three one two
three two one
It would be much easier for the user to use only one combobox instead of using three comboboxes whose available options are continuously changing.
Here is one solution.
Basically it initializes all of the boxes with all the items, and as a box's current text is changed, it removes that text from the other boxes. If the previous text that the box contained wasn't blank, it adds it back into all the other boxes.
If you care to test it with more than 8, change the variable m_numOfBoxes in mainwindow.cpp to some other value.
main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QComboBox>
#include <QList>
#include <QStringList>
#include <QMap>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void on_comboBox_changed();
private:
QList <QComboBox *> m_boxes;
QMap <QComboBox *, QString> m_previousText;
int m_numOfBoxes;
QStringList m_allItems;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QVBoxLayout * vbox = new QVBoxLayout();
m_numOfBoxes = 8;
m_allItems << "";
for(int i = 0; i< m_numOfBoxes; i++)
{
m_allItems << QString::number(i+1);
}
for(int i = 0; i< m_numOfBoxes; i++)
{
QComboBox * temp = new QComboBox;
QObject::connect(temp, SIGNAL(currentIndexChanged(int)), this, SLOT(on_comboBox_changed()));
vbox->addWidget(temp);
temp->addItems(m_allItems);
m_boxes.append(temp);
m_previousText[temp] = "";
}
this->setCentralWidget(new QWidget());
this->centralWidget()->setLayout(vbox);
}
MainWindow::~MainWindow()
{
}
void MainWindow::on_comboBox_changed()
{
QComboBox * editedBox = qobject_cast <QComboBox *> (QObject::sender());
QString currentText = editedBox->currentText();
if(currentText == m_previousText[editedBox])
{
return;
}
foreach(QComboBox * box, m_boxes)
{
if(box == editedBox)
{
continue;
}
if(currentText != "")
{
// remove the current text from the other boxes
int index = box->findText(currentText);
if(index != -1)
{
box->removeItem(index);
}
}
if(m_previousText[editedBox] != "")
{
// add the previous text back into the lists for the other boxes
box->addItem(m_previousText[editedBox]);
qDebug() << "Adding back" << m_previousText[editedBox];
}
}
m_previousText[editedBox] = currentText;
}
Note that when an item is added back into a box's list, it just gets tacked onto the end of the list, so over time the order of your items may get scrambled.
Hope that helps.

Qt - How to Remove the selected Row from the QTableView and copy it to the QLineEdit

I have 3 QLineEdits (say Name, Address & Phone No.), 2 QPushButton (Add & Modify) and a QTableView.
When I enter text in all the QLineEdits, and if I click the Add button, all 3 texts of the QLineEdits should be added in the 1st row of the QTableView.
Again if I enter 3 text's in the QLineEdit and the Add Button is Clicked, the Text's should be placed in the 2nd row of the QTableView. Like this it should go on. I did this all and it works fine.
Now if I select any row from the QTableView and once I click the Modify Button the selected Row has to be removed from the QTableView and the Items should be again placed in their respective QLineEdits.
How can I do this ?
Example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include <QWidget>
#include <QStandardItemModel>
namespace Ui {
class Example;
}
class Example : public QWidget
{
Q_OBJECT
public:
explicit Example (QWidget *parent = 0);
~Example();
private slots:
void on_addButton_released();
void on_modifyButton_released();
private:
Ui::Example*ui;
QStandardItemModel *model;
};
#endif // EXAMPLE_H
EXAMPLE.CPP
#include "Example.h"
Example::Example(QWidget *parent) :
QWidget(parent),
ui(new Ui::Example)
{
ui->setupUi(this);
model = new QStandardItemModel();
ui->tableView->setModel(model);
}
Example::~Example()
{
delete ui;
}
void Example::on_addButton_released()
{
model->setHorizontalHeaderItem(0, new QStandardItem(QString(" NAME ")));
model->setHorizontalHeaderItem(1, new QStandardItem(QString(" ADDRESS ")));
model->setHorizontalHeaderItem(2, new QStandardItem(QString(" PHONE NO ")));
QStandardItem *nameItem = new QStandardItem(ui->nameLineEdit->text());
QStandardItem *addressItem = new QStandardItem(ui->addressLineEdit->text());
QStandardItem *phoneItem = new QStandardItem(ui->phoneLineEdit->text());
QList<QStandardItem*> row;
row << nameItem << addressItem << phoneItem;
model->appendRow(row);
ui->nameLineEdit->clear();
ui->addressLineEdit->clear();
ui->mobileLineEdit->clear();
ui->emailLineEdit->clear();
}
void Example::on_modifyButton_released()
{
}
What you want to do is when the Modify button is clicked, access the selection from the QItemSelectionModel of your QTableView. Once you have the selection, if any, process it.
For example:
void Example::on_modifyButton_released()
{
if( ui->myTableView )
{
QModelIndex currentIndex = ui->myTableView->selectionModel();
// Make sure to check the index is valid, as the user
// may not have selected a row.
if( currentRow.isValid() )
{
// Add your code here to copy the data to
// your QLineEdit and remove the row from your
// QStandardModel.
...
}
}
}
For reference:
QTableView http://qt-project.org/doc/qt-4.8/QTableView.html
QItemSelectionModel http://qt-project.org/doc/qt-4.8/QItemSelectionModel.html

Qt - several QTextBlock inline

Is it possible to arrange several QTextBlocks in QTextDocument in one horizontal line?
I need to know which block of text was clicked and QTextBlock would be nice to use due to its method setUserState(int), which can be used to hold id of particular block. Are there better approaches?
Not sure if I got your question right, but I am taking a shot at it (some three years after the question was asked.....)
In principle you can put QTextBlocks in a horizontal line using a QTextTable. If you then create a class which inherits from QTextEdit you can catch the mouse events and find out which text block was clicked.
I post some code below where I have a very simply dialog that only has a textedit in it (of the derived class mentioned above). I create a table laying out three text blocks in a horizontal line and set their user state to the column number. Then I have the text edit class only with the overloaded mouseEvent method which only prints the userState of whatever text block it is in to the command line, just to show the principle.
Let me know if this is of any help or if misunderstood your question.
dialog.h
#ifndef MYDIALOG_H
#define MYDIALOG_H
#include "ui_dialog.h"
class MyDialog : public QDialog, public Ui::Dialog
{
public:
MyDialog(QWidget * parent = 0, Qt::WindowFlags f = 0);
void createTable();
};
#endif
dialog.cpp
#include "dialog.h"
#include <QTextTable>
#include <QTextTableFormat>
MyDialog::MyDialog(QWidget * parent, Qt::WindowFlags f) :
QDialog(parent,f)
{
setupUi(this);
}
void MyDialog::createTable()
{
QTextCursor cursor = textEdit->textCursor();
QTextTableFormat tableFormat;
tableFormat.setCellPadding(40);
tableFormat.setBorderStyle(QTextFrameFormat::BorderStyle_None);
QTextTable* table=cursor.insertTable(1,3,tableFormat);
for( int col = 0; col < table->columns(); ++col ) {
cursor = table->cellAt(0, col).firstCursorPosition();
cursor.insertBlock();
cursor.block().setUserState(col);
cursor.insertText(QString("Block in Column ")+QString::number(col));
}
}
mytextedit.h
#ifndef MYTEXTEDIT_H
#define MYTEXTEDIT_H
#include <QTextEdit>
class MyTextEdit : public QTextEdit
{
public:
MyTextEdit(QWidget * parent = 0);
void mousePressEvent(QMouseEvent *event);
};
#endif
mytextedit.cpp
#include "mytextedit.h"
#include <QMouseEvent>
#include <QTextBlock>
#include <QtCore>
MyTextEdit::MyTextEdit(QWidget * parent) :
QTextEdit(parent)
{
}
void MyTextEdit::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton) {
qDebug() << this->cursorForPosition(event->pos()).block().userState();
}
}
main.cpp (just for completeness)
#include <QApplication>
#include "dialog.h"
int main(int argc, char** argv)
{
QApplication app(argc,argv);
MyDialog dialog;
dialog.show();
dialog.createTable();
return app.exec();
}

Resources