关于qt:如何制作QML切换按钮来跟踪/控制任何布尔属性

How to make a QML toggle button that tracks/controls any boolean property

我今天进行的QML / QtQuick练习是制作一个ToggleButton小部件,我可以实例化该部件来监视指定的布尔QML属性的状态,当我单击ToggleButton时,也可以切换该属性的状态。

到目前为止,我的ToggleButton组件具有以下功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Contents of ToggleButton.qml
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4

Button {
    property bool isActive: false

    onClicked: {
        isActive = !isActive;
    }

    style: ButtonStyle {
        background: Rectangle {
            border.width: control.activeFocus ? 2 : 1
            border.color:"black"
            radius: 4
            color:  isActive ?"red" :"gray";
        }
    }
}

....这是我的小测试工具,用于查看它是否按照我想要的方式工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Contents of main.qml
import QtQuick 2.6
import QtQuick.Window 2.2

Window {
   visible: true
   width: 360
   height: 360

   Rectangle {
      property bool lighten: false;

      id:blueRect
      x: 32; y:32; width:64; height:64
      color: lighten ?"lightBlue" :"blue";

      MouseArea {
         anchors.fill: parent
         onClicked:    parent.lighten = !parent.lighten;
      }
   }

   Rectangle {
      property bool lighten: false;

      id:greenRect
      x:192; y:32; width:64; height:64
      color: lighten ?"lightGreen" :"green";

      MouseArea {
         anchors.fill: parent
         onClicked:    parent.lighten = !parent.lighten;
      }
   }

   ToggleButton {
      x:32; y:128
      text:"Bright Blue Rect"
      isActive: blueRect.lighten
   }

   ToggleButton {
      x:192; y:128
      text:"Bright Green Rect"
      isActive: greenRect.lighten
   }
}

您可以通过将代码分别保存到ToggleButton.qml和main.qml,然后运行" qmlscene main.qml"来运行它。

请注意,如果单击蓝色或绿色矩形,它将按预期工作;开启和关闭Rectangle对象的布尔" lighten"属性,导致Rectangle更改颜色,并且关联的ToggleButton也做出适当的反应(当" lighten"属性为true时,将自身变为红色;当" lighten"属性为true时,则变为灰色"属性为假)。

到目前为止,还算不错,但是如果您单击ToggleButton本身,则绑定已断开:也就是说,单击ToggleButton会导致ToggleButton变成预期的红色/灰色,但是矩形的颜色不符合要求,并且之后,单击矩形不再导致ToggleButton的状态发生变化。

我的问题是,正确执行此操作的诀窍是什么,以便在ToggleButton和它跟踪的属性之间始终保持双向对应? (请注意,理想的解决方案是将尽可能少的代码添加到main.qml中,因为我希望将此功能封装在ToggleButton.qml中,以最大程度地减少暴露给其余QML应用程序的复杂性)


这不起作用的原因是您正在用固定值覆盖绑定。通过在onClicked中手动分配值,可以用值覆盖绑定。

问题是:QML现在不支持2way绑定。但是,有一些技巧可以创建一个。参见http://imaginativethinking.ca/bi-direction-data-binding-qt-quick/

注意:为什么不使用按钮的isActive属性,而不使用按钮的选中状态? (来自文档)即使您单击按钮,绑定也不会中断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Contents of ToggleButton.qml
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4

Button {
    //use the"checked" property instead of your own"isActive"

    checkable: true

    style: ButtonStyle {
        background: Rectangle {
            border.width: control.activeFocus ? 2 : 1
            border.color:"black"
            radius: 4
            color:  checked?"red" :"gray";
        }
    }
}

看来解决此问题的一种方法是让ToggleButton使用别名属性而不是常规属性声明其状态。这样,只有一个外部属性(因为ToggleButton的内部属性实际上只是外部属性的别名),因此不需要双向绑定。这是按预期工作的QML代码的更新版本:

ToggleButton.qml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Contents of ToggleButton.qml
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4

Button {
    onClicked: {
        isActive = !isActive;
    }

    style: ButtonStyle {
        background: Rectangle {
            border.width: control.activeFocus ? 2 : 1
            border.color:"black"
            radius: 4
            color:  isActive ?"red" :"gray";
        }
    }    
}

main.qml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Contents of main.qml
import QtQuick 2.6
import QtQuick.Window 2.2

Window {
   visible: true
   width: 360
   height: 360

   Rectangle {
      property bool lighten: false;

      id:blueRect
      x: 32; y:32; width:64; height:64
      color: lighten ?"lightBlue" :"blue";

      MouseArea {
         anchors.fill: parent
         onClicked:    parent.lighten = !parent.lighten;
      }
   }

   Rectangle {
      property bool lighten: false;

      id:greenRect
      x:192; y:32; width:64; height:64
      color: lighten ?"lightGreen" :"green";

      MouseArea {
         anchors.fill: parent
         onClicked:    parent.lighten = !parent.lighten;
      }
   }

   ToggleButton {
      x:32; y:128
      text:"Bright Blue Rect"
      property alias isActive: blueRect.lighten
   }

   ToggleButton {
      x:192; y:128
      text:"Bright Green Rect"
      property alias isActive: greenRect.lighten
   }
}