Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to create in QT main chat windows like this?



  • Hello,

    I would like to create GUI for chat with other person.

    I would like something like that:

    example.png

    I have a problem with the main Window ( there are text messages: "Hi!" , "How are you?" ).

    I think I have to use QLabel with setStyleSheet and set background-color to yellow. But how Can I add QLabel to QWidget like QTableView?


  • Lifetime Qt Champion

    Hi
    While it is possible to insert say a QLabel on top of a cell in a Table/ListView, it
    becomes heavy quite fast if you got many "chats"

    The better solution is to use a Delegate.
    https://doc.qt.io/qt-5/model-view-programming.html
    (Delegate Classes)

    Here is some samples
    https://stackoverflow.com/questions/1956542/how-to-make-item-view-render-rich-html-text-in-qt
    https://gist.github.com/niklasf/6124645

    You can also have a look at a real chat app ( written in Qt)
    https://github.com/telegramdesktop/tdesktop

    For the network part/sending text etc. i would use
    https://wiki.qt.io/WIP-How_to_create_a_simple_chat_application



  • @mrjj Thank you.

    I check Delegate and I think this isn't what I want. Of course I can add QLabel to QTableView, but when I click on QLabel I see the QTableView's cell. I setShowGrid to false but this isn't what I need.

    I check this chat app tdesktop. This application is WOW, but at this moment it's too hard and too big for me to understand and find where is QLabel with text's messages ( or something what do the same ).

    EDIT:

    I try with delegate and it may works.


  • Lifetime Qt Champion

    @qwe3
    Telegram uses a delegate.
    Delegate is for custom drawing.
    But it also controls the editing via CreateEditor function.
    So you can program it to work as you wish.

    • of course I can add QLabel to QTableView
      Did you expect to type into the QLabel ?

    • but when I click on QLabel I see the QTableView's cell.

    Im not sure i follow?
    QLabel is non editable normally so that is expected.
    You cannot directly type to it.



  • @mrjj I wrote "I can add QLabel to QTableView". This is wrong. Now my code is very strange - I try do something good.

    My class delegate:

    delegate::delegate()
    {
    
    }
    
    QWidget *delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
            QLabel *label = new QLabel( parent);
            label->setStyleSheet("QLabel {background-color:red}");
            return label;
    }
    
    void delegate::setEditorData(QWidget *editor, const QModelIndex &index) const
    {
            QLabel *label = static_cast<QLabel *>(editor);
            label->setText(index.data().toString());
    }
    
    void delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
    {
        QLabel *label = static_cast<QLabel *>(editor);
        label->setText("abcde");
        model->setData(index, index.data());
    }
    
    void delegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        editor->setGeometry(option.rect);
    }
    
    void delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
    
    }
    
    

    I find in youtube movie about Qt Delegate and rewrite the code and try change it to my needs.

    My mainApp:

        table = new QTableView(this);
        deleg = new delegate;
        table->setGeometry(0,0,500,500);
        model = new QStandardItemModel(10,2, this);
        item = new QStandardItem("somethingABC");
        model->setItem(0,0,item);
    
        table->setModel(model);
        for(int i=0;i<100;i++)
            model->appendRow(QList<QStandardItem *>());
    
        table->setItemDelegate(deleg);
        table->setShowGrid(false);
        table->verticalHeader()->hide();
        table->horizontalHeader()->hide();
        table->setColumnWidth(0,250);
        table->setColumnWidth(1,250);
    

    When I double click on first cel ( 0,0 ) I get red rectangle with text "somethingABC". I would like to have this text when I execute the app ( no when I double click in QTableView ).

    EDIT:

    I would like something like:
    After 5 sec ( I can use QTimer and timeout()) I would like to add to my QTableView new QLabel in color red with text ( for example "Hello!" ) and this QLabel I would like to be rounded ( I know I can use setStyleSheet ).

    I would like to create GUI like in first post.


  • Lifetime Qt Champion

    Hi
    2 things that caught my eye.

    1:

    void delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
    {
        QLabel *label = static_cast<QLabel *>(editor);
        label->setText("abcde");
        model->setData(index, index.data());
    }
    This function writes back the data to the model. You write back the same data
    so not sure what you do with label :) 
    

    2:
    You are not painting anything. (no code)

    void delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
    call base class paint here or simply delete it if you have no need for extra paitning
    }
    
    

    Hi
    Did you read about Delegates ?
    The Editor is only active when editing, rest of the time its not shown.
    Then its paint job to show the data.



  • @mrjj Yes, I read about editor. But my problem is: I don't know what can I write in paint. I have 3 args: painter, option and index. I don't access to QLabel which I create in createEditor() function.


  • Lifetime Qt Champion

    @qwe3
    Ok. its important to read the docs as the Delegate follow a system so
    one has to understand how it acts.

    In paint you have access to the model which has the data.
    So you can use QPainter and drawText to draw it. Also make it red if needed etc.
    The QLabel you use is for Editing and not display.



  • @mrjj Okay. But Can I create text in red background which is in rounded background by QPainter. I never do something like that :)


  • Lifetime Qt Champion

    @qwe3
    Yes all StyleSheet and Widgets use QPainter internally.
    So you can make look like anything you like.

    https://stackoverflow.com/questions/29196610/qt-drawing-a-filled-rounded-rectangle-with-border

    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);
    QPainterPath path;
    path.addRoundedRect(QRectF(10, 10, 100, 50), 10, 10);  
    QPen pen(Qt::black, 10);
    p.setPen(pen);
    p.fillPath(path, Qt::red);
    p.drawPath(path);
    

    Do note the cell area is in option.rect
    so you have to use its values to draw the right place. else you draw somewhere else on the view :)



  • @mrjj Now I add to paint() function:

        QPen pen(Qt::black, 1);
        painter->setPen(pen);
        painter->drawText(0,0,100,100,0,index.data().toString());
    

    and it works!

    But I see next 2 problems:

    1. I have to write 100,100 in drawText() function. Is there any way to get size which my index.data().toString() needs?

    2. When I add this lines to my paint() function I only see this text in first cell (0,0).

    I have:

        item = new QStandardItem("somethingABC");
        item2 = new QStandardItem("somethingABC");
        item3 = new QStandardItem("somethingABC");
        model->setItem(0,0,item);
        model->setItem(1,0,item2);
        model->setItem(2,0,item3);
    

    So I think I have to get 3 texts "somethingABC" in my QTableView. Right?


  • Lifetime Qt Champion

    @qwe3 said in How to create in QT main chat windows like this?:

    1
    The cell area is in option.rect. (one of the parameters to paint)
    2:
    i think its related to 1 as all cells then paint its text in 100,100 :)

    painter->drawText(option.rect, index.data().toString());



  • @mrjj One line and 2 problems aren't now problems :D Perfect!


  • Lifetime Qt Champion

    @qwe3
    Super.
    Delegates are a bit complicated to begin with but later you love them as you can do anything you like.
    and then have good performance too \o/



  • @mrjj I create rounded rectangle and it works :)

    But I don't know how can I do the last thing:

    The message text can be longer than one line. I think I have to change row's height. This is not a problem: table->setRowHeight(1,240);

    But when I have to do that? In delegate functions()?


  • Lifetime Qt Champion

    @qwe3
    Hi
    Nope, its not the delegates job
    if its for all rows then

    QHeaderView *verticalHeader = myTableView->verticalHeader();
    verticalHeader->setSectionResizeMode(QHeaderView::Fixed);
    verticalHeader->setDefaultSectionSize(24);
    


  • @mrjj Thank you! I have to change your code to:

    verticalHeader->setSectionResizeMode(QHeaderView::ResizeToContents);
    

    But you helped me next time! :)

    Thank you


  • Lifetime Qt Champion

    @qwe3
    Hi
    Super.
    Congratulation on your first Delegate.



  • @mrjj Only in 50%.

    I need one more:

    QWidget *delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        return nullptr;
    }
    
    void delegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        editor->setGeometry(option.rect);
    }
    
    void delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        if(index.row()%2==0 && index.column()!=1)
        {
            QPen pen2(Qt::yellow, 1);
            painter->setPen(pen2);
        
            QPainterPath path;
            path.addRoundedRect(QRectF(option.rect), 10, 10);
            painter->fillPath(path, Qt::yellow);
            painter->drawPath(path);
            QPen pen(Qt::black, 1);
            painter->setPen(pen);
            painter->drawText(option.rect,Qt::AlignCenter, index.data().toString());
        }
    }
    

    I would like to select text message, click on it right mouse button, get the list, where is "copy" and other possibilities. How can I do this?

    Now I can't select text and click right mouse button


  • Lifetime Qt Champion



  • @mrjj Next small problem :)

    I have now ( in the picture is small error - the third row have smaller rectangle ( whre is text "somethingABC ") - I change it in paint application; this rectangle have width like the others )

    example4.png

    My delegate:

    path.addRoundedRect(QRectF(option.rect), 10, 10);
    

    When I change it to:

    path.addRoundedRect(QRectF(option.rect.x(), option.rect.y(),option.rect.width()/2,option.rect.height()), 10, 10);
    

    I get:
    example5.png

    There are 3 columns in QTableView:
    the first one for my messages
    the second one - seperator
    the third for other person's messages

    I would like to align to right the third column - yellow rectangles. Like this:
    example6.png

    With the text there is not a problem:

    painter->drawText(QRect(option.rect),Qt::AlignVCenter, index.data().toString());
    

    But function

    painter->drawPath(path);
    

    doesn't have a parameter with alignments


  • Lifetime Qt Champion

    @qwe3
    Hi
    Only drawText uses align. Not other paint functions as far as i know :0

    Since option.rect IS the cell, i think you just need to set
    ui->table->horizontalHeader()->setStretchLastSection(true);

    so the last col is actually to the far right



  • @mrjj Thank you, I think I can use something like:

    path.addRoundedRect(QRectF(option.rect.x()+100, option.rect.y(),option.rect.width()/2,option.rect.height()), 10, 10);
    

    for the last column.

    But there is next problem :D

    When I use:

    table->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    

    I get the perfect height for cell (0,0). Perfect. But cell (0,1) must has the same height like cell (0,0), so it can be too big for cell (0,1).


  • Lifetime Qt Champion

    @qwe3
    Hi

    • I get the perfect height for cell (0,0). Perfect. But cell (0,1) must has the same height like cell (0,0), so it can be too big for cell (0,1).

    Im not really sure what you say here :)



  • example6.png
    Please look at first row. In cell (0,0) there is text "somethingABC\ntext\njdsa". In cell ( 0,2 ) there is text "so". When I use

    table->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    

    QT resize height of the first row to height which cell (0,0 ) needs. But cell (0,2) must have the same height. In cell (0,2) there is only text "so" , so it can have smaller height.


  • Lifetime Qt Champion

    Hi
    But that is what ResizeToContents does ?
    So if it has lesser text, it will be smaller.

    Do you want all to have same size regardless of text ?

    ahh you mean the "so" should be smaller ?



  • @mrjj
    I would like something like that:

    example7.png


  • Lifetime Qt Champion

    @qwe3
    Hi
    I think that will be hard as one row can only have one height.
    https://doc.qt.io/archives/qt-4.8/qtableview.html#setRowHeight
    but you can span cells to give them uneven sizes
    void QTableView::setSpan(int row, int column, int rowSpanCount, int columnSpanCount)

    But im wondering if it would not be easier to just use a QListView ( you can reuse delegate)
    and have 2 of them .
    one in each side and then just some white Qwidget in between ?



  • @mrjj I never used QListView. I don't need to use QTableView. But when I add 2 QListView there are two sliders to move? I would like to have only one like in QTableView ( on the right )


  • Lifetime Qt Champion

    @qwe3
    Its just like TableView but just having one column.

    Then each row can be any height you wish pr item/shape and same for the right one.

    Else i think it get complicated to do with one tableView.
    You would have to make one row as hight as the biggest shape or merge cells to allow it to be uneven.
    Not easy in my book to calc that.
    But your call.

    Well its not so hard to make both scroll at same time

    connect(view1->horizontalScrollBar(), SIGNAL(valueChanged(int)), view2->horizontalScrollBar(), SLOT(setValue(int)));
    connect(view2->horizontalScrollBar(), SIGNAL(valueChanged(int)), view1->horizontalScrollBar(), SLOT(setValue(int)));
    

    and ofc you can just hide one of them if you wish



  • @mrjj Hello,

    Could you tell me how will you do this GUI? Using QTableView, QListView or something other? At the moment please forget about 3 columns. Change it to only one column. In telegram application I see only one column for all people ( maybe two - the first one is for avatar in circle ). Do you know how it is done in telegram application? You told me that application has delegate. And it's using QListView too? What about the size of circle where is the text? How it is implemented that we have circle with text in other sizes?


Log in to reply