Wykrywanie dwóch klawiszy - CapsLock i Shift

0

Robię sobie podklasę QLineEdit i napotkałem na problem przy zamianie znaków małe->duże i duże->małe o ile samo wprowadzanie i zamiana znaków nie jest jakimś wielkim problemem, to już problemem jest wykrycie takiej kombinacji gdzie jest załączony CapsLock i jak wciśnie się klawisz Shift to literki powinny robić się małe - ale niestety wykrywanie tej kombinacji nie działa... i nie mam pojęcia czemu

mam taki kod - próbowałem już wielu innych kombinacji ale bezskutecznie i tego wszystkiego tutaj nie umieszczę, bo zwyczajnie tego nie mam ale to moja ostatnia wersja kodu, na której próbuję wykryć kombinację wciśniętych klawiszy CapsLock i Shift - ta kombinacja powinna dać małe litery

void MyQLineEdit::keyPressEvent(QKeyEvent *event)
{
    if(event->type() == QEvent::KeyPress){
        QString inputText = event->text();
        int numberKey = event->key();

        int test = Qt::Key_CapsLock & Qt::Key_Shift;

        if(test & (Qt::Key_CapsLock & Qt::Key_Shift)){
            qDebug()<< "shift + caps";
        }
        if(event->key() == Qt::Key_CapsLock){
            qDebug()<< "caps";
        }
        if(event->key() == Qt::Key_Shift){
            qDebug()<< "shift";
        }
    }
}

powyższy kod działa ale jak wcisnę cokolwiek, to pierwszy warunek zawsze jest spełniony... pomoże ktoś to ogarnąć?

3

Radzę poczytać dokumentację, np https://doc.qt.io/qt-6/qkeyevent.html#text daj efektywny znak.
Natomiast https://doc.qt.io/qt-6/qkeyevent.html#key daje aktualny klawisz (zawsze małą litera).

Nie rozumiem co właściwie chcesz osiągnąć, więc raczej nie wskażę czego dokładnie masz użyć.

0
MarekR22 napisał(a):

https://doc.qt.io/qt-6/qkeyevent.html#modifiers
https://doc.qt.io/qt-6/qt.html#KeyboardModifier-enum

znam, robiłem działa ale nie mogę wykryć tej kombinacji mimo to, bo owszem jest Qt::ShiftModifier ale CapsLock musiałbym wykryć już tak event->key() == Qt::Key_CapsLock czyli warunek jaki pisałem miałem taki

if((event->modifiers() & Qt::ShiftModifier) && (event->key() == Qt::Key_CapsLock))

to ten warunek najzwyczajniej nie działa... tak jak oczekuję

void MyQLineEdit::keyPressEvent(QKeyEvent *event)
{
    if(event->type() == QEvent::KeyPress){
        QString inputText = event->text();
        int numberKey = event->key();

        int test = Qt::Key_CapsLock & Qt::Key_Shift;

        if((event->modifiers() & Qt::ShiftModifier) && (event->key() == Qt::Key_CapsLock)){
            qDebug()<< "caps + shift -> rob małe litery";
        }
        if(event->modifiers() & Qt::ShiftModifier){
            qDebug()<< "shift -> rob duże litery";
        }
        if(event->key() == Qt::Key_CapsLock){
            qDebug()<< "caps -> rob duże litery";
        }
    }
}

kiedy wcisnę shift mam napis shift
kiedy wcisnę caps mam napis caps
ale kiedy wcisnę shift i go trzymam i załączę caps to wykonują mi się wszystkie trzy warunki na raz

poza tym, warunek jest dobrze napisany?

0
MarekR22 napisał(a):

Radzę poczytać dokumentację, np https://doc.qt.io/qt-6/qkeyevent.html#text daj efektywny znak.
Natomiast https://doc.qt.io/qt-6/qkeyevent.html#key daje aktualny klawisz (zawsze małą litera).

Nie rozumiem co właściwie chcesz osiągnąć, więc raczej nie wskażę czego dokładnie masz użyć.

no chcę osiągnąć to, co działa w każdym polu do wprowadzania danych czyli

  1. wciskam i trzymam klawisz shift to mam duże litery
  2. wciskam klawisz capsLock to mam duże litery
  3. ale jak nie zauważę, że mam włączony capsLock i wcisnę dodatkowo klawisz shift to mam małe litery, bo klawisz shift w tym momencie znosi działanie capsLocka - jak nie wierzysz, to przetestuj to u siebie w notatniku i zobacz
1

Nie bardzo rozumiem w czym problem
wielka/mała litera + SHIFT+ CAPS LOCK normalnie działa w Qt (zgodnie z opisem by OP)

W Qt nie ma mechanizmu odczytania stanu caps lock (wierze ze chatgpt ma rację)
trzeba posiłkować się funkcjami z danego systemu operacyjnego

#include <QApplication>
#include <QLineEdit>
#include <QKeyEvent>
#include <QDebug>

#ifdef WIN32
    #include <windows.h>
#else
#endif


#ifdef WIN32
  bool isCapsLockOn() { return (GetKeyState(VK_CAPITAL) & 0x0001) != 0; }
#else
  // NA WAYLAND nie PYKNIE !!! ;)
  bool isCapsLockOn() {
    Display *d = XOpenDisplay((char *)0);
    if (d == NULL) return false;
    unsigned n; 
    XkbGetIndicatorState(d, XkbUseCoreKbd, &n); 
    return (n & 0x01) == 1;
  }
#endif

class MyQLineEdit : public QLineEdit {

public:
    MyQLineEdit(QWidget *parent = nullptr) : QLineEdit(parent) {}

protected:
    void keyPressEvent(QKeyEvent *event) override {
        qDebug() << "isCapsLockOn = "<< isCapsLockOn() ;
        qDebug() << "text = "<< event->text() ;
        QLineEdit::keyPressEvent(event); // Call the base class implementation
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    MyQLineEdit lineEdit;
    lineEdit.show();

    return app.exec();
}
0
Marius.Maximus napisał(a):

Nie bardzo rozumiem w czym problem
wielka/mała litera + SHIFT+ CAPS LOCK normalnie działa w Qt (zgodnie z opisem by OP)

masz rację, bo wystarczy taka funkcja

void MyQLineEdit::keyPressEvent(QKeyEvent *event)
{
    qDebug()<< event->text();
}

i możesz walić w klawiaturę na przemian i na wyjściu jest to, co trzeba

swoją drogą przy okazji zauważyłem, że aby osiągnąć efekt taki sam - to musiałbym zaprząc do pracy jeszcze taką funkcję

void MyQLineEdit::keyReleaseEvent(QKeyEvent *event)
{
    
}

czyli za pomocą powyższej funkcji musiałbym ustawić jakieś flagi co i kiedy zostało zwolnione i wciśnięte aby uzyskać to, co napisałem - ale skoro to działa "samo z siebie" to po co się wysilać? a ja tu walczę z tym kilka dni...

0

@zkubinski: kolejny raz przypominam o tworzeniu kawałka kodu który prezentuje błąd,
jak zaczniesz go tworzyć i wyjdzie że problem wcale nie występuje (tam gdzie myślisz ze powinien być) to znaczy że trzeba spojrzeć szerzej albo przejść się na spacer
https://kobieta.onet.pl/warto-wiedziec/steve-jobs-i-jego-regula-10-minut-genialny-trik-miliardera-ktory-zalatwi-awans/cxf1z6k

To rozwiazanie z ::text() podał @MarekR22 (warto zaznaczyc odpowiedz i dodac łapke)
ja tylko zapodałęm Ci kawalek kodu od chatgpt z przykladem ze to działa

0
Marius.Maximus napisał(a):

@zkubinski: kolejny raz przypominam o tworzeniu kawałka kodu który prezentuje błąd,

w sumie tu nie było błędu ale nie miałem napisanego warunku, poza tym nie mogłem dać czegoś co w sumie pokasowałem, a miałem kilka wersji kodu i ta która została to tą wersję podałem w pierwszym poście.

a i mnie tak samo bing wygenerował taki kod

#include <Windows.h> // Dodaj tę linię do pliku nagłówkowego

void MyQLineEdit::keyPressEvent(QKeyEvent *event)
{
    QString inputText = event->text();
    int numberKey = event->key();
    bool shiftPressed = event->modifiers() & Qt::ShiftModifier;

    // Sprawdzenie stanu CapsLock (Windows)
    bool capsLockOn = GetKeyState(VK_CAPITAL) & 0x0001;

    // Obsługa klawiszy
    if (shiftPressed && inputText.length() == 1 && inputText[0].isLower()) {
        inputText = inputText.toUpper();
    }

    if (capsLockOn && inputText.length() == 1 && inputText[0].isLower()) {
        inputText = inputText.toUpper();
    }

    if (numberKey == Qt::Key_CapsLock) {
        qDebug() << "caps" << inputText.toUpper();
    } else if (shiftPressed) {
        qDebug() << "shift" << inputText.toUpper();
    } else if ((numberKey != Qt::Key_CapsLock) && !shiftPressed) {
        qDebug() << "male znaki" << inputText;
    }

    // Ustawianie tekstu w QLineEdit
    if (!inputText.isEmpty()) {
        this->setTextInField.append(inputText);
        this->setText(this->setTextInField);
    }

    // Inne klawisze - delegowanie do domyślnej obsługi
    QLineEdit::keyPressEvent(event);
}

ale go nie sprawdzałem... bo nie pasowała mi ta biblioteka windowsowa, bo co jak za chwilę przeniosę kod na linuxa i będę się bujał od początku? Dlatego zlałem bingowe rozwiązanie

0

@Marius.Maximus już wiem czemu mi nie działała klawiatura tak, jak oczekiwałem. No cóż wkurzają mnie te lakoniczne opisy klas - jakby to był jakiś bełkot naukowy dla wtajemniczonych, jak jestem twórcą czegoś, to wiem jak to działa i jakie są zależności - mogliby chociaż tam jakoś to wszystko treściwie opisać, że np "nie da się zmapować od nowa wczytywania klawiatury bo to dzieje się na jakimś innym poziomie" np na poziomie jądra, a klasa tylko ma uchwyt do wywołania w jądrze i klasa QKeyEvent tylko i wyłącznie obiera zdarzenia z klawiatury i tą klasę można wykorzystać tylko i wyłącznie do wychwytywania wpisanych znaków i skrótów klawiaturowych z klawiatury - i kurde, życie było by od razu prostsze, a nie piszą jakieś ogólniki udające jakiegoś edukowanego ludka tudzież profesóra The QKeyEvent class describes a key event - co według nich znaczy "opisuje" - dobra, nie chce mi się nad tym rozwodzić ale wkurzają mnie te zbyt ogólne opisy z których nic nie wynika i domyślaj się człowieku co autor miał na myśli... ale do rzeczy

dlaczego nie działała klawiatura tak jak opisałem to w poście? Dla przypomnienia, pisałem, że

  1. wciskam i trzymam klawisz shift to mam duże litery
  2. wciskam klawisz capsLock to mam duże litery
  3. ale jak nie zauważę, że mam włączony capsLock i wcisnę dodatkowo klawisz shift to mam małe litery, bo klawisz shift w tym momencie znosi działanie capsLocka - jak nie wierzysz, to przetestuj to u siebie w notatniku i zobacz

nie da się tego zrobić. Z prostego powodu - przyznam kolesiom, że mają finezję i piszą genialnie kod ale opisy wszystkiego w dokumentacji mają do D $ % - szkoda, że nie ma większej czcionki takiej na 100 pt !!!

więc jest taka fajna funkcja o nazwie event - za bardzo skupiłem swoją uwagę na tych dwóch funkcjach keyPressEvent and keyReleaseEvent. Więc kolega @Marius.Maximus miał rację żeby dać cały kod, a nie jakiś kawałek, bo wydaje nam się, że tam jest problem ale tak naprawdę nie musi... no ale w moim przypadku nie wiem czy by to coś dało, bo ja sam nie rozumiem jak wiele rzeczy działa i nie dam rady doprecyzować pytania w taki sposób aby dało się was nakierować na rozwiązanie - jedynie mogę powiedzieć robię taką czynność i oczekuję na wyjściu tego - dobra więc o co chodzi z tymi trzema funkcjami i dlaczego to nie działało jak lamer myślał. Ano powód jest banalny - przynajmniej tak to rozumiem.

  1. Qt - biblioteka jako całość odbiera wywołania z jądra - nie obsługuje sprzętu na niskim poziomie - w tym przypadku mowa o klawiaturze i na tym się skupiam - bo chyba byłby to bezsens, skoro na dzień dobry klawiaturę obsługuje system operacyjny i z przerwań można odczytać co klepie ludek
  2. skoro system operacyjny załatwia warstwę abstrakcyjną sprzętu - to dlaczego nie napisać funkcji event i tam odbierać wywołania z jajka, a zadaniem usera będzie w tejże właśnie funkcji filtrować wpisywane sekwencje znaków np wpiszesz literę, to funkcja event ją wyłapie, wpiszesz skrót klawiaturowy, to funkcja ją wyłapie.
    jak do tego doszedłem? ano tak, że odbiłem się od ściany - nie dosłownie - wybaczcie, ja się uczę, tak? ileż można? - jestem sam jak palec :) no dobra - odbiłem się od ściany w następujący sposób

szkielet funkcji event - krok po kroku - pusta wygląda tak i jak jest pusta to... nic nie zwraca, znaczy zwraca bool :D - to nie jest śmieszne

bool MyQLineEdit::event(QEvent *event)
{
    return true;
}

miałem tą funkcję wypełnioną moim warunkiem którego zadanie jest... przerysowanie widgetu na nowo, tak, przerysowałem go i działa jak chcę

bool MyQLineEdit::event(QEvent *event)
{
    if(event->type() == QEvent::Paint){
        QPaintEvent *ev = static_cast<QPaintEvent*>(event);
        MyQLineEdit::paintEvent(ev);

        return true;
    }

return QLineEdit::event(event);
}

ale idźmy dalej... na końcu tej funkcji, jest magiczne wyjście z wywołaniem tejże funkcji czyli mowa o return QLineEdit::event(event); i tutaj rozpoczyna się moja zabawa, bo zwracałem domyślną implementację nie połączyłem kropek i walczyłem z czymś innym niż powinienem był walczyć - ale tak to jest jak odkrywa się nieznane... no więc jak zwracałem domyślną implementację, to zwracałem tak naprawdę resztę tego czego nie nadpisałem w warunkach i odnosząc się do klawiatury nie nadpisywałem dwóch warunków QEvent::KeyPress and QEvent::KeyRelease - czyli całość powinna wyglądać tak - olewamy mój paint, bo nie o tym tutaj mowa

bool MyQLineEdit::event(QEvent *event)
{
  if(event->type() == QEvent::KeyPress){
    return true;
  }

  if(event->type() == QEvent::KeyRelease){
    return true;
  } 
}

więc jak zrobiłem te dwa warunki to nagle okazało się, że klawiatura nie działa - huraaa - jestem w domu - nie nie jestem, bo zaraz zacznie się gorączkowe szukanie w bibliotece, gdzie jest ta klawiatura obsługiwana - zgadniecie? pewnie większość z was zgadnie, ja tak szybko nie zgadłem, bo zanim dopowiedziałem sobie resztę, to upłynęło trochę czasu... i wiecie co? Właśnie tutaj zderzyłem się ze ścianą, przyłożyłem i mnie olśniło... i dopowiedziałem sobie resztę - to tylko moje przypuszczenia - że klawiatura jest obsługiwana na etapie jądra i jak pisałem na początku, że biblioteka przechwytuje wywołania z jądra, więc dlatego nie mogłem napisać obsługi CapsLocka na przemian z Shiftem - bo to robi już za mnie jądro, a biblioteka nie udostępnia fizycznego dostępu do sprzętu... bo i po co? jak już jest obsługiwany. Więc połączyłem kropki i mi wyszło, że biblioteka QKeyEvent służy do tego aby

  1. wychwycić jakiś znak lub skrót klawiaturowy
  2. obsłużyć go
  3. po obsłudze odpalić jakieś zdarzenie

tak, odkrył amerykę powiecie, tak kurde, odkryłem, bo wyobraźcie sobie, że porywacie się na głęboką wodę i nadpisujecie jakiś widget jak ja to robię w tym przypadku, że nadpisuję QLineEdit tworząc własną podklasę tego obiektu no i co dalej? Trzeba go obsłużyć i po to właśnie są te funkcje event i reszta czyli jaki wyłania się z tego obrazek? No taki

  1. Napisałem podklasę QLineEdit, bo nie podoba mi się domyślna implementacja - chodzi o wygląd
  2. więc jak robię swój wygląd to okazało się, że trzeba zadbać o resztę czyli o klawiaturę i skróty klawiaturowe i jak to całościowo wygląda? no mniej więcej z grubsza tak... to tylko kod pokazowy, nie jakieś tam rozwiązanie

przechwytuję wciśnięcie przez użytkownika kombinacji klawiszy shift + a and shift + b - jak przechwycę, to co dalej z tym zrobić? Można to sobie wyobrazić jako "kopiuj" - "wklej"

bool MyQLineEdit::event(QEvent *event)
{
    if(event->type() == QEvent::KeyPress){
        QKeyEvent *evKey = static_cast<QKeyEvent*>(event);

        if(evKey->key() == Qt::Key_A && evKey->modifiers() == Qt::ShiftModifier){
            QKeyEvent k(QEvent::KeyPress, 65, Qt::ShiftModifier, "A+shift");

            MyQLineEdit::keyPressEvent(&k); //przesyłam wciśniętą kombinację do funkcji keyPressEvent

            return true; //po obsłużeniu wypad z funkcji, bo działa w EventLoop
        }

        if(evKey->key() == Qt::Key_B && evKey->modifiers() == Qt::ShiftModifier){
            QKeyEvent k(QEvent::KeyPress, 66, Qt::ShiftModifier, "B+shift");

            MyQLineEdit::keyPressEvent(&k); //przesyłam wciśniętą kombinację do funkcji keyPressEvent

            return true; //po obsłużeniu wypad z funkcji, bo działa w EventLoop
        }

        return QLineEdit::event(event);
    }
    if(event->type() == QEvent::KeyRelease){
        return true;
    }
    return QLineEdit::event(event); //zwrot domyślnej implementacji, która nie jest nadpisana
}

po przechwyceniu którejś kombinacji, obsługa zaczyna się tutaj czyli - co program ma zrobić jak wciśniesz te klawisze? Np wklej... ale jeszcze nie do widgetu

void MyQLineEdit::keyPressEvent(QKeyEvent *event)
{
    if(event->device()->type() == QInputDevice::DeviceType::Keyboard){
        if(event->type() == QEvent::KeyPress){
            if((event->key() == Qt::Key_A) && (event->modifiers() & Qt::ShiftModifier)){
                qDebug()<< event->text(); //tutaj po wykryciu szukanej kombinacji pokazuje tekst
            }
            if((event->key() == Qt::Key_B) && (event->modifiers() & Qt::ShiftModifier)){
                qDebug()<< event->text(); //tutaj po wykryciu szukanej kombinacji pokazuje tekst
            }
        }
    }
}

a co dalej jak się puści klawisze? Np wklej do widgetu - np validujesz tekst i jak jest poprawnie zvalidowany, to pokaż go w widgecie...

void MyQLineEdit::keyReleaseEvent(QKeyEvent *event)
{
//tu jeszcze nic nie ma, bo nie wiem ale pomysł jest wyżej - wyświetl bezpośrednio w widgecie
}

to chyba tyle, trochę długi post wyszedł, nie wiem czy komuś będzie chciało się to czytać no... ale napisałem, bo przy szukaniu rozwiązania i zrozumieniu tego, spociłem się nieźle ale chyba rozumiem po co to jest i co tam się dzieje

1

Bracie @zkubinski być może znajdziesz inspirację w moim dawnym wątku https://4programmers.net/Forum/C_i_C++/354172-natywne_eventy_klawiatury_pod_linuksem_w_qt_potrzeba_bo_nie_dziala_poprawnie_qkeyeventisautorepeat
Istnieje coś takiego zwącego się QAbstractNativeEventFilter który pozwala łapać natywne zdarzenia systemu zanim zostaną opakowane w QEventy.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.