How can I make Qt recognize a sequence of keys? - c++

In my current application, I need to make QT close a window by pressing Shift + Eesc or by pressing Esc 3x.
First, I tried Shift + Esc, it went this way
if ((event->key() == Qt::Key_Escape) && (event->key() == Qt::Key_Shift))
{
cout << "test" << endl;
on_close_x_button_clicked();
}
But for some reason, it just doesn't work. I googled it and found something about a QKeySequence but I didn't find any example of how to do it properly. I tried some ways with no success like:
if ((event->key() == Qt::QKeySequence(Qt::Key_Escape + Qt::Key_Shift)))
{
cout << "teste" << endl;
on_close_x_button_clicked();
}
}
But again, no dice. Can someone point me how to proper implement this functionality?
I also could not find anything that allowed me to create an event based on pressing Escape key 3x. Can someone, please, also teach me how to do it?
I also tried using Shortcuts, and it went like this:
LinAdvancedTestWidget::LinAdvancedTestWidget(QWidget *parent,
QObject *event_filter,
SystemEvent *event, int dpi)
: AdvancedTestWidget(parent, event_filter, event) {
(void)dpi;
KeyShiftEsc = new QShortcut(this);
KeyShiftEsc->setKey(Qt::Key_Shift + Qt::Key_Escape);
connect(KeyShiftEsc, SIGNAL(activated()), this, SLOT(slotShortcutShiftEsc()));
}
void LinAdvancedTestWidget::slotShortcutShiftEsc()
{
cout << "Escape LinAdvancedTestWidget" << endl;
on_close_x_button_clicked();
}
But it again, also does not work :/

Shift is a modifier, not a key, as you seem to compare. You would need to write something like this:
if ((event->key() == Qt::Key_Escape) && (event->modifiers() & Qt::Key_Shift))
{
...
}
Also, it is better if you use the QWidget::addAction(...) method as:
QAction *action = new QAction("my action", "Shift+Esc");
myWidget->addAction(action);
You can also set multiple shortcuts on an action:
action->setShorcuts({"Shift+Esc", QKeySequence(Qt::Key_Esc, Qt::Key_Esc, Qt::Key_Esc)});

Related

Multiple key presses detected on SFML, can't solve it in any other way and works differently on different machines

this is my first post here.
I am trying to solve a problem I am having with my SFML project where I am using multiple clients, that communicate through texts that can be typed in the rendered window and then sent to the other sockets using a selector.
My problem is that everytime i press one button of the keyboard, the window detects like 3 or 4, and if I try it on another machine, the behaviour changes.
I tried almost every solution, including the setKeyRepeatEnabled(false);
This is the update function
void Client::Update(Input* input,sf::Event& Ev, sf::Font& font, sf::RenderWindow& window)
{
if (input->isKeyDown(sf::Keyboard::Return))
{
sf::Packet packet;
packet << id + ": " + text;
socket.send(packet);
sf::Text displayText(text, font, 20);
displayText.setFillColor(sf::Color::Red);
chat.push_back(displayText);
text = "";
input->setKeyUp(sf::Keyboard::Return);
}
else if (input->isKeyDown(sf::Keyboard::Backspace))
{
if (text.size() > 0)
text.pop_back();
}
else if (input->isKeyDown(sf::Keyboard::Space))
{
text += ' ';
}
else if (Ev.type == sf::Event::TextEntered)
{
text += Ev.text.unicode;
return;
}
//sf::Event::TextEntered
//text += Ev.text.unicode;
}
This is the render one.
void Client::Render(sf::Font& font, sf::RenderWindow& window)
{
sf::Packet packet;
socket.receive(packet);
std::string temptext;
if (packet >> temptext)
{
sf::Text displayText(temptext, font, 20);
displayText.setFillColor(sf::Color::Blue);
chat.push_back(displayText);
}
int i = 0;
for (i; i < chat.size(); i++)
{
chat[i].setPosition(0, i * 20);
window.draw(chat[i]);
}
sf::Text drawText(text, font, 20);
drawText.setFillColor(sf::Color::Red);
drawText.setPosition(0, i * 20);
window.draw(drawText);
}
I don't recognize the isKeyDown function as part of SFML, so I assume you either is something you implemented or is part of a previous version of SFML (being the current 2.5.1).
There are three ways to detect input from keyboard in SFML.
sf::Keyboard::isKeyPressed: looks like this is the one you are using. It will be true every cycle that the key is pressed. You definetly don't want this. The window.setKeyRepeatEnabled(false) will obviously not work because you don't get the input through the window, but directly from the keyboard.
sf::Event::KeyPressed and sf::Event::KeyReleased events: for this, window.setKeyRepeatEnabled(false) will work, but still it's not the recommended way to deal with text input, as it would require a lot of juggling by your side to handle key combinations (accents, uppa.
sf::Event::TextEntered event: now, this is the best and recommended way to handle typing. Check the tutorial to see how to use it.

Issues trying to make a filter using Qt

I'm trying to make my software filter a list of elements.
I think I need to put my data in a TableWidget because I have to display other information.
I also put 3 Line Edit to search respectively in each category (called leName, leSource, leDestination)
so I tried to implement my filter for the name property, for now I have :
void MyClass::on_leName_textEdited(const QString &arg1)
{
for (int i=0;ui->tableWidget->rowCount()-1;i++)
{
qDebug()<<"before if";
if(ui->tableWidget->item(0,1)->text().contains(arg1) //tried with &arg1, I have the same issue, for now
{
qDebug()<<"If validated";
ui->tableWidget->showRow(i);
}else{
qDebug()<<"If not validated"
ui->tableWidget->hideRow(i)
}
}
}
When I hit a key, my soft crashes
I get "Before if", "If (not) validated", "Before if" from qDebug
I launched with debbugger and got Segmentation fault as error
not sure what I could add as detail about my error
Maybe I did nothing in the good way, I'm no expert in Qt nor c++
If you have any idea of what I should do to correct this, I would appreciate it =)
The fact that a cell exists does not imply that it has an associated QTableWidgetItem, so you must verify that it is not null.
QTableWidgetItem * item = ui->tableWidget->item(0, 1);
if(item && item->text().contains(arg1))
{
qDebug() << "If validated";
ui->tableWidget->showRow(i);
}else{
qDebug() << "If not validated"
ui->tableWidget->hideRow(i)
}

Moving object using keyboard qt

I'm trying to move an object using my arrow keys, but when I launch the app, nothing happens. Do you have an idea on how to fix it ?
#Update : my rectangle only moves once to the left and to the right, but if I use qDebug it recognizes all the times I click left or right, any ideas ?
void MouvementJoueur::keyPressEvent(QKeyEvent *e)
{
switch ( e->key() )
{
case Qt::Key_Left:
rectangle->setPos(x()-10,y());
qDebug() << "You pressed the Key left";
break;
case Qt::Key_Right:
rectangle->setPos(x()+10,y());
qDebug() << "You pressed the Key right";
break;
}
}
Thank you in advance !
Your issue seems to be, that you take position of the parent, and set position of rectangle based on that. This is probably not what you want to do with key presses here. You should set the position like this:
rectangle->setPos(rectangle->x() - 10, rectangle->y());
You can refresh the GUI with the following command
view->processEvents();
and you can debug it. It detect the key press or not.
void MouvementJoueur::keyPressEvent(QKeyEvent *e)
{
if(e->key() == Qt::Key_Left)
{
rectangle->setPos(x()-10,y());
qDebug() << "You pressed the Key x";
}
if(e->key() == Qt::Key_Right)
{
rectangle->setPos(x()+10,y());
qDebug() << "You pressed the Key x";
}
}

Capturing modifier keys Qt

I am trying to understand how to handle various events with Qt and have found an issue I cannot understand with key modifiers e.g. Ctrl Shift Alt etc. I have made a default Qt GUI Application in Qt Creator extending QMainWindow and have found that the following example does not produce understandable results.
void MainWindow::keyPressEvent(QKeyEvent *event)
{
qDebug() << "Modifier " << event->modifiers().testFlag(Qt::ControlModifier);
qDebug() << "Key " << event->key();
qDebug() << "Brute force " << (event->key() == Qt::Key_Control);
}
Using the modifiers() function on the event never is true while the brute force method returns the correct value.
What have I done wrong?
Try using this to check for shift:
if(event->modifiers() & Qt::ShiftModifier){...}
this to check for control:
if(event->modifiers() & Qt::ControlModifier){...}
and so on. That works well for me.
EDIT:
To get the modifiers of a wheel event, you need to check the QWheelEvent object passed to your wheelEvent() method:
void MainWindow::wheelEvent( QWheelEvent *wheelEvent )
{
if( wheelEvent->modifiers() & Qt::ShiftModifier )
{
// do something awesome
}
else if( wheelEvent->modifiers() & Qt::ControlModifier )
{
// do something even awesomer!
}
}
According to the documentation, QKeyEvent::modifiers cannot always be trusted. Try to use QApplication::keyboardModifiers() static function instead.
From Qt 5 Doc. – Qt::KeyboardModifiers QKeyEvent::modifiers() const:
Warning: This function cannot always be trusted. The user can confuse it by pressing both Shift keys simultaneously and releasing one of them, for example.

C++: GetKeyState() has to run once

I need to listen keyboard key states for my tiny application.
#include <windows.h>
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
while(1)
{
if(GetKeyState(VK_SPACE) & 0x80)
{
cout << "Space pressed.\r\n";
DoSpaceKeyTask();
}
if(GetKeyState(OTHER_KEY) & 0x80)
{
cout << "Other key pressed.\r\n";
DoOtherKeyTask();
}
}
return 0;
}
Once I click some keys from my keyboard, these functions has to run once. They're just some tiny tasks for my applications, which is not related in this topic.
My problem is, when I press a key, it executes the functions few times, due to while(1) looping few times during key press. I cannot use Sleep() in this case, because it still won't be effective.
I'm looking for a solution like this.
I press SPACE key.
DoSpaceKeyTask() executes "once."
I press OTHER key.
DoOtherKeyTask() executes "once."
I have like 5 keys that I will be using. Could anyone help me on this case?
Ps. If GetKeyState() function isn't useful on this task, feel free to suggest yours. My function knowledge is pretty limited on C++.
Your functions are called multiple times because of the duration time the button stays pressed. The system is very sensitive. So this is a workaround.
You could do something like this (set a flag that will assign a value when the key is down, and then reasign it when the key is up).
int k=0;//Flag
while(1){
//check if the key was pressed (key_down)
if((GetAsyncKeyState('0') & 0x8000) && (k == 0)){k=1; cout<<"'0' PRESSED."<<k<<endl;}//do your stuff here
//check if the key was released (key up)
else if(GetAsyncKeyState('0') == 0) k = 0;//reset the flag
}
I think that you will prefer to execute your functions "once" only when you have released up your keys, not when you have depressed (pressed) them down.
You don't need any additional flags and helper variables to define, allocate, assign to 0, and set each one to 1 and reset to 0 and so on, in order to achieve this goal. All what you need is just: First you have to use GetKeyState function inside the scope of while(1) to check when you have depressed a key. When the expression returns true, the executor pointer (the arrow that carry out a code line and then proceeds forward to the next code line when you either step into or step over) will enter into the if statement's scope. Then immediately trap it inside a loop and keep it trapped in there while the key you have depressed is still down and stop it before it is going to execute the function and free it when you have released that key up and then let it to execute the function.
For example, to execute the DoSpaceKeyTask function only "once" when you have depressed and released the space bar, then do the following code that should work:
while (1)
{
if (GetKeyState(VK_SPACE) & 0x80)
{
//The code here executes ONCE at the moment the space bar was pressed
cout << "Space pressed.\r\n";
while (GetKeyState(VK_SPACE) & 0x80) //You can write there ';' instead '{' and '}' below
{
//The executor pointer is trapped here while space bar is depressed and it will be free once space bar is released
}
//The code here executes ONCE at the moment the space bar was released
cout << "Space released.\r\n";
DoSpaceKeyTask();
}
}
Just the same with DoOtherKeyTask function:
while (1)
{
if (GetKeyState(OTHER_KEY) & 0x80)
{
//The code here executes ONCE at the moment the other key was pressed
cout << "Other key pressed.\r\n";
while (GetKeyState(OTHER_KEY) & 0x80) //You can write there ';' instead '{' and '}' below
{
//The executor pointer is trapped here while other key is depressed and it will be free once other key is released
}
//The code here executes ONCE at the moment the other key was released
cout << "Other key released.\r\n";
DoOtherKeyTask();
}
}
If you have already used either BT_'s idea or Pawel Zubrycki's idea, and now you want to use my idea, then you can delete all flags and variables that they suggested, because you don't need them anymore.
By the way, I have already tried the code that Pawel Zubrycki posted, but it doesn't work for me. The output that says that I have pressed either space bar or other key was not displayed when I have really pressed the space bar or other key that I chose.
Try this approach:
#include <windows.h>
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
char lastSpaceState = 0, lastOtherKeyState = 0, spaceState, otherKeyState;
while(1)
{
spaceState = (GetKeyState(VK_SPACE & 0x80) != 0);
lastSpaceState = (spaceState && !lastSpaceState);
if(lastSpaceState)
{
cout << "Space pressed.\r\n";
DoSpaceKeyTask();
}
otherKeyState = (GetKeyState(OTHER_KEY) & 0x80 != 0);
lastOtherKeyState = (otherKeyState && !lastOtherKeyState);
if(lastOtherKeyState)
{
cout << "Other key pressed.\r\n";
DoOtherKeyTask();
}
}
return 0;
}
or as Chris suggest in OP comment more "modern" async approach.
You want a windows hook to hook the game and react to the keyboard input that game gets. Now I haven't really done this specific type of hook, but I can give you a good idea of the flow. I'll leave it up to you to cut down the space by looping through a map of keys you need rather than a huge, repetitive switch, and also to work out any small kinks I put in.
int main()
{
//I imagine the last argument here is the thread
//containing the game's message loop
HHOOK hook = SetWindowsHookEx (WH_CALLWNDPROC, hookProc, NULL, NULL);
//main loop
UnhookWindowsHookEx (hook);
}
LRESULT CALLBACK hookProc (int code, WPARAM wParam, LPARAM lParam)
{
if (code == HC_ACTION)
{
CWPSTRUCT details = *((LPCWPSTRUCT)lParam);
if (details.message == WM_KEYDOWN)
{
switch (details.wParam)
{
case KEY_ONE:
if (last [KEY_ONE] == UP)
{
DoKeyOneStuff();
last [KEY_ONE] = DOWN;
}
break;
}
}
else if (details.message == WM_KEYUP)
{
switch (details.wParam)
{
case KEY_ONE:
last [KEY_ONE] = UP;
break;
}
}
}
return CallNextHookEx (NULL, code, wParam, lParam);
}
Note how I use last [KEY_ONE]. I would recommend using an std::map to store keys you need by their vk code. Then you can just loop through the map and cut down a lot of space that a switch would take.
I was having the same issue. I have several keys that act like toggle buttons and only want to register the key events once per press. My solution was to make a simple object to handle the logic. This keeps the main code clean:
class KeyToggle {
public:
KeyToggle(int key):mKey(key),mActive(false){}
operator bool() {
if(GetAsyncKeyState(mKey)){
if (!mActive){
mActive = true;
return true;
}
}
else
mActive = false;
return false;
}
private:
int mKey;
bool mActive;
};
And here is the usage:
#include <windows.h>
KeyToggle toggleT(0x54); // T key toggle
KeyToggle toggleF(0x46); // F key toggle
void main(void)
{
while(true){
if(toggleT) {;} // do something
if(toggleF) {;} // do something
}
}
I know that this is pretty old thread but I still want to share my solution. I think that creating some kind of "flags" or "switches" is really not needed. Here is my code of looping all keycodes:
while (true)
{
for (int i = 0x01; i < 0xFE; i++)
{
if (GetKeyState(i) & 0x8000)
{
std::cout << (char)i << "\n";
while (GetKeyState(i) & 0x8000) {}
}
}
}
As you can see, you can easily just use while and the same GetKeyState function to wait for the key unpress. Much simpler solution.
Try sth. like this;
while (!GetKeyState(VK_SPACE) && !GetKeyState('A') == 1) {
//std::cout << "Key not pressed... \n";
Sleep(40);
}
if (GetKeyState('A')) {
std::cout << "\"A \" key pressed... \n";
}
else if (GetKeyState(VK_SPACE)) {
std::cout << "Space key pressed... \n";
}