Which is the proper way to update Qprogressbar from a widget



  • I read some post, but dont find the solution
    I read thinks like:
    The gui may only be controlled by the main thread,
    You can't modify GUI objects from another thread,
    Must to use QMetaType.

    But i need a short example.
    My scenario is , in a mainwindow have a mapframe for drawing, it inherit Qwidget
    and emit two signal:
    In the mapframe.header have:
    signals:

    @ void drawingProgress( int, int );
    void xyCoordinates( const QPointF & p );// current mouse position
    @

    Inside of mapframe paintEvent have a for that render points:

    @
    void MapFrame::paintEvent(QPaintEvent * /* event */)

    for (int i=0;i<m_PointFeatures.size();i++)
    {
    painter->setBrush(m_PointFeatures.at(i).color);
    painter->drawEllipse(translator.mapToPixel(m_PointFeatures.at(i).x,m_PointFeatures.at(i).y), 3, 3);
    if( i % 1000==0)
    {
    emit drawingProgress(m_PointFeatures.size(),i);
    }
    }
    @

    The mapframe is inside of manwindow
    in the constructor of main window, connect the signals to the slots:

    @
    connect( mapframe, SIGNAL( xyCoordinates( const QPointF & ) ),
    this, SLOT( showMouseCoordinate( const QPointF & ) ) );

     connect( mapframe, SIGNAL( drawingProgress(int,int ) ),
              this, SLOT( showDrawingProgress( int,int ) ) );
    

    @


    When update mouse coordenates to a Qlabel
    it work ok:

    @
    void dlgGraph::showMouseCoordinate( const QPointF & pt)
    {
    lbMousePosition->setText(QString("Lat:%1,Long:%2").arg(pt.x(),0,'f',5).arg(pt.y(),0,'f',5));
    }
    @
    But when update the QProgressBar, iot crash the main window:

    @
    void dlgGraph::showDrawingProgress(int t,int i)
    {
    qDebug()<< i;
    progressBar->setMaximum(t);
    progressBar->setValue(i);//->CRASH
    }
    @

    How make a proper implementation of it?



  • Signals and Slots is the best way to update the same. Since you are doing everything inside the main thread(painting and updating the progress bar), you need to process the events locally. Program may be crashing due to some wrong values or huge values.

    You can try something like following. Just simulated your scenario.

    @void Widget::paintEvent(QPaintEvent *){
    int count=10;
    qDebug() << "Painting"<<endl;
    for (int i=0; i<100000000;i++){
    if (i ==0){
    qDebug() << " i="<<i << endl;
    QThread::sleep(1);
    updateprogbar(count);
    count+=10;
    }
    }
    }

    void Widget::updateprogbar(int i){
    qDebug() << " UpdateBar="<<i << endl;
    QEventLoop loop;
    this->bar->setValue(i);
    loop.processEvents();
    }
    @



  • @
    void dlgGraph::showDrawingProgress(int t,int i)
    {
    qDebug()<< i;
    progressBar->setMaximum(t);
    progressBar->setValue(i);//->CRASH
    }
    @

    May be a silly question but have you allocated progressBar ?
    Another question is why do you set maximum on each call to showDrawingProgress?
    One time before starting a loop is enough.
    What are values of t and i in showDrawingProgress when it crashes?
    Does it crash immediately on a first call or after some time?

    I would suggest to connect QProgressBar::setRange and QProgressBar::setValue to the signals in mapframe and them emit setRange before starting a loop and emit setValue in the loop.



  • Good point andreyc. I did not suspect on ProgressBar allocation as the crash is in setValue. If it crashes at line#, allocation should be definitely a problem and it should crash every time.



  • The progresbar is outside of widget.
    What mean allocated,
    i add the progress to the layout:

    @
    progressBar = new QProgressBar() ;
    progressBar->setRange(0,100);
    mainLayout->addWidget(progressBar);
    @

    Now in mapframe.h split the signal and add another signal to setBarMaxValue
    before the for init.

    @
    void progressMaxValue(int);
    void progressDrawValue(int);
    @

    In the main window connect the signal to slot
    @
    connect( mapframe, SIGNAL( progressDrawValue(int ) ),
    this, SLOT( setBarValue( int ) ) );
    connect( mapframe, SIGNAL( progressMaxValue(int) ),
    this, SLOT( setBarMaxValue( int ) ) );
    @

    The paint even of the MapFrame widget emit the signal progressMaxValue
    before starting the for loop.

    @
    void MapFrame::drawPointFeatures(QPainter *painter)
    {

    if (m_PointFeatures.size()>0)
    {
    emit progressMaxValue(m_PointFeatures.size());
    }
    for (int i=0;i<m_PointFeatures.size();i++)
    {
          painter->setBrush(m_PointFeatures.at(i).color);
          painter->drawEllipse(translator.mapToPixel(m_PointFeatures.at(i).x,m_PointFeatures.at(i).y), 3, 3);          
          if( 1 % 1000==0) emit progressDrawValue(i);
    }
    

    }
    @

    It crash in the first emit when i=0
    Greetings



  • Do you work on Windows on OSX, Linux?
    If OSX,Linux could show a backtrace for the crash?
    I don't know how to get backtrace on Windows but if you know how then it may help here.



  • [quote author="marceloarguello700" date="1410970001"]
    The progresbar is outside of widget.
    What mean allocated,
    i add the progress to the layout:

    @
    progressBar = new QProgressBar() ; // <<-- here is allocation
    progressBar->setRange(0,100);
    mainLayout->addWidget(progressBar);
    @
    [/quote]

    Yes, you allocated a progress bar

    [quote author="marceloarguello700" date="1410970001"]
    @
    void MapFrame::drawPointFeatures(QPainter *painter)
    {

    if (m_PointFeatures.size()>0)
    {
    emit progressMaxValue(m_PointFeatures.size());
    }
    for (int i=0;i<m_PointFeatures.size();i++)
    {
          painter->setBrush(m_PointFeatures.at(i).color);
          painter->drawEllipse(translator.mapToPixel(m_PointFeatures.at(i).x,m_PointFeatures.at(i).y), 3, 3);          
          if( 1 % 1000==0) emit progressDrawValue(i);
    }
    

    }
    @
    [/quote]

    I guess it is a type in the comment and in the source code you have
    @
    if( i % 1000==0) emit progressDrawValue(i);
    @

    instead of
    @
    if( 1 % 1000==0) emit progressDrawValue(i);
    @



  • Thanks for the reply,
    I run over window

    My original code is with i, dont know, why appear a one (1)
    @
    if( i % 1000==0) emit progressDrawValue(i);
    @

    the question is what need to make to avoid crashing?



  • You may try to connect the progressDrawValue and progressMaxValue directly to QProgressBar

    @
    connect( mapframe, SIGNAL( progressDrawValue(int ) ),
    progressBar, SLOT( setValue( int ) ) );
    connect( mapframe, SIGNAL( progressMaxValue(int) ),
    progressBar, SLOT( setMaximum( int ) ) );
    @



  • I try like you suggest but crash in the firts emit

    According to this page
    http://qt-project.org/doc/qt-4.8/threads-qobject.html

    If you are calling a function on an QObject subclass that doesn't
    live in the current thread and the object might receive events,
    you must protect all access to your QObject subclass's internal
    data with a mutex; otherwise, you may* experience crashes* or other undesired behavior.

    May be it is the cause, but i dont know how fix it.



  • Do you have MapFrame::drawPointFeatures and QProgressBar in separate threads ?

    If so, then it will not work. All UI related classes must be used in a "main thread":http://qt-project.org/doc/qt-5/threads-qobject.html#qobject-reentrancy.


Log in to reply
 

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