QTableWidget, Cellwidget, QLabel - c++

I've created a QTableWidget. Some of the cells are filled with a cell widget (a modified QLabel, sending a clicked signal).
Now, I want to react on a click on this label. I add some debugging functions.
Clicking on an empty cell writes the correct row and column to console.
Clicking on the label is reckognized as a click, but with the wrong row and column (the previous cell data is used).
Question: How can I get the correct row and column for clicking on the label.
Greetings and thanks, Michael
MtTimeTable::MtTimeTable( QWidget* parent )
: QTableWidget { parent }, mIdArray { MtIdArray() },
mDate { QDate::currentDate() }
{
this->fillLesssonWidgets();
connect( this, &MtTimeTable::cellClicked,
this, &MtTimeTable::slotCellActivated );
}
void MtTimeTable::fillLessonWidgets()
{
MtLessonVec lessonVec { true }; // Data to show, loaded from file
auto cit { lessonVec.begin() };
while( cit != lessonVec.end() ) {
// Class MtLessonWidget derived from QLabel
MtLessonWidget* lessonWidget { new MtLessonWidget };
lessonWidget->setLesson( *cit );
// Using member functions of MtLessonwidget, working correct
this->setCellWidget( lessonWidget->startingRow(),
lessonWidget->startingCol(),
lessonWidget );
}
connect( lessonWidget, &MtLessonWidget::sigClicked,
this, &MtTimeTable::slotLessonWidgetClicked );
++cit;
}
}
I've tried to reduce the code to a minimum.
void MtTimeTable::slotLessonWidgetClicked()
{
std::cerr << "Table Cell: [" << this->currentRow() << ","
<< this->currentColumn() << "]" << std::endl;
}

According to the docs:
int QTableWidget::currentColumn() const
Returns the column of the current item.
int QTableWidget::currentRow() const
Returns the row of the current item.
That is, it is the position of the item, and it refers to a QTableWidgetItem, but if we use setCellWidget an item is not created, so those positions are not suitable, we must look for another means to obtain the row and column associated with the widget.
One way is to use the indexAt() method that returns a QModelIndex associated with the cell given its position relative to the viewport() of QTableWidget, and that is the one that should be used:
void MtTimeTable::slotLessonWidgetClicked(){
auto lbl = qobject_cast<MtLessonWidget *>(sender());
if(lbl){
auto ix = indexAt(lbl->pos());
qDebug()<<"Table Cell: [" <<ix.row()<< "," <<ix.column()<< "]";
}
}
Complete Example:
#include <QApplication>
#include <QLabel>
#include <QTableWidget>
#include <QDebug>
class CustomLabel: public QLabel{
Q_OBJECT
protected:
void mousePressEvent(QMouseEvent *){
emit clicked();
}
signals:
void clicked();
};
class TableWidget: public QTableWidget{
Q_OBJECT
public:
TableWidget(QWidget* parent=Q_NULLPTR):QTableWidget(parent){
setRowCount(10);
setColumnCount(10);
for(int i=0; i<rowCount(); i++){
for(int j=0; j<columnCount(); j++){
auto lbl = new CustomLabel;
setCellWidget(i, j, lbl);
connect(lbl, &CustomLabel::clicked, this, &TableWidget::onClicked);
}
}
}
private slots:
void onClicked(){
auto lbl = qobject_cast<CustomLabel *>(sender());
if(lbl){
auto ix = indexAt(lbl->pos());
qDebug()<<"Table Cell: [" <<ix.row()<< "," <<ix.column()<< "]";
}
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TableWidget w;
w.show();
return a.exec();
}
In the following link I show the complete example.

Related

QTableWidget to multiple files

I’ve got QTableWidget with data like this:
table.png
The table can contains only names from the QList:
QList<QString> shapes { "Triangle", "Circle", "Trapeze", "Square", "Rectangle", "Diamond" };
with random int values in the neighboring cell.
Table can contain all "shapes" or only a part of it (like in the example).
I try to create separate file for each shape form the table and write down corresponding int values to them.
To achieve this I wrote something like that:
QList<QTableWidgetItem *> ItemList
/.../
for(int i = 0; i < rows; ++i)
{
for(int i = 0; i<columns; ++i)
{
foreach(QString itm, shapes )
{
ItemList = ui->tableWidget->findItems(itm, Qt::MatchExactly);
QFile mFile(itm + ".txt");
if(mFile.open(QFile::ReadWrite))
{
for(int i = 0; i < ItemList.count(); ++i)
{
int rowNR = ItemList.at(i)->row();
int columnNR = ItemList.at(i)->column();
out << "Values = " << ui->tableWidget->item(rowNR, columnNR+1)->text() << endl;
}
}
}
mFile.flush();
mFile.close();
}
}
Files are created for every item from the QList – if the shape from the QList is not in the table, an empty file is created.
How to create files only on the basis of available names in the table?
You can write like this.
QList<QTableWidgetItem *> ItemList
/.../
for(QString str : Shapes){
ItemList = ui->tableWidget->findItems(itm, Qt::MatchExactly); // Get the matching list
if(ItemList.isEmpty(){
continue; // If shape does not exist in table skip the iteration
}
QFile mFile(str + ".txt");
if(!mFile.open(QFile::ReadWrite){
return; // This should not happen ; this is error
}
for(QTableWidgetItem *item : ItemList){
int row = item->row();
int col = item->column()+1; // since it is neighboring cell
QString Value = ui->tableWidget->item(row,col)->text();
mFile.write(Value.toUtf8()); // You can change the way in which values are written
}
mFile.flush();
mFile.close();
}

How to close a dialog with an OK button using a condition [duplicate]

This question already has answers here:
How can I prevent QDialog class from closing
(3 answers)
Closed 5 years ago.
My code
my first attempt so I could get the result in Mainwindow.cpp but it could be //incorrect
void Dialog::on_buttonBox_accepted()
{
Cities.clear();
for(int row = 0; row<ui->tableWidget->rowCount(); row++)
{
cities s(get_city(row),get_time(row,3),get_time(row,4));
Cities.push_back(s);
}
}
// my attempt to make another button but I could not get a result in
//Mainwindow.cpp but could check correctly
void Dialog::on_pushButton_clicked()
{
if(cities_is_filled())
{
Cities.clear();
for(int row = 0; row<ui->tableWidget->rowCount(); row++)
{
cities s(get_city(row),get_time(row,3),get_time(row,4));
Cities.push_back(s);
}
}
}
//GET RESULT
void MainWindow::on_actionAdd_train_triggered()
{
Dialog e;
if(e.exec())
{
for(auto City: e.Cities)
{
ui->textBrowser->append(City.city_+ " " + City.depart_+ " "
+City.leave_);
}
}
}
If the table cell is empty when I press OK, the dialog closes - but I would like it to not close. How can I implement this?
Press right click mouse on your buttonBox then select Go To Slot... then choose accepted() slot.
Now add your condition to the accepted function:
void MainWindow::on_buttonBox_accepted()
{
if(ui->tableWidget->item(1,2)->text() != "") // for example
{
}
else
{
qApp->exit();
}
}
QDialogButtonBox Class

QT/C++ Access QLineEdit/ QTextArea functions using QWidget [duplicate]

I am working in Qt 4.7, and I have a QWidget object in my dialog. I need to go through each of the children and extract the current text into a QStringList. Every other object is a QCheckBox, and the rest are QComboBoxes (I would just need the text portion of both). So far the only way I could think of to do this would be to use the children() function to get them as QObject*'s and cast them, like this:
QStringList textlist;
for(int i = 0; i < ui->myWidget->children().size(); i++)
{
if(i % 2 == 0)
{
QCheckBox *q = (QCheckBox *)ui->myWidget->children().at(i);
textlist.append(q->text());
}
else
{
QComboBox *q = (QComboBox *)ui->myWidget->children().at(i);
textlist.append(q->currentText());
}
}
However, when I try to use this, it builds and compiles fine, but then crashes when it's run. I checked and both classes are subclasses (albeit indirectly through QAbstractButton and QWidget) of QObject, which is the type of the objects in the list ui->myWidget->children(), so I feel like they should be able to cast this way. I haven't worked much with this kind of thing before so I'm not sure if there's a better way to do this. If anyone has any ideas, it would be greatly appreciated. Thanks!
UPDATE: So, I can't get the casting to work this way or with the qobject_cast. I HAVE however discovered that I can go from QObject to QWidget, and I think I should be able to go from QWidget to the needed objects with dynamic_cast, but that doesn't work either. Right now I have this:
QStringList textlist;
for(int i = 0; i < ui->myWidget->children().size(); i++)
{
QWidget *qw = qobject_cast<QWidget*>(ui->myWidget->children().at(i)
if(i % 2 == 0)
{
QComboBox *q = dynamic_cast<QComboBox*>(qw);
if(q)
{
textlist.append(q->text());
}
}
else
{
QCheckBox *q = dynamic_cast<QCheckBox*>(qw);
if(q)
{
textlist.append(q->currentText());
}
}
}
If anyone has any ideas, I'd appreciate the help. Thank you.
UPDATE2: I haven't found much online that helps with this still, so I may as well ask as well, if there is anyway to do this WITHOUT casting, i.e. getting the objects directly from the QWidget in their original type, I would really appreciate that as well. I'm not heartset on my current strategy or anything, it was just the only way I could think to do it - I'll take anything that works at this point.
You should think about using qobject_cast. After the cast you should also check if the object is valid.
QStringList textlist;
for(int i = 0; i < ui->myWidget->children().size(); i++)
{
if(i % 2 == 0)
{
QCheckBox *q = qobject_cast<QCheckBox*>(ui->myWidget->children().at(i));
if(q)
textlist.append(q->text());
}
else
{
QComboBox *q = qobject_cast<QComboBox*>(ui->myWidget->children().at(i));
if(q)
textlist.append(q->currentText());
}
}
But this still seems like a bad design to me. The widget class that contains the comboboxes and checkboxes should have a function, that goes through the checkboxes and comboboxes and returns a QStringList. Then you could just call that function.
Here is an example
mywidget.h:
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
QStringList getComboTextList();
QStringList getCheckBoxTextList();
private:
Ui::MyWidget *ui;
QList<QComboBox*> comboList;
QList<QCheckBox*> checkList;
};
 
mywidget.cpp:
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
this->setLayout(new QVBoxLayout);
for(int i = 0; i < 5; i++)
{
QCheckBox *checkBox = new QCheckBox(this);
this->layout()->addWidget(checkBox);
checkBox->setText(QString("Check box #%1").arg(i));
checkList.append(checkBox);
}
for(int i = 0; i < 5; i++)
{
QComboBox *comboBox = new QComboBox(this);
this->layout()->addWidget(comboBox);
comboBox->addItem("Combo box item 1");
comboBox->addItem("Combo box item 2");
comboList.append(comboBox);
}
}
MyWidget::~MyWidget()
{
delete ui;
}
QStringList MyWidget::getComboTextList()
{
QStringList returnList;
for(int i = 0; i < comboList.length(); i++)
{
returnList << comboList[i]->currentText();
}
return returnList;
}
QStringList MyWidget::getCheckBoxTextList()
{
QStringList returnList;
for(int i = 0; i < checkList.length(); i++)
{
returnList << checkList[i]->text();
}
return returnList;
}
Then in your other class you can just call getCheckBoxTextList or getComboTextList like this:
QStringList comboTextList = myWidget->getComboBoxList();
QStringList checkTextList = myWidget->getCheckBoxTextList();

C++ How to enumerate QGraphicsItem's?

Using this function, I can delete selected QGraphicsItem's from a QGraphicsView.
How can I get my ellipses enumerated in order to receive a notification like "Deleted ellipse n°...".
void MainWindow::deleteItem()
{
foreach (QGraphicsItem *item, scene->selectedItems()) {
if (item->type() == ellipse->Type) {
scene->removeItem(item);
delete item;
QMessageBox::information(this,"Notification", "Deleted");
}
}
}
A few ways
Assuming you only care about that iteration:
int ix = 0; // add this
foreach (QGraphicsItem *item, scene->selectedItems()) {
if (item->type() == ellipse->Type) {
scene->removeItem(item);
delete item;
std::cout << "Deleted ellipse number " << ix++ << std::endl; // and add this
QMessageBox::information(this,"Notification", "Deleted");
}
}
The above only works if your ordering only corresponds to that foreach() loop. If your items are in some arbitrary order:
std::unordered_map<QGraphicsItem*, int> mGraphicsItems;
That's assuming you can populate it, of course. If you can, do a lookup before calling delete(), to get the value which is the enumeration. Not very elegant though, and adds space.
Other way is to subclass QGraphicsItem [Untested code but you get the idea]
class MyGraphicsItem : public QGraphicsItem
{
Q_OBJECT
public:
// snip
int index() const { return mIndex; }
void setIndex( int i ) { mIndex = i; }
private:
int mIndex;
};
Just set the index in whatever way you want when the QGraphicsItem is created, and before calling delete, print out (or do whatever) with item->index();

Qt - How to convert from QObject to UI elements?

I am working in Qt 4.7, and I have a QWidget object in my dialog. I need to go through each of the children and extract the current text into a QStringList. Every other object is a QCheckBox, and the rest are QComboBoxes (I would just need the text portion of both). So far the only way I could think of to do this would be to use the children() function to get them as QObject*'s and cast them, like this:
QStringList textlist;
for(int i = 0; i < ui->myWidget->children().size(); i++)
{
if(i % 2 == 0)
{
QCheckBox *q = (QCheckBox *)ui->myWidget->children().at(i);
textlist.append(q->text());
}
else
{
QComboBox *q = (QComboBox *)ui->myWidget->children().at(i);
textlist.append(q->currentText());
}
}
However, when I try to use this, it builds and compiles fine, but then crashes when it's run. I checked and both classes are subclasses (albeit indirectly through QAbstractButton and QWidget) of QObject, which is the type of the objects in the list ui->myWidget->children(), so I feel like they should be able to cast this way. I haven't worked much with this kind of thing before so I'm not sure if there's a better way to do this. If anyone has any ideas, it would be greatly appreciated. Thanks!
UPDATE: So, I can't get the casting to work this way or with the qobject_cast. I HAVE however discovered that I can go from QObject to QWidget, and I think I should be able to go from QWidget to the needed objects with dynamic_cast, but that doesn't work either. Right now I have this:
QStringList textlist;
for(int i = 0; i < ui->myWidget->children().size(); i++)
{
QWidget *qw = qobject_cast<QWidget*>(ui->myWidget->children().at(i)
if(i % 2 == 0)
{
QComboBox *q = dynamic_cast<QComboBox*>(qw);
if(q)
{
textlist.append(q->text());
}
}
else
{
QCheckBox *q = dynamic_cast<QCheckBox*>(qw);
if(q)
{
textlist.append(q->currentText());
}
}
}
If anyone has any ideas, I'd appreciate the help. Thank you.
UPDATE2: I haven't found much online that helps with this still, so I may as well ask as well, if there is anyway to do this WITHOUT casting, i.e. getting the objects directly from the QWidget in their original type, I would really appreciate that as well. I'm not heartset on my current strategy or anything, it was just the only way I could think to do it - I'll take anything that works at this point.
You should think about using qobject_cast. After the cast you should also check if the object is valid.
QStringList textlist;
for(int i = 0; i < ui->myWidget->children().size(); i++)
{
if(i % 2 == 0)
{
QCheckBox *q = qobject_cast<QCheckBox*>(ui->myWidget->children().at(i));
if(q)
textlist.append(q->text());
}
else
{
QComboBox *q = qobject_cast<QComboBox*>(ui->myWidget->children().at(i));
if(q)
textlist.append(q->currentText());
}
}
But this still seems like a bad design to me. The widget class that contains the comboboxes and checkboxes should have a function, that goes through the checkboxes and comboboxes and returns a QStringList. Then you could just call that function.
Here is an example
mywidget.h:
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
QStringList getComboTextList();
QStringList getCheckBoxTextList();
private:
Ui::MyWidget *ui;
QList<QComboBox*> comboList;
QList<QCheckBox*> checkList;
};
 
mywidget.cpp:
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
this->setLayout(new QVBoxLayout);
for(int i = 0; i < 5; i++)
{
QCheckBox *checkBox = new QCheckBox(this);
this->layout()->addWidget(checkBox);
checkBox->setText(QString("Check box #%1").arg(i));
checkList.append(checkBox);
}
for(int i = 0; i < 5; i++)
{
QComboBox *comboBox = new QComboBox(this);
this->layout()->addWidget(comboBox);
comboBox->addItem("Combo box item 1");
comboBox->addItem("Combo box item 2");
comboList.append(comboBox);
}
}
MyWidget::~MyWidget()
{
delete ui;
}
QStringList MyWidget::getComboTextList()
{
QStringList returnList;
for(int i = 0; i < comboList.length(); i++)
{
returnList << comboList[i]->currentText();
}
return returnList;
}
QStringList MyWidget::getCheckBoxTextList()
{
QStringList returnList;
for(int i = 0; i < checkList.length(); i++)
{
returnList << checkList[i]->text();
}
return returnList;
}
Then in your other class you can just call getCheckBoxTextList or getComboTextList like this:
QStringList comboTextList = myWidget->getComboBoxList();
QStringList checkTextList = myWidget->getCheckBoxTextList();