How to draw and correctly animate progress bar in QTableView?


  • Lifetime Qt Champion

    @hamov
    Your idea is good but the paint function is only called if the widget thinks it need to update/redraw.
    For test you could use a timer to force it to redraw

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), YourQTableView, SLOT(update()));
    timer->start(1000);
    


  • @mrjj
    Thanks for the idea with timer, it was supposed to work. Can't imagine why, but it isn't work... I'm tried to connect
    with QWidget::repaint() slot instead QWidget::update(), and have tried to update()/repaint() whole window which contain my table,
    but it is not help too.
    Besides that, I have tried to call update()/repaint() for table and window directly, from timerEvent() and it also didn't help.

        TrainThread* tThread = new TrainThread(this, m_prodLine, settings);
        bool c = connect(tThread, SIGNAL(finished()), tThread, SLOT(deleteLater()));
        assert(c);
    
        m_timer = new QTimer(m_table);
        c = connect(m_timer, SIGNAL(timeout()), m_table, SLOT(update()));
        assert(c);
        c = connect(tThread, SIGNAL(finished()), m_timer, SLOT(stop()));
        assert(c);
    
        tThread->start(QThread::InheritPriority);
        m_timer->start(2000);
    

  • Lifetime Qt Champion

    @hamov
    I would have bet my mom on that to work :)

    I assume you put break point to be really sure the update/repaint did not happen ?

    Just a sanity check, if you put a button in same window and call m_table->update, it does update right ?

    Hopefully I can find a QItemDelegate sample and play around with to get new ideas.

    update:
    Found a sample with QItemDelegate that draws button. button dont animate when pressed but it does
    happy update when asked.
    i will try with progressbar just to know :)

    update 2:
    you code works perfectly here. I added a static counter +the timer
    and it did/looked just a expected. (with your paint)

    my best best is that it is related to your threads somehow.
    (since i do not have a thread)



  • @mrjj
    I'm too. :-)))

    I've create a clean example with window, table and delegate, without using threads, but to my surprise it didn't work too.
    Looks like something wrong with my Qt...
    I'm using Qt 5.4.2 64bit build from source package + VS2013 + Qt VS Addin 1.2.4


  • Lifetime Qt Champion

    @hamov
    Oh, that sounds so strange.
    This is my mini test. Can you test that is not working either ? (if time)

    .h

    #include <QStyledItemDelegate>
    #include <QItemDelegate>
    #include <QStyle>
    
    class Order_ListViewDelegate : public QStyledItemDelegate {
    public:
        Order_ListViewDelegate();
    protected:
        void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;  
    };
    

    .cpp

    void Order_ListViewDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
    {
    	static int cc = 0;
    	cc++;
    
    	QStyledItemDelegate::paint ( painter, option, index );
    
    	QStyleOptionProgressBar progressBarOption;
    	progressBarOption.rect = option.rect;
    
    	int progress = cc;
    
    	progressBarOption.minimum = 0;
    	progressBarOption.maximum = 100;
    	progressBarOption.textAlignment = Qt::AlignCenter;
    	progressBarOption.progress = progress;
    	progressBarOption.text = QString ( "%1%" ).arg ( progress );
    	progressBarOption.textVisible = true;
    	QApplication::style()->drawControl ( QStyle::CE_ProgressBar, &progressBarOption, painter );
    }
    Order_ListViewDelegate::Order_ListViewDelegate(){ }
    

    in main
    ui->orderlist->setItemDelegate ( new Order_ListViewDelegate() );

    orderlist being a normal QListView



  • @mrjj
    Unfortunately, your example didn't work too... :(
    The only difference is that the QTableView redraws progress on window resize/move/click, and QListView
    redraws progress also when mouse enters in window.


  • Lifetime Qt Champion

    ok ?!?
    So even with this mini sample, trying to update from say a button, do not work ?
    or the QTimer hookup does nothing ?

    I must agree with you then. Something not right.
    I wish i had VC to test the other way around.



  • here is a complete example of how to use QProgressBar in item delegate



  • @mrjj Thanks for helping.
    Yep, it didn't work even with button. There are no animation after button release(I'm connected button release() with QListView's update()). Only on mouse hover or resize.



  • @cincirin Thanks, I know about that example, my code is same as in example. Problem is that the progress bar is not draw animation.


  • Lifetime Qt Champion

    @hamov

    Just to be sure. By animation, you mean that the green part cover more/moves. as when moving the mouse over.

    Since it can draw , (when mouse move/over) it somehow must be the signal that get lost ?

    you did also try direct repaint ?

    void MainWin::on_toolButton_clicked()
    {
    m_table->repaint();
    }

    If that dont work then your Qt install must be funky somehow.



  • @mrjj
    Yes, that's right.
    Here is my code with timer and button:
    *.hpp

    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
    private slots:
        void buttonPressed();
    private:
        void createList();
        void startTimer();
    private:
        QListView* m_list;
        QStandardItemModel* m_model;
        QPushButton* m_button;
        QTimer* m_timer;
    };
    

    *.cpp

    class TestDelegate : public QStyledItemDelegate
    {
    public:
        TestDelegate(QObject* parent = 0);
    
    public:
        virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    };
    
    TestDelegate::
    TestDelegate(QObject* parent)
        : QStyledItemDelegate(parent)
    {
    
    }
    
    void TestDelegate::
    paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        static int cc = 0;
        cc++;
    
        QStyledItemDelegate::paint ( painter, option, index );
    
        QStyleOptionProgressBar progressBarOption;
        progressBarOption.rect = option.rect;
    
        int progress = cc;
    
        progressBarOption.minimum = 0;
        progressBarOption.maximum = 100;
        progressBarOption.textAlignment = Qt::AlignCenter;
        progressBarOption.progress = progress;
        progressBarOption.text = QString ( "%1%" ).arg ( progress );
        progressBarOption.textVisible = true;
        QApplication::style()->drawControl ( QStyle::CE_ProgressBar, &progressBarOption, painter );
    }
    
    MainWindow::
    MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        createList();
        startTimer();
    }
    
    void MainWindow::
    createList()
    {
        m_model = new QStandardItemModel();
        m_model->setRowCount(1);
        m_model->setColumnCount(1);
    
        TestDelegate* delegate = new TestDelegate(this);
    
        m_list = new QListView(this);
        m_list->setModel(m_model);
        m_list->setItemDelegate(delegate);
    
        m_button = new QPushButton("Paint!", this);
        bool c = connect(m_button, SIGNAL(released()), this, SLOT(buttonPressed()));
        assert(c);
    
        QVBoxLayout* wLayout = new QVBoxLayout();
        wLayout->addWidget(m_list);
        wLayout->addWidget(m_button);
    
        QWidget* cWidget = new QWidget(this);
        cWidget->setLayout(wLayout);
    
        setCentralWidget(cWidget);
    }
    
    void MainWindow::
    startTimer()
    {
        m_timer = new QTimer(this);
        bool c = connect(m_timer, SIGNAL(timeout()), m_list, SLOT(update()));
        assert(c);
    
        m_timer->start(2000);
    }
    
    void MainWindow::
    buttonPressed()
    {
        m_list->repaint();
    }
    

    I also build the code using Qt 4.8.0 + VS2010, but result is same...no animation until move/resize. Don't know what to think...


  • Lifetime Qt Champion

    well, that does not work for me either.
    And im on mingw compiler :)

    So thats good news :)

    its 2 in night so will first have good look tomorrow.


  • Lifetime Qt Champion

    Hi
    Funny/strange thing was I got a project where it did work in.

    I then try to recreate it (new project) and that DID not work.
    Nearly drove me nuts so after
    3 hours of comparing/removing, i found out the following:

    if I set set a stylesheet

    MainWindow::MainWindow ( QWidget *parent )
        : QMainWindow ( parent )
    {
      createList();
      startTimer();
      setStyleSheet(QLatin1String("QFrame { border: 1px solid gray;margin-top 0px padding-top 0px}"));
    }
    

    for main window,
    It does work. (your code)

    If I do not, it does not. Same result as you. no repaint on demand/timer.

    So it seems to work if mainwin has a stylesheet ?!?

    Im a bit excited to see if it works the same for you.



  • @mrjj
    Wow!...unbelievable...but setting stylesheet for main window works for me too... :-))) I would never have thought of it. :-)) Thanks a lot for helping, this can be a workaround. :-))
    The only remaining question is how setting style sheet refers to repaint process??!... :-))


  • Lifetime Qt Champion

    @hamov

    Yeah blew my mind too. Glad it at least worked for you.

    The strange thing is that the stylesheet do not even alter QListView

    I have no idea why setting a stylesheet on mainwindow makes it respond to ->repaint()

    Looking at the source
    https://github.com/Vitallium/qt5/blob/master/qtbase/src/widgets/itemviews/qlistview.cpp

    There is nothing that (that i see) which could explain it.

    Update:
    After digging around more. I think the reason for ignoring update/repaint
    is that we do not change the model.
    so the view thinks it is fully updated.
    So one has to emit datachanged() somehow.
    Or actually change the data in the model.



  • @mrjj
    You are absolutely right. I'm created a simple model class with function updateData() which calls beginResetData() and endResetData(). Also I've changed the timer, now it is QObject::startTimer() call and QObject::timerEvent() handler. In example I didn't call update()/repaint() at all. Delegate class can be taken from previous example.
    *.hpp

    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
    private:
        void createList();
    protected:
        virtual void timerEvent(QTimerEvent * event);
    private:
        QListView* m_list;
        TestModel* m_model;
        int m_timerId;
    };
    

    *.cpp

    class TestModel : public QStandardItemModel
    {
    public:
        TestModel(QObject* parent = 0);
    public:
        void updateData();
    };
    
    TestModel::
    TestModel( QObject* parent)
        : QStandardItemModel(parent)
    {
    }
    
    void TestModel::
    updateData()
    {
        beginResetModel();
        endResetModel();
    }
    
    
    MainWindow::
    MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        createList();
        m_timerId = startTimer(2000);
    }
    
    void MainWindow::
    createList()
    {
        m_model = new TestModel();
        m_model->setRowCount(1);
        m_model->setColumnCount(1);
    
        TestDelegate* delegate = new TestDelegate(this);
    
        m_list = new QListView(this);
        m_list->setModel(m_model);
        m_list->setItemDelegate(delegate);
    
        setCentralWidget(m_list);
    }
    
    void MainWindow::
    timerEvent( QTimerEvent * event )
    {
        m_model->updateData();
    }
    

    And all works fine! I'm tested with Qt 4.8.0 but I'm sure this will work also for 5.4.2. The example works also for QTableView and QTreeView. But this isn't match my logic a bit. I think model data update and direct repaint should be independent since:

    1. There are no data in my model which need to be updated.
    2. update()/repaint() have no sense. It is enough to emit dataChanged() for repaint.

    So the conclusion of all turns:

    1. If you want to repaint view, you should update model data even if there are no data(or 'default' data).
    2. QWidget's update()/repaint() functions doesn't make sense for any View.

    Am I right?


  • Lifetime Qt Champion

    @hamov said:

    Also found out the same effect can happen by simply call
    m_model->reset() from main window.
    But that is bad for a tree since it resets all open states etc.

    I think one is to use the

    void	dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles = QVector<int> ())
    

    From (inside?) the model for optimized redrawing.

    I agree with you.
    Repaint should still repaint even if model is not dirty. Also it redraws on mousemove even
    model is not changed so a bit odd that it then ignores direct repaints .

    You are right.
    I guess we can add to the conclusion
    that if you have no data/very static , the Item-based ListWidget might work better (?)

    update:
    ListWidget suffer the same. update/repaint does nothing.
    So not sure how one would go about asking it to redraw.



  • @mrjj
    I have tested that signal for treeview and all works fine. :-)
    Seems update()/repaint() didn't work for any widget/view. Is it a Qt bug?...or such behavior of redraw was planned by developers?!


  • Lifetime Qt Champion

    @hamov

    well digging around, I also found
    m_list->viewport()->repaint();

    which works for WidgetList. Did not test with model/view yet.

    I think it is by design as it does redraw when data changes but we are not alone finding
    it strange working if one google it


Log in to reply
 

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