After adding some points to QtChart it gets slow - c++

I need help. Here is my code (i show main part):
// Arduino Reader
serial.setPortName("/dev/ttyACM0");
serial.open(QIODevice::ReadWrite);
serial.setBaudRate(QSerialPort::Baud9600);
serial.setDataBits(QSerialPort::Data8);
serial.setParity(QSerialPort::NoParity);
serial.setStopBits(QSerialPort::OneStop);
serial.setFlowControl(QSerialPort::NoFlowControl);
connect(&serial,SIGNAL(readyRead()),this,SLOT(getNewData()));
...
void MainWindow::getNewData()
{
std::clock_t starter = std::clock();
QByteArray data = serial.readAll();
QDataStream in(serial.readAll());
getData = getData + data;
int start = getData.indexOf(":START:");
int end = getData.indexOf(":END:",start);
QString nowWork = getData.mid(start,end-start+5);
if (nowWork.startsWith(":START:") && nowWork.endsWith(":END:")){
QStringList mod = nowWork.remove(":START:").remove(":END:").split(":");
int xPoint = mod[0].toInt();
int yPoint = mod[1].toInt();
int zPoint = mod[2].toInt();
int aPoint = mod[3].toInt();
int bPoint = mod[4].toInt();
int cPoint = mod[5].toInt();
addQuery(1,xPoint,yPoint,zPoint);
addQuery(2,aPoint,bPoint,cPoint);
getData = getData.right(end+5);
}
std::clock_t ender = std::clock();
ui->ping->setText(QString::number( (ender-starter)/ (double)CLOCKS_PER_SEC *100));
return;
}
...
void MainWindow::addQuery(int to, int x, int y, int z){
seriesAllD1->append(now, (x+y+z)/3 );
seriesXD1->append(now,x);
seriesYD1->append(now,y);
seriesZD1->append(now,z);
seriesAllD1->remove(0);
seriesXD1->remove(0);
seriesYD1->remove(0);
seriesZD1->remove(0);
chartAllD1->axisX()->setRange(now-100,now);
chartX->axisX()->setRange(now-100,now);
chartY->axisX()->setRange(now-100,now);
chartZ->axisX()->setRange(now-100,now);
now++;
return;
}
I try to describe code:
1) It gets data from arduino (Like a ":START:X:Y:Z:A:B:C:END::START:...").
2) It gets indicates from 2-sensors (3-axis accelometr). And draws Graphs.
I show some code block. After adding ~900 point it gets slow from 60 ms to 1000 ms. I think it is related to QtChart (i use them in project) or addind points to series. Please help me(

This is an old topic - however I also ran into it when working with QtCharts - so: for others who will stumble on the same problems with QtCharts and would also like to use QtCharts here are my solutions:
Use OpenGL:
call setUseOpenGL(true) on the series in the plot (will only work with QLineSeries and QScatterSeries) - unfortunately this is not the default, but it could be a problem on some systems
When not using OpenGL (i.e. also using QAreaSeries):
do not use AntiAliasing
when adding large amounts of data in one loop to the series use rather replace than add - see https://stackoverflow.com/a/52334549/2056545
And this special trick a colleague came up with when working with real time data:
set all series to signalsBlocked(true), start a timer when blocking, unblock after a second, block again and start timer when adding new data again - this will update the plots on (about) every second, but the performance impact is low

Related

Updating QChart from QLineSeries in a running while loop

I want to make my QChart dynamically update whenever a point is added to the QLineSeries object attached to it, but it seems that this update only occurs after the while loop I am running has finished. I am using said while loop in interface.cpp that calls a function updatePlot() which adds the data point to the line series, but this only updates the chart after the while loop has completely finished. Pseudo code of what is happening here:
qtwindow.cpp
// Constructor that initializes the series which will be passed into the interface
AlgoWindow::AlgoWindow( ..., TradingInterface* interface, ... ) {
...
QLineSeries* series = new QLineSeries();
QLineSeries* benchmark = new QLineSeries();
QChart* chart = new QChart();
chart->addSeries(series);
chart->addSeries(benchmark);
// Also creates custom axes which are attached to each series
...
}
// Slot connected to a button signal
void AlgoWindow::buttonClicked() {
// Runs the backtest
interface->runbacktest(..., series, benchmark, ...);
}
interface.cpp
void TradingInterface::runbacktest(..., QtCharts::QLineSeries* algoplot, QtCharts::QLineSeries* benchplot) {
// Runs a huge while loop that continuously checks for events
while (continue_backtest) {
if (!eventsqueue.isEmpty()) {
// Handle each event for the bar
} else {
// All events have been handled for the day, so plot
updatePlot(algoplot, benchplot);
}
}
}
void TradingInterface::updatePlot(QtCharts::QLineSeries *algoseries,
QtCharts::QLineSeries *benchseries) {
// Get the date and the information to put in each point
long date = portfolio.bars->latestDates.back();
double equitycurve = portfolio.all_holdings.rbegin().operator*().second["equitycurve"];
double benchcurve = benchmarkportfolio.all_holdings.rbegin().operator*.second["equitycurve"];
// Append the new points to their respective QLineSeries
algoseries->append(date * 1000, equitycurve*100);
benchseries->append(date * 1000, benchcurve*100);
}
This gives me no errors and the while loop completes, but the lines are only plotted after runbacktest() exits. It then plots all the data correctly, but all at once.
What I need to happen is for the QChart to update every time the lines are added, which my guess was to use some form of custom signal-slot listener but I have no clue how to go about that. If the graph will not update until after the function completes, is it even possible within the QChart framework?
Also, I have already tried QChart::update() and QChartView::repaint(). Both produced the same results as without.
EDIT: I tried setting up a new thread that emits a signal back to the main thread whenever the data is completed but it seems to have changed nothing. The QChart still does not update until after all the data has been inputted. I added a couple lines to help debug and it seems like the function which emits the signal runs consistently just fine, but the slot function which receives the signal only runs after the thread has finished. Not only that, but slowing the signals down with a sleep does not make it plot slowly (like I thought), as the QChart still refuses to update until after the final update to addData().
Either remove your while loop and perform the work one step at a time with a timer.
Or run your runbacktest function in another thread and send a signal to update the QChart in the UI's thread when the data is ready.
Either way you need to give control back to the event loop so that the chart can be repainted.
The Qt idiom for running an operation “continuously” is to use a zero-duration “timer”. It’s not a timer really, but Qt calls it one.
You can do the operation in chunks that take approximately a millisecond. For this, invert the control flow. Qt doesn't provide too much syntactic sugar for it, but it's easy to remedy.
Convert this code, which maintains a loop:
for (int i = 0; i < 1000; ++i) {
doSomething(i);
}
into this lambda, which is invoked by the event loop:
m_tasks.addTask([this](i = 0) mutable {
doSomething(i);
++i;
return i < 1000;
});
assuming:
class Controller : public QObject {
Tasks m_tasks;
...
};
where the Tasks class maintains a list of tasks to be executed by the event loop:
class Tasks : public QObject {
Q_OBJECT
QBasicTimer timer;
std::list<std::function<bool()>> tasks;
protected:
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() != timer.timerId())
return;
for (auto it = tasks.begin(); it != tasks.end(); ) {
bool keep = (*it)();
if (!keep)
it = tasks.erase(it);
else
++it;
}
if (tasks.empty())
timer.stop();
}
public:
using QObject :: QObject;
template <typename F> void addTask(F &&fun) {
tasks.emplace_back(std::forward(fun));
if (!timer.isActive())
timer.start(0, this);
}
};

Spawning waves of enemies C++

I'm creating a simple game with qt 5.0.1. It's something like Warblade.
I have problem with creating waves of enemies.
int k;
int pos = 100;
for (k = 0; k < 5; k++)
{
pos = 100;
for (int i = 0; i < 9; i++)
{
player->spawn_in_pos(pos);
pos += 100;
}
//QThread::sleep(2);
}
When i use sleep() function, my game just can't run. It's waiting for loop finish and then it shows.
I'm also dealing with second option:
QTimer * timer = new QTimer();
QObject::connect( timer, SIGNAL(timeout()), player, SLOT(spawn_in_pos(pos)) );
timer->start(450);
But it looks like SLOT can't get the position.
Edit:
I just did what #ddriver said, and that helped me a lot.
Now I'm getting some 'laggy' style enemies movement.
Edit2:
I'm moving my enemies down like this:
setPos(x(),y()+1);
with that timer:
// connect
QTimer * timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(move()));
// start the timer
timer->start(10);
It looks like very smooth movement but probably +1 pixel down and a 10 timer is to less:((
I'm not sure what you are trying to achieve, but in your second option, you cannot get the position, because the timeout doesn't send it.
The signal is timeout(void) and your slot expects an parameter. I guess you lack some basic understanding of the signal/slot mechanism.
The QT Documentation is pretty neat:
http://doc.qt.io/qt-5/signalsandslots.html
And if you just want to create a game out of nothing, here you can find a little tutorial, how to write games in QT:
https://www.youtube.com/watch?v=8ntEQpg7gck
Calling sleep is going to stop the thread from processing anything, which is not what you want to do.
Using C++ 11, you can use the QTimer with a lambda function like this: -
int pos = 100;
int nextWaveTime = 2000; // 2 seconds per wave
for (k = 0; k < 5; k++) // 5 waves of enemies
{
for (int i = 0; i < 9; i++) // 9 enemies per wave
{
QTimer * timer = new QTimer();
timer->setSingleShot(true);
pos = pos + (100*i); // set the pos, which is captured by value, in the lambda function
QObject::connect( timer, QTimer::timeout, [=](){
player->spawn_in_pos(pos);
timer->deleteLater(); // must cleanup the timer
});
timer->start(450 + (k*nextWaveTime));
}
}
In order to pass parameters with signals and slots in Qt, the signal parameters must match the parameters of the slot (or function since Qt 5).
One way to solve the issue is to use a lambda as in TheDarkKnight's answer.
What I would suggest is to use encapsulation - you could create a Spawner object, dedicated to spawning enemies and keep the position internal to it. This way the spawner will manage the position, and you can have something like Spawner::createWave() slot with no parameters, since the position is internal. Then setup the timer and connect it to createWave() and you are set.
Also it is a very bad idea to hardcode stuff like that, you really need more flexibility, the option to change enemy and wave count, the wave time as well as the screen width, so that your game can change those things as it gets harder.
class Spawner : public QObject {
Q_OBJECT
public:
Spawner(int wCount = 5, int eCount = 9, int time = 2000, int sWidth = 1000)
: waveCount(wCount), enemyCount(eCount), currentWave(0), screenWidth(sWidth) {
timer.setInterval(time);
connect(&timer, SIGNAL(timeout()), this, SLOT(createWave()));
}
void set(int wCount, int eCount, int time) {
timer.setInterval(time);
waveCount = wCount;
enemyCount = eCount;
}
void changeWidth(int w) { screenWidth = w; }
public slots:
void start() { timer.start(); }
void stop() {
timer.stop();
currentWave = 0;
}
private slots:
void createWave() {
int pos = screenWidth / (enemyCount + 1);
int step = pos;
for (int i = 0; i < enemyCount; ++i) {
Game::spawnEnemyAt(pos);
pos += step;
}
if (++currentWave >= waveCount) stop();
}
private:
QTimer timer;
int waveCount, enemyCount, currentWave, screenWidth;
};
Create a Spawner object and connect the game new level to start() - it will span the given number waves of enemies evenly across the game screen, when you finish the waves off, you adjust the spawner settings and start a new level.
That encapsulation will come in handy later on as your game becomes less of a test and more like a real game - with increasing difficulty, changing spawning and attack patterns and so on. So it is a good idea to implement it right from the start and build upon a good and flexible design rather than going back and changing stuff around, which may break other code. You really don't want to start without a good design and make design changes later. Thus the need to encapsulate functionality and responsibility and just connect the pieces rather than building on a pile of spaghetti code. In this line of thought, I noticed you are using player->spawn_in_pos(pos); - which is an example of bad design, as spawning should be a responsibility of the Game class, not the Player class. A good design is not only flexible, but also clean. The Spawner object is only responsible for spawning waves of enemies, and its visible interface is limited to start(), stop() and set().
Edit:
class Game : public QObject {
Q_OBJECT
public:
Game() {
if (!scene) scene = new QGraphicsScene(this);
connect(this, SIGNAL(newLevel()), &spawner, SLOT(start()));
}
static void spawnEnemyAt(int x = 0) {
scene->addItem(new Enemy(x, 0));
qDebug() << "enemy created";
}
public slots:
void newGame() {
// initialize game
emit newLevel(); // begin spawning
}
void onLevelEnd() {
// spawner.set(new level settings);
emit newLevel();
}
void onGameEnded() {
// ...
}
signals:
void newLevel();
private:
Spawner spawner;
static QGraphicsScene * scene;
};
// in game.cpp
QGraphicsScene * Game::scene = nullptr;
If you don't want to use static members, you can make spawnEnemyAt() and scene instance members, but then you will have to pass the Game instance to the Spawner in the constructor so that you have a reference to the game the spawner operates on and use game->spawnEnemyAt() instead. This way you can create multiple games with their own dedicated scenes. Or parent the spawner to the game and cast the spawner's parent() to a Game * to access the game instance which is a little hacky, but saves on the extra member by reusing the parent.

How do I make a custom event with VTK?

I'm making a thread software with VTK, where I need to change the model itself in real time, while I need to change his method of rendering. Everything is working fine, but, the problem start with the interactor->start(); , the model data gets updated just fine, but it's only showed on screen when I move The camera. Also I have selected some methods for generating a 3D data from a imagedata file, for that I need to close the vtk window (interactor window) and then the code will reopen it and send the new data generated to it...
I would need something like these:
int force close_window = false; int refresh_interactor = false;
I managed to make the Window close, but only with vtkcommand::Keypressed command, but idk how do I do with a new command :S, I tried the vtkcommand::UserEvent but I didn't found a good information about how to deal with that data (like some way to call it)
the way I'm dealing with VTK is with two threads, the first one, is just about the vtk iren loop, and the second one would manage the models and check if iren requires to be updated.
In my dream code it should be something like this:
=======================================================
bool VTKWindow()
{
...
vtkSmartPointer ator = vtkSmartPointer::New();
iren = vtkSmartPointer::New();
RenWindow = vtkSmartPointer::New();
render->SetBackground(.1, .2, .3);
RenWindow->AddRenderer(renderer);
iren->SetRenderWindow(RenWindow);
if(data_type == voxel_type)
{
Render->AddViewProp(VoxelData);
}
else
{
actor->SetMapper(PolyData);
Render->AddActor(Actor);
}
RenWindow->Render();
iren->Start();
}
void ManageVTK()
{
while true loop...
if(force close_window == true)
do some command to exit the iren loop
if(refresh_interactor == true)
do some command to refresh iren
}
Sorry for the english, it's not my native language, and also sorry about the question format, it's the first time I'm using stackoverflow
It may sounds stupid, but, I found a kind of solution for the problem.
I saw on related links this guy vtkRenderWindowInteractor event loop and threading and, it's almost the same problem...
class VTKNewEvent : public vtkCommand{
public:
vtkTypeMacro(VTKNewEvent , vtkCommand);
static VTKNewEvent * New(){
return new VTKNewEvent ;
}
void Execute(vtkObject * caller, unsigned long vtkNotUsed(eventId), void * vtkNotUsed(callData)){
vtkRenderWindowInteractor *iren = static_cast<vtkRenderWindowInteractor*>(caller);
if (iren_close == true){
iren->GetRenderWindow()->Finalize // Stop the interactor
iren->TerminateApp();
iren_close = false;
}
if (iren_update== true){
renderJanela->Render();
iren_update= false;
}
}
};
bool VTKWindow(){
vtkSmartPointer<VTKNewEvent > IrenRefresh= vtkSmartPointer<VTKNewEvent>::New();
...
iren->CreateRepeatingTimer(1);//this makes that IrenRefresh will be called at every 1ms
iren->AddObserver(vtkCommand::TimerEvent, IrenRefresh);
iren->Start();
...
}
it's simple, but, maybe not the best, but it did Th job, I hope this link will help people that are starting into the VTK world, since threads + rendering loop wasn't a simple job to understand what was going on

Animation using .png files cocos2dx

I have 34 .png image files for 9 different scenarios. Each time the user picks 1 of those 9 scenarios and according to that I generate animation using the following
std::string name;
MixingScreen::animation = CCAnimation::create();
// load image file from local file system to CCSpriteFrame, then add into CCAnimation
for (int i = 0; i < 34; i++)
{
std::stringstream st,ii;
st << Constants::flav;
if (i<10)
{
ii<<i;
name="screen/screen02/fx/sugar/0" + st.str()+"/a_0000"+ii.str()+".png";
}
else
{
ii<<i;
name="screen/screen02/fx/sugar/0" + st.str()+"/a_000"+ii.str()+".png";
}
//sprintf(szImageFileName, "Images/grossini_dance_%02d.png", i);
MixingScreen::animation->addSpriteFrameWithFileName(name.c_str());
}
MixingScreen::animation->setDelayPerUnit(5.0f / 34.0f);
action = CCAnimate::create(MixingScreen::animation);
CCCallFunc *done = CCCallFunc::create(this, callfunc_selector(MixingScreen::doneMixing));
CCSequence *readySequence = CCSequence::create(action,done,NULL);
particles->runAction(readySequence);
The problem I am facing is that when this animation runs, there is a time lag(everything stops for few seconds) and then the animation starts. Any solution?
Every time you call animation->addSpriteFrameWithFileName() a new "CCSpriteFrame" is created.
Instead you should first add the sprite frames to CCSpriteFrameCache and use
animation->addSpriteFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameWithName(""))
Ps: these function names might be a little different but i hope you get the idea.

QTime->addSecs cause segmentation fault

I'm writing simple pomodoro application, which is basically countdown timer. Right now, I've got countdown working, but the weird thing is that when I add another attribute to my class (arbitrary), I get Sedmentation fault error.
Using gdb, the problem should be here:
void Status::showPomodoroTime() {
QTime time = pomodoroTime->addSecs(elapsed);
activeTime->display(time.toString("mm:ss"));
}
where activeTime is QLCDNumber widget and elapsed is int.
More context:
void Status::createDefaultIntervals()
{
pomodoroInterval = new QTime(0, 25);
pomodoroBreak = new QTime(0, 5);
pomodoroLongBreak = new QTime(0, 15);
}
void Status::run()
{
if (pomodoroActive == STOP) {
pomodoroTime = pomodoroInterval;
showPomodoroTime();
}
pomodoroActive = RUN;
updateStatusArea();
timerTick();
}
CreateDefaultInterval definitely runs before showPomodoroTime.
What bugs me, that whole application works fine. Just when I add another attribute, it starts to throw sedfault.
How can variable declaration in *.h file cause segfault in *.cpp?
If you want more code, I can put it anywhere. I just don't know, what place is persistent enough. Don't want to post it here (about 300 lines of code).
check if(pomodoro!= NULL) and then do addSecs().
pomodoroTime is probably uninitialized or deleted