QTableWidget::itemAt() returns seemingly random items - c++

I've just started using Qt, so please bear with me. When I use QTableWidget->getItemAt(), it returns a different item from if I used currentItemChanged and clicked the same item. I believe it's necessary to use itemAt() since I need to get the first column of whatever row was clicked.
Some example code is below:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QList<QString> rowContents;
rowContents << "Foo" << "Bar" << "Baz" << "Qux" << "Quux" << "Corge" << "Grault" << "Garply" << "Waldo" << "Fred";
for(int i =0; i < 10; ++i)
{
ui->tableTest->insertRow(i);
ui->tableTest->setItem(i, 0, new QTableWidgetItem(rowContents[i]));
ui->tableTest->setItem(i, 1, new QTableWidgetItem(QString::number(i)));
}
}
//...
void MainWindow::on_tableTest_currentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
{
ui->lblColumn->setText(QString::number(current->column()));
ui->lblRow->setText(QString::number(current->row()));
ui->lblCurrentItem->setText(current->text());
ui->lblCurrentCell->setText(ui->tableTest->itemAt(current->row(), current->column())->text());
}
For the item at 1x9, lblCurrentItem displays "9" (as it should,) whereas lblCurrentCell displays "Quux". Am I doing something wrong?

Qt documenration says:
QTableWidgetItem * QTableWidget::itemAt ( int ax, int ay ) const
Returns the item at the position equivalent to QPoint(ax, ay) in the table widget's coordinate system, or returns 0 if the specified point is not covered by an item in the table widget.
See also item().
So you should probably use item(row, column) instead:
ui->lblCurrentCell->setText(ui->tableTest->item(current->row(), current->column())->text());

Looks like your table is getting sorted as per the 0th ("Foo, Bar, ...") column. That way 'Q'uux being at 9, before Waldo makes sense. Either insert the numbers at 0th column or disable sorting or I think you got the point. There are many solutions.

Related

Selecting a QListWidgetItem when adding it

I have a QListWidget in a Form with a QListWidgetItem displaying "Add new". When I click on it, I'd like a serie of things to happen:
A QInputDialog::getText asks for the content of the new item.
A new item is added to the list with the given text.
The list is sorted, except for the "Add new" that stays at the end (this is done by removing the "Add new" item, sorting, and adding the removed item again).
The new item is selected.
That last part is the one I'm having trouble. I've tried many different approaches, all leading to the same result: the item I want selected has a dashed border and it understood as selected (by ui->list->selectedItems() for example), but the selection color stays on the last item before the "Add new".
What I tried
item->setSelected(true);
ui->list->setCurrentItem(item);
ui->list->setCurrentRow(ui->list->row(item);
What I noticed
When the debugger is on with a breakpoint that slowly goes through those steps, I notice everything seems to work nicely, but the UI isn't updated before the function I'm calling is done.
Also, when I want to select a given item from the list from a slot called by another button click, it works correctly with item->setSelected(true); (and the others too).
My guess: I can't select the item in the same function I add it because I can't graphically select something that isn't there yet.
Any guess on how to achieve this?
Note
If you're experiencing the same problem, please read the comment on the selected answer, it was actually a signal problem!
Did you try to select added item and after that set current row to row index of added item. This works in my example.
Example: mainwindow.cpp
#include <QInputDialog>
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->listWidget->addItem("Add New");
connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(slot_itemClicked(QListWidgetItem *)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::slot_itemClicked(QListWidgetItem *item)
{
if (item && (item->text() == "Add New"))
{
QString text = QInputDialog::getText(this, "Input item text", "Text: ");
QListWidgetItem *newItem = new QListWidgetItem(text);
// Add new item and sort list
ui->listWidget->addItem(newItem);
ui->listWidget->sortItems();
// Move "Add New" item to list end
item = ui->listWidget->takeItem(ui->listWidget->row(item));
ui->listWidget->addItem(item);
// Select new item
// Set current row to index of new item row
newItem->setSelected(true);
ui->listWidget->setCurrentRow(ui->listWidget->row(newItem));
}
}
If you can select item from regular slot so just emit dummy signal from very short timer. Like this
//add item
//...
QTimer::singleShot(1, this, SLOT(MySlotForSelectItem())); // 1 ms timer
MainWindow::MySlotForSelectItem()
{
//select item
}

QListWidget horizontal scrollbar causes selection to go out of view

I've asked this question previously and a wonderful person lead me to a decent workaround for the issue. However, I am hoping to see if there is a better solution. One that actually prevents any shifting in my QListWidget entirely.
Working demo example
ListDemo zipfile
http://nexrem.com/test/ListDemo.zip
ListDemo cpp code
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
myListWidget = new QListWidget();
/*
* The signal-slot below is a temporary workaround for the shifting issue.
* This will ensure that the item selected remains in view,
* This is achieved by forcing the item to be in the center of the window;
* however, this has an undesired side-effect of visible 'jumping' as the list
* scrolls to center the item.
*/
//connect (myListWidget, SIGNAL(itemClicked(QListWidgetItem*)), this,
// SLOT(scrollToItem(QListWidgetItem*)));
for (int i = 0; i <= 1000; ++i)
{
QListWidgetItem * myItem = new QListWidgetItem(myListWidget);
QString text("");
for (int i = 0; i <= 40; ++i)
{
text.append("W");
}
myItem->setText(text + QString::number(i));
}
for (int i = 0; i <= 1000; ++i)
{
if (i%2)
myListWidget->item(i)->setHidden(true);
}
auto selected = myListWidget->selectedItems();
if (selected.size() == 1)
{
myListWidget->scrollToItem(selected.front());
}
setCentralWidget(myListWidget);
}
void MainWindow::scrollToItem(QListWidgetItem * item)
{
std::cout << "Scrolling to the item." << std::endl;
myListWidget->scrollToItem(item, QAbstractItemView::PositionAtCenter);
}
The problem:
Whenever I have a QListWidget with horizontal scrollbars and hidden rows present, I get an undesired behaviour that whenever a user clicks on an item, it disappears from view, and the entire list shifts down.
In the example above, I've hidden every other row, to demonstrate this behaviour.
The workaround:
Workaround is having a signal-slot connection that forces the selected item to be scrolled back into view and positioned at the center.
Note, that I have to use PositionAtCenter as EnsureVisible does not work. It thinks the item is visible when it is out of view.
This workaround is acceptable; however, there is visible 'jump' when your selection is forced to be positioned at the center. This is an undesirable side effect.
At this point I am not sure whether this is a QT bug (I don't think having horizontal scrollbar should force your selection out of view) or my code is missing something vital.
The Fix:
As per #G.M.'s comment, all that was missing is myListWidget->setAutoScroll(false);
As mentioned in the comment...
To prevent automatic scrolling on selection disable the autoScroll property. So, in the example code provided do...
myListWidget->setAutoScroll(false);
Note that this property also has an effect when items are dragged over the list view so if you want your list view to act as a drop site then you will probably want to re-enable this property when you get a QDragEnterEvent.

Id of an item in QTreeWidget changed

There is something very strange about Qt.
I have a button ui->addPointButton and a QtreeWidget ui->pointListBox. When I click on the button, it adds a point to the tree. mScenePtr is a pointer to the class I put all my points.AddPoint is a class creating a window asking for some information about the point.
void AddPointsWindow::on_addPointButton_clicked(bool clicked)
{
Q_UNUSED(clicked);
AddPoint addPointWindow(mScenePtr->getColor_or_texture());
int addPointWindowResult = addPointWindow.exec();
if (addPointWindowResult == QDialog::Accepted)
{
SVertex vertex = addPointWindow.getVertex();
mScenePtr->addVertex(vertex);
QTreeWidgetItem* itemPtr = new QTreeWidgetItem(ui->pointListBox);
cout << "id" << ui->pointListBox->indexOfTopLevelItem(itemPtr) << endl;
//itemPtr->setText(0,QString::number(mScenePtr->getVertexNumber()));
//itemPtr->setText(0, QString::number(ui->pointListBox->indexOfTopLevelItem(itemPtr)));
itemPtr->setText(0, "hjhjh");
cout << "id" << ui->pointListBox->indexOfTopLevelItem(itemPtr) << endl;
itemPtr->setText(1, QString::number(vertex.x));
itemPtr->setText(2, QString::number(vertex.y));
itemPtr->setText(3, QString::number(vertex.z));
if (color == mScenePtr->getColor_or_texture())
{
itemPtr->setText(4, QString::number(vertex.r));
itemPtr->setText(5, QString::number(vertex.g));
itemPtr->setText(6, QString::number(vertex.b));
}
//ui->pointListBox->insertTopLevelItem(ui->pointListBox->topLevelItemCount(), itemPtr);
cout << "value : " << vertex.x << endl;
}
}
In this exemple, I click twice on the buttons, create two points with vertex.x = 0 for the first and 1 for the second.
Look at the three lines in the middle :
//itemPtr->setText(0,QString::number(mScenePtr->getVertexNumber()));
//itemPtr->setText(0, QString::number(ui->pointListBox->indexOfTopLevelItem(itemPtr)));
itemPtr->setText(0, "hjhjh");
If there is only the third line, the result is
id0
id0
value : 0
id1
id1
value : 1
Everythong is ok.
But if I put one of the two others lines, the result in both cases is :
id0
id0
value : 0
id1
id0
value : 1
How is it possible ? How can the call to ui->pointListBox->indexOfTopLevelItem(itemPtr) or mScenePtr->getVertexNumber() can change the id of the item ?
Qt 5.5
After using setText, the items in the tree might have sorted automatically.
So in the two commented line cases, when you add the number (using setText), the nodes are getting sorted and the earlier node has become the top level item.
That is the reason you are seeing two different IDs "before setText" and "after setText", when you are querying "top level item".
To see the results properly, Turn off the sorting for the tree. (may be in your constructor)
ui->pointListBox->setSortingEnabled(false);

How to add a string and an int to a QTableView when a button is clicked

How can I add a QSting and an int to a tableView at same time when a button is clicked?
What I want is to have the first column with names and the second column with numbers. Both items need to be added at the same row when a button is clicked.
Col 1--- Col 2---
First Name 1
Second Name 2
Third Name 3
Here is what I have which adds two strings, how can I convert the second cell to be an int?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
model = new QStandardItemModel();
model->setRowCount(0);
ui->tableView->setModel(model);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QStandardItem *userName = new QStandardItem(ui->lineEdit_Name->text());
QStandardItem *userNumber = new QStandardItem(ui->lineEdit_Number->text());
QList<QStandardItem*> row;
row <<userName << userNumber;
model->appendRow(row);
}
Thanks a lot
Does it really have to be an integer or can it just look like an integer?
Conventionally in tables, text is displayed left-aligned and numbers are displayed right-aligned. You can get this effect by setting the alignment for your second column:
QStandardItem *userNumber = new QStandardItem(ui->lineEdit_Number->text());
userNumber->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
If you need to use the data as an integer somewhere else in your program, you can convert the QString to an integer when you need it.
Ok, i will gather all in this answer:
First:
every data in a QStandardItemModel is a QVariant, so you can query the data either
to the model with
virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const
to the QStandardItem with:
virtual QVariant data ( int role = Qt::UserRole + 1 ) const
Both will return a QVariant. You can get the int with
int toInt(bool * ok = 0) const
Note that the bool is optional, but will return true if the conversion was posible.
Also, you can check if it can be converted to an int:
bool QVariant::canConvert(int targetTypeId) const
yourVariant.canConvert( QMetaType::Int ) should return true.
To get what you want. I would use takeRow method from QStandardItemModel:
QList<QStandardItem *> takeRow(int row)
That its, the reverse to what you doing to append the row. So you know that you can ask the second element of the returned QList for the Int value.

Qt - Access a checkbox in a table

I have a table and each row in the table has a checkbox in it's first column. I need to make it so I can detect which checkboxes are checked and delete those rows when a button is pressed.
QWidget * chkWidget = new QWidget();
QHBoxLayout *center = new QHBoxLayout();
center->setAlignment( Qt::AlignCenter );
center->addWidget( new QCheckBox );
chkWidget->setLayout( center );
ui->data_table->setCellWidget(rowCount,0, chkWidget);
Was this done right? If so how do I access the checkboxes at each row?
I talk about a QTableWidget. You can use a QList.You save your QCheckBox into this QList and use it, when there is some change
Maybe you should check out the documentation
QTableWidget: Link
QList: Link
Here is a solution. I cannot run it at the moment, so please tell me if it works. Please validate the row value. I am not sure if it's possible, that row can have the value -1 when you delete the last row ;)
#include "TestTableWidget.h"
#include "ui_TestTableWidget.h"
TestTableWidget::TestTableWidget(QWidget *parent) : QMainWindow(parent), ui(new Ui::TestTableWidget)
{
ui->setupUi(this);
tableWidget = new QTableWidget(this);
tableWidget->setColumnCount(1); // Just an example
ui->gridLayout->addWidget(tableWidget);
connect(tableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotChange()));
for(int i = 1; i < 10; i++)
{
addRow("Row " + QString::number(i));
}
}
TestTableWidget::~TestTableWidget()
{
delete ui;
}
void TestTableWidget::addRow(QString text)
{
int row = tableWidget->rowCount();
qDebug() << "Current row count is " + QString::number(row);
// Add new one
QTableWidgetItem *item = new QTableWidgetItem(text);
tableWidget->insertRow(row);
tableWidget->setItem(row, 0, item);
// Add item to our list
}
void TestTableWidget::slotChange()
{
int row = tableWidget->currentRow();
qDebug() << "Change in table. Current row-index: " + QString::number(row);
// This value is zero-based, so you can use it in our list
}