How to create timed transition in Qt? - c++

I am trying to create a timed transition in Qt (C++) and this is driving me nuts. I have three "LEDs" that are illuminated on the UI. When the user clicks a button, I want them to die sequentially, like this but in reverse:
I have this function that is called when the button is pressed (this is the slot):
void MainWindow::turnOff(int i){
while(i>0){
QTest::qSleep(250);
changeBrightness(i);
qInfo("\n\n\at easeOff %d", brightness);
i--;
}
}
and the changeBrightness function:
void MainWindow::changeBrightness(int i){
if(i > 0 && i <= 20){
ui->LED1->setStyleSheet("QLabel { color: black; }");
...
}
brightness = i;
}
The console shows the value changing, but the actual UI transition only happens at the ends (all the LEDs go off at once). Brightness is a value between 1 and 100. Any help is appreciated.

#sophryshko's suggestion of the repaint() function worked!
void MainWindow::turnOff(int i){
while(i>0){
QTest::qSleep(250);
ui->LED1->repaint();
changeBrightness(i);
qInfo("\n\n\at easeOff %d", brightness);
i--;
}
}
Thank you for the help. Will add the QTimer object as well but this was sufficient to change the UI behavior.

Related

Is there a better way to connect an array of QPushButton's to a slot so each button has a unique identifier when pushed?

I am creating a checkers board as part of a university assignment. There has been very little teaching around the GUI side of things. So I may have overcomplicated this project a bit and become stuck while trying to access the clicks from the board GUI.
I have read about lambda's in the documentation https://doc.qt.io/qt-5/qsignalmapper.html for signal mappers, but, I am not sure if it is right for the application I am coding. I have tried to implement the signal mapping so that when a particular button is pushed on the checker's board, the slot can identify it with its unique signal.
If there is a better way, or if lambdas are the best way, could you please explain how to implement this?
At the moment, my current code can't access the clicks made. I also get an error for the connect() function:
no known conversion from 'void QAbstractButton::* (bool)' to 'const char*' for the second argument.
connect(&checkers[i], &QPushButton::clicked, signalMapper, &QSignalMapper::map);
However, this compiles but does not work:
connect(&checkers[i], SIGNAL(clicked), signalMapper, SLOT(map));
Below is more complete code for greater context.
Constructor and board set function:
// Constructor
GameWindow::GameWindow(QWidget *parent):
QDialog(parent), ui(new Ui::GameWindow)
{
ui->setupUi(this);
BoardSetup(4);
}
/* Sets up the initial gameboard display with the other game information displays also being
* initialised. The game board is an array of QpushButtons that have there Icon
* changed according to where the pieces are on the board.
* Called by the GameWindow Constructor*/
void GameWindow::BoardSetup(int size)
{
{
QSignalMapper *signalMapper = new QSignalMapper(this->checkers);
connect(signalMapper, &QSignalMapper::mappedInt, this, &GameWindow::SquareClicked );
bool col=1;
for (int j=0;j<size;j++)
{
col=!col;
for(int i=0; i<size; i++)
{
if(col == 1) // logically check to see what Color the square needs to be.
{
checkers[i+j*size].setGeometry(QRect(width/size*j,width/size*i,width/size,width/size));
checkers[i+j*size].setFixedSize(QSize(width/size,width/size));
checkers[i+j*size].setStyleSheet("background-color: black");
}
else if(col==0)
{
checkers[i+j*size].setGeometry(QRect(width/size*j,width/size*i,width/size,width/size));
checkers[i+j*size].setFixedSize(QSize(width/size,width/size));
checkers[i+j*size].setStyleSheet("background-color: white");
}
col=!col;
signalMapper->setMapping(&checkers[i], i);
connect(&checkers[i], &QPushButton::clicked, signalMapper, &QSignalMapper::map);
}
}
Grid = new QGridLayout;
Grid->setGeometry(QRect(0,0,width,width));
for(int j=0; j<size; j++)
{
for(int i=0; i<size; i++)
{
Grid->addWidget(&checkers[i+j*size], j, i, Qt::AlignmentFlag::AlignCenter);
}
}
GridGroup = new QGroupBox();
GridGroup->setLayout(Grid);
ui->BoardGrid->addWidget(GridGroup);
ui->BoardGrid->setGeometry(QRect(0,0,width,width));
ui->BoardGrid->addWidget(GridGroup);
setWindowTitle("Checkers Game");
ui->P1PieceCount->setFontPointSize(40);
ui->P1PieceCount->setAlignment(Qt::AlignCenter);
ui->P2PieceCount->setFontPointSize(40);
ui->P2PieceCount->setAlignment(Qt::AlignCenter);
ui->TurnCount->setFontPointSize(40);
ui->TurnCount->setAlignment(Qt::AlignCenter);
ui->Player2Name->setAlignment(Qt::AlignCenter);
CheckersBoard(size);
SetInitialDisplay(size);
}
return;
}
How I am trying to access the clicks.
GetClicks() and WaitForClick() functions:
/* This function will get the clicks that are required to make the human move.
* Called by GameWindow::HumanMove */
void GameWindow::GetClicks()
{
connect(this, &GameWindow::SquareClicked, this, &GameWindow::WaitForClick);
}
/* This function will get the clicks that are required to make the human move.
* Called by GameWindow::GetClicks via signal SquareClicked */
void GameWindow::WaitForClick(int square)
{
firstclick = !firstclick;
unsigned int test=square;
if (firstclick ==0 && test != StartPos)
{
StartPos = square;
checkers[square].setChecked(1);
}
else if (firstclick == 1 && test == StartPos)
{
checkers[square].setChecked(0);
}
else if (firstclick==1 && test!= StartPos)
{
EndPos = square;
}
}
The StartPos and EndPos clicked are used in a possible move function to validate the move. It is then updated to the GUI.
Any help on how best to implement this signal and slot mechanism would be greatly appreciated.
Have a look at Qt calculator example?
https://doc.qt.io/qt-5/qtwidgets-widgets-calculator-example.html
you can connect multiple buttons to the same slot.
in slot, find out which button sent the signal using QObject::sender().
Once have the button, extract the position using MyButton::Pos().
(MyButton is the widget used for each of the board button)

Minesweeper. segfault at recursiv check

i am trying to programm Minesweeper with Qt in c++11.
If i press a button with 0 bombs, i want to check for buttons around this button and if they have 0 bombs, too. If they have 0 bombs, i want to check the buttons. (picture: red square)
This is my button-class:
#ifndef ABUTTON_H
#define ABUTTON_H
#include "QPushButton"
#include "QMouseEvent"
class AButton : public QPushButton
{
Q_OBJECT
public:
AButton(QWidget* parent);
//AButton();
~AButton();
bool& set(bool state); //set bomb state
bool get(); //get bomb state
void increment_counter(); //increment counter for surrounding bombs
int get_counter(); //get bomb counter
bool get_locked(); //is it locked (->flagged)
void set_locked(bool); //set it to locked
AButton* get_Button(char c); //get Button above, beneath, left, right
void set_Button(AButton* Button, char c); //set Button above, ...; char is for setting the right one
private:
bool bomb; //is button a bomb
int Nachbar_Bomben; // how many bombs around this button
bool locked; // is the button locked
AButton* b_links; //pointer to the button to the left
AButton* b_rechts; //pointer to the button to the right
AButton* b_oben; //pointer to the button above
AButton* b_unten; //pointer to the button beneath
public slots:
void mousePressEvent(QMouseEvent *event);
signals:
void rightclicked();
void leftclicked();
};
#endif // ABUTTON_H
This is what happens if a button is clicked:
void Layout::ButtonClicked()
{
char Buffer [50];
AButton *clickedButton = qobject_cast <AButton*>(sender()); //which button
if (!clickedButton->get_locked())
{
clickedButton->setChecked(1); //set button to checked
{
if (clickedButton->get()) //Is button a bomb?
{
clickedButton->setText(QString ("B"));
Fehlermeldung *Fehler = new Fehlermeldung(); //make error window
Fehler->show();
}
else
{
if(clickedButton->get_counter() == 0) //has this button 0 bombs?
{
check_for_surrounding_bombs(clickedButton); //start the recursiv check, if there are buttons with 0 bombs around
}
else
{
sprintf(Buffer, "%i", clickedButton->get_counter());
clickedButton->setText(QString (Buffer)); //write how many bombs are in this button
}}}}}
My problem is, that i get a SegFault by calling the function "check_for_surrounding_bombs".
void Layout::check_for_surrounding_bombs(AButton* clickedButton) //function doesnt work
{
if (clickedButton->get_Button('o')) //does the button above exist?
{
if (clickedButton->get_Button('o')->get_counter()== 0) //has this button 0 bombs
{
clickedButton->get_Button('o')->setText(QString ("")); // write nothing in it
if (!clickedButton->get_Button('o')->get_locked()) //if it isnt locked (= set flag)
{
clickedButton->get_Button('o')->setChecked(1); //set the button to checked
}
check_for_surrounding_bombs(clickedButton->get_Button('o')); //do the same thing for the button above
}
//... the function does the same with the buttons to the left, right, beneath
I am unsure if my recursiv-approach is the right way.
The debugger gives me this error when calling the function "check_for_surrounding_bombs(clickedButton);":
enter image description here
Implementation of the get_Button member fct.
AButton* AButton::get_Button(char c)
{
if (c == 'o')
return b_oben; //return button above
else if (c == 'u')
return b_unten; //return button beneath
else if (c == 'l')
return b_links; //return button to the left
else if (c == 'r')
return b_rechts; //return button to the right
else
return 0;
}
Any idea?
Thank you in advance.
The crash is probably caused by a stack overflow. This occurs because your recursive solution has no mechanism to simplify the problem.
If you change the state of each button you have visited before recursing, then the problem would become bounded and your problem mostly go away.
A recursive solution requires some stack for each step in the solution. This is a relatively limited resource, as the stack has a bound, and can't grow into memory used for another purpose. So this case, if you had a grid layout with 100x100 elements, you could have 10,000 steps.
As such I would look to another non-recursive solution, where you create a vector or list of items left to visit, and add items to that.
It is still imperative
Don't add a location twice to the list.
Don't visit a location again, after it has been visited.

SFML sf::Text::setFillColor is broken or am I doing something wrong?

The code I've written displays the sf::Drawable objects only for the top state of the state stack. Rendering works fine for everything, except the sf::Text type, that does not change the color of the text when button.getText().setFillColor(sf::Color:Red) is called. However, when I construct a button with a red text, whenever I try to set another color to that button, I only get a white text, no matter what color I request.
Here's where I change the color of a button:
void GameState_MainMenu::handleRealTimeInput()
{
for each (TextButton button in mButtons)
{
if (button.isSpriteClicked())
{
button.getText().setFillColor(sf::Color::Red);
button.triggerAction();
sf::Clock wait;
sf::Time timer = sf::Time::Zero;
timer = sf::seconds(0.15f);
while (wait.getElapsedTime() < timer)
{
}
wait.restart();
}
}
}
and this is my Game::render() method:
void Game::render()
{
GameState *currentState = getActiveState();
if (currentState != nullptr)
{
mWindow.clear();
currentState->draw();
}
mWindow.display();
}
Lastly, this is the draw method of the MainMenu state:
void GameState_MainMenu::draw()
{
game->mWindow.draw(game->mBackground);
game->mWindow.draw(mSelector.mSprite);
for each (TextButton button in mButtons)
{
game->mWindow.draw(button.getText());
}
}
It's probably because you have a while loop in the GameState_MainMenu::handleRealTimeInput that the program is getting stuck in.
You can try to use threads, though that way could get pretty messy. I suggest revising your code.
Okay, so I figured out this had something to do with c++'s for each instruction. As soon as I switched to the classic array-like traversal, my buttons started changing colors. I'm not saying this is THE solution, just that it worked for me. If anyone has the same problem, you might want to check that.

How to get steps and current value, after clicking on up/down control of QDoubleSpinBox?

My question is, how can I get the control of stepSize and "currentvalue" before "value" in the spinbox(I required double spinbox (QDoubleSpinBox)) gets incremented/decremented ?
Note:
I know, "valueChanged" signal is there but, slot gets called only after currentvalue incremented/decremented
In second comment I'm pointing out that you misunderstand functionality of QAbstractSpinBox::stepBy.
So when you subclass QDoubleSpinBox you can do it like that:
void SubclassOfDoubleSpinBox::stepBy(int steps) {
if (steps>0) {
if (steps>=10) { // note some events call stepBy with step value 10 or -10
setSingleStep(incrementStep10());
} else {
setSingleStep(incrementStep1());
}
} else {
if (steps<=-10) {
setSingleStep(decrementStep10());
} else {
setSingleStep(decrementStep1());
}
}
QDoubleSpinBox::stepBy(steps);
}
this will do the job with logarithmic increment decrement logic.

qslider sliderReleased value

I m trying to make a media player . The time status of the track is shown using QSlider. Track seek should happen when the user releases the slider somewhere on the QSlider. I had a look on the list of QSlider signals. The one that seems to fit is sliderReleased() which happens when the user releases the slider, but it does not get the latest value of where slider is. So to get sliders latest value I used sliderMoved() and stored reference.
Signal and slot connection
connect(this->ui->songProgress, SIGNAL(sliderMoved(int)), this,
SLOT(searchSliderMoved(int)));
connect(this->ui->songProgress, SIGNAL(sliderReleased()), this,
SLOT(searchSliderReleased()));
Functions
void MainWindow::searchSliderMoved(int search_percent)
{
value=this->ui->songProgress->value();
std::cout<<"Moved: Slider value"<<value<<std::endl;
}
Now I am Using the "value" variable inside searchSliderReleased for seeking
void MainWindow::searchSliderReleased()
{
std::cout<<"Released: Slider value"<<value<<std::endl;
emit search(value);//seek the track to location value
}
But the problem with above technique is that sliderMoved signal does not give the value where the slider is dropped but it give the location from where the slider was moved. How do I obtain the value where the slider was dropped?
You can use the valueChanged(int) signal of the slider, but set the flag
ui->horizontalSlider->setTracking(false);
Just finished building a DiectShow media player with QSlider here at work. Here's a few slot snippets on how I did it.
Widget::sliderPressed()
{
if( isPlaying() )
{
m_wasPlaying = true;
pause();
}
else
{
m_wasPlaying = false;
}
// Set a flag to denote slider drag is starting.
m_isSliderPressed = true;
}
Widget::sliderReleased()
{
if( m_wasPlaying )
{
play();
}
m_wasPlaying = false;
m_isSliderPressed = false;
}
Widget::valueChanged( int value )
{
if( m_isSliderPressed )
{
// Do seeking code here
}
else
{
// Sometime Qt when the user clicks the slider
// (no drag, just a click) Qt will signals
// pressed, released, valueChanged.
// So lets handle that here.
}
}
Had the same problem and calling sliderPosition() instead of value() directly when handling sliderReleased() worked for me.
I prefer to save the last value set by our app, like this pseudocode:
// Save our last value
lastSoftwareVal = 0;
// Set value from program
slider_set_value(value){
lastSoftwareVal = value;
slider.setValue(value)
}
// Slider on change callback
slider_on_change(value){
if(value == lastSoftwareVal){
// Value was changed by our app
return false;
}
// User has changed the value, do something
}