In my game I need a stopwatch to measure and show the elapsed time.
For this purpose I made a simple widget:
ZuulStopwatchWidget::ZuulStopwatchWidget(QWidget *parent) :
QWidget(parent)
{
num = new QLCDNumber(this); // create the display
num->setDigitCount(9);
time = new QTime();
time->setHMS(0,0,0,0); // set the time
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(showTime()));
i=0;
QString text = time->toString("hh:mm:ss");
num->display(text);
//num->setStyleSheet("* { background-color:rgb(199,147,88);color:rgb(255,255,255); padding: 7px}}");
num->setSegmentStyle(QLCDNumber::Flat); //filled flat outline
//setStyleSheet("* { background-color:rgb(236,219,187)}}");
layout = new QVBoxLayout(this);
layout->addWidget(num);
setMinimumHeight(70);
}
ZuulStopwatchWidget::~ZuulStopwatchWidget()
{
// No need to delete any object that has a parent which is properly deleted.
}
void ZuulStopwatchWidget::resetTime()
{
time->setHMS(0,0,0);
QString text = time->toString("hh:mm:ss");
num->display(text);
i=0;
stopTime();
}
void ZuulStopwatchWidget::startTime()
{
//flag=0;
timer->start(1);
}
void ZuulStopwatchWidget::stopTime()
{
timer->stop();
}
void ZuulStopwatchWidget::showTime()
{
QTime newtime;
//if(flag==1)
//i=i-1;
i=i+1;
newtime=time->addMSecs(i);
QString text = newtime.toString("mm:ss:zzz");
num->display(text);
}
But when I run my game the CPU usage is at about 13% on a 2,5Ghz i5. I know this is not problematic but it sure is ridiculous for a stupid clock.
Am I doing it completely wrong or is this common practice ?!
Many thanks in advance.
Start(1) sets the timer to trigger every millisecond
You then want to format a string and print it on the screen 16times faster than the screen is probably updating anyway
Related
How to initializa the operation of QProgressBar, I already declare her maximum, minimum, range and values.
I want to assimilate the progress of QProgressBar with the "sleep_for" function.
Current code:
void MainPrograma::on_pushCorre_clicked()
{
QPlainTextEdit *printNaTela = ui->plainTextEdit;
printNaTela->moveCursor(QTextCursor::End);
printNaTela->insertPlainText("corrida iniciada\n");
QProgressBar *progresso = ui->progressBar;
progresso->setMaximum(100);
progresso->setMinimum(0);
progresso->setRange(0, 100);
progresso->setValue(0);
progresso->show();
WORD wEndereco = 53606;
WORD wValor = 01;
WORD ifValor = 0;
EscreveVariavel(wEndereco, wValor);
//How to assimilate QProgressBar to this function:
std::this_thread::sleep_for(std::chrono::milliseconds(15000));
//StackOverFlow help me please
EscreveVariavel(wEndereco, ifValor);
use a QTimer
and in the slot update the value of the progressbar
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
t = new QTimer(this);
t->setSingleShot(false);
c = 0;
connect(t, &QTimer::timeout, [this]()
{ c++;
if (c==100) {
c=0;
}
qDebug() << "T...";
ui->progressBar->setValue(c);
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
t->start(100);
}
I'm not sure about your intentions with such sleep: are you simulating long wait? do you have feedback about progress during such process? Is it a blocking task (as in the example) or it will be asynchronous?
As a direct answer (fixed waiting time, blocking) I think it is enough to make a loop with smaller sleeps, like:
EscreveVariavel(wEndereco, wValor);
for (int ii = 0; ii < 100; ++ii) {
progresso->setValue(ii);
qApp->processEvents(); // necessary to update the UI
std::this_thread::sleep_for(std::chrono::milliseconds(150));
}
EscreveVariavel(wEndereco, ifValor);
Note that you may end waiting a bit more time due to thread scheduling and UI refresh.
For an async task you should pass the progress bar to be updated, or some kind of callback that does such update. Keep in mind that UI can only be refreshed from main thread.
I have a function that should send data to a raspberry pi for a given period of time depending on the parameter.
//headerfiles
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QUdpSocket udpSocket;
// movement Timer
QTimer* movementTimer;
private slots:
void sendDatagram(); // Sends to the RaspberryPi
void processFrameAndUpdateGUI();
void turnLeft(double time);
void turnRight(double time);
void goStraight(double time);
void MovRobot();
};
// MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
movementTimer = new QTimer(this);
//send datagram sends data to tehe PI.
connect(movementTimer, SIGNAL(timeout()), this, SLOT(sendDatagram()));
MovRobot(); // MovRobot() function
}
// controls robot to turn left for specified time
void TurnLeft(double time)
{
s = 1; // sets the value to be sent to the PI.
movementTimer->setInterval(time);
movementTimer->setSingleShot(true);
movementTimer->start();
}
//sendDatagram() slot
void MainWindow::sendDatagram() {
QString datagramOutput = "start," +
QString::number(w) + ',' + QString::number(a) + ',' +
QString::number(s) + ',' + QString::number(d) + ',' +
QString::number(ui->motorSpeedSlider->value()) + ',' +
QString::number(dispenserSignal);
datagramOutput += ",end";
QByteArray datagram;
QDataStream out(&datagram,QIODevice::WriteOnly);
out << datagramOutput;
udpSocket.writeDatagram(datagram,QHostAddress("192.168.0.104"),12345);
}
//MovRobot Function;
void MainWindow :: MovRobot() {
if (ui->pushButton_3->isChecked()) {
MapArea(); // This function maps the area....
// final_plan is a vector<Point2i> that stores positions on the map for
// the robot to move to
for (int i = 0; i < final_plan.size(); i++) {
for (int j = final_plan.size() - 1; j>=0; j--) {
do {
Mat src;
bool bsuccess = cap.read(src);
if (!bsuccess) {
ui->label_34->setText("Status: Can't read frame.");
ui->pushButton_3->setChecked(0);
}
GetRobotPosition(src);
// AngleToGoal calculates the angle of the robot relative
// to the final goal.
double tempAngle = AngletoGoal(final_plan[i][j]);
if (tempAngle>=0) {
turnLeft(TimeToTurn(tempAngle));
}
else {
turnRight(TimeToTurn(tempAngle));
}
double tempDistance = DistancetoGoal(final_plan[i][j]);
goStraight(tempDistance);
} while (DistancetoGoal(final_plan[i][j])<20);
}
}
}
}
The timer should only send the data over to the PI for the duration it in time sendDatagram is the function that sends the data over to the Pi. Is there anything I'm missing here. The timer doesn't start inside the TurnLeft() function and doesn't run at all. Am I going about this wrong?
EDIT: 13/03/2016:
My apologies for the late reply. I've been quite sick the past few days. I've added the relevant parts of the code. MovRobot() is the main function responsible for the movement and this is called in the constructor for MainWindow. I have debugged and stepped through the program and yes, TurnLeft() is called. However, the sendDatagram() slot doesn't actually send anything in the function. To confirm sendDatagram() was actually working, I used another timer to continuously send information to the PI on the robot to control the arm.
// Header File
QTimer* tmrTimer;
private slot:
void processFrameAndUpdateGUI();
// MainWindow Constructor:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
connect(tmrTimer, SIGNAL(timeout()), this, SLOT(processFrameAndUpdateGUI()));
tmrTimer->start(10);
}
void MainWindow::processFrameAndUpdateGUI() {
sendDatagram();
}
The sendDatagram() slot is pretty much the same with the exception of me changing what values are being sent to the PI and this seems to work perfectly.
However, my original problem is, I would like to send the data to the robot with for a specified amount of time as that makes the robot turn x degrees. This is why I've made movementTimer() single shot.
Stepping through my program, I know this line is called within my TurnLeft function.
movementTimer->start();
but the sendDatagram() slot itself doesn't actually send anything to the PI.
Based on your code. The program never called TurnLeft(). So the timer never started. It will be better to start the timer in constrator instead.
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(MySlot()));
timer->start(1000);
I would try setting the timer to start in the constructor.
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.
I am trying to get a text edit box to display the current time every 5 seconds using QTimer. I am having the current time figured in a separate method and then having the QTimer call that method and display the current time. I can not for the life of me figure out how to pass the variable from the setCurrentTime method to the QTimer. Im sure it a really easy fix but I cant figure it out. Here is my code.
void noheatmode::setCurrentTime()
{
QTime time = QTime::currentTime();
QString sTime = time.toString("hh:mm:mm");
// ui->tempTimeNoHeatMode->append(sTime);
}
void noheatmode::on_timeButton_clicked()
{
QTimer *timer =new QTimer(this);
connect(timer,SIGNAL(timeout()), this, SLOT(setCurrentTime()));
timer->start(5000);
ui->tempTimeNoHeatMode->append(sTime);
}
If I got your problem right, you just have minutes variable instead of a seconds. Just change "hh:mm:mm" to "hh:mm:ss"
void noheatmode::setCurrentTime()
{
QTime time = QTime::currentTime();
QString sTime = time.toString("hh:mm:ss");
ui->tempTimeNoHeatMode->append(sTime);
}
With your code:
void noheatmode::on_timeButton_clicked()
{
QTimer *timer =new QTimer(this);
connect(timer,SIGNAL(timeout()), this, SLOT(setCurrentTime()));
timer->start(5000);
ui->tempTimeNoHeatMode->append(sTime);
}
This means that the function within SLOT will be called every 5000 milliseconds which = 5 seconds. What could be done then is that you set your function setCurrentTime() to update your text box every time it is called.
Example:
void Class::setCurrentTime()
{
QTime times = (QTime::currentTime());
QString currentTime=times.toString("hh:mm:ss");
ui->label->setText(currentTime);
//Assuming you are using a label to output text, else substitute for what you are using instead
//Every time this function is called, it will receive the current time
//and update label to display the time received
}
Almost done with that application I've been working on BUT, now I have one more problem. I created a QProgressBar and connected it to a QTimer. It goes up one percent per second but surpasses the actual progress. I have yet to program in the former however I set up the timer to go up one every second. Here is my problem the progress bar goes up to one percent then stops. It hits the if statement every second I know that, but it doesn’t go any higher then 1%.
Edit:
Sorry meant to add the code.
#include "thiwindow.h"
#include "ui_thiwindow.h"
#include <QProcess>
#include <fstream>
#include <sstream>
int ModeI;
ThiWindow::ThiWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ThiWindow)
{
ui->setupUi(this);
std::ifstream ModeF;
ModeF.open ("/tmp/Mode.txt");
getline (ModeF,ModeS);
std::stringstream ss(ModeS);
ss >> ModeI;
ModeF.close();
SecCount = new QTimer(this);
Aproc = new QProcess;
proc = new QProcess;
connect(SecCount, SIGNAL(timeout()), this, SLOT(UpdateProcess()));
connect(Aproc, SIGNAL(readyRead()), this, SLOT(updateText()));
connect(proc, SIGNAL(readyRead()), this, SLOT(updateText()));
SecCount->start(1000);
if (ModeI==1)
Aproc->start("gksudo /home/brooks/Documents/Programming/AutoClean/LPB.pyc");
else
proc->start("/home/brooks/Documents/Programming/AutoClean/LPB.pyc");
ui->progressBar->setValue(0);
}
ThiWindow::~ThiWindow()
{
delete ui;
}
void ThiWindow::updateText()
{
if (ModeI==1){
QString appendText(Aproc->readAll());
ui->textEdit->append(appendText);}
else{
QString appendText(proc->readAll());
ui->textEdit->append(appendText);}
}
void ThiWindow::UpdateProcess()
{
SecCount->start(1000);
int Count=0;
float Increments;
int Percent_limit;
if (ModeI==1){
Increments = 100/5;
Percent_limit = Increments;
if (Count<Percent_limit) {
Count += 1;
ui->progressBar->setValue(Count);
}
}
}
If you need more let me know.
Thanks,
Brooks Rady
You are always incrementing zero. int Count=0; This have to be removed from this function and moved for example to constructor where timer is started and declare it in header file ( shown in last two code snipets )
void ThiWindow::UpdateProcess()
{
SecCount->start(1000);
int Count=0; // Count is 0
float Increments;
int Percent_limit;
if (ModeI==1){
Increments = 100/5;
Percent_limit = Increments;
if (Count<Percent_limit) {
Count += 1; // Count is 0 + 1
ui->progressBar->setValue(Count); // progressBar is 1
}
}
}
You have to declare Count in header file. Count will be stored as long as ThiWindows exists. Not only for a few miliseconds in your example ( Count was destroyed when your UpdateProccess functions finish and then recreated again when it was called again )
class ThiWindow : public QMainWindow {
Q_OBJECT
public:
// whatever you have
private:
int Count;
}
Count should be initialized before Timer starts
SecCount = new QTimer(this);
Aproc = new QProcess;
proc = new QProcess;
connect(SecCount, SIGNAL(timeout()), this, SLOT(UpdateProcess()));
connect(Aproc, SIGNAL(readyRead()), this, SLOT(updateText()));
connect(proc, SIGNAL(readyRead()), this, SLOT(updateText()));
Count = 0; // << move Count variable here
SecCount->start(1000);