在 Qt 中确定父窗体的类型
Posted
技术标签:
【中文标题】在 Qt 中确定父窗体的类型【英文标题】:Determining type of parent form in Qt 【发布时间】:2017-12-10 23:21:14 【问题描述】:我有一个带有表 X 的数据库:我们称之为clients
。我还有一个与编辑表 X 内容相关的表单。当需要从表 X 中“挑选”一条记录时,它可以自行打开(浏览/编辑)以及从其他表单中打开。
我如何告诉父母一个特定的QModelIndex
已直接从on_tableView_doubleClicked(const QModelIndex &index)
信号处理程序中挑选出来?
目前,我只有一个“爸爸”表单,所以我知道将哪种类型的指针放入子表单(因此它可以保存指向父表单的指针)并且只是转换了它的类型的指针。
if (parent) daddy = qobject_cast<InvoiceEd*>(parent);
现在我想从另一个表单添加一个调用,我意识到我必须从QWidget*
指针中投射不同的指针,我不知道如何确定QWidget* parent
指针下的“变相”是什么。我该怎么做?
【问题讨论】:
【参考方案1】:我建议使用接口和dynamic_cast
来传递有关所选项目的信息:
InvoiceEdInterface.hpp
:
class InvoiceEdInterface
public:
virtual void SetSelectedItem (SelectedItemClass i_selected_item) = 0;
;
InvoiceEd.hpp
:
class InvoiceEd: public InvoiceEdInterface
public:
void SetSelectedItem (SelectedItemClass i_selected_item) override
// Process setting selected item.
;
DataForm.hpp
class DataForm
...
void on_tableView_doubleClicked(const QModelIndex &index)
auto invoice_ed dynamic_cast< InvoiceEdInterface* >(parent ());
if (invoice_ed)
invoice_ed->SetSelectedItem (...);
...
;
解决方案背后的想法非常简单:如果父窗口小部件实现了指定的接口,则调用SetSelectedItem()
。否则,什么都不会发生。
但是请注意,建议的解决方案可能不是Qt-ish。使用 Qt,您可以添加一个通知所选项目的信号。当在父控件中创建DataForm
对象以选择某个项目时,父控件应建立信号槽连接。这种解决方案比上面提出的解决方案更灵活,因为它允许程序中的任何对象获取有关所选项目的信息。
两种建议的解决方案都可以动态工作,不受基于模板的解决方案的限制。
【讨论】:
如果指针可以转换为QObject*
,那么qobject_cast
应该是一个更轻松的调用。至少对于较旧的 C++,许多人避免 dynamic_cast
只是因为它需要将 RTTI 添加到可执行文件中。不过,现在更频繁地使用动态转换。我猜处理器变得更快和更多内存。
@AlexanderVX,你是对的,qobject_cast
也可以用来达到同样的效果。显示基于dynamic_cast
的方法有两个原因:1) 我忘记了qobject_cast
,2) 它不仅可以用于基于 QObject 的类,因此更通用。基于dynamic_cast
的方法的另一个优点是它不需要您编写任何额外的代码——一切都在RTTI 的帮助下自动运行。而且我认为只有在有某些原因(例如分析结果或最小可执行文件大小的要求)时才应避免使用 RTTI。
你好,我在子窗体中公开了tableView,并在创建子窗体后将它的信号简单地连接到父窗体中的插槽。它有效!所以我会接受你的回答,因为你建议重新考虑信号槽连接【参考方案2】:
在一些现有的方法实现中有一段代码可以解析小部件类型:
// dataform.cpp
void DataForm::myEdit()
///
if (parent()) daddy = qobject_cast<InvoiceEd*>(parent());
作者想让它更灵活,例如根据调用者具体转换为某些小部件类型。这是可以做到的。让我们将所需的类型传递给它:
class DataForm
public:
// was void myEdit()
template <typename T = InvoiceEd> // defaults to InvoiceEd
void myEdit()
///
T* daddy = qobject_cast<T*>(parent());
// now we operate with the proper pointer so that
// exact instance does virtual function calls etc.
///
;
pDataForm->myEdit(); // default case
pDataForm->myEdit<MutatedInvoiceEd>(); // specified case
附:以上没有批评有问题的设计。在 OOP 中,我们通常不希望知道调用方法的上下文,或者对象不希望彼此识别。在这种情况下,您应该为不同的用途创建两种不同的方法,或者可能提供一个附加参数(可能具有默认值 void myEdit(bool insideOfContainerEdit = true)
以便代码了解一些主要用例。有很多方法可以处理它,但我们看不到你的全部代码。
当然,C++ 中模板的存在使我们能够在一定程度上解决问题。我自己在模板中找到帮助,有时可以避免编写更多代码或从类型等派生,但过度使用这种方法会导致很多头痛。理想情况下,我们应该要么依赖多态继承,要么完全通过带有参数类型的模板来处理这种情况。
【讨论】:
对我来说,如果它是一个独立的调用,我似乎可以在没有参数的情况下知道,因为我在表单的构造函数中有这个QWidget*
指针,当它出现时它将是 == 0是一个。所以我只是在我当前的类中添加了两个带有两个方法的模板(将构造函数放入其中以存储指针和信号处理程序以从存储的指针中调用不同的方法)?
从设计的角度来看:类接口通常越少越好。但当然,事情应该是有效的。以上是关于在 Qt 中确定父窗体的类型的主要内容,如果未能解决你的问题,请参考以下文章
qt编程,想在一个大的窗体上显示一个小的窗口,应该怎么编写程序呀,相当于一个父窗体,一个子窗体
qt QListWidget 添加鼠标移动事件(mouseMoveEvent),让父窗体可以监听到鼠标移动
Qt学习笔记2.窗体Widget && 屏幕坐标 && 布局