How to update progress bar?
-
I am having some trouble updating the progress bar in my UI. I created a class called counter that inherits from qobject (2nd Code snipet). Then in mainwindow I create an instance of that class called enginePowerCounter and connect enginePowerCounter signal valueChanged with progress bar slot setValue. Later on I set a new value of enginePowerCounter and when that new value is set I want a signal to be sent to the progress bar and set a new value with value of enginePowerCounter.
The instance declaration happens after all of the #includes. The connect happens after ui->setupUi(this), and the value of enginePowerCounter is updated in void MainWindow::setup(). In the 2nd code snippet, I define slot but I do not define signal. Originally I tried defining signal but then I got an error saying "This is not a signal declaration".
Admittedly I believe I do not correctly understand how signals and slots work, expecially when creating custom ones. I can get signals from windowform.ui alright, but I think its the custom stuff that I have more problems with. I read the documentation where the idea for the counter class actually came from but I may be missing something fundamental. Can anybody help me get this working and understanding signals and slots better?
mainwindow.cpp
#define _USE_MATH_DEFINES #include "mainwindow.h" #include "./ui_mainwindow.h" #include "EngineCharacteristicsNamespace.hpp" #include "math.h" #include "counter.hpp" #include "QObject" #include <vtkVersion.h> #include <vtkSmartPointer.h> #include <vtkFloatArray.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkContextView.h> #include <vtkContextScene.h> #include <vtkChartXY.h> #include <vtkTable.h> #include <vtkPlot.h> #include <vtkContextInteractorStyle.h> #include <vtkEventQtSlotConnect.h> #include <QVTKOpenGLWidget.h> #include <vtkGenericOpenGLRenderWindow.h> Counter enginePowerCounter; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); connect(&enginePowerCounter,SIGNAL(valueChanged(int newValue)),ui->progressBar,SLOT(setValue(int))); mQVtkWidget= new QVTKOpenGLWidget(this); QGridLayout* layout = new QGridLayout(ui->frame); layout->addWidget(mQVtkWidget,1,1); ui->frame->setLayout(layout); vtkNew<vtkGenericOpenGLRenderWindow> window; mQVtkWidget->SetRenderWindow(window); mView->SetRenderWindow(window); mView->SetInteractor(window->GetInteractor()); setup(); } void MainWindow::on_pushButton_simulateEngine_clicked() { EngineCharacteristics::rodToHalfStrokeRatio = EngineCharacteristics::rodLength/(EngineCharacteristics::strokeLength/2); EngineCharacteristics::cylinderVolume = M_PI*pow((EngineCharacteristics::bore/2),2)*EngineCharacteristics::strokeLength; EngineCharacteristics::combustionChamberVolume = EngineCharacteristics::cylinderVolume/(EngineCharacteristics::compressionRatio-1); setup(); } void MainWindow::selectionChanged(vtkObject*, unsigned long, void*, void* callData) { vtkChartPlotData* plotData = reinterpret_cast<vtkChartPlotData*>(callData); if(plotData) { //Plot values. float x=plotData->Position.GetX(); float y=plotData->Position.GetY(); QString xlabel="X: "+QString::number(x); ui->xLabel->setText(xlabel); QString ylabel="Y: "+QString::number(y); ui->yLabel->setText(ylabel); this->mQVtkWidget->GetRenderWindow()->Render(); } } void MainWindow::setup() { vtkNew<vtkTable> table; vtkNew<vtkFloatArray> RPM; RPM->SetName("RPM"); table->AddColumn(RPM); vtkNew<vtkFloatArray> power; power->SetName("Engine Power"); table->AddColumn(power); // Fill in the table with some example values int numPoints = EngineCharacteristics::maximumRevolutionsPerSecond/100; table->SetNumberOfRows(numPoints); for (int i = 0; i < numPoints; ++i) { table->SetValue(i, 0, i * 100); if (i==0) { EngineCharacteristics::enginePower = 0; } else { EngineCharacteristics::enginePower = -(calculate_total_work(EngineCharacteristics::constantPressureSpecificHeat, EngineCharacteristics::constantVolumeSpecificHeat, EngineCharacteristics::gasConstant, EngineCharacteristics::intakePlenumAirTemperature,EngineCharacteristics::heatingValueOfFuel,EngineCharacteristics::airToFuelRatio,EngineCharacteristics::compressionRatio,EngineCharacteristics::rodToHalfStrokeRatio,EngineCharacteristics::combustionChamberVolume,EngineCharacteristics::intakeValveDiameter,EngineCharacteristics::intakeValveStemDiameter,EngineCharacteristics::atmosphericPressure,EngineCharacteristics::ratioOfSpecificHeats,EngineCharacteristics::frictionCoefficient,EngineCharacteristics::maximumIntakeValveLift,EngineCharacteristics::intakeAdvertisedDuration,EngineCharacteristics::lobeSeparationAngle,EngineCharacteristics::exhaustAdvertisedDuration,EngineCharacteristics::maximumExhaustValveLift,EngineCharacteristics::exhaustValveDiameter,EngineCharacteristics::exhaustValveStemDiameter,EngineCharacteristics::engineLoad,EngineCharacteristics::combustionStartCrankAngle,EngineCharacteristics::combustionDurationInAngles,EngineCharacteristics::weibeFormFactorN,EngineCharacteristics::weibeEfficiencyFactorA,i*100))*EngineCharacteristics::numberOfCylinders * (i*100) / (60 * 2 * 1000); enginePowerCounter.setValue(i*100/numPoints); } table->SetValue(i, 1, EngineCharacteristics::enginePower); } // Set up the view mView->GetRenderer()->SetBackground(1.0, 1.0, 1.0); // Add multiple line plots, setting the colors etc mChart->SetTitle("Hales Dynamometer"); mView->GetScene()->AddItem(mChart); vtkSmartPointer<vtkPlot> line = mChart->AddPlot(vtkChart::LINE); line->SetInputData(table, 0, 1); line->SetColor(255, 0, 0, 255); line->SetWidth(2.0); mConnections->Connect( mChart, vtkCommand::SelectionChangedEvent, this, SLOT(selectionChanged(vtkObject*, unsigned long, void*, void*))); this->mQVtkWidget->GetRenderWindow()->Render(); } MainWindow::~MainWindow() { delete ui; }
counter.hpp
#ifndef COUNTER_HPP #define COUNTER_HPP #include <QObject> class Counter : public QObject { Q_OBJECT public: Counter() { m_value = 0; } int value() const { return m_value; } public slots: void setValue(int value) { m_value = value; } signals: void valueChanged(int newValue); private: int m_value; }; #endif // COUNTER_HPP
-
Hi,
First thing: do not create static QObject based object like that.
Next: when calling setup from the constructor, you try to update a progress bar in a widget that in fact does not exists physically yet. You won't see anything while doing this in your constructor.
As for why there's no update at all: you never emit the signal in your counter object. In your slot you just update the value. Therefore nothing more will happen. Signals are not automatically emitted when you implement it like that. What you should do is to compare if the new value is different from the old one and emit the signal it's the case.
-
@ke7omc said in How to update progress bar?:
connect(&enginePowerCounter,SIGNAL(valueChanged(int newValue)),ui->progressBar,SLOT(setValue(int)));
Have you verified this returned success? Do you get a message? I was unable to find a formal definition for whether old-style
SIGNAL
/SLOT()
actually accepts parameter names, but according to https://www.informit.com/articles/article.aspx?p=1405224&seqNum=2Similarly, Qt will give a warning if parameter names are included in the signal or slot signatures.
You really should change over to New Signal Slot Syntax.
-
@SGaist
Thank you for responding. Regarding the first thing about static QObject, could you help me understand what should be done? I am fairly new to programming and was just trying to follow the example in the documentation here.Regarding the second thing. I might be getting confused about the order of things. So when the program opens there is some small sample code that is run and updating the progress bar is not so important. However, after the user inputs their data and clicks simulate engine button, setup() is called a second time. During the second time is when I would like the progress bar to start updating. Are you saying that the widget also does not yet physically exist after the user clicks the simulate button? If so, I am a little confused as the widget is essentially a graph that after clicking simulate engine button would show the initial graph from just opening the program, as well as showing a second line in the same graph from the user input data.
As for the third thing, thank you for helping me understand that. I have now added the compare that you suggested as well as the emit command.
-
@JonB
Thanks for the suggestion Jon! I was not aware that the notation had changed and was just trying to follow some examples I found online. I do have a question about the new method. Here is the new code I tried:connect(enginePowerCounter,&enginePowerCounter::valueChanged,ui->progressBar,&progressBar::setValue);
However, I got these errors:
mainwindow.cpp:34:33: error: 'enginePowerCounter' is not a class, namespace, or enumeration
mainwindow.cpp:26:9: note: 'enginePowerCounter' declared here
mainwindow.cpp:34:82: error: use of undeclared identifier 'progressBar'Can you help me understand what I am doing wrong with the new notation?
Thanks, -
@ke7omc
Re-read, for your own benefit, that wiki page.Note that the slots are methods in a class. You cannot address them via
instance::method
, it must beClass::method
. So:connect(enginePowerCounter, &EnginePowerCounterClass::valueChanged, ui->progressBar, &QProgressBar::setValue);
-
Make your instance of counter a class member.
-
@JonB
Thanks Jon. Originally I had done that but I got this error:
"mainwindow.cpp:34:85: error: call to non-static member function without an object argument"
which is why I thought I might have to use the instance of the Counter Class. I am not really sure why I am getting this error if it is supposed to be Class::Method. -
@ke7omc
You also need to do as @SGaist said :)Make your instance of counter a class member.
You're going to be calling a class method, so you're going to need to call it on a correct class instance object!
Note how for the slot you have
ui->progressBar, &QProgressBar::setValue
ui->progressBar
is an instance ofQProgressBar
. You need the same for the signal side.connect(senderInstance, &SenderClass::signalMethod, receiverInstance, &ReceiverClass::slotMethod)
This will arrange, somehow, for
emit senderInstance->signalMethod()
to callreceiverInstance->slotMethod()
. By magic.