Spaces:
Runtime error
Runtime error
Upload 56 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +2 -34
- .github/workflows/codeql.yml +92 -0
- .gitignore +6 -3
- LICENSE +201 -0
- README.md +118 -15
- banner.jpeg +0 -0
- build.py +41 -0
- codes/Demo/pom.xml +54 -0
- codes/Demo/src/main/java/org/cubegpt/54672e99/Main.java +30 -0
- codes/Demo/src/main/resources/config.yml +1 -0
- codes/Demo/src/main/resources/plugin.yml +10 -0
- codes/Demo/target/Demo-1.0-SNAPSHOT.jar +0 -0
- codes/Demo/target/classes/config.yml +1 -0
- codes/Demo/target/classes/org/cubegpt/54672e99/Main.class +0 -0
- codes/Demo/target/classes/plugin.yml +10 -0
- codes/Demo/target/maven-archiver/pom.properties +3 -0
- codes/Demo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +1 -0
- codes/Demo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +1 -0
- codes/ExamplePlugin4/pom.xml +40 -0
- codes/ExamplePlugin4/src/main/java/org/cubegpt/188eba63/Main.java +20 -0
- codes/ExamplePlugin4/src/main/resources/config.yml +1 -0
- codes/ExamplePlugin4/src/main/resources/plugin.yml +5 -0
- codes/ExamplePlugin4/target/ExamplePlugin4-1.0-SNAPSHOT.jar +0 -0
- codes/ExamplePlugin4/target/classes/config.yml +1 -0
- codes/ExamplePlugin4/target/classes/org/cubegpt/188eba63/Main.class +0 -0
- codes/ExamplePlugin4/target/classes/plugin.yml +5 -0
- codes/ExamplePlugin4/target/maven-archiver/pom.properties +3 -0
- codes/ExamplePlugin4/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +1 -0
- codes/ExamplePlugin4/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +1 -0
- config.py +60 -0
- config.yaml +90 -0
- console.py +121 -0
- core.py +177 -0
- cube_qgui/__init__.py +18 -0
- cube_qgui/__main__.py +122 -0
- cube_qgui/banner_tools.py +106 -0
- cube_qgui/base_frame.py +286 -0
- cube_qgui/base_tools.py +212 -0
- cube_qgui/factory.py +168 -0
- cube_qgui/manager.py +80 -0
- cube_qgui/notebook_tools.py +952 -0
- cube_qgui/os_tools.py +45 -0
- cube_qgui/resources/demo/panda.jpg +0 -0
- cube_qgui/resources/icon/double_down.png +0 -0
- cube_qgui/resources/icon/double_up.png +0 -0
- cube_qgui/resources/icon/github.png +0 -0
- cube_qgui/resources/icon/play_w.png +0 -0
- cube_qgui/resources/icon/up_cloud.png +0 -0
- cube_qgui/theme/ttkbootstrap_themes.json +104 -0
- cube_qgui/third_party/__init__.py +4 -0
.gitattributes
CHANGED
@@ -1,34 +1,2 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
29 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
30 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
31 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
32 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
33 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
34 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
1 |
+
# Auto detect text files and perform LF normalization
|
2 |
+
* text=auto
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.github/workflows/codeql.yml
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# For most projects, this workflow file will not need changing; you simply need
|
2 |
+
# to commit it to your repository.
|
3 |
+
#
|
4 |
+
# You may wish to alter this file to override the set of languages analyzed,
|
5 |
+
# or to provide custom queries or build logic.
|
6 |
+
#
|
7 |
+
# ******** NOTE ********
|
8 |
+
# We have attempted to detect the languages in your repository. Please check
|
9 |
+
# the `language` matrix defined below to confirm you have the correct set of
|
10 |
+
# supported CodeQL languages.
|
11 |
+
#
|
12 |
+
name: "CodeQL"
|
13 |
+
|
14 |
+
on:
|
15 |
+
push:
|
16 |
+
branches: [ "main" ]
|
17 |
+
pull_request:
|
18 |
+
branches: [ "main" ]
|
19 |
+
schedule:
|
20 |
+
- cron: '17 3 * * 1'
|
21 |
+
|
22 |
+
jobs:
|
23 |
+
analyze:
|
24 |
+
name: Analyze (${{ matrix.language }})
|
25 |
+
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
26 |
+
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
27 |
+
# - https://gh.io/supported-runners-and-hardware-resources
|
28 |
+
# - https://gh.io/using-larger-runners (GitHub.com only)
|
29 |
+
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
30 |
+
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
31 |
+
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
32 |
+
permissions:
|
33 |
+
# required for all workflows
|
34 |
+
security-events: write
|
35 |
+
|
36 |
+
# required to fetch internal or private CodeQL packs
|
37 |
+
packages: read
|
38 |
+
|
39 |
+
# only required for workflows in private repositories
|
40 |
+
actions: read
|
41 |
+
contents: read
|
42 |
+
|
43 |
+
strategy:
|
44 |
+
fail-fast: false
|
45 |
+
matrix:
|
46 |
+
include:
|
47 |
+
- language: python
|
48 |
+
build-mode: none
|
49 |
+
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
50 |
+
# Use `c-cpp` to analyze code written in C, C++ or both
|
51 |
+
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
52 |
+
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
53 |
+
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
54 |
+
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
55 |
+
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
56 |
+
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
57 |
+
steps:
|
58 |
+
- name: Checkout repository
|
59 |
+
uses: actions/checkout@v4
|
60 |
+
|
61 |
+
# Initializes the CodeQL tools for scanning.
|
62 |
+
- name: Initialize CodeQL
|
63 |
+
uses: github/codeql-action/init@v3
|
64 |
+
with:
|
65 |
+
languages: ${{ matrix.language }}
|
66 |
+
build-mode: ${{ matrix.build-mode }}
|
67 |
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
68 |
+
# By default, queries listed here will override any specified in a config file.
|
69 |
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
70 |
+
|
71 |
+
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
72 |
+
# queries: security-extended,security-and-quality
|
73 |
+
|
74 |
+
# If the analyze step fails for one of the languages you are analyzing with
|
75 |
+
# "We were unable to automatically build your code", modify the matrix above
|
76 |
+
# to set the build mode to "manual" for that language. Then modify this step
|
77 |
+
# to build your code.
|
78 |
+
# ℹ️ Command-line programs to run using the OS shell.
|
79 |
+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
80 |
+
- if: matrix.build-mode == 'manual'
|
81 |
+
run: |
|
82 |
+
echo 'If you are using a "manual" build mode for one or more of the' \
|
83 |
+
'languages you are analyzing, replace this with the commands to build' \
|
84 |
+
'your code, for example:'
|
85 |
+
echo ' make bootstrap'
|
86 |
+
echo ' make release'
|
87 |
+
exit 1
|
88 |
+
|
89 |
+
- name: Perform CodeQL Analysis
|
90 |
+
uses: github/codeql-action/analyze@v3
|
91 |
+
with:
|
92 |
+
category: "/language:${{matrix.language}}"
|
.gitignore
CHANGED
@@ -1,4 +1,7 @@
|
|
1 |
-
|
2 |
-
.venv/
|
3 |
__pycache__
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
__pycache__
|
2 |
+
generated/*
|
3 |
+
temp/*
|
4 |
+
build/*
|
5 |
+
logs/*
|
6 |
+
test.py
|
7 |
+
_config.yaml
|
LICENSE
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Apache License
|
2 |
+
Version 2.0, January 2004
|
3 |
+
http://www.apache.org/licenses/
|
4 |
+
|
5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6 |
+
|
7 |
+
1. Definitions.
|
8 |
+
|
9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
11 |
+
|
12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
13 |
+
the copyright owner that is granting the License.
|
14 |
+
|
15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
16 |
+
other entities that control, are controlled by, or are under common
|
17 |
+
control with that entity. For the purposes of this definition,
|
18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
19 |
+
direction or management of such entity, whether by contract or
|
20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
22 |
+
|
23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
24 |
+
exercising permissions granted by this License.
|
25 |
+
|
26 |
+
"Source" form shall mean the preferred form for making modifications,
|
27 |
+
including but not limited to software source code, documentation
|
28 |
+
source, and configuration files.
|
29 |
+
|
30 |
+
"Object" form shall mean any form resulting from mechanical
|
31 |
+
transformation or translation of a Source form, including but
|
32 |
+
not limited to compiled object code, generated documentation,
|
33 |
+
and conversions to other media types.
|
34 |
+
|
35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
36 |
+
Object form, made available under the License, as indicated by a
|
37 |
+
copyright notice that is included in or attached to the work
|
38 |
+
(an example is provided in the Appendix below).
|
39 |
+
|
40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
41 |
+
form, that is based on (or derived from) the Work and for which the
|
42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
44 |
+
of this License, Derivative Works shall not include works that remain
|
45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
46 |
+
the Work and Derivative Works thereof.
|
47 |
+
|
48 |
+
"Contribution" shall mean any work of authorship, including
|
49 |
+
the original version of the Work and any modifications or additions
|
50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
54 |
+
means any form of electronic, verbal, or written communication sent
|
55 |
+
to the Licensor or its representatives, including but not limited to
|
56 |
+
communication on electronic mailing lists, source code control systems,
|
57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
59 |
+
excluding communication that is conspicuously marked or otherwise
|
60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
61 |
+
|
62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
64 |
+
subsequently incorporated within the Work.
|
65 |
+
|
66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
71 |
+
Work and such Derivative Works in Source or Object form.
|
72 |
+
|
73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
76 |
+
(except as stated in this section) patent license to make, have made,
|
77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
78 |
+
where such license applies only to those patent claims licensable
|
79 |
+
by such Contributor that are necessarily infringed by their
|
80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
82 |
+
institute patent litigation against any entity (including a
|
83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
84 |
+
or a Contribution incorporated within the Work constitutes direct
|
85 |
+
or contributory patent infringement, then any patent licenses
|
86 |
+
granted to You under this License for that Work shall terminate
|
87 |
+
as of the date such litigation is filed.
|
88 |
+
|
89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
90 |
+
Work or Derivative Works thereof in any medium, with or without
|
91 |
+
modifications, and in Source or Object form, provided that You
|
92 |
+
meet the following conditions:
|
93 |
+
|
94 |
+
(a) You must give any other recipients of the Work or
|
95 |
+
Derivative Works a copy of this License; and
|
96 |
+
|
97 |
+
(b) You must cause any modified files to carry prominent notices
|
98 |
+
stating that You changed the files; and
|
99 |
+
|
100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
101 |
+
that You distribute, all copyright, patent, trademark, and
|
102 |
+
attribution notices from the Source form of the Work,
|
103 |
+
excluding those notices that do not pertain to any part of
|
104 |
+
the Derivative Works; and
|
105 |
+
|
106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
107 |
+
distribution, then any Derivative Works that You distribute must
|
108 |
+
include a readable copy of the attribution notices contained
|
109 |
+
within such NOTICE file, excluding those notices that do not
|
110 |
+
pertain to any part of the Derivative Works, in at least one
|
111 |
+
of the following places: within a NOTICE text file distributed
|
112 |
+
as part of the Derivative Works; within the Source form or
|
113 |
+
documentation, if provided along with the Derivative Works; or,
|
114 |
+
within a display generated by the Derivative Works, if and
|
115 |
+
wherever such third-party notices normally appear. The contents
|
116 |
+
of the NOTICE file are for informational purposes only and
|
117 |
+
do not modify the License. You may add Your own attribution
|
118 |
+
notices within Derivative Works that You distribute, alongside
|
119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
120 |
+
that such additional attribution notices cannot be construed
|
121 |
+
as modifying the License.
|
122 |
+
|
123 |
+
You may add Your own copyright statement to Your modifications and
|
124 |
+
may provide additional or different license terms and conditions
|
125 |
+
for use, reproduction, or distribution of Your modifications, or
|
126 |
+
for any such Derivative Works as a whole, provided Your use,
|
127 |
+
reproduction, and distribution of the Work otherwise complies with
|
128 |
+
the conditions stated in this License.
|
129 |
+
|
130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
132 |
+
by You to the Licensor shall be under the terms and conditions of
|
133 |
+
this License, without any additional terms or conditions.
|
134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
135 |
+
the terms of any separate license agreement you may have executed
|
136 |
+
with Licensor regarding such Contributions.
|
137 |
+
|
138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
140 |
+
except as required for reasonable and customary use in describing the
|
141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
142 |
+
|
143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
144 |
+
agreed to in writing, Licensor provides the Work (and each
|
145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
147 |
+
implied, including, without limitation, any warranties or conditions
|
148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
150 |
+
appropriateness of using or redistributing the Work and assume any
|
151 |
+
risks associated with Your exercise of permissions under this License.
|
152 |
+
|
153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
154 |
+
whether in tort (including negligence), contract, or otherwise,
|
155 |
+
unless required by applicable law (such as deliberate and grossly
|
156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
157 |
+
liable to You for damages, including any direct, indirect, special,
|
158 |
+
incidental, or consequential damages of any character arising as a
|
159 |
+
result of this License or out of the use or inability to use the
|
160 |
+
Work (including but not limited to damages for loss of goodwill,
|
161 |
+
work stoppage, computer failure or malfunction, or any and all
|
162 |
+
other commercial damages or losses), even if such Contributor
|
163 |
+
has been advised of the possibility of such damages.
|
164 |
+
|
165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
168 |
+
or other liability obligations and/or rights consistent with this
|
169 |
+
License. However, in accepting such obligations, You may act only
|
170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
171 |
+
of any other Contributor, and only if You agree to indemnify,
|
172 |
+
defend, and hold each Contributor harmless for any liability
|
173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
174 |
+
of your accepting any such warranty or additional liability.
|
175 |
+
|
176 |
+
END OF TERMS AND CONDITIONS
|
177 |
+
|
178 |
+
APPENDIX: How to apply the Apache License to your work.
|
179 |
+
|
180 |
+
To apply the Apache License to your work, attach the following
|
181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
182 |
+
replaced with your own identifying information. (Don't include
|
183 |
+
the brackets!) The text should be enclosed in the appropriate
|
184 |
+
comment syntax for the file format. We also recommend that a
|
185 |
+
file or class name and description of purpose be included on the
|
186 |
+
same "printed page" as the copyright notice for easier
|
187 |
+
identification within third-party archives.
|
188 |
+
|
189 |
+
Copyright [yyyy] [name of copyright owner]
|
190 |
+
|
191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
192 |
+
you may not use this file except in compliance with the License.
|
193 |
+
You may obtain a copy of the License at
|
194 |
+
|
195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
196 |
+
|
197 |
+
Unless required by applicable law or agreed to in writing, software
|
198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200 |
+
See the License for the specific language governing permissions and
|
201 |
+
limitations under the License.
|
README.md
CHANGED
@@ -1,20 +1,123 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
|
|
|
|
|
|
10 |
|
11 |
-
|
|
|
12 |
|
|
|
|
|
13 |
|
14 |
-
|
15 |
|
16 |
-
|
17 |
-
|
18 |
-
3) Then run the app with `shiny run --reload`
|
19 |
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div align="center">
|
2 |
+
<img src="https://github.com/CubeGPT/CubeAgents/blob/master/banner.jpeg?raw=true"/>
|
3 |
+
<img src="https://img.shields.io/badge/Cube-Agents-blue">
|
4 |
+
<a href="https://github.com/CubeGPT/BuilderGPT/pulls"><img src="https://img.shields.io/badge/PRs-welcome-20BF20"></a>
|
5 |
+
<img src="https://img.shields.io/badge/License-Apache-red">
|
6 |
+
<a href="https://discord.gg/kTZtXw8s7r"><img src="https://img.shields.io/discord/1212765516532289587
|
7 |
+
"></a>
|
8 |
+
<!-- <p>English | <a href="https://github.com/CubeGPT/CubeAgents/blob/master/README-zh_cn.md">简体中文</a></p> -->
|
9 |
+
<br>
|
10 |
+
<a href="https://discord.gg/kTZtXw8s7r">Join our discord</a>
|
11 |
+
<br/>
|
12 |
+
</div>
|
13 |
|
14 |
+
> [!NOTE]
|
15 |
+
> Developers and translators are welcome to join the CubeGPT Team!
|
16 |
|
17 |
+
## Introduction
|
18 |
+
> Give GPT your idea, AI generates customized Minecraft server plugins with one click, which is suitable for Bukkit, Spigot, Paper, Purpur, Arclight, CatServer, Magma, Mohist and other Bukkit-based servers.
|
19 |
|
20 |
+
BukkitGPT is an open source, free, AI-powered Minecraft Bukkit plugin generator. It was developed for minecraft server owners who are not technically savvy but need to implement all kinds of customized small plugins. From code to build, debug, all done by gpt.
|
21 |
|
22 |
+
## WebApp
|
23 |
+
Don't want to prepare the Python & Maven environment? Try our [WebApp](http://cubegpt.org/), designed for non-developers, just enter the plugin name and description, and you can get the plugin jar file.
|
|
|
24 |
|
25 |
+
*The service is paid since the API key we are using is not free. You can get 1 key for 5 generations for $1 [here](https://buymeacoffee.com/baimoqilin/e/293180) or [here (for Chinese users)](https://afdian.com/item/b839835461e311efbd1252540025c377)
|
26 |
+
|
27 |
+
## Partner
|
28 |
+
[](https://bisecthosting.com/cubegpt)
|
29 |
+
|
30 |
+
## Features
|
31 |
+
|
32 |
+
### Core
|
33 |
+
- Automatically generate code
|
34 |
+
- Automatically fix bugs
|
35 |
+
- AI `Better Description`
|
36 |
+
|
37 |
+
### GUI
|
38 |
+
- Creating projects
|
39 |
+
- Projects management
|
40 |
+
|
41 |
+
## Plans and TODOs
|
42 |
+
|
43 |
+
Moved to [Projects Tab](https://github.com/orgs/CubeGPT/projects/4).
|
44 |
+
|
45 |
+
### Other projects of CubeGPT Team
|
46 |
+
- [x] Bukkit plugin generator. {*.jar} ([BukkitGPT-v3](https://github.com/CubeGPT/BukkitGPT-v3))
|
47 |
+
- [ ] Structure generator. {*.schem} (BuilderGPT, or something?)
|
48 |
+
- [ ] Serverpack generator. {*.zip} (ServerpackGPT or ServerGPT, or..?)
|
49 |
+
- [ ] Have ideas or want to join our team? Send [us](mailto:[email protected]) an email!
|
50 |
+
|
51 |
+
## How it works
|
52 |
+
When the user types the plugin description, the program lets `gpt-3.5-turbo` optimize the prompt, and then gives the optimized prompt to `gpt-4-turbo-preview`. `gpt-4-turbo-preview` will return it in json format, for example:
|
53 |
+
```
|
54 |
+
{
|
55 |
+
"output": [
|
56 |
+
{
|
57 |
+
"file": "%WORKING_PATH%/Main.java",
|
58 |
+
"code": "package ...;\nimport org.bukkit.Bukkit;\npublic class Main extends JavaPlugin implements CommandExecutor {\n..."
|
59 |
+
},
|
60 |
+
{
|
61 |
+
"file": "src/main/resources/plugin.yml",
|
62 |
+
"code": "name: ...\nversion: ...\n..."
|
63 |
+
},
|
64 |
+
{
|
65 |
+
"file\": "src/main/resources/config.yml",
|
66 |
+
"code\": "..."
|
67 |
+
},
|
68 |
+
{
|
69 |
+
"file": "pom.xml",
|
70 |
+
"code": "..."
|
71 |
+
}
|
72 |
+
]
|
73 |
+
}
|
74 |
+
```
|
75 |
+
The program parses this prompt, copies the entire `projects/template` folder and names it `artifact_name`, and puts the code from the prompt into the each file. Finally the program builds the jar using maven.
|
76 |
+
|
77 |
+
## Requirements
|
78 |
+
You can use BukkitGPT on any computer with [Java](https://www.azul.com/downloads/), [Maven](https://maven.apache.org/), [Python 3+](https://www.python.org/).
|
79 |
+
|
80 |
+
And you need to install this package:
|
81 |
+
```
|
82 |
+
pip install openai
|
83 |
+
```
|
84 |
+
|
85 |
+
## Quick Start
|
86 |
+
|
87 |
+
*(Make sure you have the [Python](https://www.python.org) environment installed on your computer)*
|
88 |
+
|
89 |
+
|
90 |
+
### Python/UI
|
91 |
+
|
92 |
+
1. Download `Source Code.zip` from [the release page](https://github.com/CubeGPT/BukkitGPT-v3/releases) and unzip it.
|
93 |
+
2. Edit `config.yaml`, fill in your OpenAI Apikey. If you don't know how, remember that [Google](https://www.google.com/) and [Bing](https://www.bing.com/) are always your best friends.
|
94 |
+
3. Install dependencies by running `pip install -r requirements.txt`.
|
95 |
+
4. Run `ui.py` (bash `python console.py`).
|
96 |
+
5. Enter the artifact name & description & package id as instructed to generate your plugin.
|
97 |
+
6. Copy your plugin from `projects/<artifact_name>/target/<artifact_name>-<version>.jar` to your server `plugins/` folder.
|
98 |
+
7. Restart your server and enjoy your AI-powered-plugin.
|
99 |
+
|
100 |
+
## Troubleshooting
|
101 |
+
|
102 |
+
### The POM for org.spigotmc:spigot:jar:1.13.2-R0.1-SNAPSHOT is missing
|
103 |
+
Solution: [Download BuildTools](https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar), place it in an empty folder, double-click it, choose "1.13.2" in `Settings/Select Version`, click `Compile` in the bottom right corner and let it finish. And then go to your BukkitGPT folder, in `projects/<artifact_name_of_your_plugin>`, double-click `build.bat`. You'll find your plugin in `projects/<artifact_name_of_your_plugin>/target` folder.
|
104 |
+
|
105 |
+
## Contributing
|
106 |
+
If you like the project, you can give the project a star, or [submit an issue](https://github.com/CubeGPT/BukkitGPT-v3/issues) or [pull request](https://github.com/CubeGPT/BukkitGPT-v3/pulls) to help make it better.
|
107 |
+
|
108 |
+
## License
|
109 |
+
```
|
110 |
+
Copyright [2024] [CubeGPT Team]
|
111 |
+
|
112 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
113 |
+
you may not use this file except in compliance with the License.
|
114 |
+
You may obtain a copy of the License at
|
115 |
+
|
116 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
117 |
+
|
118 |
+
Unless required by applicable law or agreed to in writing, software
|
119 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
120 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
121 |
+
See the License for the specific language governing permissions and
|
122 |
+
limitations under the License.
|
123 |
+
```
|
banner.jpeg
ADDED
![]() |
build.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from subprocess import Popen, PIPE, STDOUT
|
2 |
+
from log_writer import logger
|
3 |
+
|
4 |
+
|
5 |
+
def build_plugin(artifact_name):
|
6 |
+
project_path = f"codes/{artifact_name}"
|
7 |
+
build_command = [
|
8 |
+
"cd",
|
9 |
+
project_path,
|
10 |
+
"&&",
|
11 |
+
"mvn",
|
12 |
+
"-V",
|
13 |
+
"-B",
|
14 |
+
"clean",
|
15 |
+
"package",
|
16 |
+
"--file",
|
17 |
+
"pom.xml",
|
18 |
+
]
|
19 |
+
|
20 |
+
process = Popen(build_command, stdout=PIPE, stderr=STDOUT, shell=True)
|
21 |
+
|
22 |
+
def log_subprocess_output(pipe):
|
23 |
+
output = ""
|
24 |
+
for line in iter(pipe.readline, b""):
|
25 |
+
str_line = str(line)
|
26 |
+
output += str_line
|
27 |
+
logger(f"building -> {str_line}")
|
28 |
+
return output
|
29 |
+
|
30 |
+
with process.stdout:
|
31 |
+
output = log_subprocess_output(process.stdout)
|
32 |
+
|
33 |
+
process.wait()
|
34 |
+
|
35 |
+
return output
|
36 |
+
|
37 |
+
|
38 |
+
if __name__ == "__main__":
|
39 |
+
result = build_plugin("ExamplePlugin2")
|
40 |
+
print(result)
|
41 |
+
print(type(result))
|
codes/Demo/pom.xml
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
3 |
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
4 |
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
5 |
+
<modelVersion>4.0.0</modelVersion>
|
6 |
+
|
7 |
+
<groupId>org.cubegpt</groupId>
|
8 |
+
<artifactId>Demo</artifactId>
|
9 |
+
<version>1.0-SNAPSHOT</version>
|
10 |
+
<packaging>jar</packaging>
|
11 |
+
|
12 |
+
<dependencies>
|
13 |
+
<dependency>
|
14 |
+
<groupId>org.bukkit</groupId>
|
15 |
+
<artifactId>bukkit</artifactId>
|
16 |
+
<version>1.13.2-R0.1-SNAPSHOT</version>
|
17 |
+
<scope>provided</scope>
|
18 |
+
</dependency>
|
19 |
+
</dependencies>
|
20 |
+
|
21 |
+
<build>
|
22 |
+
<sourceDirectory>src/main/java</sourceDirectory>
|
23 |
+
<resources>
|
24 |
+
<resource>
|
25 |
+
<directory>src/main/resources</directory>
|
26 |
+
<filtering>true</filtering>
|
27 |
+
</resource>
|
28 |
+
</resources>
|
29 |
+
<plugins>
|
30 |
+
<plugin>
|
31 |
+
<groupId>org.apache.maven.plugins</groupId>
|
32 |
+
<artifactId>maven-compiler-plugin</artifactId>
|
33 |
+
<version>3.8.1</version>
|
34 |
+
<configuration>
|
35 |
+
<source>1.8</source>
|
36 |
+
<target>1.8</target>
|
37 |
+
</configuration>
|
38 |
+
</plugin>
|
39 |
+
<plugin>
|
40 |
+
<groupId>org.apache.maven.plugins</groupId>
|
41 |
+
<artifactId>maven-jar-plugin</artifactId>
|
42 |
+
<configuration>
|
43 |
+
<archive>
|
44 |
+
<manifest>
|
45 |
+
<addClasspath>true</addClasspath>
|
46 |
+
<classpathPrefix>lib/</classpathPrefix>
|
47 |
+
<mainClass>org.cubegpt._54672e99.Main</mainClass>
|
48 |
+
</manifest>
|
49 |
+
</archive>
|
50 |
+
</configuration>
|
51 |
+
</plugin>
|
52 |
+
</plugins>
|
53 |
+
</build>
|
54 |
+
</project>
|
codes/Demo/src/main/java/org/cubegpt/54672e99/Main.java
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package org.cubegpt._54672e99;
|
2 |
+
|
3 |
+
import org.bukkit.Bukkit;
|
4 |
+
import org.bukkit.plugin.java.JavaPlugin;
|
5 |
+
import org.bukkit.event.Listener;
|
6 |
+
import org.bukkit.event.EventHandler;
|
7 |
+
import org.bukkit.event.player.PlayerJoinEvent;
|
8 |
+
import org.bukkit.command.CommandExecutor;
|
9 |
+
import org.bukkit.command.Command;
|
10 |
+
import org.bukkit.command.CommandSender;
|
11 |
+
import org.bukkit.entity.Player;
|
12 |
+
|
13 |
+
public class Main extends JavaPlugin implements Listener {
|
14 |
+
|
15 |
+
@Override
|
16 |
+
public void onEnable() {
|
17 |
+
getServer().getPluginManager().registerEvents(this, this);
|
18 |
+
Bukkit.getConsoleSender().sendMessage("Plugin enabled!");
|
19 |
+
}
|
20 |
+
|
21 |
+
@EventHandler
|
22 |
+
public void onPlayerJoin(PlayerJoinEvent event) {
|
23 |
+
event.getPlayer().sendMessage("hello");
|
24 |
+
}
|
25 |
+
|
26 |
+
@Override
|
27 |
+
public void onDisable() {
|
28 |
+
Bukkit.getConsoleSender().sendMessage("Plugin disabled!");
|
29 |
+
}
|
30 |
+
}
|
codes/Demo/src/main/resources/config.yml
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
greeting-message: 'hello'
|
codes/Demo/src/main/resources/plugin.yml
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Demo
|
2 |
+
version: 1.0
|
3 |
+
main: org.cubegpt._54672e99.Main
|
4 |
+
api-version: 1.13
|
5 |
+
author: CubeGPT
|
6 |
+
dependencies: []
|
7 |
+
commands:
|
8 |
+
demo:
|
9 |
+
description: Sends a greeting message to everyone.
|
10 |
+
usage: /<command>
|
codes/Demo/target/Demo-1.0-SNAPSHOT.jar
ADDED
Binary file (3.22 kB). View file
|
|
codes/Demo/target/classes/config.yml
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
greeting-message: 'hello'
|
codes/Demo/target/classes/org/cubegpt/54672e99/Main.class
ADDED
Binary file (919 Bytes). View file
|
|
codes/Demo/target/classes/plugin.yml
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Demo
|
2 |
+
version: 1.0
|
3 |
+
main: org.cubegpt._54672e99.Main
|
4 |
+
api-version: 1.13
|
5 |
+
author: CubeGPT
|
6 |
+
dependencies: []
|
7 |
+
commands:
|
8 |
+
demo:
|
9 |
+
description: Sends a greeting message to everyone.
|
10 |
+
usage: /<command>
|
codes/Demo/target/maven-archiver/pom.properties
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
artifactId=Demo
|
2 |
+
groupId=org.cubegpt
|
3 |
+
version=1.0-SNAPSHOT
|
codes/Demo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
org\cubegpt\_54672e99\Main.class
|
codes/Demo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
D:\zhousl\BukkitGPT\BukkitGPT-v3\codes\Demo\src\main\java\org\cubegpt\54672e99\Main.java
|
codes/ExamplePlugin4/pom.xml
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
3 |
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
4 |
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
5 |
+
<modelVersion>4.0.0</modelVersion>
|
6 |
+
|
7 |
+
<groupId>org.cubegpt</groupId>
|
8 |
+
<artifactId>ExamplePlugin4</artifactId>
|
9 |
+
<version>1.0-SNAPSHOT</version>
|
10 |
+
<packaging>jar</packaging>
|
11 |
+
|
12 |
+
<dependencies>
|
13 |
+
<dependency>
|
14 |
+
<groupId>org.bukkit</groupId>
|
15 |
+
<artifactId>bukkit</artifactId>
|
16 |
+
<version>1.13.2-R0.1-SNAPSHOT</version>
|
17 |
+
<scope>provided</scope>
|
18 |
+
</dependency>
|
19 |
+
</dependencies>
|
20 |
+
|
21 |
+
<build>
|
22 |
+
<sourceDirectory>src/main/java</sourceDirectory>
|
23 |
+
<resources>
|
24 |
+
<resource>
|
25 |
+
<directory>src/main/resources</directory>
|
26 |
+
</resource>
|
27 |
+
</resources>
|
28 |
+
<plugins>
|
29 |
+
<plugin>
|
30 |
+
<groupId>org.apache.maven.plugins</groupId>
|
31 |
+
<artifactId>maven-compiler-plugin</artifactId>
|
32 |
+
<version>3.8.0</version>
|
33 |
+
<configuration>
|
34 |
+
<source>1.8</source>
|
35 |
+
<target>1.8</target>
|
36 |
+
</configuration>
|
37 |
+
</plugin>
|
38 |
+
</plugins>
|
39 |
+
</build>
|
40 |
+
</project>
|
codes/ExamplePlugin4/src/main/java/org/cubegpt/188eba63/Main.java
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package org.cubegpt._188eba63;
|
2 |
+
|
3 |
+
import org.bukkit.Bukkit;
|
4 |
+
import org.bukkit.event.EventHandler;
|
5 |
+
import org.bukkit.event.Listener;
|
6 |
+
import org.bukkit.event.player.PlayerJoinEvent;
|
7 |
+
import org.bukkit.plugin.java.JavaPlugin;
|
8 |
+
|
9 |
+
public class Main extends JavaPlugin implements Listener {
|
10 |
+
|
11 |
+
@Override
|
12 |
+
public void onEnable() {
|
13 |
+
Bukkit.getServer().getPluginManager().registerEvents(this, this);
|
14 |
+
}
|
15 |
+
|
16 |
+
@EventHandler
|
17 |
+
public void onPlayerJoin(PlayerJoinEvent event) {
|
18 |
+
event.getPlayer().sendMessage("hello");
|
19 |
+
}
|
20 |
+
}
|
codes/ExamplePlugin4/src/main/resources/config.yml
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
message: "hello"
|
codes/ExamplePlugin4/src/main/resources/plugin.yml
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: ExamplePlugin4
|
2 |
+
version: 1.0
|
3 |
+
main: org.cubegpt._188eba63.Main
|
4 |
+
api-version: 1.13
|
5 |
+
author: CubeGPT
|
codes/ExamplePlugin4/target/ExamplePlugin4-1.0-SNAPSHOT.jar
ADDED
Binary file (2.96 kB). View file
|
|
codes/ExamplePlugin4/target/classes/config.yml
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
message: "hello"
|
codes/ExamplePlugin4/target/classes/org/cubegpt/188eba63/Main.class
ADDED
Binary file (841 Bytes). View file
|
|
codes/ExamplePlugin4/target/classes/plugin.yml
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: ExamplePlugin4
|
2 |
+
version: 1.0
|
3 |
+
main: org.cubegpt._188eba63.Main
|
4 |
+
api-version: 1.13
|
5 |
+
author: CubeGPT
|
codes/ExamplePlugin4/target/maven-archiver/pom.properties
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
artifactId=ExamplePlugin4
|
2 |
+
groupId=org.cubegpt
|
3 |
+
version=1.0-SNAPSHOT
|
codes/ExamplePlugin4/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
org\cubegpt\_188eba63\Main.class
|
codes/ExamplePlugin4/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
D:\zhousl\BukkitGPT\BukkitGPT-v3\codes\ExamplePlugin4\src\main\java\org\cubegpt\188eba63\Main.java
|
config.py
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import yaml
|
2 |
+
from log_writer import logger
|
3 |
+
|
4 |
+
|
5 |
+
def load_config():
|
6 |
+
"""
|
7 |
+
Loads the configuration from the 'config.yaml' file and sets the global variables accordingly.
|
8 |
+
|
9 |
+
If the 'GENERATE_MODEL' key in the configuration is set to 'gpt-4', it forces the use of 'gpt-4-turbo-preview'
|
10 |
+
as the value for the 'GENERATE_MODEL' key, since 'gpt-4' no longer supports json modes.
|
11 |
+
|
12 |
+
Returns:
|
13 |
+
None
|
14 |
+
"""
|
15 |
+
with open("config.yaml", "r") as conf:
|
16 |
+
config_content = yaml.safe_load(conf)
|
17 |
+
for key, value in config_content.items():
|
18 |
+
if key == "GENERATE_MODEL" and value == "gpt-4":
|
19 |
+
globals()[
|
20 |
+
key
|
21 |
+
] = "gpt-4-turbo-preview" # Force using gpt-4-turbo-preview if the user set the GENERATE_MODEL to gpt-4. Because gpt-4 is not longer supports json modes.
|
22 |
+
globals()[key] = value
|
23 |
+
logger(f"config: {key} -> {value}")
|
24 |
+
|
25 |
+
|
26 |
+
def edit_config(key, value):
|
27 |
+
"""
|
28 |
+
Edits the config file.
|
29 |
+
|
30 |
+
Args:
|
31 |
+
key (str): The key to edit.
|
32 |
+
value (str): The value to set.
|
33 |
+
|
34 |
+
Returns:
|
35 |
+
bool: True
|
36 |
+
"""
|
37 |
+
|
38 |
+
with open("config.yaml", "r") as conf:
|
39 |
+
config_content = conf.readlines()
|
40 |
+
|
41 |
+
with open("config.yaml", "w") as conf:
|
42 |
+
for line in config_content:
|
43 |
+
if line.startswith(key):
|
44 |
+
if value == True:
|
45 |
+
write_value = "True"
|
46 |
+
elif value == False:
|
47 |
+
write_value = "False"
|
48 |
+
else:
|
49 |
+
write_value = f'"{value}"'
|
50 |
+
if "#" in line:
|
51 |
+
conf.write(f"{key}: {write_value} # {line.split('#')[1]}\n")
|
52 |
+
else:
|
53 |
+
conf.write(f"{key}: {write_value}\n")
|
54 |
+
else:
|
55 |
+
conf.write(line)
|
56 |
+
|
57 |
+
return True
|
58 |
+
|
59 |
+
|
60 |
+
load_config()
|
config.yaml
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
########## EDIT REQUIRED ##########
|
2 |
+
|
3 |
+
# GPT SETTINGS #
|
4 |
+
# Get your api key from openai. Remember google/bing is always your best friend.
|
5 |
+
# Model names: gpt-4-turbo-preview, gpt-3.5-turbo, etc.
|
6 |
+
# Recommend -> gpt-4-turbo (Better performance, more expensive), gpt-4-o (Good performance, cheaper)
|
7 |
+
|
8 |
+
API_KEY: "" # Free API Key with GPT-4 access: https://github.com/CubeGPT/.github/discussions/1
|
9 |
+
BASE_URL: "https://api.openai.com/v1"
|
10 |
+
|
11 |
+
GENERATION_MODEL: "gpt-4-turbo-2024-04-09"
|
12 |
+
FIXING_MODEL: "gpt-4-turbo-2024-04-09"
|
13 |
+
|
14 |
+
# DEVELOPER SETTINGS #
|
15 |
+
VERSION_NUMBER: "0.1.1"
|
16 |
+
|
17 |
+
# PROMPT SETTINGS #
|
18 |
+
# If you don't know what it is, please don't touch it. Be sure to backup before editing.
|
19 |
+
|
20 |
+
## Code Generation ##
|
21 |
+
SYS_GEN: |
|
22 |
+
You're a minecraft bukkit plugin coder AI. Game Version: 1.13.2 (1.13.2-R0.1-SNAPSHOT)
|
23 |
+
Write the code & choose a artifact name for the following files with the infomation which is also provided by the user:
|
24 |
+
codes/%ARTIFACT_NAME%/src/main/java/%PKG_ID_LST%Main.java
|
25 |
+
codes/%ARTIFACT_NAME%/src/main/resources/plugin.yml
|
26 |
+
codes/%ARTIFACT_NAME%/src/main/resources/config.yml
|
27 |
+
codes/%ARTIFACT_NAME%/pom.xml
|
28 |
+
Response in json format:
|
29 |
+
{
|
30 |
+
\"codes\": [
|
31 |
+
{
|
32 |
+
\"file\": \"codes/%ARTIFACT_NAME%/src/main/java/%PKG_ID_LST%Main.java\",
|
33 |
+
\"code\": \"package ...;\\nimport org.bukkit.Bukkit;\\npublic class Main extends JavaPlugin implements CommandExecutor {\\n... (The code you need to write)\"
|
34 |
+
},
|
35 |
+
{
|
36 |
+
\"file\": \"codes/%ARTIFACT_NAME%/src/main/resources/plugin.yml\",
|
37 |
+
\"code\": \"name: ...\\nversion: ...\\n...\"
|
38 |
+
},
|
39 |
+
{
|
40 |
+
\"file\": \"codes/%ARTIFACT_NAME%/src/main/resources/config.yml\",
|
41 |
+
\"code\": \"...\"
|
42 |
+
},
|
43 |
+
{
|
44 |
+
\"file\": \"codes/%ARTIFACT_NAME%/pom.xml\",
|
45 |
+
\"code\": \"...\"
|
46 |
+
}
|
47 |
+
]
|
48 |
+
}
|
49 |
+
You should never response anything else. Never use Markdown format. Use \n for line feed, and never forget to use \ before ". Never write uncompeleted codes, such as leave a comment that says "// Your codes here" or "// Uncompeleted".
|
50 |
+
|
51 |
+
USR_GEN: |
|
52 |
+
%DESCRIPTION%
|
53 |
+
|
54 |
+
SYS_FIX: |
|
55 |
+
You're a minecraft bukkit plugin coder AI. Game Version: 1.13.2 (1.13.2-R0.1-SNAPSHOT)
|
56 |
+
Fix the error in the code provided by user. The error message is also provided by the user.
|
57 |
+
Response in json format:
|
58 |
+
{
|
59 |
+
\"codes\": [
|
60 |
+
{
|
61 |
+
\"file\": \"codes/%ARTIFACT_NAME%/src/main/java/%PKG_ID_LST%Main.java\",
|
62 |
+
\"code\": \"package ...;\\nimport org.bukkit.Bukkit;\\npublic class Main extends JavaPlugin implements CommandExecutor {\\n... (The code you need to write)\"
|
63 |
+
},
|
64 |
+
{
|
65 |
+
\"file\": \"codes/%ARTIFACT_NAME%/src/main/resources/plugin.yml\",
|
66 |
+
\"code\": \"name: ...\\nversion: ...\\n...\"
|
67 |
+
},
|
68 |
+
{
|
69 |
+
\"file\": \"codes/%ARTIFACT_NAME%/src/main/resources/config.yml\",
|
70 |
+
\"code\": \"...\"
|
71 |
+
},
|
72 |
+
{
|
73 |
+
\"file\": \"codes/%ARTIFACT_NAME%/pom.xml\",
|
74 |
+
\"code\": \"...\"
|
75 |
+
}
|
76 |
+
]
|
77 |
+
}
|
78 |
+
You should never response anything else. Never use Markdown format. Use \n for line feed, and never forget to use \ before ". Never write uncompeleted codes, such as leave a comment that says "// Your codes here" or "// Original code" or "// Uncompeleted".
|
79 |
+
|
80 |
+
USR_FIX: |
|
81 |
+
Main.java:
|
82 |
+
%MAIN_JAVA%
|
83 |
+
plugin.yml:
|
84 |
+
%PLUGIN_YML%
|
85 |
+
config.yml:
|
86 |
+
%CONFIG_YML%
|
87 |
+
pom.xml:
|
88 |
+
%POM_XML%
|
89 |
+
error message:
|
90 |
+
%P_ERROR_MSG%
|
console.py
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
import uuid
|
3 |
+
import shutil
|
4 |
+
|
5 |
+
from log_writer import logger
|
6 |
+
import core
|
7 |
+
import config
|
8 |
+
import build
|
9 |
+
|
10 |
+
if __name__ == "__main__":
|
11 |
+
main_java = None
|
12 |
+
plugin_yml = None
|
13 |
+
config_yml = None
|
14 |
+
pom_xml = None
|
15 |
+
|
16 |
+
core.initialize()
|
17 |
+
|
18 |
+
print("BukkitGPT v3 beta console running")
|
19 |
+
|
20 |
+
# Get user inputs
|
21 |
+
name = input("Enter the plugin name: ")
|
22 |
+
description = input("Enter the plugin description: ")
|
23 |
+
|
24 |
+
artifact_name = name.replace(" ", "")
|
25 |
+
package_id = f"org.cubegpt.{uuid.uuid4().hex[:8]}"
|
26 |
+
|
27 |
+
pkg_id_path = ""
|
28 |
+
for id in package_id.split("."):
|
29 |
+
pkg_id_path += id + "/"
|
30 |
+
|
31 |
+
logger(f"user_input -> name: {name}")
|
32 |
+
logger(f"user_input -> description: {description}")
|
33 |
+
logger(f"random_generate -> package_id: {package_id}")
|
34 |
+
logger(f"str_path -> pkg_id_path: {pkg_id_path}")
|
35 |
+
|
36 |
+
print("Generating plugin...")
|
37 |
+
|
38 |
+
codes = core.askgpt(
|
39 |
+
config.SYS_GEN.replace("%ARTIFACT_NAME%", artifact_name).replace(
|
40 |
+
"%PKG_ID_LST%", pkg_id_path
|
41 |
+
),
|
42 |
+
config.USR_GEN.replace("%DESCRIPTION", description),
|
43 |
+
config.GENERATION_MODEL,
|
44 |
+
)
|
45 |
+
logger(f"codes: {codes}")
|
46 |
+
|
47 |
+
core.response_to_action(codes)
|
48 |
+
|
49 |
+
print("Code generated. Building now...")
|
50 |
+
|
51 |
+
result = build.build_plugin(artifact_name)
|
52 |
+
|
53 |
+
if "BUILD SUCCESS" in result:
|
54 |
+
print(
|
55 |
+
f"Build complete. Find your plugin at 'codes/{artifact_name}/target/{artifact_name}.jar'"
|
56 |
+
)
|
57 |
+
elif "Compilation failure":
|
58 |
+
print("Build failed. Passing the error to ChatGPT and let it to fix it?")
|
59 |
+
fix = input("Y/n: ")
|
60 |
+
if fix == "n":
|
61 |
+
print("Exiting...")
|
62 |
+
sys.exit(0)
|
63 |
+
else:
|
64 |
+
print("Passing the error to ChatGPT...")
|
65 |
+
|
66 |
+
files = [
|
67 |
+
f"codes/{artifact_name}/src/main/java/{pkg_id_path}Main.java",
|
68 |
+
f"codes/{artifact_name}/src/main/resources/plugin.yml",
|
69 |
+
f"codes/{artifact_name}/src/main/resources/config.yml",
|
70 |
+
f"codes/{artifact_name}/pom.xml",
|
71 |
+
]
|
72 |
+
|
73 |
+
ids = ["main_java", "plugin_yml", "config_yml", "pom_xml"]
|
74 |
+
|
75 |
+
for file in files:
|
76 |
+
with open(file, "r") as f:
|
77 |
+
code = f.read()
|
78 |
+
id = ids[files.index(file)]
|
79 |
+
globals()[id] = code
|
80 |
+
|
81 |
+
print("Generating...")
|
82 |
+
codes = core.askgpt(
|
83 |
+
config.SYS_FIX.replace("%ARTIFACT_NAME%", artifact_name),
|
84 |
+
config.USR_FIX.replace("%MAIN_JAVA%", main_java)
|
85 |
+
.replace("%PLUGIN_YML%", plugin_yml)
|
86 |
+
.replace("%CONFIG_YML%", config_yml)
|
87 |
+
.replace("%POM_XML%", pom_xml)
|
88 |
+
.replace("%P_ERROR_MSG%", result),
|
89 |
+
config.FIXING_MODEL,
|
90 |
+
)
|
91 |
+
|
92 |
+
shutil.rmtree(f"codes/{artifact_name}")
|
93 |
+
core.response_to_action(codes)
|
94 |
+
|
95 |
+
print("Code generated. Building now...")
|
96 |
+
|
97 |
+
result = build.build_plugin(artifact_name)
|
98 |
+
|
99 |
+
if "BUILD SUCCESS" in result:
|
100 |
+
print(
|
101 |
+
f"Build complete. Find your plugin at 'codes/{artifact_name}/target/{artifact_name}.jar'"
|
102 |
+
)
|
103 |
+
else:
|
104 |
+
print(
|
105 |
+
"Build failed. Please check the logs && send the log to @BaimoQilin on discord."
|
106 |
+
)
|
107 |
+
print("Exiting...")
|
108 |
+
sys.exit(0)
|
109 |
+
|
110 |
+
else:
|
111 |
+
print(
|
112 |
+
"Unknown error. Please check the logs && send the log to @BaimoQilin on discord."
|
113 |
+
)
|
114 |
+
print("Exiting...")
|
115 |
+
sys.exit(0)
|
116 |
+
|
117 |
+
|
118 |
+
else:
|
119 |
+
print(
|
120 |
+
"Error: Please run console.py as the main program instead of importing it from another program."
|
121 |
+
)
|
core.py
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from openai import OpenAI
|
2 |
+
import chardet
|
3 |
+
import sys
|
4 |
+
import json
|
5 |
+
import locale
|
6 |
+
import os
|
7 |
+
|
8 |
+
from log_writer import logger
|
9 |
+
import config
|
10 |
+
|
11 |
+
|
12 |
+
def initialize():
|
13 |
+
"""
|
14 |
+
Initializes the software.
|
15 |
+
|
16 |
+
This function logs the software launch, including the version number and platform.
|
17 |
+
|
18 |
+
Args:
|
19 |
+
None
|
20 |
+
|
21 |
+
Returns:
|
22 |
+
None
|
23 |
+
"""
|
24 |
+
locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
|
25 |
+
logger(f"Launch. Software version {config.VERSION_NUMBER}, platform {sys.platform}")
|
26 |
+
|
27 |
+
if (
|
28 |
+
"gpt-3.5" in config.GENERATION_MODEL
|
29 |
+
and config.BYPASS_NO_GPT35_FOR_GENERATION_LIMIT is False
|
30 |
+
):
|
31 |
+
print(
|
32 |
+
"gpt-3.5 writes bugs *all the time* and is not recommended for code generation. Switching to gpt-4."
|
33 |
+
)
|
34 |
+
config.edit_config(
|
35 |
+
"GENERATION_MODEL", config.GENERATION_MODEL.replace("gpt-3.5", "gpt-4")
|
36 |
+
)
|
37 |
+
|
38 |
+
|
39 |
+
def askgpt(
|
40 |
+
system_prompt: str,
|
41 |
+
user_prompt: str,
|
42 |
+
model_name: str,
|
43 |
+
disable_json_mode: bool = False,
|
44 |
+
image_url: str = None,
|
45 |
+
):
|
46 |
+
"""
|
47 |
+
Interacts with ChatGPT using the specified prompts.
|
48 |
+
|
49 |
+
Args:
|
50 |
+
system_prompt (str): The system prompt.
|
51 |
+
user_prompt (str): The user prompt.
|
52 |
+
model_name (str): The model name to use.
|
53 |
+
disable_json_mode (bool): Whether to disable JSON mode.
|
54 |
+
|
55 |
+
Returns:
|
56 |
+
str: The response from ChatGPT.
|
57 |
+
"""
|
58 |
+
if image_url is not None and config.USE_DIFFERENT_APIKEY_FOR_VISION_MODEL:
|
59 |
+
logger("Using different API key for vision model.")
|
60 |
+
client = OpenAI(api_key=config.VISION_API_KEY, base_url=config.VISION_BASE_URL)
|
61 |
+
else:
|
62 |
+
client = OpenAI(api_key=config.API_KEY, base_url=config.BASE_URL)
|
63 |
+
|
64 |
+
logger("Initialized the OpenAI client.")
|
65 |
+
|
66 |
+
# Define the messages for the conversation
|
67 |
+
if image_url is not None:
|
68 |
+
messages = [
|
69 |
+
{"role": "system", "content": system_prompt},
|
70 |
+
{
|
71 |
+
"role": "user",
|
72 |
+
"content": [
|
73 |
+
{"type": "text", "text": user_prompt},
|
74 |
+
{"type": "image_url", "image_url": {"url": image_url}},
|
75 |
+
],
|
76 |
+
},
|
77 |
+
]
|
78 |
+
else:
|
79 |
+
messages = [
|
80 |
+
{"role": "system", "content": system_prompt},
|
81 |
+
{"role": "user", "content": user_prompt},
|
82 |
+
]
|
83 |
+
|
84 |
+
logger(f"askgpt: system {system_prompt}")
|
85 |
+
logger(f"askgpt: user {user_prompt}")
|
86 |
+
|
87 |
+
# Create a chat completion
|
88 |
+
if disable_json_mode:
|
89 |
+
response = client.chat.completions.create(model=model_name, messages=messages)
|
90 |
+
else:
|
91 |
+
response = client.chat.completions.create(
|
92 |
+
model=model_name, response_format={"type": "json_object"}, messages=messages
|
93 |
+
)
|
94 |
+
|
95 |
+
logger(f"askgpt: response {response}")
|
96 |
+
|
97 |
+
# Extract the assistant's reply
|
98 |
+
assistant_reply = response.choices[0].message.content
|
99 |
+
logger(f"askgpt: extracted reply {assistant_reply}")
|
100 |
+
return assistant_reply
|
101 |
+
|
102 |
+
|
103 |
+
def response_to_action(msg):
|
104 |
+
"""
|
105 |
+
Converts a response from ChatGPT to an action.
|
106 |
+
|
107 |
+
Args:
|
108 |
+
msg (str): The response from ChatGPT.
|
109 |
+
|
110 |
+
Returns:
|
111 |
+
str: The action to take.
|
112 |
+
"""
|
113 |
+
text = json.loads(msg)
|
114 |
+
|
115 |
+
codes = text["codes"]
|
116 |
+
|
117 |
+
for section in codes:
|
118 |
+
file = section["file"]
|
119 |
+
code = section["code"]
|
120 |
+
|
121 |
+
paths = file.split("/")
|
122 |
+
|
123 |
+
# Join the list elements to form a path
|
124 |
+
path = os.path.join(*paths)
|
125 |
+
|
126 |
+
# Get the directory path and the file name
|
127 |
+
dir_path, file_name = os.path.split(path)
|
128 |
+
|
129 |
+
# Create directories, if they don't exist
|
130 |
+
try:
|
131 |
+
os.makedirs(dir_path, exist_ok=True)
|
132 |
+
except FileNotFoundError:
|
133 |
+
pass
|
134 |
+
|
135 |
+
# Create the file
|
136 |
+
with open(path, "w") as f:
|
137 |
+
f.write(code) # Write an empty string to the file
|
138 |
+
|
139 |
+
|
140 |
+
def mixed_decode(text: str):
|
141 |
+
"""
|
142 |
+
Decode a mixed text containing both normal text and a byte sequence.
|
143 |
+
|
144 |
+
Args:
|
145 |
+
text (str): The mixed text to be decoded.
|
146 |
+
|
147 |
+
Returns:
|
148 |
+
str: The decoded text, where the byte sequence has been converted to its corresponding characters.
|
149 |
+
|
150 |
+
"""
|
151 |
+
# Split the normal text and the byte sequence
|
152 |
+
# Assuming the byte sequence is everything after the last colon and space ": "
|
153 |
+
try:
|
154 |
+
normal_text, byte_text = text.rsplit(": ", 1)
|
155 |
+
except (TypeError, ValueError):
|
156 |
+
# The text only contains normal text
|
157 |
+
return text
|
158 |
+
|
159 |
+
# Convert the byte sequence to actual bytes
|
160 |
+
byte_sequence = byte_text.encode(
|
161 |
+
"latin1"
|
162 |
+
) # latin1 encoding maps byte values directly to unicode code points
|
163 |
+
|
164 |
+
# Detect the encoding of the byte sequence
|
165 |
+
detected_encoding = chardet.detect(byte_sequence)
|
166 |
+
encoding = detected_encoding["encoding"]
|
167 |
+
|
168 |
+
# Decode the byte sequence
|
169 |
+
decoded_text = byte_sequence.decode(encoding)
|
170 |
+
|
171 |
+
# Combine the normal text with the decoded byte sequence
|
172 |
+
final_text = normal_text + ": " + decoded_text
|
173 |
+
return final_text
|
174 |
+
|
175 |
+
|
176 |
+
if __name__ == "__main__":
|
177 |
+
print("This script is not meant to be run directly. Please run console.py instead.")
|
cube_qgui/__init__.py
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Author: Acer Zhang
|
2 |
+
# Edit: CubeGPT Team
|
3 |
+
# Original Version Datetime: 2021/9/16
|
4 |
+
# Edited Version Datetime: 2024/5/31
|
5 |
+
# Copyright belongs to the author.
|
6 |
+
# Please indicate the source for reprinting.
|
7 |
+
|
8 |
+
import cube_qgui.base_tools
|
9 |
+
import cube_qgui.factory
|
10 |
+
# import qgui.notebook_tools
|
11 |
+
# import qgui.banner_tools
|
12 |
+
|
13 |
+
|
14 |
+
from cube_qgui.factory import CreateQGUI
|
15 |
+
|
16 |
+
from cube_qgui.manager import *
|
17 |
+
|
18 |
+
from cube_qgui.base_tools import ArgInfo
|
cube_qgui/__main__.py
ADDED
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import time
|
2 |
+
|
3 |
+
# 导入CreateQGUI模块
|
4 |
+
from qgui import CreateQGUI, MessageBox
|
5 |
+
# 【可选】导入自定义导航栏按钮模块、GitHub导航栏模块
|
6 |
+
from qgui.banner_tools import BaseBarTool, GitHub, AIStudio
|
7 |
+
# 【可选】一次性导入所有的主界面工具模块
|
8 |
+
from qgui.notebook_tools import *
|
9 |
+
# 【可选】导入占位符
|
10 |
+
from qgui.manager import QStyle, HORIZONTAL
|
11 |
+
|
12 |
+
|
13 |
+
def click(args: dict):
|
14 |
+
MessageBox.info("要开始啦~")
|
15 |
+
# 证明一下自己被点到了
|
16 |
+
print("你点到我啦~")
|
17 |
+
# 通过ChooseFileTextButton(name="文件选择")中预先设置的name参数,使用get方法即可获取对应的输入框信息
|
18 |
+
print("你选择的文件是:", args["文件选择"].get())
|
19 |
+
# 当然也可以通过name参数来设置对应的内容,使用set方法即可完成设置
|
20 |
+
print("保存位置修改为“快看,我被修改啦”", args["保存位置"].set("快看,我被修改啦"))
|
21 |
+
# 即使没有指定name,我们照样可以拿到所有的小工具情况
|
22 |
+
for arg, v_fun in args.items():
|
23 |
+
print("自定义组件Name:", arg, "状态:", v_fun.get())
|
24 |
+
|
25 |
+
# 若我们绑定了进度条,那么每当需要设置进度的时候,通过args["进度条"].set(当前进度)来进行设置吧,倒吸进度条也是可以哒
|
26 |
+
for i in range(1, 101):
|
27 |
+
time.sleep(0.01)
|
28 |
+
args["进度条"].set(i)
|
29 |
+
# 增加打印间隔
|
30 |
+
if i % 20 == 0:
|
31 |
+
print("当前进度", i)
|
32 |
+
MessageBox.warning(text="给个评价吧亲~")
|
33 |
+
# 也可以在终端中打印组件,顺便绑定用户调研函数
|
34 |
+
q_gui.print_tool(RadioButton(["满意", "一般", "你好垃圾啊"], title="体验如何?", name="feedback", bind_func=feedback))
|
35 |
+
# 甚至打印图片
|
36 |
+
from qgui import RESOURCES_PATH
|
37 |
+
q_gui.print_image(os.path.join(RESOURCES_PATH, "demo/panda.jpg"))
|
38 |
+
|
39 |
+
|
40 |
+
def feedback(args: dict):
|
41 |
+
# 用户调研Callback
|
42 |
+
info = args["feedback"].get()
|
43 |
+
if info == "满意":
|
44 |
+
print("么么哒")
|
45 |
+
elif info == "一般":
|
46 |
+
print("啊啊啊,告诉GT哪里没做好吧")
|
47 |
+
else:
|
48 |
+
print("以后漂流瓶见吧,拜拜!")
|
49 |
+
|
50 |
+
|
51 |
+
def bind_dir(args: dict):
|
52 |
+
# 获取所选择文件所在的文件夹路径
|
53 |
+
path = os.path.dirname(args["文件选择"].get())
|
54 |
+
# 可以通过name参数来设置对应的内容,使用set方法即可完成设置
|
55 |
+
args["保存位置"].set(path)
|
56 |
+
print("保存位置已自动修改为:", path)
|
57 |
+
|
58 |
+
|
59 |
+
def go_to_first_page(args: dict):
|
60 |
+
args["QGUI-BaseNoteBook"].set(0)
|
61 |
+
|
62 |
+
|
63 |
+
# 创建主界面
|
64 |
+
q_gui = CreateQGUI(title="一个新应用", # 界面标题
|
65 |
+
tab_names=["主控制台", "选择按钮", "其他小工具"], # 界面中心部分的分页标题 - 可不填
|
66 |
+
style=QStyle.default) # 皮肤
|
67 |
+
|
68 |
+
# 在界面最上方添加一个按钮,链接到GitHub主页
|
69 |
+
q_gui.add_banner_tool(GitHub(url="https://github.com/QPT-Family/QGUI"))
|
70 |
+
# 也可以是AI Studio
|
71 |
+
q_gui.add_banner_tool(AIStudio(url="https://aistudio.baidu.com/aistudio/personalcenter/thirdview/29724"))
|
72 |
+
# 要不试试自定义Banner按钮,在大家点击它时触发刚刚定义的click函数,并向它传递其他组件的情况
|
73 |
+
q_gui.add_banner_tool(BaseBarTool(bind_func=click, name="一个新组件"))
|
74 |
+
|
75 |
+
# 在主界面部分添加一个文件选择工具吧,并在选择文件后自动变为文件所在的路径
|
76 |
+
q_gui.add_notebook_tool(ChooseFileTextButton(name="文件选择", bind_func=bind_dir))
|
77 |
+
# 再加个文件夹选择工具
|
78 |
+
q_gui.add_notebook_tool(ChooseDirTextButton(name="保存位置"))
|
79 |
+
# 当然也可以来个输入框
|
80 |
+
q_gui.add_notebook_tool(InputBox(name="我是个木有感情的输入框"))
|
81 |
+
# 想要加一个 进度条 和 运行按钮 而且俩要水平方向排列该如何做?
|
82 |
+
# 试试HorizontalToolsCombine,它可以接受一组工具并将其进行水平排列
|
83 |
+
# 这里我们也为RunButton绑定click函数
|
84 |
+
run_menu = HorizontalToolsCombine([Progressbar(name="进度条"),
|
85 |
+
RunButton(bind_func=click)],
|
86 |
+
text="试试HorizontalToolsCombine,它可以接受一组工具并将其进行水平排列")
|
87 |
+
q_gui.add_notebook_tool(run_menu)
|
88 |
+
|
89 |
+
# 第二页 - 复选框和单选框
|
90 |
+
# 使用VerticalFrameCombine可以将他们在垂直方向快速组合,它们会从左到右按顺序排列
|
91 |
+
combine_left = VerticalFrameCombine([CheckButton(options=[("选择1", 0), ("选择2", 1), ("选择3", 0)]),
|
92 |
+
CheckToolButton(options=[("选择1", 0), ("选择2", 1), ("选择3", 0)]),
|
93 |
+
CheckObviousToolButton(options=[("选择1", 0), ("选择2", 1), ("选择3", 0)]),
|
94 |
+
ToggleButton(options=("开", 1))],
|
95 |
+
tab_index=1,
|
96 |
+
text="使用VerticalFrameCombine可以将他们在垂直方向快速组合,它们会从左到右按顺序排列")
|
97 |
+
q_gui.add_notebook_tool(combine_left)
|
98 |
+
# 设置title参数��会为其增加标题
|
99 |
+
combine_right = VerticalFrameCombine([RadioButton(["选择1", "选择2", "选择3"], tab_index=1),
|
100 |
+
RadioToolButton(["选择1", "选择2", "选择3"], tab_index=1),
|
101 |
+
RadioObviousToolButton(["选择1", "选择2", "选择3"], tab_index=1)],
|
102 |
+
title="右侧的复选框")
|
103 |
+
q_gui.add_notebook_tool(combine_right)
|
104 |
+
|
105 |
+
# 第三页
|
106 |
+
q_gui.add_notebook_tool(Label(text="这只是个简单的Label组件", alignment=RIGHT + TOP, tab_index=2))
|
107 |
+
q_gui.add_notebook_tool(Slider(default=4, tab_index=2))
|
108 |
+
q_gui.add_notebook_tool(Combobox(options=["选择1", "选择2", "选择3"], tab_index=2))
|
109 |
+
q_gui.add_notebook_tool(BaseButton(bind_func=go_to_first_page, text="回到首页", tab_index=2))
|
110 |
+
|
111 |
+
# 左侧信息栏
|
112 |
+
# 简单加个简介
|
113 |
+
q_gui.set_navigation_about(author="GT",
|
114 |
+
version="0.0.1",
|
115 |
+
github_url="https://github.com/QPT-Family/QGUI",
|
116 |
+
other_info=["欢迎加入QPT!"])
|
117 |
+
# 也可以加一下其他信息
|
118 |
+
q_gui.set_navigation_info(title="随便写段话", info="除了QGUI,你还可以试试例如AgentQGUI这样同样简单的GUI框架")
|
119 |
+
print("小Tips:占位符可以被Print,不信你看HORIZONTAL的描述被打印了出来->", HORIZONTAL)
|
120 |
+
|
121 |
+
# 跑起来~切记!一定要放在程序末尾
|
122 |
+
q_gui.run()
|
cube_qgui/banner_tools.py
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Author: Acer Zhang
|
2 |
+
# Datetime: 2021/9/16
|
3 |
+
# Copyright belongs to the author.
|
4 |
+
# Please indicate the source for reprinting.
|
5 |
+
import os
|
6 |
+
import webbrowser
|
7 |
+
|
8 |
+
import tkinter
|
9 |
+
from tkinter import ttk
|
10 |
+
from cube_qgui.manager import ICON_PATH, ConcurrencyModeFlag
|
11 |
+
from cube_qgui.base_tools import ArgInfo, BaseTool
|
12 |
+
|
13 |
+
RUN_ICON = os.path.join(ICON_PATH, "play_w.png")
|
14 |
+
GITHUB_ICON = os.path.join(ICON_PATH, "github.png")
|
15 |
+
AI_STUDIO_ICON = os.path.join(ICON_PATH, "up_cloud.png")
|
16 |
+
|
17 |
+
|
18 |
+
class BaseBarTool(BaseTool):
|
19 |
+
"""
|
20 |
+
基础Banner工具集
|
21 |
+
需注意的是,如需增加异步等操作,请为函数添加_callback
|
22 |
+
"""
|
23 |
+
|
24 |
+
def __init__(self,
|
25 |
+
bind_func,
|
26 |
+
name="Unnamed Tool",
|
27 |
+
icon=None,
|
28 |
+
style=None,
|
29 |
+
async_run: bool = True,
|
30 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG):
|
31 |
+
super().__init__(bind_func=bind_func,
|
32 |
+
name=name,
|
33 |
+
style=style,
|
34 |
+
async_run=async_run,
|
35 |
+
concurrency_mode=concurrency_mode)
|
36 |
+
|
37 |
+
if icon and not os.path.exists(icon):
|
38 |
+
raise f"Please check if {os.path.abspath(icon)} exists."
|
39 |
+
if not icon:
|
40 |
+
icon = RUN_ICON
|
41 |
+
self.icon = icon
|
42 |
+
|
43 |
+
def build(self, *args, **kwargs):
|
44 |
+
super().build(*args, **kwargs)
|
45 |
+
self.img = tkinter.PhotoImage(file=self.icon)
|
46 |
+
|
47 |
+
btn = ttk.Button(self.master,
|
48 |
+
text=self.name,
|
49 |
+
image=self.img,
|
50 |
+
compound="left",
|
51 |
+
command=self._callback(self.bind_func) if self.async_run else self.bind_func,
|
52 |
+
style=self.style + "TButton")
|
53 |
+
|
54 |
+
btn.pack(side="left", ipadx=5, ipady=5, padx=0, pady=1)
|
55 |
+
|
56 |
+
|
57 |
+
class RunTool(BaseBarTool):
|
58 |
+
def __init__(self,
|
59 |
+
bind_func,
|
60 |
+
name="Start Processing",
|
61 |
+
icon=None,
|
62 |
+
style="success",
|
63 |
+
async_run: bool = True,
|
64 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG):
|
65 |
+
if not icon:
|
66 |
+
icon = RUN_ICON
|
67 |
+
super(RunTool, self).__init__(bind_func,
|
68 |
+
name=name,
|
69 |
+
icon=icon,
|
70 |
+
style=style,
|
71 |
+
async_run=async_run,
|
72 |
+
concurrency_mode=concurrency_mode)
|
73 |
+
|
74 |
+
|
75 |
+
class GitHub(BaseBarTool):
|
76 |
+
def __init__(self,
|
77 |
+
url,
|
78 |
+
name="View on GitHub",
|
79 |
+
style="primary"):
|
80 |
+
icon = GITHUB_ICON
|
81 |
+
bind_func = self.github_callback
|
82 |
+
super().__init__(bind_func,
|
83 |
+
name=name,
|
84 |
+
icon=icon,
|
85 |
+
style=style)
|
86 |
+
self.github_url = url
|
87 |
+
|
88 |
+
def github_callback(self, args):
|
89 |
+
webbrowser.open_new(self.github_url)
|
90 |
+
|
91 |
+
|
92 |
+
class AIStudio(BaseBarTool):
|
93 |
+
def __init__(self,
|
94 |
+
url,
|
95 |
+
name="Use on AI Studio",
|
96 |
+
style="primary"):
|
97 |
+
icon = AI_STUDIO_ICON
|
98 |
+
bind_func = self.ai_studio_callback
|
99 |
+
super().__init__(bind_func,
|
100 |
+
name=name,
|
101 |
+
icon=icon,
|
102 |
+
style=style)
|
103 |
+
self.ai_studio_url = url
|
104 |
+
|
105 |
+
def ai_studio_callback(self, args):
|
106 |
+
webbrowser.open_new(self.ai_studio_url)
|
cube_qgui/base_frame.py
ADDED
@@ -0,0 +1,286 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Author: Acer Zhang
|
2 |
+
# Datetime: 2021/9/14
|
3 |
+
# Copyright belongs to the author.
|
4 |
+
# Please indicate the source for reprinting.
|
5 |
+
|
6 |
+
import sys
|
7 |
+
import webbrowser
|
8 |
+
import time
|
9 |
+
from typing import List
|
10 |
+
|
11 |
+
import tkinter
|
12 |
+
from tkinter import ttk
|
13 |
+
from tkinter.scrolledtext import ScrolledText
|
14 |
+
|
15 |
+
from cube_qgui.manager import BLACK, FONT
|
16 |
+
from cube_qgui.banner_tools import BaseBarTool
|
17 |
+
from cube_qgui.third_party.collapsing_frame import CollapsingFrame
|
18 |
+
from cube_qgui.notebook_tools import BaseNotebookTool
|
19 |
+
from cube_qgui.os_tools import StdOutWrapper, DataCache
|
20 |
+
from cube_qgui.base_tools import ArgInfo
|
21 |
+
|
22 |
+
TITLE_BG_COLOR = BLACK
|
23 |
+
|
24 |
+
|
25 |
+
# ToDo 主题部分可考虑通过增加warmup来解决
|
26 |
+
|
27 |
+
class _Backbone:
|
28 |
+
"""
|
29 |
+
整个界面的基础,存放共有的变量
|
30 |
+
"""
|
31 |
+
|
32 |
+
def __init__(self, f_style="primary"):
|
33 |
+
"""
|
34 |
+
请务必检查self.frame是否做了pack等定位操作,无操作将不会被显示
|
35 |
+
:param f_style:
|
36 |
+
"""
|
37 |
+
# 统一用place
|
38 |
+
self.style = f_style
|
39 |
+
|
40 |
+
# 全局变量
|
41 |
+
self.global_info = ArgInfo()
|
42 |
+
|
43 |
+
def build(self, master, global_info):
|
44 |
+
self.frame = ttk.Frame(master, style=self.style + ".TFrame")
|
45 |
+
self.global_info = global_info
|
46 |
+
|
47 |
+
|
48 |
+
class BaseNavigation(_Backbone):
|
49 |
+
"""
|
50 |
+
左侧导航栏基本框架
|
51 |
+
"""
|
52 |
+
|
53 |
+
def __init__(self, style="primary"):
|
54 |
+
super(BaseNavigation, self).__init__(f_style=style)
|
55 |
+
self.tabs = dict()
|
56 |
+
|
57 |
+
def add_about(self,
|
58 |
+
author: str = "未知作者",
|
59 |
+
version: str = "0.0.1",
|
60 |
+
github_url: str = None,
|
61 |
+
bilibili_url: str = None,
|
62 |
+
blog_url: str = None,
|
63 |
+
other_info: List[str] = None):
|
64 |
+
bus_cf = CollapsingFrame(self.frame)
|
65 |
+
bus_cf.pack(fill='x', pady=0)
|
66 |
+
|
67 |
+
bus_frm = ttk.Frame(bus_cf, padding=5)
|
68 |
+
bus_frm.columnconfigure(1, weight=1)
|
69 |
+
bus_cf.add(bus_frm, title="Info", style='secondary.TButton')
|
70 |
+
|
71 |
+
ttk.Label(bus_frm, text=f"Author:\t{author}", style="TLabel", justify="left", wraplength=160).pack(anchor="nw")
|
72 |
+
ttk.Label(bus_frm, text=f"Version:\t{version}", style="TLabel", justify="left", wraplength=160).pack(anchor="nw")
|
73 |
+
|
74 |
+
if other_info:
|
75 |
+
for line in other_info:
|
76 |
+
ttk.Label(bus_frm, text=line, style="TLabel").pack(anchor="nw")
|
77 |
+
|
78 |
+
if github_url:
|
79 |
+
def github_callback(event):
|
80 |
+
webbrowser.open_new(github_url)
|
81 |
+
|
82 |
+
github_label = ttk.Label(bus_frm, text=f"> View on GitHub", style="info.TLabel", justify="left")
|
83 |
+
github_label.pack(anchor="nw")
|
84 |
+
github_label.bind("<Button-1>", github_callback)
|
85 |
+
|
86 |
+
if bilibili_url:
|
87 |
+
def bilibili_callback(event):
|
88 |
+
webbrowser.open_new(bilibili_url)
|
89 |
+
|
90 |
+
bilibili_label = ttk.Label(bus_frm, text=f"> View on bilibili", style="info.TLabel", justify="left")
|
91 |
+
bilibili_label.pack(anchor="nw")
|
92 |
+
bilibili_label.bind("<Button-1>", bilibili_callback)
|
93 |
+
|
94 |
+
if blog_url:
|
95 |
+
def blog_callback(event):
|
96 |
+
webbrowser.open_new(blog_url)
|
97 |
+
|
98 |
+
blog_label = ttk.Label(bus_frm, text=f"> View on blog", style="info.TLabel", justify="left")
|
99 |
+
blog_label.pack(anchor="nw")
|
100 |
+
blog_label.bind("<Button-1>", blog_callback)
|
101 |
+
|
102 |
+
def add_info(self,
|
103 |
+
title: str,
|
104 |
+
info: str):
|
105 |
+
bus_cf = CollapsingFrame(self.frame)
|
106 |
+
bus_cf.pack(fill='x', pady=0)
|
107 |
+
|
108 |
+
bus_frm = ttk.Frame(bus_cf, padding=5)
|
109 |
+
bus_frm.columnconfigure(1, weight=1)
|
110 |
+
bus_cf.add(bus_frm, title=title, style='secondary.TButton', justify="left")
|
111 |
+
|
112 |
+
ttk.Label(bus_frm, text=info, style="TLabel", wraplength=160).pack(anchor="nw")
|
113 |
+
|
114 |
+
# def add_homepage(self, tool):
|
115 |
+
# btn = ttk.Button(self.frame,
|
116 |
+
# text=tool.name,
|
117 |
+
# image=tool.name,
|
118 |
+
# compound='left',
|
119 |
+
# command=tool.bind_func)
|
120 |
+
# btn.pack(side='left', ipadx=5, ipady=5, padx=0, pady=1)
|
121 |
+
def build(self, master, global_info):
|
122 |
+
super(BaseNavigation, self).build(master, global_info)
|
123 |
+
self.frame.place(x=0, y=50, width=180, height=470)
|
124 |
+
|
125 |
+
|
126 |
+
class BaseNoteBook(_Backbone):
|
127 |
+
"""
|
128 |
+
中间Notebook部分框架
|
129 |
+
"""
|
130 |
+
|
131 |
+
def __init__(self,
|
132 |
+
style="primary",
|
133 |
+
tab_names: List[str] = None,
|
134 |
+
stdout=None):
|
135 |
+
super(BaseNoteBook, self).__init__(f_style=style)
|
136 |
+
self.tab_names = tab_names
|
137 |
+
self.nb_frames = list()
|
138 |
+
|
139 |
+
# 初始化总输出行数
|
140 |
+
self.line_len = 2
|
141 |
+
if not stdout:
|
142 |
+
stdout = sys.stdout
|
143 |
+
self.stdout = stdout
|
144 |
+
|
145 |
+
sys.stdout = StdOutWrapper(self.stdout, callback=self._write_log_callback)
|
146 |
+
sys.stderr = StdOutWrapper(self.stdout, callback=self._write_log_callback)
|
147 |
+
|
148 |
+
self.image_cache = DataCache()
|
149 |
+
|
150 |
+
def add_tool(self, tool: BaseNotebookTool, to_notebook=True):
|
151 |
+
|
152 |
+
if tool.tab_index >= len(self.nb_frames):
|
153 |
+
raise ValueError(f"设置的index大小越界,当前页面数量为{len(self.nb_frames)},分别为:{self.nb_frames},而"
|
154 |
+
f"您设置的index为{tool.tab_index},超过了当前页面数量。")
|
155 |
+
if to_notebook:
|
156 |
+
frame = self.nb_frames[tool.tab_index]
|
157 |
+
tool_frame = tool.build(master=frame, global_info=self.global_info)
|
158 |
+
else:
|
159 |
+
tool_frame = tool.build(global_info=self.global_info)
|
160 |
+
tool_info = tool.get_arg_info()
|
161 |
+
self.global_info += tool_info
|
162 |
+
return tool_frame
|
163 |
+
|
164 |
+
def build(self, master, global_info):
|
165 |
+
super(BaseNoteBook, self).build(master, global_info)
|
166 |
+
self.frame.place(x=182, y=55, width=750, height=460)
|
167 |
+
self.nb = ttk.Notebook(self.frame)
|
168 |
+
self.nb.pack(side="top", fill="both")
|
169 |
+
|
170 |
+
if self.tab_names:
|
171 |
+
for tab_name in self.tab_names:
|
172 |
+
sub_frame = ttk.Frame(self.nb)
|
173 |
+
sub_frame.pack(anchor="nw", expand="yes")
|
174 |
+
self.nb_frames.append(sub_frame)
|
175 |
+
self.nb.add(sub_frame, text=tab_name)
|
176 |
+
else:
|
177 |
+
sub_frame = ttk.Frame(self.nb)
|
178 |
+
sub_frame.pack(anchor="nw", expand="yes")
|
179 |
+
self.nb_frames.append(sub_frame)
|
180 |
+
self.nb.add(sub_frame, text="Generate")
|
181 |
+
self.global_info += ArgInfo(name="QGUI-BaseNoteBook",
|
182 |
+
set_func=self._select_notebook_callback,
|
183 |
+
get_func=lambda: print("BaseNoteBook不支持get"))
|
184 |
+
|
185 |
+
# 增加OutPut
|
186 |
+
self.console_frame = ttk.Frame(self.frame,
|
187 |
+
style=self.style + ".TFrame")
|
188 |
+
self.console_frame.pack(side="top", fill='both', expand="yes")
|
189 |
+
|
190 |
+
# 标题
|
191 |
+
self.title = ttk.Label(self.console_frame,
|
192 |
+
font=(FONT, 15),
|
193 |
+
style=self.style + ".Inverse.TLabel",
|
194 |
+
text="Console Log",
|
195 |
+
justify="left")
|
196 |
+
self.title.pack(side="top", fill="x", padx=10, pady=5)
|
197 |
+
|
198 |
+
# 文本
|
199 |
+
self.text_area = ScrolledText(self.console_frame,
|
200 |
+
highlightcolor=master.style.colors.primary,
|
201 |
+
highlightbackground=master.style.colors.border,
|
202 |
+
highlightthickness=1)
|
203 |
+
|
204 |
+
self.text_area.pack(fill="both", expand="yes")
|
205 |
+
|
206 |
+
self.text_area.insert("end", "Console Connected\n")
|
207 |
+
self.text_area.configure(state="disable")
|
208 |
+
|
209 |
+
def print_tool(self, tool: BaseNotebookTool):
|
210 |
+
self.text_area.configure(state="normal")
|
211 |
+
self.text_area.window_create("end", window=self.add_tool(tool, to_notebook=False))
|
212 |
+
self.text_area.configure(state="disable")
|
213 |
+
print("")
|
214 |
+
|
215 |
+
def print_image(self, image):
|
216 |
+
from PIL import Image, ImageTk
|
217 |
+
if isinstance(image, str):
|
218 |
+
image = Image.open(image)
|
219 |
+
w, h = image.size
|
220 |
+
scale = 128 / max(w, h)
|
221 |
+
w *= scale
|
222 |
+
h *= scale
|
223 |
+
image = image.resize((int(w), int(h)))
|
224 |
+
image = ImageTk.PhotoImage(image)
|
225 |
+
self.image_cache += image
|
226 |
+
self.text_area.configure(state="normal")
|
227 |
+
self.text_area.image_create("end", image=image)
|
228 |
+
self.text_area.configure(state="disable")
|
229 |
+
print("")
|
230 |
+
|
231 |
+
def _select_notebook_callback(self, index):
|
232 |
+
self.nb.select(index)
|
233 |
+
|
234 |
+
def _write_log_callback(self, text):
|
235 |
+
self.text_area.configure(state="normal")
|
236 |
+
|
237 |
+
# 对print形式的进度条进行适配
|
238 |
+
if "\r" in text:
|
239 |
+
self.text_area.delete(str(self.line_len) + ".0", str(self.line_len) + ".end")
|
240 |
+
self.line_len -= 1
|
241 |
+
text = text[text.index("\r") + 1:] + " "
|
242 |
+
|
243 |
+
if len(text) > 0 and text != "\n":
|
244 |
+
text = time.strftime("%H:%M:%S", time.localtime()) + "\t" + text
|
245 |
+
|
246 |
+
self.text_area.insert("end", text)
|
247 |
+
self.line_len += 1
|
248 |
+
self.text_area.configure(state="disable")
|
249 |
+
self.text_area.see("end")
|
250 |
+
|
251 |
+
|
252 |
+
class BaseBanner(_Backbone):
|
253 |
+
def __init__(self,
|
254 |
+
title: str = "QGUI Tesing Program",
|
255 |
+
style="primary"):
|
256 |
+
super(BaseBanner, self).__init__(f_style=style)
|
257 |
+
self.img_info = dict()
|
258 |
+
self.title = title
|
259 |
+
|
260 |
+
def add_tool(self, tool: BaseBarTool):
|
261 |
+
"""
|
262 |
+
添加小工具组件
|
263 |
+
:param
|
264 |
+
"""
|
265 |
+
tool.build(master=self.frame, global_info=self.global_info)
|
266 |
+
tool_info = tool.get_arg_info()
|
267 |
+
self.global_info += tool_info
|
268 |
+
|
269 |
+
def build(self, master, global_info):
|
270 |
+
super(BaseBanner, self).build(master, global_info)
|
271 |
+
self.frame.place(x=0, y=0, width=940, height=50)
|
272 |
+
# 占位标题
|
273 |
+
black = tkinter.Frame(self.frame,
|
274 |
+
height=10,
|
275 |
+
bg=TITLE_BG_COLOR)
|
276 |
+
black.pack(side="right", anchor="se")
|
277 |
+
# 主标题
|
278 |
+
title = ttk.Label(self.frame,
|
279 |
+
font=(FONT, 22),
|
280 |
+
text=self.title,
|
281 |
+
style=self.style + ".Inverse.TLabel")
|
282 |
+
title.pack(side="right", anchor="se", padx=5, pady=3)
|
283 |
+
|
284 |
+
|
285 |
+
if __name__ == '__main__':
|
286 |
+
pass
|
cube_qgui/base_tools.py
ADDED
@@ -0,0 +1,212 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Author: Acer Zhang
|
2 |
+
# Datetime:2021/9/21
|
3 |
+
# Copyright belongs to the author.
|
4 |
+
# Please indicate the source for reprinting.
|
5 |
+
import threading
|
6 |
+
import traceback
|
7 |
+
import sys
|
8 |
+
from typing import Dict
|
9 |
+
|
10 |
+
import tkinter
|
11 |
+
|
12 |
+
from cube_qgui.manager import *
|
13 |
+
|
14 |
+
from log_writer import logger
|
15 |
+
|
16 |
+
|
17 |
+
def check_callable(bind_func):
|
18 |
+
if bind_func and not hasattr(bind_func, "__call__"):
|
19 |
+
if hasattr(bind_func, "__name__"):
|
20 |
+
name = bind_func.__name__
|
21 |
+
else:
|
22 |
+
name = bind_func
|
23 |
+
raise Exception(f"{name}的bind_func不能被调用,其至少需要具备__call__方法,建议在此传入函数/方法或自行构建具备__call__方法的对象。\n"
|
24 |
+
f"Example:\n"
|
25 |
+
f" def xxx():\n"
|
26 |
+
f" Do sth\n"
|
27 |
+
f" MakeThisTool(bind_func=xxx)\n"
|
28 |
+
f"Error example:\n"
|
29 |
+
f" def xxx():\n"
|
30 |
+
f" Do sth\n"
|
31 |
+
f" MakeThisTool(bind_func=xxx())")
|
32 |
+
|
33 |
+
|
34 |
+
def make_anchor(anchor):
|
35 |
+
if anchor:
|
36 |
+
r_anchor = str()
|
37 |
+
if TOP in anchor:
|
38 |
+
r_anchor = "n"
|
39 |
+
if BOTTOM in anchor:
|
40 |
+
r_anchor = "s"
|
41 |
+
if LEFT in anchor:
|
42 |
+
r_anchor += "w"
|
43 |
+
if RIGHT in anchor:
|
44 |
+
r_anchor += "e"
|
45 |
+
return r_anchor
|
46 |
+
else:
|
47 |
+
return None
|
48 |
+
|
49 |
+
|
50 |
+
def make_side(side):
|
51 |
+
if side:
|
52 |
+
if side == TOP:
|
53 |
+
side = "top"
|
54 |
+
elif side == BOTTOM:
|
55 |
+
side = "bottom"
|
56 |
+
elif side == LEFT:
|
57 |
+
side = "left"
|
58 |
+
else:
|
59 |
+
side = "right"
|
60 |
+
return side
|
61 |
+
else:
|
62 |
+
return None
|
63 |
+
|
64 |
+
|
65 |
+
class ArgInfo:
|
66 |
+
def __init__(self, name=None, set_func=None, get_func=None):
|
67 |
+
if not name and (set_func or get_func):
|
68 |
+
raise Exception(f"请设置{self.__class__.__name__}的name")
|
69 |
+
if name:
|
70 |
+
self.all_info = {name: self}
|
71 |
+
else:
|
72 |
+
self.all_info = dict()
|
73 |
+
|
74 |
+
check_callable(set_func)
|
75 |
+
check_callable(get_func)
|
76 |
+
self.set_func = set_func
|
77 |
+
self.get_func = get_func
|
78 |
+
|
79 |
+
def set(self, *args, **kwargs):
|
80 |
+
return self.set_func(*args, **kwargs)
|
81 |
+
|
82 |
+
def get(self, *args, **kwargs):
|
83 |
+
return self.get_func(*args, **kwargs)
|
84 |
+
|
85 |
+
def get_info(self):
|
86 |
+
return self.all_info
|
87 |
+
|
88 |
+
def __add__(self, other):
|
89 |
+
other_info = other.all_info
|
90 |
+
if other_info:
|
91 |
+
for info_name in other_info:
|
92 |
+
if info_name in self.all_info:
|
93 |
+
self.all_info[f"{info_name}-QGUI-Conflict-Field-{len(self.all_info)}"] = other_info[info_name]
|
94 |
+
else:
|
95 |
+
self.all_info[info_name] = other_info[info_name]
|
96 |
+
return self
|
97 |
+
|
98 |
+
def __getitem__(self, item):
|
99 |
+
return self.all_info[item]
|
100 |
+
|
101 |
+
|
102 |
+
def select_var_dtype(dtype):
|
103 |
+
if issubclass(dtype, int):
|
104 |
+
return tkinter.IntVar
|
105 |
+
elif issubclass(dtype, float):
|
106 |
+
return tkinter.DoubleVar
|
107 |
+
elif issubclass(dtype, str):
|
108 |
+
return tkinter.StringVar
|
109 |
+
elif issubclass(dtype, bool):
|
110 |
+
return tkinter.BooleanVar
|
111 |
+
|
112 |
+
|
113 |
+
class BaseTool:
|
114 |
+
"""
|
115 |
+
基础工具集,提供基础异步Callback
|
116 |
+
1. 写Build,记得继承才会有self.master,继承时候传**kwargs
|
117 |
+
2. 若需返回信息,请重写get_info方法->ArgInfo
|
118 |
+
3. 如绑定func,需要封装Callback
|
119 |
+
"""
|
120 |
+
|
121 |
+
def __init__(self,
|
122 |
+
bind_func=None,
|
123 |
+
name: str = None,
|
124 |
+
style: str = "primary",
|
125 |
+
async_run: bool = False,
|
126 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG):
|
127 |
+
check_callable(bind_func)
|
128 |
+
self.bind_func = bind_func
|
129 |
+
self.name = name
|
130 |
+
self.style = style + "." if style else ""
|
131 |
+
self.async_run = async_run
|
132 |
+
# 控制并发模式
|
133 |
+
self.concurrency_mode = concurrency_mode
|
134 |
+
|
135 |
+
# 占位符
|
136 |
+
self.global_info = None
|
137 |
+
self.master = None
|
138 |
+
|
139 |
+
# 重复点击的Flag
|
140 |
+
self.async_run_event = threading.Event()
|
141 |
+
self.thread_pool = list()
|
142 |
+
|
143 |
+
def _callback(self, func, start_func=None, end_func=None):
|
144 |
+
"""
|
145 |
+
支持同步和异步的Callback
|
146 |
+
:param func: 函数对象
|
147 |
+
:param start_func: 开始前的函数对象
|
148 |
+
:param end_func: 结束后的函数对象
|
149 |
+
"""
|
150 |
+
if func:
|
151 |
+
if not self.async_run:
|
152 |
+
def render():
|
153 |
+
if start_func:
|
154 |
+
start_func()
|
155 |
+
func(self.global_info.get_info())
|
156 |
+
if end_func:
|
157 |
+
end_func()
|
158 |
+
else:
|
159 |
+
def render():
|
160 |
+
# 若不允许并发则在启动时加Flag
|
161 |
+
if self.async_run_event.is_set():
|
162 |
+
if self.concurrency_mode == ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG:
|
163 |
+
return lambda: print("��前设置为禁止并发,请勿重复点击,因为点了也没用")
|
164 |
+
else:
|
165 |
+
self.async_run_event.set()
|
166 |
+
|
167 |
+
if start_func:
|
168 |
+
start_func()
|
169 |
+
|
170 |
+
def new_func(obj):
|
171 |
+
try:
|
172 |
+
func(obj)
|
173 |
+
except Exception as e:
|
174 |
+
print("-----ERROR MSG START-----")
|
175 |
+
print(traceback.print_exc())
|
176 |
+
print("-----ERROR MSG END-----")
|
177 |
+
|
178 |
+
# Record the error message to the log
|
179 |
+
logger(f"Error: {e}")
|
180 |
+
if end_func:
|
181 |
+
end_func()
|
182 |
+
# 清除Flag,此时按钮可以再次点击
|
183 |
+
self.async_run_event.clear()
|
184 |
+
|
185 |
+
t = threading.Thread(target=new_func, args=(self.global_info.get_info(),))
|
186 |
+
t.setDaemon(True)
|
187 |
+
t.start()
|
188 |
+
|
189 |
+
self.thread_pool.append(t)
|
190 |
+
return render
|
191 |
+
else:
|
192 |
+
def none():
|
193 |
+
pass
|
194 |
+
|
195 |
+
return none
|
196 |
+
|
197 |
+
def build(self, *args, **kwargs) -> tkinter.Frame:
|
198 |
+
self.global_info = kwargs.get("global_info")
|
199 |
+
self.master = kwargs.get("master")
|
200 |
+
|
201 |
+
def get_arg_info(self) -> ArgInfo:
|
202 |
+
return ArgInfo()
|
203 |
+
|
204 |
+
|
205 |
+
if __name__ == '__main__':
|
206 |
+
n = ArgInfo(set_func=lambda: print("a"))
|
207 |
+
a = ArgInfo("A", None, lambda: print("a"))
|
208 |
+
b = ArgInfo("B", None, lambda: print("b"))
|
209 |
+
c = ArgInfo("A", None, lambda: print("a"))
|
210 |
+
n += a + b + c
|
211 |
+
a.get()
|
212 |
+
pass
|
cube_qgui/factory.py
ADDED
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Author: Acer Zhang
|
2 |
+
# Datetime: 2021/8/31
|
3 |
+
# Copyright belongs to the author.
|
4 |
+
# Please indicate the source for reprinting.
|
5 |
+
from typing import List
|
6 |
+
|
7 |
+
import tkinter
|
8 |
+
import tkinter.font
|
9 |
+
from ttkbootstrap import Style
|
10 |
+
|
11 |
+
from cube_qgui.manager import QStyle, FONT
|
12 |
+
from cube_qgui.base_frame import BaseNoteBook, BaseBanner, BaseNavigation
|
13 |
+
from cube_qgui.banner_tools import BaseBarTool
|
14 |
+
from cube_qgui.notebook_tools import BaseNotebookTool
|
15 |
+
from cube_qgui.base_tools import check_callable
|
16 |
+
|
17 |
+
|
18 |
+
class CreateQGUI:
|
19 |
+
"""
|
20 |
+
创建最基础的QGUI程序
|
21 |
+
|
22 |
+
:param title: 主程序标题
|
23 |
+
:param style: 皮肤,需通过QStyle来确定
|
24 |
+
:param stout: 标准输出流
|
25 |
+
:param tab_names: List[str] 功能区Tab页面,默认为“主程序控制台”
|
26 |
+
:param banner: QGUI的Banner对象
|
27 |
+
:param navigation: QGUI的navigation对象
|
28 |
+
:param notebook: QGUI的notebook对象
|
29 |
+
:param bind_func: 全局事件绑定
|
30 |
+
"""
|
31 |
+
|
32 |
+
def __init__(self,
|
33 |
+
title="Unnamed",
|
34 |
+
style: dict = None,
|
35 |
+
stout=None,
|
36 |
+
tab_names: List[str] = None,
|
37 |
+
banner: BaseBanner = None,
|
38 |
+
navigation: BaseNavigation = None,
|
39 |
+
notebook: BaseNoteBook = None,
|
40 |
+
bind_func=None):
|
41 |
+
super().__init__()
|
42 |
+
self.title = title
|
43 |
+
self.style = style
|
44 |
+
|
45 |
+
self.root = tkinter.Tk()
|
46 |
+
if bind_func:
|
47 |
+
check_callable(bind_func=bind_func)
|
48 |
+
self.root.bind_all("<1>", bind_func)
|
49 |
+
if self.style:
|
50 |
+
self.root.style = Style(**self.style)
|
51 |
+
else:
|
52 |
+
self.root.style = Style(**QStyle.default)
|
53 |
+
self.root.style.configure('bg.TFrame', background=self.root.style.colors.inputbg)
|
54 |
+
self.root.style.configure('bg.TLabel', background=self.root.style.colors.inputbg)
|
55 |
+
default_font = tkinter.font.nametofont("TkDefaultFont")
|
56 |
+
default_font.configure(family=FONT, size=10)
|
57 |
+
self.root.option_add("*Font", "TkDefaultFont")
|
58 |
+
self.root.geometry("940x520")
|
59 |
+
self.root.wm_resizable(False, False)
|
60 |
+
self.root.title(self.title)
|
61 |
+
|
62 |
+
# 初始化组件
|
63 |
+
self.banner = banner if banner else BaseBanner(title=self.title)
|
64 |
+
self.navigation = navigation if navigation else BaseNavigation()
|
65 |
+
self.notebook = notebook if notebook else BaseNoteBook(tab_names=tab_names, stdout=stout)
|
66 |
+
|
67 |
+
self.banner.build(self.root, self.get_global_info)
|
68 |
+
self.navigation.build(self.root, self.get_global_info)
|
69 |
+
self.notebook.build(self.root, self.get_global_info)
|
70 |
+
|
71 |
+
@property
|
72 |
+
def get_global_info(self):
|
73 |
+
# ToDo 做个 global_info管理器,目前信息只从Notebook中流出
|
74 |
+
return self.notebook.global_info
|
75 |
+
|
76 |
+
def add_banner_tool(self, tool: BaseBarTool):
|
77 |
+
"""
|
78 |
+
在程序最上方添加小组件
|
79 |
+
:param tool: 继承于BaseBarTool的组件对象
|
80 |
+
|
81 |
+
Example
|
82 |
+
from qgui.banner_tools import GitHub
|
83 |
+
q_gui = CreateQGUI()
|
84 |
+
q_gui.add_banner_tool(GitHub())
|
85 |
+
"""
|
86 |
+
self.banner.add_tool(tool)
|
87 |
+
|
88 |
+
abt = add_banner_tool
|
89 |
+
|
90 |
+
def add_notebook_tool(self, tool: BaseNotebookTool):
|
91 |
+
"""
|
92 |
+
在程序中央功能区添加小组件
|
93 |
+
:param tool: 继承于BaseNotebookTool的组件对象
|
94 |
+
|
95 |
+
Example
|
96 |
+
from qgui.notebook_tools import RunButton
|
97 |
+
q_gui.add_notebook_tool(RunButton())
|
98 |
+
"""
|
99 |
+
self.notebook.add_tool(tool)
|
100 |
+
|
101 |
+
ant = add_notebook_tool
|
102 |
+
|
103 |
+
def set_navigation_about(self,
|
104 |
+
author: str = "Unknow Author",
|
105 |
+
version: str = "N/A",
|
106 |
+
github_url: str = None,
|
107 |
+
bilibili_url: str = None,
|
108 |
+
blog_url: str = None,
|
109 |
+
other_info: List[str] = None):
|
110 |
+
"""
|
111 |
+
设置左侧导航栏的程序基本信息
|
112 |
+
:param author: 作者
|
113 |
+
:param version: 版本号
|
114 |
+
:param github_url: GitHub链接
|
115 |
+
:param bilibili_url: bilibili链接
|
116 |
+
:param blog_url: blog链接
|
117 |
+
"""
|
118 |
+
self.navigation.add_about(author=author,
|
119 |
+
version=version,
|
120 |
+
github_url=github_url,
|
121 |
+
bilibili_url=bilibili_url,
|
122 |
+
blog_url=blog_url,
|
123 |
+
other_info=other_info)
|
124 |
+
|
125 |
+
sna = set_navigation_about
|
126 |
+
|
127 |
+
def set_navigation_info(self,
|
128 |
+
title: str,
|
129 |
+
info: str):
|
130 |
+
"""
|
131 |
+
设置左侧导航栏其他信息
|
132 |
+
:param title: 标题
|
133 |
+
:param info: 信息
|
134 |
+
"""
|
135 |
+
self.navigation.add_info(title=title, info=info)
|
136 |
+
|
137 |
+
sni = set_navigation_info
|
138 |
+
|
139 |
+
def print_tool(self, tool: BaseNotebookTool):
|
140 |
+
"""
|
141 |
+
在终端中打印组件
|
142 |
+
:param tool: 继承于BaseNotebookTool的���件对象
|
143 |
+
"""
|
144 |
+
self.notebook.print_tool(tool)
|
145 |
+
|
146 |
+
def print_image(self, image):
|
147 |
+
"""
|
148 |
+
在终端中打印图像
|
149 |
+
:param image: 图像所在路径 or pillow图片对象
|
150 |
+
"""
|
151 |
+
self.notebook.print_image(image)
|
152 |
+
|
153 |
+
def run(self):
|
154 |
+
"""
|
155 |
+
展示GUI界面
|
156 |
+
"""
|
157 |
+
self.root.mainloop()
|
158 |
+
|
159 |
+
|
160 |
+
if __name__ == '__main__':
|
161 |
+
from qgui.banner_tools import BaseBarTool
|
162 |
+
from qgui.notebook_tools import BaseChooseFileTextButton
|
163 |
+
|
164 |
+
_tmp = CreateQGUI()
|
165 |
+
_tmp.add_banner_tool(BaseBarTool(lambda: print(0)))
|
166 |
+
_tmp.add_notebook_tool(BaseChooseFileTextButton(lambda: print(1)))
|
167 |
+
_tmp.set_navigation_about()
|
168 |
+
_tmp.run()
|
cube_qgui/manager.py
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Author: Acer Zhang
|
2 |
+
# Datetime: 2021/9/14
|
3 |
+
# Copyright belongs to the author.
|
4 |
+
# Please indicate the source for reprinting.
|
5 |
+
|
6 |
+
import os
|
7 |
+
import platform
|
8 |
+
|
9 |
+
from tkinter import messagebox
|
10 |
+
from ttkbootstrap import Style
|
11 |
+
|
12 |
+
import cube_qgui as cqgui
|
13 |
+
|
14 |
+
# 资源部分
|
15 |
+
QGUI_BASE_PATH = os.path.dirname(cqgui.__file__)
|
16 |
+
RESOURCES_PATH = os.path.join(QGUI_BASE_PATH, "resources")
|
17 |
+
ICON_PATH = os.path.join(RESOURCES_PATH, "icon")
|
18 |
+
THEME_PATH = os.path.join(QGUI_BASE_PATH, "theme/ttkbootstrap_themes.json")
|
19 |
+
|
20 |
+
HORIZONTAL = "Horizontal水平方向"
|
21 |
+
VERTICAL = "Vertical垂直方向"
|
22 |
+
LEFT = "左侧"
|
23 |
+
RIGHT = "右侧"
|
24 |
+
TOP = "顶端"
|
25 |
+
BOTTOM = "底部"
|
26 |
+
|
27 |
+
|
28 |
+
# Tools部分
|
29 |
+
class ConcurrencyModeFlag:
|
30 |
+
# QUEUE_ = "触发后相关事件会以队列的形式执行"
|
31 |
+
SAFE_CONCURRENCY_MODE_FLAG = "不允许并发,禁止触发下一个事件"
|
32 |
+
# FORCE_CONCURRENCY_MODE_FLAG = "不允许并发,下一个事件被触发时结束上一个事件"
|
33 |
+
|
34 |
+
|
35 |
+
class QStyle:
|
36 |
+
default = {"theme": "qgui", "themes_file": THEME_PATH}
|
37 |
+
|
38 |
+
lumen = {"theme": "lumen"}
|
39 |
+
|
40 |
+
paddle = {"theme": "paddlelight", "themes_file": THEME_PATH}
|
41 |
+
|
42 |
+
paddle_dark = {"theme": "paddledark", "themes_file": THEME_PATH}
|
43 |
+
|
44 |
+
pytorch = {"theme": "pytorch", "themes_file": THEME_PATH}
|
45 |
+
|
46 |
+
tensorflow = {"theme": "tensorflow", "themes_file": THEME_PATH}
|
47 |
+
|
48 |
+
|
49 |
+
class MessageBox:
|
50 |
+
@staticmethod
|
51 |
+
def info(text: str, title: str = "Info - QGUI"):
|
52 |
+
messagebox.showinfo(title, text)
|
53 |
+
|
54 |
+
@staticmethod
|
55 |
+
def warning(text: str, title: str = "Warning - QGUI"):
|
56 |
+
messagebox.showwarning(title, text)
|
57 |
+
|
58 |
+
@staticmethod
|
59 |
+
def error(text: str, title: str = "Error - QGUI"):
|
60 |
+
messagebox.showerror(title, text)
|
61 |
+
|
62 |
+
|
63 |
+
def show_file_or_path(path, return_func=True):
|
64 |
+
def render(*args, **kwargs):
|
65 |
+
if platform.system().lower() == "darwin":
|
66 |
+
import subprocess
|
67 |
+
subprocess.call(["open", path])
|
68 |
+
else:
|
69 |
+
os.startfile(path)
|
70 |
+
|
71 |
+
if return_func:
|
72 |
+
return render
|
73 |
+
else:
|
74 |
+
return render()
|
75 |
+
|
76 |
+
|
77 |
+
BLACK = "#24262d"
|
78 |
+
GRAY = "#e3e3e3"
|
79 |
+
GREEN = "#76b67e"
|
80 |
+
FONT = "黑体"
|
cube_qgui/notebook_tools.py
ADDED
@@ -0,0 +1,952 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Author: Acer Zhang
|
2 |
+
# Datetime: 2021/9/16
|
3 |
+
# Copyright belongs to the author.
|
4 |
+
# Please indicate the source for reprinting.
|
5 |
+
|
6 |
+
from typing import List, Dict, Tuple
|
7 |
+
from collections import OrderedDict
|
8 |
+
|
9 |
+
import tkinter
|
10 |
+
from tkinter import ttk
|
11 |
+
from tkinter import filedialog
|
12 |
+
|
13 |
+
from cube_qgui.manager import *
|
14 |
+
from cube_qgui.base_tools import ConcurrencyModeFlag, check_callable, ArgInfo, select_var_dtype, BaseTool, make_anchor, \
|
15 |
+
make_side
|
16 |
+
|
17 |
+
RUN_ICON = os.path.join(ICON_PATH, "play_w.png")
|
18 |
+
|
19 |
+
LEFT_PAD_LEN = 10
|
20 |
+
LABEL_WIDTH = 12
|
21 |
+
INPUT_BOX_LEN = 70
|
22 |
+
DEFAULT_PAD = 5
|
23 |
+
|
24 |
+
|
25 |
+
class BaseNotebookTool(BaseTool):
|
26 |
+
"""
|
27 |
+
基础Notebook工具集,提供基础异步Callback
|
28 |
+
1. 写Build,记得继承才会有self.master,继承时候传**kwargs
|
29 |
+
2. 若需返回信息,请重写get_info方法->ArgInfo
|
30 |
+
3. 如绑定func,需要封装Callback
|
31 |
+
"""
|
32 |
+
|
33 |
+
def __init__(self,
|
34 |
+
bind_func=None,
|
35 |
+
name: str = None,
|
36 |
+
style: str = "primary",
|
37 |
+
tab_index: int = 0,
|
38 |
+
async_run: bool = False,
|
39 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
40 |
+
frame: tkinter.Frame = None):
|
41 |
+
super().__init__(bind_func=bind_func,
|
42 |
+
name=name,
|
43 |
+
style=style,
|
44 |
+
async_run=async_run,
|
45 |
+
concurrency_mode=concurrency_mode)
|
46 |
+
self.tab_index = tab_index
|
47 |
+
self.frame = frame
|
48 |
+
|
49 |
+
|
50 |
+
class BaseChooseFileTextButton(BaseNotebookTool):
|
51 |
+
def __init__(self,
|
52 |
+
bind_func=None,
|
53 |
+
name: str = None,
|
54 |
+
label_info: str = "Target File Path",
|
55 |
+
entry_info: str = "Please select file path",
|
56 |
+
button_info: str = "Select File",
|
57 |
+
style: str = "primary",
|
58 |
+
tab_index: int = 0,
|
59 |
+
async_run: bool = False,
|
60 |
+
mode="file",
|
61 |
+
frame: tkinter.Frame = None):
|
62 |
+
super().__init__(bind_func, name=name, style=style, tab_index=tab_index, async_run=async_run, frame=frame)
|
63 |
+
|
64 |
+
self.label_info = label_info
|
65 |
+
self.button_info = button_info
|
66 |
+
self.name = name
|
67 |
+
self.mode = mode
|
68 |
+
|
69 |
+
self.entry_var = tkinter.StringVar(value=entry_info)
|
70 |
+
|
71 |
+
def build(self, **kwargs) -> tkinter.Frame:
|
72 |
+
super().build(**kwargs)
|
73 |
+
if self.frame:
|
74 |
+
frame = self.frame
|
75 |
+
else:
|
76 |
+
frame = ttk.Frame(self.master, style="TFrame")
|
77 |
+
frame.pack(side="top", fill="x", padx=5, pady=2)
|
78 |
+
label = ttk.Label(frame,
|
79 |
+
text=self.label_info,
|
80 |
+
style="TLabel",
|
81 |
+
width=LABEL_WIDTH)
|
82 |
+
label.pack(side="left")
|
83 |
+
entry = ttk.Entry(frame,
|
84 |
+
style=self.style + "info.TEntry",
|
85 |
+
textvariable=self.entry_var)
|
86 |
+
entry.pack(side="left", fill="x", expand="yes", padx=5, pady=2)
|
87 |
+
|
88 |
+
if self.mode == "file":
|
89 |
+
if not hasattr(self, "filetypes"):
|
90 |
+
self.filetypes = [('All Files', '*')]
|
91 |
+
|
92 |
+
def render():
|
93 |
+
file_path = filedialog.askopenfilename(title="Select File",
|
94 |
+
filetypes=self.filetypes)
|
95 |
+
if file_path:
|
96 |
+
self.entry_var.set(file_path)
|
97 |
+
|
98 |
+
else:
|
99 |
+
def render():
|
100 |
+
file_path = filedialog.askdirectory(title="Select Directory")
|
101 |
+
if file_path:
|
102 |
+
self.entry_var.set(file_path)
|
103 |
+
|
104 |
+
command = self._callback(self.bind_func, start_func=render) if self.bind_func else render
|
105 |
+
button = ttk.Button(frame,
|
106 |
+
text=self.button_info,
|
107 |
+
style=self.style + "TButton",
|
108 |
+
command=command,
|
109 |
+
width=12)
|
110 |
+
button.pack(side="right")
|
111 |
+
return frame
|
112 |
+
|
113 |
+
def get_arg_info(self) -> ArgInfo:
|
114 |
+
field = self.name if self.name else self.__class__.__name__
|
115 |
+
arg_info = ArgInfo(name=field, set_func=self.entry_var.set, get_func=self.entry_var.get)
|
116 |
+
|
117 |
+
return arg_info
|
118 |
+
|
119 |
+
|
120 |
+
class ChooseFileTextButton(BaseChooseFileTextButton):
|
121 |
+
def __init__(self,
|
122 |
+
bind_func=None,
|
123 |
+
name: str = None,
|
124 |
+
label_info: str = "Target File Path",
|
125 |
+
entry_info: str = "Please select file path",
|
126 |
+
button_info: str = "Select File",
|
127 |
+
filetypes: bool = None,
|
128 |
+
style: str = "primary",
|
129 |
+
tab_index: int = 0,
|
130 |
+
async_run: bool = False,
|
131 |
+
frame: tkinter.Frame = None):
|
132 |
+
self.filetypes = [('All Files', '*')] if filetypes is None else filetypes
|
133 |
+
|
134 |
+
super().__init__(bind_func=bind_func,
|
135 |
+
name=name,
|
136 |
+
label_info=label_info,
|
137 |
+
entry_info=entry_info,
|
138 |
+
button_info=button_info,
|
139 |
+
style=style,
|
140 |
+
tab_index=tab_index,
|
141 |
+
async_run=async_run,
|
142 |
+
frame=frame)
|
143 |
+
|
144 |
+
|
145 |
+
class ChooseDirTextButton(BaseChooseFileTextButton):
|
146 |
+
def __init__(self,
|
147 |
+
bind_func=None,
|
148 |
+
name=None,
|
149 |
+
label_info: str = "Target Directory Path",
|
150 |
+
entry_info: str = "Please select directory path",
|
151 |
+
button_info: str = "Select Directory",
|
152 |
+
style: str = "primary",
|
153 |
+
tab_index: int = 0,
|
154 |
+
async_run: bool = False,
|
155 |
+
frame: tkinter.Frame = None):
|
156 |
+
super().__init__(bind_func=bind_func,
|
157 |
+
name=name,
|
158 |
+
label_info=label_info,
|
159 |
+
entry_info=entry_info,
|
160 |
+
button_info=button_info,
|
161 |
+
style=style,
|
162 |
+
tab_index=tab_index,
|
163 |
+
async_run=async_run,
|
164 |
+
mode="dir",
|
165 |
+
frame=frame)
|
166 |
+
|
167 |
+
|
168 |
+
class BaseButton(BaseNotebookTool):
|
169 |
+
def __init__(self,
|
170 |
+
bind_func,
|
171 |
+
name: str = None,
|
172 |
+
text: str = "Start",
|
173 |
+
icon: str = None,
|
174 |
+
checked_text: str = None,
|
175 |
+
async_run: bool = True,
|
176 |
+
style: str = "primary",
|
177 |
+
tab_index: int = 0,
|
178 |
+
concurrency_mode: bool = False,
|
179 |
+
side: str = RIGHT,
|
180 |
+
add_width=8,
|
181 |
+
frame: tkinter.Frame = None):
|
182 |
+
super().__init__(bind_func,
|
183 |
+
name=name,
|
184 |
+
style=style,
|
185 |
+
tab_index=tab_index,
|
186 |
+
async_run=async_run,
|
187 |
+
concurrency_mode=concurrency_mode,
|
188 |
+
frame=frame)
|
189 |
+
self.text = text
|
190 |
+
self.checked_text = checked_text
|
191 |
+
self.add_width = add_width
|
192 |
+
self.side = side
|
193 |
+
|
194 |
+
self.icon = icon
|
195 |
+
|
196 |
+
def build(self, **kwargs) -> tkinter.Frame:
|
197 |
+
super().build(**kwargs)
|
198 |
+
if self.frame:
|
199 |
+
frame = self.frame
|
200 |
+
else:
|
201 |
+
frame = ttk.Frame(self.master, style="TFrame")
|
202 |
+
frame.pack(side="top", fill="x", padx=5, pady=5)
|
203 |
+
if self.icon:
|
204 |
+
self.icon = tkinter.PhotoImage(file=self.icon)
|
205 |
+
else:
|
206 |
+
self.icon = None
|
207 |
+
|
208 |
+
self.text_var = tkinter.StringVar(frame, value=self.text)
|
209 |
+
|
210 |
+
def click_btn():
|
211 |
+
self.btn.configure(style=self.style + "TButton")
|
212 |
+
self.btn.configure(state="disable")
|
213 |
+
if self.checked_text:
|
214 |
+
self.text_var.set(self.checked_text)
|
215 |
+
|
216 |
+
def done_btn():
|
217 |
+
self.btn.configure(style=self.style + "TButton")
|
218 |
+
self.btn.configure(state="normal")
|
219 |
+
self.text_var.set(self.text)
|
220 |
+
|
221 |
+
if not self.bind_func:
|
222 |
+
# 不知道为啥必须要有,不然文字不会显示,会头Debug一下
|
223 |
+
self.bind_func = lambda x: None
|
224 |
+
self.btn = ttk.Button(frame,
|
225 |
+
textvariable=self.text_var,
|
226 |
+
image=self.icon,
|
227 |
+
width=len(self.text) + self.add_width,
|
228 |
+
compound='left',
|
229 |
+
command=self._callback(self.bind_func, click_btn, done_btn),
|
230 |
+
style=self.style + "TButton")
|
231 |
+
|
232 |
+
self.btn.pack(side=make_side(self.side), padx=5, pady=5)
|
233 |
+
return frame
|
234 |
+
|
235 |
+
|
236 |
+
class RunButton(BaseButton):
|
237 |
+
def __init__(self,
|
238 |
+
bind_func,
|
239 |
+
name: str = None,
|
240 |
+
text: str = "Start Processing",
|
241 |
+
checked_text: str = "Processing...",
|
242 |
+
async_run: bool = True,
|
243 |
+
style: str = "success",
|
244 |
+
tab_index: int = 0,
|
245 |
+
concurrency_mode: bool = False,
|
246 |
+
side: str = RIGHT,
|
247 |
+
frame: tkinter.Frame = None):
|
248 |
+
super().__init__(bind_func=bind_func,
|
249 |
+
name=name,
|
250 |
+
text=text,
|
251 |
+
checked_text=checked_text,
|
252 |
+
async_run=async_run,
|
253 |
+
style=style,
|
254 |
+
tab_index=tab_index,
|
255 |
+
concurrency_mode=concurrency_mode,
|
256 |
+
add_width=6,
|
257 |
+
icon=RUN_ICON,
|
258 |
+
side=side,
|
259 |
+
frame=frame)
|
260 |
+
|
261 |
+
|
262 |
+
class InputBox(BaseNotebookTool):
|
263 |
+
def __init__(self,
|
264 |
+
name: str = None,
|
265 |
+
default: str = "Please input here...",
|
266 |
+
label_info: str = "InputBox",
|
267 |
+
style: str = "primary",
|
268 |
+
tab_index=0,
|
269 |
+
frame: tkinter.Frame = None):
|
270 |
+
super().__init__(name=name,
|
271 |
+
style=style,
|
272 |
+
tab_index=tab_index,
|
273 |
+
frame=frame)
|
274 |
+
self.input_vars = tkinter.StringVar(value=default)
|
275 |
+
self.label_info = label_info
|
276 |
+
|
277 |
+
def build(self, **kwargs):
|
278 |
+
super().build(**kwargs)
|
279 |
+
if self.frame:
|
280 |
+
frame = self.frame
|
281 |
+
else:
|
282 |
+
frame = ttk.Frame(self.master, style="TFrame")
|
283 |
+
frame.pack(side="top", fill="x", padx=5, pady=5)
|
284 |
+
label = ttk.Label(frame,
|
285 |
+
text=self.label_info,
|
286 |
+
style="TLabel",
|
287 |
+
width=LABEL_WIDTH)
|
288 |
+
label.pack(side="left")
|
289 |
+
|
290 |
+
entry = ttk.Entry(frame,
|
291 |
+
style=self.style + "info.TEntry",
|
292 |
+
textvariable=self.input_vars,
|
293 |
+
width=INPUT_BOX_LEN)
|
294 |
+
entry.pack(side="left", fill="x", padx=5, pady=2)
|
295 |
+
return frame
|
296 |
+
|
297 |
+
def get_arg_info(self) -> ArgInfo:
|
298 |
+
field = self.name if self.name else self.__class__.__name__
|
299 |
+
arg_info = ArgInfo(name=field, set_func=self.input_vars.set, get_func=self.input_vars.get)
|
300 |
+
|
301 |
+
return arg_info
|
302 |
+
|
303 |
+
|
304 |
+
class Combobox(BaseNotebookTool):
|
305 |
+
def __init__(self,
|
306 |
+
bind_func=None,
|
307 |
+
name=None,
|
308 |
+
title: str = "Please select",
|
309 |
+
options: List[str] = None,
|
310 |
+
style="custom",
|
311 |
+
tab_index=0,
|
312 |
+
frame: tkinter.Frame = None):
|
313 |
+
super().__init__(bind_func=bind_func,
|
314 |
+
name=name,
|
315 |
+
style=style,
|
316 |
+
tab_index=tab_index,
|
317 |
+
frame=frame)
|
318 |
+
self.title = title
|
319 |
+
self.options = options
|
320 |
+
|
321 |
+
self.options = options if options else ["--请选择--"]
|
322 |
+
|
323 |
+
def build(self, **kwargs):
|
324 |
+
super().build(**kwargs)
|
325 |
+
if self.frame:
|
326 |
+
frame = self.frame
|
327 |
+
else:
|
328 |
+
frame = ttk.Frame(self.master, style="TFrame")
|
329 |
+
frame.pack(side="top", fill="x", padx=5, pady=5)
|
330 |
+
label = ttk.Label(frame,
|
331 |
+
text=self.title,
|
332 |
+
style="TLabel",
|
333 |
+
width=LABEL_WIDTH)
|
334 |
+
label.pack(side="left")
|
335 |
+
self.comb = ttk.Combobox(frame,
|
336 |
+
style=self.style + "TCombobox",
|
337 |
+
values=self.options)
|
338 |
+
self.comb.current(0)
|
339 |
+
if self.bind_func:
|
340 |
+
self.comb.bind('<<ComboboxSelected>>', self._callback(self.bind_func))
|
341 |
+
self.comb.pack(side="left", padx=5, pady=2)
|
342 |
+
|
343 |
+
return frame
|
344 |
+
|
345 |
+
def get_arg_info(self) -> ArgInfo:
|
346 |
+
field = self.name if self.name else self.__class__.__name__
|
347 |
+
arg_info = ArgInfo(name=field, set_func=self.comb.set, get_func=self.comb.get)
|
348 |
+
|
349 |
+
return arg_info
|
350 |
+
|
351 |
+
|
352 |
+
class Slider(BaseNotebookTool):
|
353 |
+
def __init__(self,
|
354 |
+
name=None,
|
355 |
+
title: str = "Please slide",
|
356 |
+
default: int = 0,
|
357 |
+
min_size: int = 0,
|
358 |
+
max_size: int = 100,
|
359 |
+
dtype=int,
|
360 |
+
style: str = "primary",
|
361 |
+
tab_index: int = 0,
|
362 |
+
frame: tkinter.Frame = None):
|
363 |
+
super().__init__(name=name,
|
364 |
+
style=style,
|
365 |
+
tab_index=tab_index,
|
366 |
+
frame=frame)
|
367 |
+
self.title = title
|
368 |
+
self.default = default
|
369 |
+
self.min_size = min_size
|
370 |
+
self.max_size = max_size
|
371 |
+
self.dtype = dtype
|
372 |
+
|
373 |
+
def slider_var_trace(self, *args):
|
374 |
+
v = self.scale.get()
|
375 |
+
self.value_var.set(f"Current: {self.dtype(v)}")
|
376 |
+
|
377 |
+
def build(self, **kwargs):
|
378 |
+
super().build(**kwargs)
|
379 |
+
if self.frame:
|
380 |
+
frame = self.frame
|
381 |
+
else:
|
382 |
+
frame = ttk.Frame(self.master, style="TFrame")
|
383 |
+
frame.pack(side="top", fill="x", padx=5, pady=5)
|
384 |
+
|
385 |
+
self.slider_var = select_var_dtype(self.dtype)(frame, value=self.default)
|
386 |
+
self.value_var = tkinter.StringVar(frame, value=f"Current: {self.default}")
|
387 |
+
self.slider_var.trace("w", self.slider_var_trace)
|
388 |
+
|
389 |
+
label = ttk.Label(frame,
|
390 |
+
text=self.title,
|
391 |
+
style="TLabel",
|
392 |
+
width=LABEL_WIDTH)
|
393 |
+
label.pack(side="left")
|
394 |
+
self.scale = ttk.Scale(frame,
|
395 |
+
from_=self.min_size,
|
396 |
+
to=self.max_size,
|
397 |
+
value=self.default,
|
398 |
+
variable=self.slider_var)
|
399 |
+
# ToDo ttk 的Bug
|
400 |
+
# self.scale.configure(style="info.TSlider")
|
401 |
+
self.scale.pack(side="left", padx=5, fill="x", expand="yes")
|
402 |
+
self.value = ttk.Label(frame,
|
403 |
+
textvariable=self.value_var,
|
404 |
+
style="TLabel",
|
405 |
+
width=LABEL_WIDTH)
|
406 |
+
self.value.pack(side="right")
|
407 |
+
return frame
|
408 |
+
|
409 |
+
def get_arg_info(self) -> ArgInfo:
|
410 |
+
field = self.name if self.name else self.__class__.__name__
|
411 |
+
arg_info = ArgInfo(name=field, set_func=self.scale.set, get_func=self.scale.get)
|
412 |
+
|
413 |
+
return arg_info
|
414 |
+
|
415 |
+
|
416 |
+
class BaseCheckButton(BaseNotebookTool):
|
417 |
+
def __init__(self,
|
418 |
+
options: str or Tuple[str, bool] or List[Tuple[str, bool]],
|
419 |
+
bind_func=None,
|
420 |
+
name=None,
|
421 |
+
title="Please select",
|
422 |
+
style="primary",
|
423 |
+
button_style="TCheckbutton",
|
424 |
+
tab_index=0,
|
425 |
+
async_run=False,
|
426 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
427 |
+
mode=None,
|
428 |
+
frame: tkinter.Frame = None):
|
429 |
+
super().__init__(bind_func=bind_func,
|
430 |
+
name=name,
|
431 |
+
style=style,
|
432 |
+
tab_index=tab_index,
|
433 |
+
async_run=async_run,
|
434 |
+
concurrency_mode=concurrency_mode,
|
435 |
+
frame=frame)
|
436 |
+
self.title = title
|
437 |
+
self.mode = mode
|
438 |
+
if isinstance(options, str):
|
439 |
+
self.options = {options: 0}
|
440 |
+
if isinstance(options, tuple):
|
441 |
+
self.options = {options[0]: 1 if options[1] else 0}
|
442 |
+
if isinstance(options, list):
|
443 |
+
self.options = OrderedDict()
|
444 |
+
if len(options[0]) != 2:
|
445 |
+
raise TypeError(f"The 'options' arg of {self.__class__.__name__} should be str or List[Tuple[str, bool]] format\n"
|
446 |
+
f"Example:\n"
|
447 |
+
f"'选择框1' or [('选择1', 0), ('选择2', 1), ('选择3', 0)]")
|
448 |
+
for option in options:
|
449 |
+
self.options[option[0]] = 1 if option[1] else 0
|
450 |
+
self.button_style = button_style
|
451 |
+
|
452 |
+
def build(self, *args, **kwargs):
|
453 |
+
super().build(*args, **kwargs)
|
454 |
+
if self.frame:
|
455 |
+
frame = self.frame
|
456 |
+
else:
|
457 |
+
frame = ttk.Frame(self.master, style="TFrame")
|
458 |
+
frame.pack(side="top", fill="x", padx=5, pady=5)
|
459 |
+
label = ttk.Label(frame,
|
460 |
+
text=self.title,
|
461 |
+
style="TLabel",
|
462 |
+
width=LABEL_WIDTH)
|
463 |
+
label.pack(side="left")
|
464 |
+
|
465 |
+
self.value_vars = dict()
|
466 |
+
for option in self.options:
|
467 |
+
self.value_vars[option] = tkinter.StringVar(frame, value=self.options[option])
|
468 |
+
if self.mode == "ToolButton":
|
469 |
+
pad_x = 0
|
470 |
+
else:
|
471 |
+
pad_x = 5
|
472 |
+
ttk.Checkbutton(frame,
|
473 |
+
text=option,
|
474 |
+
style=self.style + self.button_style,
|
475 |
+
variable=self.value_vars[option],
|
476 |
+
command=self._callback(self.bind_func)).pack(side="left", padx=pad_x)
|
477 |
+
return frame
|
478 |
+
|
479 |
+
def get_arg_info(self) -> ArgInfo:
|
480 |
+
field = self.name if self.name else self.__class__.__name__
|
481 |
+
arg_info = ArgInfo()
|
482 |
+
for v in self.value_vars:
|
483 |
+
arg_info += ArgInfo(name=field + "-" + v, set_func=self.value_vars[v].set, get_func=self.value_vars[v].get)
|
484 |
+
|
485 |
+
return arg_info
|
486 |
+
|
487 |
+
|
488 |
+
class CheckButton(BaseCheckButton):
|
489 |
+
def __init__(self,
|
490 |
+
options: str or Tuple[str] or List[Tuple[str, bool]],
|
491 |
+
bind_func=None,
|
492 |
+
name=None,
|
493 |
+
title="Please select",
|
494 |
+
style="primary",
|
495 |
+
tab_index=0,
|
496 |
+
async_run=False,
|
497 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
498 |
+
frame: tkinter.Frame = None):
|
499 |
+
super().__init__(options=options,
|
500 |
+
bind_func=bind_func,
|
501 |
+
name=name,
|
502 |
+
title=title,
|
503 |
+
style=style,
|
504 |
+
button_style="TCheckbutton",
|
505 |
+
tab_index=tab_index,
|
506 |
+
async_run=async_run,
|
507 |
+
concurrency_mode=concurrency_mode,
|
508 |
+
frame=frame)
|
509 |
+
|
510 |
+
|
511 |
+
class CheckToolButton(BaseCheckButton):
|
512 |
+
def __init__(self,
|
513 |
+
options: str or Tuple[str] or List[Tuple[str, bool]],
|
514 |
+
bind_func=None,
|
515 |
+
name=None,
|
516 |
+
title="Please select",
|
517 |
+
style="info",
|
518 |
+
tab_index=0,
|
519 |
+
async_run=False,
|
520 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
521 |
+
frame: tkinter.Frame = None):
|
522 |
+
super().__init__(options=options,
|
523 |
+
bind_func=bind_func,
|
524 |
+
name=name,
|
525 |
+
title=title,
|
526 |
+
style=style,
|
527 |
+
button_style="Toolbutton",
|
528 |
+
tab_index=tab_index,
|
529 |
+
async_run=async_run,
|
530 |
+
concurrency_mode=concurrency_mode,
|
531 |
+
mode="ToolButton",
|
532 |
+
frame=frame)
|
533 |
+
|
534 |
+
|
535 |
+
class CheckObviousToolButton(BaseCheckButton):
|
536 |
+
def __init__(self,
|
537 |
+
options: str or Tuple[str] or List[Tuple[str, bool]],
|
538 |
+
bind_func=None,
|
539 |
+
name=None,
|
540 |
+
title="Please select",
|
541 |
+
style="primary",
|
542 |
+
tab_index=0,
|
543 |
+
async_run=False,
|
544 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
545 |
+
frame: tkinter.Frame = None):
|
546 |
+
super().__init__(options=options,
|
547 |
+
bind_func=bind_func,
|
548 |
+
name=name,
|
549 |
+
title=title,
|
550 |
+
style=style,
|
551 |
+
button_style="Outline.Toolbutton",
|
552 |
+
tab_index=tab_index,
|
553 |
+
async_run=async_run,
|
554 |
+
concurrency_mode=concurrency_mode,
|
555 |
+
mode="ToolButton",
|
556 |
+
frame=frame)
|
557 |
+
|
558 |
+
|
559 |
+
class ToggleButton(BaseCheckButton):
|
560 |
+
def __init__(self,
|
561 |
+
options: str or Tuple[str],
|
562 |
+
bind_func=None,
|
563 |
+
name=None,
|
564 |
+
title="Please select",
|
565 |
+
style="primary",
|
566 |
+
tab_index=0,
|
567 |
+
async_run=False,
|
568 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
569 |
+
frame: tkinter.Frame = None):
|
570 |
+
assert not isinstance(options, list), "There should be only one option for ToggleButton"
|
571 |
+
super().__init__(options=options,
|
572 |
+
bind_func=bind_func,
|
573 |
+
name=name,
|
574 |
+
title=title,
|
575 |
+
style=style,
|
576 |
+
button_style="Roundtoggle.Toolbutton",
|
577 |
+
tab_index=tab_index,
|
578 |
+
async_run=async_run,
|
579 |
+
concurrency_mode=concurrency_mode,
|
580 |
+
frame=frame)
|
581 |
+
|
582 |
+
|
583 |
+
class BaseRadioButton(BaseNotebookTool):
|
584 |
+
def __init__(self,
|
585 |
+
options: str or List[str],
|
586 |
+
default: str = None,
|
587 |
+
bind_func=None,
|
588 |
+
name=None,
|
589 |
+
title="Please select",
|
590 |
+
style="primary",
|
591 |
+
button_style="TRadiobutton",
|
592 |
+
tab_index=0,
|
593 |
+
async_run=False,
|
594 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
595 |
+
mode=None,
|
596 |
+
frame: tkinter.Frame = None):
|
597 |
+
super().__init__(bind_func=bind_func,
|
598 |
+
name=name,
|
599 |
+
style=style,
|
600 |
+
tab_index=tab_index,
|
601 |
+
async_run=async_run,
|
602 |
+
concurrency_mode=concurrency_mode,
|
603 |
+
frame=frame)
|
604 |
+
self.title = title
|
605 |
+
self.mode = mode
|
606 |
+
self.options = [options] if isinstance(options, str) else options
|
607 |
+
self.default = default if default else options[0]
|
608 |
+
self.button_style = button_style
|
609 |
+
|
610 |
+
def build(self, *args, **kwargs):
|
611 |
+
super().build(*args, **kwargs)
|
612 |
+
if self.frame:
|
613 |
+
frame = self.frame
|
614 |
+
else:
|
615 |
+
frame = ttk.Frame(self.master, style="TFrame")
|
616 |
+
frame.pack(side="top", fill="x", padx=5, pady=5)
|
617 |
+
|
618 |
+
label = ttk.Label(frame,
|
619 |
+
text=self.title,
|
620 |
+
style="TLabel",
|
621 |
+
width=LABEL_WIDTH)
|
622 |
+
label.pack(side="left")
|
623 |
+
|
624 |
+
self.value_var = tkinter.StringVar(frame, value=self.options[0])
|
625 |
+
for option in self.options:
|
626 |
+
if self.mode == "ToolButton":
|
627 |
+
pad_x = 0
|
628 |
+
else:
|
629 |
+
pad_x = 5
|
630 |
+
ttk.Radiobutton(frame,
|
631 |
+
text=option,
|
632 |
+
style=self.style + self.button_style,
|
633 |
+
variable=self.value_var,
|
634 |
+
value=option,
|
635 |
+
command=self._callback(self.bind_func)).pack(side="left", padx=pad_x)
|
636 |
+
return frame
|
637 |
+
|
638 |
+
def get_arg_info(self) -> ArgInfo:
|
639 |
+
field = self.name if self.name else self.__class__.__name__
|
640 |
+
arg_info = ArgInfo(name=field, set_func=self.value_var.set, get_func=self.value_var.get)
|
641 |
+
|
642 |
+
return arg_info
|
643 |
+
|
644 |
+
|
645 |
+
class RadioButton(BaseRadioButton):
|
646 |
+
def __init__(self,
|
647 |
+
options: str or List[str],
|
648 |
+
default: str = None,
|
649 |
+
bind_func=None,
|
650 |
+
name=None,
|
651 |
+
title="Please select",
|
652 |
+
style="primary",
|
653 |
+
tab_index=0,
|
654 |
+
async_run=False,
|
655 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
656 |
+
frame: tkinter.Frame = None):
|
657 |
+
super().__init__(options=options,
|
658 |
+
default=default,
|
659 |
+
bind_func=bind_func,
|
660 |
+
name=name,
|
661 |
+
title=title,
|
662 |
+
style=style,
|
663 |
+
button_style="TRadiobutton",
|
664 |
+
tab_index=tab_index,
|
665 |
+
async_run=async_run,
|
666 |
+
concurrency_mode=concurrency_mode,
|
667 |
+
mode=None,
|
668 |
+
frame=frame)
|
669 |
+
|
670 |
+
|
671 |
+
class RadioToolButton(BaseRadioButton):
|
672 |
+
def __init__(self,
|
673 |
+
options: str or List[str],
|
674 |
+
default: str = None,
|
675 |
+
bind_func=None,
|
676 |
+
name=None,
|
677 |
+
title="Please select",
|
678 |
+
style="info",
|
679 |
+
tab_index=0,
|
680 |
+
async_run=False,
|
681 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
682 |
+
frame: tkinter.Frame = None):
|
683 |
+
super().__init__(options=options,
|
684 |
+
default=default,
|
685 |
+
bind_func=bind_func,
|
686 |
+
name=name,
|
687 |
+
title=title,
|
688 |
+
style=style,
|
689 |
+
button_style="Toolbutton",
|
690 |
+
tab_index=tab_index,
|
691 |
+
async_run=async_run,
|
692 |
+
concurrency_mode=concurrency_mode,
|
693 |
+
mode="ToolButton",
|
694 |
+
frame=frame)
|
695 |
+
|
696 |
+
|
697 |
+
class RadioObviousToolButton(BaseRadioButton):
|
698 |
+
def __init__(self,
|
699 |
+
options: str or List[str],
|
700 |
+
default: str = None,
|
701 |
+
bind_func=None,
|
702 |
+
name=None,
|
703 |
+
title="Please select",
|
704 |
+
style="primary",
|
705 |
+
tab_index=0,
|
706 |
+
async_run=False,
|
707 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
708 |
+
frame: tkinter.Frame = None):
|
709 |
+
super().__init__(options=options,
|
710 |
+
default=default,
|
711 |
+
bind_func=bind_func,
|
712 |
+
name=name,
|
713 |
+
title=title,
|
714 |
+
style=style,
|
715 |
+
button_style="Outline.Toolbutton",
|
716 |
+
tab_index=tab_index,
|
717 |
+
async_run=async_run,
|
718 |
+
concurrency_mode=concurrency_mode,
|
719 |
+
mode="ToolButton",
|
720 |
+
frame=frame)
|
721 |
+
|
722 |
+
|
723 |
+
class Progressbar(BaseNotebookTool):
|
724 |
+
def __init__(self,
|
725 |
+
title: str = "Progressbar",
|
726 |
+
default: int = 0,
|
727 |
+
max_size: int = 100,
|
728 |
+
name: str = None,
|
729 |
+
style: str = "primary",
|
730 |
+
tab_index: int = 0,
|
731 |
+
async_run: bool = False,
|
732 |
+
concurrency_mode=ConcurrencyModeFlag.SAFE_CONCURRENCY_MODE_FLAG,
|
733 |
+
frame: tkinter.Frame = None):
|
734 |
+
super().__init__(name=name,
|
735 |
+
style=style,
|
736 |
+
tab_index=tab_index,
|
737 |
+
async_run=async_run,
|
738 |
+
concurrency_mode=concurrency_mode,
|
739 |
+
frame=frame)
|
740 |
+
self.title = title
|
741 |
+
self.default_value = default
|
742 |
+
self.max_size = max_size
|
743 |
+
|
744 |
+
def progressbar_var_trace(self, *args):
|
745 |
+
v = self.progressbar_var.get()
|
746 |
+
self.value_var.set(f"Progress {v:.2f}%")
|
747 |
+
|
748 |
+
def build(self, *args, **kwargs):
|
749 |
+
super().build(*args, **kwargs)
|
750 |
+
if self.frame:
|
751 |
+
frame = self.frame
|
752 |
+
else:
|
753 |
+
frame = ttk.Frame(self.master, style="TFrame")
|
754 |
+
frame.pack(side="top", fill="x", padx=5, pady=5, expand="yes")
|
755 |
+
|
756 |
+
self.progressbar_var = tkinter.IntVar(frame, value=self.default_value)
|
757 |
+
self.value_var = tkinter.StringVar(frame, value=f"Progress: {self.default_value:.2f}%")
|
758 |
+
self.progressbar_var.trace("w", self.progressbar_var_trace)
|
759 |
+
|
760 |
+
label = ttk.Label(frame,
|
761 |
+
text=self.title,
|
762 |
+
style="TLabel",
|
763 |
+
width=LABEL_WIDTH)
|
764 |
+
label.pack(side="left")
|
765 |
+
|
766 |
+
progressbar = ttk.Progressbar(frame,
|
767 |
+
variable=self.progressbar_var,
|
768 |
+
style=self.style + "Striped.Horizontal.TProgressbar")
|
769 |
+
progressbar.pack(side="left", fill="x", expand="yes", padx=5, pady=2)
|
770 |
+
|
771 |
+
self.value = ttk.Label(frame,
|
772 |
+
textvariable=self.value_var,
|
773 |
+
style="TLabel",
|
774 |
+
width=LABEL_WIDTH)
|
775 |
+
self.value.pack(side="left")
|
776 |
+
return frame
|
777 |
+
|
778 |
+
def get_arg_info(self) -> ArgInfo:
|
779 |
+
field = self.name if self.name else self.__class__.__name__
|
780 |
+
arg_info = ArgInfo(name=field, set_func=self.progressbar_var.set, get_func=self.progressbar_var.get)
|
781 |
+
|
782 |
+
return arg_info
|
783 |
+
|
784 |
+
|
785 |
+
class BaseCombine(BaseNotebookTool):
|
786 |
+
def __init__(self,
|
787 |
+
tools: BaseNotebookTool or List[BaseNotebookTool],
|
788 |
+
side=HORIZONTAL,
|
789 |
+
title: str = None,
|
790 |
+
text: str = None,
|
791 |
+
style: str = None,
|
792 |
+
tab_index: int = None,
|
793 |
+
frame: tkinter.Frame = None):
|
794 |
+
super().__init__(tab_index=tab_index, style=style, frame=frame)
|
795 |
+
self.side = "top" if side == HORIZONTAL else "left"
|
796 |
+
self.title = title
|
797 |
+
self.text = text
|
798 |
+
|
799 |
+
self.tools = tools if isinstance(tools, list) else [tools]
|
800 |
+
|
801 |
+
self.tab_index = tab_index if tab_index else self.tools[0].tab_index
|
802 |
+
|
803 |
+
for tool_id in range(len(self.tools)):
|
804 |
+
self.tools[tool_id].tab_index = self.tab_index
|
805 |
+
|
806 |
+
def get_arg_info(self) -> ArgInfo:
|
807 |
+
local_info = ArgInfo()
|
808 |
+
for tool_id in range(len(self.tools)):
|
809 |
+
local_info += self.tools[tool_id].get_arg_info()
|
810 |
+
return local_info
|
811 |
+
|
812 |
+
|
813 |
+
class BaseFrameCombine(BaseCombine):
|
814 |
+
def build(self, *args, **kwargs):
|
815 |
+
super().build(self, *args, **kwargs)
|
816 |
+
|
817 |
+
if self.frame:
|
818 |
+
frame = self.frame
|
819 |
+
else:
|
820 |
+
style_mode = "TLabelframe" if self.title else "TFrame"
|
821 |
+
if self.title:
|
822 |
+
frame = ttk.LabelFrame(self.master, text=self.title, style=self.style + style_mode)
|
823 |
+
else:
|
824 |
+
frame = ttk.Frame(self.master, text=self.title, style=self.style + style_mode)
|
825 |
+
frame.pack(side="left", anchor="nw", fill="both", expand="yes", padx=DEFAULT_PAD, pady=DEFAULT_PAD)
|
826 |
+
if self.text:
|
827 |
+
label = ttk.Label(frame,
|
828 |
+
text=self.text,
|
829 |
+
style="TLabel")
|
830 |
+
label.pack(side="top", anchor="nw", padx=5)
|
831 |
+
for tool in self.tools:
|
832 |
+
kwargs["master"] = frame
|
833 |
+
tool.build(*args, **kwargs)
|
834 |
+
return frame
|
835 |
+
|
836 |
+
|
837 |
+
class HorizontalFrameCombine(BaseFrameCombine):
|
838 |
+
def __init__(self,
|
839 |
+
tools: BaseNotebookTool or List[BaseNotebookTool],
|
840 |
+
title=None,
|
841 |
+
style: str = None,
|
842 |
+
text: str = None,
|
843 |
+
tab_index: int = 0,
|
844 |
+
frame: tkinter.Frame = None):
|
845 |
+
super().__init__(tools=tools,
|
846 |
+
side=HORIZONTAL,
|
847 |
+
title=title,
|
848 |
+
style=style,
|
849 |
+
text=text,
|
850 |
+
tab_index=tab_index,
|
851 |
+
frame=frame)
|
852 |
+
|
853 |
+
|
854 |
+
class VerticalFrameCombine(BaseFrameCombine):
|
855 |
+
def __init__(self,
|
856 |
+
tools: BaseNotebookTool or List[BaseNotebookTool],
|
857 |
+
title=None,
|
858 |
+
style: str = None,
|
859 |
+
text: str = None,
|
860 |
+
tab_index: int = 0,
|
861 |
+
frame: tkinter.Frame = None):
|
862 |
+
super().__init__(tools=tools,
|
863 |
+
side=VERTICAL,
|
864 |
+
title=title,
|
865 |
+
style=style,
|
866 |
+
text=text,
|
867 |
+
tab_index=tab_index,
|
868 |
+
frame=frame)
|
869 |
+
|
870 |
+
|
871 |
+
class HorizontalToolsCombine(BaseCombine):
|
872 |
+
def __init__(self,
|
873 |
+
tools: BaseNotebookTool or List[BaseNotebookTool],
|
874 |
+
title=None,
|
875 |
+
style: str = None,
|
876 |
+
text: str = None,
|
877 |
+
tab_index: int = None,
|
878 |
+
frame: tkinter.Frame = None):
|
879 |
+
super().__init__(tools=tools,
|
880 |
+
side=HORIZONTAL,
|
881 |
+
title=title,
|
882 |
+
style=style,
|
883 |
+
text=text,
|
884 |
+
tab_index=tab_index,
|
885 |
+
frame=frame)
|
886 |
+
|
887 |
+
def build(self, *args, **kwargs):
|
888 |
+
super().build(self, *args, **kwargs)
|
889 |
+
|
890 |
+
style_mode = "TLabelframe" if self.title else "TFrame"
|
891 |
+
if self.title:
|
892 |
+
frame = ttk.LabelFrame(self.master, text=self.title, style=self.style + style_mode)
|
893 |
+
else:
|
894 |
+
frame = ttk.Frame(self.master, style=self.style + style_mode)
|
895 |
+
frame.pack(side="top", fill="x", padx=DEFAULT_PAD, pady=DEFAULT_PAD)
|
896 |
+
if self.text:
|
897 |
+
label = ttk.Label(frame,
|
898 |
+
text=self.text,
|
899 |
+
style="TLabel")
|
900 |
+
label.pack(side="top", anchor="nw", padx=DEFAULT_PAD)
|
901 |
+
for tool in self.tools:
|
902 |
+
kwargs["master"] = self.frame
|
903 |
+
tool.frame = frame
|
904 |
+
tool.build(*args, **kwargs)
|
905 |
+
return frame
|
906 |
+
|
907 |
+
|
908 |
+
class Label(BaseNotebookTool):
|
909 |
+
def __init__(self,
|
910 |
+
name: str = None,
|
911 |
+
text: str = None,
|
912 |
+
title: str = None,
|
913 |
+
alignment: str = LEFT + TOP,
|
914 |
+
style: str = "primary",
|
915 |
+
tab_index: int = 0,
|
916 |
+
frame: tkinter.Frame = None):
|
917 |
+
super(Label, self).__init__(name=name,
|
918 |
+
style=style,
|
919 |
+
tab_index=tab_index,
|
920 |
+
frame=frame)
|
921 |
+
self.text = text
|
922 |
+
self.title = title
|
923 |
+
self.alignment = alignment
|
924 |
+
|
925 |
+
self.label_var = tkinter.StringVar(value=self.text)
|
926 |
+
|
927 |
+
def build(self, *args, **kwargs) -> tkinter.Frame:
|
928 |
+
super(Label, self).build(*args, **kwargs)
|
929 |
+
if self.frame:
|
930 |
+
frame = self.frame
|
931 |
+
else:
|
932 |
+
frame = ttk.Frame(self.master)
|
933 |
+
frame.pack(side="top", fill="both", padx=DEFAULT_PAD, pady=DEFAULT_PAD)
|
934 |
+
|
935 |
+
title = ttk.Label(frame,
|
936 |
+
text=self.title,
|
937 |
+
style="TLabel",
|
938 |
+
width=LABEL_WIDTH)
|
939 |
+
title.pack(side="left")
|
940 |
+
|
941 |
+
label = ttk.Label(frame,
|
942 |
+
text=self.text,
|
943 |
+
textvariable=self.label_var,
|
944 |
+
style="TLabel")
|
945 |
+
# make_anchor(self.alignment)
|
946 |
+
label.pack(anchor=make_anchor(self.alignment), padx=DEFAULT_PAD)
|
947 |
+
return frame
|
948 |
+
|
949 |
+
def get_arg_info(self) -> ArgInfo:
|
950 |
+
field = self.name if self.name else self.__class__.__name__
|
951 |
+
local_info = ArgInfo(field, set_func=self.label_var.set, get_func=self.label_var.get)
|
952 |
+
return local_info
|
cube_qgui/os_tools.py
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Author: Acer Zhang
|
2 |
+
# Datetime: 2021/9/17
|
3 |
+
# Copyright belongs to the author.
|
4 |
+
# Please indicate the source for reprinting.
|
5 |
+
import sys
|
6 |
+
|
7 |
+
|
8 |
+
class StdOutWrapper:
|
9 |
+
def __init__(self, stdout, callback=None, do_print=True):
|
10 |
+
self.buff = ""
|
11 |
+
self.stdout = stdout
|
12 |
+
self.callback = callback
|
13 |
+
self.do_print = do_print
|
14 |
+
|
15 |
+
def write(self, output_stream):
|
16 |
+
self.buff += output_stream
|
17 |
+
if self.do_print:
|
18 |
+
self.stdout.write(output_stream)
|
19 |
+
if self.callback and ("\n" in self.buff or "\r" in output_stream):
|
20 |
+
self.callback(self.buff)
|
21 |
+
self.buff = ""
|
22 |
+
|
23 |
+
def flush(self):
|
24 |
+
self.buff = ""
|
25 |
+
|
26 |
+
def __del__(self):
|
27 |
+
sys.stdout = self.stdout
|
28 |
+
|
29 |
+
|
30 |
+
class DataCache:
|
31 |
+
def __init__(self, seq_len=10, cache=7):
|
32 |
+
assert seq_len >= cache, "请设置seq_len的值低于cache"
|
33 |
+
self.seq_len = seq_len
|
34 |
+
self.cache = cache
|
35 |
+
self.seq = list()
|
36 |
+
|
37 |
+
def add(self, item):
|
38 |
+
if len(self.seq) == self.seq_len:
|
39 |
+
for i in range(self.seq_len - self.cache):
|
40 |
+
self.seq.pop(0)
|
41 |
+
self.seq.append(item)
|
42 |
+
|
43 |
+
def __add__(self, other):
|
44 |
+
self.add(other)
|
45 |
+
return self
|
cube_qgui/resources/demo/panda.jpg
ADDED
![]() |
cube_qgui/resources/icon/double_down.png
ADDED
![]() |
cube_qgui/resources/icon/double_up.png
ADDED
![]() |
cube_qgui/resources/icon/github.png
ADDED
![]() |
cube_qgui/resources/icon/play_w.png
ADDED
![]() |
cube_qgui/resources/icon/up_cloud.png
ADDED
![]() |
cube_qgui/theme/ttkbootstrap_themes.json
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"themes": [
|
3 |
+
{
|
4 |
+
"name": "paddlelight",
|
5 |
+
"font": "Helvetica 10",
|
6 |
+
"type": "light",
|
7 |
+
"colors": {
|
8 |
+
"primary": "#2932e1",
|
9 |
+
"secondary": "#4e9bf8",
|
10 |
+
"success": "#18bc9c",
|
11 |
+
"info": "#3498db",
|
12 |
+
"warning": "#f39c12",
|
13 |
+
"danger": "#e74c3c",
|
14 |
+
"bg": "#ffffff",
|
15 |
+
"fg": "#212529",
|
16 |
+
"selectbg": "#95a5a6",
|
17 |
+
"selectfg": "#ffffff",
|
18 |
+
"border": "#ced4da",
|
19 |
+
"inputfg": "#212529",
|
20 |
+
"inputbg": "#ecf0f1"
|
21 |
+
}
|
22 |
+
},
|
23 |
+
{
|
24 |
+
"name": "paddledark",
|
25 |
+
"font": "Helvetica 10",
|
26 |
+
"type": "dark",
|
27 |
+
"colors": {
|
28 |
+
"primary": "#2932e1",
|
29 |
+
"secondary": "#4e9bf8",
|
30 |
+
"success": "#00bc8c",
|
31 |
+
"info": "#3498db",
|
32 |
+
"warning": "#f39c12",
|
33 |
+
"danger": "#e74c3c",
|
34 |
+
"bg": "#666666",
|
35 |
+
"fg": "#ffffff",
|
36 |
+
"selectbg": "#444444",
|
37 |
+
"selectfg": "#ffffff",
|
38 |
+
"border": "#222222",
|
39 |
+
"inputfg": "#333333",
|
40 |
+
"inputbg": "#adb5bd"
|
41 |
+
}
|
42 |
+
},
|
43 |
+
{
|
44 |
+
"name": "pytorch",
|
45 |
+
"font": "Helvetica 10",
|
46 |
+
"type": "light",
|
47 |
+
"colors": {
|
48 |
+
"primary": "#ee4c2c",
|
49 |
+
"secondary": "#db593a",
|
50 |
+
"success": "#18bc9c",
|
51 |
+
"info": "#3498db",
|
52 |
+
"warning": "#f39c12",
|
53 |
+
"danger": "#e74c3c",
|
54 |
+
"bg": "#ffffff",
|
55 |
+
"fg": "#212529",
|
56 |
+
"selectbg": "#321e5d",
|
57 |
+
"selectfg": "#ffffff",
|
58 |
+
"border": "#ced4da",
|
59 |
+
"inputfg": "#212529",
|
60 |
+
"inputbg": "#ecf0f1"
|
61 |
+
}
|
62 |
+
},
|
63 |
+
{
|
64 |
+
"name": "tensorflow",
|
65 |
+
"font": "Helvetica 10",
|
66 |
+
"type": "light",
|
67 |
+
"colors": {
|
68 |
+
"primary": "#ed722f",
|
69 |
+
"secondary": "#95a5a6",
|
70 |
+
"success": "#18bc9c",
|
71 |
+
"info": "#3498db",
|
72 |
+
"warning": "#f39c12",
|
73 |
+
"danger": "#e74c3c",
|
74 |
+
"bg": "#ffffff",
|
75 |
+
"fg": "#212529",
|
76 |
+
"selectbg": "#95a5a6",
|
77 |
+
"selectfg": "#ffffff",
|
78 |
+
"border": "#ced4da",
|
79 |
+
"inputfg": "#212529",
|
80 |
+
"inputbg": "#ecf0f1"
|
81 |
+
}
|
82 |
+
},
|
83 |
+
{
|
84 |
+
"name": "qgui",
|
85 |
+
"font": "Helvetica 10",
|
86 |
+
"type": "light",
|
87 |
+
"colors": {
|
88 |
+
"primary": "#2c3e50",
|
89 |
+
"secondary": "#4e9bf8",
|
90 |
+
"success": "#18bc9c",
|
91 |
+
"info": "#3498db",
|
92 |
+
"warning": "#f39c12",
|
93 |
+
"danger": "#e74c3c",
|
94 |
+
"bg": "#ffffff",
|
95 |
+
"fg": "#212529",
|
96 |
+
"selectbg": "#95a5a6",
|
97 |
+
"selectfg": "#ffffff",
|
98 |
+
"border": "#ced4da",
|
99 |
+
"inputfg": "#212529",
|
100 |
+
"inputbg": "#ecf0f1"
|
101 |
+
}
|
102 |
+
}
|
103 |
+
]
|
104 |
+
}
|
cube_qgui/third_party/__init__.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Author: Acer Zhang
|
2 |
+
# Datetime: 2021/9/17
|
3 |
+
# Copyright belongs to the author.
|
4 |
+
# Please indicate the source for reprinting.
|