I'm in a situation whereby I am trying to read in a JSON config file which dictates what key commands map to given actions. For example:
...
{
"Action": "Quit",
"Combo" : "CTRL+Q"
},
...
Constructing a QKeySequence from the combo tag is trivial but I need to monitor QKeyEvents in order to trigger actions. Please note I have to monitor QKeyEvents because they are used for other purposes in the application as well. i.e. it would not be acceptable to only monitor key commands for QKeySequences (if that is even possible).
Short of writing a custom parser to construct a QKeyEvent object for each "Combo" tag, is there anyway of comparing a QkeyEvent to a QKeySequence? For example:
QKeyEvent KeyCommandsHandler::toKeyEvent(QKeySequence sequence) {
//somehow convert to QKeyEvent
}
In general, you cannot compare QKeyEvent and QKeySequence objects. QKeyEvent represents the event of a single key press or release, whereas a QKeySequence can contain a sequence of up to four keys, each with optional modifier information.
You can, however, compare the objects if you are sure that your key sequences will always contain just one key:
bool isEquiv(const QKeyEvent& event, const QKeySequence& seq)
{
if (seq.count() != 1)
return false;
return seq[0] == (event.key() | event.modifiers());
}
You can even write a conversion function for QKeyEvent to QKeySequence:
QKeySequence toKeySequence(const QKeyEvent& event)
{
return QKeySequence(event.key() | event.modifiers());
}
Note that it does not make sense to convert a QKeySequence to a QKeyEvent, though, since you have to choose a specific event type such as QEvent::KeyPress or QEvent::KeyRelease.
A simple solution (written in python):
key = QKeySequence(event.modifiers()|event.key()).toString()
Will give you the entire sequence in string form, such as "Ctrl+Q".
The benefits are (at least in python) that you can find in a dict of shortcuts, while a QKeySequence would not have been hashable.
Beware that this expects you use the correct typecase and spacing. "ctrl +Q" will not match. To avoid all issues, you can do the following when first reading the shortcuts:
shortcut = shortcut.lower().remove(' ')
and match/find using
key = QKeySequence(event.modifiers()|event.key()).toString().lower()
or better yet:
shortcut = QKeySequence(shortcut).toString()
and match directly.
A Qt4.7 note with code for converting KeyEvent to KeySequence. (But the code is flawed because it casts an int for the keycode from QKeyEvent.key() to a string. Better to use QKeyEvent.text() ?)
Also, the code in Ferdinand's answer:
QKeySequence(event.key() | event.modifiers())
is not type safe (mixes int and QKeyboardModifiers) and if converted to Python fails in PyQt, but not in PySide?
Also, "QKeyEvent represents the event of a single key press or release" doesn't really explain it. A QKeyEvent can tell you what combination of keys were down, just not the order in which they were pressed. When the user presses keys in sequence, your app might get a sequence of QKeyEvents (depending on whether your app is using default versus overridden handlers for QKeyEvent.) The later QKeyEvents will show you all the keys that were down at the time of the event. They might no longer be down. It is rather complicated.
Related
So inside the player controller there is a nice function called WasInputKeyJustPressed, you give it a key and it outputs a bool. I was curious if there was a similar function for Action Keys. For example, if I had an action mapping called MyActionKey which was tied to the Space bar. Is there a function like WasActionKeyJustPressed that essentially does the same thing as the original? If not how can I detect that with my player controller in a similar manner to WasInputKeyJustPressed?
I am not interested with the InputAction MyActionKey for this case it does not work for me. I am really looking for a function that will just output whether or not that particular Action Key is pressed in a bool form.
If this is a custom function that needs to be written then it would be nice if say I could pass in the name of the Action Key. I'd prefer if the solution was in C++.
I was able to find a sort of work around. Essentially, you can grab the input settings and then from there grab your axis mappings or your action mappings. They are both very similar. From there I looped through each of the mappings and grabbed the key it was referencing and called the WasInputKeyJustPressed function. An example below:
TArray<FInputAxisKeyMapping> axisMapping;
UInputSettings::GetInputSettings()->GetAxisMappingByName(axisName, axisMapping);
for(auto axis : axisMapping)
{
if (playerController->WasInputKeyJustPressed(axis.Key))
{
return true;
}
}
return false;
To handle text input I've set up a char-event callback with glfwSetCharCallback, and to handle non-text keypresses (arrow keys & hotkeys) I've set up a key-event callback with glfwSetKeyCallback.
What happens in this situation is that for a key press of a character key, I get two calls, one in the key-event callback, and then one in the char-event callback. This can cause unwanted effects - for example let's suppose the user configured the key "a" to enter "Append Mode" of a text editor - after it enters the mode it will also enter the character "a".. Is there a good way to handle this?
So far I've relied on both events arriving together before glfwPollEvents returns, and have merged them. But I get reports that this scheme doesn't work well on some Ubuntu systems..
I've been having trouble with this one as well. After some rudimentary debugging I found that if you press, hold then release a 'typable' key (meaning a key which may fire both the glfwKeyCallback and glfwCharCallback), the output is as follows:
KeyCallback - pressed
CharCallback - typed
KeyCallback - repeated
CharCallback - typed
(3. and 4. repeat until key is released)
KeyCallback - released
With this, and judging from the fact that there is a 0ms delay between the two events firing, they're probably fired sequentially. The solution I came up with (is rather janky), and involves creating some sort of KeyEvent structure:
(examples are in C++)
enum KeyEventType
{
Pressed,
Repeated,
Released
}
struct KeyEvent
{
KeyEventType Type;
int Key;
unsigned char Codepoint;
bool IsTyped;
}
and store it along with an index variable, such as
[main/input class]
std::vector<KeyEvent> m_KeyEvents;
size_t m_LastKeyEventIndex;
in the main file.
Then, when the glfwKeyCallback fires, push a new KeyEvent into the vector:
[glfwKeyCallback]
KeyEventType type = (action == GLFW_PRESS ? KeyEventType::Pressed : (action == GLFW_REPEAT ? KeyEventType::Repeated : KeyEventType::Released));
KeyEvent event = KeyEvent(type, key);
m_KeyEvents.push_back(event);
m_LastKeyEventIndex = m_KeyEvents.size() - 1;
and if the glfwCharCallback fires, we know from the debugging that it should be (immediately) after the corresponding keyCallback event, so you can modify the last added entry in the vector to add the codepoint and mark it as a 'typed' event, after-the-fact. This also gives the added benefit of tying the actual key that was pressed to the generated codepoint, which could come in useful.
[glfwCharCallback]
m_KeyEvents.at(m_LastKeyEventIndex).Codepoint = codepoint;
m_KeyEvents.at(m_LastKeyEventIndex).IsTyped = true;
Finally, in the main loop when you go to call glfwPollEvents(), process all those pending KeyEvents and then clear the vector and reset the index.
I haven't fully tested this yet, but some very rudimentary debugging shows this as a promising solution, resulting in the following*:
*I'm using a custom Key enum in place of the int Key. You could probably use glfwGetKeyName() to get the printable key name, however this resulted in exceptions for me when pressing some keys.
In wxWidgets, I'm capturing the wxKeyDown event, which gives me a wxKeyEvent. I need to get the name of the key that was pressed (e.g. "F1", "Del", "Home", "A") to display to the user, however the closest I have found is wxKeyEvent::GetUnicodeKey():
void OnKeyDown(wxKeyEvent &event)
{
wxMessageBox(wxString::Format("Key pressed: %c", event.GetUnicodeKey()));
}
The only other solution I have found is to use a switch statement with keys that do not have a Unicode representation (e.g. Del). Is there any other way to retrieve the name of the key that was pressed?
There is no built-in function to get the name of the key, but you can see how to do it for most (and maybe even all the) special keys in the keyboard sample.
EDIT: Actually there is one function I didn't think of: wxAcceleratorEntry::ToString(). It is rather roundabout but you probably could use wxAcceleratorEntry(0 /* no modifiers */, keycode).ToString() to return a reasonably user-friendly description of the key.
I want to append chars to QLineEdit by sending KeyEvent.
I'm using code like this:
ui.myEdit->setFocus();
for(size_t i = 0; i < 10; ++i) {
QKeyEvent keyPressed(QKeyEvent::KeyPress, 'a', Qt::NoModifier);
QWidget::keyPressEvent(&keyPressed); // or
//QApplication::sendEvent(QApplication::focusWidget(), &keyPressed);
}
Why there is no change in myEdit?
You can change the change the text of QLineEdit simply by :
ui->myEdit->setText(ui->myEdit->text().append("a"));
But if you really want to change it by sending QKeyEvent you can try this :
QKeyEvent * eve1 = new QKeyEvent (QEvent::KeyPress,Qt::Key_A,Qt::NoModifier,"a");
QKeyEvent * eve2 = new QKeyEvent (QEvent::KeyRelease,Qt::Key_A,Qt::NoModifier,"a");
qApp->postEvent((QObject*)ui->myEdit,(QEvent *)eve1);
qApp->postEvent((QObject*)ui->myEdit,(QEvent *)eve2);
Your approach is not wise.
Setting the focus yourself may annoy more than one user which loose focus from one UI element for the other.
By calling keyPressEvent directly you are skipping many layers of processing from the framework. Only misbehavior await down this path.
To reply to
I want to append chars to QLineEdit
You can obtain the line edit text, modify at your will and set it back.
QString currentText = ui.myEdit->text();
QString toappend = "aaaaaaaaaa";
QString nextText = currentText + toappend;
ui.myEdit->setText(nextText);
or one line
ui.myEdit->setText(ui.myEdit->text()+mystring);
Synthesizing a key press event to append characters to a line edit is asking for endless trouble. You'd need to retain the state of the control to ensure that you are in fact appending characters. If the cursor is not at the end, you'll be inserting or prepending characters. If any modifiers are active, you may cause the widget to act as if, say, a clipboard shortcut was activated. Say if you "append" an X while Ctrl/⌘ is held down, you'll cause any selected text to disappear from the line edit.
In other words: if you want to append something to a textedit, simply append it, don't synthesize keystrokes.
lineEdit->setText(lineEdit->text() + "appended");
That's it. To do it properly via appending keystrokes requires about a page of code, and even then it can't but rely on Qt's implementation details.
I want to implement a Keyboard class like below;
class Keyboard
{
public:
Keyboard();
~Keyboard();
bool IsKeyDown(Key);
bool IsKeyUp(Key);
void SetPressedKey(int);
void SetReleasedKey(int);
private:
Key pressedKey;
Key releasedKey;
};
Key is an enum like below;
enum Key
{
A,
Enter,
Up,
Down
};
Here is the window callback function;
case WM_KEYDOWN:
kb.SetPressedKey(wParam);
break;
case WM_KEYUP:
kb.SetReleasedKey(wParam);
break;
First of all, my design can be completely wrong. If it's an acceptable design, there are some questions that I couldn't answer. Can value of the pressedKey be overwritten if user presses two buttons at the same time and how can I determine if user uses combinations like CTRL+C. The other question is I couldn't find a way to make a relationship between wParam's value and enum keys' indexes.
Most of the implementation of this class is unnecessary and should be removed. You are re-implementing functionality that the operating system already provides. If you want to know the state of a key, simply call GetKeyState.
You may choose to wrap up the low-level Win32 API GetKeyState with your class. But do not store the information as state in your class. When you need to know the state of a key, i.e. when implementing IsKeyDown() and IsKeyUp(), call GetKeyState.
Another good reason for this is that you cannot guarantee that all keyboard messages will arrive in your window. Somebody may press a key whilst a different window is active, and then switch to your program. At that point, your attempt to track state using keyboard messages will break down because you never got the key down message. However, GetKeyState will work.
That said, if you need to know the instantaneous state, outside of a window procedure, then you would use GetAsyncKeyState rather than GetKeyState. Only you fully understand the purpose of this class and so are in a position to make that decision.
Windows already keeps track of all this information. If you want to know if a key is currently pressed, GetKeyState. This queries the keyboard state tied to the current message. So for example it's accurate to use GetKeyState to know if CTRL was pressed inside a WM_KEYDOWN message.
There is also GetAsyncKeyState, if you want to query the keyboard state "right now", rather than the state when the current message was generated.
In terms of your design I personally would try to make it a bit better by having it as a set of Key object and have the keys maintain their own state. That way you can model multiple key presses. The example (incomplete and not tested!) shows the idea.
class Keyboard
{
public:
Keyboard();
~Keyboard();
bool isPressed(Key);
void pressKey(int); // or Key
void releaseKey(int);
private:
std::set<Key> keys;
};
class Keyboard
{
public:
// define your comparison operators to use std::set or else write a comparator.
Key(uint32 id);
~Key();
bool
void setPressed();
void setReleased();
bool isPressed() const;
private:
uint32 keyId;
bool pressed;
};
In order to relate the windows key values to Key objects you could build a factory to return the keyIds that you need. Your KeyIds could be an enum or just re-use the windows values. As long as they are unique and your application understands then then there is no point in being restrictive.