Varför kan man inte sätta en QWidget i en QTableWidget? OOP klurig fråga i C++

Permalänk

Varför kan man inte sätta en QWidget i en QTableWidget? OOP klurig fråga i C++

En C++ fråga angående om QTableWidget.

QTableWidget är en UI-klass för att få en tabell upp på en form. Väldigt smidig klass och enkel att använda...men!
Med QTableWidget så kan man faktiskt ersätta varje cell i en tabell med t.ex. en knapp QPushButton eller något annat kul.

Jag har problem att tillsätta en QWidget som brukar användas som arvsklass närman vill skapa en form.

Låt oss säga att vi har denna kod.

// Vi sätter in UI formen DatabaseMeasurementType() i en cell QTableWidget cellTable.setCellWidget(rows-1, i, new DatabaseMeasurementType()); // Vi plockar fram widget:en från cellen QWidget *p = cellTable.cellWidget(rows-1, i); // Jag kollar om p är null if(p == nullptr) qDebug() << "yes"; // Skriver ut på terminalen

Den visar dessutom yes.

Klassen DatabaseMeasurementType ser ut så här

#include <QWidget> #include <QTableWidgetItem> namespace Ui { class DatabaseMeasurementType; } class DatabaseMeasurementType : public QWidget { Q_OBJECT public: explicit DatabaseMeasurementType(QWidget *parent = nullptr); ~DatabaseMeasurementType(); private: Ui::DatabaseMeasurementType *ui; }; #endif // DATABASEMEASUREMENTTYPE_H

Här ser vi att konstruktören har ett standardargument som är null.

QWidget *parent = nullptr

Kan detta vara orsaken varför qDebug() skriver ut null?
Jag menar, borde jag inte få min form DatabaseMeasurementType i varje cell istället?

Källfilen ser ut så här.

#include "databasemeasurementtype.h" #include "ui_databasemeasurementtype.h" DatabaseMeasurementType::DatabaseMeasurementType(QWidget *parent) : QWidget(parent), ui(new Ui::DatabaseMeasurementType) { ui->setupUi(this); } DatabaseMeasurementType::~DatabaseMeasurementType() { delete ui; }

Permalänk
Hedersmedlem
Skrivet av heretic16:

Här ser vi att konstruktören har ett standardargument som är null.

QWidget *parent = nullptr

Kan detta vara orsaken varför qDebug() skriver ut null?

Nja, det är nog något annat som är fel (sådär brukar det kunna se ut). Fungerar det alltså om du byter till QPushButton utan att ändra något annat?

Citat:

QTableWidget cellTable.setCellWidget(rows-1, i, new DatabaseMeasurementType());

Detta ser lite skumt ut också, eller var det bara för att illustrera?

Permalänk
Skrivet av Elgot:

Nja, det är nog något annat som är fel (sådär brukar det kunna se ut). Fungerar det alltså om du byter till QPushButton utan att ändra något annat?

Detta ser lite skumt ut också, eller var det bara för att illustrera?

Nja.
Om jag byter till QPushButton så kommer det bli en knapp i varje cell.

Ja, för att illustrera.

Permalänk
Hedersmedlem
Skrivet av heretic16:

Nja.
Om jag byter till QPushButton så kommer det bli en knapp i varje cell.

Fungerar din widget fristående (utan tabell)?

Permalänk
Skrivet av Elgot:

Fungerar din widget fristående (utan tabell)?

Ja. Den fungerar om jag anropar ->show();

Permalänk
Hedersmedlem

@heretic16
Jag gjorde ett snabbt test:

Dialog.cpp

#include "dialog.h" #include "./ui_dialog.h" #include <QPushButton> #include "widget1.h" Dialog::Dialog(QWidget *parent) : QDialog(parent) , ui(new Ui::Dialog) { ui->setupUi(this); ui->tableWidget->setRowCount(3); ui->tableWidget->setColumnCount(3); ui->tableWidget->setCellWidget(2, 2, new QPushButton("test")); ui->tableWidget->setCellWidget(1, 1, new Widget1()); } Dialog::~Dialog() { delete ui; }

Widget1.h

#ifndef WIDGET1_H #define WIDGET1_H #include <QWidget> namespace Ui { class Widget1; } class Widget1 : public QWidget { Q_OBJECT public: explicit Widget1(QWidget *parent = nullptr); ~Widget1(); private: Ui::Widget1 *ui; }; #endif // WIDGET1_H

Widget1.cpp

#include "widget1.h" #include "ui_widget1.h" Widget1::Widget1(QWidget *parent) : QWidget(parent), ui(new Ui::Widget1) { ui->setupUi(this); this->setStyleSheet("background: orange;"); this->setAttribute(Qt::WA_StyledBackground, true); } Widget1::~Widget1() { delete ui; }

Ett standard-dialog-projekt (Dialog) med en TableWidget och en standard-widget (Widget1) med orange färg.
Har du några fundamentala skillnader?

Permalänk

Hmmm!

Jag har gjort så här.

#include "databasemeasurementtemplate.h" #include "ui_databasemeasurementtemplate.h" #include "databasemeasurementtype/databasemeasurementtype.h" DatabaseMeasurementTemplate::DatabaseMeasurementTemplate(QWidget *parent) : QWidget(parent), ui(new Ui::DatabaseMeasurementTemplate) { ui->setupUi(this); } DatabaseMeasurementTemplate::~DatabaseMeasurementTemplate() { delete ui; } void DatabaseMeasurementTemplate::on_addRowPushButton_clicked() { ui->templateTableWidget->setRowCount(++rows); for(int i = 0; i < columns; i++) ui->templateTableWidget->setCellWidget(rows-1, i, new QPushButton(QString::number(rows-1) + "x" + QString::number(i))); } void DatabaseMeasurementTemplate::on_appendColumnPushButton_clicked() { ui->templateTableWidget->setColumnCount(++columns); for(int i = 0; i < rows; i++) ui->templateTableWidget->setCellWidget(i, columns-1, new QPushButton(QString::number(i) + "x" + QString::number(columns-1))); } void DatabaseMeasurementTemplate::on_removeColumnPushButton_clicked() { QModelIndexList selectedColumn = ui->templateTableWidget->selectionModel()->selectedColumns(); for(int i = 0; i < selectedColumn.length(); i++) ui->templateTableWidget->removeColumn(selectedColumn.at(i).column()); } void DatabaseMeasurementTemplate::on_deleteRowPushButton_clicked() { QModelIndexList selectedRows = ui->templateTableWidget->selectionModel()->selectedRows(); for(int i = 0; i < selectedRows.length(); i++) ui->templateTableWidget->removeRow(selectedRows.at(i).row()); }

Då får jag detta. Jag har råkat klicka på knapp 1x2. Därför är den markerad med lite orange färg.

Så om jag byter ut "new QPushButton()" så det ser ut så här:

void DatabaseMeasurementTemplate::on_addRowPushButton_clicked() { ui->templateTableWidget->setRowCount(++rows); for(int i = 0; i < columns; i++) ui->templateTableWidget->setCellWidget(rows-1, i, new DatabaseMeasurementType()); } void DatabaseMeasurementTemplate::on_appendColumnPushButton_clicked() { ui->templateTableWidget->setColumnCount(++columns); for(int i = 0; i < rows; i++) ui->templateTableWidget->setCellWidget(i, columns-1, new DatabaseMeasurementType()); }

Jag lägger även till ett klick-event.

void DatabaseMeasurementTemplate::on_templateTableWidget_doubleClicked(const QModelIndex &index) { ui->templateTableWidget->cellWidget(index.row(), index.column())->show(); }

För varje gång denna körs och jag klickar på en cell. Då händer det INGET. Inget nullptr verkar det som. Varför får jag inte upp mitt fönster när jag klickar på cellen?

DatabaseMeasurementType har koden.

#include "databasemeasurementtype.h" #include "ui_databasemeasurementtype.h" DatabaseMeasurementType::DatabaseMeasurementType(QWidget *parent) : QWidget(parent), ui(new Ui::DatabaseMeasurementType) { ui->setupUi(this); } DatabaseMeasurementType::~DatabaseMeasurementType() { delete ui; }

#ifndef DATABASEMEASUREMENTTYPE_H #define DATABASEMEASUREMENTTYPE_H #include <QWidget> #include <QTableWidgetItem> namespace Ui { class DatabaseMeasurementType; } class DatabaseMeasurementType : public QWidget { Q_OBJECT public: explicit DatabaseMeasurementType(QWidget *parent = nullptr); ~DatabaseMeasurementType(); private: Ui::DatabaseMeasurementType *ui; }; #endif // DATABASEMEASUREMENTTYPE_H

<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>DatabaseMeasurementType</class> <widget class="QWidget" name="DatabaseMeasurementType"> <property name="windowModality"> <enum>Qt::ApplicationModal</enum> </property> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> </widget> <resources/> <connections/> </ui>

Permalänk
Hedersmedlem
Skrivet av heretic16:

<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>DatabaseMeasurementType</class> <widget class="QWidget" name="DatabaseMeasurementType"> <property name="windowModality"> <enum>Qt::ApplicationModal</enum> </property> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> </widget> <resources/> <connections/> </ui>

Vad händer om du lägger till innehåll (en knapp eller bakgrundsfärg till exempel)? Det är inte så att det fungerar fast bara inte syns?
När man kör setcellwidget kommer troligen kontrollen tilldelas en ny ägare, så show kommer nog inte ge ett nytt fönster längre utan snarare visa kontrollen i tabellen (men den borde ju redan vara synlig).

Permalänk
Skrivet av Elgot:

Vad händer om du lägger till innehåll (en knapp eller bakgrundsfärg till exempel)? Det är inte så att det fungerar fast bara inte syns?
När man kör setcellwidget kommer troligen kontrollen tilldelas en ny ägare, så show kommer nog inte ge ett nytt fönster längre utan snarare visa kontrollen i tabellen (men den borde ju redan vara synlig).

Ja. Nu när jag lägger till en liten komponent så syns den i cellen.
Men det är som du säger ->show() anropas inte och fungerar inte.

Hur skulle det vara om jag vill ha en 2D matris där jag anropar x,y för att få fram min cell?
QTableWidget fungerar ju inte som en 2D matris.

Permalänk

Vad tror du om min lösning nu?

Jag användern en QMap<QString, MinForm*> matrix där varje nyckel är en koordinat på formen "Rad x Kolumn".

#include "databasemeasurementtemplate.h" #include "ui_databasemeasurementtemplate.h" #include "databasemeasurementtype/databasemeasurementtype.h" DatabaseMeasurementTemplate::DatabaseMeasurementTemplate(QWidget *parent) : QWidget(parent), ui(new Ui::DatabaseMeasurementTemplate) { ui->setupUi(this); } DatabaseMeasurementTemplate::~DatabaseMeasurementTemplate() { delete ui; } void DatabaseMeasurementTemplate::on_addRowPushButton_clicked() { ui->templateTableWidget->setRowCount(++rows); for(int i = 0; i < columns; i++) matrix[QString::number(rows-1) + "x" + QString::number(i)] = new DatabaseMeasurementType(); } void DatabaseMeasurementTemplate::on_appendColumnPushButton_clicked() { ui->templateTableWidget->setColumnCount(++columns); for(int i = 0; i < rows; i++) matrix[QString::number(i) + "x" + QString::number(columns-1)] = new DatabaseMeasurementType(); } void DatabaseMeasurementTemplate::on_removeColumnPushButton_clicked() { QModelIndexList selectedColumn = ui->templateTableWidget->selectionModel()->selectedColumns(); for(int i = 0; i < selectedColumn.length(); i++){ int column = selectedColumn.at(i).column(); for(int j = 0; j < rows; j++) matrix.remove(QString::number(j) + "x" + QString::number(column)); ui->templateTableWidget->removeColumn(column); columns--; } if(ui->templateTableWidget->columnCount() == 0){ matrix.clear(); ui->templateTableWidget->setColumnCount(0); ui->templateTableWidget->setRowCount(0); rows = columns = 0; } } void DatabaseMeasurementTemplate::on_deleteRowPushButton_clicked() { QModelIndexList selectedRows = ui->templateTableWidget->selectionModel()->selectedRows(); for(int i = 0; i < selectedRows.length(); i++){ int row = selectedRows.at(i).row(); for(int j = 0; j < columns; j++) matrix.remove(QString::number(row) + "x" + QString::number(j)); ui->templateTableWidget->removeRow(row); rows--; } if(ui->templateTableWidget->rowCount() == 0){ matrix.clear(); ui->templateTableWidget->setColumnCount(0); ui->templateTableWidget->setRowCount(0); rows = columns = 0; } } void DatabaseMeasurementTemplate::on_templateTableWidget_doubleClicked(const QModelIndex &index) { matrix[QString::number(index.row()) + "x" + QString::number(index.column())]->show(); }

Permalänk
Hedersmedlem

@heretic16
Jag vet inte exakt vad du har för behov, men det moderna sättet att presentera data i listor och tabeller är egentligen view-klasserna (QTableView till exempel). Där behöver man inte skapa varje element och lägga in i en lista, utan har istället data och regler för hur den skall presenteras vid behov.

Om man istället vill kunna placera kontroller exakt där man vill kan man lägga dem i en QGraphicsScene.

Permalänk
Skrivet av Elgot:

@heretic16
Jag vet inte exakt vad du har för behov, men det moderna sättet att presentera data i listor och tabeller är egentligen view-klasserna (QTableView till exempel). Där behöver man inte skapa varje element och lägga in i en lista, utan har istället data och regler för hur den skall presenteras vid behov.

Om man istället vill kunna placera kontroller exakt där man vill kan man lägga dem i en QGraphicsScene.

Mitt behov är att varje cell skall vara länkad till en form. Klickar jag på cellen, så kommer det upp en unik form som jag kan skriva värden till.
I detta fall så kommer varje form ha samma struktur.

Permalänk
Hedersmedlem
Skrivet av heretic16:

Mitt behov är att varje cell skall vara länkad till en form. Klickar jag på cellen, så kommer det upp en unik form som jag kan skriva värden till.
I detta fall så kommer varje form ha samma struktur.

Man skulle ju till exempel kunna använda QPushButton-lösningen och koppla varje knapp till att visa (och eventuellt först skapa) din kontroll.

Permalänk
Skrivet av Elgot:

Man skulle ju till exempel kunna använda QPushButton-lösningen och koppla varje knapp till att visa (och eventuellt först skapa) din kontroll.

Hur skulle man kunna göra det?
Vore intressant. Just nu har jag en lösning på problemet. Men finns det bättre lösning = mindre kod, så väljer jag den vägen också.

Just nu ser det ut så här. Denna cellItem är bara till för att jag ska kunna få min klickhändelse att fungera när jag klickar på en cell. Lägger jag dit en knapp i cellen, så fungerar inte min klickhändelse. Det cellItem gör, är att jag kan placera en text på den i formen DatabaseMeasurementType.

QTableWidgetItem *cellItem = new QTableWidgetItem();

#include "databasemeasurementtemplate.h" #include "ui_databasemeasurementtemplate.h" #include "databasemeasurementtype/databasemeasurementtype.h" DatabaseMeasurementTemplate::DatabaseMeasurementTemplate(QWidget *parent) : QWidget(parent), ui(new Ui::DatabaseMeasurementTemplate) { ui->setupUi(this); } DatabaseMeasurementTemplate::~DatabaseMeasurementTemplate() { delete ui; } void DatabaseMeasurementTemplate::on_addRowPushButton_clicked() { ui->templateTableWidget->setRowCount(++rows); for(int i = 0; i < columns; i++){ QTableWidgetItem *cellItem = new QTableWidgetItem(); DatabaseMeasurementType *cell = new DatabaseMeasurementType(nullptr, cellItem); matrix[QString::number(rows-1) + "x" + QString::number(i)] = cell; ui->templateTableWidget->setItem(rows-1, i, cellItem); } } void DatabaseMeasurementTemplate::on_appendColumnPushButton_clicked() { ui->templateTableWidget->setColumnCount(++columns); for(int i = 0; i < rows; i++){ QTableWidgetItem *cellItem = new QTableWidgetItem(); DatabaseMeasurementType *cell = new DatabaseMeasurementType(nullptr, cellItem); matrix[QString::number(i) + "x" + QString::number(columns-1)] = cell; ui->templateTableWidget->setItem(i, columns-1, cellItem); } } void DatabaseMeasurementTemplate::on_removeColumnPushButton_clicked() { QModelIndexList selectedColumn = ui->templateTableWidget->selectionModel()->selectedColumns(); for(int i = 0; i < selectedColumn.length(); i++){ int column = selectedColumn.at(i).column(); for(int j = 0; j < rows; j++) matrix.remove(QString::number(j) + "x" + QString::number(column)); ui->templateTableWidget->removeColumn(column); columns--; } /* Delete all when there are no columns */ if(ui->templateTableWidget->columnCount() == 0){ matrix.clear(); ui->templateTableWidget->setColumnCount(0); ui->templateTableWidget->setRowCount(0); rows = columns = 0; } } void DatabaseMeasurementTemplate::on_deleteRowPushButton_clicked() { QModelIndexList selectedRows = ui->templateTableWidget->selectionModel()->selectedRows(); for(int i = 0; i < selectedRows.length(); i++){ int row = selectedRows.at(i).row(); for(int j = 0; j < columns; j++) matrix.remove(QString::number(row) + "x" + QString::number(j)); ui->templateTableWidget->removeRow(row); rows--; } /* Delete all when there are no rows */ if(ui->templateTableWidget->rowCount() == 0){ matrix.clear(); ui->templateTableWidget->setColumnCount(0); ui->templateTableWidget->setRowCount(0); rows = columns = 0; } } void DatabaseMeasurementTemplate::on_templateTableWidget_clicked(const QModelIndex &index) { matrix[QString::number(index.row()) + "x" + QString::number(index.column())]->show(); }

Permalänk
Hedersmedlem
Skrivet av heretic16:

Hur skulle man kunna göra det?
Vore intressant. Just nu har jag en lösning på problemet. Men finns det bättre lösning = mindre kod, så väljer jag den vägen också.

I mitt exempel ovan borde det kunna bli såhär:

QPushButton* b = new QPushButton("test"); connect(b, &QPushButton::clicked, [=]() { Widget1* w = new Widget1(); w1->show();}); ui->tableWidget->setCellWidget(2, 2,b);

Permalänk
Skrivet av Elgot:

I mitt exempel ovan borde det kunna bli såhär:

QPushButton* b = new QPushButton("test"); connect(b, &QPushButton::clicked, [=]() { Widget1* w = new Widget1(); w1->show();}); ui->tableWidget->setCellWidget(2, 2,b);

Tackar!
Detta löste problemet!

QPushButton* cellButton = new QPushButton("Empty"); DatabaseMeasurementType* cellForm = new DatabaseMeasurementType(nullptr, cellButton); connect(cellButton, &QPushButton::clicked, [=]() { cellForm->show();}); ui->templateTableWidget->setCellWidget(i, columns-1, cellButton);

Helt fantastiskt med lambda-funktioner. Har typ aldrig använt mig av dessa, men dom verkar riktigt bra. Har noll koll på hur dom fungerar, men denna kod förstår jag.

Dessutom tycker jag att QT har verkligen gjort C++ till ett riktigt högnivåspråk. I nivå med Java typ.
Det skulle bara fattas ett LombokProject för C++. Då hade QT varit helt optimalt.

Permalänk
Hedersmedlem
Skrivet av heretic16:

Helt fantastiskt med lambda-funktioner.

Instämmer. När man väl har vant sig undrar man hur man klarade sig innan.
I just detta fall, om man bara behöver koppla ihop två redan existerande objekt, kan man dock förenkla det till

connect(cellButton, &QPushButton::clicked, cellForm, &QWidget::show);

Citat:

Dessutom tycker jag att QT har verkligen gjort C++ till ett riktigt högnivåspråk. I nivå med Java typ.
Det skulle bara fattas ett LombokProject för C++. Då hade QT varit helt optimalt.

Ja, utöver att erbjuda ett (eller två, faktiskt) trevligt gui-ramverk fyller det i många av de luckor som c++-standarden lämnar till användaren. QMetaObject-grejerna tänjer ju också nästan på vad man trodde var möjligt att göra i c++.

Lombok har jag dålig koll på; vad är det man skulle vilja ha därifrån?