[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.



  • 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.



  • 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.



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



  • 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.



  • 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:
    @
    enum FontFlag {Bold = 0x1, Italic = 0x2, Underline = 0x4};
    Q_DECLARE_FLAGS(FontFlags, FontFlag)

    ...
    Q_DECLARE_OPERATORS_FOR_FLAGS(MyClass::Options)

    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().

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



  • -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
    @#include "test1.h"
    #include <QtGui/QApplication>

    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    test1 w;
    return a.exec();
    }@
    test1.h
    @#ifndef TEST1_H
    #define TEST1_H

    #include <QtGui>

    class test1 : public QMainWindow
    {
    Q_OBJECT

    public:
    QWidget centralWidget;
    QPushButton *pushButton;
    QTableWidget *tableWidget;

    test1(QWidget *parent = 0, Qt::WFlags flags = 0);
    ~test1();

    public slots:
    void toggleBold();
    void updateButton(int cr, int cc, int pr, int pc);
    };

    #endif // TEST1_H@
    test1.cpp
    @#include "test1.h"

    test1::test1(QWidget *parent, Qt::WFlags flags)
    : QMainWindow(parent, flags)
    {
    centralWidget.resize(600, 300);
    pushButton = new QPushButton(&centralWidget);
    pushButton->setGeometry(QRect(70, 70, 75, 23));
    pushButton->setCheckable(true);
    pushButton->setText("Bold");
    tableWidget = new QTableWidget(&centralWidget);
    tableWidget->setGeometry(QRect(90, 130, 256, 192));
    tableWidget->setRowCount(2);
    tableWidget->setColumnCount(2);
    centralWidget.show();

    connect(pushButton, SIGNAL(clicked()), SLOT(toggleBold()));
    for(int r = 0; r < tableWidget->rowCount(); r++)
    {
    for(int c = 0; c < tableWidget->columnCount(); c++)
    {
    QTableWidgetItem *item = new QTableWidgetItem;
    tableWidget->setItem(r, c, item);
    }
    }
    connect(tableWidget, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(updateButton(int, int, int, int)));

    }

    test1::~test1()
    {

    }

    void test1::toggleBold()
    {
    if(tableWidget->currentColumn() != -1 && tableWidget->currentRow() != -1)
    {
    QTableWidgetItem *item = tableWidget->itemAt(tableWidget->currentRow(), tableWidget->currentColumn());
    QFont font = item->font();
    font.setBold(!font.bold());
    item->setFont(font);
    pushButton->setChecked(item->font().bold());
    }
    }

    void test1::updateButton(int cr, int cc, int pr, int pc)
    {
    if(tableWidget->currentColumn() != -1 && tableWidget->currentRow() != -1)
    {
    QTableWidgetItem *item = tableWidget->itemAt(tableWidget->currentRow(), tableWidget->currentColumn());
    pushButton->setChecked(item->font().bold()) ;
    }
    }@
    Is this the way it should be done? If so, please compile this, run and follow the instruction below:

    Select cell at index (0,0),

    Type something in and press Enter,

    Click the Bold button - the entered text will be bold and button will be checked,

    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



  • 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:
    @#include "test1.h"

    test1::test1(QWidget *parent, Qt::WFlags flags)
    : QMainWindow(parent, flags)
    {
    centralWidget.resize(600, 300);
    pushButton = new QPushButton(&centralWidget);
    pushButton->setGeometry(QRect(70, 70, 75, 23));
    pushButton->setCheckable(true);
    pushButton->setText("Bold");
    tableWidget = new QTableWidget(&centralWidget);
    tableWidget->setGeometry(QRect(90, 130, 256, 192));
    tableWidget->setRowCount(2);
    tableWidget->setColumnCount(2);
    centralWidget.show();

    connect(pushButton, SIGNAL(clicked()), SLOT(toggleBold()));
    for(int r = 0; r < tableWidget->rowCount(); r++)
    {
    for(int c = 0; c < tableWidget->columnCount(); c++)
    {
    QTableWidgetItem *item = new QTableWidgetItem;
    tableWidget->setItem(r, c, item);
    }
    }
    connect(tableWidget, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(updateButton(int, int, int, int)));

    }

    test1::~test1()
    {

    }

    void test1::toggleBold()
    {
    QTableWidgetItem* item = tableWidget->currentItem();
    if (item) {
    QFont font = item->font();
    font.setBold(!font.bold());
    item->setFont(font);
    pushButton->setChecked(font.bold());
    }
    }

    void test1::updateButton(int cr, int cc, int pr, int pc)
    {
    QTableWidgetItem* item = tableWidget->currentItem();
    if (item) {
    pushButton->setChecked(item->font().bold());
    }
    }
    @

    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.



  • 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 ;-).


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.