Adr*_*ire 6 c++ qt focus drop-down-menu
对于自定义小部件,类似于菜单,当空间不足时,我在“下拉”框架内放置一些内容:
对于此下拉功能,我将这些按钮的框架设置为frame.setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);. 这工作得很好,只是它扰乱了制表顺序。
小部件的结构如下所示:
MenuWidget
|- Tab1
|- Group_<without_name>
...
|- Group_File
...
|- Group_Export
|-Frame (can be either standard sub-widget, or a popup widgets)
|- Button 1
|- Button 2
....
Run Code Online (Sandbox Code Playgroud)
您可以在那里查看当前的 WIP: https: //github.com/Escain/QTopMenu
我将问题简化为以下代码,该代码在开始时具有正确的制表顺序,并且在我设置Qt::Popup并返回到Qt::Widget. 任何恢复正确的制表顺序的尝试都会失败。
设置前Qt::Popup:
设置Qt::Popup和设置后Qt::Widget:
这是重现该问题的最小示例。
免责声明:这不是生产代码,只是旨在重现相同问题的快速完成的代码。
#include <QApplication>
#include <QDebug>
#include <QWidget>
#include <QPushButton>
class TestWidget: public QWidget
{
Q_OBJECT
public:
explicit TestWidget( QWidget* parent=nullptr)
: QWidget(parent)
, frame(this)
, but1(&frame)
, but2(&frame)
{
but1.setGeometry(10, 10, 80, 30);
but2.setGeometry(110, 10, 80, 30);
frame.setGeometry(0,0,200,50);
resize(200,50);
setStyleSheet("background-color: rgba(0,128,128,32);");
setFocusProxy(&frame);
frame.setFocusProxy(&but1);
setFocusPolicy(Qt::FocusPolicy::StrongFocus);
connect(&but1, &QPushButton::clicked, [this]()
{
const auto flag = frame.windowFlags();
// In some circumstances, the widget needs to show it content as drop-down
frame.setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);
//try to recover here
frame.setWindowFlags(flag);
frame.show();
emit needsTabOrder();
});
}
QWidget* orderTabs( QWidget* first)
{
setTabOrder(first, &but1);
setTabOrder(&but1, &but2);
return &but2;
}
signals:
void needsTabOrder();
private:
QWidget frame;
QPushButton but1, but2;
};
#include "main.moc"
auto main (int argn, char* args[])-> int
{
QApplication app(argn, args);
QWidget window;
TestWidget t1(&window);
TestWidget t3(&window); //Intentionally out of order
TestWidget t2(&window);
TestWidget t4(&window);
t1.move(0,10);
t2.move(200,10);
t3.move(400,10);
t4.move(600,10);
QWidget::setTabOrder(&t1, &t2);
QWidget::setTabOrder(&t2, &t3);
QWidget::setTabOrder(&t3, &t4);
auto tryRecoverThisMess = [&t1, &t2, &t3, &t4]()
{
qDebug() << "Trying to recover this mess";
// Attempt 1: not working
QWidget::setTabOrder(&t1, &t2);
QWidget::setTabOrder(&t2, &t3);
QWidget::setTabOrder(&t3, &t4);
// Attempt 2: very hacky and not working
QWidget* w=nullptr;
w = t1.orderTabs( w);
w = t2.orderTabs( w);
w = t3.orderTabs( w);
w = t4.orderTabs( w);
};
QWidget::connect(&t1, &TestWidget::needsTabOrder, tryRecoverThisMess);
QWidget::connect(&t2, &TestWidget::needsTabOrder, tryRecoverThisMess);
QWidget::connect(&t3, &TestWidget::needsTabOrder, tryRecoverThisMess);
QWidget::connect(&t4, &TestWidget::needsTabOrder, tryRecoverThisMess);
window.resize(800,200);
window.show();
return app.exec();
}
Run Code Online (Sandbox Code Playgroud)
使用 Qt 5.15.2
CMakeLists 看起来像:
cmake_minimum_required(VERSION 3.13)
cmake_policy( SET CMP0076 NEW )
project("QtTest")
set(CMAKE_AUTOMOC ON)
find_package(Qt5Widgets REQUIRED)
include_directories(${Qt5Widgets_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
set(TARGET "QtTest")
add_executable(${TARGET})
target_compile_options( ${TARGET} PRIVATE -Wall -pedantic -Wno-padded -Werror=return-type)
target_compile_features( ${TARGET} PUBLIC cxx_std_17)
target_sources( ${TARGET} PRIVATE "main.cpp")
target_link_libraries(${TARGET} "Qt5::Widgets" )
Run Code Online (Sandbox Code Playgroud)
我尝试调用setTabOrder父小部件以及每个按钮(hacky)。没有成功。
要打印制表符顺序,它确实不会按照setTabOrder
不同的setWindowFlags价值观
setFocusProxy和的几种组合setFocusPolicy
让框架始终为“弹出”,并将其上的所有小部件在框架和主小部件之间移动。
设置标志后如何恢复 Tab 键顺序Qt::Popup?
use*_*148 -1
切勿使用自动
仅在枪口下使用 Lambda
auto tryRecoverThisMess = [&window, &t1, &t2, &t3, &t4]()
{
qDebug() << "Trying to recover this mess";
// Attempt 1: not working
window.setTabOrder( &t1, &t2 );
window.setTabOrder( &t2, &t3 );
window.setTabOrder( &t3, &t4 );
Run Code Online (Sandbox Code Playgroud)
#if 0 // 尝试 2:非常 hacky 并且不起作用 QWidget *w=nullptr; w = t1.orderTabs( w ); w = t2.orderTabs( w ); w = t3.orderTabs( w ); w = t4.orderTabs( w ); #万一
};
Run Code Online (Sandbox Code Playgroud)
您是否创建了一个从 QWidget 派生的类 MyClass 并给悲伤的类一个插槽 void tryRecoverThisMess();
您不需要参数,因为 TestWidget 项目将是成员变量。更重要的是,只需要插槽。
setTabOrder( &t1, &t2 );
setTabOrder( &t2, &t3 );
setTabOrder( &t3, &t4 );
Run Code Online (Sandbox Code Playgroud)
因为它将在小部件内执行。
您正在使用 auto 类型在 Lambda 内调用 QTabWidget::setTabOrder。编译器可以自由地确定此 Lambda 是 QWidget 并为其设置 Tab 键顺序,或者创建一个临时 QWidget,设置其 Tab 键顺序,然后在 Lambda 生命周期结束时将其丢弃。
您的代码中没有任何内容表明您想要更改“窗口”的选项卡顺序。
诚然,我在使用 Qt 5.12.8 的 Ubuntu 20.04 LTS 上尝试过此操作,但我确实看到单击后内容跳转到第三个按钮。我修复后他们没有这样做。“正确”的修复方法是将这些东西放入一个类中,并将 SIGNAL 连接到该类中的一个插槽。
我怀疑您的其他代码中也存在类似的育儿问题。
重组您的代码以删除所有 Lambda 并删除所有 auto。
Lambda 的 as slot 确实很危险。 当涉及创建然后移动到另一个线程的对象时,它们变得极其危险。Lambda 不是对象的一部分,它不必移动。这意味着您可能有一个排队连接,并且该 Lambda 槽尝试返回的对象可能会被删除。
年轻的开发人员喜欢使用 auto 和 Lambda,因为这可以让他们走捷径。
任意两点之间的最长距离就是捷径。
当你长大后,你会意识到这是 IT 领域的事实。
欢迎来到奇妙的编程世界!
根据您的要求,我“修补”了您的代码。它需要从头开始重新设计才能“修复”。
#include <QApplication>
#include <QDebug>
#include <QWidget>
#include <QPushButton>
class TestWidget: public QWidget
{
Q_OBJECT
public:
explicit TestWidget( QString txt, QWidget *parent=nullptr )
: QWidget( parent )
, button1( nullptr )
, button2( nullptr )
{
// don't create widgets on the stack
//
QString lbl1 = QString( "%1-%2" ).arg( txt ).arg( 1 );
QString lbl2 = QString( "%1-%2" ).arg( txt ).arg( 2 );
button1 = new QPushButton( lbl1, this );
button2 = new QPushButton( lbl2, this );
button1->setGeometry( 10, 10, 80, 30 );
button2->setGeometry( 110, 10, 80, 30 );
setGeometry( 0,0,200,50 );
resize( 200,50 ); // ????
setStyleSheet( "QPushButton{ background-color: rgba(170, 176, 81, 200); }"
"QPushButton:focus{ background-color: rgba(72, 75, 224, 200); } "
"QPushButton:focus:pressed{ background-color: rgba(229, 141, 29, 200); } "
"QPushButton:pressed{ background-color: rgba(241, 5, 10, 250); } " );
setFocusProxy( this );
setFocusProxy( button1 );
setFocusPolicy( Qt::FocusPolicy::StrongFocus );
setInternalTabOrder();
connect( button1, &QPushButton::clicked, [this]()
{
const auto flag = this->windowFlags();
// In some circumstances, the widget needs to show it content as drop-down
this->setWindowFlags( Qt::Popup | Qt::FramelessWindowHint );
//try to recover here
this->setWindowFlags( flag );
this->show();
emit needsTabOrder();
this->setFocus( ); // without this clicked button won't grab
// focus but a clicked button2 will
} );
}
~TestWidget()
{
if ( button1 != nullptr )
{
delete button1;
button1 = nullptr;
}
if ( button2 != nullptr )
{
delete button2;
button2 = nullptr;
}
}
void setInternalTabOrder()
{
setTabOrder( button1, button2 );
}
signals:
void needsTabOrder();
private:
QPushButton *button1;
QPushButton *button2;
};
#include "main.moc"
auto main ( int argn, char *args[] )-> int
{
QApplication app( argn, args );
// don't create widgets on the stack
//
QWidget *window = new QWidget();
TestWidget *t1 = new TestWidget( "Fred", window );
TestWidget *t3 = new TestWidget( "Ethyl", window ); //Intentionally out of order
TestWidget *t2 = new TestWidget( "Lucy", window );
TestWidget *t4 = new TestWidget( "Ricky", window );
t1->move( 0,10 );
t2->move( 200,10 );
t3->move( 400,10 );
t4->move( 600,10 );
QWidget::setTabOrder( t1, t2 );
QWidget::setTabOrder( t2, t3 );
QWidget::setTabOrder( t3, t4 );
auto tryRecoverThisMess = [window, t1, t2, t3, t4]()
{
qDebug() << "Trying to recover this mess";
window->setTabOrder( t1, t2 );
window->setTabOrder( t2, t3 );
window->setTabOrder( t3, t4 );
};
QWidget::connect( t1, &TestWidget::needsTabOrder, tryRecoverThisMess );
QWidget::connect( t2, &TestWidget::needsTabOrder, tryRecoverThisMess );
QWidget::connect( t3, &TestWidget::needsTabOrder, tryRecoverThisMess );
QWidget::connect( t4, &TestWidget::needsTabOrder, tryRecoverThisMess );
window->resize( 800,200 );
window->show();
return app.exec();
}
Run Code Online (Sandbox Code Playgroud)
尽管所有想要在任何地方使用 auto 和 lambda 的仇恨者都投了反对票,但这是唯一已发布的工作代码,并且已经过测试。没有其他人花时间实际编写工作代码。