Qt QColorDialog - Escape Key 不是 "eaten"(即不从事件队列中删除)
Qt QColorDialog - Escape Key not "eaten" (i.e. not remove from the event queue)
在我的应用程序中,我有一个带有一列的 QTableWidget,用户可以在其中单击以 select 一种颜色。它在以下限制下正常工作:当用户键入 Escape 键时,对话框将按预期取消并关闭,但按键事件仍保留在队列中。这导致此键事件的第二个效果,当对话框关闭时,就好像用户按下了两次键。
完整代码如下:
void CChildrenConfigScreen::actionCellClicked(int row, int col)
{
// ui->config_children_table->selectRow(row);
switch(col) {
case eColChildColor: {
CBiStateButton* cbox = reinterpret_cast<CBiStateButton*>(ui->config_children_table->cellWidget(row, 0));
assert( cbox != nullptr );
if( cbox->state() != Qt::Unchecked ) {
QTableWidgetItem* cell = ui->config_children_table->item(row, col);
cell->setSelected(false);
// =======
QColorDialog dialog(this);
dialog.setCurrentColor(cell->background().color());
dialog.exec();
if( dialog.result() == QDialog::Accepted ) {
cell->setBackground(QBrush(dialog.currentColor()));
} else {
// drop escape key event ???
}
// =======
}
}
break;
}
}
所以,我的问题:
- 这是正常行为吗?还是 Qt 中的错误?
- 我是不是做错了什么?
- 如果是,什么?
- 如果没有,我该怎么做才能“吃掉”这个活动呢? (最好没有创建 QColorDialog 女儿的麻烦 class)
这是我实施的解决方案。不是很漂亮,但是到目前为止我能做到的最好,并且可以按要求工作。定义有点冗长,但使用起来确实方便。
首先,一个基于堆栈的对象(Android 和 MacOS 尚未实现,仅 Windows):
// ==============================
// Used to disable the Escape key
class CKeyEventFilter {
public:
typedef char KeyType;
typedef std::function<void()> Functor;
private:
class CFilter: public QAbstractNativeEventFilter
{
public:
private:
KeyType mKey;
Functor mAction;
public:
explicit CFilter(KeyType key, Functor action)
: mKey(key)
, mAction(action)
{
}
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override
{
bool stop_it = false;
#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
if (eventType == "xcb_generic_event_t") {
xcb_generic_event_t* event = reinterpret_cast<xcb_generic_event_t *>(message);
assert(event != nullptr);
// ...
}
#elif defined(Q_OS_MACOS)
if (eventType == "mac_generic_NSEvent" {
MSG * event = reinterpret_cast<NSEvent*>(message);
assert(event != nullptr);
// ...
}
#elif defined(Q_OS_WINDOWS)
if (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG") {
MSG* event = reinterpret_cast<MSG*>(message);
assert(event != nullptr);
if(event->message == WM_KEYDOWN || event->message == WM_KEYUP ) {
if( event->wParam == mKey ) {
// keep stop_it to false for the key being processed
// but tells the 'next client' that he doesn't need
// to process it a second time.
mAction();
}
}
}
#else
#error "Yet unsupported OS"
#endif
return stop_it;
}
};
CFilter mFilter;
public:
explicit CKeyEventFilter(KeyType key, Functor action)
: mFilter(key, action)
{
qApp->installNativeEventFilter(&mFilter);
}
virtual ~CKeyEventFilter()
{
qApp->removeNativeEventFilter(&mFilter);
}
};
用法:
{
// Workaround with this stack based object (0x1B = 27 = ascii code for Esc char)
CKeyEventFilter filter(0x1B, []() { CMainWindow::win()->setSkipEscape(); } );
// Example of purpose: if dialog is left with the Escape Key,
// CMainWindow::setSkipEscape() in the lambda above will be called
QTableWidgetItem* cell = ui->config_children_table->item(row, col);
cell->setSelected(false);
QColorDialog dialog(this);
dialog.setCurrentColor(cell->background().color());
dialog.exec();
if( dialog.result() == QDialog::Accepted ) {
cell->setBackground(QBrush(dialog.currentColor()));
}
}
跳过实际上已经处理过的 Esc 键(在此示例中,在上面的 QColorDialog 中,但可能是 QMessageBox 或任何其他对话框):
void CMainWindow::popScreen()
{
// mSkipEscape is initialized to false in CMainWindow's constructor
// and set by CMainWindow::setSkipEscape() in the lambda above.
if( mSkipEscape ) {
mSkipEscape = false;
} else {
if( ! mScreenStack.empty()) {
EScreens screen = mScreenStack.top();
mScreenStack.pop();
this->goScreen(screen);
}
}
}
我希望这对某些人有用。
在我的应用程序中,我有一个带有一列的 QTableWidget,用户可以在其中单击以 select 一种颜色。它在以下限制下正常工作:当用户键入 Escape 键时,对话框将按预期取消并关闭,但按键事件仍保留在队列中。这导致此键事件的第二个效果,当对话框关闭时,就好像用户按下了两次键。
完整代码如下:
void CChildrenConfigScreen::actionCellClicked(int row, int col)
{
// ui->config_children_table->selectRow(row);
switch(col) {
case eColChildColor: {
CBiStateButton* cbox = reinterpret_cast<CBiStateButton*>(ui->config_children_table->cellWidget(row, 0));
assert( cbox != nullptr );
if( cbox->state() != Qt::Unchecked ) {
QTableWidgetItem* cell = ui->config_children_table->item(row, col);
cell->setSelected(false);
// =======
QColorDialog dialog(this);
dialog.setCurrentColor(cell->background().color());
dialog.exec();
if( dialog.result() == QDialog::Accepted ) {
cell->setBackground(QBrush(dialog.currentColor()));
} else {
// drop escape key event ???
}
// =======
}
}
break;
}
}
所以,我的问题:
- 这是正常行为吗?还是 Qt 中的错误?
- 我是不是做错了什么?
- 如果是,什么?
- 如果没有,我该怎么做才能“吃掉”这个活动呢? (最好没有创建 QColorDialog 女儿的麻烦 class)
这是我实施的解决方案。不是很漂亮,但是到目前为止我能做到的最好,并且可以按要求工作。定义有点冗长,但使用起来确实方便。
首先,一个基于堆栈的对象(Android 和 MacOS 尚未实现,仅 Windows):
// ==============================
// Used to disable the Escape key
class CKeyEventFilter {
public:
typedef char KeyType;
typedef std::function<void()> Functor;
private:
class CFilter: public QAbstractNativeEventFilter
{
public:
private:
KeyType mKey;
Functor mAction;
public:
explicit CFilter(KeyType key, Functor action)
: mKey(key)
, mAction(action)
{
}
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override
{
bool stop_it = false;
#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
if (eventType == "xcb_generic_event_t") {
xcb_generic_event_t* event = reinterpret_cast<xcb_generic_event_t *>(message);
assert(event != nullptr);
// ...
}
#elif defined(Q_OS_MACOS)
if (eventType == "mac_generic_NSEvent" {
MSG * event = reinterpret_cast<NSEvent*>(message);
assert(event != nullptr);
// ...
}
#elif defined(Q_OS_WINDOWS)
if (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG") {
MSG* event = reinterpret_cast<MSG*>(message);
assert(event != nullptr);
if(event->message == WM_KEYDOWN || event->message == WM_KEYUP ) {
if( event->wParam == mKey ) {
// keep stop_it to false for the key being processed
// but tells the 'next client' that he doesn't need
// to process it a second time.
mAction();
}
}
}
#else
#error "Yet unsupported OS"
#endif
return stop_it;
}
};
CFilter mFilter;
public:
explicit CKeyEventFilter(KeyType key, Functor action)
: mFilter(key, action)
{
qApp->installNativeEventFilter(&mFilter);
}
virtual ~CKeyEventFilter()
{
qApp->removeNativeEventFilter(&mFilter);
}
};
用法:
{
// Workaround with this stack based object (0x1B = 27 = ascii code for Esc char)
CKeyEventFilter filter(0x1B, []() { CMainWindow::win()->setSkipEscape(); } );
// Example of purpose: if dialog is left with the Escape Key,
// CMainWindow::setSkipEscape() in the lambda above will be called
QTableWidgetItem* cell = ui->config_children_table->item(row, col);
cell->setSelected(false);
QColorDialog dialog(this);
dialog.setCurrentColor(cell->background().color());
dialog.exec();
if( dialog.result() == QDialog::Accepted ) {
cell->setBackground(QBrush(dialog.currentColor()));
}
}
跳过实际上已经处理过的 Esc 键(在此示例中,在上面的 QColorDialog 中,但可能是 QMessageBox 或任何其他对话框):
void CMainWindow::popScreen()
{
// mSkipEscape is initialized to false in CMainWindow's constructor
// and set by CMainWindow::setSkipEscape() in the lambda above.
if( mSkipEscape ) {
mSkipEscape = false;
} else {
if( ! mScreenStack.empty()) {
EScreens screen = mScreenStack.top();
mScreenStack.pop();
this->goScreen(screen);
}
}
}
我希望这对某些人有用。