March 4, 2011

d2uriel d2uriel
Lab Rat
80 posts

[Solved] QTableWidgetItem::setFont() - how to do it properly

 

Dear Qt programmers,

Probably a simple question but I haven’t found an answer on these forums nor on Google. So here I go.

I have a QTableWidget object with QTableWidgetItem’s inside. I’d like to change selected items font type to what the user selected. Say I have 3 buttons: bold, italic and underline. If I click bold, font in the current cell should change to bold. If I click italic afterwards, it should be bold italic.
If I do it like this:
@void myTableEditor::setBold()
{ QList<QTableWidgetItem *> list = currentTable()->selectedItems(); QList<QTableWidgetItem *>::iterator it; for(it = list.begin(); it != list.end(); ++it) { QFont originalFont = (*it)->font(); originalFont.setBold(true); (*it)->setFont(originalFont); }
}@then I cannot retrieve information about this font via
@(*it)->font().bold();@method. It returns false even if the font was set to bold. I guess it’s because I instantiate a QFont object locally in this method and after returning to main event loop it’s being destroyed.

I wanted to do something else:
@void myTableEditor::setBold()
{ QList<QTableWidgetItem *> list = currentTable()->selectedItems(); QList<QTableWidgetItem *>::iterator it; for(it = list.begin(); it != list.end(); ++it) { QFont *newFont = new QFont((*it)->font()); newFont->setBold(true); (*it)->setFont(*newFont); }
}@but then I don’t know what happens when I will set the font to bold a couple of times. Will setFont() destroy the previous QFont object used? Or do I need to take care of it myself? If so, what’s the smart way of doing that? Creating a
@QVector <QVector <QFont *>> fonts;@2D array which will hold pointers to QFont objects for every cell?

Thank you for your suggestions.

 Signature 

“Do not judge, and you will never be mistaken.” - Jean-Jacques Rousseau

8 replies

March 4, 2011

Andre Andre
Robot Herder
6295 posts

I think your first way should be the way to go. There is nothing wrong with modifying the current font the way you do. Your second way creates a memory leak.

I don’t immediately spot the error in your first method.

 Signature 

Looking for Qt developers to join our team @ i-Optics: https://qt-project.org/forums/viewthread/25393/

March 4, 2011

d2uriel d2uriel
Lab Rat
80 posts

I didn’t exactly explain what’s wrong with the first method when I tried using it.

In a different class I had a slot which was connected to QTableWidget’s currentCellChanged() signal. This slot looks like that:
@void LatTE::updateGUIonCellChange(int currentRow, int currentColumn, int previousRow, int previousColumn)
{ if(editor->currentTable()->itemAt(currentRow, currentColumn)->font().bold()) ui.actionBold->setChecked(true); else ui.actionBold->setChecked(false);

// ….. }@and the expression in the if statement always returned false. Even if the actual cell had bold font in it. editor is a pointer to an instance of my class and it’s currentTable() method returns a pointer to a QTableWidget object which is currently being visible in the GUI.
 Signature 

“Do not judge, and you will never be mistaken.” - Jean-Jacques Rousseau

March 4, 2011

Andre Andre
Robot Herder
6295 posts

Does the cell render as bold in the table view itself?

 Signature 

Looking for Qt developers to join our team @ i-Optics: https://qt-project.org/forums/viewthread/25393/

March 4, 2011

d2uriel d2uriel
Lab Rat
80 posts

Yes, it does.

Edit: I’ve created a 2D array of QFont pointers each pointing to a QFont object for each and every cell in QTableWidget. This works like a charm. I’ll try to create a simple project to reproduce this behavior.

 Signature 

“Do not judge, and you will never be mistaken.” - Jean-Jacques Rousseau

March 4, 2011

Andre Andre
Robot Herder
6295 posts

OK, will wait for the example code. If the cell is properly rendered, then the first code block in your first posting is just fine. If you keep having problems reading that value back, instead of creating a whole new array, you may consider using a custom data role and doing something like this:

Create an enum to create some flags:

  1. enum FontFlag {Bold = 0x1, Italic = 0x2, Underline = 0x4};
  2. Q_DECLARE_FLAGS(FontFlags, FontFlag)
  3.  
  4. ...
  5. Q_DECLARE_OPERATORS_FOR_FLAGS(MyClass::Options)
  6.  
  7. const int FontFlagsRole = Qt::UserRole + 1; //or some other offset

Then, in your code where you toggle the value, you simply set an instance of this value as a custom data role (using QTableWidgetItem::setData()), and read it back from that role with data().

  1. void LatTE::updateGUIonCellChange(int currentRow, int currentColumn,
  2.        int previousRow, int previousColumn)
  3. {
  4.     FontFlags font = FontFlags(editor->currentTable()->itemAt(currentRow, currentColumn)->data(FontFlagsRole).toInt());
  5.     ui.actionBold->setChecked(font.testFlag(Bold));
  6.     ui.actionItalics->setChecked(font.testFlag(Italics));
  7.     ui.actionUnderline->setChecked(font.testFlag(Underline));
  8. }

 Signature 

Looking for Qt developers to join our team @ i-Optics: https://qt-project.org/forums/viewthread/25393/

March 4, 2011

d2uriel d2uriel
Lab Rat
80 posts

Sorry, I edited my previous post (added the code). Didn’t want to double post. Pleae take a look at it.

Here’s the test project:

main.cpp

  1. #include "test1.h"
  2. #include <QtGui/QApplication>
  3.  
  4. int main(int argc, char *argv[])
  5. {
  6.  QApplication a(argc, argv);
  7.  test1 w;
  8.  return a.exec();
  9. }

test1.h
  1. #ifndef TEST1_H
  2. #define TEST1_H
  3.  
  4. #include <QtGui>
  5.  
  6. class test1 : public QMainWindow
  7. {
  8.  Q_OBJECT
  9.  
  10. public:
  11.  QWidget centralWidget;
  12.  QPushButton *pushButton;
  13.  QTableWidget *tableWidget;
  14.    
  15.  test1(QWidget *parent = 0, Qt::WFlags flags = 0);
  16.  ~test1();
  17.  
  18. public slots:
  19.  void toggleBold();
  20.  void updateButton(int cr, int cc, int pr, int pc);
  21. };
  22.  
  23. #endif // TEST1_H

test1.cpp
  1. #include "test1.h"
  2.  
  3. test1::test1(QWidget *parent, Qt::WFlags flags)
  4.  : QMainWindow(parent, flags)
  5. {
  6.  centralWidget.resize(600, 300);
  7.  pushButton = new QPushButton(&centralWidget);
  8.  pushButton->setGeometry(QRect(70, 70, 75, 23));
  9.  pushButton->setCheckable(true);
  10.  pushButton->setText("Bold");
  11.  tableWidget = new QTableWidget(&centralWidget);
  12.  tableWidget->setGeometry(QRect(90, 130, 256, 192));
  13.  tableWidget->setRowCount(2);
  14.  tableWidget->setColumnCount(2);
  15.  centralWidget.show();
  16.  
  17.  connect(pushButton, SIGNAL(clicked()), SLOT(toggleBold()));
  18.  for(int r = 0; r < tableWidget->rowCount(); r++)
  19.  {
  20.   for(int c = 0; c < tableWidget->columnCount(); c++)
  21.   {
  22.    QTableWidgetItem *item = new QTableWidgetItem;
  23.    tableWidget->setItem(r, c, item);
  24.   }
  25.  }
  26.  connect(tableWidget, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(updateButton(int, int, int, int)));
  27.  
  28. }
  29.  
  30. test1::~test1()
  31. {
  32.  
  33. }
  34.  
  35. void test1::toggleBold()
  36. {
  37.  if(tableWidget->currentColumn() != -1 && tableWidget->currentRow() != -1)
  38.  {
  39.   QTableWidgetItem *item = tableWidget->itemAt(tableWidget->currentRow(), tableWidget->currentColumn());
  40.   QFont font = item->font();
  41.   font.setBold(!font.bold());
  42.   item->setFont(font);
  43.   pushButton->setChecked(item->font().bold());
  44.  }
  45. }
  46.  
  47. void test1::updateButton(int cr, int cc, int pr, int pc)
  48. {
  49.  if(tableWidget->currentColumn() != -1 && tableWidget->currentRow() != -1)
  50.  {
  51.   QTableWidgetItem *item = tableWidget->itemAt(tableWidget->currentRow(), tableWidget->currentColumn());
  52.   pushButton->setChecked(item->font().bold()) ;
  53.  }
  54. }

Is this the way it should be done? If so, please compile this, run and follow the instruction below:
  1. Select cell at index (0,0),
  2. Type something in and press Enter,
  3. Click the Bold button – the entered text will be bold and button will be checked,
  4. Select an empty cell – the button should uncheck itself but it doesn’t.

This is what I meant by not working right. Unless I’m doing something wrong.

Edit: simply moved the code from your previous post to this one; Andre

 Signature 

“Do not judge, and you will never be mistaken.” - Jean-Jacques Rousseau

March 4, 2011

Andre Andre
Robot Herder
6295 posts

I have compiled your code, and found the issue.

Your code to get the current item somehow does not work. It always returns the top left item (0,0). You can spot that if you move to a different cell, and toggle the bold button. You’ll notice that the top left cell will change its bold status, not the current cell. Changing your .cpp to this works however:

test1.cpp:

  1. #include "test1.h"
  2.  
  3. test1::test1(QWidget *parent, Qt::WFlags flags)
  4.  : QMainWindow(parent, flags)
  5. {
  6.  centralWidget.resize(600, 300);
  7.  pushButton = new QPushButton(&centralWidget);
  8.  pushButton->setGeometry(QRect(70, 70, 75, 23));
  9.  pushButton->setCheckable(true);
  10.  pushButton->setText("Bold");
  11.  tableWidget = new QTableWidget(&centralWidget);
  12.  tableWidget->setGeometry(QRect(90, 130, 256, 192));
  13.  tableWidget->setRowCount(2);
  14.  tableWidget->setColumnCount(2);
  15.  centralWidget.show();
  16.  
  17.  connect(pushButton, SIGNAL(clicked()), SLOT(toggleBold()));
  18.  for(int r = 0; r < tableWidget->rowCount(); r++)
  19.  {
  20.   for(int c = 0; c < tableWidget->columnCount(); c++)
  21.   {
  22.    QTableWidgetItem *item = new QTableWidgetItem;
  23.    tableWidget->setItem(r, c, item);
  24.   }
  25.  }
  26.  connect(tableWidget, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(updateButton(int, int, int, int)));
  27.  
  28. }
  29.  
  30. test1::~test1()
  31. {
  32.  
  33. }
  34.  
  35. void test1::toggleBold()
  36. {
  37.     QTableWidgetItem* item = tableWidget->currentItem();
  38.     if (item) {
  39.         QFont font = item->font();
  40.         font.setBold(!font.bold());
  41.         item->setFont(font);
  42.         pushButton->setChecked(font.bold());
  43.     }
  44. }
  45.  
  46. void test1::updateButton(int cr, int cc, int pr, int pc)
  47. {
  48.     QTableWidgetItem* item = tableWidget->currentItem();
  49.     if (item) {
  50.         pushButton->setChecked(item->font().bold());
  51.     }
  52. }

Notice that instead of going through the row and column numbers, I simply use the currentItem() method directly, and check if it isn’t 0. This works just fine in my tests.

 Signature 

Looking for Qt developers to join our team @ i-Optics: https://qt-project.org/forums/viewthread/25393/

March 4, 2011

d2uriel d2uriel
Lab Rat
80 posts

Yay. Works… and I’ve spent like an hour to create all this 2D QFont pointers array and all of it’s surrounding (methods for inserting and removing rows and cloumns) as a workaround. Geez…

Thank you very much Andre! Let’s cut out the useless code of my project then ;-).

 Signature 

“Do not judge, and you will never be mistaken.” - Jean-Jacques Rousseau

 
  ‹‹ QWidget signals emitted when menu selected - how do I stop this?      Qstreamreader and treewidget problem ››

You must log in to post a reply. Not a member yet? Register here!