How to format cells in a QTableWidget - c++

my problem is not so simple for me, because I'm a niewbie in qt, so asking to the experts: the problem, I populate dynamically a QTableWidget from a button on a toolbar, selecting data from a *.csv file.
All works fine but the formatting of the last column text rests on center-vertical position.
I had tryed to change styles or set directly on the code result: the app crash or nothing appens to the last column text.
(And if is possible how to change headers appearance).
Thank You in advance (& sorry for English mistakes).
void ToolBar::setDrawTable(QStringList sl)
{
m_table->clear();
QStringList slHeader = sl.at(0).split(';');
sl.removeAt(0);
m_table->setRowCount(sl.count());
m_table->setColumnCount(slHeader.count());
m_table->setHorizontalHeaderLabels(slHeader);
for (int r= 0; r<sl.count(); r++){
QStringList slRow = sl.at(r).split(';');
for (int c = 0; c < slRow.count(); c++)
{
QTableWidgetItem *item = new QTableWidgetItem(slRow.at(c));
m_table->setItem(r, c, item);
item->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
}
slRow.clear();
}
m_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
}

Related

Fetch values from QWidgets added to QTreeWidgets via QTreeWidgetItems

I've searched many places and found lots of interesting information, but none of that seems to work for what I want. I've tried to follow the solution shown at https://stackoverflow.com/a/9986293/11035837 to no avail.
Basics of my structure: I have a QTreeWidget. I add top level QTreeWidgetItems dynamically (upon the push of a button in a header button box). Each top level QTreeWidgetItem then gets other widgets added to it using:
QTreeWidget* treeWidget = new QTreeWidget;
QTreeWidgetItem* new_record = new QTreeWidgetItem;
QPushButton* add_child = new QPushButton;
QLineEdit* user_input = new QLineEdit;
treeWidget->setItemWidget(new_record,1,add_child);
treeWidget->setItemWidget(new_record,2,user_input);
The add_child button works perfectly. I have a display that inserts all my QLabels, QLineEdits, and QPushButtons in a tree tiered fashion. My buttons work for adding and removing the visual display of items even triggering the visibility of various other elements.
However, I cannot get the user input data out of the QLineEdits to process for anything (such as writing to an output file).
I have my output function iterate through the QTreeWidget:
QTreeWidgetItemIterator iter(treeWidget);
while (*iter)
{
stream.writeStartElement("record");
if ((*iter) != nullptr)
{
for (int i = 0; i < 12; i++)
{
if((*iter)->text(i) != nullptr) stream.writeAttribute("record_name", (*iter)->text(i));
}
}
for (int i = 0; i < 12; i++)
{
if ((*iter) != nullptr && (*iter)->child(i) != nullptr)
{
for (int j = 0; j < 12; j++)
{
if ((*iter)->child(i)->text(j) != nullptr) stream.writeAttribute("record_name", (*iter)->child(i)->text(j));
}
}
}
++iter;
}
This prints as many records with record_name displayed as were created, but it doesn't display any of the other data, because the pointer defined by (*iter)->child(i) is nullptr regardless of i
I then tried using data();
stream.writeAttribute("record_name ", (*iter)->data(2, Qt::UserRole).toString());
This doesn't err out because of nullptr, but it prints out record_name="" rather than record_name="<user_input>"
I'm able to get the user input for QLineEdit widgets that are not in the QTreeWidget, just not the ones in the tree. I assume if I can figure out how to get the data out of the QLineEdits within the tree that I should be able to adapt that to getting the QLineEdits out of a custom QWidget also within the tree.
I found a solution using information from https://www.qtcentre.org/threads/23228-typecast-of-QWidget
stream.writeAttribute("record_name", qobject_cast<QLineEdit*>(treeWidget->itemWidget((*iter), 2))->text() );
The issue was that the QLineEdit widgets were being recalled as QWidgets and not as QLineEdit widgets and as such the text() method/function was not available to it until I cast it back as the desired type.

How to get current row of QTableWidget if I clicked on its child?

I have created a QTableWidget in which I've used setCellWidget(QWidget*). I've set QLineEdit in the cell widget. I've also created a delete button and clicking that button sends a signal to the function deleteRow. I've also used a function currentRow() to get the current row, but it returns -1 because of the QLineEdit. The code snippet is below.
void createTable() {
m_table = new QTableWidget(QDialog); //member variable
for (int i = 0; i < 3; i++)
{
QLineEdit *lineEdit = new QLineEdit(m_table);
m_table->setCellWidget(i, 0, lineEdit);
}
QPushButton *deleteBut = new QPushButton(QDiaolg);
connect(deleteBut, SIGNAL(clicked()), QDialog, SLOT(editRow()));
}
editRow() {
int row = m_table->currentRow(); // This gives -1
m_table->remove(row);
}
In above scenario I click in the QLineEdit and then click on the button delete. Please help me out with a solution.
Just tried it here, it seems that currentRow of the table returns -1 when clicking the button right after program start, and when first selecting a cell, then selecting the QLineEdit and then clicking the button, the correct row is returned.
I would do the following as a workaround: Save the row number in the QLineEdit, e.g. by using QObject::setProperty:
QLineEdit *lineEdit = new QLineEdit(m_table);
lineEdit->setProperty("row", i);
m_table->setCellWidget(i, 0, lineEdit);
Then, in the editRow handler, retrieve the property by asking the QTableWidget for its focused child:
int row = m_table->currentRow();
if (row == -1) {
if (QWidget* focused = m_table->focusWidget()) {
row = focused->property("row").toInt();
}
}
The accepted solution, as is, would not work if rows might get deleted while the program runs. Thus the approach would require to update all the properties. Can be done, if this is a rare operation.
I got away with an iteration approach:
for(unsigned int i = 0; i < table->rowCount(); ++i)
{
if(table->cellWidget(i, relevantColumn) == QObject::sender())
{
return i;
}
}
return -1;
Quick, dirty, but worked, and in my case more suitable, as rows got deleted often or changed their positions, only buttons in the widget were connected to the slot and the slot was never called directly. If these conditions are not met, further checks might get necessary (if(QObject::sender()) { /* */ }, ...).
Karsten's answer will work correctly only if QLineEdit's property is recalculated each time a row is deleted, which might be a lot of work. And Aconcagua's answer works only if the method is invoked via signal/slot mechanism. In my solution, I just calculate the position of the QlineEdit which has focus (assuming all table items were set with setCellWidget):
int getCurrentRow() {
for (int i=0; i<myTable->rowCount(); i++)
for (int j=0; j<myTable->columnCount(); j++) {
if (myTable->cellWidget(i,j) == myTable->focusWidget()) {
return i;
}
}
return -1;
}

Qt 5.3: Accessing/returning/calling checkboxes that are created dynamically(?)

So I have reached my ceiling of knowledge when it comes to Qt and C++ in general I guess. I am creating check boxes in a QScrollArea based off the input from a QComboBox. Depending on the value selected in the QComboBox, a specific number of check boxes are created. Once I created those check boxes, I am having a problem understanding how to interact (in my case, simply check to see if they are checked or not) with them outside of the function they are being created and called in. I know how to work with them if the buttons were static, but since the check boxes are dynamic (is that the right word?) and can change, I don't know what to do. Below is a little snippet of code on how the check boxes are created. If I now want to simply check if any of the boxes are checked, how do I do that. Can I "return" or "call" the created check boxes in another function somehow? I know I'll simply need to loop through the array and check, I just simply don't know how to get the array of check boxes into another function or how to return them in the function below.
Thanks for the help!
void MyProgram::create_checkboxes(QString opnum)
{
QWidget* MDAcheckboxes = new QWidget(ui->MDA);
QVBoxLayout* MDAlayout = new QVBoxLayout(MDAcheckboxes);
QCheckBox *MDAmycheckBox[9];
QList<QString> boxes;
if (opnum == "640")
{
boxes << "16-1" << "16-2";
for (int i = 0; i < 2; i++)
{
MDAmycheckBox[i] = new QCheckBox(MDAcheckboxes);
MDAmycheckBox[i]->setText(boxes[i]);
MDAlayout->addWidget(MDAmycheckBox[i]);
}
ui->MDA->setWidget(MDAcheckboxes);
}
else if (opnum == "645")
{
boxes << "13-01"<<"13-2"<<"13-3"<<"13-4"<<"13-5";
for (int i = 0; i < 5; i++)
{
MDAmycheckBox[i] = new QCheckBox(MDAcheckboxes);
MDAmycheckBox[i]->setText(boxes[i]);
MDAlayout->addWidget(MDAmycheckBox[i]);
}
ui->MDA->setWidget(MDAcheckboxes);
}
else if (opnum == "650")
{
boxes << "13-6"<<"13-7"<<"13-8"<<"13-9"<<"13-10"<<"13-11"<<"13-12"<<"13-13"<<"13-14";
for (int i = 0; i < 9; i++)
{
MDAmycheckBox[i] = new QCheckBox(MDAcheckboxes);
MDAmycheckBox[i]->setText(boxes[i]);
MDAlayout->addWidget(MDAmycheckBox[i]);
}
ui->MDA->setWidget(MDAcheckboxes);
}
}
All your checkBoxes should have a parent. In this case you will be able to find it with findChildren. It also can be done without groupBox if you sure that app has no any other checkboxes and findChildren will not return you checkboxes which you don't need.
Try this:
QList<QCheckBox *> allButtons = ui->groupBox->findChildren<QCheckBox *>();
qDebug() <<allButtons.size();
for(int i = 0; i < allButtons.size(); ++i)
{
if(allButtons.at(i)->isChecked())
qDebug() << "Use" << allButtons.at(i)->text()<< i;//or what you need
}
In general case:
QList<QCheckBox*> allButtons = parentOfCheckBoxes->findChildren<QCheckBox *>();
Moreover findChildren allows you to find children with special objectName which can be useful in some cases. Note that you can set the same objectName to the different objects.
http://qt-project.org/doc/qt-5/qobject.html#findChildren

Copying a (rtf) table into the clipboard via QT (or: Writing a QTextDocument into clipboard)

I need my QT application to create a table and copy this table into the clipboard, so that it can be pasted as table into libreoffice Writer or MS Word later.
My first approach was to create html code for the table and insert it into the clipboard with
QClipboard *clipboard = QApplication::clipboard();
QMimeData *mimeData = new QMimeData();
mimeData->setData("text/html", html.toUtf8());
clipboard->setMimeData(mimeData, QClipboard::Clipboard);
This approach didn't work. When pasting, the table cells where just appended to each other and inserted without formatting.
My second approach using RTF:
QTextDocument rtfDocument;
rtfDocument.setHtml(html);
But I found no way to copy this QTextDocument into the clipboard. Is there any?
If I could get the RTF code out of the QTextDocument, I could use a way like
QClipboard *clipboard = QApplication::clipboard();
QMimeData *mimeData = new QMimeData();
mimeData->setData("text/rtf", rtfDocument.getCode());
clipboard->setMimeData(mimeData, QClipboard::Clipboard);
But I also didn't find a function returning the rtf code.
edit:
With the last code box above I have a working way to copy rtf code into the clipboard. So any solution that can create RTF code representing a table would solve my problem.
I'm not sure what the source of your data is, but here is code we used to subclass the normal QTableView to make it copy-able. Some of the code has been cut out, but you can get the basic idea. RTF/HTML is overkill--all the spreadsheets accept good ol' CSV.
Of course, this answer won't help at all if you require formatting. I wasn't clear from your question if that was a requirement or not.
// Escapes a string according to RFC-4180 specification.
static QString csvEscape(const QString &value) {
if (value.contains(QRegExp(QLatin1String("[\"\\n\\r,]")))) {
QString escaped(value);
escaped.replace(QLatin1String("\""), QLatin1String("\"\""));
return QString::fromLatin1("\"%1\"").arg(escaped);
} else {
return value;
}
}
void ClipboardAwareTableView::Copy() const {
QModelIndexList indexes = selectedIndexes();
Q_ASSERT(!indexes.isEmpty());
if(indexes.isEmpty()) {
return;
}
// The default sort is by rows then columns. This is what we want.
qSort(indexes);
// Remember the mapping between model columns and visible columns. This is
// local instead of an instance member because it would need to be invalidated
// any time a column is added, removed, or moved. The minor performance hit
// is worth the simplicity.
QHash<int, int> map_cache;
// Before we start exporting text, we have to know the index of the left-
// most column in our selection range so we can add the appropriate number
// of column separators.
int minimum_column = GetViewColumnIndex(indexes.first().column(), &map_cache);
for (int i = 1; i < indexes.size(); ++i) {
minimum_column =
qMin(minimum_column,
GetViewColumnIndex(indexes.at(i).column(), &map_cache));
}
// Keep track of the previous index so that we know if we need a new line and
// how many column separators to insert. We start with an invalid index.
QModelIndex previous;
QString text;
for (int i = 0; i < indexes.size(); ++i) {
QModelIndex current = indexes.at(i);
// Do we need to add a new line character?
if (previous.isValid() && current.row() != previous.row()) {
text.append(QLatin1String("\n"));
}
// Are we on a new line?
if (!previous.isValid() || current.row() != previous.row()) {
// Add enough separators to get from the minimum to the current column.
text.append(QString::fromLatin1(",")
.repeated(GetViewColumnIndex(current.column(), &map_cache) -
minimum_column));
} else {
// Add enough separators to get from the previous to the current column.
text.append(QString::fromLatin1(",")
.repeated(GetViewColumnIndex(current.column(), &map_cache) -
GetViewColumnIndex(previous.column(), &map_cache)));
}
// Append the text. If the column delegate is a QStyledItemDelegate, we use
// the display text.
QStyledItemDelegate *delegate =
qobject_cast<QStyledItemDelegate*>(
itemDelegateForColumn(current.column()));
if (delegate) {
text.append(csvEscape(delegate->displayText(current.data(), QLocale())));
} else {
text.append(csvEscape(current.data().toString()));
}
previous = current;
}
qApp->clipboard()->setText(text);
}
int ClipboardAwareTableView::GetViewColumnIndex(
int model_column_index,
QHash<int, int> *cached_mappings) const {
if (cached_mappings->contains(model_column_index)) {
return cached_mappings->value(model_column_index);
}
int view_index = 0;
for (int i = 0; i < model()->columnCount(); ++i) {
if (model_column_index == i) {
cached_mappings->insert(model_column_index, view_index);
return view_index;
} else if (!isColumnHidden(i)) {
++view_index;
}
}
throw std::invalid_argument("model_column_index was out of range.");
}
void ClipboardAwareTableView::keyPressEvent(QKeyEvent *event) {
if (event->matches(QKeySequence::Copy) && !selectedIndexes().isEmpty()) {
Copy();
event->accept();
return; // The base class implementation will overwrite the clipboard.
}
event->ignore();
QTableView::keyPressEvent(event);
}
You could try using QTextDocument::toHtml() and set the mime type to text/html
I wrote in gedit 1[tab space]2[tab space]3\n4[tab space]5[tab space]6 and copied it to spreadsheet and it worked. So, I think if you use "\t" for separating cells in rows and "\n" for separating rows, it will work.

QMaemoListPickSelector not showing anything

Alright I tried using a QMaemo5ListPickSelector together with a QMaemo5ValueButton, but when I click on the button, a popup dialog box does come up, but it doesnt any list ..
Here is a picture of what I mean:
This is the code I'm using to start up the above mentioned two components and to populate the lists:
QMaemo5ValueButton *x = new QMaemo5ValueButton("Testing .. !");
QStandardItemModel model (10,2);
int j,k;
for(j=0;j<=1;j++)
{
k=0;
for(i=0;i<=9;i++)
{
QStandardItem *item = new QStandardItem(QString("%0").arg(k));
k+=5;
model.setItem(i,j,item);
}
}
x->setValueLayout(QMaemo5ValueButton::ValueBesideText);
QMaemo5ListPickSelector *sel = new QMaemo5ListPickSelector();
sel->setModel(&model);
x->setPickSelector(sel);
hbox->addWidget(x);
I would say I'm probably populating the list incorrectly ..