Qt是一个不错的框架和优秀的UI工具包,它有many useful features and concepts。我们大多数人可能都同意Trolltech,最近Nokia,在开发它方面做得非常好。 Qt的最新进展之一是QML,我发现这是一项令人着迷的进步。
但是,我发现一些设计糟糕或执行不当的概念,例如Model/View(概念很好,但实现不是),Phonon媒体框架也是如此。有人说meta-object这个概念让他们疯狂。
所有这些显然或多或少都是主观的,但是你觉得在Qt中使用哪些特征或概念令人烦恼或负担,你如何绕过它们?
答案 0 :(得分:28)
我对Qt的大多数抱怨来自于API并未完全接受QObject提供的动态。如果你敢于创建一个元对象编译器来为C ++添加动态行为,为什么还要害羞呢?
我在下面列出的所有内容都是我的团队在某些时候需要的东西,我们必须自己编写代码。这很有趣,我们学到了很多关于Qt内部的知识,但我不介意它是否已经完成并准备好使用。
无分布式QObject
你知道,就像在Cocoa一样。他们用QtDBus走了一半 - 唯一剩下的就是网络。我们必须为此实现我们自己的解决方案,因为我们生活在Qt代码之外,我们无法更改内部实现所有不错的功能。
没有用于数据存储的API
当然,每个人都会编写自己不完整的QObject-to-SQLite库。不过,QDataStream是一个非常好的开端。
无数据绑定
好吧,Qt Quick有数据绑定,但数据绑定应该存在于QtCore中。通过良好的数据绑定,编写代表QObject集合的QAbstractItemModel应该是过去的事情:QObjectListModel应该是您所需要的。
(是的,QDataWidgetMapper是个笑话。)
没有QObjects的自动撤消管理
我们的模型类通常是QObjects,Q_PROPERTY有一个可选的NOTIFY信号,正是实现自动撤销所需的信号。这很容易做到它应该已经成为Qt的一部分。 (但是,它需要一些kludges。)
没有收藏属性
并非所有属性都是平等的。其中一些是收藏品。能够以抽象的方式处理这些将是一件好事。
半生不熟的QMetaStuff API
我只讨厌这个API因为我喜欢它。例如,一个人不能:
几乎所有这些都可以轻松解决。 #2的解决方案:
QVariant call(QObject* object, QMetaMethod metaMethod, QVariantList args)
{
QList<QGenericArgument> arguments;
for (int i = 0; i < args.size(); i++) {
// Notice that we have to take a reference to the argument. A
// const_cast is needed because calling data() would detach
// the QVariant.
QVariant& argument = args[i];
QGenericArgument genericArgument(
QMetaType::typeName(argument.userType()),
const_cast<void*>(argument.constData())
);
arguments << genericArgument;
}
QVariant returnValue(QMetaType::type(metaMethod.typeName()),
static_cast<void*>(NULL));
QGenericReturnArgument returnArgument(
metaMethod.typeName(),
const_cast<void*>(returnValue.constData())
);
// Perform the call
bool ok = metaMethod.invoke(
object,
Qt::AutoConnection, // In case the object is in another thread.
returnArgument,
arguments.value(0),
arguments.value(1),
arguments.value(2),
arguments.value(3),
arguments.value(4),
arguments.value(5),
arguments.value(6),
arguments.value(7),
arguments.value(8),
arguments.value(9)
);
if (!ok) {
// Handle the error...
} else {
return returnValue;
}
}
可能会删除有用的功能
在qt-interest中有一个关于DOM,style sheets和custom file engines将在未来版本的Qt中删除的话题。
Phonon没有跨平台后端
除了没有真正起作用之外,Phonon没有稳定的后端可以在三个最常见的平台上运行:Windows,Linux和Mac OS X.有一个VLC后端,但它绝对不稳定,它的许可证尚不清楚,而且,VLC对Mac的支持是“resting on shaky ground”。当然,责任完全在Linux上。多媒体支持有never been one of its strengths。它缺少类似Quicktime或DirectStuff的东西。
没有加密课程
有QCryptographicHash和QSSLSocket(及其funny error modes),就是这样。幸运的是,有两个好的库来填补这个空白:Botan和QCA。 QCA基于Qt,但是从Java加密类复制其API,所以不是很好。 Botan有一个漂亮的界面,(但?)是“纯粹的”C ++。 Qt风格的加密库仍然缺乏。
答案 1 :(得分:11)
对于制裁来说,这是一个奇怪的问题,但这里有:
qmake
牙齿很长(我not the only one to say so)。我使用cmake
,尽管它有自己的尴尬。
信号/插槽/等的元对象预处理构建步骤。是一个巨大的买入。许多愿意接受增加的抽象层次的人是那些被其他环境所吸引的人(Java,C#,无论如何)。在围栏的另一边是硬核C ++程序员,他们宁愿使用std::thread
而不是QThread
。
(如果一个C ++程序更加面向服务器且没有GUI,人们似乎会避开Qt,我明白了他们的观点。)
模型/视图既不在这里也不在那里,但它有点微不足道。我批评了线程亲和力问题:
http://blog.hostilefork.com/qt-model-view-different-threads/
此外,我已经将应用程序交叉编译到Mac,Windows和Linux,并且发现Qt并没有像我希望的那样保护我免受平台问题的影响。如果您查看内部以及如何通过极其多样化的代码(例如qnd_x11.cpp
,qdnd_win.cpp
和qdnd_mac.mm
)实现拖放,那么您会看到"Leaky Abstraction"原则发挥作用。 Qt并没有强加强烈的形式主义;你会得到不同顺序或重复的消息 - 或者在某些平台上根本没有消息。
但除了所有批评之外,我确实喜欢Qt的设计,文档,社区支持和一般审美。你可以做得更糟! (我在看着你,wxWidgets和GTK。)
答案 2 :(得分:5)
我试图解决的问题是Qt,wxWidgets以及可能的其他UI框架的一般问题:
void MainDialog::OnCppException() { throw std::runtime_error("test unhandled exception"); }
这种未处理的C ++异常被Qt框架捕获,阻止了立即异常调试或生成信息性崩溃转储。在Qt允许处理这种情况的地方,原始异常信息和堆栈跟踪将丢失。我试图在两个框架中解决这个问题,并没有找到可接受的解决方案。 Qt专业人士的建议“就是不要这样做”就是我所拥有的,这实际上意味着:不要犯错误,一切都会好的。这是我对Qt和wxWidjets的最大失望。
答案 3 :(得分:1)
我主要在Qt for S60环境中工作,所以有些问题是针对该平台的。
插件系统+ QObjects
你不能用信号声明插件接口,因为插件实现应该从QObject和多个接口派生,所以接口不应该是QObject本身(如果你想在你的接口中有一些信号,则需要)。我在Qt-interest邮件列表中找到的解决方法是将MyQObject * getter添加到您的插件接口,并将所有信号添加到具体的MyQObject类。它有效,但它违反直觉和丑陋。
QSet和其他Qt容器的功能不如stl或boost容器
例如,您无法定义在将元素插入QSet时应使用的较少函数。我想念的其他东西是remove_if和find_if。
QtMobility包中的QServiceFramework
荒谬的图书馆我最近被迫使用。要使用QServiceFramework中安装的“服务”,您必须链接到包含该服务的dll(考虑到其中一个QSf目标是隐藏依赖项,这是毫无意义的)或使用不提供编译的QMetaObject :: invokeMethod时间检查方法,参数类型等,并降低代码可读性:
// using QMetaObject::invokeMethod
QVariantHash data;
bool ok = QMetaObject::invokeMethod(myObject, "getStuff",
Q_RETURN_ARG(QVariantHash, data)
Q_ARG(QString, QString("blah")));
Q_ASSERT(ok);
// using normal syntax
QVariantHash data(myObject->getStuff("blah"));
更糟糕的是,它使用了很多文件系统(迭代dirs寻找插件,与SQLite数据库进行通信),这在S60上运行缓慢。
QPixmap需要QApplication ...
...只有QPixmap具有在原生S60图像(CFbsBitmap类)和Qt数据之间进行转换的方法。因此,您必须将您的应用程序设为QApplication(这会增加启动时间和内存消耗),或者您必须将数据存储在本机S60结构中(这使得整个代码特定于Symbian)
答案 4 :(得分:1)
我有同样的问题,尝试使用变量参数调用方法。我在这里报告了这个问题:https://bugreports.qt-project.org/browse/QTBUG-28833
他们实际上建议我使用未记录的实现细节来解决问题。所以我认为值得将它链接到这里。
最终接受了这个问题(优先级低,仅限Qt5)。