mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
_tkinter pt. 1 (#5583)
* Add _tkinter module and gate * add tkinter module * add tcl_error * fix tk setup and add demo * fix TK_VERSION * create and TkApp
This commit is contained in:
1 parent
c585678ec9
commit
9779de98b8
18 files changed
+9112
-15
No files matched your search
Generated
+381
-15
@@ -116,6 +116,39 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "bind_syn"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d6608ba072b4bc847774fac76963956592b5cdfa3751afcefa252fb61cb85b9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.64.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"lazy_static 1.5.0",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash 1.1.0",
|
||||
"shlex",
|
||||
"syn 1.0.109",
|
||||
"which 4.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -232,6 +265,37 @@ dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cex"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b0114a3f232423fadbbdbb692688e3e68c3b58b4b063ac3a7d0190d561080da"
|
||||
dependencies = [
|
||||
"cex_derive",
|
||||
"enumx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cex_derive"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b5048cd656d7d2e739960fa33d9f95693005792dc8aad0af8b8f0b7d76c938d"
|
||||
dependencies = [
|
||||
"indexmap 1.9.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -258,6 +322,17 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
@@ -269,6 +344,19 @@ dependencies = [
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clib"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fda1a698cd341f055d3ae1fdbfb1cc441e7f9bacce795356ecc685e69134e957"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bindgen",
|
||||
"inwelling",
|
||||
"pkg-config",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.0"
|
||||
@@ -383,7 +471,7 @@ dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
"log",
|
||||
"regalloc2",
|
||||
"rustc-hash",
|
||||
"rustc-hash 2.1.1",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"target-lexicon 0.13.2",
|
||||
@@ -678,6 +766,27 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||
|
||||
[[package]]
|
||||
name = "enumx"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32875abeb14f7fe2c2b8ad15e58f41701f455d124d0a03bc88132d5face2663f"
|
||||
dependencies = [
|
||||
"enumx_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumx_derive"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa5d66efdd1eab6ea85ba31bdb58bed1e4ce218c1361061384ece88f40ebeb49"
|
||||
dependencies = [
|
||||
"indexmap 1.9.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.9.3"
|
||||
@@ -765,7 +874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168cbad48fdda10be94de9c6319f9e8ac5d3cf0a1abda1864269dfcca3d302a"
|
||||
dependencies = [
|
||||
"flame",
|
||||
"indexmap",
|
||||
"indexmap 2.7.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
@@ -871,7 +980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
dependencies = [
|
||||
"fallible-iterator",
|
||||
"indexmap",
|
||||
"indexmap 2.7.1",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
@@ -897,6 +1006,12 @@ dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
@@ -912,12 +1027,30 @@ dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "heredom"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7a4d76fa670b51cfb56e908ad8bfd44a14fee853ea764790e46634d3fcdf4d"
|
||||
dependencies = [
|
||||
"tuplex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@@ -977,6 +1110,16 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
@@ -1006,13 +1149,23 @@ dependencies = [
|
||||
"similar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inwelling"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f6292c68ffca1fa94ca8f95ca5ad2885d79d96377f1d37ced6a47cd26cfaf8c"
|
||||
dependencies = [
|
||||
"toml",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-macro"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
@@ -1098,6 +1251,12 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-parse-float"
|
||||
version = "0.8.5"
|
||||
@@ -1355,6 +1514,12 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.4"
|
||||
@@ -1373,6 +1538,12 @@ dependencies = [
|
||||
"rand_core 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mutf8"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b444426a4c188e9ad33560853ebd52309ab72811f536a9e6f37907fd12cf45"
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
@@ -1395,6 +1566,16 @@ dependencies = [
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
@@ -1447,6 +1628,7 @@ version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
@@ -1563,6 +1745,12 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
@@ -1681,6 +1869,15 @@ dependencies = [
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
|
||||
dependencies = [
|
||||
"toml_edit 0.22.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
@@ -1756,7 +1953,7 @@ version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
@@ -1905,7 +2102,7 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"hashbrown 0.15.2",
|
||||
"log",
|
||||
"rustc-hash",
|
||||
"rustc-hash 2.1.1",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@@ -1985,7 +2182,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
"rustc-hash 2.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2023,7 +2220,7 @@ dependencies = [
|
||||
"ruff_python_ast",
|
||||
"ruff_python_trivia",
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
"rustc-hash 2.1.1",
|
||||
"static_assertions",
|
||||
"unicode-ident",
|
||||
"unicode-normalization",
|
||||
@@ -2055,6 +2252,12 @@ name = "ruff_text_size"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.1"
|
||||
@@ -2111,7 +2314,7 @@ version = "0.4.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bitflags 2.8.0",
|
||||
"indexmap",
|
||||
"indexmap 2.7.1",
|
||||
"insta",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
@@ -2292,7 +2495,7 @@ dependencies = [
|
||||
"foreign-types-shared",
|
||||
"gethostname",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"indexmap 2.7.1",
|
||||
"itertools 0.14.0",
|
||||
"junction",
|
||||
"libc",
|
||||
@@ -2328,8 +2531,10 @@ dependencies = [
|
||||
"sha3",
|
||||
"socket2",
|
||||
"system-configuration",
|
||||
"tcl",
|
||||
"termios",
|
||||
"thread_local",
|
||||
"tk",
|
||||
"ucd",
|
||||
"unic-char-property",
|
||||
"unic-normal",
|
||||
@@ -2365,7 +2570,7 @@ dependencies = [
|
||||
"glob",
|
||||
"half 2.4.1",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"indexmap 2.7.1",
|
||||
"is-macro",
|
||||
"itertools 0.14.0",
|
||||
"junction",
|
||||
@@ -2407,8 +2612,8 @@ dependencies = [
|
||||
"schannel",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"strum 0.27.1",
|
||||
"strum_macros 0.27.1",
|
||||
"thiserror 2.0.11",
|
||||
"thread_local",
|
||||
"timsort",
|
||||
@@ -2419,7 +2624,7 @@ dependencies = [
|
||||
"unicode-casing",
|
||||
"unicode_names2",
|
||||
"wasm-bindgen",
|
||||
"which",
|
||||
"which 6.0.3",
|
||||
"widestring",
|
||||
"windows",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -2563,6 +2768,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.10.1"
|
||||
@@ -2647,19 +2861,37 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.19.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b89a286a7e3b5720b9a477b23253bc50debac207c8d21505f8e70b36792f11b5"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5"
|
||||
dependencies = [
|
||||
"heck 0.3.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
@@ -2738,6 +2970,36 @@ version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a"
|
||||
|
||||
[[package]]
|
||||
name = "tcl"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e0d0928e8b4dca8ebd485f687f725bb34e454c7a28c1d353bf7d1b8060581bf"
|
||||
dependencies = [
|
||||
"cex",
|
||||
"clib",
|
||||
"enumx",
|
||||
"inwelling",
|
||||
"mutf8",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"tcl_derive",
|
||||
"tuplex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tcl_derive"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "625d95e672231bbf31dead6861b0ad72bcb71a2891b26b0c4924cd1cc9687b93"
|
||||
dependencies = [
|
||||
"bind_syn",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
@@ -2863,6 +3125,79 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tk"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6fbe29c813c9eee5e0d4d996a4a615f6538220f0ad181269a413f21c13eb077"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cex",
|
||||
"clib",
|
||||
"enumx",
|
||||
"heredom",
|
||||
"inwelling",
|
||||
"num_enum",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"strum 0.19.5",
|
||||
"strum_macros 0.19.4",
|
||||
"tcl",
|
||||
"tcl_derive",
|
||||
"tuplex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.19.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||
dependencies = [
|
||||
"indexmap 2.7.1",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.5.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||
dependencies = [
|
||||
"indexmap 2.7.1",
|
||||
"toml_datetime",
|
||||
"winnow 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tuplex"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "676ac81d5454c4dcf37955d34fa8626ede3490f744b86ca14a7b90168d2a08aa"
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "1.6.3"
|
||||
@@ -3085,6 +3420,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"getrandom 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3223,6 +3559,18 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "6.0.3"
|
||||
@@ -3439,6 +3787,24 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.55.0"
|
||||
|
||||
@@ -22,6 +22,7 @@ bz2 = ["stdlib", "rustpython-stdlib/bz2"]
|
||||
sqlite = ["rustpython-stdlib/sqlite"]
|
||||
ssl = ["rustpython-stdlib/ssl"]
|
||||
ssl-vendor = ["ssl", "rustpython-stdlib/ssl-vendor"]
|
||||
tkinter = ["rustpython-stdlib/tkinter"]
|
||||
|
||||
[dependencies]
|
||||
rustpython-compiler = { workspace = true }
|
||||
|
||||
Vendored
+4984
File diff suppressed because it is too large.
Load diff
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
"""Main entry point"""
|
||||
|
||||
import sys
|
||||
if sys.argv[0].endswith("__main__.py"):
|
||||
sys.argv[0] = "python -m tkinter"
|
||||
from . import _test as main
|
||||
main()
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
# tk common color chooser dialogue
|
||||
#
|
||||
# this module provides an interface to the native color dialogue
|
||||
# available in Tk 4.2 and newer.
|
||||
#
|
||||
# written by Fredrik Lundh, May 1997
|
||||
#
|
||||
# fixed initialcolor handling in August 1998
|
||||
#
|
||||
|
||||
|
||||
from tkinter.commondialog import Dialog
|
||||
|
||||
__all__ = ["Chooser", "askcolor"]
|
||||
|
||||
|
||||
class Chooser(Dialog):
|
||||
"""Create a dialog for the tk_chooseColor command.
|
||||
|
||||
Args:
|
||||
master: The master widget for this dialog. If not provided,
|
||||
defaults to options['parent'] (if defined).
|
||||
options: Dictionary of options for the tk_chooseColor call.
|
||||
initialcolor: Specifies the selected color when the
|
||||
dialog is first displayed. This can be a tk color
|
||||
string or a 3-tuple of ints in the range (0, 255)
|
||||
for an RGB triplet.
|
||||
parent: The parent window of the color dialog. The
|
||||
color dialog is displayed on top of this.
|
||||
title: A string for the title of the dialog box.
|
||||
"""
|
||||
|
||||
command = "tk_chooseColor"
|
||||
|
||||
def _fixoptions(self):
|
||||
"""Ensure initialcolor is a tk color string.
|
||||
|
||||
Convert initialcolor from a RGB triplet to a color string.
|
||||
"""
|
||||
try:
|
||||
color = self.options["initialcolor"]
|
||||
if isinstance(color, tuple):
|
||||
# Assume an RGB triplet.
|
||||
self.options["initialcolor"] = "#%02x%02x%02x" % color
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
"""Adjust result returned from call to tk_chooseColor.
|
||||
|
||||
Return both an RGB tuple of ints in the range (0, 255) and the
|
||||
tk color string in the form #rrggbb.
|
||||
"""
|
||||
# Result can be many things: an empty tuple, an empty string, or
|
||||
# a _tkinter.Tcl_Obj, so this somewhat weird check handles that.
|
||||
if not result or not str(result):
|
||||
return None, None # canceled
|
||||
|
||||
# To simplify application code, the color chooser returns
|
||||
# an RGB tuple together with the Tk color string.
|
||||
r, g, b = widget.winfo_rgb(result)
|
||||
return (r//256, g//256, b//256), str(result)
|
||||
|
||||
|
||||
#
|
||||
# convenience stuff
|
||||
|
||||
def askcolor(color=None, **options):
|
||||
"""Display dialog window for selection of a color.
|
||||
|
||||
Convenience wrapper for the Chooser class. Displays the color
|
||||
chooser dialog with color as the initial value.
|
||||
"""
|
||||
|
||||
if color:
|
||||
options = options.copy()
|
||||
options["initialcolor"] = color
|
||||
|
||||
return Chooser(**options).show()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# test stuff
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("color", askcolor())
|
||||
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
# base class for tk common dialogues
|
||||
#
|
||||
# this module provides a base class for accessing the common
|
||||
# dialogues available in Tk 4.2 and newer. use filedialog,
|
||||
# colorchooser, and messagebox to access the individual
|
||||
# dialogs.
|
||||
#
|
||||
# written by Fredrik Lundh, May 1997
|
||||
#
|
||||
|
||||
__all__ = ["Dialog"]
|
||||
|
||||
from tkinter import _get_temp_root, _destroy_temp_root
|
||||
|
||||
|
||||
class Dialog:
|
||||
|
||||
command = None
|
||||
|
||||
def __init__(self, master=None, **options):
|
||||
if master is None:
|
||||
master = options.get('parent')
|
||||
self.master = master
|
||||
self.options = options
|
||||
|
||||
def _fixoptions(self):
|
||||
pass # hook
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
return result # hook
|
||||
|
||||
def show(self, **options):
|
||||
|
||||
# update instance options
|
||||
for k, v in options.items():
|
||||
self.options[k] = v
|
||||
|
||||
self._fixoptions()
|
||||
|
||||
master = self.master
|
||||
if master is None:
|
||||
master = _get_temp_root()
|
||||
try:
|
||||
self._test_callback(master) # The function below is replaced for some tests.
|
||||
s = master.tk.call(self.command, *master._options(self.options))
|
||||
s = self._fixresult(master, s)
|
||||
finally:
|
||||
_destroy_temp_root(master)
|
||||
|
||||
return s
|
||||
|
||||
def _test_callback(self, master):
|
||||
pass
|
||||
Vendored
+110
@@ -0,0 +1,110 @@
|
||||
# Symbolic constants for Tk
|
||||
|
||||
# Booleans
|
||||
NO=FALSE=OFF=0
|
||||
YES=TRUE=ON=1
|
||||
|
||||
# -anchor and -sticky
|
||||
N='n'
|
||||
S='s'
|
||||
W='w'
|
||||
E='e'
|
||||
NW='nw'
|
||||
SW='sw'
|
||||
NE='ne'
|
||||
SE='se'
|
||||
NS='ns'
|
||||
EW='ew'
|
||||
NSEW='nsew'
|
||||
CENTER='center'
|
||||
|
||||
# -fill
|
||||
NONE='none'
|
||||
X='x'
|
||||
Y='y'
|
||||
BOTH='both'
|
||||
|
||||
# -side
|
||||
LEFT='left'
|
||||
TOP='top'
|
||||
RIGHT='right'
|
||||
BOTTOM='bottom'
|
||||
|
||||
# -relief
|
||||
RAISED='raised'
|
||||
SUNKEN='sunken'
|
||||
FLAT='flat'
|
||||
RIDGE='ridge'
|
||||
GROOVE='groove'
|
||||
SOLID = 'solid'
|
||||
|
||||
# -orient
|
||||
HORIZONTAL='horizontal'
|
||||
VERTICAL='vertical'
|
||||
|
||||
# -tabs
|
||||
NUMERIC='numeric'
|
||||
|
||||
# -wrap
|
||||
CHAR='char'
|
||||
WORD='word'
|
||||
|
||||
# -align
|
||||
BASELINE='baseline'
|
||||
|
||||
# -bordermode
|
||||
INSIDE='inside'
|
||||
OUTSIDE='outside'
|
||||
|
||||
# Special tags, marks and insert positions
|
||||
SEL='sel'
|
||||
SEL_FIRST='sel.first'
|
||||
SEL_LAST='sel.last'
|
||||
END='end'
|
||||
INSERT='insert'
|
||||
CURRENT='current'
|
||||
ANCHOR='anchor'
|
||||
ALL='all' # e.g. Canvas.delete(ALL)
|
||||
|
||||
# Text widget and button states
|
||||
NORMAL='normal'
|
||||
DISABLED='disabled'
|
||||
ACTIVE='active'
|
||||
# Canvas state
|
||||
HIDDEN='hidden'
|
||||
|
||||
# Menu item types
|
||||
CASCADE='cascade'
|
||||
CHECKBUTTON='checkbutton'
|
||||
COMMAND='command'
|
||||
RADIOBUTTON='radiobutton'
|
||||
SEPARATOR='separator'
|
||||
|
||||
# Selection modes for list boxes
|
||||
SINGLE='single'
|
||||
BROWSE='browse'
|
||||
MULTIPLE='multiple'
|
||||
EXTENDED='extended'
|
||||
|
||||
# Activestyle for list boxes
|
||||
# NONE='none' is also valid
|
||||
DOTBOX='dotbox'
|
||||
UNDERLINE='underline'
|
||||
|
||||
# Various canvas styles
|
||||
PIESLICE='pieslice'
|
||||
CHORD='chord'
|
||||
ARC='arc'
|
||||
FIRST='first'
|
||||
LAST='last'
|
||||
BUTT='butt'
|
||||
PROJECTING='projecting'
|
||||
ROUND='round'
|
||||
BEVEL='bevel'
|
||||
MITER='miter'
|
||||
|
||||
# Arguments to xview/yview
|
||||
MOVETO='moveto'
|
||||
SCROLL='scroll'
|
||||
UNITS='units'
|
||||
PAGES='pages'
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
# dialog.py -- Tkinter interface to the tk_dialog script.
|
||||
|
||||
from tkinter import _cnfmerge, Widget, TclError, Button, Pack
|
||||
|
||||
__all__ = ["Dialog"]
|
||||
|
||||
DIALOG_ICON = 'questhead'
|
||||
|
||||
|
||||
class Dialog(Widget):
|
||||
def __init__(self, master=None, cnf={}, **kw):
|
||||
cnf = _cnfmerge((cnf, kw))
|
||||
self.widgetName = '__dialog__'
|
||||
self._setup(master, cnf)
|
||||
self.num = self.tk.getint(
|
||||
self.tk.call(
|
||||
'tk_dialog', self._w,
|
||||
cnf['title'], cnf['text'],
|
||||
cnf['bitmap'], cnf['default'],
|
||||
*cnf['strings']))
|
||||
try: Widget.destroy(self)
|
||||
except TclError: pass
|
||||
|
||||
def destroy(self): pass
|
||||
|
||||
|
||||
def _test():
|
||||
d = Dialog(None, {'title': 'File Modified',
|
||||
'text':
|
||||
'File "Python.h" has been modified'
|
||||
' since the last time it was saved.'
|
||||
' Do you want to save it before'
|
||||
' exiting the application.',
|
||||
'bitmap': DIALOG_ICON,
|
||||
'default': 0,
|
||||
'strings': ('Save File',
|
||||
'Discard Changes',
|
||||
'Return to Editor')})
|
||||
print(d.num)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
t = Button(None, {'text': 'Test',
|
||||
'command': _test,
|
||||
Pack: {}})
|
||||
q = Button(None, {'text': 'Quit',
|
||||
'command': t.quit,
|
||||
Pack: {}})
|
||||
t.mainloop()
|
||||
Vendored
+324
@@ -0,0 +1,324 @@
|
||||
"""Drag-and-drop support for Tkinter.
|
||||
|
||||
This is very preliminary. I currently only support dnd *within* one
|
||||
application, between different windows (or within the same window).
|
||||
|
||||
I am trying to make this as generic as possible -- not dependent on
|
||||
the use of a particular widget or icon type, etc. I also hope that
|
||||
this will work with Pmw.
|
||||
|
||||
To enable an object to be dragged, you must create an event binding
|
||||
for it that starts the drag-and-drop process. Typically, you should
|
||||
bind <ButtonPress> to a callback function that you write. The function
|
||||
should call Tkdnd.dnd_start(source, event), where 'source' is the
|
||||
object to be dragged, and 'event' is the event that invoked the call
|
||||
(the argument to your callback function). Even though this is a class
|
||||
instantiation, the returned instance should not be stored -- it will
|
||||
be kept alive automatically for the duration of the drag-and-drop.
|
||||
|
||||
When a drag-and-drop is already in process for the Tk interpreter, the
|
||||
call is *ignored*; this normally averts starting multiple simultaneous
|
||||
dnd processes, e.g. because different button callbacks all
|
||||
dnd_start().
|
||||
|
||||
The object is *not* necessarily a widget -- it can be any
|
||||
application-specific object that is meaningful to potential
|
||||
drag-and-drop targets.
|
||||
|
||||
Potential drag-and-drop targets are discovered as follows. Whenever
|
||||
the mouse moves, and at the start and end of a drag-and-drop move, the
|
||||
Tk widget directly under the mouse is inspected. This is the target
|
||||
widget (not to be confused with the target object, yet to be
|
||||
determined). If there is no target widget, there is no dnd target
|
||||
object. If there is a target widget, and it has an attribute
|
||||
dnd_accept, this should be a function (or any callable object). The
|
||||
function is called as dnd_accept(source, event), where 'source' is the
|
||||
object being dragged (the object passed to dnd_start() above), and
|
||||
'event' is the most recent event object (generally a <Motion> event;
|
||||
it can also be <ButtonPress> or <ButtonRelease>). If the dnd_accept()
|
||||
function returns something other than None, this is the new dnd target
|
||||
object. If dnd_accept() returns None, or if the target widget has no
|
||||
dnd_accept attribute, the target widget's parent is considered as the
|
||||
target widget, and the search for a target object is repeated from
|
||||
there. If necessary, the search is repeated all the way up to the
|
||||
root widget. If none of the target widgets can produce a target
|
||||
object, there is no target object (the target object is None).
|
||||
|
||||
The target object thus produced, if any, is called the new target
|
||||
object. It is compared with the old target object (or None, if there
|
||||
was no old target widget). There are several cases ('source' is the
|
||||
source object, and 'event' is the most recent event object):
|
||||
|
||||
- Both the old and new target objects are None. Nothing happens.
|
||||
|
||||
- The old and new target objects are the same object. Its method
|
||||
dnd_motion(source, event) is called.
|
||||
|
||||
- The old target object was None, and the new target object is not
|
||||
None. The new target object's method dnd_enter(source, event) is
|
||||
called.
|
||||
|
||||
- The new target object is None, and the old target object is not
|
||||
None. The old target object's method dnd_leave(source, event) is
|
||||
called.
|
||||
|
||||
- The old and new target objects differ and neither is None. The old
|
||||
target object's method dnd_leave(source, event), and then the new
|
||||
target object's method dnd_enter(source, event) is called.
|
||||
|
||||
Once this is done, the new target object replaces the old one, and the
|
||||
Tk mainloop proceeds. The return value of the methods mentioned above
|
||||
is ignored; if they raise an exception, the normal exception handling
|
||||
mechanisms take over.
|
||||
|
||||
The drag-and-drop processes can end in two ways: a final target object
|
||||
is selected, or no final target object is selected. When a final
|
||||
target object is selected, it will always have been notified of the
|
||||
potential drop by a call to its dnd_enter() method, as described
|
||||
above, and possibly one or more calls to its dnd_motion() method; its
|
||||
dnd_leave() method has not been called since the last call to
|
||||
dnd_enter(). The target is notified of the drop by a call to its
|
||||
method dnd_commit(source, event).
|
||||
|
||||
If no final target object is selected, and there was an old target
|
||||
object, its dnd_leave(source, event) method is called to complete the
|
||||
dnd sequence.
|
||||
|
||||
Finally, the source object is notified that the drag-and-drop process
|
||||
is over, by a call to source.dnd_end(target, event), specifying either
|
||||
the selected target object, or None if no target object was selected.
|
||||
The source object can use this to implement the commit action; this is
|
||||
sometimes simpler than to do it in the target's dnd_commit(). The
|
||||
target's dnd_commit() method could then simply be aliased to
|
||||
dnd_leave().
|
||||
|
||||
At any time during a dnd sequence, the application can cancel the
|
||||
sequence by calling the cancel() method on the object returned by
|
||||
dnd_start(). This will call dnd_leave() if a target is currently
|
||||
active; it will never call dnd_commit().
|
||||
|
||||
"""
|
||||
|
||||
import tkinter
|
||||
|
||||
__all__ = ["dnd_start", "DndHandler"]
|
||||
|
||||
|
||||
# The factory function
|
||||
|
||||
def dnd_start(source, event):
|
||||
h = DndHandler(source, event)
|
||||
if h.root is not None:
|
||||
return h
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
# The class that does the work
|
||||
|
||||
class DndHandler:
|
||||
|
||||
root = None
|
||||
|
||||
def __init__(self, source, event):
|
||||
if event.num > 5:
|
||||
return
|
||||
root = event.widget._root()
|
||||
try:
|
||||
root.__dnd
|
||||
return # Don't start recursive dnd
|
||||
except AttributeError:
|
||||
root.__dnd = self
|
||||
self.root = root
|
||||
self.source = source
|
||||
self.target = None
|
||||
self.initial_button = button = event.num
|
||||
self.initial_widget = widget = event.widget
|
||||
self.release_pattern = "<B%d-ButtonRelease-%d>" % (button, button)
|
||||
self.save_cursor = widget['cursor'] or ""
|
||||
widget.bind(self.release_pattern, self.on_release)
|
||||
widget.bind("<Motion>", self.on_motion)
|
||||
widget['cursor'] = "hand2"
|
||||
|
||||
def __del__(self):
|
||||
root = self.root
|
||||
self.root = None
|
||||
if root is not None:
|
||||
try:
|
||||
del root.__dnd
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def on_motion(self, event):
|
||||
x, y = event.x_root, event.y_root
|
||||
target_widget = self.initial_widget.winfo_containing(x, y)
|
||||
source = self.source
|
||||
new_target = None
|
||||
while target_widget is not None:
|
||||
try:
|
||||
attr = target_widget.dnd_accept
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
new_target = attr(source, event)
|
||||
if new_target is not None:
|
||||
break
|
||||
target_widget = target_widget.master
|
||||
old_target = self.target
|
||||
if old_target is new_target:
|
||||
if old_target is not None:
|
||||
old_target.dnd_motion(source, event)
|
||||
else:
|
||||
if old_target is not None:
|
||||
self.target = None
|
||||
old_target.dnd_leave(source, event)
|
||||
if new_target is not None:
|
||||
new_target.dnd_enter(source, event)
|
||||
self.target = new_target
|
||||
|
||||
def on_release(self, event):
|
||||
self.finish(event, 1)
|
||||
|
||||
def cancel(self, event=None):
|
||||
self.finish(event, 0)
|
||||
|
||||
def finish(self, event, commit=0):
|
||||
target = self.target
|
||||
source = self.source
|
||||
widget = self.initial_widget
|
||||
root = self.root
|
||||
try:
|
||||
del root.__dnd
|
||||
self.initial_widget.unbind(self.release_pattern)
|
||||
self.initial_widget.unbind("<Motion>")
|
||||
widget['cursor'] = self.save_cursor
|
||||
self.target = self.source = self.initial_widget = self.root = None
|
||||
if target is not None:
|
||||
if commit:
|
||||
target.dnd_commit(source, event)
|
||||
else:
|
||||
target.dnd_leave(source, event)
|
||||
finally:
|
||||
source.dnd_end(target, event)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# The rest is here for testing and demonstration purposes only!
|
||||
|
||||
class Icon:
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.canvas = self.label = self.id = None
|
||||
|
||||
def attach(self, canvas, x=10, y=10):
|
||||
if canvas is self.canvas:
|
||||
self.canvas.coords(self.id, x, y)
|
||||
return
|
||||
if self.canvas is not None:
|
||||
self.detach()
|
||||
if canvas is None:
|
||||
return
|
||||
label = tkinter.Label(canvas, text=self.name,
|
||||
borderwidth=2, relief="raised")
|
||||
id = canvas.create_window(x, y, window=label, anchor="nw")
|
||||
self.canvas = canvas
|
||||
self.label = label
|
||||
self.id = id
|
||||
label.bind("<ButtonPress>", self.press)
|
||||
|
||||
def detach(self):
|
||||
canvas = self.canvas
|
||||
if canvas is None:
|
||||
return
|
||||
id = self.id
|
||||
label = self.label
|
||||
self.canvas = self.label = self.id = None
|
||||
canvas.delete(id)
|
||||
label.destroy()
|
||||
|
||||
def press(self, event):
|
||||
if dnd_start(self, event):
|
||||
# where the pointer is relative to the label widget:
|
||||
self.x_off = event.x
|
||||
self.y_off = event.y
|
||||
# where the widget is relative to the canvas:
|
||||
self.x_orig, self.y_orig = self.canvas.coords(self.id)
|
||||
|
||||
def move(self, event):
|
||||
x, y = self.where(self.canvas, event)
|
||||
self.canvas.coords(self.id, x, y)
|
||||
|
||||
def putback(self):
|
||||
self.canvas.coords(self.id, self.x_orig, self.y_orig)
|
||||
|
||||
def where(self, canvas, event):
|
||||
# where the corner of the canvas is relative to the screen:
|
||||
x_org = canvas.winfo_rootx()
|
||||
y_org = canvas.winfo_rooty()
|
||||
# where the pointer is relative to the canvas widget:
|
||||
x = event.x_root - x_org
|
||||
y = event.y_root - y_org
|
||||
# compensate for initial pointer offset
|
||||
return x - self.x_off, y - self.y_off
|
||||
|
||||
def dnd_end(self, target, event):
|
||||
pass
|
||||
|
||||
|
||||
class Tester:
|
||||
|
||||
def __init__(self, root):
|
||||
self.top = tkinter.Toplevel(root)
|
||||
self.canvas = tkinter.Canvas(self.top, width=100, height=100)
|
||||
self.canvas.pack(fill="both", expand=1)
|
||||
self.canvas.dnd_accept = self.dnd_accept
|
||||
|
||||
def dnd_accept(self, source, event):
|
||||
return self
|
||||
|
||||
def dnd_enter(self, source, event):
|
||||
self.canvas.focus_set() # Show highlight border
|
||||
x, y = source.where(self.canvas, event)
|
||||
x1, y1, x2, y2 = source.canvas.bbox(source.id)
|
||||
dx, dy = x2-x1, y2-y1
|
||||
self.dndid = self.canvas.create_rectangle(x, y, x+dx, y+dy)
|
||||
self.dnd_motion(source, event)
|
||||
|
||||
def dnd_motion(self, source, event):
|
||||
x, y = source.where(self.canvas, event)
|
||||
x1, y1, x2, y2 = self.canvas.bbox(self.dndid)
|
||||
self.canvas.move(self.dndid, x-x1, y-y1)
|
||||
|
||||
def dnd_leave(self, source, event):
|
||||
self.top.focus_set() # Hide highlight border
|
||||
self.canvas.delete(self.dndid)
|
||||
self.dndid = None
|
||||
|
||||
def dnd_commit(self, source, event):
|
||||
self.dnd_leave(source, event)
|
||||
x, y = source.where(self.canvas, event)
|
||||
source.attach(self.canvas, x, y)
|
||||
|
||||
|
||||
def test():
|
||||
root = tkinter.Tk()
|
||||
root.geometry("+1+1")
|
||||
tkinter.Button(command=root.quit, text="Quit").pack()
|
||||
t1 = Tester(root)
|
||||
t1.top.geometry("+1+60")
|
||||
t2 = Tester(root)
|
||||
t2.top.geometry("+120+60")
|
||||
t3 = Tester(root)
|
||||
t3.top.geometry("+240+60")
|
||||
i1 = Icon("ICON1")
|
||||
i2 = Icon("ICON2")
|
||||
i3 = Icon("ICON3")
|
||||
i1.attach(t1.canvas)
|
||||
i2.attach(t2.canvas)
|
||||
i3.attach(t3.canvas)
|
||||
root.mainloop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
Vendored
+492
@@ -0,0 +1,492 @@
|
||||
"""File selection dialog classes.
|
||||
|
||||
Classes:
|
||||
|
||||
- FileDialog
|
||||
- LoadFileDialog
|
||||
- SaveFileDialog
|
||||
|
||||
This module also presents tk common file dialogues, it provides interfaces
|
||||
to the native file dialogues available in Tk 4.2 and newer, and the
|
||||
directory dialogue available in Tk 8.3 and newer.
|
||||
These interfaces were written by Fredrik Lundh, May 1997.
|
||||
"""
|
||||
__all__ = ["FileDialog", "LoadFileDialog", "SaveFileDialog",
|
||||
"Open", "SaveAs", "Directory",
|
||||
"askopenfilename", "asksaveasfilename", "askopenfilenames",
|
||||
"askopenfile", "askopenfiles", "asksaveasfile", "askdirectory"]
|
||||
|
||||
import fnmatch
|
||||
import os
|
||||
from tkinter import (
|
||||
Frame, LEFT, YES, BOTTOM, Entry, TOP, Button, Tk, X,
|
||||
Toplevel, RIGHT, Y, END, Listbox, BOTH, Scrollbar,
|
||||
)
|
||||
from tkinter.dialog import Dialog
|
||||
from tkinter import commondialog
|
||||
from tkinter.simpledialog import _setup_dialog
|
||||
|
||||
|
||||
dialogstates = {}
|
||||
|
||||
|
||||
class FileDialog:
|
||||
|
||||
"""Standard file selection dialog -- no checks on selected file.
|
||||
|
||||
Usage:
|
||||
|
||||
d = FileDialog(master)
|
||||
fname = d.go(dir_or_file, pattern, default, key)
|
||||
if fname is None: ...canceled...
|
||||
else: ...open file...
|
||||
|
||||
All arguments to go() are optional.
|
||||
|
||||
The 'key' argument specifies a key in the global dictionary
|
||||
'dialogstates', which keeps track of the values for the directory
|
||||
and pattern arguments, overriding the values passed in (it does
|
||||
not keep track of the default argument!). If no key is specified,
|
||||
the dialog keeps no memory of previous state. Note that memory is
|
||||
kept even when the dialog is canceled. (All this emulates the
|
||||
behavior of the Macintosh file selection dialogs.)
|
||||
|
||||
"""
|
||||
|
||||
title = "File Selection Dialog"
|
||||
|
||||
def __init__(self, master, title=None):
|
||||
if title is None: title = self.title
|
||||
self.master = master
|
||||
self.directory = None
|
||||
|
||||
self.top = Toplevel(master)
|
||||
self.top.title(title)
|
||||
self.top.iconname(title)
|
||||
_setup_dialog(self.top)
|
||||
|
||||
self.botframe = Frame(self.top)
|
||||
self.botframe.pack(side=BOTTOM, fill=X)
|
||||
|
||||
self.selection = Entry(self.top)
|
||||
self.selection.pack(side=BOTTOM, fill=X)
|
||||
self.selection.bind('<Return>', self.ok_event)
|
||||
|
||||
self.filter = Entry(self.top)
|
||||
self.filter.pack(side=TOP, fill=X)
|
||||
self.filter.bind('<Return>', self.filter_command)
|
||||
|
||||
self.midframe = Frame(self.top)
|
||||
self.midframe.pack(expand=YES, fill=BOTH)
|
||||
|
||||
self.filesbar = Scrollbar(self.midframe)
|
||||
self.filesbar.pack(side=RIGHT, fill=Y)
|
||||
self.files = Listbox(self.midframe, exportselection=0,
|
||||
yscrollcommand=(self.filesbar, 'set'))
|
||||
self.files.pack(side=RIGHT, expand=YES, fill=BOTH)
|
||||
btags = self.files.bindtags()
|
||||
self.files.bindtags(btags[1:] + btags[:1])
|
||||
self.files.bind('<ButtonRelease-1>', self.files_select_event)
|
||||
self.files.bind('<Double-ButtonRelease-1>', self.files_double_event)
|
||||
self.filesbar.config(command=(self.files, 'yview'))
|
||||
|
||||
self.dirsbar = Scrollbar(self.midframe)
|
||||
self.dirsbar.pack(side=LEFT, fill=Y)
|
||||
self.dirs = Listbox(self.midframe, exportselection=0,
|
||||
yscrollcommand=(self.dirsbar, 'set'))
|
||||
self.dirs.pack(side=LEFT, expand=YES, fill=BOTH)
|
||||
self.dirsbar.config(command=(self.dirs, 'yview'))
|
||||
btags = self.dirs.bindtags()
|
||||
self.dirs.bindtags(btags[1:] + btags[:1])
|
||||
self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event)
|
||||
self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event)
|
||||
|
||||
self.ok_button = Button(self.botframe,
|
||||
text="OK",
|
||||
command=self.ok_command)
|
||||
self.ok_button.pack(side=LEFT)
|
||||
self.filter_button = Button(self.botframe,
|
||||
text="Filter",
|
||||
command=self.filter_command)
|
||||
self.filter_button.pack(side=LEFT, expand=YES)
|
||||
self.cancel_button = Button(self.botframe,
|
||||
text="Cancel",
|
||||
command=self.cancel_command)
|
||||
self.cancel_button.pack(side=RIGHT)
|
||||
|
||||
self.top.protocol('WM_DELETE_WINDOW', self.cancel_command)
|
||||
# XXX Are the following okay for a general audience?
|
||||
self.top.bind('<Alt-w>', self.cancel_command)
|
||||
self.top.bind('<Alt-W>', self.cancel_command)
|
||||
|
||||
def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None):
|
||||
if key and key in dialogstates:
|
||||
self.directory, pattern = dialogstates[key]
|
||||
else:
|
||||
dir_or_file = os.path.expanduser(dir_or_file)
|
||||
if os.path.isdir(dir_or_file):
|
||||
self.directory = dir_or_file
|
||||
else:
|
||||
self.directory, default = os.path.split(dir_or_file)
|
||||
self.set_filter(self.directory, pattern)
|
||||
self.set_selection(default)
|
||||
self.filter_command()
|
||||
self.selection.focus_set()
|
||||
self.top.wait_visibility() # window needs to be visible for the grab
|
||||
self.top.grab_set()
|
||||
self.how = None
|
||||
self.master.mainloop() # Exited by self.quit(how)
|
||||
if key:
|
||||
directory, pattern = self.get_filter()
|
||||
if self.how:
|
||||
directory = os.path.dirname(self.how)
|
||||
dialogstates[key] = directory, pattern
|
||||
self.top.destroy()
|
||||
return self.how
|
||||
|
||||
def quit(self, how=None):
|
||||
self.how = how
|
||||
self.master.quit() # Exit mainloop()
|
||||
|
||||
def dirs_double_event(self, event):
|
||||
self.filter_command()
|
||||
|
||||
def dirs_select_event(self, event):
|
||||
dir, pat = self.get_filter()
|
||||
subdir = self.dirs.get('active')
|
||||
dir = os.path.normpath(os.path.join(self.directory, subdir))
|
||||
self.set_filter(dir, pat)
|
||||
|
||||
def files_double_event(self, event):
|
||||
self.ok_command()
|
||||
|
||||
def files_select_event(self, event):
|
||||
file = self.files.get('active')
|
||||
self.set_selection(file)
|
||||
|
||||
def ok_event(self, event):
|
||||
self.ok_command()
|
||||
|
||||
def ok_command(self):
|
||||
self.quit(self.get_selection())
|
||||
|
||||
def filter_command(self, event=None):
|
||||
dir, pat = self.get_filter()
|
||||
try:
|
||||
names = os.listdir(dir)
|
||||
except OSError:
|
||||
self.master.bell()
|
||||
return
|
||||
self.directory = dir
|
||||
self.set_filter(dir, pat)
|
||||
names.sort()
|
||||
subdirs = [os.pardir]
|
||||
matchingfiles = []
|
||||
for name in names:
|
||||
fullname = os.path.join(dir, name)
|
||||
if os.path.isdir(fullname):
|
||||
subdirs.append(name)
|
||||
elif fnmatch.fnmatch(name, pat):
|
||||
matchingfiles.append(name)
|
||||
self.dirs.delete(0, END)
|
||||
for name in subdirs:
|
||||
self.dirs.insert(END, name)
|
||||
self.files.delete(0, END)
|
||||
for name in matchingfiles:
|
||||
self.files.insert(END, name)
|
||||
head, tail = os.path.split(self.get_selection())
|
||||
if tail == os.curdir: tail = ''
|
||||
self.set_selection(tail)
|
||||
|
||||
def get_filter(self):
|
||||
filter = self.filter.get()
|
||||
filter = os.path.expanduser(filter)
|
||||
if filter[-1:] == os.sep or os.path.isdir(filter):
|
||||
filter = os.path.join(filter, "*")
|
||||
return os.path.split(filter)
|
||||
|
||||
def get_selection(self):
|
||||
file = self.selection.get()
|
||||
file = os.path.expanduser(file)
|
||||
return file
|
||||
|
||||
def cancel_command(self, event=None):
|
||||
self.quit()
|
||||
|
||||
def set_filter(self, dir, pat):
|
||||
if not os.path.isabs(dir):
|
||||
try:
|
||||
pwd = os.getcwd()
|
||||
except OSError:
|
||||
pwd = None
|
||||
if pwd:
|
||||
dir = os.path.join(pwd, dir)
|
||||
dir = os.path.normpath(dir)
|
||||
self.filter.delete(0, END)
|
||||
self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
|
||||
|
||||
def set_selection(self, file):
|
||||
self.selection.delete(0, END)
|
||||
self.selection.insert(END, os.path.join(self.directory, file))
|
||||
|
||||
|
||||
class LoadFileDialog(FileDialog):
|
||||
|
||||
"""File selection dialog which checks that the file exists."""
|
||||
|
||||
title = "Load File Selection Dialog"
|
||||
|
||||
def ok_command(self):
|
||||
file = self.get_selection()
|
||||
if not os.path.isfile(file):
|
||||
self.master.bell()
|
||||
else:
|
||||
self.quit(file)
|
||||
|
||||
|
||||
class SaveFileDialog(FileDialog):
|
||||
|
||||
"""File selection dialog which checks that the file may be created."""
|
||||
|
||||
title = "Save File Selection Dialog"
|
||||
|
||||
def ok_command(self):
|
||||
file = self.get_selection()
|
||||
if os.path.exists(file):
|
||||
if os.path.isdir(file):
|
||||
self.master.bell()
|
||||
return
|
||||
d = Dialog(self.top,
|
||||
title="Overwrite Existing File Question",
|
||||
text="Overwrite existing file %r?" % (file,),
|
||||
bitmap='questhead',
|
||||
default=1,
|
||||
strings=("Yes", "Cancel"))
|
||||
if d.num != 0:
|
||||
return
|
||||
else:
|
||||
head, tail = os.path.split(file)
|
||||
if not os.path.isdir(head):
|
||||
self.master.bell()
|
||||
return
|
||||
self.quit(file)
|
||||
|
||||
|
||||
# For the following classes and modules:
|
||||
#
|
||||
# options (all have default values):
|
||||
#
|
||||
# - defaultextension: added to filename if not explicitly given
|
||||
#
|
||||
# - filetypes: sequence of (label, pattern) tuples. the same pattern
|
||||
# may occur with several patterns. use "*" as pattern to indicate
|
||||
# all files.
|
||||
#
|
||||
# - initialdir: initial directory. preserved by dialog instance.
|
||||
#
|
||||
# - initialfile: initial file (ignored by the open dialog). preserved
|
||||
# by dialog instance.
|
||||
#
|
||||
# - parent: which window to place the dialog on top of
|
||||
#
|
||||
# - title: dialog title
|
||||
#
|
||||
# - multiple: if true user may select more than one file
|
||||
#
|
||||
# options for the directory chooser:
|
||||
#
|
||||
# - initialdir, parent, title: see above
|
||||
#
|
||||
# - mustexist: if true, user must pick an existing directory
|
||||
#
|
||||
|
||||
|
||||
class _Dialog(commondialog.Dialog):
|
||||
|
||||
def _fixoptions(self):
|
||||
try:
|
||||
# make sure "filetypes" is a tuple
|
||||
self.options["filetypes"] = tuple(self.options["filetypes"])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
if result:
|
||||
# keep directory and filename until next time
|
||||
# convert Tcl path objects to strings
|
||||
try:
|
||||
result = result.string
|
||||
except AttributeError:
|
||||
# it already is a string
|
||||
pass
|
||||
path, file = os.path.split(result)
|
||||
self.options["initialdir"] = path
|
||||
self.options["initialfile"] = file
|
||||
self.filename = result # compatibility
|
||||
return result
|
||||
|
||||
|
||||
#
|
||||
# file dialogs
|
||||
|
||||
class Open(_Dialog):
|
||||
"Ask for a filename to open"
|
||||
|
||||
command = "tk_getOpenFile"
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
if isinstance(result, tuple):
|
||||
# multiple results:
|
||||
result = tuple([getattr(r, "string", r) for r in result])
|
||||
if result:
|
||||
path, file = os.path.split(result[0])
|
||||
self.options["initialdir"] = path
|
||||
# don't set initialfile or filename, as we have multiple of these
|
||||
return result
|
||||
if not widget.tk.wantobjects() and "multiple" in self.options:
|
||||
# Need to split result explicitly
|
||||
return self._fixresult(widget, widget.tk.splitlist(result))
|
||||
return _Dialog._fixresult(self, widget, result)
|
||||
|
||||
|
||||
class SaveAs(_Dialog):
|
||||
"Ask for a filename to save as"
|
||||
|
||||
command = "tk_getSaveFile"
|
||||
|
||||
|
||||
# the directory dialog has its own _fix routines.
|
||||
class Directory(commondialog.Dialog):
|
||||
"Ask for a directory"
|
||||
|
||||
command = "tk_chooseDirectory"
|
||||
|
||||
def _fixresult(self, widget, result):
|
||||
if result:
|
||||
# convert Tcl path objects to strings
|
||||
try:
|
||||
result = result.string
|
||||
except AttributeError:
|
||||
# it already is a string
|
||||
pass
|
||||
# keep directory until next time
|
||||
self.options["initialdir"] = result
|
||||
self.directory = result # compatibility
|
||||
return result
|
||||
|
||||
#
|
||||
# convenience stuff
|
||||
|
||||
|
||||
def askopenfilename(**options):
|
||||
"Ask for a filename to open"
|
||||
|
||||
return Open(**options).show()
|
||||
|
||||
|
||||
def asksaveasfilename(**options):
|
||||
"Ask for a filename to save as"
|
||||
|
||||
return SaveAs(**options).show()
|
||||
|
||||
|
||||
def askopenfilenames(**options):
|
||||
"""Ask for multiple filenames to open
|
||||
|
||||
Returns a list of filenames or empty list if
|
||||
cancel button selected
|
||||
"""
|
||||
options["multiple"]=1
|
||||
return Open(**options).show()
|
||||
|
||||
# FIXME: are the following perhaps a bit too convenient?
|
||||
|
||||
|
||||
def askopenfile(mode = "r", **options):
|
||||
"Ask for a filename to open, and returned the opened file"
|
||||
|
||||
filename = Open(**options).show()
|
||||
if filename:
|
||||
return open(filename, mode)
|
||||
return None
|
||||
|
||||
|
||||
def askopenfiles(mode = "r", **options):
|
||||
"""Ask for multiple filenames and return the open file
|
||||
objects
|
||||
|
||||
returns a list of open file objects or an empty list if
|
||||
cancel selected
|
||||
"""
|
||||
|
||||
files = askopenfilenames(**options)
|
||||
if files:
|
||||
ofiles=[]
|
||||
for filename in files:
|
||||
ofiles.append(open(filename, mode))
|
||||
files=ofiles
|
||||
return files
|
||||
|
||||
|
||||
def asksaveasfile(mode = "w", **options):
|
||||
"Ask for a filename to save as, and returned the opened file"
|
||||
|
||||
filename = SaveAs(**options).show()
|
||||
if filename:
|
||||
return open(filename, mode)
|
||||
return None
|
||||
|
||||
|
||||
def askdirectory (**options):
|
||||
"Ask for a directory, and return the file name"
|
||||
return Directory(**options).show()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# test stuff
|
||||
|
||||
def test():
|
||||
"""Simple test program."""
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
fd = LoadFileDialog(root)
|
||||
loadfile = fd.go(key="test")
|
||||
fd = SaveFileDialog(root)
|
||||
savefile = fd.go(key="test")
|
||||
print(loadfile, savefile)
|
||||
|
||||
# Since the file name may contain non-ASCII characters, we need
|
||||
# to find an encoding that likely supports the file name, and
|
||||
# displays correctly on the terminal.
|
||||
|
||||
# Start off with UTF-8
|
||||
enc = "utf-8"
|
||||
|
||||
# See whether CODESET is defined
|
||||
try:
|
||||
import locale
|
||||
locale.setlocale(locale.LC_ALL,'')
|
||||
enc = locale.nl_langinfo(locale.CODESET)
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
# dialog for opening files
|
||||
|
||||
openfilename=askopenfilename(filetypes=[("all files", "*")])
|
||||
try:
|
||||
fp=open(openfilename,"r")
|
||||
fp.close()
|
||||
except BaseException as exc:
|
||||
print("Could not open File: ")
|
||||
print(exc)
|
||||
|
||||
print("open", openfilename.encode(enc))
|
||||
|
||||
# dialog for saving files
|
||||
|
||||
saveasfilename=asksaveasfilename()
|
||||
print("saveas", saveasfilename.encode(enc))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
Vendored
+239
@@ -0,0 +1,239 @@
|
||||
# Tkinter font wrapper
|
||||
#
|
||||
# written by Fredrik Lundh, February 1998
|
||||
#
|
||||
|
||||
import itertools
|
||||
import tkinter
|
||||
|
||||
__version__ = "0.9"
|
||||
__all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC",
|
||||
"nametofont", "Font", "families", "names"]
|
||||
|
||||
# weight/slant
|
||||
NORMAL = "normal"
|
||||
ROMAN = "roman"
|
||||
BOLD = "bold"
|
||||
ITALIC = "italic"
|
||||
|
||||
|
||||
def nametofont(name, root=None):
|
||||
"""Given the name of a tk named font, returns a Font representation.
|
||||
"""
|
||||
return Font(name=name, exists=True, root=root)
|
||||
|
||||
|
||||
class Font:
|
||||
"""Represents a named font.
|
||||
|
||||
Constructor options are:
|
||||
|
||||
font -- font specifier (name, system font, or (family, size, style)-tuple)
|
||||
name -- name to use for this font configuration (defaults to a unique name)
|
||||
exists -- does a named font by this name already exist?
|
||||
Creates a new named font if False, points to the existing font if True.
|
||||
Raises _tkinter.TclError if the assertion is false.
|
||||
|
||||
the following are ignored if font is specified:
|
||||
|
||||
family -- font 'family', e.g. Courier, Times, Helvetica
|
||||
size -- font size in points
|
||||
weight -- font thickness: NORMAL, BOLD
|
||||
slant -- font slant: ROMAN, ITALIC
|
||||
underline -- font underlining: false (0), true (1)
|
||||
overstrike -- font strikeout: false (0), true (1)
|
||||
|
||||
"""
|
||||
|
||||
counter = itertools.count(1)
|
||||
|
||||
def _set(self, kw):
|
||||
options = []
|
||||
for k, v in kw.items():
|
||||
options.append("-"+k)
|
||||
options.append(str(v))
|
||||
return tuple(options)
|
||||
|
||||
def _get(self, args):
|
||||
options = []
|
||||
for k in args:
|
||||
options.append("-"+k)
|
||||
return tuple(options)
|
||||
|
||||
def _mkdict(self, args):
|
||||
options = {}
|
||||
for i in range(0, len(args), 2):
|
||||
options[args[i][1:]] = args[i+1]
|
||||
return options
|
||||
|
||||
def __init__(self, root=None, font=None, name=None, exists=False,
|
||||
**options):
|
||||
if root is None:
|
||||
root = tkinter._get_default_root('use font')
|
||||
tk = getattr(root, 'tk', root)
|
||||
if font:
|
||||
# get actual settings corresponding to the given font
|
||||
font = tk.splitlist(tk.call("font", "actual", font))
|
||||
else:
|
||||
font = self._set(options)
|
||||
if not name:
|
||||
name = "font" + str(next(self.counter))
|
||||
self.name = name
|
||||
|
||||
if exists:
|
||||
self.delete_font = False
|
||||
# confirm font exists
|
||||
if self.name not in tk.splitlist(tk.call("font", "names")):
|
||||
raise tkinter._tkinter.TclError(
|
||||
"named font %s does not already exist" % (self.name,))
|
||||
# if font config info supplied, apply it
|
||||
if font:
|
||||
tk.call("font", "configure", self.name, *font)
|
||||
else:
|
||||
# create new font (raises TclError if the font exists)
|
||||
tk.call("font", "create", self.name, *font)
|
||||
self.delete_font = True
|
||||
self._tk = tk
|
||||
self._split = tk.splitlist
|
||||
self._call = tk.call
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__module__}.{self.__class__.__qualname__}" \
|
||||
f" object {self.name!r}>"
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Font):
|
||||
return NotImplemented
|
||||
return self.name == other.name and self._tk == other._tk
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.cget(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.configure(**{key: value})
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
if self.delete_font:
|
||||
self._call("font", "delete", self.name)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def copy(self):
|
||||
"Return a distinct copy of the current font"
|
||||
return Font(self._tk, **self.actual())
|
||||
|
||||
def actual(self, option=None, displayof=None):
|
||||
"Return actual font attributes"
|
||||
args = ()
|
||||
if displayof:
|
||||
args = ('-displayof', displayof)
|
||||
if option:
|
||||
args = args + ('-' + option, )
|
||||
return self._call("font", "actual", self.name, *args)
|
||||
else:
|
||||
return self._mkdict(
|
||||
self._split(self._call("font", "actual", self.name, *args)))
|
||||
|
||||
def cget(self, option):
|
||||
"Get font attribute"
|
||||
return self._call("font", "config", self.name, "-"+option)
|
||||
|
||||
def config(self, **options):
|
||||
"Modify font attributes"
|
||||
if options:
|
||||
self._call("font", "config", self.name,
|
||||
*self._set(options))
|
||||
else:
|
||||
return self._mkdict(
|
||||
self._split(self._call("font", "config", self.name)))
|
||||
|
||||
configure = config
|
||||
|
||||
def measure(self, text, displayof=None):
|
||||
"Return text width"
|
||||
args = (text,)
|
||||
if displayof:
|
||||
args = ('-displayof', displayof, text)
|
||||
return self._tk.getint(self._call("font", "measure", self.name, *args))
|
||||
|
||||
def metrics(self, *options, **kw):
|
||||
"""Return font metrics.
|
||||
|
||||
For best performance, create a dummy widget
|
||||
using this font before calling this method."""
|
||||
args = ()
|
||||
displayof = kw.pop('displayof', None)
|
||||
if displayof:
|
||||
args = ('-displayof', displayof)
|
||||
if options:
|
||||
args = args + self._get(options)
|
||||
return self._tk.getint(
|
||||
self._call("font", "metrics", self.name, *args))
|
||||
else:
|
||||
res = self._split(self._call("font", "metrics", self.name, *args))
|
||||
options = {}
|
||||
for i in range(0, len(res), 2):
|
||||
options[res[i][1:]] = self._tk.getint(res[i+1])
|
||||
return options
|
||||
|
||||
|
||||
def families(root=None, displayof=None):
|
||||
"Get font families (as a tuple)"
|
||||
if root is None:
|
||||
root = tkinter._get_default_root('use font.families()')
|
||||
args = ()
|
||||
if displayof:
|
||||
args = ('-displayof', displayof)
|
||||
return root.tk.splitlist(root.tk.call("font", "families", *args))
|
||||
|
||||
|
||||
def names(root=None):
|
||||
"Get names of defined fonts (as a tuple)"
|
||||
if root is None:
|
||||
root = tkinter._get_default_root('use font.names()')
|
||||
return root.tk.splitlist(root.tk.call("font", "names"))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# test stuff
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
root = tkinter.Tk()
|
||||
|
||||
# create a font
|
||||
f = Font(family="times", size=30, weight=NORMAL)
|
||||
|
||||
print(f.actual())
|
||||
print(f.actual("family"))
|
||||
print(f.actual("weight"))
|
||||
|
||||
print(f.config())
|
||||
print(f.cget("family"))
|
||||
print(f.cget("weight"))
|
||||
|
||||
print(names())
|
||||
|
||||
print(f.measure("hello"), f.metrics("linespace"))
|
||||
|
||||
print(f.metrics(displayof=root))
|
||||
|
||||
f = Font(font=("Courier", 20, "bold"))
|
||||
print(f.measure("hello"), f.metrics("linespace", displayof=root))
|
||||
|
||||
w = tkinter.Label(root, text="Hello, world", font=f)
|
||||
w.pack()
|
||||
|
||||
w = tkinter.Button(root, text="Quit!", command=root.destroy)
|
||||
w.pack()
|
||||
|
||||
fb = Font(font=w["font"]).copy()
|
||||
fb.config(weight=BOLD)
|
||||
|
||||
w.config(font=fb)
|
||||
|
||||
tkinter.mainloop()
|
||||
Vendored
+146
@@ -0,0 +1,146 @@
|
||||
# tk common message boxes
|
||||
#
|
||||
# this module provides an interface to the native message boxes
|
||||
# available in Tk 4.2 and newer.
|
||||
#
|
||||
# written by Fredrik Lundh, May 1997
|
||||
#
|
||||
|
||||
#
|
||||
# options (all have default values):
|
||||
#
|
||||
# - default: which button to make default (one of the reply codes)
|
||||
#
|
||||
# - icon: which icon to display (see below)
|
||||
#
|
||||
# - message: the message to display
|
||||
#
|
||||
# - parent: which window to place the dialog on top of
|
||||
#
|
||||
# - title: dialog title
|
||||
#
|
||||
# - type: dialog type; that is, which buttons to display (see below)
|
||||
#
|
||||
|
||||
from tkinter.commondialog import Dialog
|
||||
|
||||
__all__ = ["showinfo", "showwarning", "showerror",
|
||||
"askquestion", "askokcancel", "askyesno",
|
||||
"askyesnocancel", "askretrycancel"]
|
||||
|
||||
#
|
||||
# constants
|
||||
|
||||
# icons
|
||||
ERROR = "error"
|
||||
INFO = "info"
|
||||
QUESTION = "question"
|
||||
WARNING = "warning"
|
||||
|
||||
# types
|
||||
ABORTRETRYIGNORE = "abortretryignore"
|
||||
OK = "ok"
|
||||
OKCANCEL = "okcancel"
|
||||
RETRYCANCEL = "retrycancel"
|
||||
YESNO = "yesno"
|
||||
YESNOCANCEL = "yesnocancel"
|
||||
|
||||
# replies
|
||||
ABORT = "abort"
|
||||
RETRY = "retry"
|
||||
IGNORE = "ignore"
|
||||
OK = "ok"
|
||||
CANCEL = "cancel"
|
||||
YES = "yes"
|
||||
NO = "no"
|
||||
|
||||
|
||||
#
|
||||
# message dialog class
|
||||
|
||||
class Message(Dialog):
|
||||
"A message box"
|
||||
|
||||
command = "tk_messageBox"
|
||||
|
||||
|
||||
#
|
||||
# convenience stuff
|
||||
|
||||
# Rename _icon and _type options to allow overriding them in options
|
||||
def _show(title=None, message=None, _icon=None, _type=None, **options):
|
||||
if _icon and "icon" not in options: options["icon"] = _icon
|
||||
if _type and "type" not in options: options["type"] = _type
|
||||
if title: options["title"] = title
|
||||
if message: options["message"] = message
|
||||
res = Message(**options).show()
|
||||
# In some Tcl installations, yes/no is converted into a boolean.
|
||||
if isinstance(res, bool):
|
||||
if res:
|
||||
return YES
|
||||
return NO
|
||||
# In others we get a Tcl_Obj.
|
||||
return str(res)
|
||||
|
||||
|
||||
def showinfo(title=None, message=None, **options):
|
||||
"Show an info message"
|
||||
return _show(title, message, INFO, OK, **options)
|
||||
|
||||
|
||||
def showwarning(title=None, message=None, **options):
|
||||
"Show a warning message"
|
||||
return _show(title, message, WARNING, OK, **options)
|
||||
|
||||
|
||||
def showerror(title=None, message=None, **options):
|
||||
"Show an error message"
|
||||
return _show(title, message, ERROR, OK, **options)
|
||||
|
||||
|
||||
def askquestion(title=None, message=None, **options):
|
||||
"Ask a question"
|
||||
return _show(title, message, QUESTION, YESNO, **options)
|
||||
|
||||
|
||||
def askokcancel(title=None, message=None, **options):
|
||||
"Ask if operation should proceed; return true if the answer is ok"
|
||||
s = _show(title, message, QUESTION, OKCANCEL, **options)
|
||||
return s == OK
|
||||
|
||||
|
||||
def askyesno(title=None, message=None, **options):
|
||||
"Ask a question; return true if the answer is yes"
|
||||
s = _show(title, message, QUESTION, YESNO, **options)
|
||||
return s == YES
|
||||
|
||||
|
||||
def askyesnocancel(title=None, message=None, **options):
|
||||
"Ask a question; return true if the answer is yes, None if cancelled."
|
||||
s = _show(title, message, QUESTION, YESNOCANCEL, **options)
|
||||
# s might be a Tcl index object, so convert it to a string
|
||||
s = str(s)
|
||||
if s == CANCEL:
|
||||
return None
|
||||
return s == YES
|
||||
|
||||
|
||||
def askretrycancel(title=None, message=None, **options):
|
||||
"Ask if operation should be retried; return true if the answer is yes"
|
||||
s = _show(title, message, WARNING, RETRYCANCEL, **options)
|
||||
return s == RETRY
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# test stuff
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
print("info", showinfo("Spam", "Egg Information"))
|
||||
print("warning", showwarning("Spam", "Egg Warning"))
|
||||
print("error", showerror("Spam", "Egg Alert"))
|
||||
print("question", askquestion("Spam", "Question?"))
|
||||
print("proceed", askokcancel("Spam", "Proceed?"))
|
||||
print("yes/no", askyesno("Spam", "Got it?"))
|
||||
print("yes/no/cancel", askyesnocancel("Spam", "Want it?"))
|
||||
print("try again", askretrycancel("Spam", "Try again?"))
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
"""A ScrolledText widget feels like a text widget but also has a
|
||||
vertical scroll bar on its right. (Later, options may be added to
|
||||
add a horizontal bar as well, to make the bars disappear
|
||||
automatically when not needed, to move them to the other side of the
|
||||
window, etc.)
|
||||
|
||||
Configuration options are passed to the Text widget.
|
||||
A Frame widget is inserted between the master and the text, to hold
|
||||
the Scrollbar widget.
|
||||
Most methods calls are inherited from the Text widget; Pack, Grid and
|
||||
Place methods are redirected to the Frame widget however.
|
||||
"""
|
||||
|
||||
from tkinter import Frame, Text, Scrollbar, Pack, Grid, Place
|
||||
from tkinter.constants import RIGHT, LEFT, Y, BOTH
|
||||
|
||||
__all__ = ['ScrolledText']
|
||||
|
||||
|
||||
class ScrolledText(Text):
|
||||
def __init__(self, master=None, **kw):
|
||||
self.frame = Frame(master)
|
||||
self.vbar = Scrollbar(self.frame)
|
||||
self.vbar.pack(side=RIGHT, fill=Y)
|
||||
|
||||
kw.update({'yscrollcommand': self.vbar.set})
|
||||
Text.__init__(self, self.frame, **kw)
|
||||
self.pack(side=LEFT, fill=BOTH, expand=True)
|
||||
self.vbar['command'] = self.yview
|
||||
|
||||
# Copy geometry methods of self.frame without overriding Text
|
||||
# methods -- hack!
|
||||
text_meths = vars(Text).keys()
|
||||
methods = vars(Pack).keys() | vars(Grid).keys() | vars(Place).keys()
|
||||
methods = methods.difference(text_meths)
|
||||
|
||||
for m in methods:
|
||||
if m[0] != '_' and m != 'config' and m != 'configure':
|
||||
setattr(self, m, getattr(self.frame, m))
|
||||
|
||||
def __str__(self):
|
||||
return str(self.frame)
|
||||
|
||||
|
||||
def example():
|
||||
from tkinter.constants import END
|
||||
|
||||
stext = ScrolledText(bg='white', height=10)
|
||||
stext.insert(END, __doc__)
|
||||
stext.pack(fill=BOTH, side=LEFT, expand=True)
|
||||
stext.focus_set()
|
||||
stext.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
example()
|
||||
Vendored
+441
@@ -0,0 +1,441 @@
|
||||
#
|
||||
# An Introduction to Tkinter
|
||||
#
|
||||
# Copyright (c) 1997 by Fredrik Lundh
|
||||
#
|
||||
# This copyright applies to Dialog, askinteger, askfloat and asktring
|
||||
#
|
||||
# fredrik@pythonware.com
|
||||
# http://www.pythonware.com
|
||||
#
|
||||
"""This modules handles dialog boxes.
|
||||
|
||||
It contains the following public symbols:
|
||||
|
||||
SimpleDialog -- A simple but flexible modal dialog box
|
||||
|
||||
Dialog -- a base class for dialogs
|
||||
|
||||
askinteger -- get an integer from the user
|
||||
|
||||
askfloat -- get a float from the user
|
||||
|
||||
askstring -- get a string from the user
|
||||
"""
|
||||
|
||||
from tkinter import *
|
||||
from tkinter import _get_temp_root, _destroy_temp_root
|
||||
from tkinter import messagebox
|
||||
|
||||
|
||||
class SimpleDialog:
|
||||
|
||||
def __init__(self, master,
|
||||
text='', buttons=[], default=None, cancel=None,
|
||||
title=None, class_=None):
|
||||
if class_:
|
||||
self.root = Toplevel(master, class_=class_)
|
||||
else:
|
||||
self.root = Toplevel(master)
|
||||
if title:
|
||||
self.root.title(title)
|
||||
self.root.iconname(title)
|
||||
|
||||
_setup_dialog(self.root)
|
||||
|
||||
self.message = Message(self.root, text=text, aspect=400)
|
||||
self.message.pack(expand=1, fill=BOTH)
|
||||
self.frame = Frame(self.root)
|
||||
self.frame.pack()
|
||||
self.num = default
|
||||
self.cancel = cancel
|
||||
self.default = default
|
||||
self.root.bind('<Return>', self.return_event)
|
||||
for num in range(len(buttons)):
|
||||
s = buttons[num]
|
||||
b = Button(self.frame, text=s,
|
||||
command=(lambda self=self, num=num: self.done(num)))
|
||||
if num == default:
|
||||
b.config(relief=RIDGE, borderwidth=8)
|
||||
b.pack(side=LEFT, fill=BOTH, expand=1)
|
||||
self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window)
|
||||
self.root.transient(master)
|
||||
_place_window(self.root, master)
|
||||
|
||||
def go(self):
|
||||
self.root.wait_visibility()
|
||||
self.root.grab_set()
|
||||
self.root.mainloop()
|
||||
self.root.destroy()
|
||||
return self.num
|
||||
|
||||
def return_event(self, event):
|
||||
if self.default is None:
|
||||
self.root.bell()
|
||||
else:
|
||||
self.done(self.default)
|
||||
|
||||
def wm_delete_window(self):
|
||||
if self.cancel is None:
|
||||
self.root.bell()
|
||||
else:
|
||||
self.done(self.cancel)
|
||||
|
||||
def done(self, num):
|
||||
self.num = num
|
||||
self.root.quit()
|
||||
|
||||
|
||||
class Dialog(Toplevel):
|
||||
|
||||
'''Class to open dialogs.
|
||||
|
||||
This class is intended as a base class for custom dialogs
|
||||
'''
|
||||
|
||||
def __init__(self, parent, title = None):
|
||||
'''Initialize a dialog.
|
||||
|
||||
Arguments:
|
||||
|
||||
parent -- a parent window (the application window)
|
||||
|
||||
title -- the dialog title
|
||||
'''
|
||||
master = parent
|
||||
if master is None:
|
||||
master = _get_temp_root()
|
||||
|
||||
Toplevel.__init__(self, master)
|
||||
|
||||
self.withdraw() # remain invisible for now
|
||||
# If the parent is not viewable, don't
|
||||
# make the child transient, or else it
|
||||
# would be opened withdrawn
|
||||
if parent is not None and parent.winfo_viewable():
|
||||
self.transient(parent)
|
||||
|
||||
if title:
|
||||
self.title(title)
|
||||
|
||||
_setup_dialog(self)
|
||||
|
||||
self.parent = parent
|
||||
|
||||
self.result = None
|
||||
|
||||
body = Frame(self)
|
||||
self.initial_focus = self.body(body)
|
||||
body.pack(padx=5, pady=5)
|
||||
|
||||
self.buttonbox()
|
||||
|
||||
if self.initial_focus is None:
|
||||
self.initial_focus = self
|
||||
|
||||
self.protocol("WM_DELETE_WINDOW", self.cancel)
|
||||
|
||||
_place_window(self, parent)
|
||||
|
||||
self.initial_focus.focus_set()
|
||||
|
||||
# wait for window to appear on screen before calling grab_set
|
||||
self.wait_visibility()
|
||||
self.grab_set()
|
||||
self.wait_window(self)
|
||||
|
||||
def destroy(self):
|
||||
'''Destroy the window'''
|
||||
self.initial_focus = None
|
||||
Toplevel.destroy(self)
|
||||
_destroy_temp_root(self.master)
|
||||
|
||||
#
|
||||
# construction hooks
|
||||
|
||||
def body(self, master):
|
||||
'''create dialog body.
|
||||
|
||||
return widget that should have initial focus.
|
||||
This method should be overridden, and is called
|
||||
by the __init__ method.
|
||||
'''
|
||||
pass
|
||||
|
||||
def buttonbox(self):
|
||||
'''add standard button box.
|
||||
|
||||
override if you do not want the standard buttons
|
||||
'''
|
||||
|
||||
box = Frame(self)
|
||||
|
||||
w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
|
||||
w.pack(side=LEFT, padx=5, pady=5)
|
||||
w = Button(box, text="Cancel", width=10, command=self.cancel)
|
||||
w.pack(side=LEFT, padx=5, pady=5)
|
||||
|
||||
self.bind("<Return>", self.ok)
|
||||
self.bind("<Escape>", self.cancel)
|
||||
|
||||
box.pack()
|
||||
|
||||
#
|
||||
# standard button semantics
|
||||
|
||||
def ok(self, event=None):
|
||||
|
||||
if not self.validate():
|
||||
self.initial_focus.focus_set() # put focus back
|
||||
return
|
||||
|
||||
self.withdraw()
|
||||
self.update_idletasks()
|
||||
|
||||
try:
|
||||
self.apply()
|
||||
finally:
|
||||
self.cancel()
|
||||
|
||||
def cancel(self, event=None):
|
||||
|
||||
# put focus back to the parent window
|
||||
if self.parent is not None:
|
||||
self.parent.focus_set()
|
||||
self.destroy()
|
||||
|
||||
#
|
||||
# command hooks
|
||||
|
||||
def validate(self):
|
||||
'''validate the data
|
||||
|
||||
This method is called automatically to validate the data before the
|
||||
dialog is destroyed. By default, it always validates OK.
|
||||
'''
|
||||
|
||||
return 1 # override
|
||||
|
||||
def apply(self):
|
||||
'''process the data
|
||||
|
||||
This method is called automatically to process the data, *after*
|
||||
the dialog is destroyed. By default, it does nothing.
|
||||
'''
|
||||
|
||||
pass # override
|
||||
|
||||
|
||||
# Place a toplevel window at the center of parent or screen
|
||||
# It is a Python implementation of ::tk::PlaceWindow.
|
||||
def _place_window(w, parent=None):
|
||||
w.wm_withdraw() # Remain invisible while we figure out the geometry
|
||||
w.update_idletasks() # Actualize geometry information
|
||||
|
||||
minwidth = w.winfo_reqwidth()
|
||||
minheight = w.winfo_reqheight()
|
||||
maxwidth = w.winfo_vrootwidth()
|
||||
maxheight = w.winfo_vrootheight()
|
||||
if parent is not None and parent.winfo_ismapped():
|
||||
x = parent.winfo_rootx() + (parent.winfo_width() - minwidth) // 2
|
||||
y = parent.winfo_rooty() + (parent.winfo_height() - minheight) // 2
|
||||
vrootx = w.winfo_vrootx()
|
||||
vrooty = w.winfo_vrooty()
|
||||
x = min(x, vrootx + maxwidth - minwidth)
|
||||
x = max(x, vrootx)
|
||||
y = min(y, vrooty + maxheight - minheight)
|
||||
y = max(y, vrooty)
|
||||
if w._windowingsystem == 'aqua':
|
||||
# Avoid the native menu bar which sits on top of everything.
|
||||
y = max(y, 22)
|
||||
else:
|
||||
x = (w.winfo_screenwidth() - minwidth) // 2
|
||||
y = (w.winfo_screenheight() - minheight) // 2
|
||||
|
||||
w.wm_maxsize(maxwidth, maxheight)
|
||||
w.wm_geometry('+%d+%d' % (x, y))
|
||||
w.wm_deiconify() # Become visible at the desired location
|
||||
|
||||
|
||||
def _setup_dialog(w):
|
||||
if w._windowingsystem == "aqua":
|
||||
w.tk.call("::tk::unsupported::MacWindowStyle", "style",
|
||||
w, "moveableModal", "")
|
||||
elif w._windowingsystem == "x11":
|
||||
w.wm_attributes(type="dialog")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# convenience dialogues
|
||||
|
||||
class _QueryDialog(Dialog):
|
||||
|
||||
def __init__(self, title, prompt,
|
||||
initialvalue=None,
|
||||
minvalue = None, maxvalue = None,
|
||||
parent = None):
|
||||
|
||||
self.prompt = prompt
|
||||
self.minvalue = minvalue
|
||||
self.maxvalue = maxvalue
|
||||
|
||||
self.initialvalue = initialvalue
|
||||
|
||||
Dialog.__init__(self, parent, title)
|
||||
|
||||
def destroy(self):
|
||||
self.entry = None
|
||||
Dialog.destroy(self)
|
||||
|
||||
def body(self, master):
|
||||
|
||||
w = Label(master, text=self.prompt, justify=LEFT)
|
||||
w.grid(row=0, padx=5, sticky=W)
|
||||
|
||||
self.entry = Entry(master, name="entry")
|
||||
self.entry.grid(row=1, padx=5, sticky=W+E)
|
||||
|
||||
if self.initialvalue is not None:
|
||||
self.entry.insert(0, self.initialvalue)
|
||||
self.entry.select_range(0, END)
|
||||
|
||||
return self.entry
|
||||
|
||||
def validate(self):
|
||||
try:
|
||||
result = self.getresult()
|
||||
except ValueError:
|
||||
messagebox.showwarning(
|
||||
"Illegal value",
|
||||
self.errormessage + "\nPlease try again",
|
||||
parent = self
|
||||
)
|
||||
return 0
|
||||
|
||||
if self.minvalue is not None and result < self.minvalue:
|
||||
messagebox.showwarning(
|
||||
"Too small",
|
||||
"The allowed minimum value is %s. "
|
||||
"Please try again." % self.minvalue,
|
||||
parent = self
|
||||
)
|
||||
return 0
|
||||
|
||||
if self.maxvalue is not None and result > self.maxvalue:
|
||||
messagebox.showwarning(
|
||||
"Too large",
|
||||
"The allowed maximum value is %s. "
|
||||
"Please try again." % self.maxvalue,
|
||||
parent = self
|
||||
)
|
||||
return 0
|
||||
|
||||
self.result = result
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
class _QueryInteger(_QueryDialog):
|
||||
errormessage = "Not an integer."
|
||||
|
||||
def getresult(self):
|
||||
return self.getint(self.entry.get())
|
||||
|
||||
|
||||
def askinteger(title, prompt, **kw):
|
||||
'''get an integer from the user
|
||||
|
||||
Arguments:
|
||||
|
||||
title -- the dialog title
|
||||
prompt -- the label text
|
||||
**kw -- see SimpleDialog class
|
||||
|
||||
Return value is an integer
|
||||
'''
|
||||
d = _QueryInteger(title, prompt, **kw)
|
||||
return d.result
|
||||
|
||||
|
||||
class _QueryFloat(_QueryDialog):
|
||||
errormessage = "Not a floating-point value."
|
||||
|
||||
def getresult(self):
|
||||
return self.getdouble(self.entry.get())
|
||||
|
||||
|
||||
def askfloat(title, prompt, **kw):
|
||||
'''get a float from the user
|
||||
|
||||
Arguments:
|
||||
|
||||
title -- the dialog title
|
||||
prompt -- the label text
|
||||
**kw -- see SimpleDialog class
|
||||
|
||||
Return value is a float
|
||||
'''
|
||||
d = _QueryFloat(title, prompt, **kw)
|
||||
return d.result
|
||||
|
||||
|
||||
class _QueryString(_QueryDialog):
|
||||
def __init__(self, *args, **kw):
|
||||
if "show" in kw:
|
||||
self.__show = kw["show"]
|
||||
del kw["show"]
|
||||
else:
|
||||
self.__show = None
|
||||
_QueryDialog.__init__(self, *args, **kw)
|
||||
|
||||
def body(self, master):
|
||||
entry = _QueryDialog.body(self, master)
|
||||
if self.__show is not None:
|
||||
entry.configure(show=self.__show)
|
||||
return entry
|
||||
|
||||
def getresult(self):
|
||||
return self.entry.get()
|
||||
|
||||
|
||||
def askstring(title, prompt, **kw):
|
||||
'''get a string from the user
|
||||
|
||||
Arguments:
|
||||
|
||||
title -- the dialog title
|
||||
prompt -- the label text
|
||||
**kw -- see SimpleDialog class
|
||||
|
||||
Return value is a string
|
||||
'''
|
||||
d = _QueryString(title, prompt, **kw)
|
||||
return d.result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
def test():
|
||||
root = Tk()
|
||||
def doit(root=root):
|
||||
d = SimpleDialog(root,
|
||||
text="This is a test dialog. "
|
||||
"Would this have been an actual dialog, "
|
||||
"the buttons below would have been glowing "
|
||||
"in soft pink light.\n"
|
||||
"Do you believe this?",
|
||||
buttons=["Yes", "No", "Cancel"],
|
||||
default=0,
|
||||
cancel=2,
|
||||
title="Test Dialog")
|
||||
print(d.go())
|
||||
print(askinteger("Spam", "Egg count", initialvalue=12*12))
|
||||
print(askfloat("Spam", "Egg weight\n(in tons)", minvalue=1,
|
||||
maxvalue=100))
|
||||
print(askstring("Spam", "Egg label"))
|
||||
t = Button(root, text='Test', command=doit)
|
||||
t.pack()
|
||||
q = Button(root, text='Quit', command=t.quit)
|
||||
q.pack()
|
||||
t.mainloop()
|
||||
|
||||
test()
|
||||
Vendored
+1647
File diff suppressed because it is too large.
Load diff
@@ -18,6 +18,7 @@ bz2 = ["bzip2"]
|
||||
sqlite = ["dep:libsqlite3-sys"]
|
||||
ssl = ["openssl", "openssl-sys", "foreign-types-shared", "openssl-probe"]
|
||||
ssl-vendor = ["ssl", "openssl/vendored"]
|
||||
tkinter = ["dep:tk", "dep:tcl"]
|
||||
|
||||
[dependencies]
|
||||
# rustpython crates
|
||||
@@ -83,6 +84,10 @@ flate2 = { version = "1.1", default-features = false, features = ["zlib-rs"] }
|
||||
libz-sys = { package = "libz-rs-sys", version = "0.4" }
|
||||
bzip2 = { version = "0.4", optional = true }
|
||||
|
||||
# tkinter
|
||||
tk = { version = "0.1.10", optional = true }
|
||||
tcl = { version = "0.1.9", optional = true }
|
||||
|
||||
# uuid
|
||||
[target.'cfg(not(any(target_os = "ios", target_os = "android", target_os = "windows", target_arch = "wasm32", target_os = "redox")))'.dependencies]
|
||||
mac_address = "1.1.3"
|
||||
|
||||
@@ -80,6 +80,9 @@ mod termios;
|
||||
)))]
|
||||
mod uuid;
|
||||
|
||||
#[cfg(feature = "tkinter")]
|
||||
mod tkinter;
|
||||
|
||||
use rustpython_common as common;
|
||||
use rustpython_vm as vm;
|
||||
|
||||
@@ -192,5 +195,9 @@ pub fn get_module_inits() -> impl Iterator<Item = (Cow<'static, str>, StdlibInit
|
||||
{
|
||||
"_locale" => locale::make_module,
|
||||
}
|
||||
#[cfg(feature = "tkinter")]
|
||||
{
|
||||
"_tkinter" => tkinter::make_module,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
pub(crate) use self::_tkinter::make_module;
|
||||
|
||||
#[pymodule]
|
||||
mod _tkinter {
|
||||
use crate::builtins::PyTypeRef;
|
||||
use rustpython_vm::function::{Either, FuncArgs};
|
||||
use rustpython_vm::{PyResult, VirtualMachine, function::OptionalArg};
|
||||
|
||||
use crate::common::lock::PyRwLock;
|
||||
use std::sync::Arc;
|
||||
use tk::cmd::*;
|
||||
use tk::*;
|
||||
|
||||
#[pyattr]
|
||||
const TK_VERSION: &str = "8.6";
|
||||
#[pyattr]
|
||||
const TCL_VERSION: &str = "8.6";
|
||||
#[pyattr]
|
||||
const READABLE: i32 = 2;
|
||||
#[pyattr]
|
||||
const WRITABLE: i32 = 4;
|
||||
#[pyattr]
|
||||
const EXCEPTION: i32 = 8;
|
||||
|
||||
fn demo() -> tk::TkResult<()> {
|
||||
let tk = make_tk!()?;
|
||||
let root = tk.root();
|
||||
root.add_label(-text("constructs widgets and layout step by step"))?
|
||||
.pack(())?;
|
||||
let f = root.add_frame(())?.pack(())?;
|
||||
let _btn = f
|
||||
.add_button("btn" - text("quit") - command("destroy ."))?
|
||||
.pack(())?;
|
||||
Ok(main_loop())
|
||||
}
|
||||
|
||||
#[pyattr(once, name = "TclError")]
|
||||
fn tcl_error(vm: &VirtualMachine) -> PyTypeRef {
|
||||
vm.ctx.new_exception_type(
|
||||
"zlib",
|
||||
"TclError",
|
||||
Some(vec![vm.ctx.exceptions.exception_type.to_owned()]),
|
||||
)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn create(args: FuncArgs, _vm: &VirtualMachine) -> PyResult<TkApp> {
|
||||
// TODO: handle arguements
|
||||
// TODO: this means creating 2 tk instances is not possible.
|
||||
let tk = Tk::new(()).unwrap();
|
||||
Ok(TkApp {
|
||||
tk: Arc::new(PyRwLock::new(tk)),
|
||||
})
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "tkapp")]
|
||||
#[derive(PyPayload)]
|
||||
struct TkApp {
|
||||
tk: Arc<PyRwLock<tk::Tk<()>>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for TkApp {}
|
||||
|
||||
unsafe impl Sync for TkApp {}
|
||||
|
||||
impl std::fmt::Debug for TkApp {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TkApp").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
impl TkApp {
|
||||
#[pymethod]
|
||||
fn getvar(&self, name: &str) -> PyResult<String> {
|
||||
let tk = self.tk.read().unwrap();
|
||||
Ok(tk.getvar(name).unwrap())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn createcommand(&self, name: String, callback: PyObjectRef) {}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user