├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── c++ ├── qml │ ├── .editorconfig │ ├── .gitignore │ ├── CMakeLists.txt │ ├── README.md │ ├── build.sh │ ├── clean.sh │ └── src │ │ ├── main.cpp │ │ └── resources │ │ ├── components │ │ ├── common │ │ │ ├── CircleProgress.qml │ │ │ ├── Clock.qml │ │ │ ├── KakaListView.qml │ │ │ ├── KakaProgress.qml │ │ │ └── qmldir │ │ └── components.qrc │ │ ├── images │ │ ├── about.ico │ │ ├── exit.ico │ │ └── newFile.ico │ │ ├── main.qml │ │ ├── qml.qrc │ │ └── tabs │ │ ├── Tab1.qml │ │ └── Tab2.qml └── qt │ ├── .editorconfig │ ├── .gitignore │ ├── CMakeLists.txt │ ├── README.md │ ├── build.sh │ ├── clean.sh │ └── src │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ └── ui_mainwindow.h ├── docker-run.sh ├── docker ├── 5.12.0 │ ├── Dockerfile │ ├── build.sh │ ├── deploy.sh │ └── gpu.Dockerfile ├── 5.15.10 │ ├── Dockerfile │ ├── build.sh │ └── deploy.sh ├── Guide.md ├── README.md └── images │ └── mac_gui_docker_ssh.png ├── images ├── qml-template.png └── qt-template.png ├── python ├── pyqt5 │ ├── qml │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── README.md │ │ ├── qml_template.py │ │ └── src │ │ │ ├── __init__.py │ │ │ ├── components.py │ │ │ ├── main.py │ │ │ ├── qml.py │ │ │ └── resources │ │ │ ├── components │ │ │ ├── common │ │ │ │ ├── CircleProgress.qml │ │ │ │ ├── Clock.qml │ │ │ │ ├── KakaListView.qml │ │ │ │ ├── KakaProgress.qml │ │ │ │ └── qmldir │ │ │ └── components.qrc │ │ │ ├── images │ │ │ ├── about.ico │ │ │ ├── exit.ico │ │ │ └── newFile.ico │ │ │ ├── main.qml │ │ │ ├── qml.qrc │ │ │ └── tabs │ │ │ ├── Tab1.qml │ │ │ └── Tab2.qml │ └── qt │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── README.md │ │ ├── app │ │ └── __init__.py │ │ ├── gui │ │ ├── __init__.py │ │ ├── app.py │ │ ├── hellobox.py │ │ ├── hellotab.py │ │ ├── mainwindow.py │ │ ├── ui_hellobox.py │ │ └── ui_mainwindow.py │ │ ├── main.py │ │ └── requirements.txt └── pyside2 │ ├── qml │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── qml_template.py │ └── src │ │ ├── __init__.py │ │ ├── components.py │ │ ├── main.py │ │ ├── qml.py │ │ └── resources │ │ ├── components │ │ ├── common │ │ │ ├── CircleProgress.qml │ │ │ ├── Clock.qml │ │ │ ├── KakaListView.qml │ │ │ ├── KakaProgress.qml │ │ │ └── qmldir │ │ └── components.qrc │ │ ├── images │ │ ├── about.ico │ │ ├── exit.ico │ │ └── newFile.ico │ │ ├── main.qml │ │ ├── qml.qrc │ │ └── tabs │ │ ├── Tab1.qml │ │ └── Tab2.qml │ └── qt │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── gui │ ├── __init__.py │ ├── app.py │ ├── hellobox.py │ ├── hellotab.py │ ├── mainwindow.py │ ├── ui_hellobox.py │ └── ui_mainwindow.py │ ├── main.py │ └── requirements.txt └── videos └── wids-workshop-2020.mp4 /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.qml] 16 | indent_size = 4 17 | 18 | [*.js] 19 | indent_size = 2 20 | 21 | # Tab indentation (no size specified) 22 | [Makefile] 23 | indent_style = tab 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Mac 5 | .DS_Store 6 | ._.DS_Store 7 | 8 | # pyenv 9 | venv/ 10 | .venv/ 11 | 12 | # python 13 | *.pyc 14 | *.pro 15 | *~ 16 | __pycache__ 17 | 18 | # QML 19 | *.qmlc 20 | 21 | # cmake 22 | build/ 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright <2019> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qt Template/Example 2 | 3 | This is `template/example` that can rapid to build ```Qt/QML``` application. 4 | 5 | You can just copy you prefer program language's folder and start to code and build and run. 6 | 7 | Applications that build with this `Qt Template`: [Examples](#examples) 8 | 9 | ## Usage 10 | 11 | 1. Clone this repo 12 | 13 | ```bash 14 | $ git clone https://github.com/kaka-lin/qt-template.git 15 | ``` 16 | 17 | 2. Choosing you prefer program language, ex: ```c++``` 18 | 19 | ```bash 20 | $ cp -r c++/qml 21 | ``` 22 | 23 | 3. Buinding application 24 | 25 | Follow the steps of each folder's ```README``` 26 | 27 | ## Running and showing the Templates 28 | 29 | ### 1. Run with Docker (Recommend) 30 | 31 | You can use the docker image that we already build, as below 32 | 33 | ```bash 34 | $ docker pull kakalin/qt:5.12.0 35 | ``` 36 | 37 | Or you can build it from scratch, please check the document in the [docker folder](docker/README.md). 38 | 39 | And then running with docker: 40 | 41 | ```bash 42 | $ ./docker-run.sh 43 | ``` 44 | 45 | ### 2. Run with local Qt 46 | 47 | Please install Qt/QML on your local machine. 48 | 49 | ### 3. Run the template 50 | 51 | Chose the version that you want to use. 52 | 53 | ##### QML 54 | 55 | ```sh 56 | $ cd python/pyqt5/qml 57 | $ python3 qml_template.py 58 | ``` 59 | 60 | ![](./images/qml-template.png) 61 | 62 | ##### QT 63 | 64 | ```sh 65 | $ cd python/pyqt5/qt 66 | $ python3 main.py 67 | ``` 68 | 69 | ![](./images/qt-template.png) 70 | 71 | ## Examples 72 | 73 | Applications that build with `Qt`, you can reference projects as below, 74 | 75 | - Qt5/QML 76 | - [YUV Player](https://github.com/kaka-lin/YUVPlayer) 77 | - [Qt Video Player](https://github.com/kaka-lin/qt-video-player) 78 | - [Network Configuration Tool](https://github.com/kaka-lin/network-configuration-tool) 79 | 80 | - PyQt5 81 | 82 | - [serialport-gui](https://github.com/kaka-lin/serialport-gui) 83 | 84 | UART and ModBus 85 | 86 | - [pyqt-image-recognition](https://github.com/kaka-lin/pyqt-image-recognition) 87 | 88 | Handwriting recognition 89 | 90 | - [cvs-ptool](https://github.com/kaka-lin/csv-ptool) 91 | 92 | Embedding `Matplotlib` in Qt 93 | 94 | - [audio-analysis-tools](https://github.com/kaka-lin/audio-analysis-tools) 95 | 96 | Embedding `Matplotlib` in Qt 97 | 98 | - PyQt5/QML 99 | 100 | - [pytest-gui](https://github.com/kaka-lin/pytest-gui) 101 | 102 | Can see the result of unittest on GUI immediately. 103 | 104 | - [canbus-tool](https://github.com/kaka-lin/canbus-tool) 105 | 106 | Can dump canbus information 107 | -------------------------------------------------------------------------------- /c++/qml/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.qml] 16 | indent_size = 4 17 | 18 | [*.js] 19 | indent_size = 2 20 | 21 | # Tab indentation (no size specified) 22 | [Makefile] 23 | indent_style = tab 24 | -------------------------------------------------------------------------------- /c++/qml/.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Mac 5 | .DS_Store 6 | ._.DS_Store 7 | 8 | # pyenv 9 | venv/ 10 | .venv/ 11 | 12 | # python 13 | *.pyc 14 | *.pro 15 | *~ 16 | __pycache__ 17 | 18 | # QML 19 | *.qmlc 20 | 21 | # cmake 22 | build/ 23 | -------------------------------------------------------------------------------- /c++/qml/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(qml-template) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_CXX_EXTENSION OFF) 8 | 9 | # Find Qt location 10 | # Please change this to your qt5 path 11 | list(APPEND CMAKE_PREFIX_PATH "/usr/local/qt5") 12 | 13 | # Find includes in corresponding build directories 14 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 15 | # Instruct CMake to run moc automatically when needed. 16 | set(CMAKE_AUTOMOC ON) 17 | # Instruct CMake to run rcc automatically when needed. 18 | set(CMAKE_AUTORCC ON) 19 | 20 | # Find Qt5 libraies 21 | find_package(Qt5 COMPONENTS Core Qml Quick REQUIRED) 22 | 23 | # Source files 24 | FILE(GLOB_RECURSE SOURCE_FILES 25 | "${CMAKE_SOURCE_DIR}/src/*.cpp" 26 | "${CMAKE_SOURCE_DIR}/src/*.c" 27 | ) 28 | 29 | # Qt resources 30 | FILE(GLOB_RECURSE RESOURCES 31 | "${CMAKE_SOURCE_DIR}/src/*.qrc" 32 | ) 33 | 34 | add_definitions(-DFLUID_LOCAL) 35 | # Tell CMake to create the xxx executable 36 | add_executable(${PROJECT_NAME} ${SOURCE_FILES} ${RESOURCES}) 37 | 38 | # Use module from Qt5 39 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 40 | target_link_libraries(${PROJECT_NAME} 41 | PRIVATE Qt5::Core Qt5::Qml Qt5::Quick) 42 | 43 | install(TARGETS ${PROJECT_NAME} DESTINATION bin) 44 | -------------------------------------------------------------------------------- /c++/qml/README.md: -------------------------------------------------------------------------------- 1 | # QML Template 2 | 3 | ## Require 4 | 5 | - Qt5.10+ 6 | - CMake & Ninja 7 | 8 | ## build 9 | 10 | ### Method 1 11 | 12 | ```bash 13 | $ mkdir -p build && cd build 14 | $ cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug .. 15 | $ cmake --build . 16 | ``` 17 | 18 | ### Method 2 19 | 20 | ```bash 21 | $ chmod +x build.sh 22 | 23 | $ ./build.sh 24 | ``` 25 | 26 | ## clean 27 | 28 | ```bash 29 | $ chmod +x clean.sh 30 | 31 | $ ./clean.sh 32 | ``` 33 | 34 | ## Trobuleshooting 35 | 36 | ### Qt is not found 37 | 38 | Opening ```CMakeLists.txt``` and uncomment ```list(APPEND CMAKE_PREFIX_PATH ${Your Qt path})``` and modify Qt location 39 | -------------------------------------------------------------------------------- /c++/qml/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p build && cd build 4 | cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug .. 5 | cmake --build . 6 | -------------------------------------------------------------------------------- /c++/qml/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf build 4 | -------------------------------------------------------------------------------- /c++/qml/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | //#include "app/xxx.h" 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 11 | QGuiApplication app(argc, argv); 12 | QString program_name(argv[0]); 13 | 14 | // QML engine 15 | QQmlApplicationEngine engine; 16 | 17 | // QML context 18 | //engine.rootContext()->setContextProperty("xxx", new xxx); 19 | 20 | // Load QML scene 21 | engine.addImportPath(QStringLiteral("qrc:/resources")); 22 | engine.load(QUrl(QStringLiteral("qrc:/resources/main.qml"))); 23 | if (engine.rootObjects().isEmpty()) { 24 | return -1; 25 | } 26 | 27 | return app.exec(); 28 | } 29 | -------------------------------------------------------------------------------- /c++/qml/src/resources/components/common/CircleProgress.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | 4 | Item { 5 | id: root 6 | 7 | width: radius * 2 8 | height: radius * 2 9 | 10 | property int radius: 100 11 | property real arcBegin: 0 // start arc angle in degree 12 | property real arcEnd: 180 // end arc angle in degree 13 | property real arcOffset: 0 // rotation 14 | property real minValue: 0 15 | property real maxValue: 100 16 | property real value: 50 17 | property string text: "%" 18 | property bool isValue: true 19 | property bool showBackground: false // a full circle as a background of the arc 20 | property real lineWidth: 20 // width of the line 21 | property string circleColor: "#1dc58f" 22 | property string backgroundColor: "#E6E6E6" 23 | property string textColor: "#FFFFFF" 24 | 25 | property alias beginAnimation: animationArcBegin.enabled 26 | property alias endAnimation: animationArcEnd.enabled 27 | property alias valueAnimation: animationValue.enabled 28 | 29 | property int animationDuration: 200 30 | 31 | onArcBeginChanged: canvas.requestPaint() 32 | onArcEndChanged: canvas.requestPaint() 33 | onValueChanged: canvas.requestPaint() 34 | 35 | Behavior on arcBegin { 36 | id: animationArcBegin 37 | enabled: true 38 | NumberAnimation { 39 | duration: root.animationDuration 40 | easing.type: Easing.InOutCubic 41 | } 42 | } 43 | 44 | Behavior on arcEnd { 45 | id: animationArcEnd 46 | enabled: true 47 | NumberAnimation { 48 | duration: root.animationDuration 49 | easing.type: Easing.InOutCubic 50 | } 51 | } 52 | 53 | Behavior on value { 54 | id: animationValue 55 | enabled: true 56 | NumberAnimation { 57 | duration: root.animationDuration 58 | easing.type: Easing.InOutCubic 59 | } 60 | } 61 | 62 | Canvas { 63 | id: canvas 64 | anchors.fill: parent 65 | rotation: -90 + parent.arcOffset 66 | 67 | onPaint: { 68 | var ctx = getContext("2d") 69 | var x = root.radius 70 | var y = root.radius 71 | var angle = (value - minValue) / (maxValue - minValue) * 2 * Math.PI 72 | var start = Math.PI * (parent.arcBegin / 180) 73 | var end = Math.PI * (parent.arcEnd / 180) 74 | ctx.reset() 75 | 76 | if (root.isValue) { 77 | if (root.showBackground) { 78 | ctx.beginPath(); 79 | // arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise) 80 | ctx.arc(x, y, radius - root.lineWidth / 2, 0, Math.PI * 2, false) 81 | ctx.lineWidth = root.lineWidth 82 | ctx.strokeStyle = root.backgroundColor 83 | ctx.stroke() 84 | } 85 | ctx.beginPath(); 86 | ctx.arc(x, y, radius - root.lineWidth / 2, 0, angle, false) 87 | ctx.lineWidth = root.lineWidth 88 | ctx.strokeStyle = root.circleColor 89 | ctx.stroke() 90 | } else { 91 | if (root.showBackground) { 92 | ctx.beginPath(); 93 | ctx.arc(x, y, radius - root.lineWidth / 2, 0, Math.PI * 2, false) 94 | ctx.lineWidth = root.lineWidth 95 | ctx.strokeStyle = root.backgroundColor 96 | ctx.stroke() 97 | } 98 | ctx.beginPath(); 99 | ctx.arc(x, y, radius - root.lineWidth / 2, start, end, false) 100 | ctx.lineWidth = root.lineWidth 101 | ctx.strokeStyle = root.circleColor 102 | ctx.stroke() 103 | } 104 | } 105 | } 106 | 107 | Text { 108 | anchors.centerIn: parent 109 | 110 | text: root.value + root.text 111 | color: root.textColor 112 | 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /c++/qml/src/resources/components/common/Clock.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Rectangle { 4 | width: 100 5 | height: 20 6 | 7 | property real currentTimestamp 8 | 9 | function updateTime() { 10 | var now = new Date(); 11 | currentTimestamp = now.getTime(); 12 | } 13 | 14 | Timer { 15 | interval: 1000 16 | repeat: true 17 | running: true 18 | onTriggered: { updateTime(); } 19 | } 20 | 21 | Text { 22 | text: Qt.formatTime (new Date (currentTimestamp + (0 * 60000)), "A hh:mm") 23 | font.pixelSize: 18 24 | } 25 | 26 | Component.onCompleted: { updateTime(); } 27 | } 28 | -------------------------------------------------------------------------------- /c++/qml/src/resources/components/common/KakaListView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Layouts 1.3 3 | 4 | Rectangle { 5 | id: root 6 | anchors.fill: parent 7 | 8 | property variant internalModel 9 | signal rowChanged 10 | 11 | onRowChanged: { 12 | //listView.positionViewAtEnd(); 13 | listView.positionViewAtIndex(listView.count - 1, ListView.Beginning) 14 | } 15 | 16 | ListView { 17 | id: listView 18 | anchors.fill: parent 19 | contentWidth: 320 20 | clip: true 21 | 22 | model: parent.internalModel 23 | 24 | delegate: listDelegate 25 | } 26 | 27 | Component { 28 | id: listDelegate 29 | 30 | Rectangle { 31 | width: root.width 32 | height: 40 33 | color: index % 2 == 0 ? "white" : "lightgray" 34 | 35 | GridLayout { 36 | anchors.fill: parent 37 | columns: 2 38 | columnSpacing: (parent.width - itemText.contentWidth - dataText.contentWidth) - 10 39 | 40 | Text { 41 | id: itemText 42 | text: model.item 43 | font.bold: true 44 | font.pixelSize: 20 45 | } 46 | 47 | Text { 48 | id: dataText 49 | text: model.data 50 | font.pixelSize: 20 51 | color: "red" 52 | } 53 | } 54 | } 55 | } 56 | 57 | Component.onCompleted: { 58 | listView.positionViewAtEnd(); 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /c++/qml/src/resources/components/common/KakaProgress.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.2 3 | import QtGraphicalEffects 1.0 4 | 5 | ProgressBar { 6 | id: progressBar 7 | 8 | value: 0 9 | clip: true 10 | 11 | background: Rectangle { 12 | id: barFrame 13 | implicitWidth: 300 14 | implicitHeight: 20 15 | border.color: "#999999" 16 | radius: 5 17 | } 18 | 19 | contentItem: Item { 20 | id: item 21 | implicitWidth: 300 22 | implicitHeight: 18 23 | 24 | Rectangle { 25 | id: bar 26 | width: progressBar.visualPosition * parent.width 27 | height: parent.height 28 | radius: barFrame.radius 29 | 30 | /* 31 | Text { 32 | text: progressBar.value + '%' 33 | font.pointSize: 12 34 | font.weight: Font.Bold 35 | font.bold: true 36 | color: 'black' 37 | } 38 | */ 39 | } 40 | 41 | // 左右漸層顏色(綠 -> 亮綠 -> 綠) 42 | LinearGradient { 43 | anchors.fill: bar 44 | start: Qt.point(0, 0) 45 | end: Qt.point(bar.width, 0) 46 | source: bar 47 | opacity: 0.9 48 | 49 | gradient: Gradient { 50 | GradientStop { position: 0.0; color: "#17a81a" } 51 | GradientStop { id: grad; position: 0.5; color: Qt.lighter("#17a81a", 2) } 52 | GradientStop { position: 1.0; color: "#17a81a" } 53 | } 54 | 55 | /* 56 | PropertyAnimation { 57 | target: grad 58 | property: "position" 59 | from: 0.1 60 | to: 0.9 61 | duration: 1000 62 | running: true 63 | loops: Animation.Infinite 64 | } 65 | */ 66 | 67 | } 68 | 69 | // 上下漸層顏色 70 | LinearGradient { 71 | anchors.fill: bar 72 | start: Qt.point(0, 0) 73 | end: Qt.point(0, bar.height) 74 | source: bar 75 | opacity: 0.9 76 | 77 | gradient: Gradient { 78 | GradientStop { position: 0.0; color: Qt.rgba(0,0,0,0) } 79 | GradientStop { position: 0.5; color: Qt.rgba(1,1,1,0.3) } 80 | GradientStop { position: 1.0; color: Qt.rgba(0,0,0,0.05) } 81 | } 82 | } 83 | } 84 | 85 | /* 86 | PropertyAnimation { 87 | target: progressBar 88 | property: "value" 89 | from: 0 90 | to: 1 91 | duration: 5000 92 | running: true 93 | loops: Animation.Infinite 94 | } 95 | */ 96 | } 97 | -------------------------------------------------------------------------------- /c++/qml/src/resources/components/common/qmldir: -------------------------------------------------------------------------------- 1 | module components.common 2 | 3 | CircleProgress 1.0 CircleProgress.qml 4 | Clock 1.0 Clock.qml 5 | KakaProgress 1.0 KakaProgress.qml 6 | KakaListView 1.0 KakaListView.qml 7 | -------------------------------------------------------------------------------- /c++/qml/src/resources/components/components.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | common/CircleProgress.qml 4 | common/Clock.qml 5 | common/KakaProgress.qml 6 | common/KakaListView.qml 7 | common/qmldir 8 | 9 | 10 | -------------------------------------------------------------------------------- /c++/qml/src/resources/images/about.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/c++/qml/src/resources/images/about.ico -------------------------------------------------------------------------------- /c++/qml/src/resources/images/exit.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/c++/qml/src/resources/images/exit.ico -------------------------------------------------------------------------------- /c++/qml/src/resources/images/newFile.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/c++/qml/src/resources/images/newFile.ico -------------------------------------------------------------------------------- /c++/qml/src/resources/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Controls 2.1 3 | import QtQuick.Layouts 1.3 4 | import QtQuick.Dialogs 1.2 5 | 6 | import "tabs" 7 | import components.common 1.0 8 | 9 | ApplicationWindow { 10 | id: window 11 | visible: true 12 | minimumWidth: 640 13 | minimumHeight: 480 14 | 15 | title: qsTr("Qml Template") 16 | 17 | ////////////////////////////////////////////////////////////////////////// 18 | // menu 19 | header: ToolBar { 20 | id: menu 21 | 22 | background: Rectangle { 23 | implicitWidth: 100 24 | implicitHeight: 50 25 | border.color: "#999" 26 | 27 | gradient: Gradient { 28 | GradientStop { position: 0 ; color: "#fff" } 29 | GradientStop { position: 1 ; color: "#eee" } 30 | } 31 | } 32 | 33 | Row { 34 | anchors.fill: parent 35 | spacing: 5 36 | 37 | ToolButton { 38 | Image { 39 | id: newFileImage 40 | source: "images/newFile.ico" 41 | asynchronous:true 42 | fillMode: Image.PreserveAspectFit 43 | anchors.fill: parent 44 | } 45 | anchors.verticalCenter: parent.verticalCenter 46 | onClicked: fileDialog.open(); 47 | } 48 | 49 | ToolButton { 50 | Image { 51 | id: aboutImage 52 | source: "images/about.ico" 53 | asynchronous:true 54 | fillMode: Image.PreserveAspectFit 55 | anchors.fill: parent 56 | } 57 | anchors.verticalCenter: parent.verticalCenter 58 | onClicked: aboutBox.open(); 59 | } 60 | 61 | ToolButton { 62 | Image { 63 | id: exitImage 64 | source: "images/exit.ico" 65 | asynchronous:true 66 | fillMode: Image.PreserveAspectFit 67 | anchors.fill: parent 68 | } 69 | anchors.verticalCenter: parent.verticalCenter 70 | onClicked: { 71 | Qt.quit(); 72 | } 73 | } 74 | } 75 | 76 | Clock { 77 | id: clock 78 | anchors.right: parent.right 79 | anchors.verticalCenter: parent.verticalCenter 80 | 81 | gradient: Gradient { 82 | GradientStop { position: 0 ; color: "#fff" } 83 | GradientStop { position: 1 ; color: "#eee" } 84 | } 85 | } 86 | } 87 | 88 | MessageDialog { 89 | id: aboutBox 90 | title: "About" 91 | text: " 92 | This is QML Template\n 93 | Version: 0.1 94 | Date:2018/10/11" 95 | icon: StandardIcon.Information 96 | } 97 | 98 | FileDialog { 99 | id: fileDialog 100 | visible: false 101 | title: "Please choose a file" 102 | folder: shortcuts.home 103 | selectFolder: true 104 | } 105 | ////////////////////////////////////////////////////////////////////////// 106 | // footer 107 | 108 | footer: TabBar { 109 | id: tabBar 110 | width: parent.width 111 | height: 40 112 | currentIndex: 0 113 | 114 | TabButton { 115 | text: qsTr("Tab1") 116 | onClicked: { 117 | // 118 | } 119 | } 120 | 121 | TabButton { 122 | text: qsTr("Tab2") 123 | onClicked: { 124 | // 125 | } 126 | } 127 | } 128 | 129 | StackLayout { 130 | id: layout 131 | anchors.fill: parent 132 | currentIndex: tabBar.currentIndex 133 | 134 | Tab1 { 135 | id: aTab 136 | } 137 | 138 | Tab2 { 139 | id: bTab 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /c++/qml/src/resources/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | tabs/Tab1.qml 6 | tabs/Tab2.qml 7 | 8 | images/about.ico 9 | images/exit.ico 10 | images/newFile.ico 11 | 12 | 13 | -------------------------------------------------------------------------------- /c++/qml/src/resources/tabs/Tab1.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Controls 2.1 3 | import QtQuick.Layouts 1.3 4 | 5 | Page { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /c++/qml/src/resources/tabs/Tab2.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Controls 2.1 3 | import QtQuick.Layouts 1.3 4 | 5 | Page { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /c++/qt/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.qml] 16 | indent_size = 4 17 | 18 | [*.js] 19 | indent_size = 2 20 | 21 | # Tab indentation (no size specified) 22 | [Makefile] 23 | indent_style = tab 24 | -------------------------------------------------------------------------------- /c++/qt/.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Mac 5 | .DS_Store 6 | ._.DS_Store 7 | 8 | # pyenv 9 | venv/ 10 | .venv/ 11 | 12 | # python 13 | *.pyc 14 | *.pro 15 | *~ 16 | __pycache__ 17 | 18 | # cmake 19 | build/ 20 | -------------------------------------------------------------------------------- /c++/qt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(qt_template) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_CXX_EXTENSION OFF) 8 | 9 | # Find Qt location 10 | # Please change this to your qt5 path 11 | list(APPEND CMAKE_PREFIX_PATH "/usr/local/qt5") 12 | 13 | # Find includes in corresponding build directories 14 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 15 | # Instruct CMake to run moc automatically when needed. 16 | set(CMAKE_AUTOMOC ON) 17 | # Instruct CMake to run rcc automatically when needed. 18 | set(CMAKE_AUTORCC ON) 19 | 20 | # Find Qt5 libraies 21 | find_package(Qt5 REQUIRED COMPONENTS 22 | Core Gui Widgets) 23 | 24 | # Source files 25 | FILE(GLOB_RECURSE SOURCE_FILES 26 | "${CMAKE_SOURCE_DIR}/src/*.cpp" 27 | "${CMAKE_SOURCE_DIR}/src/*.c" 28 | ) 29 | 30 | add_definitions(-DFLUID_LOCAL) 31 | # Tell CMake to create the xxx executable 32 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 33 | 34 | # Use module from Qt5 35 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 36 | target_link_libraries(${PROJECT_NAME} 37 | PRIVATE Qt5::Core Qt5::Widgets) 38 | 39 | install(TARGETS ${PROJECT_NAME} DESTINATION bin) 40 | -------------------------------------------------------------------------------- /c++/qt/README.md: -------------------------------------------------------------------------------- 1 | # Qt Template 2 | 3 | ## Require 4 | 5 | - Qt5.10+ 6 | - CMake & Ninja 7 | 8 | ## build 9 | 10 | ### Method 1 11 | 12 | ```bash 13 | $ mkdir -p build && cd build 14 | $ cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug .. 15 | $ cmake --build . 16 | ``` 17 | 18 | ### Method 2 19 | 20 | ```bash 21 | $ chmod +x build.sh 22 | 23 | $ ./build.sh 24 | ``` 25 | 26 | ## clean 27 | 28 | ```bash 29 | $ chmod +x clean.sh 30 | 31 | $ ./clean.sh 32 | ``` 33 | 34 | ## Trobuleshooting 35 | 36 | ### Qt is not found 37 | 38 | Opening ```CMakeLists.txt``` and uncomment ```list(APPEND CMAKE_PREFIX_PATH ${Your Qt path})``` and modify Qt location 39 | -------------------------------------------------------------------------------- /c++/qt/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p build && cd build 4 | cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug .. 5 | cmake --build . 6 | -------------------------------------------------------------------------------- /c++/qt/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf build 4 | -------------------------------------------------------------------------------- /c++/qt/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication app(argc, argv); 7 | MainWindow main_window; 8 | 9 | main_window.show(); 10 | 11 | return app.exec(); 12 | } 13 | -------------------------------------------------------------------------------- /c++/qt/src/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | 4 | MainWindow::MainWindow(QWidget *parent) : 5 | QMainWindow(parent), 6 | ui(new Ui::MainWindow) 7 | { 8 | ui->setupUi(this); 9 | 10 | connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::btnClicked); 11 | } 12 | 13 | MainWindow::~MainWindow() 14 | { 15 | delete ui; 16 | } 17 | 18 | void MainWindow::btnClicked() 19 | { 20 | ui->pushButton->setText(tr("Button clicked")); 21 | } 22 | -------------------------------------------------------------------------------- /c++/qt/src/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | 7 | namespace Ui { 8 | class MainWindow; 9 | } 10 | 11 | class MainWindow : public QMainWindow 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit MainWindow(QWidget *parent = 0); 17 | ~MainWindow(); 18 | 19 | public slots: 20 | void btnClicked(); 21 | 22 | private: 23 | Ui::MainWindow *ui; 24 | }; 25 | 26 | #endif // MAINWINDOW_H 27 | -------------------------------------------------------------------------------- /c++/qt/src/ui_mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef UI_MAINWINDOW_H 2 | #define UI_MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | QT_BEGIN_NAMESPACE 10 | 11 | class Ui_MainWindow 12 | { 13 | public: 14 | QWidget *centralwidget; 15 | QPushButton *pushButton; 16 | 17 | void setupUi(QMainWindow *MainWindow) 18 | { 19 | if (MainWindow->objectName().isEmpty()) 20 | MainWindow->setObjectName(QStringLiteral("MainWindow")); 21 | MainWindow->resize(640, 480); 22 | 23 | centralwidget = new QWidget(MainWindow); 24 | centralwidget->setObjectName(QStringLiteral("centralwidget")); 25 | pushButton = new QPushButton(centralwidget); 26 | pushButton->setObjectName(QStringLiteral("pushButton")); 27 | pushButton->setGeometry(QRect(50, 220, 150, 60)); 28 | MainWindow->setCentralWidget(centralwidget); 29 | 30 | retranslateUi(MainWindow); 31 | } 32 | 33 | void retranslateUi(QMainWindow *MainWindow) 34 | { 35 | MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", Q_NULLPTR)); 36 | pushButton->setText(QApplication::translate("Mainwindow", "Button", Q_NULLPTR)); 37 | } 38 | }; 39 | 40 | namespace Ui { 41 | class MainWindow: public Ui_MainWindow {}; 42 | } 43 | 44 | QT_END_NAMESPACE 45 | 46 | #endif // UI_MAINWINDOW_H 47 | -------------------------------------------------------------------------------- /docker-run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | XSOCK=/tmp/.X11-unix 4 | # change the XAUTH to your own .Xauthority file 5 | XAUTH=/root/.Xauthority 6 | 7 | DOCKER_REPO=kakalin/ 8 | BRAND=qt 9 | VERSION=5.15.10 10 | IMAGE_NAME=${DOCKER_REPO}${BRAND}:${VERSION} 11 | 12 | # https://stackoverflow.com/questions/3162385/how-to-split-a-string-in-shell-and-get-the-last-field 13 | PROJECT_FOLDER=${PWD##*/} 14 | 15 | GPU_DEVICE="" 16 | if [[ -z $1 ]]; then 17 | GPU_DEVICE="all" 18 | else 19 | GPU_DEVICE="device=$1" 20 | fi 21 | 22 | # -e QT_X11_NO_MITSHM=1: Disables MIT-SHM extension for Qt applications. 23 | # -e XAUTHORITY=$XAUTH: Sets the XAUTHORITY environment variable to the .Xauthority file. 24 | # -v $XSOCK:$XSOCK: Mounts the X11 socket to the container. 25 | # -v $HOME/.Xauthority:$XAUTH: Mounts the .Xauthority file to the container. 26 | # --network=host: Shares the host network with the container. 27 | # --privileged: Enables privileged mode for the container.s 28 | docker_run_params=$(cat <<-END 29 | --rm -it \ 30 | -e QT_X11_NO_MITSHM=1 \ 31 | -e XAUTHORITY=$XAUTH \ 32 | -v $XSOCK:$XSOCK \ 33 | -v $HOME/.Xauthority:$XAUTH \ 34 | --network=host \ 35 | -v $PWD:/root/$PROJECT_FOLDER \ 36 | -w /root/$PROJECT_FOLDER \ 37 | --privileged \ 38 | $IMAGE_NAME 39 | END 40 | ) 41 | 42 | # xhost 開放權限 43 | ## on Local machine 44 | xhost +localhost # for macOS 45 | xhost +local:docker # for Linux 46 | ## on Remote machine 47 | # xhost + # or xhost + 48 | if [[ "$OSTYPE" == "linux-gnu"* ]]; then 49 | GPU=$(lspci | grep -i '.* vga .* nvidia .*') 50 | if [[ $GPU == *' NVIDIA '* ]]; then 51 | # -e PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native: Sets the PulseAudio server environment variable. 52 | # -v ${XDG_RUNTIME_DIR}/pulse/native:${XDG_RUNTIME_DIR}/pulse/native: Mounts the PulseAudio socket to the container. 53 | # -v $PWD:/home/user/$PROJECT_FOLDER: Mounts the current directory to the container. 54 | docker run \ 55 | --gpus $GPU_DEVICE \ 56 | -e DISPLAY=$DISPLAY \ 57 | -e PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native \ 58 | -v ${XDG_RUNTIME_DIR}/pulse/native:${XDG_RUNTIME_DIR}/pulse/native \ 59 | -v ~/.config/pulse/cookie:/root/.config/pulse/cookie \ 60 | $docker_run_params 61 | else 62 | docker run \ 63 | -e QT_QUICK_BACKEND=software \ 64 | -e DISPLAY=$DISPLAY \ 65 | -e PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native \ 66 | -v ${XDG_RUNTIME_DIR}/pulse/native:${XDG_RUNTIME_DIR}/pulse/native \ 67 | -v ~/.config/pulse/cookie:/root/.config/pulse/cookie \ 68 | $docker_run_params 69 | fi 70 | elif [[ "$OSTYPE" == "darwin"* ]]; then 71 | docker run \ 72 | -e DISPLAY=host.docker.internal:0 \ 73 | -e QT_QUICK_BACKEND=software \ 74 | $docker_run_params 75 | else 76 | echo "Haven't supported this OS Type, please check command\n and update it." 77 | fi 78 | -------------------------------------------------------------------------------- /docker/5.12.0/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | LABEL maintainer="kaka " 3 | 4 | # Ubuntu 18.04: tzdata issue 5 | # set noninteractive installation 6 | RUN apt-get update && \ 7 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 8 | tzdata locales\ 9 | git wget curl\ 10 | build-essential cmake ninja-build g++ \ 11 | sudo apt-utils \ 12 | pkg-config \ 13 | perl \ 14 | python-dev python3-dev \ 15 | python-pip python3-pip \ 16 | libdbus-1-3 \ 17 | # Libxcb 18 | '^libxcb.*-dev'\ 19 | libx11-xcb-dev \ 20 | libglu1-mesa-dev \ 21 | libxrender-dev \ 22 | libxi-dev \ 23 | libxkbcommon-dev \ 24 | libxkbcommon-x11-dev \ 25 | # Qt WebKit\ 26 | ruby bison flex gperf \ 27 | libicu-dev libxslt-dev \ 28 | # Qt WebEngine 29 | libfontconfig1 libfontconfig1-dev libudev-dev \ 30 | # Qt Multimedia 31 | libasound2-dev \ 32 | libgstreamer1.0-dev \ 33 | libgstreamer-plugins-base1.0-dev \ 34 | libgstreamer-plugins-good1.0-dev \ 35 | libgstreamer-plugins-bad1.0-dev \ 36 | mesa-common-dev libgl1-mesa-dev 37 | 38 | # GStreamer support 39 | RUN apt-get install -y --no-install-recommends \ 40 | libgstreamer1.0-0 \ 41 | gstreamer1.0-libav \ 42 | gstreamer1.0-plugins-bad \ 43 | gstreamer1.0-plugins-base \ 44 | gstreamer1.0-plugins-good \ 45 | gstreamer1.0-plugins-ugly \ 46 | gstreamer1.0-libav \ 47 | gstreamer1.0-doc \ 48 | gstreamer1.0-tools \ 49 | libpulse-mainloop-glib0 \ 50 | alsa-base \ 51 | alsa-utils \ 52 | pulseaudio 53 | 54 | # OpenGL support: GLUT / GLFW / GLEW support 55 | # mesa-utils: provides glxgears and glxinfo 56 | # x11-apps: provides xeyes 57 | RUN apt-get install -y --no-install-recommends \ 58 | x11-apps \ 59 | mesa-utils \ 60 | freeglut3-dev \ 61 | libglfw3-dev \ 62 | libglew-dev 63 | 64 | RUN apt -y autoremove && \ 65 | apt -y autoclean && \ 66 | apt -y clean && \ 67 | rm -rf /var/lib/apt/lists/* 68 | 69 | # Set timezone 70 | ENV TZ=Asia/Taipei 71 | RUN ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && \ 72 | echo $TZ > /etc/timezone 73 | RUN dpkg-reconfigure -f noninteractive tzdata 74 | 75 | # Set locale 76 | ENV LANG C.UTF-8 77 | ENV LC_ALL C.UTF-8 78 | 79 | # Download Qt5.12.0 80 | RUN cd /opt && \ 81 | wget https://download.qt.io/archive/qt/5.12/5.12.0/single/qt-everywhere-src-5.12.0.tar.xz && \ 82 | tar Jxvf qt-everywhere-src-5.12.0.tar.xz && \ 83 | rm -rf qt-everywhere-src-5.12.0.tar.xz && \ 84 | cd /opt/qt-everywhere-src-5.12.0 &&\ 85 | ./configure -release -opensource -confirm-license -prefix /usr/local/qt5 \ 86 | -nomake examples -nomake tests -no-pch -no-icu -no-cups -no-linuxfb -no-directfb \ 87 | -skip qtgamepad -skip qtdoc -skip qtlocation -skip qtx11extras -skip qtxmlpatterns && \ 88 | make -j$(nproc) && \ 89 | make install 90 | 91 | RUN rm -rf /opt/qt-everywhere-src-5.12.0 92 | 93 | # Add the binaries (qmake and others) to the path 94 | ENV PATH="/usr/local/qt5/bin:$PATH" 95 | # Add the libraries to the library path 96 | ENV LD_LIBRARY_PATH="/usr/local/qt5/lib:$LD_LIBRARY_PATH" 97 | 98 | # Install python3.9 with miniconda 99 | ENV PYTHON_VERSION="3.8" 100 | ENV CONDA_PATH="/opt/conda" 101 | # PLATFORM=$(uname -m) && echo "export PLATFORM=${PLATFORM}" >> ~/.bashrc 102 | ENV PLATFORM="x86_64" 103 | RUN DOWNLOAD_PATH="https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-${PLATFORM}.sh" && \ 104 | wget ${DOWNLOAD_PATH} && \ 105 | sudo sh Miniconda3-latest-Linux-${PLATFORM}.sh -b -p ${CONDA_PATH} && \ 106 | # sudo ${CONDA_PATH}/bin/conda update -n base conda && \ 107 | sudo ${CONDA_PATH}/bin/conda install python=${PYTHON_VERSION} && \ 108 | sudo ${CONDA_PATH}/bin/conda clean -y -a && \ 109 | rm -rf Miniconda3-latest-Linux-${PLATFORM}.sh && \ 110 | rm -rf /temp/* 111 | ENV PATH=${CONDA_PATH}/bin:$PATH 112 | 113 | # Install PyQt5.12.0 114 | RUN pip install --upgrade pip 115 | RUN pip install pyqt5==5.12.0 116 | 117 | # Would cause issue: 118 | # Cannot mix incompatible Qt library (version 0x50c00) with this library (version 0x50c01) 119 | # 120 | # Qt path 121 | # ENV PATH="/usr/local/qt5/bin:$PATH" 122 | # ENV LD_LIBRARY_PATH="/usr/local/qt5/lib:$LD_LIBRARY_PATH" 123 | # So we need to unset `LD_LIBRARY_PATH` 124 | ENV LD_LIBRARY_PATH="" 125 | 126 | # libQt5MultimediaQuick.so.5: cannot open shared object file: No such file or directory 127 | # ref: https://stackoverflow.com/questions/61426174/libqt5multimediaquick-so-5-cannot-open-shared-object-file-no-such-file-or-dire 128 | # RUN sudo cp /usr/local/qt5/lib/libQt5MultimediaQuick.so.5 \ 129 | # /home/user/.local/lib/python${PYTHON_VERSION}/site-packages/PyQt5/Qt5/lib/ 130 | 131 | # nvidia-container-runtime 132 | ## can run OpenGL in Container 133 | ## - compute: Enables CUDA compute capabilities. 134 | ## - utility: Enables NVIDIA driver utility capabilities, like nvidia-smi. 135 | ## - graphics: Allows access to the GPU's graphics capabilities. 136 | ## - video: Enables video processing features. 137 | ## - display: Provides access to GPU display features. 138 | ENV NVIDIA_VISIBLE_DEVICES \ 139 | ${NVIDIA_VISIBLE_DEVICES:-all} 140 | ENV NVIDIA_DRIVER_CAPABILITIES \ 141 | ${NVIDIA_DRIVER_CAPABILITIES:+$NVIDIA_DRIVER_CAPABILITIES,}graphics 142 | 143 | # OpenGL 144 | ## This fix: libGL error: No matching fbConfigs or visuals found 145 | ENV LIBGL_ALWAYS_INDIRECT 1 146 | ## To fix: QGLXContext: Failed to create dummy context 147 | #ENV QT_QUICK_BACKEND software 148 | -------------------------------------------------------------------------------- /docker/5.12.0/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CONTAINER_REGISTRY="kakalin" 4 | CONTAINER_REPOSITORY="qt" 5 | CONTAINER_TAG="5.12.0" 6 | 7 | docker build --rm -t $CONTAINER_REGISTRY/$CONTAINER_REPOSITORY:$CONTAINER_TAG . 8 | -------------------------------------------------------------------------------- /docker/5.12.0/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CONTAINER_REGISTRY="kakalin" 4 | CONTAINER_REPOSITORY="qt" 5 | CONTAINER_TAG="5.12.0" 6 | 7 | docker build --rm -t $CONTAINER_REGISTRY/$CONTAINER_REPOSITORY:$CONTAINER_TAG . 8 | docker push $CONTAINER_REGISTRY/$CONTAINER_REPOSITORY:$CONTAINER_TAG 9 | -------------------------------------------------------------------------------- /docker/5.12.0/gpu.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu18.04 2 | ENV CUDA_VERSION 11.8 3 | ENV CUDNN_VERSION 8.6.0.163 4 | LABEL com.nvidia.cudnn.version="${CUDNN_VERSION}" 5 | LABEL maintainer="kaka " 6 | 7 | # Ubuntu 18.04: tzdata issue 8 | # set noninteractive installation 9 | RUN apt-get update && \ 10 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 11 | tzdata locales\ 12 | git wget curl\ 13 | build-essential cmake ninja-build g++ \ 14 | sudo apt-utils \ 15 | pkg-config \ 16 | perl \ 17 | python-dev python3-dev \ 18 | python-pip python3-pip \ 19 | libdbus-1-3 \ 20 | # Libxcb 21 | '^libxcb.*-dev'\ 22 | libx11-xcb-dev \ 23 | libglu1-mesa-dev \ 24 | libxrender-dev \ 25 | libxi-dev \ 26 | libxkbcommon-dev \ 27 | libxkbcommon-x11-dev \ 28 | # Qt WebKit\ 29 | ruby bison flex gperf \ 30 | libicu-dev libxslt-dev \ 31 | # Qt WebEngine 32 | libfontconfig1 libfontconfig1-dev libudev-dev \ 33 | # Qt Multimedia 34 | libasound2-dev \ 35 | libgstreamer1.0-dev \ 36 | libgstreamer-plugins-base1.0-dev \ 37 | libgstreamer-plugins-good1.0-dev \ 38 | libgstreamer-plugins-bad1.0-dev \ 39 | mesa-common-dev libgl1-mesa-dev 40 | 41 | # GStreamer support 42 | RUN apt-get install -y --no-install-recommends \ 43 | libgstreamer1.0-0 \ 44 | gstreamer1.0-libav \ 45 | gstreamer1.0-plugins-bad \ 46 | gstreamer1.0-plugins-base \ 47 | gstreamer1.0-plugins-good \ 48 | gstreamer1.0-plugins-ugly \ 49 | gstreamer1.0-libav \ 50 | gstreamer1.0-doc \ 51 | gstreamer1.0-tools \ 52 | libpulse-mainloop-glib0 \ 53 | alsa-base \ 54 | alsa-utils \ 55 | pulseaudio 56 | 57 | # OpenGL support: GLUT / GLFW / GLEW support 58 | # mesa-utils: provides glxgears and glxinfo 59 | # x11-apps: provides xeyes 60 | RUN apt-get install -y --no-install-recommends \ 61 | x11-apps \ 62 | mesa-utils \ 63 | freeglut3-dev \ 64 | libglfw3-dev \ 65 | libglew-dev 66 | 67 | RUN apt -y autoremove && \ 68 | apt -y autoclean && \ 69 | apt -y clean && \ 70 | rm -rf /var/lib/apt/lists/* 71 | 72 | # Set timezone 73 | ENV TZ=Asia/Taipei 74 | RUN ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && \ 75 | echo $TZ > /etc/timezone 76 | RUN dpkg-reconfigure -f noninteractive tzdata 77 | 78 | # Set locale 79 | ENV LANG C.UTF-8 80 | ENV LC_ALL C.UTF-8 81 | 82 | # Download Qt5.12.0 83 | RUN cd /opt && \ 84 | wget https://download.qt.io/archive/qt/5.12/5.12.0/single/qt-everywhere-src-5.12.0.tar.xz && \ 85 | tar Jxvf qt-everywhere-src-5.12.0.tar.xz && \ 86 | rm -rf qt-everywhere-src-5.12.0.tar.xz && \ 87 | cd /opt/qt-everywhere-src-5.12.0 &&\ 88 | ./configure -release -opensource -confirm-license -prefix /usr/local/qt5 \ 89 | -nomake examples -nomake tests -no-pch -no-icu -no-cups -no-linuxfb -no-directfb \ 90 | -skip qtgamepad -skip qtdoc -skip qtlocation -skip qtx11extras -skip qtxmlpatterns && \ 91 | make -j$(nproc) && \ 92 | make install 93 | 94 | RUN rm -rf /opt/qt-everywhere-src-5.12.0 95 | 96 | # Add the binaries (qmake and others) to the path 97 | ENV PATH="/usr/local/qt5/bin:$PATH" 98 | # Add the libraries to the library path 99 | ENV LD_LIBRARY_PATH="/usr/local/qt5/lib:$LD_LIBRARY_PATH" 100 | 101 | # Install python3.9 with miniconda 102 | ENV PYTHON_VERSION="3.8" 103 | ENV CONDA_PATH="/opt/conda" 104 | # PLATFORM=$(uname -m) && echo "export PLATFORM=${PLATFORM}" >> ~/.bashrc 105 | ENV PLATFORM="x86_64" 106 | RUN DOWNLOAD_PATH="https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-${PLATFORM}.sh" && \ 107 | wget ${DOWNLOAD_PATH} && \ 108 | sudo sh Miniconda3-latest-Linux-${PLATFORM}.sh -b -p ${CONDA_PATH} && \ 109 | # sudo ${CONDA_PATH}/bin/conda update -n base conda && \ 110 | sudo ${CONDA_PATH}/bin/conda install python=${PYTHON_VERSION} && \ 111 | sudo ${CONDA_PATH}/bin/conda clean -y -a && \ 112 | rm -rf Miniconda3-latest-Linux-${PLATFORM}.sh && \ 113 | rm -rf /temp/* 114 | ENV PATH=${CONDA_PATH}/bin:$PATH 115 | 116 | # Install PyQt5.12.0 117 | RUN pip install --upgrade pip 118 | RUN pip install pyqt5==5.12.0 119 | 120 | # Would cause issue: 121 | # Cannot mix incompatible Qt library (version 0x50c00) with this library (version 0x50c01) 122 | # 123 | # Qt path 124 | # ENV PATH="/usr/local/qt5/bin:$PATH" 125 | # ENV LD_LIBRARY_PATH="/usr/local/qt5/lib:$LD_LIBRARY_PATH" 126 | # So we need to unset `LD_LIBRARY_PATH` 127 | ENV LD_LIBRARY_PATH="" 128 | 129 | # libQt5MultimediaQuick.so.5: cannot open shared object file: No such file or directory 130 | # ref: https://stackoverflow.com/questions/61426174/libqt5multimediaquick-so-5-cannot-open-shared-object-file-no-such-file-or-dire 131 | # RUN sudo cp /usr/local/qt5/lib/libQt5MultimediaQuick.so.5 \ 132 | # /home/user/.local/lib/python${PYTHON_VERSION}/site-packages/PyQt5/Qt5/lib/ 133 | 134 | # nvidia-container-runtime 135 | ## can run OpenGL in Container 136 | ## - compute: Enables CUDA compute capabilities. 137 | ## - utility: Enables NVIDIA driver utility capabilities, like nvidia-smi. 138 | ## - graphics: Allows access to the GPU's graphics capabilities. 139 | ## - video: Enables video processing features. 140 | ## - display: Provides access to GPU display features. 141 | ENV NVIDIA_VISIBLE_DEVICES \ 142 | ${NVIDIA_VISIBLE_DEVICES:-all} 143 | ENV NVIDIA_DRIVER_CAPABILITIES \ 144 | ${NVIDIA_DRIVER_CAPABILITIES:+$NVIDIA_DRIVER_CAPABILITIES,}compute,utility,graphics,video,display 145 | 146 | 147 | # OpenGL 148 | ## This fix: libGL error: No matching fbConfigs or visuals found 149 | ENV LIBGL_ALWAYS_INDIRECT 1 150 | ## To fix: QGLXContext: Failed to create dummy context 151 | #ENV QT_QUICK_BACKEND software 152 | -------------------------------------------------------------------------------- /docker/5.15.10/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | LABEL maintainer="kaka " 3 | 4 | # Ubuntu 18.04: tzdata issue 5 | # set noninteractive installation 6 | RUN apt-get update && \ 7 | DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 8 | tzdata locales\ 9 | git wget curl\ 10 | build-essential cmake ninja-build g++ \ 11 | sudo apt-utils \ 12 | pkg-config \ 13 | perl \ 14 | python-dev python3-dev \ 15 | python-pip python3-pip \ 16 | libdbus-1-3 \ 17 | # Libxcb 18 | '^libxcb.*-dev'\ 19 | libx11-xcb-dev \ 20 | libglu1-mesa-dev \ 21 | libxrender-dev \ 22 | libxi-dev \ 23 | libxkbcommon-dev \ 24 | libxkbcommon-x11-dev \ 25 | # Qt WebKit\ 26 | ruby bison flex gperf \ 27 | libicu-dev libxslt-dev \ 28 | # Qt WebEngine 29 | libfontconfig1 libfontconfig1-dev libudev-dev \ 30 | # Qt Multimedia 31 | libasound2-dev \ 32 | libgstreamer1.0-dev \ 33 | libgstreamer-plugins-base1.0-dev \ 34 | libgstreamer-plugins-good1.0-dev \ 35 | libgstreamer-plugins-bad1.0-dev \ 36 | mesa-common-dev libgl1-mesa-dev 37 | 38 | # GStreamer support 39 | RUN apt-get install -y --no-install-recommends \ 40 | libgstreamer1.0-0 \ 41 | gstreamer1.0-libav \ 42 | gstreamer1.0-plugins-bad \ 43 | gstreamer1.0-plugins-base \ 44 | gstreamer1.0-plugins-good \ 45 | gstreamer1.0-plugins-ugly \ 46 | gstreamer1.0-libav \ 47 | gstreamer1.0-doc \ 48 | gstreamer1.0-tools \ 49 | libpulse-mainloop-glib0 \ 50 | alsa-base \ 51 | alsa-utils \ 52 | pulseaudio 53 | 54 | # OpenGL support: GLUT / GLFW / GLEW support 55 | # mesa-utils: provides glxgears and glxinfo 56 | # x11-apps: provides xeyes 57 | RUN apt-get install -y --no-install-recommends \ 58 | x11-apps \ 59 | mesa-utils \ 60 | freeglut3-dev \ 61 | libglfw3-dev \ 62 | libglew-dev 63 | 64 | # For fix error: ERROR at //build/config/linux/pkg_config.gni:103:17: Script returned non-zero exit code. 65 | RUN apt-get install -y \ 66 | libnss3 libnss3-dev libdbus-1-dev \ 67 | # For WebEngine (Qt 5.15 need) 68 | llvm libclang-dev nodejs \ 69 | # For D-Bus or AT-SPI for X11 Accessibility Bridge 70 | dbus at-spi2-core 71 | 72 | RUN apt -y autoremove && \ 73 | apt -y autoclean && \ 74 | apt -y clean && \ 75 | rm -rf /var/lib/apt/lists/* 76 | 77 | # Set timezone 78 | ENV TZ=Asia/Taipei 79 | RUN ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && \ 80 | echo $TZ > /etc/timezone 81 | RUN dpkg-reconfigure -f noninteractive tzdata 82 | 83 | # Set locale 84 | ENV LANG C.UTF-8 85 | ENV LC_ALL C.UTF-8 86 | 87 | # Download Qt 5.15.10 88 | 89 | ENV QT_VERSION="5.15.10" 90 | RUN cd /opt && \ 91 | wget https://download.qt.io/archive/qt/5.15/5.15.10/single/qt-everywhere-opensource-src-5.15.10.tar.xz && \ 92 | tar Jxvf qt-everywhere-opensource-src-5.15.10.tar.xz && \ 93 | rm -rf qt-everywhere-opensource-src-5.15.10.tar.xz && \ 94 | cd /opt/qt-everywhere-src-5.15.10 && \ 95 | ./configure -release -opensource -confirm-license -prefix /usr/local/qt5 \ 96 | -nomake examples -nomake tests -no-pch -no-icu -no-cups -no-linuxfb -no-directfb \ 97 | -skip qtgamepad -skip qtdoc -skip qtlocation -skip qtx11extras -skip qtxmlpatterns && \ 98 | make -j$(nproc) && \ 99 | make install 100 | 101 | RUN rm -rf /opt/qt-everywhere-src-5.15.10 102 | 103 | # Add the binaries (qmake and others) to the path 104 | ENV PATH="/usr/local/qt5/bin:$PATH" 105 | # Add the libraries to the library path 106 | ENV LD_LIBRARY_PATH="/usr/local/qt5/lib:$LD_LIBRARY_PATH" 107 | 108 | # Install python3.10 with miniconda 109 | ENV PYTHON_VERSION="3.10" 110 | ENV CONDA_PATH="/opt/conda" 111 | # PLATFORM=$(uname -m) && echo "export PLATFORM=${PLATFORM}" >> ~/.bashrc 112 | ENV PLATFORM="x86_64" 113 | RUN DOWNLOAD_PATH="https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-${PLATFORM}.sh" && \ 114 | wget ${DOWNLOAD_PATH} && \ 115 | sudo sh Miniconda3-latest-Linux-${PLATFORM}.sh -b -p ${CONDA_PATH} && \ 116 | sudo ${CONDA_PATH}/bin/conda update -n base conda && \ 117 | sudo ${CONDA_PATH}/bin/conda install python=${PYTHON_VERSION} && \ 118 | sudo ${CONDA_PATH}/bin/conda clean -y -a && \ 119 | # init conda for bash and reload the environment 120 | # Because you nedd run 'conda init' before 'conda activate' in the Dockerfile 121 | # 122 | # Enable conda for the for all users (conda initialize) 123 | ln -s ${CONDA_PATH}/etc/profile.d/conda.sh /etc/profile.d/conda.sh && \ 124 | # Enable conda for the current user (conda initialize) 125 | echo ". /opt/conda/etc/profile.d/conda.sh" >> ~/.bashrc && \ 126 | echo "conda activate py310" >> ~/.bashrc && \ 127 | rm -rf Miniconda3-latest-Linux-${PLATFORM}.sh && \ 128 | rm -rf /temp/* 129 | 130 | ENV PATH=${CONDA_PATH}/bin:$PATH 131 | 132 | # Make RUN commands use `bash --login`: 133 | SHELL [ "bin/bash","--login","-c" ] 134 | 135 | # Install PyQt5 and PySide2 136 | # Create env py310 for fix error: 137 | # ImportError: /usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2: symbol krb5_ser_context_init version krb5_3_MIT not defined in file libkrb5.so.3 with link time reference 138 | RUN conda create -n py310 python=${PYTHON_VERSION} 139 | RUN conda activate py310 && \ 140 | pip install --upgrade pip && \ 141 | pip install pyqt5==5.15.10 pyside2==5.15.2.1 PyOpenGL 142 | 143 | # Would cause issue: 144 | # Cannot mix incompatible Qt library (version 0x50c00) with this library (version 0x50c01) 145 | # 146 | # Qt path 147 | # ENV PATH="/usr/local/qt5/bin:$PATH" 148 | # ENV LD_LIBRARY_PATH="/usr/local/qt5/lib:$LD_LIBRARY_PATH" 149 | # So we need to unset `LD_LIBRARY_PATH` 150 | ENV LD_LIBRARY_PATH="" 151 | 152 | # libQt5MultimediaQuick.so.5: cannot open shared object file: No such file or directory 153 | # ref: https://stackoverflow.com/questions/61426174/libqt5multimediaquick-so-5-cannot-open-shared-object-file-no-such-file-or-dire 154 | # RUN sudo cp /usr/local/qt5/lib/libQt5MultimediaQuick.so.5 \ 155 | # /home/user/.local/lib/python${PYTHON_VERSION}/site-packages/PyQt5/Qt5/lib/ 156 | 157 | # nvidia-container-runtime 158 | ## can run OpenGL in Container 159 | ## - compute: Enables CUDA compute capabilities. 160 | ## - utility: Enables NVIDIA driver utility capabilities, like nvidia-smi. 161 | ## - graphics: Allows access to the GPU's graphics capabilities. 162 | ## - video: Enables video processing features. 163 | ## - display: Provides access to GPU display features. 164 | ENV NVIDIA_VISIBLE_DEVICES \ 165 | ${NVIDIA_VISIBLE_DEVICES:-all} 166 | ENV NVIDIA_DRIVER_CAPABILITIES \ 167 | ${NVIDIA_DRIVER_CAPABILITIES:+$NVIDIA_DRIVER_CAPABILITIES,}graphics 168 | 169 | # OpenGL 170 | ## This fix: libGL error: No matching fbConfigs or visuals found 171 | ENV LIBGL_ALWAYS_INDIRECT 1 172 | ## To fix: QGLXContext: Failed to create dummy context 173 | #ENV QT_QUICK_BACKEND software 174 | -------------------------------------------------------------------------------- /docker/5.15.10/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CONTAINER_REGISTRY="kakalin" 4 | CONTAINER_REPOSITORY="qt" 5 | CONTAINER_TAG="5.15.10" 6 | PLATFORM="linux/amd64" 7 | ARCH="" 8 | 9 | # if [[ "$OSTYPE" == "linux-gnu"* ]]; then 10 | # PLATFORM="linux/amd64" 11 | # ARCH="amd64" 12 | # elif [[ "$OSTYPE" == "darwin"* ]]; then 13 | # if [[ $(uname -m) == "arm64" ]]; then 14 | # PLATFORM="linux/arm64" 15 | # ARCH="arm64" 16 | # elif [[ $(uname -m) == "x86_64" ]]; then 17 | # PLATFORM="linux/amd64" 18 | # ARCH="amd64" 19 | # fi 20 | # else 21 | # echo "Haven't supported this OS Type, please check command\n and update it." 22 | # fi 23 | 24 | echo "Platform: $PLATFORM" 25 | docker build --rm \ 26 | --platform $PLATFORM \ 27 | -t $CONTAINER_REGISTRY/$CONTAINER_REPOSITORY:$CONTAINER_TAG . 28 | -------------------------------------------------------------------------------- /docker/5.15.10/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CONTAINER_REGISTRY="kakalin" 4 | CONTAINER_REPOSITORY="qt" 5 | CONTAINER_TAG="5.15.10" 6 | 7 | docker build --rm -t $CONTAINER_REGISTRY/$CONTAINER_REPOSITORY:$CONTAINER_TAG . 8 | docker push $CONTAINER_REGISTRY/$CONTAINER_REPOSITORY:$CONTAINER_TAG 9 | -------------------------------------------------------------------------------- /docker/Guide.md: -------------------------------------------------------------------------------- 1 | # The GUIDE of Qt Container Image 2 | 3 | This file is about all the issues of running qt environments of docker. 4 | 5 | > Running the GUI application in a Docker container: 6 | > - on the local machine 7 | > - on a remote machine via SSH. 8 | 9 | ## Guides 10 | 11 | We divided into the following parts. 12 | 13 | - Host: macOS, container: macOS (local machine) 14 | - Host: macOS, conrainer: Linux without GPU (remote) 15 | - Host: macOS, conrainer: Linux with GPU (remote) 16 | - Host: Linux without GPU, container: Linux without GPU 17 | - Host: Linux without GPU, container: Linux with GPU 18 | - Host: Linux with GPU, container: Linux without GPU 19 | - Host: Linux with GPU, container: Linux with GPU 20 | 21 | ### 1. xhost 開啟權限 22 | 23 | - container on local machine 24 | 25 | - macOS: `xhost +localhost` 26 | - Linux: `xhost +local:docker` 27 | 28 | - container on remote machine: `xhost +` 29 | 30 | ### 2. X11 Authentication 31 | 32 | ![](images/mac_gui_docker_ssh.png) 33 | 34 | 使用 X11 Forwarding 時,會隨機產生一個授權的 cookie,存放在 SSH Server (就是 remote machine / X11 Client, 如上圖所示) 的 `~/.Xauthority` 文件夾中。這個 cookie 會在每次 X11 Clinet 發送數據時用到。 35 | 36 | 當我們使用了 `--network=host` 參數後,容器中的 X11 Clinet (X Application) 將直接通過 `TCP/IP` 與外部通訊。然而容器內並沒有這個授權文件,因此我們需要將它 mount 進容器內,如下: 37 | 38 | ``` 39 | -v $HOME/.Xauthority:$HOME/.Xauthority 40 | ``` 41 | 42 | ### 3. OpenGL issues related (尚待解決) 43 | 44 | #### Host: macOS 45 | 46 | 只要 Host machine 為 macOS 且 container image 跑在 Ubuntu server 上,不管 server 是否有 GPU, 都需使用 `QT_QUICK_BACKEND=software`,否則會發生 error: `Buffer creation failed`, 如下: 47 | 48 | ```sh 49 | Buffer creation failed 50 | QOpenGLShaderProgram: could not create shader program 51 | QOpenGLShader: could not create shader 52 | QOpenGLShader: could not create shader 53 | shader compilation failed: 54 | "" 55 | QOpenGLShaderProgram::uniformLocation(matrix): shader program is not linked 56 | QOpenGLShaderProgram::uniformLocation(opacity): shader program is not linked 57 | QOpenGLShaderProgram: could not create shader program 58 | QOpenGLShader: could not create shader 59 | QOpenGLShader: could not create shader 60 | shader compilation failed: 61 | "" 62 | ... 63 | qt.qpa.xcb: QXcbConnection: XCB error: 167 (Unknown), sequence: 484, resource id: 0, major code: 149 (Unknown), minor code: 1 64 | qt.glx: qglx_findConfig: Failed to finding matching FBConfig (8 8 8 8) 65 | qt.glx: qglx_findConfig: Failed to finding matching FBConfig (1 8 8 8) 66 | qt.glx: qglx_findConfig: Failed to finding matching FBConfig (1 1 8 8) 67 | qt.glx: qglx_findConfig: Failed to finding matching FBConfig (1 1 1 8) 68 | qt.glx: qglx_findConfig: Failed to finding matching FBConfig (1 1 1 8) 69 | qt.glx: qglx_findConfig: Failed to finding matching FBConfig (1 1 1 8) 70 | qt.glx: qglx_findConfig: Failed to finding matching FBConfig (1 1 1 8) 71 | qt.glx: qglx_findConfig: Failed to finding matching FBConfig (1 1 1 8) 72 | ``` 73 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Qt/QML Container Image 2 | 3 | - Qt/QML: 5.12.0 4 | - Support PyQt5 5 | 6 | ## Build the image (Optional) 7 | 8 | ```bash 9 | # $ docker build --rm -t kakalin/qt:5.12.0 . 10 | $ ./build.sh 11 | ``` 12 | 13 | ### Push to Docker Hub 14 | 15 | ```bash 16 | $ docker login 17 | # $ docker push kakalin/qt:5.12.0 18 | $ ./deploy.sh 19 | ``` 20 | 21 | ## Run 22 | 23 | ### 1. Run it on `Ubuntu` 24 | 25 | ```bash 26 | # Expose the X server on the host 27 | $ xhost +local:docker 28 | ``` 29 | 30 | #### Note: 31 | 32 | ``` 33 | Notice current path, if you use `$PWD`, 34 | you should in the root path of the project, 35 | ``` 36 | --- 37 | 38 | - GPU 39 | 40 | ```bash 41 | $ docker run --rm -it \ 42 | --gpus all \ 43 | -e DISPLAY=$DISPLAY \ 44 | -e QT_X11_NO_MITSHM=1 \ 45 | --volume="/tmp/.X11-unix:/tmp/.X11-unix" \ 46 | --volume="$PWD:/home/user/qt-template" \ 47 | --privileged \ 48 | kakalin/qt:5.12.0 49 | ``` 50 | 51 | - CPU 52 | 53 | ```bash 54 | $ docker run --rm -it \ 55 | -e DISPLAY=$DISPLAY \ 56 | -e QT_X11_NO_MITSHM=1 \ 57 | --volume="/tmp/.X11-unix:/tmp/.X11-unix" \ 58 | --volume="$PWD:/home/user/qt-template" \ 59 | --privileged \ 60 | kakalin/qt:5.12.0 61 | ``` 62 | 63 | ### 2. Run it on `macOS` 64 | 65 | Reference: [X11 in docker on macOS](https://gist.github.com/cschiewek/246a244ba23da8b9f0e7b11a68bf3285) 66 | 67 | 1. Install [XQuartz](https://dl.bintray.com/xquartz/downloads/XQuartz-2.7.11.dmg) from official website or through [Homebrew](https://brew.sh/) 68 | 69 | ```bash 70 | $ brew install --cask xquartz 71 | ``` 72 | 73 | and then `Restart OS`. 74 | 75 | 2. Open XQuartz and Check the option: `XQuartz -> Preferences -> Security -> "Allow connections from network clients"` 76 | 77 | ```bash 78 | # open XQuartz 79 | $ open -a XQuartz 80 | ``` 81 | 82 | 3. In terminal: 83 | 84 | ```bash 85 | # Expose the X server on the host 86 | $ xhost +localhost 87 | ``` 88 | 89 | Creating a bridge between a network socket with a TCP listener on port `6000` (the default port of the X window system) and the X window server on my OS X host. 90 | 91 | ``` 92 | $ socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" 93 | ``` 94 | 95 | ```bash 96 | $ docker run --rm -it \ 97 | -e DISPLAY=host.docker.internal:0 \ 98 | -e QT_X11_NO_MITSHM=1 \ 99 | -e QT_QUICK_BACKEND=software \ 100 | --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \ 101 | --volume="$PWD:/home/user/qt-template" \ 102 | kakalin/qt:5.12.0 103 | ``` 104 | 105 | ## Troubleshooting 106 | 107 | 1. `libGL error: No matching fbConfigs or visuals found` 108 | 109 | ```bash 110 | $ export LIBGL_ALWAYS_INDIRECT=1 111 | ``` 112 | 113 | 2. `QGLXContext: Failed to create dummy context` 114 | 115 | Workaround: 116 | 117 | ```bash 118 | $ export QT_QUICK_BACKEND=software 119 | ``` 120 | 121 | 3. Miniconda install issue: 122 | - Install Python3.8, `ModuleNotFoundError: No module named 'conda.cli.main_info'` 123 | 124 | ``` 125 | comment `conda update -n base conda` 126 | ``` 127 | 128 | 2022/12/6 update: If the old command has error, we can replace it as below: 129 | 130 | ```dockerfile 131 | # Install miniconda for python 3.8 132 | ENV PYTHON_VERSION="3.8" 133 | ENV CONDA_PATH="/usr/local/anaconda3" 134 | ENV MINICONDA_FILE="Miniconda3-py38_4.12.0-Linux-x86_64.sh" 135 | RUN wget https://repo.continuum.io/miniconda/${MINICONDA_FILE} && \ 136 | sh ${MINICONDA_FILE} -b -p ${CONDA_PATH} && \ 137 | ${CONDA_PATH}/bin/conda update -n base conda && \ 138 | ${CONDA_PATH}/bin/conda install python=${PYTHON_VERSION} && \ 139 | ${CONDA_PATH}/bin/conda clean -y -a && \ 140 | rm -rf ${MINICONDA_FILE} && \ 141 | rm -rf /temp/* 142 | ``` 143 | -------------------------------------------------------------------------------- /docker/images/mac_gui_docker_ssh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/docker/images/mac_gui_docker_ssh.png -------------------------------------------------------------------------------- /images/qml-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/images/qml-template.png -------------------------------------------------------------------------------- /images/qt-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/images/qt-template.png -------------------------------------------------------------------------------- /python/pyqt5/qml/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.qml] 16 | indent_size = 4 17 | 18 | [*.js] 19 | indent_size = 2 20 | 21 | # Tab indentation (no size specified) 22 | [Makefile] 23 | indent_style = tab 24 | -------------------------------------------------------------------------------- /python/pyqt5/qml/.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Mac 5 | .DS_Store 6 | ._.DS_Store 7 | 8 | # pyenv 9 | venv/ 10 | .venv/ 11 | 12 | # python 13 | *.pyc 14 | *.pro 15 | *~ 16 | __pycache__ 17 | 18 | # QML 19 | *.qmlc 20 | -------------------------------------------------------------------------------- /python/pyqt5/qml/README.md: -------------------------------------------------------------------------------- 1 | # QML Template 2 | 3 | ## Require 4 | 5 | - Python3+ 6 | - PyQt5+ 7 | 8 | ## Usage 9 | 10 | ```bash 11 | python3 qml_template.py 12 | ``` 13 | 14 | ## Packaging 15 | 16 | if you want to packing Python programs into ```stand-alone executables``` 17 | 18 | 1. Converting ```*.qrc``` (a collection of resource) files into ```*.py``` (Python source) file 19 | 20 | ```bash 21 | $ pyrcc5 -o src/qml.py src/resources/qml.qrc 22 | 23 | $ pyrcc5 -o src/components.py src/resources/components/components.qrc 24 | ``` 25 | 26 | 2. Testing 27 | 28 | Add ```prod``` argument to run freeze mode 29 | 30 | ```bash 31 | python3 qml_template.py prod 32 | ``` 33 | -------------------------------------------------------------------------------- /python/pyqt5/qml/qml_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import sys 5 | 6 | mode = '' 7 | # use: python3 mtool.py prod 8 | if len(sys.argv) == 2: 9 | mode = sys.argv[1] 10 | 11 | if hasattr(sys, "frozen"): 12 | # running in a bundle 13 | dir_name = os.path.dirname(os.path.abspath(sys.executable)) 14 | mode = 'prod' 15 | else: 16 | # running live 17 | dir_name = os.path.dirname(os.path.abspath(__file__)) 18 | 19 | 20 | from PyQt5.QtGui import QGuiApplication 21 | from src import main 22 | app = QGuiApplication(sys.argv) 23 | main.run(app, dir_name, mode) 24 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyqt5/qml/src/__init__.py -------------------------------------------------------------------------------- /python/pyqt5/qml/src/components.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Resource object code 4 | # 5 | # Created by: The Resource Compiler for PyQt5 (Qt v5.11.1) 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore 10 | 11 | qt_resource_data = b"\ 12 | \x00\x00\x05\x2e\ 13 | \x69\ 14 | \x6d\x70\x6f\x72\x74\x20\x51\x74\x51\x75\x69\x63\x6b\x20\x32\x2e\ 15 | \x38\x0a\x69\x6d\x70\x6f\x72\x74\x20\x51\x74\x51\x75\x69\x63\x6b\ 16 | \x2e\x4c\x61\x79\x6f\x75\x74\x73\x20\x31\x2e\x33\x0a\x0a\x52\x65\ 17 | \x63\x74\x61\x6e\x67\x6c\x65\x20\x7b\x0a\x20\x20\x20\x20\x69\x64\ 18 | \x3a\x20\x72\x6f\x6f\x74\x0a\x20\x20\x20\x20\x61\x6e\x63\x68\x6f\ 19 | \x72\x73\x2e\x66\x69\x6c\x6c\x3a\x20\x70\x61\x72\x65\x6e\x74\x0a\ 20 | \x0a\x20\x20\x20\x20\x70\x72\x6f\x70\x65\x72\x74\x79\x20\x76\x61\ 21 | \x72\x69\x61\x6e\x74\x20\x69\x6e\x74\x65\x72\x6e\x61\x6c\x4d\x6f\ 22 | \x64\x65\x6c\x0a\x20\x20\x20\x20\x73\x69\x67\x6e\x61\x6c\x20\x72\ 23 | \x6f\x77\x43\x68\x61\x6e\x67\x65\x64\x0a\x0a\x20\x20\x20\x20\x6f\ 24 | \x6e\x52\x6f\x77\x43\x68\x61\x6e\x67\x65\x64\x3a\x20\x7b\x0a\x20\ 25 | \x20\x20\x20\x20\x20\x20\x20\x2f\x2f\x6c\x69\x73\x74\x56\x69\x65\ 26 | \x77\x2e\x70\x6f\x73\x69\x74\x69\x6f\x6e\x56\x69\x65\x77\x41\x74\ 27 | \x45\x6e\x64\x28\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x6c\ 28 | \x69\x73\x74\x56\x69\x65\x77\x2e\x70\x6f\x73\x69\x74\x69\x6f\x6e\ 29 | \x56\x69\x65\x77\x41\x74\x49\x6e\x64\x65\x78\x28\x6c\x69\x73\x74\ 30 | \x56\x69\x65\x77\x2e\x63\x6f\x75\x6e\x74\x20\x2d\x20\x31\x2c\x20\ 31 | \x4c\x69\x73\x74\x56\x69\x65\x77\x2e\x42\x65\x67\x69\x6e\x6e\x69\ 32 | \x6e\x67\x29\x0a\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\x20\x20\x4c\ 33 | \x69\x73\x74\x56\x69\x65\x77\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\ 34 | \x20\x20\x69\x64\x3a\x20\x6c\x69\x73\x74\x56\x69\x65\x77\x0a\x20\ 35 | \x20\x20\x20\x20\x20\x20\x20\x61\x6e\x63\x68\x6f\x72\x73\x2e\x66\ 36 | \x69\x6c\x6c\x3a\x20\x70\x61\x72\x65\x6e\x74\x0a\x20\x20\x20\x20\ 37 | \x20\x20\x20\x20\x63\x6f\x6e\x74\x65\x6e\x74\x57\x69\x64\x74\x68\ 38 | \x3a\x20\x33\x32\x30\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x63\x6c\ 39 | \x69\x70\x3a\x20\x74\x72\x75\x65\x0a\x0a\x20\x20\x20\x20\x20\x20\ 40 | \x20\x20\x6d\x6f\x64\x65\x6c\x3a\x20\x70\x61\x72\x65\x6e\x74\x2e\ 41 | \x69\x6e\x74\x65\x72\x6e\x61\x6c\x4d\x6f\x64\x65\x6c\x0a\x0a\x20\ 42 | \x20\x20\x20\x20\x20\x20\x20\x64\x65\x6c\x65\x67\x61\x74\x65\x3a\ 43 | \x20\x6c\x69\x73\x74\x44\x65\x6c\x65\x67\x61\x74\x65\x0a\x20\x20\ 44 | \x20\x20\x7d\x0a\x0a\x20\x20\x20\x20\x43\x6f\x6d\x70\x6f\x6e\x65\ 45 | \x6e\x74\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3a\ 46 | \x20\x6c\x69\x73\x74\x44\x65\x6c\x65\x67\x61\x74\x65\x0a\x0a\x20\ 47 | \x20\x20\x20\x20\x20\x20\x20\x52\x65\x63\x74\x61\x6e\x67\x6c\x65\ 48 | \x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x77\ 49 | \x69\x64\x74\x68\x3a\x20\x72\x6f\x6f\x74\x2e\x77\x69\x64\x74\x68\ 50 | \x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\ 51 | \x67\x68\x74\x3a\x20\x34\x30\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ 52 | \x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x69\x6e\x64\x65\x78\ 53 | \x20\x25\x20\x32\x20\x3d\x3d\x20\x30\x20\x3f\x20\x22\x77\x68\x69\ 54 | \x74\x65\x22\x20\x3a\x20\x22\x6c\x69\x67\x68\x74\x67\x72\x61\x79\ 55 | \x22\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x47\ 56 | \x72\x69\x64\x4c\x61\x79\x6f\x75\x74\x20\x7b\x0a\x20\x20\x20\x20\ 57 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x61\x6e\x63\x68\ 58 | \x6f\x72\x73\x2e\x66\x69\x6c\x6c\x3a\x20\x70\x61\x72\x65\x6e\x74\ 59 | \x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 60 | \x20\x63\x6f\x6c\x75\x6d\x6e\x73\x3a\x20\x32\x0a\x20\x20\x20\x20\ 61 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x63\x6f\x6c\x75\ 62 | \x6d\x6e\x53\x70\x61\x63\x69\x6e\x67\x3a\x20\x28\x70\x61\x72\x65\ 63 | \x6e\x74\x2e\x77\x69\x64\x74\x68\x20\x2d\x20\x69\x74\x65\x6d\x54\ 64 | \x65\x78\x74\x2e\x63\x6f\x6e\x74\x65\x6e\x74\x57\x69\x64\x74\x68\ 65 | \x20\x2d\x20\x64\x61\x74\x61\x54\x65\x78\x74\x2e\x63\x6f\x6e\x74\ 66 | \x65\x6e\x74\x57\x69\x64\x74\x68\x29\x20\x2d\x20\x31\x30\x0a\x0a\ 67 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 68 | \x54\x65\x78\x74\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 69 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3a\x20\x69\ 70 | \x74\x65\x6d\x54\x65\x78\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ 71 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x74\x65\x78\x74\ 72 | \x3a\x20\x6d\x6f\x64\x65\x6c\x2e\x69\x74\x65\x6d\x0a\x20\x20\x20\ 73 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 74 | \x20\x66\x6f\x6e\x74\x2e\x62\x6f\x6c\x64\x3a\x20\x74\x72\x75\x65\ 75 | \x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 76 | \x20\x20\x20\x20\x20\x66\x6f\x6e\x74\x2e\x70\x69\x78\x65\x6c\x53\ 77 | \x69\x7a\x65\x3a\x20\x32\x30\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ 78 | \x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\x20\x20\x20\ 79 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x54\x65\x78\x74\x20\ 80 | \x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 81 | \x20\x20\x20\x20\x20\x20\x69\x64\x3a\x20\x64\x61\x74\x61\x54\x65\ 82 | \x78\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 83 | \x20\x20\x20\x20\x20\x20\x20\x74\x65\x78\x74\x3a\x20\x6d\x6f\x64\ 84 | \x65\x6c\x2e\x64\x61\x74\x61\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ 85 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x66\x6f\x6e\x74\ 86 | \x2e\x70\x69\x78\x65\x6c\x53\x69\x7a\x65\x3a\x20\x32\x30\x0a\x20\ 87 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 88 | \x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x22\x72\x65\x64\x22\x0a\ 89 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 90 | \x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\ 91 | \x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x7d\x0a\ 92 | \x0a\x20\x20\x20\x20\x43\x6f\x6d\x70\x6f\x6e\x65\x6e\x74\x2e\x6f\ 93 | \x6e\x43\x6f\x6d\x70\x6c\x65\x74\x65\x64\x3a\x20\x7b\x0a\x20\x20\ 94 | \x20\x20\x20\x20\x20\x20\x6c\x69\x73\x74\x56\x69\x65\x77\x2e\x70\ 95 | \x6f\x73\x69\x74\x69\x6f\x6e\x56\x69\x65\x77\x41\x74\x45\x6e\x64\ 96 | \x28\x29\x3b\x0a\x20\x20\x20\x20\x7d\x0a\x7d\x0a\x0a\ 97 | \x00\x00\x03\x76\ 98 | \x00\ 99 | \x00\x0e\x0c\x78\x9c\xed\x57\x51\x6f\xda\x30\x10\x7e\xe7\x57\x9c\ 100 | \x98\x26\x85\x95\x02\x45\xea\xd4\x31\xf1\xd0\xb2\x4e\xe2\x61\x6b\ 101 | \xab\x4d\xdb\xb3\x49\x0c\xb1\x08\x36\x73\x1c\x0a\x9a\xf8\xef\x3b\ 102 | \xdb\x89\x21\x89\x53\x5a\x4d\x9a\x36\x69\x41\x2d\xf5\xdd\x77\xe7\ 103 | \xbb\xcf\xe7\xbb\x94\xad\xd6\x42\x2a\x78\x50\x0f\x19\x0b\x97\x30\ 104 | \xec\x0d\x5a\xcc\x89\x56\x09\x0a\x86\xad\xd6\x54\xd1\x15\xfc\x6c\ 105 | \x01\x3e\x2c\x1a\x81\x14\x42\xb5\xcc\xea\x91\x45\x2a\x46\x01\x89\ 106 | \x58\x96\xc2\x1b\x18\x1a\x69\x4c\xd9\x22\x56\x25\xb1\x91\xaf\xa5\ 107 | \x58\x53\xa9\x76\xc0\xb8\xca\x95\x23\xb8\x18\x0c\xca\x4a\x49\x49\ 108 | \x02\x44\x86\x37\x74\xc1\xf8\x08\x06\x70\xf4\xf4\xfb\x90\x2a\x82\ 109 | \xc1\xa1\x1e\x08\x5f\x24\x14\x7d\x41\x44\x17\x92\x52\xbf\x97\x5b\ 110 | \x8e\x01\x5f\x5c\x55\xbd\x50\x1e\x3d\xdf\xc7\xdd\x7c\x9e\x52\x55\ 111 | \x0e\x05\x7d\x48\xa1\x88\x62\x82\x7b\x8c\x56\x8c\x7f\x23\x49\x46\ 112 | \xd1\xc6\xa7\x25\xdb\x5c\xeb\x4f\x7e\x63\x95\x97\x15\x5d\xaa\x24\ 113 | \xe3\x0b\x50\x74\x8b\xb1\xb4\x5f\xb7\xcb\xda\x99\x10\x09\xb0\x34\ 114 | \x77\xac\x64\x46\x3d\xfa\x34\x16\x8f\x37\x24\x5c\x2e\xa4\xc8\x34\ 115 | \x31\x73\x92\xa4\xd4\x24\x43\x60\x9e\x25\x09\x84\x4c\x86\x48\x08\ 116 | \x49\x51\x30\x73\x40\x10\x73\x50\x31\xd5\x5c\x78\xc2\x4d\x18\xa7\ 117 | \xdf\x6d\x21\x0c\x07\x25\x86\x4c\x79\x14\xc6\x1a\xe6\x4d\xc8\xee\ 118 | \x39\x11\x89\x90\x98\xd7\xab\x8b\x28\xbc\xbc\x9a\xb7\xbd\xd0\x43\ 119 | \x48\x0e\x7e\xfb\x56\x7f\xfc\x70\x4d\x95\x03\x7e\x34\x4f\xbb\x52\ 120 | \x8a\x24\x61\x98\xec\x4c\xd7\xda\x35\x67\x2b\x73\xa0\x23\x2c\x8b\ 121 | \xfc\xcf\xeb\xbc\x0e\x7b\x94\x93\x59\x42\x23\x9f\x31\xd6\x52\x83\ 122 | \x29\x16\xdf\x53\x86\xe6\x9c\x7d\xa6\xe6\x10\x9d\x65\xfd\xee\x38\ 123 | \xe0\x87\x4c\xe6\xb6\x43\xac\x24\x03\x3c\xc4\x3c\x89\xb1\xba\x29\ 124 | \x9e\x72\x48\xf8\x86\xa4\x3d\x49\x7f\x64\x34\x55\xf7\x04\x5d\x04\ 125 | \x9d\x03\x18\xa3\x7c\x16\xd4\x44\x75\x02\x69\xa0\x37\x34\x26\x1b\ 126 | \x26\x24\xda\xb8\x8b\x9c\xb7\x8f\xbc\x83\xd4\xf8\x2d\x94\x79\xce\ 127 | \x47\x05\x8c\xcf\xe7\x6c\x35\xa3\xd2\x11\x75\x70\xa5\x9f\xc8\x51\ 128 | \xa0\xdb\x52\xaf\x46\xcd\x31\x96\x92\x14\xcb\xa2\xa7\x76\x6b\xbc\ 129 | \x22\xb7\x76\x31\xe5\x77\x99\x9a\x64\x33\x16\x16\xc8\x7d\xcb\xfe\ 130 | \xf6\xe5\x82\x5c\x35\x67\x82\xca\x7f\x22\x0f\x53\x77\x47\xbb\x97\ 131 | \xf2\x30\xa7\xec\x54\xde\x3c\x9e\x4e\xe4\x45\x99\x3c\x33\x95\x72\ 132 | \x2e\x13\x53\x7a\x95\x04\x6c\x3d\x3a\x11\xe1\x61\x2c\x64\xda\x9b\ 133 | \xb3\x24\x19\xc1\x9a\x48\xca\x95\x53\x16\x9d\x7b\x04\xe7\xef\x06\ 134 | \x70\x96\xab\x7b\xae\xd5\xb7\x1c\x52\x70\x53\xd9\xa3\x4a\x8a\x1b\ 135 | \x22\x21\x54\x5b\x18\xc3\x82\x62\x87\xe1\xba\xd1\x04\xed\x61\xd4\ 136 | \xee\xd4\x60\x1a\x64\x78\xb0\x03\xaf\xa6\xdf\x9d\xd0\xdb\x09\x35\ 137 | \x86\xc0\x9e\xda\xb9\x9b\x2d\x1d\xe8\x43\x50\x8c\x92\x92\x1c\x07\ 138 | \x2e\xfe\x7c\x22\x2a\xee\xdd\x4f\x6b\x0e\xed\x00\x1d\x17\x7a\x44\ 139 | \x06\x87\xfc\xed\x5d\xed\xeb\x89\x59\x4f\x45\x0f\xcd\x06\x3b\x7d\ 140 | \x2f\x3c\x56\xc8\x11\x76\x08\x64\xb4\x68\x0d\xee\xc4\xe6\x10\x98\ 141 | \xac\xf3\x89\xd5\xa9\x10\x5c\xc2\x94\xa7\x96\x0f\x5a\xec\x65\xfa\ 142 | \xf8\x3d\x06\x18\x74\xde\x7b\x41\x7a\xd2\xc9\x30\x30\x73\x6b\xdb\ 143 | \xb5\xf3\x6b\x97\x7f\xdb\x03\xc8\x17\x86\xa5\x6b\xcd\x7d\x2e\x30\ 144 | \x5d\xde\x2c\xcd\x24\x25\x5c\xb1\x30\x11\xe1\xf2\x91\xa5\xb4\xd3\ 145 | \x18\x90\xde\x0c\xf7\xd1\x5b\xd8\x97\xa1\x73\x7b\xd8\x6e\x66\x22\ 146 | \x6b\xc3\x2e\x0c\xba\x47\xb4\xe2\xda\xcc\xe5\x66\xaf\x07\xeb\x71\ 147 | \xc5\x5d\xa3\x09\x4e\x44\xb1\xa4\x5f\xd4\xce\x14\x93\x31\xaa\x0c\ 148 | \xd3\x13\xa6\x41\x3d\x9c\x7d\x4d\x72\xf2\x0c\x5e\xc6\x09\xb1\x84\ 149 | \x37\xb0\xf1\x42\x26\x1a\x58\x38\x7a\xfb\x78\xc2\xa4\x92\xfd\x1e\ 150 | \xa8\x7e\x71\xfa\x33\x35\xfb\xbf\x8e\x7e\xab\x8e\xcc\x4d\xee\xea\ 151 | \xfb\xfb\x57\x56\x92\x7f\xc4\x7d\xc5\x81\x72\x54\x33\xc5\x34\x0b\ 152 | \xb1\xdd\x52\x39\xe5\x6e\xa2\x39\x84\xfd\xaf\xc0\xc4\x61\x07\xc5\ 153 | \x99\x5d\x68\xb9\x03\x85\xf6\x5d\xd8\x29\x6c\xac\xf9\xbe\xfb\xd6\ 154 | \x2f\x05\x2b\x04\x14\ 155 | \x00\x00\x00\xa5\ 156 | \x6d\ 157 | \x6f\x64\x75\x6c\x65\x20\x63\x6f\x6d\x70\x6f\x6e\x65\x6e\x74\x73\ 158 | \x2e\x63\x6f\x6d\x6d\x6f\x6e\x0a\x0a\x43\x69\x72\x63\x6c\x65\x50\ 159 | \x72\x6f\x67\x72\x65\x73\x73\x20\x31\x2e\x30\x20\x43\x69\x72\x63\ 160 | \x6c\x65\x50\x72\x6f\x67\x72\x65\x73\x73\x2e\x71\x6d\x6c\x0a\x43\ 161 | \x6c\x6f\x63\x6b\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x31\x2e\ 162 | \x30\x20\x43\x6c\x6f\x63\x6b\x2e\x71\x6d\x6c\x0a\x4b\x61\x6b\x61\ 163 | \x50\x72\x6f\x67\x72\x65\x73\x73\x20\x20\x20\x31\x2e\x30\x20\x4b\ 164 | \x61\x6b\x61\x50\x72\x6f\x67\x72\x65\x73\x73\x2e\x71\x6d\x6c\x0a\ 165 | \x4b\x61\x6b\x61\x4c\x69\x73\x74\x56\x69\x65\x77\x20\x20\x20\x31\ 166 | \x2e\x30\x20\x4b\x61\x6b\x61\x4c\x69\x73\x74\x56\x69\x65\x77\x2e\ 167 | \x71\x6d\x6c\x0a\ 168 | \x00\x00\x01\xf9\ 169 | \x69\ 170 | \x6d\x70\x6f\x72\x74\x20\x51\x74\x51\x75\x69\x63\x6b\x20\x32\x2e\ 171 | \x30\x0a\x0a\x52\x65\x63\x74\x61\x6e\x67\x6c\x65\x20\x7b\x0a\x20\ 172 | \x20\x20\x20\x77\x69\x64\x74\x68\x3a\x20\x31\x30\x30\x0a\x20\x20\ 173 | \x20\x20\x68\x65\x69\x67\x68\x74\x3a\x20\x32\x30\x0a\x0a\x20\x20\ 174 | \x20\x20\x70\x72\x6f\x70\x65\x72\x74\x79\x20\x72\x65\x61\x6c\x20\ 175 | \x63\x75\x72\x72\x65\x6e\x74\x54\x69\x6d\x65\x73\x74\x61\x6d\x70\ 176 | \x0a\x0a\x20\x20\x20\x20\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x75\ 177 | \x70\x64\x61\x74\x65\x54\x69\x6d\x65\x28\x29\x20\x7b\x0a\x20\x20\ 178 | \x20\x20\x20\x20\x20\x20\x76\x61\x72\x20\x6e\x6f\x77\x20\x3d\x20\ 179 | \x6e\x65\x77\x20\x44\x61\x74\x65\x28\x29\x3b\x0a\x20\x20\x20\x20\ 180 | \x20\x20\x20\x20\x63\x75\x72\x72\x65\x6e\x74\x54\x69\x6d\x65\x73\ 181 | \x74\x61\x6d\x70\x20\x3d\x20\x6e\x6f\x77\x2e\x67\x65\x74\x54\x69\ 182 | \x6d\x65\x28\x29\x3b\x0a\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\x20\ 183 | \x20\x54\x69\x6d\x65\x72\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\ 184 | \x20\x69\x6e\x74\x65\x72\x76\x61\x6c\x3a\x20\x31\x30\x30\x30\x0a\ 185 | \x20\x20\x20\x20\x20\x20\x20\x20\x72\x65\x70\x65\x61\x74\x3a\x20\ 186 | \x74\x72\x75\x65\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x72\x75\x6e\ 187 | \x6e\x69\x6e\x67\x3a\x20\x74\x72\x75\x65\x0a\x20\x20\x20\x20\x20\ 188 | \x20\x20\x20\x6f\x6e\x54\x72\x69\x67\x67\x65\x72\x65\x64\x3a\x20\ 189 | \x7b\x20\x75\x70\x64\x61\x74\x65\x54\x69\x6d\x65\x28\x29\x3b\x20\ 190 | \x7d\x0a\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\x20\x20\x54\x65\x78\ 191 | \x74\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x74\x65\x78\x74\ 192 | \x3a\x20\x51\x74\x2e\x66\x6f\x72\x6d\x61\x74\x54\x69\x6d\x65\x20\ 193 | \x28\x6e\x65\x77\x20\x44\x61\x74\x65\x20\x28\x63\x75\x72\x72\x65\ 194 | \x6e\x74\x54\x69\x6d\x65\x73\x74\x61\x6d\x70\x20\x2b\x20\x28\x30\ 195 | \x20\x2a\x20\x36\x30\x30\x30\x30\x29\x29\x2c\x20\x22\x41\x20\x68\ 196 | \x68\x3a\x6d\x6d\x22\x29\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x66\ 197 | \x6f\x6e\x74\x2e\x70\x69\x78\x65\x6c\x53\x69\x7a\x65\x3a\x20\x31\ 198 | \x38\x0a\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\x20\x20\x43\x6f\x6d\ 199 | \x70\x6f\x6e\x65\x6e\x74\x2e\x6f\x6e\x43\x6f\x6d\x70\x6c\x65\x74\ 200 | \x65\x64\x3a\x20\x7b\x20\x75\x70\x64\x61\x74\x65\x54\x69\x6d\x65\ 201 | \x28\x29\x3b\x20\x7d\x0a\x7d\x0a\ 202 | \x00\x00\x09\x21\ 203 | \x69\ 204 | \x6d\x70\x6f\x72\x74\x20\x51\x74\x51\x75\x69\x63\x6b\x20\x32\x2e\ 205 | \x37\x0a\x69\x6d\x70\x6f\x72\x74\x20\x51\x74\x51\x75\x69\x63\x6b\ 206 | \x2e\x43\x6f\x6e\x74\x72\x6f\x6c\x73\x20\x32\x2e\x32\x0a\x69\x6d\ 207 | \x70\x6f\x72\x74\x20\x51\x74\x47\x72\x61\x70\x68\x69\x63\x61\x6c\ 208 | \x45\x66\x66\x65\x63\x74\x73\x20\x31\x2e\x30\x0a\x0a\x50\x72\x6f\ 209 | \x67\x72\x65\x73\x73\x42\x61\x72\x20\x7b\x0a\x20\x20\x20\x20\x69\ 210 | \x64\x3a\x20\x70\x72\x6f\x67\x72\x65\x73\x73\x42\x61\x72\x0a\x0a\ 211 | \x20\x20\x20\x20\x76\x61\x6c\x75\x65\x3a\x20\x30\x0a\x20\x20\x20\ 212 | \x20\x63\x6c\x69\x70\x3a\x20\x74\x72\x75\x65\x0a\x0a\x20\x20\x20\ 213 | \x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x52\x65\x63\ 214 | \x74\x61\x6e\x67\x6c\x65\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\ 215 | \x20\x69\x64\x3a\x20\x62\x61\x72\x46\x72\x61\x6d\x65\x0a\x20\x20\ 216 | \x20\x20\x20\x20\x20\x20\x69\x6d\x70\x6c\x69\x63\x69\x74\x57\x69\ 217 | \x64\x74\x68\x3a\x20\x33\x30\x30\x0a\x20\x20\x20\x20\x20\x20\x20\ 218 | \x20\x69\x6d\x70\x6c\x69\x63\x69\x74\x48\x65\x69\x67\x68\x74\x3a\ 219 | \x20\x32\x30\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x62\x6f\x72\x64\ 220 | \x65\x72\x2e\x63\x6f\x6c\x6f\x72\x3a\x20\x22\x23\x39\x39\x39\x39\ 221 | \x39\x39\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x72\x61\x64\x69\ 222 | \x75\x73\x3a\x20\x35\x0a\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\x20\ 223 | \x20\x63\x6f\x6e\x74\x65\x6e\x74\x49\x74\x65\x6d\x3a\x20\x49\x74\ 224 | \x65\x6d\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3a\ 225 | \x20\x69\x74\x65\x6d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6d\ 226 | \x70\x6c\x69\x63\x69\x74\x57\x69\x64\x74\x68\x3a\x20\x33\x30\x30\ 227 | \x0a\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6d\x70\x6c\x69\x63\x69\ 228 | \x74\x48\x65\x69\x67\x68\x74\x3a\x20\x31\x38\x0a\x0a\x20\x20\x20\ 229 | \x20\x20\x20\x20\x20\x52\x65\x63\x74\x61\x6e\x67\x6c\x65\x20\x7b\ 230 | \x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3a\ 231 | \x20\x62\x61\x72\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 232 | \x20\x77\x69\x64\x74\x68\x3a\x20\x70\x72\x6f\x67\x72\x65\x73\x73\ 233 | \x42\x61\x72\x2e\x76\x69\x73\x75\x61\x6c\x50\x6f\x73\x69\x74\x69\ 234 | \x6f\x6e\x20\x2a\x20\x70\x61\x72\x65\x6e\x74\x2e\x77\x69\x64\x74\ 235 | \x68\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x68\x65\ 236 | \x69\x67\x68\x74\x3a\x20\x70\x61\x72\x65\x6e\x74\x2e\x68\x65\x69\ 237 | \x67\x68\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 238 | \x72\x61\x64\x69\x75\x73\x3a\x20\x62\x61\x72\x46\x72\x61\x6d\x65\ 239 | \x2e\x72\x61\x64\x69\x75\x73\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\ 240 | \x20\x20\x20\x20\x20\x2f\x2a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ 241 | \x20\x20\x20\x20\x54\x65\x78\x74\x20\x7b\x0a\x20\x20\x20\x20\x20\ 242 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x74\x65\x78\x74\x3a\ 243 | \x20\x70\x72\x6f\x67\x72\x65\x73\x73\x42\x61\x72\x2e\x76\x61\x6c\ 244 | \x75\x65\x20\x2b\x20\x27\x25\x27\x0a\x20\x20\x20\x20\x20\x20\x20\ 245 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x66\x6f\x6e\x74\x2e\x70\x6f\ 246 | \x69\x6e\x74\x53\x69\x7a\x65\x3a\x20\x31\x32\x0a\x20\x20\x20\x20\ 247 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x66\x6f\x6e\x74\ 248 | \x2e\x77\x65\x69\x67\x68\x74\x3a\x20\x46\x6f\x6e\x74\x2e\x42\x6f\ 249 | \x6c\x64\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 250 | \x20\x20\x20\x66\x6f\x6e\x74\x2e\x62\x6f\x6c\x64\x3a\x20\x74\x72\ 251 | \x75\x65\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 252 | \x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x27\x62\x6c\x61\x63\x6b\ 253 | \x27\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\ 254 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x2f\x0a\x20\ 255 | \x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\x20\x20\x20\x20\ 256 | \x20\x20\x2f\x2f\x20\xe5\xb7\xa6\xe5\x8f\xb3\xe6\xbc\xb8\xe5\xb1\ 257 | \xa4\xe9\xa1\x8f\xe8\x89\xb2\x28\xe7\xb6\xa0\x20\x2d\x3e\x20\xe4\ 258 | \xba\xae\xe7\xb6\xa0\x20\x2d\x3e\x20\xe7\xb6\xa0\x29\x0a\x20\x20\ 259 | \x20\x20\x20\x20\x20\x20\x4c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\ 260 | \x69\x65\x6e\x74\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 261 | \x20\x20\x20\x61\x6e\x63\x68\x6f\x72\x73\x2e\x66\x69\x6c\x6c\x3a\ 262 | \x20\x62\x61\x72\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 263 | \x20\x73\x74\x61\x72\x74\x3a\x20\x51\x74\x2e\x70\x6f\x69\x6e\x74\ 264 | \x28\x30\x2c\x20\x30\x29\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 265 | \x20\x20\x20\x65\x6e\x64\x3a\x20\x51\x74\x2e\x70\x6f\x69\x6e\x74\ 266 | \x28\x62\x61\x72\x2e\x77\x69\x64\x74\x68\x2c\x20\x30\x29\x0a\x20\ 267 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x75\x72\x63\ 268 | \x65\x3a\x20\x62\x61\x72\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 269 | \x20\x20\x20\x6f\x70\x61\x63\x69\x74\x79\x3a\x20\x30\x2e\x39\x0a\ 270 | \x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\ 271 | \x64\x69\x65\x6e\x74\x3a\x20\x47\x72\x61\x64\x69\x65\x6e\x74\x20\ 272 | \x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 273 | \x20\x20\x47\x72\x61\x64\x69\x65\x6e\x74\x53\x74\x6f\x70\x20\x7b\ 274 | \x20\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3a\x20\x30\x2e\x30\x3b\x20\ 275 | \x63\x6f\x6c\x6f\x72\x3a\x20\x22\x23\x31\x37\x61\x38\x31\x61\x22\ 276 | \x20\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 277 | \x20\x20\x20\x47\x72\x61\x64\x69\x65\x6e\x74\x53\x74\x6f\x70\x20\ 278 | \x7b\x20\x69\x64\x3a\x20\x67\x72\x61\x64\x3b\x20\x70\x6f\x73\x69\ 279 | \x74\x69\x6f\x6e\x3a\x20\x30\x2e\x35\x3b\x20\x63\x6f\x6c\x6f\x72\ 280 | \x3a\x20\x51\x74\x2e\x6c\x69\x67\x68\x74\x65\x72\x28\x22\x23\x31\ 281 | \x37\x61\x38\x31\x61\x22\x2c\x20\x32\x29\x20\x7d\x0a\x20\x20\x20\ 282 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x47\x72\x61\ 283 | \x64\x69\x65\x6e\x74\x53\x74\x6f\x70\x20\x7b\x20\x70\x6f\x73\x69\ 284 | \x74\x69\x6f\x6e\x3a\x20\x31\x2e\x30\x3b\x20\x63\x6f\x6c\x6f\x72\ 285 | \x3a\x20\x22\x23\x31\x37\x61\x38\x31\x61\x22\x20\x7d\x0a\x20\x20\ 286 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\x20\ 287 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x2f\x2a\x0a\x20\x20\x20\x20\ 288 | \x20\x20\x20\x20\x20\x20\x20\x20\x50\x72\x6f\x70\x65\x72\x74\x79\ 289 | \x41\x6e\x69\x6d\x61\x74\x69\x6f\x6e\x20\x7b\x0a\x20\x20\x20\x20\ 290 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x74\x61\x72\x67\ 291 | \x65\x74\x3a\x20\x67\x72\x61\x64\x0a\x20\x20\x20\x20\x20\x20\x20\ 292 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x70\x72\x6f\x70\x65\x72\x74\ 293 | \x79\x3a\x20\x22\x70\x6f\x73\x69\x74\x69\x6f\x6e\x22\x0a\x20\x20\ 294 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x66\x72\ 295 | \x6f\x6d\x3a\x20\x30\x2e\x31\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ 296 | \x20\x20\x20\x20\x20\x20\x20\x20\x74\x6f\x3a\x20\x30\x2e\x39\x0a\ 297 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 298 | \x64\x75\x72\x61\x74\x69\x6f\x6e\x3a\x20\x31\x30\x30\x30\x0a\x20\ 299 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x72\ 300 | \x75\x6e\x6e\x69\x6e\x67\x3a\x20\x74\x72\x75\x65\x0a\x20\x20\x20\ 301 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x6f\x6f\ 302 | \x70\x73\x3a\x20\x41\x6e\x69\x6d\x61\x74\x69\x6f\x6e\x2e\x49\x6e\ 303 | \x66\x69\x6e\x69\x74\x65\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 304 | \x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 305 | \x20\x2a\x2f\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x0a\ 306 | \x20\x20\x20\x20\x20\x20\x20\x20\x2f\x2f\x20\xe4\xb8\x8a\xe4\xb8\ 307 | \x8b\xe6\xbc\xb8\xe5\xb1\xa4\xe9\xa1\x8f\xe8\x89\xb2\x0a\x20\x20\ 308 | \x20\x20\x20\x20\x20\x20\x4c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\ 309 | \x69\x65\x6e\x74\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 310 | \x20\x20\x20\x61\x6e\x63\x68\x6f\x72\x73\x2e\x66\x69\x6c\x6c\x3a\ 311 | \x20\x62\x61\x72\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 312 | \x20\x73\x74\x61\x72\x74\x3a\x20\x51\x74\x2e\x70\x6f\x69\x6e\x74\ 313 | \x28\x30\x2c\x20\x30\x29\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 314 | \x20\x20\x20\x65\x6e\x64\x3a\x20\x51\x74\x2e\x70\x6f\x69\x6e\x74\ 315 | \x28\x30\x2c\x20\x62\x61\x72\x2e\x68\x65\x69\x67\x68\x74\x29\x0a\ 316 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x75\x72\ 317 | \x63\x65\x3a\x20\x62\x61\x72\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ 318 | \x20\x20\x20\x20\x6f\x70\x61\x63\x69\x74\x79\x3a\x20\x30\x2e\x39\ 319 | \x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x67\x72\ 320 | \x61\x64\x69\x65\x6e\x74\x3a\x20\x47\x72\x61\x64\x69\x65\x6e\x74\ 321 | \x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 322 | \x20\x20\x20\x47\x72\x61\x64\x69\x65\x6e\x74\x53\x74\x6f\x70\x20\ 323 | \x7b\x20\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3a\x20\x30\x2e\x30\x3b\ 324 | \x20\x63\x6f\x6c\x6f\x72\x3a\x20\x51\x74\x2e\x72\x67\x62\x61\x28\ 325 | \x30\x2c\x30\x2c\x30\x2c\x30\x29\x20\x7d\x0a\x20\x20\x20\x20\x20\ 326 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x47\x72\x61\x64\x69\ 327 | \x65\x6e\x74\x53\x74\x6f\x70\x20\x7b\x20\x70\x6f\x73\x69\x74\x69\ 328 | \x6f\x6e\x3a\x20\x30\x2e\x35\x3b\x20\x63\x6f\x6c\x6f\x72\x3a\x20\ 329 | \x51\x74\x2e\x72\x67\x62\x61\x28\x31\x2c\x31\x2c\x31\x2c\x30\x2e\ 330 | \x33\x29\x20\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ 331 | \x20\x20\x20\x20\x20\x47\x72\x61\x64\x69\x65\x6e\x74\x53\x74\x6f\ 332 | \x70\x20\x7b\x20\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3a\x20\x31\x2e\ 333 | \x30\x3b\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x51\x74\x2e\x72\x67\x62\ 334 | \x61\x28\x30\x2c\x30\x2c\x30\x2c\x30\x2e\x30\x35\x29\x20\x7d\x0a\ 335 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\ 336 | \x20\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x7d\x0a\x0a\x20\ 337 | \x20\x20\x20\x2f\x2a\x0a\x20\x20\x20\x20\x50\x72\x6f\x70\x65\x72\ 338 | \x74\x79\x41\x6e\x69\x6d\x61\x74\x69\x6f\x6e\x20\x7b\x0a\x20\x20\ 339 | \x20\x20\x20\x20\x20\x20\x74\x61\x72\x67\x65\x74\x3a\x20\x70\x72\ 340 | \x6f\x67\x72\x65\x73\x73\x42\x61\x72\x0a\x20\x20\x20\x20\x20\x20\ 341 | \x20\x20\x70\x72\x6f\x70\x65\x72\x74\x79\x3a\x20\x22\x76\x61\x6c\ 342 | \x75\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x66\x72\x6f\x6d\ 343 | \x3a\x20\x30\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x74\x6f\x3a\x20\ 344 | \x31\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x64\x75\x72\x61\x74\x69\ 345 | \x6f\x6e\x3a\x20\x35\x30\x30\x30\x0a\x20\x20\x20\x20\x20\x20\x20\ 346 | \x20\x72\x75\x6e\x6e\x69\x6e\x67\x3a\x20\x74\x72\x75\x65\x0a\x20\ 347 | \x20\x20\x20\x20\x20\x20\x20\x6c\x6f\x6f\x70\x73\x3a\x20\x41\x6e\ 348 | \x69\x6d\x61\x74\x69\x6f\x6e\x2e\x49\x6e\x66\x69\x6e\x69\x74\x65\ 349 | \x0a\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x2a\x2f\x0a\x7d\x0a\ 350 | \ 351 | " 352 | 353 | qt_resource_name = b"\ 354 | \x00\x0a\ 355 | \x07\x6a\x09\x33\ 356 | \x00\x63\ 357 | \x00\x6f\x00\x6d\x00\x70\x00\x6f\x00\x6e\x00\x65\x00\x6e\x00\x74\x00\x73\ 358 | \x00\x06\ 359 | \x06\xa6\x44\x5e\ 360 | \x00\x63\ 361 | \x00\x6f\x00\x6d\x00\x6d\x00\x6f\x00\x6e\ 362 | \x00\x10\ 363 | \x03\xbf\x67\xfc\ 364 | \x00\x4b\ 365 | \x00\x61\x00\x6b\x00\x61\x00\x4c\x00\x69\x00\x73\x00\x74\x00\x56\x00\x69\x00\x65\x00\x77\x00\x2e\x00\x71\x00\x6d\x00\x6c\ 366 | \x00\x12\ 367 | \x0a\xd6\xc5\xbc\ 368 | \x00\x43\ 369 | \x00\x69\x00\x72\x00\x63\x00\x6c\x00\x65\x00\x50\x00\x72\x00\x6f\x00\x67\x00\x72\x00\x65\x00\x73\x00\x73\x00\x2e\x00\x71\x00\x6d\ 370 | \x00\x6c\ 371 | \x00\x06\ 372 | \x07\x84\x2b\x02\ 373 | \x00\x71\ 374 | \x00\x6d\x00\x6c\x00\x64\x00\x69\x00\x72\ 375 | \x00\x09\ 376 | \x05\x9e\xc4\x5c\ 377 | \x00\x43\ 378 | \x00\x6c\x00\x6f\x00\x63\x00\x6b\x00\x2e\x00\x71\x00\x6d\x00\x6c\ 379 | \x00\x10\ 380 | \x00\x44\x8d\x7c\ 381 | \x00\x4b\ 382 | \x00\x61\x00\x6b\x00\x61\x00\x50\x00\x72\x00\x6f\x00\x67\x00\x72\x00\x65\x00\x73\x00\x73\x00\x2e\x00\x71\x00\x6d\x00\x6c\ 383 | " 384 | 385 | qt_resource_struct_v1 = b"\ 386 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ 387 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ 388 | \x00\x00\x00\x1a\x00\x02\x00\x00\x00\x05\x00\x00\x00\x03\ 389 | \x00\x00\x00\xa6\x00\x00\x00\x00\x00\x01\x00\x00\x0b\x52\ 390 | \x00\x00\x00\x2c\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ 391 | \x00\x00\x00\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x09\x55\ 392 | \x00\x00\x00\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x08\xac\ 393 | \x00\x00\x00\x52\x00\x01\x00\x00\x00\x01\x00\x00\x05\x32\ 394 | " 395 | 396 | qt_resource_struct_v2 = b"\ 397 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ 398 | \x00\x00\x00\x00\x00\x00\x00\x00\ 399 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ 400 | \x00\x00\x00\x00\x00\x00\x00\x00\ 401 | \x00\x00\x00\x1a\x00\x02\x00\x00\x00\x05\x00\x00\x00\x03\ 402 | \x00\x00\x00\x00\x00\x00\x00\x00\ 403 | \x00\x00\x00\xa6\x00\x00\x00\x00\x00\x01\x00\x00\x0b\x52\ 404 | \x00\x00\x01\x67\x91\x4b\x11\x32\ 405 | \x00\x00\x00\x2c\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ 406 | \x00\x00\x01\x67\x91\x4b\x11\x32\ 407 | \x00\x00\x00\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x09\x55\ 408 | \x00\x00\x01\x67\x91\x4b\x11\x32\ 409 | \x00\x00\x00\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x08\xac\ 410 | \x00\x00\x01\x67\x91\x4b\x11\x32\ 411 | \x00\x00\x00\x52\x00\x01\x00\x00\x00\x01\x00\x00\x05\x32\ 412 | \x00\x00\x01\x67\x91\x4b\x11\x32\ 413 | " 414 | 415 | qt_version = [int(v) for v in QtCore.qVersion().split('.')] 416 | if qt_version < [5, 8, 0]: 417 | rcc_version = 1 418 | qt_resource_struct = qt_resource_struct_v1 419 | else: 420 | rcc_version = 2 421 | qt_resource_struct = qt_resource_struct_v2 422 | 423 | def qInitResources(): 424 | QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) 425 | 426 | def qCleanupResources(): 427 | QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) 428 | 429 | qInitResources() 430 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from PyQt5.QtQml import QQmlApplicationEngine 5 | from PyQt5.QtCore import QCoreApplication, QUrl, Qt 6 | 7 | from src import qml, components 8 | 9 | def run(app, dir_name, mode): 10 | # Create QML engine 11 | engine = QQmlApplicationEngine() 12 | context = engine.rootContext() 13 | 14 | if mode == "prod": 15 | engine.addImportPath('qrc:/') 16 | engine.load(QUrl('qrc:/main.qml')) 17 | else: 18 | engine.addImportPath(os.path.join(dir_name, "src/resources")) 19 | engine.load(QUrl(os.path.join(dir_name, "src/resources/main.qml"))) 20 | 21 | engine.quit.connect(app.quit) 22 | sys.exit(app.exec_()) 23 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/components/common/CircleProgress.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | 4 | Item { 5 | id: root 6 | 7 | width: radius * 2 8 | height: radius * 2 9 | 10 | property int radius: 100 11 | property real arcBegin: 0 // start arc angle in degree 12 | property real arcEnd: 180 // end arc angle in degree 13 | property real arcOffset: 0 // rotation 14 | property real minValue: 0 15 | property real maxValue: 100 16 | property real value: 50 17 | property string text: "%" 18 | property bool isValue: true 19 | property bool showBackground: false // a full circle as a background of the arc 20 | property real lineWidth: 20 // width of the line 21 | property string circleColor: "#1dc58f" 22 | property string backgroundColor: "#E6E6E6" 23 | property string textColor: "#FFFFFF" 24 | 25 | property alias beginAnimation: animationArcBegin.enabled 26 | property alias endAnimation: animationArcEnd.enabled 27 | property alias valueAnimation: animationValue.enabled 28 | 29 | property int animationDuration: 200 30 | 31 | onArcBeginChanged: canvas.requestPaint() 32 | onArcEndChanged: canvas.requestPaint() 33 | onValueChanged: canvas.requestPaint() 34 | 35 | Behavior on arcBegin { 36 | id: animationArcBegin 37 | enabled: true 38 | NumberAnimation { 39 | duration: root.animationDuration 40 | easing.type: Easing.InOutCubic 41 | } 42 | } 43 | 44 | Behavior on arcEnd { 45 | id: animationArcEnd 46 | enabled: true 47 | NumberAnimation { 48 | duration: root.animationDuration 49 | easing.type: Easing.InOutCubic 50 | } 51 | } 52 | 53 | Behavior on value { 54 | id: animationValue 55 | enabled: true 56 | NumberAnimation { 57 | duration: root.animationDuration 58 | easing.type: Easing.InOutCubic 59 | } 60 | } 61 | 62 | Canvas { 63 | id: canvas 64 | anchors.fill: parent 65 | rotation: -90 + parent.arcOffset 66 | 67 | onPaint: { 68 | var ctx = getContext("2d") 69 | var x = root.radius 70 | var y = root.radius 71 | var angle = (value - minValue) / (maxValue - minValue) * 2 * Math.PI 72 | var start = Math.PI * (parent.arcBegin / 180) 73 | var end = Math.PI * (parent.arcEnd / 180) 74 | ctx.reset() 75 | 76 | if (root.isValue) { 77 | if (root.showBackground) { 78 | ctx.beginPath(); 79 | // arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise) 80 | ctx.arc(x, y, radius - root.lineWidth / 2, 0, Math.PI * 2, false) 81 | ctx.lineWidth = root.lineWidth 82 | ctx.strokeStyle = root.backgroundColor 83 | ctx.stroke() 84 | } 85 | ctx.beginPath(); 86 | ctx.arc(x, y, radius - root.lineWidth / 2, 0, angle, false) 87 | ctx.lineWidth = root.lineWidth 88 | ctx.strokeStyle = root.circleColor 89 | ctx.stroke() 90 | } else { 91 | if (root.showBackground) { 92 | ctx.beginPath(); 93 | ctx.arc(x, y, radius - root.lineWidth / 2, 0, Math.PI * 2, false) 94 | ctx.lineWidth = root.lineWidth 95 | ctx.strokeStyle = root.backgroundColor 96 | ctx.stroke() 97 | } 98 | ctx.beginPath(); 99 | ctx.arc(x, y, radius - root.lineWidth / 2, start, end, false) 100 | ctx.lineWidth = root.lineWidth 101 | ctx.strokeStyle = root.circleColor 102 | ctx.stroke() 103 | } 104 | } 105 | } 106 | 107 | Text { 108 | anchors.centerIn: parent 109 | 110 | text: root.value + root.text 111 | color: root.textColor 112 | 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/components/common/Clock.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Rectangle { 4 | width: 100 5 | height: 20 6 | 7 | property real currentTimestamp 8 | 9 | function updateTime() { 10 | var now = new Date(); 11 | currentTimestamp = now.getTime(); 12 | } 13 | 14 | Timer { 15 | interval: 1000 16 | repeat: true 17 | running: true 18 | onTriggered: { updateTime(); } 19 | } 20 | 21 | Text { 22 | text: Qt.formatTime (new Date (currentTimestamp + (0 * 60000)), "A hh:mm") 23 | font.pixelSize: 18 24 | } 25 | 26 | Component.onCompleted: { updateTime(); } 27 | } 28 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/components/common/KakaListView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Layouts 1.3 3 | 4 | Rectangle { 5 | id: root 6 | anchors.fill: parent 7 | 8 | property variant internalModel 9 | signal rowChanged 10 | 11 | onRowChanged: { 12 | //listView.positionViewAtEnd(); 13 | listView.positionViewAtIndex(listView.count - 1, ListView.Beginning) 14 | } 15 | 16 | ListView { 17 | id: listView 18 | anchors.fill: parent 19 | contentWidth: 320 20 | clip: true 21 | 22 | model: parent.internalModel 23 | 24 | delegate: listDelegate 25 | } 26 | 27 | Component { 28 | id: listDelegate 29 | 30 | Rectangle { 31 | width: root.width 32 | height: 40 33 | color: index % 2 == 0 ? "white" : "lightgray" 34 | 35 | GridLayout { 36 | anchors.fill: parent 37 | columns: 2 38 | columnSpacing: (parent.width - itemText.contentWidth - dataText.contentWidth) - 10 39 | 40 | Text { 41 | id: itemText 42 | text: model.item 43 | font.bold: true 44 | font.pixelSize: 20 45 | } 46 | 47 | Text { 48 | id: dataText 49 | text: model.data 50 | font.pixelSize: 20 51 | color: "red" 52 | } 53 | } 54 | } 55 | } 56 | 57 | Component.onCompleted: { 58 | listView.positionViewAtEnd(); 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/components/common/KakaProgress.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.2 3 | import QtGraphicalEffects 1.0 4 | 5 | ProgressBar { 6 | id: progressBar 7 | 8 | value: 0 9 | clip: true 10 | 11 | background: Rectangle { 12 | id: barFrame 13 | implicitWidth: 300 14 | implicitHeight: 20 15 | border.color: "#999999" 16 | radius: 5 17 | } 18 | 19 | contentItem: Item { 20 | id: item 21 | implicitWidth: 300 22 | implicitHeight: 18 23 | 24 | Rectangle { 25 | id: bar 26 | width: progressBar.visualPosition * parent.width 27 | height: parent.height 28 | radius: barFrame.radius 29 | 30 | /* 31 | Text { 32 | text: progressBar.value + '%' 33 | font.pointSize: 12 34 | font.weight: Font.Bold 35 | font.bold: true 36 | color: 'black' 37 | } 38 | */ 39 | } 40 | 41 | // 左右漸層顏色(綠 -> 亮綠 -> 綠) 42 | LinearGradient { 43 | anchors.fill: bar 44 | start: Qt.point(0, 0) 45 | end: Qt.point(bar.width, 0) 46 | source: bar 47 | opacity: 0.9 48 | 49 | gradient: Gradient { 50 | GradientStop { position: 0.0; color: "#17a81a" } 51 | GradientStop { id: grad; position: 0.5; color: Qt.lighter("#17a81a", 2) } 52 | GradientStop { position: 1.0; color: "#17a81a" } 53 | } 54 | 55 | /* 56 | PropertyAnimation { 57 | target: grad 58 | property: "position" 59 | from: 0.1 60 | to: 0.9 61 | duration: 1000 62 | running: true 63 | loops: Animation.Infinite 64 | } 65 | */ 66 | 67 | } 68 | 69 | // 上下漸層顏色 70 | LinearGradient { 71 | anchors.fill: bar 72 | start: Qt.point(0, 0) 73 | end: Qt.point(0, bar.height) 74 | source: bar 75 | opacity: 0.9 76 | 77 | gradient: Gradient { 78 | GradientStop { position: 0.0; color: Qt.rgba(0,0,0,0) } 79 | GradientStop { position: 0.5; color: Qt.rgba(1,1,1,0.3) } 80 | GradientStop { position: 1.0; color: Qt.rgba(0,0,0,0.05) } 81 | } 82 | } 83 | } 84 | 85 | /* 86 | PropertyAnimation { 87 | target: progressBar 88 | property: "value" 89 | from: 0 90 | to: 1 91 | duration: 5000 92 | running: true 93 | loops: Animation.Infinite 94 | } 95 | */ 96 | } 97 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/components/common/qmldir: -------------------------------------------------------------------------------- 1 | module components.common 2 | 3 | CircleProgress 1.0 CircleProgress.qml 4 | Clock 1.0 Clock.qml 5 | KakaProgress 1.0 KakaProgress.qml 6 | KakaListView 1.0 KakaListView.qml 7 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/components/components.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | common/CircleProgress.qml 4 | common/Clock.qml 5 | common/KakaProgress.qml 6 | common/KakaListView.qml 7 | common/qmldir 8 | 9 | 10 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/images/about.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyqt5/qml/src/resources/images/about.ico -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/images/exit.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyqt5/qml/src/resources/images/exit.ico -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/images/newFile.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyqt5/qml/src/resources/images/newFile.ico -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Controls 2.1 3 | import QtQuick.Layouts 1.3 4 | import QtQuick.Dialogs 1.2 5 | 6 | import "tabs" 7 | import components.common 1.0 8 | 9 | ApplicationWindow { 10 | id: window 11 | visible: true 12 | minimumWidth: 640 13 | minimumHeight: 480 14 | 15 | title: qsTr("Qml Template") 16 | 17 | ////////////////////////////////////////////////////////////////////////// 18 | // menu 19 | header: ToolBar { 20 | id: menu 21 | 22 | background: Rectangle { 23 | implicitWidth: 100 24 | implicitHeight: 50 25 | border.color: "#999" 26 | 27 | gradient: Gradient { 28 | GradientStop { position: 0 ; color: "#fff" } 29 | GradientStop { position: 1 ; color: "#eee" } 30 | } 31 | } 32 | 33 | Row { 34 | anchors.fill: parent 35 | spacing: 5 36 | 37 | ToolButton { 38 | Image { 39 | id: newFileImage 40 | source: "images/newFile.ico" 41 | asynchronous:true 42 | fillMode: Image.PreserveAspectFit 43 | anchors.fill: parent 44 | } 45 | anchors.verticalCenter: parent.verticalCenter 46 | onClicked: fileDialog.open(); 47 | } 48 | 49 | ToolButton { 50 | Image { 51 | id: aboutImage 52 | source: "images/about.ico" 53 | asynchronous:true 54 | fillMode: Image.PreserveAspectFit 55 | anchors.fill: parent 56 | } 57 | anchors.verticalCenter: parent.verticalCenter 58 | onClicked: aboutBox.open(); 59 | } 60 | 61 | ToolButton { 62 | Image { 63 | id: exitImage 64 | source: "images/exit.ico" 65 | asynchronous:true 66 | fillMode: Image.PreserveAspectFit 67 | anchors.fill: parent 68 | } 69 | anchors.verticalCenter: parent.verticalCenter 70 | onClicked: { 71 | Qt.quit(); 72 | } 73 | } 74 | } 75 | 76 | Clock { 77 | id: clock 78 | anchors.right: parent.right 79 | anchors.verticalCenter: parent.verticalCenter 80 | 81 | gradient: Gradient { 82 | GradientStop { position: 0 ; color: "#fff" } 83 | GradientStop { position: 1 ; color: "#eee" } 84 | } 85 | } 86 | } 87 | 88 | MessageDialog { 89 | id: aboutBox 90 | title: "About" 91 | text: " 92 | This is QML Template\n 93 | Version: 0.1 94 | Date:2018/10/11" 95 | icon: StandardIcon.Information 96 | } 97 | 98 | FileDialog { 99 | id: fileDialog 100 | visible: false 101 | title: "Please choose a file" 102 | folder: shortcuts.home 103 | selectFolder: true 104 | } 105 | ////////////////////////////////////////////////////////////////////////// 106 | // footer 107 | 108 | footer: TabBar { 109 | id: tabBar 110 | width: parent.width 111 | height: 40 112 | currentIndex: 0 113 | 114 | TabButton { 115 | text: qsTr("Tab1") 116 | onClicked: { 117 | // 118 | } 119 | } 120 | 121 | TabButton { 122 | text: qsTr("Tab2") 123 | onClicked: { 124 | // 125 | } 126 | } 127 | } 128 | 129 | StackLayout { 130 | id: layout 131 | anchors.fill: parent 132 | currentIndex: tabBar.currentIndex 133 | 134 | Tab1 { 135 | id: aTab 136 | } 137 | 138 | Tab2 { 139 | id: bTab 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | tabs/Tab1.qml 6 | tabs/Tab2.qml 7 | 8 | images/about.ico 9 | images/exit.ico 10 | images/newFile.ico 11 | 12 | 13 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/tabs/Tab1.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Controls 2.1 3 | import QtQuick.Layouts 1.3 4 | 5 | Page { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /python/pyqt5/qml/src/resources/tabs/Tab2.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Controls 2.1 3 | import QtQuick.Layouts 1.3 4 | 5 | Page { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /python/pyqt5/qt/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.qml] 16 | indent_size = 4 17 | 18 | [*.js] 19 | indent_size = 2 20 | 21 | # Tab indentation (no size specified) 22 | [Makefile] 23 | indent_style = tab 24 | -------------------------------------------------------------------------------- /python/pyqt5/qt/.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Mac 5 | .DS_Store 6 | ._.DS_Store 7 | 8 | # pyenv 9 | venv/ 10 | .venv/ 11 | 12 | # python 13 | *.pyc 14 | *.pro 15 | *~ 16 | __pycache__ 17 | 18 | # QML 19 | *.qmlc 20 | -------------------------------------------------------------------------------- /python/pyqt5/qt/README.md: -------------------------------------------------------------------------------- 1 | # Qt Template 2 | 3 | ## Usage 4 | 5 | ```bash 6 | $ python3 main.py 7 | ``` 8 | -------------------------------------------------------------------------------- /python/pyqt5/qt/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyqt5/qt/app/__init__.py -------------------------------------------------------------------------------- /python/pyqt5/qt/gui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyqt5/qt/gui/__init__.py -------------------------------------------------------------------------------- /python/pyqt5/qt/gui/app.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PyQt5.QtCore import QTranslator, QLocale 3 | from PyQt5.QtWidgets import QApplication 4 | from gui.mainwindow import MainWindow 5 | 6 | def run(): 7 | 8 | translator = QTranslator() 9 | #translator.load(QLocale, 'main', '_', './i18n') 10 | 11 | app = QApplication(sys.argv) 12 | 13 | main_window = MainWindow(app, translator) 14 | main_window.show() 15 | 16 | sys.exit(app.exec_()) 17 | 18 | if __name__ == '__main__': 19 | run() 20 | -------------------------------------------------------------------------------- /python/pyqt5/qt/gui/hellobox.py: -------------------------------------------------------------------------------- 1 | 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | 4 | from gui.ui_hellobox import Ui_HelloBox 5 | 6 | class HelloBox(QtWidgets.QGroupBox): 7 | """ Template box """ 8 | def __init__(self, parent=None): 9 | super(HelloBox, self).__init__(parent) 10 | 11 | self.ui = Ui_HelloBox() 12 | self.ui.setupUi(self) 13 | self._setup_ui() 14 | 15 | def _setup_ui(self): 16 | """ """ 17 | 18 | def onClick(self): 19 | """ """ 20 | self.ui.label.setText("Button clicked") 21 | -------------------------------------------------------------------------------- /python/pyqt5/qt/gui/hellotab.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore, QtGui, QtWidgets 2 | from gui.hellobox import HelloBox 3 | 4 | class HelloTab(QtWidgets.QVBoxLayout): 5 | def __init__(self, parent=None): 6 | super(HelloTab, self).__init__(parent) 7 | 8 | self.hello_box = HelloBox() 9 | 10 | self.addWidget(self.hello_box) 11 | 12 | self._setup_ui() 13 | 14 | def _setup_ui(self): 15 | self.retranslateUi() 16 | 17 | def retranslateUi(self): 18 | _translate = QtCore.QCoreApplication.translate 19 | -------------------------------------------------------------------------------- /python/pyqt5/qt/gui/mainwindow.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from PyQt5.QtWidgets import QMainWindow, QFileDialog 4 | from gui.ui_mainwindow import Ui_MainWindow 5 | from gui.hellotab import HelloTab 6 | 7 | class MainWindow(QMainWindow): 8 | def __init__ (self, app, translator, parent=None): 9 | super(MainWindow, self).__init__(parent) 10 | 11 | self._app = app 12 | self._translator = translator 13 | app.installTranslator(translator) 14 | 15 | self.ui = Ui_MainWindow() 16 | self.ui.setupUi(self) 17 | self._setup_ui() 18 | 19 | def _setup_ui(self): 20 | ''' ''' 21 | self._tab = HelloTab(self.ui.tab) 22 | 23 | def openFile(self): 24 | dir = os.path.dirname(__file__) 25 | file_name = QFileDialog.getOpenFileName(self, 'Open file', dir) 26 | -------------------------------------------------------------------------------- /python/pyqt5/qt/gui/ui_hellobox.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore, QtGui, QtWidgets 2 | from PyQt5.QtWidgets import QFileDialog, QSizePolicy 3 | 4 | class Ui_HelloBox(object): 5 | def setupUi(self, helloBox): 6 | helloBox.setObjectName("helloBox") 7 | 8 | self.gridLayout = QtWidgets.QGridLayout(helloBox) 9 | self.gridLayout.setHorizontalSpacing(12) 10 | self.gridLayout.setVerticalSpacing(12) 11 | 12 | self.button = QtWidgets.QPushButton(helloBox) 13 | self.button.setObjectName("button") 14 | self.label = QtWidgets.QLabel(helloBox) 15 | self.label.setObjectName("label") 16 | 17 | self.gridLayout.addWidget(self.label, 0, 0, 1, 1) 18 | self.gridLayout.addWidget(self.button, 0, 1, 1, 1) 19 | 20 | self.retranslateUi(helloBox) 21 | 22 | self.button.clicked.connect(helloBox.onClick) 23 | 24 | def retranslateUi(self, helloBox): 25 | _translate = QtCore.QCoreApplication.translate 26 | helloBox.setTitle(_translate("helloBox", "Hello Box")) 27 | self.button.setText(_translate("VideoBox", "Button")) 28 | self.label.setText(_translate("helloBox", "Hello World")) 29 | -------------------------------------------------------------------------------- /python/pyqt5/qt/gui/ui_mainwindow.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore, QtGui, QtWidgets 2 | 3 | class Ui_MainWindow(object): 4 | def setupUi(self, MainWindow): 5 | MainWindow.setObjectName("MainWindow") 6 | MainWindow.setMinimumSize(QtCore.QSize(640, 480)) 7 | 8 | self.centralwidget = QtWidgets.QWidget(MainWindow) 9 | self.centralwidget.setObjectName("centralwidget") 10 | self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) 11 | self.verticalLayout.setObjectName("verticalLayout") 12 | self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) 13 | self.tabWidget.setObjectName("tabWidget") 14 | self.tab = QtWidgets.QWidget() 15 | self.tab.setObjectName("tab") 16 | self.tabWidget.addTab(self.tab, "") 17 | self.verticalLayout.addWidget(self.tabWidget) 18 | MainWindow.setCentralWidget(self.centralwidget) 19 | self.menubar = QtWidgets.QMenuBar(MainWindow) 20 | self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 21)) 21 | self.menubar.setObjectName("menubar") 22 | self.menuAbout = QtWidgets.QMenu(self.menubar) 23 | self.menuAbout.setObjectName("menuAbout") 24 | self.menuFile = QtWidgets.QMenu(self.menubar) 25 | self.menuFile.setObjectName("menuFile") 26 | MainWindow.setMenuBar(self.menubar) 27 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 28 | self.statusbar.setObjectName("statusbar") 29 | MainWindow.setStatusBar(self.statusbar) 30 | self.actionAbout = QtWidgets.QAction(MainWindow) 31 | self.actionAbout.setObjectName("actionAbout") 32 | self.actionOpen = QtWidgets.QAction(MainWindow) 33 | self.actionOpen.setObjectName("actionOpen") 34 | self.menuAbout.addAction(self.actionAbout) 35 | self.menuFile.addAction(self.actionOpen) 36 | self.menubar.addAction(self.menuFile.menuAction()) 37 | self.menubar.addAction(self.menuAbout.menuAction()) 38 | 39 | self.actionOpen.triggered.connect(MainWindow.openFile) 40 | 41 | self.retranslateUi(MainWindow) 42 | self.tabWidget.setCurrentIndex(0) 43 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 44 | 45 | def retranslateUi(self, MainWindow): 46 | _translate = QtCore.QCoreApplication.translate 47 | MainWindow.setWindowTitle(_translate("MainWindow", "Qt Template")) 48 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab")) 49 | self.menuAbout.setTitle(_translate("MainWindow", "&Help")) 50 | self.menuFile.setTitle(_translate("MainWindow", "&File")) 51 | self.actionAbout.setText(_translate("MainWindow", "About")) 52 | self.actionOpen.setText(_translate("MainWindow", "Open File")) 53 | -------------------------------------------------------------------------------- /python/pyqt5/qt/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from gui import app 5 | app.run() 6 | -------------------------------------------------------------------------------- /python/pyqt5/qt/requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5==5.11.3 2 | -------------------------------------------------------------------------------- /python/pyside2/qml/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.qml] 16 | indent_size = 4 17 | 18 | [*.js] 19 | indent_size = 2 20 | 21 | # Tab indentation (no size specified) 22 | [Makefile] 23 | indent_style = tab 24 | -------------------------------------------------------------------------------- /python/pyside2/qml/.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Mac 5 | .DS_Store 6 | ._.DS_Store 7 | 8 | # pyenv 9 | venv/ 10 | .venv/ 11 | 12 | # python 13 | *.pyc 14 | *.pro 15 | *~ 16 | __pycache__ 17 | 18 | # QML 19 | *.qmlc 20 | -------------------------------------------------------------------------------- /python/pyside2/qml/README.md: -------------------------------------------------------------------------------- 1 | # QML Template 2 | 3 | ## Require 4 | 5 | - Python3+ 6 | - Pyside2 7 | 8 | ## Usage 9 | 10 | ```bash 11 | python3 qt_template.py 12 | ``` 13 | 14 | ## Packaging 15 | 16 | if you want to packing Python programs into ```stand-alone executables``` 17 | 18 | 1. Converting ```*.qrc``` (a collection of resource) files into ```*.py``` (Python source) file 19 | 20 | ```bash 21 | $ pyside2-rcc -o src/qml.py src/resources/qml.qrc 22 | 23 | $ pyside2-rcc -o src/components.py src/resources/components/components.qrc 24 | ``` 25 | 26 | 2. Testing 27 | 28 | Add ```prod``` argument to run freeze mode 29 | 30 | ```bash 31 | python3 qt_template.py prod 32 | ``` 33 | -------------------------------------------------------------------------------- /python/pyside2/qml/qml_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import sys 5 | 6 | mode = '' 7 | # use: python3 mtool.py prod 8 | if len(sys.argv) == 2: 9 | mode = sys.argv[1] 10 | 11 | if hasattr(sys, "frozen"): 12 | # running in a bundle 13 | dir_name = os.path.dirname(os.path.abspath(sys.executable)) 14 | mode = 'prod' 15 | else: 16 | # running live 17 | dir_name = os.path.dirname(os.path.abspath(__file__)) 18 | 19 | from PySide2.QtGui import QGuiApplication 20 | from src import main 21 | app = QGuiApplication(sys.argv) 22 | main.run(app, dir_name, mode) 23 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyside2/qml/src/__init__.py -------------------------------------------------------------------------------- /python/pyside2/qml/src/components.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Resource object code 4 | # 5 | # Created: Sun Dec 9 17:03:20 2018 6 | # by: The Resource Compiler for PySide2 (Qt v5.11.2) 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide2 import QtCore 11 | 12 | qt_resource_data = b"\ 13 | \x00\x00\x05.\ 14 | i\ 15 | mport QtQuick 2.\ 16 | 8\x0aimport QtQuick\ 17 | .Layouts 1.3\x0a\x0aRe\ 18 | ctangle {\x0a id\ 19 | : root\x0a ancho\ 20 | rs.fill: parent\x0a\ 21 | \x0a property va\ 22 | riant internalMo\ 23 | del\x0a signal r\ 24 | owChanged\x0a\x0a o\ 25 | nRowChanged: {\x0a \ 26 | //listVie\ 27 | w.positionViewAt\ 28 | End();\x0a l\ 29 | istView.position\ 30 | ViewAtIndex(list\ 31 | View.count - 1, \ 32 | ListView.Beginni\ 33 | ng)\x0a }\x0a\x0a L\ 34 | istView {\x0a \ 35 | id: listView\x0a \ 36 | anchors.f\ 37 | ill: parent\x0a \ 38 | contentWidth\ 39 | : 320\x0a cl\ 40 | ip: true\x0a\x0a \ 41 | model: parent.\ 42 | internalModel\x0a\x0a \ 43 | delegate:\ 44 | listDelegate\x0a \ 45 | }\x0a\x0a Compone\ 46 | nt {\x0a id:\ 47 | listDelegate\x0a\x0a \ 48 | Rectangle\ 49 | {\x0a w\ 50 | idth: root.width\ 51 | \x0a hei\ 52 | ght: 40\x0a \ 53 | color: index\ 54 | % 2 == 0 ? \x22whi\ 55 | te\x22 : \x22lightgray\ 56 | \x22\x0a\x0a G\ 57 | ridLayout {\x0a \ 58 | anch\ 59 | ors.fill: parent\ 60 | \x0a \ 61 | columns: 2\x0a \ 62 | colu\ 63 | mnSpacing: (pare\ 64 | nt.width - itemT\ 65 | ext.contentWidth\ 66 | - dataText.cont\ 67 | entWidth) - 10\x0a\x0a\ 68 | \ 69 | Text {\x0a \ 70 | id: i\ 71 | temText\x0a \ 72 | text\ 73 | : model.item\x0a \ 74 | \ 75 | font.bold: true\ 76 | \x0a \ 77 | font.pixelS\ 78 | ize: 20\x0a \ 79 | }\x0a\x0a \ 80 | Text \ 81 | {\x0a \ 82 | id: dataTe\ 83 | xt\x0a \ 84 | text: mod\ 85 | el.data\x0a \ 86 | font\ 87 | .pixelSize: 20\x0a \ 88 | \ 89 | color: \x22red\x22\x0a\ 90 | \ 91 | }\x0a }\x0a\ 92 | }\x0a }\x0a\ 93 | \x0a Component.o\ 94 | nCompleted: {\x0a \ 95 | listView.p\ 96 | ositionViewAtEnd\ 97 | ();\x0a }\x0a}\x0a\x0a\ 98 | \x00\x00\x09!\ 99 | i\ 100 | mport QtQuick 2.\ 101 | 7\x0aimport QtQuick\ 102 | .Controls 2.2\x0aim\ 103 | port QtGraphical\ 104 | Effects 1.0\x0a\x0aPro\ 105 | gressBar {\x0a i\ 106 | d: progressBar\x0a\x0a\ 107 | value: 0\x0a \ 108 | clip: true\x0a\x0a \ 109 | background: Rec\ 110 | tangle {\x0a \ 111 | id: barFrame\x0a \ 112 | implicitWi\ 113 | dth: 300\x0a \ 114 | implicitHeight:\ 115 | 20\x0a bord\ 116 | er.color: \x22#9999\ 117 | 99\x22\x0a radi\ 118 | us: 5\x0a }\x0a\x0a \ 119 | contentItem: It\ 120 | em {\x0a id:\ 121 | item\x0a im\ 122 | plicitWidth: 300\ 123 | \x0a implici\ 124 | tHeight: 18\x0a\x0a \ 125 | Rectangle {\ 126 | \x0a id:\ 127 | bar\x0a \ 128 | width: progress\ 129 | Bar.visualPositi\ 130 | on * parent.widt\ 131 | h\x0a he\ 132 | ight: parent.hei\ 133 | ght\x0a \ 134 | radius: barFrame\ 135 | .radius\x0a\x0a \ 136 | /*\x0a \ 137 | Text {\x0a \ 138 | text:\ 139 | progressBar.val\ 140 | ue + '%'\x0a \ 141 | font.po\ 142 | intSize: 12\x0a \ 143 | font\ 144 | .weight: Font.Bo\ 145 | ld\x0a \ 146 | font.bold: tr\ 147 | ue\x0a \ 148 | color: 'black\ 149 | '\x0a }\x0a\ 150 | */\x0a \ 151 | }\x0a\x0a \ 152 | // \xe5\xb7\xa6\xe5\x8f\xb3\xe6\xbc\xb8\xe5\xb1\ 153 | \xa4\xe9\xa1\x8f\xe8\x89\xb2(\xe7\xb6\xa0 -> \xe4\ 154 | \xba\xae\xe7\xb6\xa0 -> \xe7\xb6\xa0)\x0a \ 155 | LinearGrad\ 156 | ient {\x0a \ 157 | anchors.fill:\ 158 | bar\x0a \ 159 | start: Qt.point\ 160 | (0, 0)\x0a \ 161 | end: Qt.point\ 162 | (bar.width, 0)\x0a \ 163 | sourc\ 164 | e: bar\x0a \ 165 | opacity: 0.9\x0a\ 166 | \x0a gra\ 167 | dient: Gradient \ 168 | {\x0a \ 169 | GradientStop {\ 170 | position: 0.0; \ 171 | color: \x22#17a81a\x22\ 172 | }\x0a \ 173 | GradientStop \ 174 | { id: grad; posi\ 175 | tion: 0.5; color\ 176 | : Qt.lighter(\x22#1\ 177 | 7a81a\x22, 2) }\x0a \ 178 | Gra\ 179 | dientStop { posi\ 180 | tion: 1.0; color\ 181 | : \x22#17a81a\x22 }\x0a \ 182 | }\x0a\x0a \ 183 | /*\x0a \ 184 | Property\ 185 | Animation {\x0a \ 186 | targ\ 187 | et: grad\x0a \ 188 | propert\ 189 | y: \x22position\x22\x0a \ 190 | fr\ 191 | om: 0.1\x0a \ 192 | to: 0.9\x0a\ 193 | \ 194 | duration: 1000\x0a \ 195 | r\ 196 | unning: true\x0a \ 197 | loo\ 198 | ps: Animation.In\ 199 | finite\x0a \ 200 | }\x0a \ 201 | */\x0a\x0a }\x0a\x0a\ 202 | // \xe4\xb8\x8a\xe4\xb8\ 203 | \x8b\xe6\xbc\xb8\xe5\xb1\xa4\xe9\xa1\x8f\xe8\x89\xb2\x0a \ 204 | LinearGrad\ 205 | ient {\x0a \ 206 | anchors.fill:\ 207 | bar\x0a \ 208 | start: Qt.point\ 209 | (0, 0)\x0a \ 210 | end: Qt.point\ 211 | (0, bar.height)\x0a\ 212 | sour\ 213 | ce: bar\x0a \ 214 | opacity: 0.9\ 215 | \x0a\x0a gr\ 216 | adient: Gradient\ 217 | {\x0a \ 218 | GradientStop \ 219 | { position: 0.0;\ 220 | color: Qt.rgba(\ 221 | 0,0,0,0) }\x0a \ 222 | Gradi\ 223 | entStop { positi\ 224 | on: 0.5; color: \ 225 | Qt.rgba(1,1,1,0.\ 226 | 3) }\x0a \ 227 | GradientSto\ 228 | p { position: 1.\ 229 | 0; color: Qt.rgb\ 230 | a(0,0,0,0.05) }\x0a\ 231 | }\x0a \ 232 | }\x0a }\x0a\x0a \ 233 | /*\x0a Proper\ 234 | tyAnimation {\x0a \ 235 | target: pr\ 236 | ogressBar\x0a \ 237 | property: \x22val\ 238 | ue\x22\x0a from\ 239 | : 0\x0a to: \ 240 | 1\x0a durati\ 241 | on: 5000\x0a \ 242 | running: true\x0a \ 243 | loops: An\ 244 | imation.Infinite\ 245 | \x0a }\x0a */\x0a}\x0a\ 246 | \ 247 | \x00\x00\x0e\x0c\ 248 | i\ 249 | mport QtQuick 2.\ 250 | 0\x0aimport QtQml 2\ 251 | .2\x0a\x0aItem {\x0a i\ 252 | d: root\x0a\x0a wid\ 253 | th: radius * 2\x0a \ 254 | height: radiu\ 255 | s * 2\x0a\x0a prope\ 256 | rty int radius: \ 257 | 100\x0a property\ 258 | real arcBegin: \ 259 | 0 // \ 260 | start arc angle \ 261 | in degree\x0a pr\ 262 | operty real arcE\ 263 | nd: 180 \ 264 | // end arc an\ 265 | gle in degree\x0a \ 266 | property real \ 267 | arcOffset: 0 \ 268 | // rotati\ 269 | on\x0a property \ 270 | real minValue: 0\ 271 | \x0a property re\ 272 | al maxValue: 100\ 273 | \x0a property re\ 274 | al value: 50\x0a \ 275 | property string\ 276 | text: \x22%\x22\x0a p\ 277 | roperty bool isV\ 278 | alue: true\x0a p\ 279 | roperty bool sho\ 280 | wBackground: fal\ 281 | se // a full ci\ 282 | rcle as a backgr\ 283 | ound of the arc\x0a\ 284 | property rea\ 285 | l lineWidth: 20 \ 286 | // widt\ 287 | h of the line\x0a \ 288 | property strin\ 289 | g circleColor: \x22\ 290 | #1dc58f\x22\x0a pro\ 291 | perty string bac\ 292 | kgroundColor: \x22#\ 293 | E6E6E6\x22\x0a prop\ 294 | erty string text\ 295 | Color: \x22#FFFFFF\x22\ 296 | \x0a\x0a property a\ 297 | lias beginAnimat\ 298 | ion: animationAr\ 299 | cBegin.enabled\x0a \ 300 | property alia\ 301 | s endAnimation: \ 302 | animationArcEnd.\ 303 | enabled\x0a prop\ 304 | erty alias value\ 305 | Animation: anima\ 306 | tionValue.enable\ 307 | d\x0a\x0a property \ 308 | int animationDur\ 309 | ation: 200\x0a\x0a \ 310 | onArcBeginChange\ 311 | d: canvas.reques\ 312 | tPaint()\x0a onA\ 313 | rcEndChanged: ca\ 314 | nvas.requestPain\ 315 | t()\x0a onValueC\ 316 | hanged: canvas.r\ 317 | equestPaint()\x0a\x0a \ 318 | Behavior on a\ 319 | rcBegin {\x0a \ 320 | id: animationAr\ 321 | cBegin\x0a en\ 322 | abled: true\x0a \ 323 | NumberAnimati\ 324 | on {\x0a \ 325 | duration: root.a\ 326 | nimationDuration\ 327 | \x0a easi\ 328 | ng.type: Easing.\ 329 | InOutCubic\x0a \ 330 | }\x0a }\x0a\x0a B\ 331 | ehavior on arcEn\ 332 | d {\x0a id: a\ 333 | nimationArcEnd\x0a \ 334 | enabled: t\ 335 | rue\x0a Numbe\ 336 | rAnimation {\x0a \ 337 | duration\ 338 | : root.animation\ 339 | Duration\x0a \ 340 | easing.type:\ 341 | Easing.InOutCub\ 342 | ic\x0a }\x0a \ 343 | }\x0a\x0a Behavior \ 344 | on value {\x0a \ 345 | id: animation\ 346 | Value\x0a en\ 347 | abled: true\x0a \ 348 | NumberAnimat\ 349 | ion {\x0a \ 350 | duration: root\ 351 | .animationDurati\ 352 | on\x0a e\ 353 | asing.type: Easi\ 354 | ng.InOutCubic\x0a \ 355 | }\x0a }\x0a\x0a \ 356 | Canvas {\x0a \ 357 | id: canvas\x0a \ 358 | anchors.f\ 359 | ill: parent\x0a \ 360 | rotation: -9\ 361 | 0 + parent.arcOf\ 362 | fset\x0a\x0a on\ 363 | Paint: {\x0a \ 364 | var ctx = g\ 365 | etContext(\x222d\x22)\x0a\ 366 | var \ 367 | x = root.radius\x0a\ 368 | var \ 369 | y = root.radius\x0a\ 370 | var \ 371 | angle = (value -\ 372 | minValue) / (ma\ 373 | xValue - minValu\ 374 | e) * 2 * Math.PI\ 375 | \x0a var\ 376 | start = Math.PI\ 377 | * (parent.arcBe\ 378 | gin / 180)\x0a \ 379 | var end =\ 380 | Math.PI * (pare\ 381 | nt.arcEnd / 180)\ 382 | \x0a ctx\ 383 | .reset()\x0a\x0a \ 384 | if (root.i\ 385 | sValue) {\x0a \ 386 | if (ro\ 387 | ot.showBackgroun\ 388 | d) {\x0a \ 389 | ctx.beg\ 390 | inPath();\x0a \ 391 | //\ 392 | arc(real x, rea\ 393 | l y, real radius\ 394 | , real startAngl\ 395 | e, real endAngle\ 396 | , bool anticlock\ 397 | wise)\x0a \ 398 | ctx.ar\ 399 | c(x, y, radius -\ 400 | root.lineWidth \ 401 | / 2, 0, Math.PI \ 402 | * 2, false)\x0a \ 403 | \ 404 | ctx.lineWidth = \ 405 | root.lineWidth\x0a \ 406 | \ 407 | ctx.strokeSty\ 408 | le = root.backgr\ 409 | oundColor\x0a \ 410 | ct\ 411 | x.stroke()\x0a \ 412 | }\x0a \ 413 | ctx\ 414 | .beginPath();\x0a \ 415 | ct\ 416 | x.arc(x, y, radi\ 417 | us - root.lineWi\ 418 | dth / 2, 0, angl\ 419 | e, false)\x0a \ 420 | ctx.li\ 421 | neWidth = root.l\ 422 | ineWidth\x0a \ 423 | ctx.str\ 424 | okeStyle = root.\ 425 | circleColor\x0a \ 426 | ctx.\ 427 | stroke()\x0a \ 428 | } else {\x0a \ 429 | if\ 430 | (root.showBackg\ 431 | round) {\x0a \ 432 | ctx\ 433 | .beginPath();\x0a \ 434 | \ 435 | ctx.arc(x, y, \ 436 | radius - root.li\ 437 | neWidth / 2, 0, \ 438 | Math.PI * 2, fal\ 439 | se)\x0a \ 440 | ctx.line\ 441 | Width = root.lin\ 442 | eWidth\x0a \ 443 | ctx.s\ 444 | trokeStyle = roo\ 445 | t.backgroundColo\ 446 | r\x0a \ 447 | ctx.stroke\ 448 | ()\x0a \ 449 | }\x0a \ 450 | ctx.beginPa\ 451 | th();\x0a \ 452 | ctx.arc(x,\ 453 | y, radius - roo\ 454 | t.lineWidth / 2,\ 455 | start, end, fal\ 456 | se)\x0a \ 457 | ctx.lineWidt\ 458 | h = root.lineWid\ 459 | th\x0a \ 460 | ctx.strokeSty\ 461 | le = root.circle\ 462 | Color\x0a \ 463 | ctx.stroke\ 464 | ()\x0a }\ 465 | \x0a }\x0a }\ 466 | \x0a\x0a Text {\x0a \ 467 | anchors.cen\ 468 | terIn: parent\x0a\x0a \ 469 | text: roo\ 470 | t.value + root.t\ 471 | ext\x0a colo\ 472 | r: root.textColo\ 473 | r\x0a\x0a }\x0a}\x0a\ 474 | \x00\x00\x00\xa5\ 475 | m\ 476 | odule components\ 477 | .common\x0a\x0aCircleP\ 478 | rogress 1.0 Circ\ 479 | leProgress.qml\x0aC\ 480 | lock 1.\ 481 | 0 Clock.qml\x0aKaka\ 482 | Progress 1.0 K\ 483 | akaProgress.qml\x0a\ 484 | KakaListView 1\ 485 | .0 KakaListView.\ 486 | qml\x0a\ 487 | \x00\x00\x01\xf9\ 488 | i\ 489 | mport QtQuick 2.\ 490 | 0\x0a\x0aRectangle {\x0a \ 491 | width: 100\x0a \ 492 | height: 20\x0a\x0a \ 493 | property real \ 494 | currentTimestamp\ 495 | \x0a\x0a function u\ 496 | pdateTime() {\x0a \ 497 | var now = \ 498 | new Date();\x0a \ 499 | currentTimes\ 500 | tamp = now.getTi\ 501 | me();\x0a }\x0a\x0a \ 502 | Timer {\x0a \ 503 | interval: 1000\x0a\ 504 | repeat: \ 505 | true\x0a run\ 506 | ning: true\x0a \ 507 | onTriggered: \ 508 | { updateTime(); \ 509 | }\x0a }\x0a\x0a Tex\ 510 | t {\x0a text\ 511 | : Qt.formatTime \ 512 | (new Date (curre\ 513 | ntTimestamp + (0\ 514 | * 60000)), \x22A h\ 515 | h:mm\x22)\x0a f\ 516 | ont.pixelSize: 1\ 517 | 8\x0a }\x0a\x0a Com\ 518 | ponent.onComplet\ 519 | ed: { updateTime\ 520 | (); }\x0a}\x0a\ 521 | " 522 | 523 | qt_resource_name = b"\ 524 | \x00\x0a\ 525 | \x07j\x093\ 526 | \x00c\ 527 | \x00o\x00m\x00p\x00o\x00n\x00e\x00n\x00t\x00s\ 528 | \x00\x06\ 529 | \x06\xa6D^\ 530 | \x00c\ 531 | \x00o\x00m\x00m\x00o\x00n\ 532 | \x00\x10\ 533 | \x03\xbfg\xfc\ 534 | \x00K\ 535 | \x00a\x00k\x00a\x00L\x00i\x00s\x00t\x00V\x00i\x00e\x00w\x00.\x00q\x00m\x00l\ 536 | \x00\x10\ 537 | \x00D\x8d|\ 538 | \x00K\ 539 | \x00a\x00k\x00a\x00P\x00r\x00o\x00g\x00r\x00e\x00s\x00s\x00.\x00q\x00m\x00l\ 540 | \x00\x12\ 541 | \x0a\xd6\xc5\xbc\ 542 | \x00C\ 543 | \x00i\x00r\x00c\x00l\x00e\x00P\x00r\x00o\x00g\x00r\x00e\x00s\x00s\x00.\x00q\x00m\ 544 | \x00l\ 545 | \x00\x06\ 546 | \x07\x84+\x02\ 547 | \x00q\ 548 | \x00m\x00l\x00d\x00i\x00r\ 549 | \x00\x09\ 550 | \x05\x9e\xc4\x5c\ 551 | \x00C\ 552 | \x00l\x00o\x00c\x00k\x00.\x00q\x00m\x00l\ 553 | " 554 | 555 | qt_resource_struct = b"\ 556 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ 557 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ 558 | \x00\x00\x00\x1a\x00\x02\x00\x00\x00\x05\x00\x00\x00\x03\ 559 | \x00\x00\x00R\x00\x00\x00\x00\x00\x01\x00\x00\x052\ 560 | \x00\x00\x00,\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ 561 | \x00\x00\x00\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x1d\x10\ 562 | \x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x1cg\ 563 | \x00\x00\x00x\x00\x00\x00\x00\x00\x01\x00\x00\x0eW\ 564 | " 565 | 566 | def qInitResources(): 567 | QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) 568 | 569 | def qCleanupResources(): 570 | QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) 571 | 572 | qInitResources() 573 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from PySide2.QtQml import QQmlApplicationEngine 5 | from PySide2.QtCore import QCoreApplication, QUrl, Qt 6 | 7 | from src import qml, components 8 | 9 | def run(app, dir_name, mode): 10 | # Create QML engine 11 | engine = QQmlApplicationEngine() 12 | context = engine.rootContext() 13 | 14 | if mode == "prod": 15 | engine.addImportPath('qrc:/') 16 | engine.load(QUrl('qrc:/main.qml')) 17 | else: 18 | engine.addImportPath(os.path.join(dir_name, "src/resources")) 19 | engine.load(QUrl(os.path.join(dir_name, "src/resources/main.qml"))) 20 | 21 | engine.quit.connect(app.quit) 22 | sys.exit(app.exec_()) 23 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/components/common/CircleProgress.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | 4 | Item { 5 | id: root 6 | 7 | width: radius * 2 8 | height: radius * 2 9 | 10 | property int radius: 100 11 | property real arcBegin: 0 // start arc angle in degree 12 | property real arcEnd: 180 // end arc angle in degree 13 | property real arcOffset: 0 // rotation 14 | property real minValue: 0 15 | property real maxValue: 100 16 | property real value: 50 17 | property string text: "%" 18 | property bool isValue: true 19 | property bool showBackground: false // a full circle as a background of the arc 20 | property real lineWidth: 20 // width of the line 21 | property string circleColor: "#1dc58f" 22 | property string backgroundColor: "#E6E6E6" 23 | property string textColor: "#FFFFFF" 24 | 25 | property alias beginAnimation: animationArcBegin.enabled 26 | property alias endAnimation: animationArcEnd.enabled 27 | property alias valueAnimation: animationValue.enabled 28 | 29 | property int animationDuration: 200 30 | 31 | onArcBeginChanged: canvas.requestPaint() 32 | onArcEndChanged: canvas.requestPaint() 33 | onValueChanged: canvas.requestPaint() 34 | 35 | Behavior on arcBegin { 36 | id: animationArcBegin 37 | enabled: true 38 | NumberAnimation { 39 | duration: root.animationDuration 40 | easing.type: Easing.InOutCubic 41 | } 42 | } 43 | 44 | Behavior on arcEnd { 45 | id: animationArcEnd 46 | enabled: true 47 | NumberAnimation { 48 | duration: root.animationDuration 49 | easing.type: Easing.InOutCubic 50 | } 51 | } 52 | 53 | Behavior on value { 54 | id: animationValue 55 | enabled: true 56 | NumberAnimation { 57 | duration: root.animationDuration 58 | easing.type: Easing.InOutCubic 59 | } 60 | } 61 | 62 | Canvas { 63 | id: canvas 64 | anchors.fill: parent 65 | rotation: -90 + parent.arcOffset 66 | 67 | onPaint: { 68 | var ctx = getContext("2d") 69 | var x = root.radius 70 | var y = root.radius 71 | var angle = (value - minValue) / (maxValue - minValue) * 2 * Math.PI 72 | var start = Math.PI * (parent.arcBegin / 180) 73 | var end = Math.PI * (parent.arcEnd / 180) 74 | ctx.reset() 75 | 76 | if (root.isValue) { 77 | if (root.showBackground) { 78 | ctx.beginPath(); 79 | // arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise) 80 | ctx.arc(x, y, radius - root.lineWidth / 2, 0, Math.PI * 2, false) 81 | ctx.lineWidth = root.lineWidth 82 | ctx.strokeStyle = root.backgroundColor 83 | ctx.stroke() 84 | } 85 | ctx.beginPath(); 86 | ctx.arc(x, y, radius - root.lineWidth / 2, 0, angle, false) 87 | ctx.lineWidth = root.lineWidth 88 | ctx.strokeStyle = root.circleColor 89 | ctx.stroke() 90 | } else { 91 | if (root.showBackground) { 92 | ctx.beginPath(); 93 | ctx.arc(x, y, radius - root.lineWidth / 2, 0, Math.PI * 2, false) 94 | ctx.lineWidth = root.lineWidth 95 | ctx.strokeStyle = root.backgroundColor 96 | ctx.stroke() 97 | } 98 | ctx.beginPath(); 99 | ctx.arc(x, y, radius - root.lineWidth / 2, start, end, false) 100 | ctx.lineWidth = root.lineWidth 101 | ctx.strokeStyle = root.circleColor 102 | ctx.stroke() 103 | } 104 | } 105 | } 106 | 107 | Text { 108 | anchors.centerIn: parent 109 | 110 | text: root.value + root.text 111 | color: root.textColor 112 | 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/components/common/Clock.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Rectangle { 4 | width: 100 5 | height: 20 6 | 7 | property real currentTimestamp 8 | 9 | function updateTime() { 10 | var now = new Date(); 11 | currentTimestamp = now.getTime(); 12 | } 13 | 14 | Timer { 15 | interval: 1000 16 | repeat: true 17 | running: true 18 | onTriggered: { updateTime(); } 19 | } 20 | 21 | Text { 22 | text: Qt.formatTime (new Date (currentTimestamp + (0 * 60000)), "A hh:mm") 23 | font.pixelSize: 18 24 | } 25 | 26 | Component.onCompleted: { updateTime(); } 27 | } 28 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/components/common/KakaListView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Layouts 1.3 3 | 4 | Rectangle { 5 | id: root 6 | anchors.fill: parent 7 | 8 | property variant internalModel 9 | signal rowChanged 10 | 11 | onRowChanged: { 12 | //listView.positionViewAtEnd(); 13 | listView.positionViewAtIndex(listView.count - 1, ListView.Beginning) 14 | } 15 | 16 | ListView { 17 | id: listView 18 | anchors.fill: parent 19 | contentWidth: 320 20 | clip: true 21 | 22 | model: parent.internalModel 23 | 24 | delegate: listDelegate 25 | } 26 | 27 | Component { 28 | id: listDelegate 29 | 30 | Rectangle { 31 | width: root.width 32 | height: 40 33 | color: index % 2 == 0 ? "white" : "lightgray" 34 | 35 | GridLayout { 36 | anchors.fill: parent 37 | columns: 2 38 | columnSpacing: (parent.width - itemText.contentWidth - dataText.contentWidth) - 10 39 | 40 | Text { 41 | id: itemText 42 | text: model.item 43 | font.bold: true 44 | font.pixelSize: 20 45 | } 46 | 47 | Text { 48 | id: dataText 49 | text: model.data 50 | font.pixelSize: 20 51 | color: "red" 52 | } 53 | } 54 | } 55 | } 56 | 57 | Component.onCompleted: { 58 | listView.positionViewAtEnd(); 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/components/common/KakaProgress.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.2 3 | import QtGraphicalEffects 1.0 4 | 5 | ProgressBar { 6 | id: progressBar 7 | 8 | value: 0 9 | clip: true 10 | 11 | background: Rectangle { 12 | id: barFrame 13 | implicitWidth: 300 14 | implicitHeight: 20 15 | border.color: "#999999" 16 | radius: 5 17 | } 18 | 19 | contentItem: Item { 20 | id: item 21 | implicitWidth: 300 22 | implicitHeight: 18 23 | 24 | Rectangle { 25 | id: bar 26 | width: progressBar.visualPosition * parent.width 27 | height: parent.height 28 | radius: barFrame.radius 29 | 30 | /* 31 | Text { 32 | text: progressBar.value + '%' 33 | font.pointSize: 12 34 | font.weight: Font.Bold 35 | font.bold: true 36 | color: 'black' 37 | } 38 | */ 39 | } 40 | 41 | // 左右漸層顏色(綠 -> 亮綠 -> 綠) 42 | LinearGradient { 43 | anchors.fill: bar 44 | start: Qt.point(0, 0) 45 | end: Qt.point(bar.width, 0) 46 | source: bar 47 | opacity: 0.9 48 | 49 | gradient: Gradient { 50 | GradientStop { position: 0.0; color: "#17a81a" } 51 | GradientStop { id: grad; position: 0.5; color: Qt.lighter("#17a81a", 2) } 52 | GradientStop { position: 1.0; color: "#17a81a" } 53 | } 54 | 55 | /* 56 | PropertyAnimation { 57 | target: grad 58 | property: "position" 59 | from: 0.1 60 | to: 0.9 61 | duration: 1000 62 | running: true 63 | loops: Animation.Infinite 64 | } 65 | */ 66 | 67 | } 68 | 69 | // 上下漸層顏色 70 | LinearGradient { 71 | anchors.fill: bar 72 | start: Qt.point(0, 0) 73 | end: Qt.point(0, bar.height) 74 | source: bar 75 | opacity: 0.9 76 | 77 | gradient: Gradient { 78 | GradientStop { position: 0.0; color: Qt.rgba(0,0,0,0) } 79 | GradientStop { position: 0.5; color: Qt.rgba(1,1,1,0.3) } 80 | GradientStop { position: 1.0; color: Qt.rgba(0,0,0,0.05) } 81 | } 82 | } 83 | } 84 | 85 | /* 86 | PropertyAnimation { 87 | target: progressBar 88 | property: "value" 89 | from: 0 90 | to: 1 91 | duration: 5000 92 | running: true 93 | loops: Animation.Infinite 94 | } 95 | */ 96 | } 97 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/components/common/qmldir: -------------------------------------------------------------------------------- 1 | module components.common 2 | 3 | CircleProgress 1.0 CircleProgress.qml 4 | Clock 1.0 Clock.qml 5 | KakaProgress 1.0 KakaProgress.qml 6 | KakaListView 1.0 KakaListView.qml 7 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/components/components.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | common/CircleProgress.qml 4 | common/Clock.qml 5 | common/KakaProgress.qml 6 | common/KakaListView.qml 7 | common/qmldir 8 | 9 | 10 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/images/about.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyside2/qml/src/resources/images/about.ico -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/images/exit.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyside2/qml/src/resources/images/exit.ico -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/images/newFile.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyside2/qml/src/resources/images/newFile.ico -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Controls 2.1 3 | import QtQuick.Layouts 1.3 4 | import QtQuick.Dialogs 1.2 5 | 6 | import "tabs" 7 | import components.common 1.0 8 | 9 | ApplicationWindow { 10 | id: window 11 | visible: true 12 | minimumWidth: 640 13 | minimumHeight: 480 14 | 15 | title: qsTr("Qml Template") 16 | 17 | ////////////////////////////////////////////////////////////////////////// 18 | // menu 19 | header: ToolBar { 20 | id: menu 21 | 22 | background: Rectangle { 23 | implicitWidth: 100 24 | implicitHeight: 50 25 | border.color: "#999" 26 | 27 | gradient: Gradient { 28 | GradientStop { position: 0 ; color: "#fff" } 29 | GradientStop { position: 1 ; color: "#eee" } 30 | } 31 | } 32 | 33 | Row { 34 | anchors.fill: parent 35 | spacing: 5 36 | 37 | ToolButton { 38 | Image { 39 | id: newFileImage 40 | source: "images/newFile.ico" 41 | asynchronous:true 42 | fillMode: Image.PreserveAspectFit 43 | anchors.fill: parent 44 | } 45 | anchors.verticalCenter: parent.verticalCenter 46 | onClicked: fileDialog.open(); 47 | } 48 | 49 | ToolButton { 50 | Image { 51 | id: aboutImage 52 | source: "images/about.ico" 53 | asynchronous:true 54 | fillMode: Image.PreserveAspectFit 55 | anchors.fill: parent 56 | } 57 | anchors.verticalCenter: parent.verticalCenter 58 | onClicked: aboutBox.open(); 59 | } 60 | 61 | ToolButton { 62 | Image { 63 | id: exitImage 64 | source: "images/exit.ico" 65 | asynchronous:true 66 | fillMode: Image.PreserveAspectFit 67 | anchors.fill: parent 68 | } 69 | anchors.verticalCenter: parent.verticalCenter 70 | onClicked: { 71 | Qt.quit(); 72 | } 73 | } 74 | } 75 | 76 | Clock { 77 | id: clock 78 | anchors.right: parent.right 79 | anchors.verticalCenter: parent.verticalCenter 80 | 81 | gradient: Gradient { 82 | GradientStop { position: 0 ; color: "#fff" } 83 | GradientStop { position: 1 ; color: "#eee" } 84 | } 85 | } 86 | } 87 | 88 | MessageDialog { 89 | id: aboutBox 90 | title: "About" 91 | text: " 92 | This is QML Template\n 93 | Version: 0.1 94 | Date:2018/10/11" 95 | icon: StandardIcon.Information 96 | } 97 | 98 | FileDialog { 99 | id: fileDialog 100 | visible: false 101 | title: "Please choose a file" 102 | folder: shortcuts.home 103 | selectFolder: true 104 | } 105 | ////////////////////////////////////////////////////////////////////////// 106 | // footer 107 | 108 | footer: TabBar { 109 | id: tabBar 110 | width: parent.width 111 | height: 40 112 | currentIndex: 0 113 | 114 | TabButton { 115 | text: qsTr("Tab1") 116 | onClicked: { 117 | // 118 | } 119 | } 120 | 121 | TabButton { 122 | text: qsTr("Tab2") 123 | onClicked: { 124 | // 125 | } 126 | } 127 | } 128 | 129 | StackLayout { 130 | id: layout 131 | anchors.fill: parent 132 | currentIndex: tabBar.currentIndex 133 | 134 | Tab1 { 135 | id: aTab 136 | } 137 | 138 | Tab2 { 139 | id: bTab 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | tabs/Tab1.qml 6 | tabs/Tab2.qml 7 | 8 | images/about.ico 9 | images/exit.ico 10 | images/newFile.ico 11 | 12 | 13 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/tabs/Tab1.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Controls 2.1 3 | import QtQuick.Layouts 1.3 4 | 5 | Page { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /python/pyside2/qml/src/resources/tabs/Tab2.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Controls 2.1 3 | import QtQuick.Layouts 1.3 4 | 5 | Page { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /python/pyside2/qt/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.qml] 16 | indent_size = 4 17 | 18 | [*.js] 19 | indent_size = 2 20 | 21 | # Tab indentation (no size specified) 22 | [Makefile] 23 | indent_style = tab 24 | -------------------------------------------------------------------------------- /python/pyside2/qt/.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Mac 5 | .DS_Store 6 | ._.DS_Store 7 | 8 | # pyenv 9 | venv/ 10 | .venv/ 11 | 12 | # python 13 | *.pyc 14 | *.pro 15 | *~ 16 | __pycache__ 17 | 18 | # QML 19 | *.qmlc 20 | -------------------------------------------------------------------------------- /python/pyside2/qt/README.md: -------------------------------------------------------------------------------- 1 | # QML Template 2 | 3 | ## Usage 4 | 5 | ```bash 6 | $ python3 main.py 7 | ``` 8 | -------------------------------------------------------------------------------- /python/pyside2/qt/gui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/python/pyside2/qt/gui/__init__.py -------------------------------------------------------------------------------- /python/pyside2/qt/gui/app.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide2.QtCore import QTranslator, QLocale 3 | from PySide2.QtWidgets import QApplication 4 | from gui.mainwindow import MainWindow 5 | 6 | def run(): 7 | 8 | translator = QTranslator() 9 | #translator.load(QLocale, 'main', '_', './i18n') 10 | 11 | app = QApplication(sys.argv) 12 | 13 | main_window = MainWindow(app, translator) 14 | main_window.show() 15 | 16 | sys.exit(app.exec_()) 17 | 18 | if __name__ == '__main__': 19 | run() 20 | -------------------------------------------------------------------------------- /python/pyside2/qt/gui/hellobox.py: -------------------------------------------------------------------------------- 1 | 2 | from PySide2 import QtCore, QtGui, QtWidgets 3 | 4 | from gui.ui_hellobox import Ui_HelloBox 5 | 6 | class HelloBox(QtWidgets.QGroupBox): 7 | """ Template box """ 8 | def __init__(self, parent=None): 9 | super(HelloBox, self).__init__(parent) 10 | 11 | self.ui = Ui_HelloBox() 12 | self.ui.setupUi(self) 13 | self._setup_ui() 14 | 15 | def _setup_ui(self): 16 | """ """ 17 | 18 | def onClick(self): 19 | """ """ 20 | self.ui.label.setText("Button clicked") 21 | -------------------------------------------------------------------------------- /python/pyside2/qt/gui/hellotab.py: -------------------------------------------------------------------------------- 1 | from PySide2 import QtCore, QtGui, QtWidgets 2 | from gui.hellobox import HelloBox 3 | 4 | class HelloTab(QtWidgets.QVBoxLayout): 5 | def __init__(self, parent=None): 6 | super(HelloTab, self).__init__(parent) 7 | 8 | self.hello_box = HelloBox() 9 | 10 | self.addWidget(self.hello_box) 11 | 12 | self._setup_ui() 13 | 14 | def _setup_ui(self): 15 | self.retranslateUi() 16 | 17 | def retranslateUi(self): 18 | _translate = QtCore.QCoreApplication.translate 19 | -------------------------------------------------------------------------------- /python/pyside2/qt/gui/mainwindow.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from PySide2.QtWidgets import QMainWindow, QFileDialog 4 | from gui.ui_mainwindow import Ui_MainWindow 5 | from gui.hellotab import HelloTab 6 | 7 | class MainWindow(QMainWindow): 8 | def __init__ (self, app, translator, parent=None): 9 | super(MainWindow, self).__init__(parent) 10 | 11 | self._app = app 12 | self._translator = translator 13 | app.installTranslator(translator) 14 | 15 | self.ui = Ui_MainWindow() 16 | self.ui.setupUi(self) 17 | self._setup_ui() 18 | 19 | def _setup_ui(self): 20 | ''' ''' 21 | self._tab = HelloTab(self.ui.tab) 22 | 23 | def openFile(self): 24 | dir = os.path.dirname(__file__) 25 | file_name = QFileDialog.getOpenFileName(self, 'Open file', dir) 26 | -------------------------------------------------------------------------------- /python/pyside2/qt/gui/ui_hellobox.py: -------------------------------------------------------------------------------- 1 | from PySide2 import QtCore, QtGui, QtWidgets 2 | from PySide2.QtWidgets import QFileDialog, QSizePolicy 3 | 4 | class Ui_HelloBox(object): 5 | def setupUi(self, helloBox): 6 | helloBox.setObjectName("helloBox") 7 | 8 | self.gridLayout = QtWidgets.QGridLayout(helloBox) 9 | self.gridLayout.setHorizontalSpacing(12) 10 | self.gridLayout.setVerticalSpacing(12) 11 | 12 | self.button = QtWidgets.QPushButton(helloBox) 13 | self.button.setObjectName("button") 14 | self.label = QtWidgets.QLabel(helloBox) 15 | self.label.setObjectName("label") 16 | 17 | self.gridLayout.addWidget(self.label, 0, 0, 1, 1) 18 | self.gridLayout.addWidget(self.button, 0, 1, 1, 1) 19 | 20 | self.retranslateUi(helloBox) 21 | 22 | self.button.clicked.connect(helloBox.onClick) 23 | 24 | def retranslateUi(self, helloBox): 25 | _translate = QtCore.QCoreApplication.translate 26 | helloBox.setTitle(_translate("helloBox", "Hello Box")) 27 | self.button.setText(_translate("VideoBox", "Button")) 28 | self.label.setText(_translate("helloBox", "Hello World")) 29 | -------------------------------------------------------------------------------- /python/pyside2/qt/gui/ui_mainwindow.py: -------------------------------------------------------------------------------- 1 | from PySide2 import QtCore, QtGui, QtWidgets 2 | 3 | class Ui_MainWindow(object): 4 | def setupUi(self, MainWindow): 5 | MainWindow.setObjectName("MainWindow") 6 | MainWindow.setMinimumSize(QtCore.QSize(640, 480)) 7 | 8 | self.centralwidget = QtWidgets.QWidget(MainWindow) 9 | self.centralwidget.setObjectName("centralwidget") 10 | self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) 11 | self.verticalLayout.setObjectName("verticalLayout") 12 | self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) 13 | self.tabWidget.setObjectName("tabWidget") 14 | self.tab = QtWidgets.QWidget() 15 | self.tab.setObjectName("tab") 16 | self.tabWidget.addTab(self.tab, "") 17 | self.verticalLayout.addWidget(self.tabWidget) 18 | MainWindow.setCentralWidget(self.centralwidget) 19 | self.menubar = QtWidgets.QMenuBar(MainWindow) 20 | self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 21)) 21 | self.menubar.setObjectName("menubar") 22 | self.menuAbout = QtWidgets.QMenu(self.menubar) 23 | self.menuAbout.setObjectName("menuAbout") 24 | self.menuFile = QtWidgets.QMenu(self.menubar) 25 | self.menuFile.setObjectName("menuFile") 26 | MainWindow.setMenuBar(self.menubar) 27 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 28 | self.statusbar.setObjectName("statusbar") 29 | MainWindow.setStatusBar(self.statusbar) 30 | self.actionAbout = QtWidgets.QAction(MainWindow) 31 | self.actionAbout.setObjectName("actionAbout") 32 | self.actionOpen = QtWidgets.QAction(MainWindow) 33 | self.actionOpen.setObjectName("actionOpen") 34 | self.menuAbout.addAction(self.actionAbout) 35 | self.menuFile.addAction(self.actionOpen) 36 | self.menubar.addAction(self.menuFile.menuAction()) 37 | self.menubar.addAction(self.menuAbout.menuAction()) 38 | 39 | self.actionOpen.triggered.connect(MainWindow.openFile) 40 | 41 | self.retranslateUi(MainWindow) 42 | self.tabWidget.setCurrentIndex(0) 43 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 44 | 45 | def retranslateUi(self, MainWindow): 46 | _translate = QtCore.QCoreApplication.translate 47 | MainWindow.setWindowTitle(_translate("MainWindow", "Qt Template")) 48 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab")) 49 | self.menuAbout.setTitle(_translate("MainWindow", "&Help")) 50 | self.menuFile.setTitle(_translate("MainWindow", "&File")) 51 | self.actionAbout.setText(_translate("MainWindow", "About")) 52 | self.actionOpen.setText(_translate("MainWindow", "Open File")) 53 | -------------------------------------------------------------------------------- /python/pyside2/qt/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from gui import app 5 | app.run() 6 | -------------------------------------------------------------------------------- /python/pyside2/qt/requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5==5.11.3 2 | -------------------------------------------------------------------------------- /videos/wids-workshop-2020.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaka-lin/qt-template/aba2a2f609dde1015ecfa1e1b94d8d564a081133/videos/wids-workshop-2020.mp4 --------------------------------------------------------------------------------