• 《QT实用小工具·三十九》仿 Windows10 画图3D 的颜色选择器, 但更加强大


    1、概述
    源码放在文章末尾

    该项目实现了仿 Windows10 画图3D 的颜色选择器,功能更加丰富更加强大。
    在这里插入图片描述

    项目部分代码如下所示:

    import QtQuick 2.15
    import QtQuick.Controls 2.15
    import QtQuick.Layouts 1.15
    import QtGraphicalEffects 1.15
    
    Item {
        id: root
        width: 460
        height: 500
        scale: 0
        opacity: 0
        enabled: false
    
        property bool movable: true
        property alias title: contentText.text
        property color initColor: "white"
        readonly property color currentColor: pickerRect.currentColor
    
        onInitColorChanged: pickerRect.setColor(initColor);
    
        signal accepted();
        signal rejected();
    
        function open() {
            focus = true;
            enabled = true;
        }
    
        function hide() {
            focus = false;
            enabled = false;
        }
    
        Keys.onEscapePressed: cancelButton.clicked();
    
        NumberAnimation on scale {
            running: root.enabled
            duration: 350
            easing.type: Easing.OutBack
            easing.overshoot: 1.0
            to: 1.0
        }
    
        NumberAnimation on opacity {
            running: root.enabled
            duration: 300
            easing.type: Easing.OutQuad
            to: 1.0
        }
    
        NumberAnimation on scale {
            running: !root.enabled
            duration: 300
            easing.type: Easing.InBack
            easing.overshoot: 1.0
            to: 0.0
        }
    
        NumberAnimation on opacity {
            running: !root.enabled
            duration: 250
            easing.type: Easing.OutQuad
            to: 0.0
        }
    
        RectangularGlow {
            width: parent.width + 4
            height: parent.height + 4
            anchors.centerIn: parent
            glowRadius: 4
            spread: 0.2
            color: "#206856E6"
            cornerRadius: 4
        }
    
        Rectangle {
            anchors.fill: parent
            color: "#f6f6f6"
            border.color: "#aea4ee"
        }
    
        MouseArea {
            anchors.fill: parent
            enabled: root.movable
    
            property point startPos: Qt.point(0, 0)
            property point offsetPos: Qt.point(0, 0)
    
            onClicked: (mouse) => mouse.accepted = false;
            onPressed: (mouse) => {
                startPos = Qt.point(mouse.x, mouse.y);
                cursorShape = Qt.SizeAllCursor;
            }
            onReleased: (mouse) => {
                startPos = Qt.point(mouse.x, mouse.y);
                cursorShape = Qt.ArrowCursor;
            }
            onPositionChanged: (mouse) => {
                if (pressed) {
                    offsetPos = Qt.point(mouse.x - startPos.x, mouse.y - startPos.y);
                    root.x = root.x + offsetPos.x;
                    root.y = root.y + offsetPos.y;
                }
            }
    
            Text {
                id: contentText
                height: 20
                anchors.top: parent.top
                anchors.topMargin: 15
                anchors.left: parent.left
                anchors.leftMargin: 25
                anchors.right: parent.right
                font.family: "微软雅黑"
                color: "#222255"
                text: qsTr("选择新颜色")
                antialiasing: true
                verticalAlignment: Text.AlignVCenter
            }
    
            Item {
                id: pickerRect
                width: 330
                height: 290
                anchors.top: contentText.bottom
                anchors.left: contentText.left
                anchors.leftMargin: -cursorWidth * 0.5
    
                property real cursorWidth: 30
                property color hueColor: {
                    let v = 1.0 - hueSlider.value;
    
                    if (0.0 <= v && v < 0.16) {
                        return Qt.rgba(1.0, 0.0, v / 0.16, 1.0);
                    } else if (0.16 <= v && v < 0.33) {
                        return Qt.rgba(1.0 - (v - 0.16) / 0.17, 0.0, 1.0, 1.0);
                    } else if (0.33 <= v && v < 0.5) {
                        return Qt.rgba(0.0, ((v - 0.33) / 0.17), 1.0, 1.0);
                    } else if (0.5 <= v && v < 0.76) {
                        return Qt.rgba(0.0, 1.0, 1.0 - (v - 0.5) / 0.26, 1.0);
                    } else if (0.76 <= v && v < 0.85) {
                        return Qt.rgba((v - 0.76) / 0.09, 1.0, 0.0, 1.0);
                    } else if (0.85 <= v && v <= 1.0) {
                        return Qt.rgba(1.0, 1.0 - (v - 0.85) / 0.15, 0.0, 1.0);
                    } else {
                        return "red";
                    }
                }
                property real saturation: colorPickerCursor.x / (width - cursorWidth)
                property real brightness: 1 - colorPickerCursor.y / (height - cursorWidth)
                property color currentColor: Qt.hsva(hueSlider.value, saturation, brightness, alphaSlider.value)
                property color __color: Qt.rgba(0, 0, 0, 0)
    
                function setColor(color) {
                    alphaSlider.x = alphaPicker.width == 0 ? 0 : (alphaPicker.width - alphaSlider.width) * color.a;
                    hueSlider.x = (huePicker.width - hueSlider.width) * (Math.max(color.hsvHue, 0));
                    colorPickerCursor.x = color.hsvSaturation * (width - cursorWidth);
                    colorPickerCursor.y = (1.0 - color.hsvValue) * (height - cursorWidth);
                }
    
                function fromColor() {
                    pickerRect.setColor(Qt.rgba(parseInt(redEditor.text) / 255.
                                                , parseInt(greenEditor.text) / 255.
                                                , parseInt(blueEditor.text) / 255.
                                                , parseInt(alphaEditor.text) / 255.));
                }
    
                function fromArgbColor() {
                    __color = '#' + argbEditor.text;
                    pickerRect.setColor(__color);
                }
    
                onCurrentColorChanged: {
                    redEditor.text = (currentColor.r * 255).toFixed(0);
                    greenEditor.text = (currentColor.g * 255).toFixed(0);
                    blueEditor.text = (currentColor.b * 255).toFixed(0);
                    alphaEditor.text = (currentColor.a * 255).toFixed(0);
                    argbEditor.text = currentColor.toString().replace("#", "");
                }
    
                Rectangle {
                    x: pickerRect.cursorWidth * 0.5
                    y: pickerRect.height - pickerRect.cursorWidth * 0.5
                    width: pickerRect.height - pickerRect.cursorWidth
                    height: pickerRect.width - pickerRect.cursorWidth
                    rotation: -90
                    transformOrigin: Item.TopLeft
                    gradient: Gradient {
                        GradientStop { position: 0.0; color: "white" }
                        GradientStop { position: 1.0; color: pickerRect.hueColor }
                    }
                }
    
                Rectangle {
                    x: pickerRect.cursorWidth * 0.5
                    y: pickerRect.cursorWidth * 0.5
                    width: pickerRect.width - pickerRect.cursorWidth
                    height: pickerRect.height - pickerRect.cursorWidth
                    gradient: Gradient {
                        GradientStop { position: 1.0; color: "#ff000000" }
                        GradientStop { position: 0.0; color: "#00000000" }
                    }
                }
    
                Rectangle {
                    id: colorPickerCursor
                    width: pickerRect.cursorWidth
                    height: pickerRect.cursorWidth
                    border.color: "#e6e6e6"
                    border.width: 1
                    color: pickerRect.currentColor
    
                    Behavior on scale { NumberAnimation { easing.type: Easing.OutBack; duration: 300 } }
    
                    Rectangle {
                        anchors.fill: parent
                        anchors.margins: 1
                        color: "transparent"
                        border.color: "white"
                        border.width: 1
                    }
                }
    
                MouseArea {
                    x: pickerRect.cursorWidth
                    y: pickerRect.cursorWidth
                    anchors.fill: parent
    
                    function handleCursorPos(x, y) {
                        let halfWidth = pickerRect.cursorWidth * 0.5;
                        colorPickerCursor.x = Math.max(0, Math.min(width , x + halfWidth) - pickerRect.cursorWidth);
                        colorPickerCursor.y = Math.max(0, Math.min(height, y + halfWidth) - pickerRect.cursorWidth);
                    }
    
                    onPositionChanged: (mouse) => handleCursorPos(mouse.x, mouse.y);
                    onPressed: (mouse) => {
                        colorPickerCursor.scale = 0.7;
                        handleCursorPos(mouse.x, mouse.y);
                    }
                    onReleased: colorPickerCursor.scale = 1.0;
                }
            }
    
            Item {
                id: previewItem
                width: 90
                height: 90
                anchors.left: pickerRect.right
                anchors.leftMargin: 10
                anchors.top: contentText.bottom
                anchors.topMargin: 15
    
                Grid {
                    id: previwBackground
                    anchors.fill: parent
                    rows: 11
                    columns: 11
                    clip: true
    
                    property real cellWidth: width / columns
                    property real cellHeight: height / rows
    
                    Repeater {
                        model: parent.columns * parent.rows
    
                        Rectangle {
                            width: previwBackground.cellWidth
                            height: width
                            color: (index % 2 == 0) ? "gray" : "transparent"
                        }
                    }
                }
    
                Rectangle {
                    anchors.fill: parent
                    anchors.margins: -2
                    color: pickerRect.currentColor
                    border.color: "#e6e6e6"
                    border.width: 2
                }
            }
    
            component ColorEditor: ColumnLayout {
                id: __layout
                width: previewItem.width
                height: 50
    
                property alias label: label.text
                property alias text: input.text
                property alias validator: input.validator
    
                signal textEdited();
                signal accepted();
    
                Text {
                    id: label
                    font.family: "微软雅黑"
                    color: "#222255"
                    verticalAlignment: Text.AlignVCenter
                    Layout.fillWidth: true
                }
    
                Rectangle {
                    clip: true
                    color: "transparent"
                    border.color: "#e6e6e6"
                    border.width: 2
                    Layout.fillHeight: true
                    Layout.fillWidth: true
    
                    TextInput {
                        id: input
                        leftPadding: 10
                        rightPadding: 10
                        selectionColor: "#398ed4"
                        selectByMouse: true
                        anchors.fill: parent
                        horizontalAlignment: TextInput.AlignRight
                        verticalAlignment: TextInput.AlignVCenter
                        onTextEdited: __layout.textEdited();
                        onAccepted: __layout.accepted();
                    }
                }
            }
    
            Column {
                anchors.top: previewItem.bottom
                anchors.topMargin: 10
                anchors.left: previewItem.left
                spacing: 6
    
                ColorEditor {
                    id: redEditor
                    label: "红色"
                    validator: IntValidator { top: 255; bottom: 0 }
                    onAccepted: pickerRect.fromColor();
                }
    
                ColorEditor {
                    id: greenEditor
                    label: "绿色"
                    validator: IntValidator { top: 255; bottom: 0 }
                    onAccepted: pickerRect.fromColor();
                }
    
                ColorEditor {
                    id: blueEditor
                    label: "蓝色"
                    validator: IntValidator { top: 255; bottom: 0 }
                    onAccepted: pickerRect.fromColor();
                }
    
                ColorEditor {
                    id: alphaEditor
                    label: "透明度"
                    validator: IntValidator { top: 255; bottom: 0 }
                    onAccepted: pickerRect.fromColor();
                }
    
                ColorEditor {
                    id: argbEditor
                    label: "十六进制 (ARGB)"
                    validator: RegularExpressionValidator { regularExpression: /[0-9a-fA-F]{0,8}/ }
                    onAccepted: pickerRect.fromArgbColor();
                }
            }
    
            Rectangle {
                id: huePicker
                width: pickerRect.width - pickerRect.cursorWidth
                height: 32
                anchors.top: pickerRect.bottom
                anchors.topMargin: 10
                anchors.left: contentText.left
                gradient: Gradient {
                    orientation: Gradient.Horizontal
                    GradientStop { position: 0.0;  color: "#ff0000" }
                    GradientStop { position: 0.16; color: "#ffff00" }
                    GradientStop { position: 0.33; color: "#00ff00" }
                    GradientStop { position: 0.5;  color: "#00ffff" }
                    GradientStop { position: 0.76; color: "#0000ff" }
                    GradientStop { position: 0.85; color: "#ff00ff" }
                    GradientStop { position: 1.0;  color: "#ff0000" }
                }
    
                Rectangle {
                    id: hueSlider
                    width: height
                    height: parent.height
                    anchors.verticalCenter: parent.verticalCenter
                    border.color: "#e6e6e6"
                    border.width: 2
                    scale: 0.9
                    color: pickerRect.hueColor
    
                    property real value: x / (parent.width - width)
    
                    Behavior on scale { NumberAnimation { easing.type: Easing.OutBack; duration: 300 } }
    
                    Rectangle {
                        anchors.fill: parent
                        anchors.margins: 1
                        color: "transparent"
                        border.color: "white"
                        border.width: 2
                    }
                }
    
                MouseArea {
                    anchors.fill: parent
    
                    function handleCursorPos(x) {
                        let halfWidth = hueSlider.width * 0.5;
                        hueSlider.x = Math.max(0, Math.min(width, x + halfWidth) - hueSlider.width);
                    }
    
                    onPressed: (mouse) => {
                        hueSlider.scale = 0.6;
                        handleCursorPos(mouse.x);
                    }
                    onReleased: hueSlider.scale = 0.9;
                    onPositionChanged: (mouse) => handleCursorPos(mouse.x);
                }
            }
    
            Item {
                id: alphaPickerItem
                width: huePicker.width
                height: huePicker.height
                anchors.top: huePicker.bottom
                anchors.topMargin: 25
                anchors.left: huePicker.left
    
                Grid {
                    id: alphaPicker
                    anchors.fill: parent
                    rows: 4
                    columns: 29
                    clip: true
    
                    property real cellWidth: width / columns
                    property real cellHeight: height / rows
    
                    Repeater {
                        model: parent.columns * parent.rows
    
                        Rectangle {
                            width: alphaPicker.cellWidth
                            height: width
                            color: (index % 2 == 0) ? "gray" : "transparent"
                        }
                    }
                }
    
                Rectangle {
                    anchors.fill: parent
                    gradient: Gradient {
                        orientation: Gradient.Horizontal
                        GradientStop { position: 1.0; color: "#ff000000" }
                        GradientStop { position: 0.0; color: "#00ffffff" }
                    }
                }
    
                Rectangle {
                    id: alphaSlider
                    x: parent.width - width
                    width: height
                    height: parent.height
                    anchors.verticalCenter: parent.verticalCenter
                    color: Qt.rgba(0.1, 0.1, 0.1, (value + 1.0) / 2.0)
                    border.color: "#e6e6e6"
                    border.width: 2
                    scale: 0.9
    
                    property real value: x / (parent.width - width)
    
                    Behavior on scale { NumberAnimation { easing.type: Easing.OutBack; duration: 300 } }
    
                    Rectangle {
                        anchors.fill: parent
                        anchors.margins: 1
                        color: "transparent"
                        border.color: "white"
                        border.width: 1
                    }
                }
    
                MouseArea {
                    anchors.fill: parent
    
                    function handleCursorPos(x) {
                        let halfWidth = alphaSlider.width * 0.5;
                        alphaSlider.x = Math.max(0, Math.min(width, x + halfWidth) - alphaSlider.width);
                    }
    
                    onPressed: (mouse) => {
                        alphaSlider.scale = 0.6;
                        handleCursorPos(mouse.x);
                    }
                    onReleased: alphaSlider.scale = 0.9;
                    onPositionChanged: (mouse) => handleCursorPos(mouse.x);
                }
            }
    
            Button {
                id: confirmButton
                width: 200
                height: alphaPickerItem.height
                anchors.top: alphaPickerItem.bottom
                anchors.topMargin: 25
                anchors.left: alphaPickerItem.left
                text: qsTr("确定")
                hoverEnabled: true
                topInset: down ? 1 : 0
                bottomInset: topInset
                leftInset: topInset
                rightInset: topInset
                font.family: "微软雅黑"
                onClicked: {
                    root.initColor = root.currentColor;
                    root.hide();
                    root.accepted();
                }
            }
    
            Button {
                id: cancelButton
                width: 200
                height: alphaPickerItem.height
                anchors.top: alphaPickerItem.bottom
                anchors.topMargin: 25
                anchors.right: parent.right
                anchors.rightMargin: 25
                text: qsTr("取消")
                hoverEnabled: true
                topInset: down ? 1 : 0
                bottomInset: topInset
                leftInset: topInset
                rightInset: topInset
                font.family: "微软雅黑"
                onClicked: {
                    pickerRect.setColor(root.initColor);
                    root.hide();
                    root.rejected();
                }
            }
        }
    }
    
    
    • 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
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549

    源码下载

  • 相关阅读:
    6.6K Star,比 Pandas 快很多的数据处理库
    .NET 6应用程序适配国产银河麒麟V10系统随记
    代码重构常用的技巧
    Hystrix服务降级fallback
    LeetCode 双周赛 103(2023/04/29)区间求和的树状数组经典应用
    必须要会回答的Java面试题(字符串篇)
    ThinkPHP8学习笔记
    Cesium点击实体,修改参数实现实体的平移和旋转
    VMware Workstation中安装系统和VMware tools
    [每日两题系列]刷算法题咯~~
  • 原文地址:https://blog.csdn.net/cs1395293598/article/details/138167779