I have a GameScreen widget that uses keyPressEvent and keyReleaseEvent to take L/R arrow key input. The events are connected to a signal that tells a QGraphicsItem inside GameScreen called Paddle to advance, and Paddle has a paint event that updates the screen. From my qDebug statements, it seems that keyReleaseEvent always triggers before keyPressEvent, forcing the user to give an extraneous input to trigger keyReleaseEvent before keyPressEvent can process anything.
What has worked: My original implementation did not use QGraphicsScene or QGraphicsView, and used QPainter paint() triggered by a timer in GameScreen. There were no issues with keyPressEvent or keyReleaseEvent.
After studying the Colliding Mice example, I changed GameScreen to use QGraphicsScene instead of QPainter; keyPressEvent and keyReleaseEvent code remain unchanged. That's where problems started.
Workaround: Originally keyEventPress and keyReleaseEvent didn't trigger at all after the switch to QGraphicsScene. After reading this SO post, I managed to get the input working again by putting setFocus() inside keyReleaseEvent.
Issue #1: Only keyReleaseEvent is processed if I don't have setFocus() in the code at all or inside keyPressEvent. So there's something blocking keyPressEvent from being able to trigger unless keyReleaseEvent activates first.
Issue #2: As mentioned in the first paragraph, the keyReleaseEvent setFocus() workaround requires pressing and releasing L/R key before the program starts triggering keyPressEvent. At this point, I might have to do a dirty hack and use a stimulated key press so the user doesn't have to.
Here's the relevant code I have. I am going to see if this solution works but I'd rather not rely on hacks to get this working. Sorry for the long post, and if any brave soul can explain what's causing this weird issue and any way to fix it, I would be extremely appreciative! Thanks in advance.
GameScreen.cpp:
GameScreen::GameScreen(QWidget *parent) : QWidget(parent)
{
paddle = new Paddle();
scene = new QGraphicsScene(0, 0, 640, 480, this);
timer = new QTimer(this);
timer->start(20);
connect(timer, SIGNAL(timeout()), scene, SLOT(advance()));
}
void GameScreen::keyReleaseEvent(QKeyEvent *e){
setFocus();
if(!e->isAutoRepeat()){
switch(e->key()){
case Qt::Key_Left:{
paddle->setKeepLeft(false);
qDebug() << "Left released";
break;
}
case Qt::Key_Right:{
paddle->setKeepRight(false);
qDebug() << "Right released";
break;
}
default:
QWidget::keyReleaseEvent(e);
}
}
}
void GameScreen::keyPressEvent(QKeyEvent *e){
if(!e->isAutoRepeat()){
switch(e->key()){
case Qt::Key_Left:{
paddle->setKeepLeft(true);
qDebug() << "Left";
break;
}
case Qt::Key_Right:{
paddle->setKeepRight(true);
qDebug() << "Right";
break;
}
default:
QWidget::keyPressEvent(e);
}
}
}
Paddle.cpp:
Paddle::Paddle()
{
x = 280, y = 400, speed = 8;
width = 80, height = 10;
QColor color("powderblue");
style = new QBrush(color, Qt::SolidPattern); //not sure how to add QColor inline
keepLeft = false, keepRight = false;
}
QRectF Paddle::boundingRect() const{
return QRectF(x, y, width, height);
}
void Paddle::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *){
painter->setPen(Qt::NoPen);
painter->setBrush(*style);
painter->drawRect(getShape());
}
void Paddle::advance(int step){
if(!step)
return;
if(keepLeft == true){
moveLeft();
update();
}
else if(keepRight == true){
moveRight();
update();
}
return;
}
void Paddle::moveLeft(){
if(x > 0){
x -= speed;
}
}
void Paddle::moveRight(){
if(x < (640-width)){
x += speed;
}
}
QRectF Paddle::getShape(){
return QRectF(x, y, width, height);
}
QBrush Paddle::getBrushStyle(){
return *style;
}
void Paddle::setKeepLeft(bool val){
keepLeft = val;
}
void Paddle::setKeepRight(bool val){
keepRight = val;
}