如何在QML中创建一个组件,允许我使用默认值指定自定义属性,这些默认值可以被命令行参数覆盖,但最初不使用默认值?
我创建了一个QML组件,可以轻松地解析命令行属性并绑定到它们的值。它的用法如下:
Window {
visibility: appArgs.fullscreen ? "FullScreen" : "Windowed"
width: appArgs.width
height: width*9/16
CommandLineArguments {
id: appArgs
property real width: Screen.width
property bool fullscreen: false
Component.onCompleted: args(
['width', ['-w', '--width'], 'initial window width'],
['fullscreen', ['-f', '--fullscreen'], 'use full-screen mode']
)
}
}
$ ./myapp --help
qml: Command-line arguments supported:
-w, --width : initial window width (default:1920)
-f, --fullscreen : use full-screen mode (default:false)
它很有效,除了......
为我的组件创建的所有绑定最初都使用默认值。例如,如果我使用-w 800
启动我的应用,则窗口的width
最初以值1920开头,然后〜立即调整为800(当Component.onCompleted
代码运行时)。这个问题在90%的情况下都是不明显的,8%的时间会有点烦人......在最终的2%中无法使用。
有时我想要控制的属性只能设置一次。例如,使用易碎代码连接到的网络端口,当它发生更改时无法断开连接并重新连接到新端口。或者是为一种视觉样式加载大量资源的映射库,如果我尝试更改样式,则会抛出错误。
所以,我需要这些属性来获取命令行值 - 如果指定 - 第一次创建它们(否则使用默认值)。我怎样才能做到这一点?
答案 0 :(得分:2)
更新:实际上,在这种特殊情况下,实际上很容易避免调整大小 - 只需将可见性设置为false,然后将属性设置为所需的值,并将可见性设置为true:
Window {
id: main
visible: false
Component.onCompleted: {
main.width = ARG_Width // replace with
main.height = ARG_Width * 9/16 // your stuff
main.visibility = ARG_Fullscreen ? Window.FullScreen : Window.Windowed
main.visible = true
}
}
在这种情况下,它很方便,因为您可以直接隐藏窗口,直到您设置所需的属性值。如果您确实需要使用正确的初始值创建组件,您可以执行以下操作:
Item {
id: main
Component {
id: win
Window {
visible: true
width: ARG_Width
height: width*9/16
visibility: ARG_Fullscreen ? Window.FullScreen : Window.Windowed
}
}
Component.onCompleted: win.createObject(main)
}
在这种情况下,应用程序将在没有任何窗口的情况下启动,所需的值将在原型级别设置,以便其创建将被延迟并从一开始就具有正确的值。
这是可以理解的,毕竟在你的应用程序加载之后你没有读入参数。因此,它将加载默认值,然后切换到提供的参数。
如果你想避免这种情况,最直接的解决方案是读入参数并在加载主qml文件之前将它们作为上下文属性公开,例如这样(发布完全正常工作的代码,因为你提到你不是C ++家伙):
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
int w = 1920; // initial
bool f = false; // values
QStringList args = app.arguments();
if (args.size() > 1) { // we have arguments
QString a1 = args.at(1);
if (a1 == "-w") w = args.at(2).toInt(); // we have a -w, read in the value
else if (a1 == "-f") f = true; // we have a -f
}
engine.rootContext()->setContextProperty("ARG_Width", w); // expose as context
engine.rootContext()->setContextProperty("ARG_Fullscreen", f); // properties
engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); // load main qml
return app.exec();
}
然后在您的main.qml
文件中:
Window {
id: main
visible: true
width: ARG_Width
height: width*9/16
visibility: ARG_Fullscreen ? Window.FullScreen : Window.Windowed
}
创建组件后,它会立即获取正确的值。
答案 1 :(得分:1)
如果我完全将界面更改为我的组件,以便将默认值传递给一个返回值的函数,那么我就可以实现我的目标。
所以,而不是:
property real width: Screen.width
...
Component.onCompleted: args(
['width', ['-w', '--width'], 'initial window width'],
)
我必须使用类似的东西:
property real width: arg(Screen.width, ['-w', '--width'], 'real', 'initial window width')
这个新界面有一些缺点:
arg()
。app filename1 filename2
)。但它有其他好处:
使用示例:
CommandLineParameters {
id: appArgs
property string message: arg('hi mom', '--message', 'string', 'message to print')
property real width: arg(400, ['-w', '--width'], 'real', 'initial window width')
property bool fullscreen: arg(false, ['-f', '--fullscreen'], 'bool', 'use full screen?')
property var resolution: arg('100x200', '--resolution', getResolution)
function getResolution(str) {
return str.split('x').map(function(s){ return s*1 });
}
}
守则:
// CommandLineParameters.qml
import QtQml 2.2
QtObject {
property var _argVals
property var _help: []
function arg(value, flags, type, help) {
if (!_argVals) { // Parse the command line once only
_argVals = {};
var key;
for (var i=1,a=Qt.application.arguments;i<a.length;++i){
if (/^--?\S/.test(a[i])) _argVals[key=a[i]] = true;
else if (key) _argVals[key]=a[i], key=0;
else console.log('Unexpected command-line parameter "'+a[i]+'');
}
}
_help.push([flags.join?flags.join(", "):flags, help||'', '(default:'+value+')']);
// Replace the default value with one from command line
if (flags.forEach) flags.forEach(lookForFlag);
else lookForFlag(flags);
// Convert types to appropriate values
if (typeof type==='function') value = type(value);
else if (type=='real' || type=='int') value *= 1;
return value;
function lookForFlag(f) { if (_argVals[f] !== undefined) value=_argVals[f] }
}
Component.onCompleted: {
// Give help, if requested
if (_argVals['-h'] || _argVals['--help']) {
var maxF=Math.max.apply(Math,_help.map(function(a){return a[0].length}));
var maxH=Math.max.apply(Math,_help.map(function(a){return a[1].length}));
var lines=_help.map(function(a){
return pad(a[0],maxF)+" : "+pad(a[1],maxH)+" "+a[2];
});
console.log("Command-line arguments supported:\n"+lines.join("\n"));
Qt.quit(); // Requires connecting the slot in the main application
}
function pad(s,n){ return s+Array(n-s.length+1).join(' ') }
}
}