Get the "name" of a key from QKeyEvent in Qt - c++

Is there an easy way of getting the name of a key (so something like "uparrow" from a key event instead of just getting the key code "16777235")? Do I have to make a list of key names myself?

Using human-readable names in your code
You can use the Qt::Key enum, or get the key as a string with QKeyEvent::text().
From QKeyEvent documentation:
int QKeyEvent::key () const
Returns the code of the key that was pressed or released.
See Qt::Key for the list of keyboard codes. These codes are independent of the underlying window system. Note that this function does not distinguish between capital and non-capital letters, use the text() function (returning the Unicode text the key generated) for this purpose.
...
Qt::Key is an enum that maps numeric key IDs (like the return value of QKeyEvent::key()) to programmer-readable names like Qt::Key_Up.
If you only care about alphanumeric keys, you can also use QKeyEvent::text() to get the value:
QString QKeyEvent::text () const
Returns the Unicode text that this key generated. The text returned can be an empty string in cases where modifier keys, such as Shift, Control, Alt, and Meta, are being pressed or released. In such cases key() will contain a valid value.
See also Qt::WA_KeyCompression.
Displaying human-readable names to the user
Use QKeySequence::toString() or build your own table of "nice" names.
The easiest way to get human-readable key names to show to the user is to use QKeySequence::toString().
Here's an example:
Qt::Key key = Qt::Key_Up;
qDebug() << QKeySequence(key).toString(); // prints "Up"
If you don't like the names that QKeySequence uses (e.g. you want to use "Up Arrow" instead of "Up"), you'll need to make your data table to remap the enum values to your preferred names.

Another approach leverages the Qt metaobject system and the introspection into most enumerations in the Qt namespace. This works in both Qt 4 and Qt 5.
// https://github.com/KubaO/stackoverflown/tree/master/questions/keyname-21764138
#include <QMetaEnum>
namespace SO {
enum KeyNameOption { KeyNameNone = 0, AppendArrow = 1 };
Q_DECLARE_FLAGS(KeyNameOptions, KeyNameOption)
}
QString keyName(int index, SO::KeyNameOptions opt = {}) {
constexpr static auto const getEnum = [](const char *name) {
int enumIndex = qt_getQtMetaObject()->indexOfEnumerator(name);
return qt_getQtMetaObject()->enumerator(enumIndex);
};
static const auto keyEnum = getEnum("Key");
static const auto modifierEnum = getEnum("KeyboardModifiers");
auto name = modifierEnum.valueToKeys(index & Qt::KeyboardModifierMask);
index &= ~Qt::KeyboardModifierMask;
if (name == "NoModifier")
name.clear();
else {
name.replace('|', '+');
name.replace("Modifier", "");
name.append('+');
}
auto keyName = keyEnum.valueToKey(index);
if (keyName)
name.append(keyName + 4);
if ((opt & SO::AppendArrow) && index >= Qt::Key_Left && index <= Qt::Key_Down)
name.append(" Arrow");
return QLatin1String(name);
}
int main() {
Q_ASSERT(keyName(Qt::Key_Tab) == "Tab");
Q_ASSERT(keyName(Qt::ShiftModifier | Qt::Key_Up, SO::AppendArrow) == "Shift+Up Arrow");
Q_ASSERT(keyName(Qt::AltModifier | Qt::Key_Down) == "Alt+Down");
}
You'd then use it in, say, keyPressEvent, as follows:
void MyWidget::keyPressEvent(QKeyEvent * ev) {
qDebug() << keyName(ev->key());
}

Related

Call different processing functions for attributes in an XML element

When handling XML attributes in C++, how should different operations be run for different attributes?
Currently, I have something like this:
// get list of attributes for an XML element into member called 'attributes'
// ...
// run appropriate functions for each attribute
for (auto attribute : attributes)
{
auto name = attribute.name;
auto value = attribute.value;
if (name == "x")
doSomethingWithX(value);
else if (name == "y")
doSomethingWithY(value);
}
For just a few attribute names, this isn't so bad - but with a larger number (>15) this starts to look messy and I'm concerned about performance issues.
What might be a better way of handling XML attributes like this?
You can use a std::unordererd_map<std::string, std::function<void (const std::string&)>> and set it up with appropriate lambda functions:
std::unordererd_map<std::string, std::function<void (const std::string&)>> attrProcessors = {
{ "X", [](const std::string& value) {
// Do something with value
} } } ,
{ "Y", [](const std::string& value) {
// Do something with value
} } }
};
// run appropriate functions for each attribute
for (auto attribute : attributes)
{
auto name = attribute.name;
auto value = attribute.value;
auto processorEntry = attrProcessors.find(name);
if(processorEntry != attrProcessors.end()) {
(*processorEntry).second(value);
}
}
I am not so sure though that maintenace of the map entries would be easier to read than the if / else if cascade.
On the other hand you won't need to create an extra function for each attribute name.

Finding Key in std::unordered_map with custom key

I'm currently creating a custom std::unordered_map declaration with my custom key:
class BASE_DLLSPEC ClientKey
{
private:
// this is always true initially until we call SetClientId
bool emptyId;
// both of these are guaranteed to be unique
QString m_connectId; // ip:port format
QString m_clientId; // {Uuid} format
// ----------
public:
ClientKey(const QString& connectId = "", const QString& clientId = "") :
emptyId(true), m_connectId(connectId), m_clientId(clientId)
{ }
void SetClientId(const QString& clientId)
{
m_clientId = clientId;
emptyId = false;
}
const QString& GetConnectId() const { return m_connectId; }
const QString& GetClientId() const { return m_clientId; }
bool operator==(const ClientKey& other) const
{
int comp1 = QString::compare(m_connectId, other.GetConnectId());
int comp2 = QString::compare(m_clientId, other.GetClientId());
return (comp1 == 0) ||
(!emptyId && comp2 == 0);
}
};
struct BASE_DLLSPEC ClientKeyHash
{
std::size_t operator()(const ClientKey& key) const
{
std::string connectId = key.GetConnectId().toStdString();
std::string clientId = key.GetClientId().toStdString();
std::size_t h1 = std::hash<std::string>()(connectId);
std::size_t h2 = std::hash<std::string>()(clientId);
return h1 ^ (h2 << 1);
}
};
struct BASE_DLLSPEC ClientKeyEqual
{
bool operator()(const ClientKey& lhs, const ClientKey& rhs) const
{
return lhs == rhs;
}
};
typedef std::unordered_map<ClientKey,
ClientPtr,
ClientKeyHash,
ClientKeyEqual> ClientMap;
I'm having difficulties finding a particular key during my iteration. For some reason my client object is never located when I pass in a key for lookup.
ClientKey key = Manager::ClientKey(connectId);
ClientManager& clientManager = Manager::ClientManager::GetInstance();
ClientMap::const_iterator clientIter = clientManager.GetClients().find(key);
Even if the key has already been inserted, clientIter is always pointing to the end iterator position. Do you think this is related to having to re-create these ClientKey values on the stack and then passing them into the map for look-up, or do I have a problem elsewhere? Thank you for the clarification and insight.
At first, some considerations to the emptyId field (do not consider invalid formats - which, by the way, is not checked by you either):
ClientKey k0("hello", "world");
ClientKey k1("hello");
k1.SetClientId("world");
Is there any particular reason that the emtpyId flag should be different for k0 and k1? I personally would say:
The flag is implemented incorrectly.
It is redundant, you get the same information via m_clientId.empty().
Now the reason for failure:
Consider again k0 and k1, but without SetClientId having been called on k1:
ClientKey k0("hello", "world");
ClientKey k1("hello");
Imagine k0 has been inserted in the map, and with k1 you try to find it. What will happen? k1 produces another hash key than k0, and the map will look at a different bucket than where k0 resides at - and will not find anything.
What I think you want to achieve is having several clients for the same connection id and being able to iterate over these for a given connection id. So you might prefer std::unordered_multimap<std::string, ClientPtr> (where the string parameter represents the connection id). You will get all clients for a given connection id via equal_range then, and your class ClientKey gets obsolete.
Your code allows that the following will return true:
ClientKey k1("hello", "world");
ClientKey k2("hello", "");
return k1 == k2;
However, your hash is based on the combination of connectId and clientId.
unordered_map::find does not do an exhaustive search of the map, instead it looks in the bucket for the given hash and compares just the entries in the bucket.
You are generating your test key with just connectId, so it is looking in the bucket for ClientKey(connectId, "") rather than the bucket for ClientKey(connectId, someOtherValue).
You should consider making the hash based exclusively on connectId.
Lastly, note your constructor:
ClientKey(const QString& connectId = "", const QString& clientId = "") :
emptyId(true), m_connectId(connectId), m_clientId(clientId)
{ }
If I write:
ClientKey ck("hello");
should emptyId really be true?

c++ protobuf: how to iterate through fields of message?

I'm new to protobuf and I'm stuck with simple task: I need to iterate through fields of message and check it's type. If type is message I will do same recursively for this message.
For example, I have such messages:
package MyTool;
message Configuration {
required GloablSettings globalSettings = 1;
optional string option1 = 2;
optional int32 option2 = 3;
optional bool option3 = 4;
}
message GloablSettings {
required bool option1 = 1;
required bool option2 = 2;
required bool option3 = 3;
}
Now, to explicitly access a field value in C++ I can do this:
MyTool::Configuration config;
fstream input("config", ios::in | ios::binary);
config.ParseFromIstream(&input);
bool option1val = config.globalSettings().option1();
bool option2val = config.globalSettings().option2();
and so on. This approach is not convenient in case when have big amount of fields.
Can I do this with iteration and get field's name and type? I know there are descriptors of type and somewhat called reflection, but I didn't have success in my attempts.
Can some one give me example of code if it's possible?
Thanks!
This is old but maybe someone will benefit. Here is a method that prints the contents of a protobuf message:
void Example::printMessageContents(std::shared_ptr<google::protobuf::Message> m)
{
const Descriptor *desc = m->GetDescriptor();
const Reflection *refl = m->GetReflection();
int fieldCount= desc->field_count();
fprintf(stderr, "The fullname of the message is %s \n", desc->full_name().c_str());
for(int i=0;i<fieldCount;i++)
{
const FieldDescriptor *field = desc->field(i);
fprintf(stderr, "The name of the %i th element is %s and the type is %s \n",i,field->name().c_str(),field->type_name());
}
}
You can find in FieldDescriptor Enum Values the possible values you get from field->type. For example for the message type you would have to check if type is equal to FieldDescriptor::TYPE_MESSAGE.
This function prints all the "metadata" of the protobuf message. However you need to check separately for each value what the type is and then call the corresponding getter function using Reflection.
So using this condition we could extract the strings :
if(field->type() == FieldDescriptor::TYPE_STRING && !field->is_repeated())
{
std::string g= refl->GetString(*m, field);
fprintf(stderr, "The value is %s ",g.c_str());
}
However fields can be either repeated or not repeated and different methods are used for both field types. So a check is used here to assure that we are using the right method. For repeated fields we have for example this method for strings :
GetRepeatedString(const Message & message, const FieldDescriptor * field, int index)
So it takes the index of the repeated field into consideration.
In the case of FieldDescriptor of type Message, the function provided will only print the name of the message, we better print its contents too.
if(field->type()==FieldDescriptor::TYPE_MESSAGE)
{
if(!field->is_repeated())
{
const Message &mfield = refl->GetMessage(*m, field);
Message *mcopy = mfield.New();
mcopy->CopyFrom(mfield);
void *ptr = new std::shared_ptr<Message>(mcopy);
std::shared_ptr<Message> *m =
static_cast<std::shared_ptr<Message> *>(ptr);
printMessageContents(*m);
}
}
And finally if the field is repeated you will have to call the FieldSize method on the reflection and iterate all repeated fields.
Take a look at how the Protobuf library implements the TextFormat::Printer class, which uses descriptors and reflection to iterate over fields and convert them to text:
https://github.com/google/protobuf/blob/master/src/google/protobuf/text_format.cc#L1473

How to validate Lua table keys from C++

I am trying to use Lua for the configuration of a C++ application and am having trouble generating helpful messages when something is wrong in the configuration, not the the Lua syntax.
For example, suppose the following is a valid configuration:
foo = { a = 0, b = 'bar' }
but the user actually typed this:
foo = { a = 0, c = 'bar' }
Now, the app knows that foo can have fields a and b. It can load foo and get the value of a. It can even tell that b is not set and use a default. But I want to detect that c is present and report a warning.
Here is an extract of my attempt at that which blows up:
static void check_table(lua_State* L)
{
lua_pushnil(L);
while ( lua_next(L, -2) )
{
// key at -2 and value at -1
if ( lua_isstring(L, -2) )
{
const char* key = lua_tostring(L, -2);
// validate here; just printing key for now
cout << key << endl;
}
lua_pop(L, 1);
}
}
This works fine as long as the table is not actually an array. When I hit one of those, it dies on the second iteration with this:
...
1
PANIC: unprotected error in call to Lua API (invalid key to 'next')
which I attribute to this from the Lua reference page:
"If the value is a number, then lua_tolstring also changes the actual value in the
stack to a string. (This change confuses lua_next when lua_tolstring is applied to
keys during a table traversal.)"
Any way around this? I am open to alternate approaches. Ideally a message could be emitted like:
WARNING: conf.lua line 18: table foo does not use key 'c', ignored
(The Lua debug API doesn't give the file name and line number either, but that is a different topic.)
PS: I know, c could benign, but it could also be a typo. In a large configuration, ignoring such things could lead to hours of head scratching.
Validation will probably be much easier if written in Lua. I have something like this in mind:
local template = { a="number", b="string"}
local function validate(t)
for k,v in pairs(t) do
if template[k]==nil then
print("field "..k.." cannot be present")
elseif type(v)~=template[k] then
print("field "..k.." should be a "..template[k])
end
end
end
validate{ a = 0, b = 'bar' }
validate{ a = 0, b = 42 }
validate{ a = 0 }
validate{ a = 0, c = 'bar' }
lua_isstring is defined:
LUA_API int lua_isstring (lua_State *L, int idx) {
int t = lua_type(L, idx);
return (t == LUA_TSTRING || t == LUA_TNUMBER);
}
So instead of:
if ( lua_isstring(L, -2) )
use:
if ( lua_type(L, -2) == LUA_TSTRING )

C++ and writing enum values into Windows registry

I am writing a function as follows:
bool abc::GetLoggingStatus() {
//true or false is returned
int value;
if (regKey->HasValue("LoggingStatus")) {
regKey->QueryValue("LoggingStatus", &value);
if (value == 1)
return true; //no logging possible
else
return false;
}
regKey->SetValue("LoggingStatus", 1);
return true;
}
Logging level is defined as:
typedef enum {
Entry,
Exit,
Debug,
Warning,
Notification,
Error
} TYPE;
What I need if I select 1 the levels for logging must be shown namely debug,error ... In regedit and if 0 nothing should be shown and logging be disabled.
You can't create dropdown menus in regedit, but what you can do is create a new entry called LoggingLevel. This entry is ignored if LoggingStatus is 0. LoggingLevel is a string defining the level.
If you want to convert this string back to an enum, the easiest way is to create a map from string to your Enum type:
std::map<std::string, TYPE> typeMap;
typeMap["Warning"] = Warning;
...
In your code you query the logging level:
char* level;
regKey->QueryValue("LoggingLevel", level);
TYPE theLevel = typeMap[level];
Of course you need to do appropriate error checking.
edit
You should add two function to get the log settings, shouldLog() and getLevel().
The log function would then look like:
void log(Logger* logger, TYPE type, string sClassName, string sMethodName, string sMessage = "") {
if (!logger || !abc::shouldLog()) {
return;
}
TYPE curLevel = abc::getLevel();
bool shouldLog = false;
if (type == Warning && (curLevel == All || curLevel == Warning) ...) {
shouldLog = true;
}
if (shouldLog) {logger->WriteEntry(sClassName, sMethodName); }
}
If you want to avoid complicated if-structures, you could also try and map the enums to a value and compare that. For example Warning = 1 and ALL = 0. Then you can check if curLevel < type to see if the logger should log.