Отправить событие клавиатуры в приложение QML


Я хочу отправить сигнал Qt::Key_Left моему приложению из потока.
Я попытался отправить свое событие со следующим кодом, но ничего не происходит:
QKeyEvent *event = new QKeyEvent ( QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier); QCoreApplication::sendEvent (app, pressEvent);

На самом деле эти функции вызываются из потока, который прослушивал порт. Возможно ли, что проблема вызвана тем, что эти функции не вызываются из основного потока?

Я просто сообщаю вам, что мое приложение в QML (не знаю, меняет ли это что-то). Я также пробовал со статической функцией QCoreApplication::postEvent

Заранее спасибо. Извините за мой уровень английского.


person gfitas    schedule 26.01.2018    source источник
comment
Согласно документации, QCoreApplication::postEvent является потокобезопасным. Однако вы пытались запустить его из основного потока?   -  person SteakOverflow    schedule 26.01.2018
comment
Нет, не видел и не вижу, как это сделать. Основной поток занят QGuiApplication::exec, я не знаю, как им управлять.   -  person gfitas    schedule 26.01.2018
comment
Вы не «берете это под свой контроль». Вы просто заставляете его что-то делать. Например, вы можете испускать сигнал, к которому подключен основной поток. Затем основной поток выполнит слот.   -  person SteakOverflow    schedule 26.01.2018
comment
Итак, я попытался понять это с помощью этого: pastebin.com/AnpwA7km, но в моем окне QML ничего не происходит. .   -  person gfitas    schedule 27.01.2018
comment
Смотрите мой ответ редактировать.   -  person dtech    schedule 27.01.2018


Ответы (2)


Нет, не видел и не вижу, как это сделать. Основной поток занят QGuiApplication::exec

Основной поток на самом деле не заблокирован, между каждым циклом цикла событий есть период, когда он прослушивает события из других потоков.

Вы можете использовать use QMetaObject::invokeMethod() или инициировать слот через сигнал, в обоих случаях используя Qt::QueuedConnection, который будет использовать это окно прослушивания для планирования вызова, который будет выполняться в следующем цикле цикла событий связанного потока.

QCoreApplication::postEvent() должен работать, я использую его для отправки событий из пользовательского экранного контроллера, реализованного в QML, и он работает нормально, однако QML живет в основном потоке, поэтому, предположительно, ваша проблема заключается в том, что вы отправляете из другого потока. Поэтому я предполагаю, что если вы запланируете отправку событий через основной поток, он будет работать так, как ожидалось.

РЕДАКТИРОВАТЬ:

ОК, я понял, что не так, по какой-то причине отправка события в основной экземпляр приложения не перенаправляет его в механизм qml и последующие объекты qml. Но если получатель настроен на корневой объект qml engine->rootObjects().at(0), он работает... несколько...

События для элементов, находящихся в фокусе, принимаются через Keys.onPressed и тому подобное, поэтому, если это все, что вам нужно, то все готово. Как ни странно, если я использую что-то вроде TextEdit, этот подход не работает для записи текста в поле, хотя эмуляция таких вещей, как использование клавиш со стрелками для перемещения курсора, работает. Я даже могу нажать модификатор Shift и выделить текст. Но не писать текст...

Так что, возможно, это не так просто, как ожидалось. Надеюсь, кто-то сможет пролить свет на проблему. Для меня это слишком типично для Qt, слишком часто очень простые и, казалось бы, логичные вещи в конечном итоге работают не так, как ожидалось, или вообще не работают из-за некоторых странных деталей реализации.

Вот код, который я использовал:

class Worker : public QObject {
    Q_OBJECT
  public slots:
    void forwardKeyEvent(int k) {
      sendKeyEvent(k);
    }
  signals:
    void sendKeyEvent(int k);
};

class Controller : public QObject {
    Q_OBJECT
  public:
    Controller(QQmlApplicationEngine * e) : engine(e) {
      w = new Worker;
      t = new QThread;
      w->moveToThread(t);
      connect(this, SIGNAL(sendKeyEvent(int)), w, SLOT(forwardKeyEvent(int)));
      connect(w, SIGNAL(sendKeyEvent(int)), this, SLOT(receiveEvent(int)));
      t->start();
    }
  public slots:
    void receiveEvent(int k) {
      QCoreApplication::postEvent(engine->rootObjects().at(0), new QKeyEvent(QEvent::KeyPress, (Qt::Key)k, Qt::NoModifier));
      QCoreApplication::postEvent(engine->rootObjects().at(0), new QKeyEvent(QEvent::KeyRelease, (Qt::Key)k, Qt::NoModifier));
    }

  private:
    Worker * w;
    QThread * t;
    QQmlApplicationEngine * engine;
  signals:
    void sendKeyEvent(int k);
};

// QML side
  Column {
    TextEdit {
      width: 400
      height: 200
      Keys.onPressed: console.log("pressed ", event.key)
      Keys.onReleased: console.log("released ", event.key)
    }
    Row {
      Repeater {
        model: [Qt.Key_Left, Qt.Key_S, Qt.Key_Right]
        delegate: Rectangle {
          width: 50
          height: 50
          color: "red"
          Text {
            anchors.centerIn: parent
            text: modelData
          }
          MouseArea {
            anchors.fill: parent
            onClicked: Work.sendKeyEvent(modelData) // this is actually the worker controller
          }
        }
      }
    }
  }

Он совершает какие-то безумные действия взад и вперед, чтобы проверить межпотоковую публикацию событий, от QML к контроллеру C++, к объекту в другом потоке и обратно к контроллеру, который затем отправляет событие. Он работает так, как предполагалось: данные из вторичного потока правильно отправляются в цикл событий, но отправленные события не работают должным образом.

person dtech    schedule 26.01.2018
comment
Спасибо, не могли бы вы дать мне название слота, в который мне нужно отправить свой сигнал? - person gfitas; 26.01.2018
comment
Любой слот любого объекта, находящегося в основном потоке, из которого вы публикуете события. Это не обязательно должно быть что-то конкретное. - person dtech; 26.01.2018
comment
Итак, я попробовал это: ссылка, и когда вызывается лямбда-функция, в моем окне QML ничего не происходит. Я сравнил QThread::currentThreadId() моей лямбды и основной, и они одинаковы. У вас есть идея? - person gfitas; 27.01.2018
comment
Это должна быть функция слота типа, производного от QObject, чтобы заставить работать соединение с очередью. - person dtech; 27.01.2018
comment
Это то, что я использую, и оно работает как положено pastebin.com/k3bGyr71 - person dtech; 27.01.2018
comment
Приложение var является частью QGuiApplication (я думаю, что оно наследует QObject) - person gfitas; 27.01.2018
comment
Но вы не имеете в виду функцию member, которая помечена как слот и, таким образом, зарегистрирована в метасистеме Qt. - person dtech; 27.01.2018
comment
Кроме того, существует два основных формата подключения: один принимает 4 параметра (кроме необязательного типа подключения) — объект-отправитель и сигнал, объект-получатель и слот, а другой — только 3 параметра — объект-отправитель и сигнал и бесплатная функция. Вы передаете объект-приемник и, по сути, бесплатную функцию, которая плохо отформатирована и, вероятно, даже не должна компилироваться. - person dtech; 27.01.2018
comment
Наконец, как видно из опубликованного фрагмента, в моем конкретном случае мне пришлось опубликовать как нажатие клавиши, так и событие отпускания, чтобы получить ожидаемое поведение. - person dtech; 27.01.2018
comment
Итак, я попытался применить то, что вы сказали здесь, но в моем окне QML ничего не происходит. - person gfitas; 27.01.2018
comment
Я попытался адаптировать здесь то, что вы сказали в своем редактировании, но все равно не работает. Думаю переключиться на шитье вместо кодинга - person gfitas; 27.01.2018
comment
В вашем случае рабочий на самом деле является объектом apiExternalLink, и все, что вам нужно, это слот ReceiveEvent контроллера, к которому вы подключаетесь. Остальное было лишь частью моего теста и не нужно. Qt может как помочь, так и помешать. Он работает очень хорошо, если вы делаете с ним обычные вещи, но он полон ошибок и конструктивных ограничений, которые крайне мешают, если вы пытаетесь сделать с ним что-то более причудливое. Мне самому приходится постоянно изобретать велосипеды, чтобы обойти недостатки Qt. - person dtech; 27.01.2018
comment
Кроме того, плохая идея создавать и заполнять эту карту каждый раз. Для нескольких значений просто используйте переключатель. Или сделайте карту членом класса и инициализируйте в конструкторе и используйте повторно. - person dtech; 27.01.2018
comment
Поэтому я изменил свой код здесь, чтобы он соответствовал вашему редактированию, и по-прежнему ничего не происходит. - person gfitas; 27.01.2018

Собственно проблема исходила от системы и пользователя, надо виндовс сфокусировать на системе, чтобы заработало.

person gfitas    schedule 23.03.2018