Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
356ebc123b |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -157,4 +157,4 @@ cython_debug/
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
.idea/
|
||||
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.12.1
|
||||
15
Pipfile
Normal file
15
Pipfile
Normal file
@@ -0,0 +1,15 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
pyside6 = "==6.6.1"
|
||||
requests = "==2.31.0"
|
||||
wmi = "==1.5.1"
|
||||
pyinstaller = "==6.3.0"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.12"
|
||||
284
Pipfile.lock
generated
Normal file
284
Pipfile.lock
generated
Normal file
@@ -0,0 +1,284 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "c9e6c224ff1fb7073100c794e46ce72371b7c482473c9704fc5709aefe9f1f33"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.12"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"altgraph": {
|
||||
"hashes": [
|
||||
"sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406",
|
||||
"sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"
|
||||
],
|
||||
"version": "==0.17.4"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f",
|
||||
"sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2024.2.2"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027",
|
||||
"sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087",
|
||||
"sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786",
|
||||
"sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8",
|
||||
"sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09",
|
||||
"sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185",
|
||||
"sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574",
|
||||
"sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e",
|
||||
"sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519",
|
||||
"sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898",
|
||||
"sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269",
|
||||
"sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3",
|
||||
"sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f",
|
||||
"sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6",
|
||||
"sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8",
|
||||
"sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a",
|
||||
"sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73",
|
||||
"sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc",
|
||||
"sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714",
|
||||
"sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2",
|
||||
"sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc",
|
||||
"sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce",
|
||||
"sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d",
|
||||
"sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e",
|
||||
"sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6",
|
||||
"sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269",
|
||||
"sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96",
|
||||
"sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d",
|
||||
"sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a",
|
||||
"sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4",
|
||||
"sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77",
|
||||
"sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d",
|
||||
"sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0",
|
||||
"sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed",
|
||||
"sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068",
|
||||
"sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac",
|
||||
"sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25",
|
||||
"sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8",
|
||||
"sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab",
|
||||
"sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26",
|
||||
"sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2",
|
||||
"sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db",
|
||||
"sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f",
|
||||
"sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5",
|
||||
"sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99",
|
||||
"sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c",
|
||||
"sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d",
|
||||
"sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811",
|
||||
"sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa",
|
||||
"sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a",
|
||||
"sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03",
|
||||
"sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b",
|
||||
"sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04",
|
||||
"sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c",
|
||||
"sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001",
|
||||
"sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458",
|
||||
"sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389",
|
||||
"sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99",
|
||||
"sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985",
|
||||
"sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537",
|
||||
"sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238",
|
||||
"sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f",
|
||||
"sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d",
|
||||
"sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796",
|
||||
"sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a",
|
||||
"sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143",
|
||||
"sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8",
|
||||
"sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c",
|
||||
"sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5",
|
||||
"sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5",
|
||||
"sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711",
|
||||
"sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4",
|
||||
"sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6",
|
||||
"sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c",
|
||||
"sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7",
|
||||
"sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4",
|
||||
"sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b",
|
||||
"sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae",
|
||||
"sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12",
|
||||
"sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c",
|
||||
"sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae",
|
||||
"sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8",
|
||||
"sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887",
|
||||
"sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b",
|
||||
"sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4",
|
||||
"sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f",
|
||||
"sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5",
|
||||
"sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33",
|
||||
"sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519",
|
||||
"sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"
|
||||
],
|
||||
"markers": "python_full_version >= '3.7.0'",
|
||||
"version": "==3.3.2"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca",
|
||||
"sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.6"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
|
||||
"sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==23.2"
|
||||
},
|
||||
"pefile": {
|
||||
"hashes": [
|
||||
"sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc",
|
||||
"sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"
|
||||
],
|
||||
"markers": "sys_platform == 'win32'",
|
||||
"version": "==2023.2.7"
|
||||
},
|
||||
"pyinstaller": {
|
||||
"hashes": [
|
||||
"sha256:0597fb04337695e5cc5250253e0655530bf14f264b7a5b7d219cc65f6889c4bd",
|
||||
"sha256:156b32ba943e0090bcc68e40ae1cb68fd92b7f1ab6fe0bdf8faf3d3cfc4e12dd",
|
||||
"sha256:1eadbd1fae84e2e6c678d8b4ed6a232ec5c8fe3a839aea5a3071c4c0282f98cc",
|
||||
"sha256:41c937fe8f07ae02009b3b5a96ac3eb0800a4f8a97af142d4100060fe2135bb9",
|
||||
"sha256:75a6f2a6f835a2e6e0899d10e60c10caf5defd25aced38b1dd48fbbabc89de07",
|
||||
"sha256:886b3b995b674905a20ad5b720b47cc395897d7b391117831027a4c8c5d67a58",
|
||||
"sha256:914d4c96cc99472e37ac552fdd82fbbe09e67bb592d0717fcffaa99ea74273df",
|
||||
"sha256:96c37a1ee5b2fd5bb25c098ef510661d6d17b6515d0b86d8fc93727dd2475ba3",
|
||||
"sha256:abe91106a3bbccc3f3a27af4325676ecdb6f46cb842ac663625002a870fc503b",
|
||||
"sha256:b721d793a33b6d9946c7dd95d3ea7589c0424b51cf1b9fe580f03c544f1336b2",
|
||||
"sha256:de25beb176f73a944758553caacec46cc665bf3910ad8a174706d79cf6e95340",
|
||||
"sha256:e436fcc0ea87c3f132baac916d508c24c84a8f6d8a06c3154fbc753f169b76c7"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version < '3.13' and python_version >= '3.8'",
|
||||
"version": "==6.3.0"
|
||||
},
|
||||
"pyinstaller-hooks-contrib": {
|
||||
"hashes": [
|
||||
"sha256:469b5690df53223e2e8abffb2e44d6ee596e7d79d4b1eed9465123b67439875a",
|
||||
"sha256:a7118c1a5c9788595e5c43ad058a7a5b7b6d59e1eceb42362f6ec1f0b61986b0"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2024.0"
|
||||
},
|
||||
"pyside6": {
|
||||
"hashes": [
|
||||
"sha256:0a67587c088cb80e90d4ce3023b02466ea858c93a6dc9c4e062b13314e03d464",
|
||||
"sha256:3593d605175e83e6952cf3b428ecc9c146af97effb36de921ecf3da2752de082",
|
||||
"sha256:3c348948fe3957b18164c9c7b8942fe065bdb39648b326f212bc114326679fa9",
|
||||
"sha256:ed3822150f0d7a06b68bf4ceebe287515b5e8309bb256e9b49ae405afd062b18"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version < '3.13' and python_version >= '3.8'",
|
||||
"version": "==6.6.1"
|
||||
},
|
||||
"pyside6-addons": {
|
||||
"hashes": [
|
||||
"sha256:5a63a8a943724ce5acd2df72e5ab04982b6906963278cbabb216656b9a26ee18",
|
||||
"sha256:7cb7af1b050c40f7ac891b0e61c758c1923863173932f5b92dc47bdfb4158b42",
|
||||
"sha256:a0982da4033319667f9df5ed6fa8eff300a88216aec103a1fff6751a172b19a0",
|
||||
"sha256:a223575c81e9a13173136c044c3447e25f6d656b462b4d71fc3c6bd9c935a709"
|
||||
],
|
||||
"markers": "python_version < '3.13' and python_version >= '3.8'",
|
||||
"version": "==6.6.1"
|
||||
},
|
||||
"pyside6-essentials": {
|
||||
"hashes": [
|
||||
"sha256:0c8917b15236956957178a8c9854641b12b11dad79ba0caf26147119164c30cf",
|
||||
"sha256:13da926e9e9ee3e26e3f66883a9d5e43726ddee70cdabddca02a07aa1ccf9484",
|
||||
"sha256:a383c3d60298392cfb621ec1a0cf24b4799321e6c5bbafc021d4cc8076ea1315",
|
||||
"sha256:c7185616083eab6f42eaed598d97d49fac4f60ae2e7415194140d54f58c2b42c"
|
||||
],
|
||||
"markers": "python_version < '3.13' and python_version >= '3.8'",
|
||||
"version": "==6.6.1"
|
||||
},
|
||||
"pywin32": {
|
||||
"hashes": [
|
||||
"sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d",
|
||||
"sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65",
|
||||
"sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e",
|
||||
"sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b",
|
||||
"sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4",
|
||||
"sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040",
|
||||
"sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a",
|
||||
"sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36",
|
||||
"sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8",
|
||||
"sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e",
|
||||
"sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802",
|
||||
"sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a",
|
||||
"sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407",
|
||||
"sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"
|
||||
],
|
||||
"version": "==306"
|
||||
},
|
||||
"pywin32-ctypes": {
|
||||
"hashes": [
|
||||
"sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60",
|
||||
"sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"
|
||||
],
|
||||
"markers": "sys_platform == 'win32'",
|
||||
"version": "==0.2.2"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
|
||||
"sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.31.0"
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05",
|
||||
"sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==69.0.3"
|
||||
},
|
||||
"shiboken6": {
|
||||
"hashes": [
|
||||
"sha256:072c35c4fe46ec13b364d9dc47b055bb2277ee3aeaab18c23650280ec362f62a",
|
||||
"sha256:a605960e72af5eef915991cee7eef4cc72f5cabe63b9ae1a955ceb3d3b0a00b9",
|
||||
"sha256:d756fd1fa945b787e8eef142f2eb571da0b4c4dc2f2eec1a7c12a474a2cf84e4",
|
||||
"sha256:fb102e4bc210006f0cdd0ce38e1aaaaf792bd871f02a2b3f01d07922c5cf4c59"
|
||||
],
|
||||
"markers": "python_version < '3.13' and python_version >= '3.8'",
|
||||
"version": "==6.6.1"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20",
|
||||
"sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"wmi": {
|
||||
"hashes": [
|
||||
"sha256:1d6b085e5c445141c475476000b661f60fff1aaa19f76bf82b7abb92e0ff4942",
|
||||
"sha256:b6a6be5711b1b6c8d55bda7a8befd75c48c12b770b9d227d31c1737dbf0d40a6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.5.1"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
||||
10
README.md
Normal file
10
README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# DailyCheck 日常检查工具
|
||||
|
||||
适用平台:Windows
|
||||
|
||||
### 版本日志:
|
||||
|
||||
#### v1.0.0
|
||||
|
||||
初始版本
|
||||
|
||||
159
da_ext_settings.py
Normal file
159
da_ext_settings.py
Normal file
@@ -0,0 +1,159 @@
|
||||
# coding: utf8
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
|
||||
|
||||
class PushButtonWithId(QtWidgets.QPushButton):
|
||||
|
||||
clicked_with_id = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, ids: str, parent: QtWidgets.QWidget = None, title: str = ""):
|
||||
super().__init__(title, parent)
|
||||
self.ids = ids
|
||||
self.clicked.connect(self.on_self_clicked)
|
||||
|
||||
def on_self_clicked(self):
|
||||
self.clicked_with_id.emit(self.ids)
|
||||
|
||||
|
||||
class DaExtSettings(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent: QtWidgets.QWidget = None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("设置")
|
||||
|
||||
self.vly_m = QtWidgets.QVBoxLayout()
|
||||
self.setLayout(self.vly_m)
|
||||
|
||||
self.gbx_exec = QtWidgets.QGroupBox("执行文件路径", self)
|
||||
self.vly_m.addWidget(self.gbx_exec)
|
||||
|
||||
self.gly_gbx_exec = QtWidgets.QGridLayout()
|
||||
self.gbx_exec.setLayout(self.gly_gbx_exec)
|
||||
|
||||
self.lb_exec_chrome = QtWidgets.QLabel("Chrome", self)
|
||||
self.lb_exec_edge = QtWidgets.QLabel("Edge", self)
|
||||
self.lb_exec_brave = QtWidgets.QLabel("Brave", self)
|
||||
self.lne_exec_chrome = QtWidgets.QLineEdit(self)
|
||||
self.lne_exec_edge = QtWidgets.QLineEdit(self)
|
||||
self.lne_exec_brave = QtWidgets.QLineEdit(self)
|
||||
self.pbn_exec_chrome = PushButtonWithId("ChromeExec", self, "选择")
|
||||
self.pbn_exec_edge = PushButtonWithId("EdgeExec", self, "选择")
|
||||
self.pbn_exec_brave = PushButtonWithId("BraveExec", self, "选择")
|
||||
|
||||
self.gly_gbx_exec.addWidget(self.lb_exec_chrome, 0, 0)
|
||||
self.gly_gbx_exec.addWidget(self.lb_exec_edge, 1, 0)
|
||||
self.gly_gbx_exec.addWidget(self.lb_exec_brave, 2, 0)
|
||||
self.gly_gbx_exec.addWidget(self.lne_exec_chrome, 0, 1)
|
||||
self.gly_gbx_exec.addWidget(self.lne_exec_edge, 1, 1)
|
||||
self.gly_gbx_exec.addWidget(self.lne_exec_brave, 2, 1)
|
||||
self.gly_gbx_exec.addWidget(self.pbn_exec_chrome, 0, 2)
|
||||
self.gly_gbx_exec.addWidget(self.pbn_exec_edge, 1, 2)
|
||||
self.gly_gbx_exec.addWidget(self.pbn_exec_brave, 2, 2)
|
||||
|
||||
self.gbx_data = QtWidgets.QGroupBox("用户数据路径", self)
|
||||
self.vly_m.addWidget(self.gbx_data)
|
||||
|
||||
self.gly_gbx_data = QtWidgets.QGridLayout()
|
||||
self.gbx_data.setLayout(self.gly_gbx_data)
|
||||
|
||||
self.lb_data_chrome = QtWidgets.QLabel("Chrome", self)
|
||||
self.lb_data_edge = QtWidgets.QLabel("Edge", self)
|
||||
self.lb_data_brave = QtWidgets.QLabel("Brave", self)
|
||||
self.lne_data_chrome = QtWidgets.QLineEdit(self)
|
||||
self.lne_data_edge = QtWidgets.QLineEdit(self)
|
||||
self.lne_data_brave = QtWidgets.QLineEdit(self)
|
||||
self.pbn_data_chrome = PushButtonWithId("ChromeData", self, "选择")
|
||||
self.pbn_data_edge = PushButtonWithId("EdgeData", self, "选择")
|
||||
self.pbn_data_brave = PushButtonWithId("BraveData", self, "选择")
|
||||
|
||||
self.gly_gbx_data.addWidget(self.lb_data_chrome, 0, 0)
|
||||
self.gly_gbx_data.addWidget(self.lb_data_edge, 1, 0)
|
||||
self.gly_gbx_data.addWidget(self.lb_data_brave, 2, 0)
|
||||
self.gly_gbx_data.addWidget(self.lne_data_chrome, 0, 1)
|
||||
self.gly_gbx_data.addWidget(self.lne_data_edge, 1, 1)
|
||||
self.gly_gbx_data.addWidget(self.lne_data_brave, 2, 1)
|
||||
self.gly_gbx_data.addWidget(self.pbn_data_chrome, 0, 2)
|
||||
self.gly_gbx_data.addWidget(self.pbn_data_edge, 1, 2)
|
||||
self.gly_gbx_data.addWidget(self.pbn_data_brave, 2, 2)
|
||||
|
||||
self.hly_bot = QtWidgets.QHBoxLayout()
|
||||
self.pbn_save = QtWidgets.QPushButton("保存", self)
|
||||
self.pbn_cancel = QtWidgets.QPushButton("取消", self)
|
||||
|
||||
self.hly_bot.addStretch(1)
|
||||
self.hly_bot.addWidget(self.pbn_save)
|
||||
self.hly_bot.addWidget(self.pbn_cancel)
|
||||
|
||||
self.vly_m.addLayout(self.hly_bot)
|
||||
self.vly_m.addStretch(1)
|
||||
|
||||
self.pbn_save.clicked.connect(self.on_pbn_save_clicked)
|
||||
self.pbn_cancel.clicked.connect(self.on_pbn_cancel_clicked)
|
||||
|
||||
self.pbn_exec_chrome.clicked_with_id.connect(self.on_pbn_exec_n_clicked_with_id)
|
||||
self.pbn_exec_edge.clicked_with_id.connect(self.on_pbn_exec_n_clicked_with_id)
|
||||
self.pbn_exec_brave.clicked_with_id.connect(self.on_pbn_exec_n_clicked_with_id)
|
||||
|
||||
self.pbn_data_chrome.clicked_with_id.connect(self.on_pbn_data_n_clicked_with_id)
|
||||
self.pbn_data_edge.clicked_with_id.connect(self.on_pbn_data_n_clicked_with_id)
|
||||
self.pbn_data_brave.clicked_with_id.connect(self.on_pbn_data_n_clicked_with_id)
|
||||
|
||||
self.read_settings()
|
||||
|
||||
def sizeHint(self):
|
||||
return QtCore.QSize(540, 140)
|
||||
|
||||
def read_settings(self):
|
||||
us = QtCore.QSettings()
|
||||
chrome_exec = str(us.value("ChromeExec", ""))
|
||||
edge_exec = str(us.value("EdgeExec", ""))
|
||||
brave_exec = str(us.value("BraveExec", ""))
|
||||
|
||||
chrome_data = str(us.value("ChromeData", ""))
|
||||
edge_data = str(us.value("EdgeData", ""))
|
||||
brave_data = str(us.value("BraveData", ""))
|
||||
|
||||
self.lne_exec_chrome.setText(chrome_exec)
|
||||
self.lne_exec_edge.setText(edge_exec)
|
||||
self.lne_exec_brave.setText(brave_exec)
|
||||
|
||||
self.lne_data_chrome.setText(chrome_data)
|
||||
self.lne_data_edge.setText(edge_data)
|
||||
self.lne_data_brave.setText(brave_data)
|
||||
|
||||
def on_pbn_exec_n_clicked_with_id(self, ids: str):
|
||||
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, f"选择 {ids}")
|
||||
if len(filename) == 0:
|
||||
return
|
||||
if ids == "ChromeExec":
|
||||
self.lne_exec_chrome.setText(filename)
|
||||
elif ids == "EdgeExec":
|
||||
self.lne_exec_edge.setText(filename)
|
||||
elif ids == "BraveExec":
|
||||
self.lne_exec_brave.setText(filename)
|
||||
|
||||
def on_pbn_data_n_clicked_with_id(self, ids: str):
|
||||
dirname = QtWidgets.QFileDialog.getExistingDirectory(self, f"选择 {ids}")
|
||||
if len(dirname) == 0:
|
||||
return
|
||||
if ids == "ChromeData":
|
||||
self.lne_data_chrome.setText(dirname)
|
||||
elif ids == "EdgeData":
|
||||
self.lne_data_edge.setText(dirname)
|
||||
elif ids == "BraveData":
|
||||
self.lne_data_brave.setText(dirname)
|
||||
|
||||
def on_pbn_save_clicked(self):
|
||||
us = QtCore.QSettings()
|
||||
us.setValue("ChromeExec", self.lne_exec_chrome.text())
|
||||
us.setValue("EdgeExec", self.lne_exec_edge.text())
|
||||
us.setValue("BraveExec", self.lne_exec_brave.text())
|
||||
|
||||
us.setValue("ChromeData", self.lne_data_chrome.text())
|
||||
us.setValue("EdgeData", self.lne_data_edge.text())
|
||||
us.setValue("BraveData", self.lne_data_brave.text())
|
||||
|
||||
self.accept()
|
||||
|
||||
def on_pbn_cancel_clicked(self):
|
||||
self.reject()
|
||||
157
da_show_profiles.py
Normal file
157
da_show_profiles.py
Normal file
@@ -0,0 +1,157 @@
|
||||
# coding: utf8
|
||||
import os
|
||||
from pathlib import Path
|
||||
from PySide6 import QtWidgets, QtCore, QtGui
|
||||
from util_ext import ProfilesData, DeleteThread, DeleteThreadManager
|
||||
from global_vars import accept_warning
|
||||
|
||||
|
||||
def sort_profiles_id_func(profile_id: str) -> int:
|
||||
if profile_id == "Default":
|
||||
return 0
|
||||
else:
|
||||
seq = profile_id.split(" ", 1)[-1]
|
||||
try:
|
||||
return int(seq)
|
||||
except ValueError:
|
||||
# if the id is weird
|
||||
return 999
|
||||
|
||||
|
||||
class ProfilesModel(QtCore.QAbstractTableModel):
|
||||
|
||||
def __init__(self,
|
||||
profiles_data: ProfilesData,
|
||||
ext_id: str,
|
||||
profiles: list[str],
|
||||
parent=None):
|
||||
super().__init__(parent)
|
||||
self.ext_id = ext_id
|
||||
self.profiles = profiles
|
||||
self.profiles.sort(key=sort_profiles_id_func)
|
||||
self.profiles_data = profiles_data
|
||||
|
||||
def rowCount(self, parent: QtCore.QModelIndex = ...):
|
||||
return len(self.profiles)
|
||||
|
||||
def columnCount(self, parent: QtCore.QModelIndex = ...):
|
||||
return 2
|
||||
|
||||
def data(self, index: QtCore.QModelIndex, role: int = ...):
|
||||
row = index.row()
|
||||
col = index.column()
|
||||
|
||||
if col == 0:
|
||||
if role == QtCore.Qt.ItemDataRole.DisplayRole:
|
||||
return self.profiles[row]
|
||||
elif col == 1:
|
||||
if role == QtCore.Qt.ItemDataRole.DisplayRole:
|
||||
return self.profiles_data[self.profiles[row]].name
|
||||
|
||||
def headerData(self, section: int, orientation: QtCore.Qt.Orientation, role: int = ...):
|
||||
if role == QtCore.Qt.ItemDataRole.DisplayRole:
|
||||
if section == 0:
|
||||
return "ID"
|
||||
if section == 1:
|
||||
return "名称"
|
||||
|
||||
|
||||
class DaShowProfiles(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self,
|
||||
browser: str,
|
||||
is_compat: bool,
|
||||
profiles_data: ProfilesData,
|
||||
ext_id: str,
|
||||
ext_name: str,
|
||||
ext_icon: QtGui.QIcon,
|
||||
profiles: list[str],
|
||||
parent: QtWidgets.QWidget = None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(ext_name)
|
||||
self.setWindowIcon(ext_icon)
|
||||
self.browser = browser
|
||||
self.is_compat = is_compat
|
||||
|
||||
self.process = QtCore.QProcess(self)
|
||||
|
||||
# ========== UI ==============
|
||||
|
||||
self.vly_m = QtWidgets.QVBoxLayout()
|
||||
self.setLayout(self.vly_m)
|
||||
|
||||
self.lne_info = QtWidgets.QLineEdit(ext_id, self)
|
||||
self.lne_info.setReadOnly(True)
|
||||
self.vly_m.addWidget(self.lne_info)
|
||||
|
||||
self.trv_profiles = QtWidgets.QTreeView(self)
|
||||
self.trv_profiles.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection)
|
||||
self.vly_m.addWidget(self.trv_profiles)
|
||||
|
||||
self.pgb_del = QtWidgets.QProgressBar(self)
|
||||
self.pgb_del.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||
self.vly_m.addWidget(self.pgb_del)
|
||||
|
||||
self.hly_bot = QtWidgets.QHBoxLayout()
|
||||
self.pbn_delete_selected = QtWidgets.QPushButton("删除所选", self)
|
||||
self.pbn_open = QtWidgets.QPushButton("打开", self)
|
||||
self.pbn_cancel = QtWidgets.QPushButton("取消", self)
|
||||
self.hly_bot.addWidget(self.pbn_delete_selected)
|
||||
self.hly_bot.addStretch(1)
|
||||
self.hly_bot.addWidget(self.pbn_open)
|
||||
self.hly_bot.addWidget(self.pbn_cancel)
|
||||
self.vly_m.addLayout(self.hly_bot)
|
||||
|
||||
self.profiles_model = ProfilesModel(profiles_data, ext_id, profiles, self)
|
||||
self.trv_profiles.setModel(self.profiles_model)
|
||||
|
||||
self.pbn_delete_selected.clicked.connect(self.on_pbn_delete_selected_clicked)
|
||||
self.pbn_open.clicked.connect(self.on_pbn_open_clicked)
|
||||
self.pbn_cancel.clicked.connect(self.reject)
|
||||
|
||||
def sizeHint(self):
|
||||
return QtCore.QSize(400, 360)
|
||||
|
||||
def on_pbn_open_clicked(self):
|
||||
us = QtCore.QSettings()
|
||||
exec_path = str(us.value(f"{self.browser}Exec", ""))
|
||||
if len(exec_path) == 0 or not os.path.exists(exec_path):
|
||||
QtWidgets.QMessageBox.critical(self, "错误", f"无法找到 {self.browser} 浏览器的执行路径")
|
||||
return
|
||||
|
||||
indexes = self.trv_profiles.selectedIndexes()
|
||||
cmd = rf'"{exec_path}" --profile-directory="{{0}}"'
|
||||
for idx in indexes:
|
||||
if idx.column() != 0:
|
||||
continue
|
||||
profile_id = self.profiles_model.data(idx, QtCore.Qt.ItemDataRole.DisplayRole)
|
||||
# setProgram 不行,不知道为什么,莫名其妙
|
||||
self.process.startCommand(cmd.format(profile_id))
|
||||
self.process.waitForFinished(10000)
|
||||
|
||||
def on_pbn_delete_selected_clicked(self):
|
||||
us = QtCore.QSettings()
|
||||
user_data_path = str(us.value(f"{self.browser}Data", ""))
|
||||
if self.is_compat:
|
||||
pref_name = "Preferences"
|
||||
else:
|
||||
pref_name = "Secure Preferences"
|
||||
|
||||
indexes = self.trv_profiles.selectedIndexes()
|
||||
total = len(indexes) // 2
|
||||
if accept_warning(self, True, "警告", f"确定要删除这 {total} 项吗?"):
|
||||
return
|
||||
|
||||
del_thd_mgr = DeleteThreadManager(total, self.pgb_del, self)
|
||||
|
||||
for idx in indexes:
|
||||
if idx.column() != 0:
|
||||
continue
|
||||
profile_id = self.profiles_model.data(idx, QtCore.Qt.ItemDataRole.DisplayRole)
|
||||
del_thd = DeleteThread(
|
||||
str(Path(user_data_path, profile_id)),
|
||||
pref_name,
|
||||
[self.lne_info.text()],
|
||||
self
|
||||
)
|
||||
del_thd_mgr.start(del_thd)
|
||||
9
daily_check.qrc
Normal file
9
daily_check.qrc
Normal file
@@ -0,0 +1,9 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>images/blank_128.png</file>
|
||||
<file>images/browsers/chrome_32.png</file>
|
||||
<file>images/browsers/edge_32.png</file>
|
||||
<file>images/browsers/brave_32.png</file>
|
||||
<file>images/dailycheck_128.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
617
daily_check_rc.py
Normal file
617
daily_check_rc.py
Normal file
@@ -0,0 +1,617 @@
|
||||
# Resource object code (Python 3)
|
||||
# Created by: object code
|
||||
# Created by: The Resource Compiler for Qt version 6.6.1
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PySide6 import QtCore
|
||||
|
||||
qt_resource_data = b"\
|
||||
\x00\x00\x01\xb8\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00\
|
||||
\x09pHYs\x00\x00\x0e\xc1\x00\x00\x0e\xc1\x01\xb8\x91\
|
||||
k\xed\x00\x00\x01MIDATx^\xed\xd21\x01\
|
||||
\x00 \x0c\xc0\xb0\x81\x7f\xcf\xe3\xc1E\x9a\xa7\x06zv\
|
||||
w\xe2\xba\xbfA5\x00\xae\x01p\x0d\x80k\x00\x5c\x03\
|
||||
\xe0\x1a\x00\xd7\x00\xb8\x06\xc05\x00\xae\x01p\x0d\x80k\
|
||||
\x00\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\x06\xc05\x00\xae\x01p\
|
||||
\x0d\x80k\x00\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\x06\xc05\x00\
|
||||
\xae\x01p\x0d\x80k\x00\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\x06\
|
||||
\xc05\x00\xae\x01p\x0d\x80k\x00\x5c\x03\xe0\x1a\x00\xd7\
|
||||
\x00\xb8\x06\xc05\x00\xae\x01p\x0d\x80k\x00\x5c\x03\xe0\
|
||||
\x1a\x00\xd7\x00\xb8\x06\xc05\x00\xae\x01p\x0d\x80k\x00\
|
||||
\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\x06\xc05\x00\xae\x01p\x0d\
|
||||
\x80k\x00\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\x06\xc05\x00\xae\
|
||||
\x01p\x0d\x80k\x00\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\x06\xc0\
|
||||
5\x00\xae\x01p\x0d\x80k\x00\x5c\x03\xe0\x1a\x00\xd7\x00\
|
||||
\xb8\x06\xc05\x00\xae\x01p\x0d\x80k\x00\x5c\x03\xe0\x1a\
|
||||
\x00\xd7\x00\xb8\x06\xc05\x00\xae\x01p\x0d\x80k\x00\x5c\
|
||||
\x03\xe0\x1a\x00\xd7\x00\xb8\x06\xc05\x00\xae\x01p\x0d\x80\
|
||||
k\x00\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\x06\xc05\x00\xae\x01\
|
||||
p\x0d\x80k\x00\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\x06\xc05\
|
||||
\x00\xae\x01p\x0d\x80k\x00\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\
|
||||
\x06\xc05\x00\xae\x01p\x0d\x80k\x00\x5c\x03\xe0\x1a\x80\
|
||||
6\xf3\x00\xedw\x03\xfd\xcd\xd11\xb4\x00\x00\x00\x00I\
|
||||
END\xaeB`\x82\
|
||||
\x00\x00\x0el\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x80\x00\x00\x00\x80\x08\x03\x00\x00\x00\xf4\xe0\x91\xf9\
|
||||
\x00\x00\x03\x00PLTEGpL\xd3\xb0\x22\xfb\xd1\
|
||||
*\xe5\xbf%\xd3\xc1'\xf9\xd1(\xdf\xba$\xe8\xca*\
|
||||
\xd7\xc4)\xd8\xc2'\xd8\xb4$\xd2\xaf#\xfa\xd1'\xfd\
|
||||
\xd4-\xf4\xcd&\xd1\xaf#\xcf\xad#\xd0\xae\x22\xf9\xcf\
|
||||
'\xfa\xd0(\xf6\xcf&\xca\xb0#\xcd\xab\x1f>\xb56\
|
||||
\xfa\xd1'\xfb\xd1'\xfb\xd2)\xfa\xd0&\xd3\xb0#\xd0\
|
||||
\xae&\xf8\xce%\xf9\xcf'\xf8\xcf&\xf5\xcb#O\xcc\
|
||||
?\xd0\xae\x22\xd5\xb2%\xd3\xb0&\xcf\xac!\xfb\xd1(\
|
||||
\xfb\xd1(\xfd\xd3+\xfb\xd2)\xfd\xd4-\xf7\xcd\x22\xf9\
|
||||
\xcf%A\xbe9\xd3\xb0$I\xca@O\xbf7\xfd\xd3\
|
||||
,\xf6\xcc \xf7\xce(\xfa\xd1'\xd1\xaf#\xd3\xb0%\
|
||||
\xcf\xad \xd3\xb0#\xd1\xaf'\xd3\xb0&@\xbc9\xfb\
|
||||
\xd1(\xf9\xcf$\xfd\xd3,\xf8\xce\x22\xda\xb7'\xd4\xb2\
|
||||
'\xd8\xb5&\xd3\xb1'\xd0\xae\x22\xcc\xaa\x1e\xca\xb3\x22\
|
||||
\xfe\xd4-\xf3\xca#\xfd\xd4,\xfd\xd3,U\xdeK\xe2\
|
||||
\xbd(\xfd\xd4-P\xd7F\xe0\xba%S\xdcHZ\xdc\
|
||||
J=\xb46D\xc4;=\xb75<\xb25\xf7\xcd!\
|
||||
L\xd6B\xef\xc6 T\xddJO\xdbF\xf6\xcc \xd9\
|
||||
\xb5$C\xc0;A\xbc:\xf3\xc9!\xf7\xcd\x22\xd8\xb5\
|
||||
&<\xb35W\xdfM\xd6\xb4*X\xdfN\xdd\xba*\
|
||||
\xf1\xc8$C\xc1;\xf7\xcd\x22V\xddJ\xb7\xbd(\xfd\
|
||||
\xd4-G\xc7>>\xb56V\xddKd\xdcF\xed\xc4\
|
||||
\x1f\xff\xcf'\xff\xcf'\xfb\xd1)\xfb\xd2*\xff\xff\xff\
|
||||
\xf9\xcf&\xfa\xd0'\xfa\xd1(\xfc\xd3,\xf7\xcd#\xfc\
|
||||
\xd3+\xfc\xd2+\xf7\xcd\x22\xf8\xce$\xf6\xcc!\xf5\xca\
|
||||
\x1e\xfa\xd0(\xf6\xcb\x1f\xf9\xcf%\xf9\xd0'T\xddJ\
|
||||
\xf5\xcb\x1f\xf4\xca\x1d\xfd\xd4-\xf4\xc9\x1c\xf6\xcc \xf8\
|
||||
\xcf%P\xdcGW\xdfNV\xdeLK\xd9AI\xd8\
|
||||
?\xf3\xc8\x1a\xf8\xce#S\xddIR\xdcHE\xd6<\
|
||||
U\xdeKP\xdbFF\xd7=G\xd7>D\xd6;O\
|
||||
\xdbEH\xd8?\xf4\xc9\x1dN\xdbEC\xd5:B\xd5\
|
||||
9\xfd\xd3,\xfd\xd4.\xf7\xcc\x22\xf7\xcc!L\xdaC\
|
||||
\xd1\xaf'\xfd\xd3-G\xd7=C\xd6:\xcb\xa9\x1eM\
|
||||
\xdaD\xfe\xd5.M\xdaCY\xe0OY\xdfO\xcf\xac\
|
||||
!@\xd47A\xd58\xdf\xba&\xf5\xcc&F\xca>\
|
||||
A\xca8\xfe\xd4.\xeb\xc4'\xda\xb6$\xd9\xb4\x1e=\
|
||||
\xb65\xd4\xb0\x1e\xfc\xfe\xfc\xe6\xc0(\xe9\xc1\x1d\xef\xc5\
|
||||
\x1cK\xd6BC\xd0:\xdf\xb8\x1f\xef\xc8$\xd4\xb1&\
|
||||
\xfa\xfc\xfa\xf0\xc9)N\xc9F\x8d\xd36H\xd1?\xe2\
|
||||
\xbb\x1eB\xc58\xe7\xce%\xed\xed\xed\x9c\xeb\x97z\xe4\
|
||||
s\xe2\xe6\xe2\xf7\xf7\xf7>\xc06\xc2\xf2\xbe\x9b\xd69\
|
||||
\xd3\xd0*Y\xc9R\xbe\xd8\xbd\xf4\xf4\xf4\xf1\xfc\xf1\xc8\
|
||||
\xd2.\xdd\xf8\xdby\xdbD`\xddI\xf6\xce)z\xcb\
|
||||
u\xae\xd4\xab\x88\xcd\x83\xfe\xd5/\x87\xe7\x81\xb4\xd31\
|
||||
\xdf\xcd%\x9c\xc3+\xcb\xdb\xcae\xc9^\xa1\xd1\x9e\xaf\
|
||||
\xef\xab\xbf\xd2/j\xdbDm\xe2ec\xdeZ\xcd\xc4\
|
||||
!\xd8\xe0\xd7q\xcbk\xb6\xef\xb2\x84\xe2}X\xd7P\
|
||||
\x98\xd0\x94[\xc16\x8b\xb6+l\xc97\x8e\xe7\x88o\
|
||||
\xb0.\xfb2\x02\xfb2\x02\x90\x0aZ\x1e\x00\x00\x01\x00\
|
||||
tRNS\x00!s\x17\x05\xcf\x0b\x07\x02\x01\x13n\
|
||||
!\xf3\x1b\xd6\xcc\x8bH6S-^\xb8\x10\xab\xb9\xa1\
|
||||
\x98\xf4h/@{\x0c\x81K\xb3R\x5c\xf0\xc0\x0e\xfa\
|
||||
\xf0\xb1Dd\x1f\x15\x83\xfb(\xfa\xc0vD4\xfa\xed\
|
||||
\x8a\xe0\xc7\xd8\xf6\x8d\xe3X\xa3\xaa\xe9@\xeab\xdd\xe6\
|
||||
\x93\xfc\x92(\xc6L\xcd\xc9vR\xd2\xe8\xaf\xa1\xea\xab\
|
||||
\xe5\xf3\xa5\x96\x90\xdd:\xec\xe5\xc4\xf5\xf9\xc2\xe8\x90d\
|
||||
\xfe\xdc\xda\xf1\xd7\xb8\xf7\xff\xff\xff\xff\xff\xff\xff\xff\xff\
|
||||
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
|
||||
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
|
||||
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
|
||||
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
|
||||
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
|
||||
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
|
||||
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
|
||||
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
|
||||
\xff\xffss\xaa\x1e4\xff\x00\x00\x0a\x1bIDAT\
|
||||
x\xda\xbd\x9byX\x14G\x1a\xc6\x9bc`\x86C\x06\
|
||||
\x04\xb9QNAP\xc1\x03T\xf0\xbeWQ\xe3\xb9f\
|
||||
\xe3\x15s'\xee\xf5<q\xdc\x85]\xc55\x82x\x22\
|
||||
F\xa2\x04c\xd4\xc4N2\x98\x80\x19f2\xc3\xc00\
|
||||
r\x88 ^A\xbc\xa2bL4\x9a\xf5\xcc\xbd\xbb\xcf\
|
||||
\xb3\xcd\x0c\xd3\xf4Q=\xf5\xf5\xd0\xf0\xfb\x8b\xae\x99\xfa\
|
||||
\xde\xb7\xab\xaa\xab\xbf\xaa\x1a\x08\xc2Q\xfa\x07\x8f\x9f\x91\
|
||||
\xf8\x0d\x85\xf7\xac\xf1!\x11r\xa2\xb7\xf1K<f\xa3\
|
||||
\xc3E\x88k/\xeb\xbb\xcf8\xc6\xc6\xb9\xb7\x1b\xe0\x0b\
|
||||
6\xdf\xc8z\xd9\xc0\x9c#\x5c\xfa\xf4\xae\x815<\x03\
|
||||
\x11\xbd\xaa?\x82\xa7\x7fdp\x8f\x89\xc9\x83\xf8#\xdc\
|
||||
9\x9f\x87\xb7{\xcf\xc8\xbb\x0e\xf3\xce\xf7\x9e\xc3\x0d\x9e\
|
||||
\xc07\x90\xef\xc7\xf9N\xdf`Y\xc2\xc0n\xeb{Y\
|
||||
\xa4\x8e\xc9\xbcX\xa5\x83\x12\x11\x06\xe6\xb0\xbe\xa2p\xb6\
|
||||
|'8\xa8{\xfa\x03\xbd\xad\xc1\x8fy\xfb3\x8b\xfd\
|
||||
\x11\xfa\xf9\xbfcY\x0c\xb6\x95\xf6\xed\x86\xfc\xa0\xd0\xfc\
|
||||
#\x9dq\x8e\xe4\x0fc\x8c\x84P\x94\x81\xfc\x11\x8cy\
|
||||
z<]\x9a\xe8\xacpT\xbf\xef\x9a\x8f\x18|\x91@\
|
||||
?\xe9\xf2\x19\x1f\xa1pf4\x1c\xb3<\xc4\xc5\xb1\xc1\
|
||||
\xef\x9cx\x98\xcd\xac\x016g\x87\x91$\xd8\xa6\xe99\
|
||||
B\x15EM\xf6!Y<l\xad9,\x0bI\xe2 \
|
||||
\xeb\xb8\x95e\x1d\xe6~4L\xfc\xdb\xd2\x19\xa1p8\
|
||||
\x7f\xb0eP\xcb\xd0\x06\xb2,\x03\xd5o\x06\xea\xa3a\
|
||||
\xa2\x0d\xbc\x82\xd6X\xd3\x9f \xfa\x08\xe8g\x85R\x1d\
|
||||
'\xd0:\xa2\xa7)\xc5<t\xa0\xc3\xde\xce\xfdC\x84\
|
||||
\x0c$\x0e\xec\x9b \xf0Q\xe3\x08\xb1-\x10\xd6\xfe\x0f\
|
||||
)\xc9\x14\xfb$(\x9e7I\xa9\xbf\xf6E\xf1O\xe1\
|
||||
\xa4\xb5\x12\x1a\xd0{\x8a\x7f\x0e'6\xfeS:^v\
|
||||
`.\x0a\xc8\x94N\xbf=\xd6\x91\xa9\xd0g\xadT\xfa\
|
||||
Y\xfa\xc9\x8e\x18Hk\x5c+\x15\xcb\xdc\x1c1\xd0'\
|
||||
S*\xfd\xf6)\x8e\xbd\x0d_\xdc,\x91\x01S\x8cc\
|
||||
\x06\x86\xea\xa5\xd1\xcfJwp\xd9\xe4\xf1\xb24\x06\x1a\
|
||||
':\x9a\x91\xc4\xb6\xbf)\x05\xa6\xe7\x1d50Y/\
|
||||
\x89\x81I\x0e\xe7dnIR\xe8\xaf\x8dt<+\x1d\
|
||||
,I\x0b\x80\xf3b\x85\xd7\x08\xaf>A\x83\x18Y\xf1\
|
||||
X\xa9[\xc0=(\xc8\xcbK\xe8\xbd0`\xbc\xadJ\
|
||||
|\xfc\xac\xb1\xe3e\xb2\x84\xf87\xa5A6N&\x1b\
|
||||
;6>~d\xe7u\x88\x172\x09\x1f\xf9wq\xb4\
|
||||
7\x9a25\x06\x8d&\xd3\xd4\xb8Yd\xd5\xb1\xa8\xf5\
|
||||
R\xb08u\x93\x81d\xa0\xd1\x8b\xf3\x80\xd8Oq\x8b\
|
||||
\x17Q\xbfBC\x89\x86\xa7\xfazN\x8e\x89\xe9\x97\x16\
|
||||
\xa6\x1cC\x92\x0d\xa6v\x11\x01\xc6\xf1\x0d\xf8\xff\x0b\xcc\
|
||||
:\x13I\xa6\x87\x8dr'\x14\x1dX\xdc\xc7M\x1dC\
|
||||
Y\xd8\x0c\x8f\xc1\xdfO\x09\x05\xd7ml 'y\xb2\
|
||||
\xdf\xaf\x94\x0f\x17\xdfp\xd2P\x01\x0e\xc2\xdbOq\x8f\
|
||||
\xff\x1b\x10=9&\x1a\xf9zw\x99B64B\xa3\
|
||||
\x84p\xa7\x00?\xb8\xbe\xcfh\xa1\x89$.\x9d\xac\x07\
|
||||
\x86\x19\xc9}\x0eB\xc1\xfa\xb1\x83\x84\xa72'\x1f\x12\
|
||||
\xda\x0639\x1b1\xc0\x1e\xa8'\xa7\xd8{\xb7+<\
|
||||
\x94\x0d\x15\xb0H\x9c=\xad\x01\x9bAT4\xc4b\x96\
|
||||
\xb9\x1e>\x86u\xa0P#Y\xe3H\xb1\x12f@\xa3\
|
||||
\xc4\xae1\x9d\xd2M\xb0X\xfe\xec\xd4\xa3b\x1d\x80\xfa\
|
||||
p'\xfc+-\x86\x04\xc5\xda\x1e\xc6\xaa\xe5k\x80\xd4\
|
||||
2\x80\x16XS4\x10\x03\x1a_v\xc3\x85\x93\xa6\xed\
|
||||
\xd8\x06P\x82\xf69<\xc2\xf17Sa 9\x0f\xf3\
|
||||
\xa8I$\xb6\x114\xc0\xec:\xda\x84\xd3\xd7\x93\xa4/\
|
||||
\xcf\xf7T\x92\xd4\xdb\xad\xb5\xc1\x07\x98\xdby,\xc3\xdc\
|
||||
\xbe\x86LG\xad\xd5&\xa7\x93\x9a\x8a\xb7\x84\x81/\xb1\
|
||||
\xa7\xd8\x0b\xf3\x96\xbe\x81\x8cE'E.\xb1d\x83^\
|
||||
\xb8\x22|\x93#\xc6N\x14\xea\xf6\xc3\x87\x0a5\xa5b\
|
||||
h\xb8\x9dF\x98\x07\xdf\xe0\xce\x14\xd4\xafo S\xed\
|
||||
=\xcbN\xa9dC\xfdv4\x22\xd67\xf3\x04Bd\
|
||||
S\xb7\x9ff\xffQ\x92\xa7\x8d\xa1\x1aa\x03\x0a\x11k\
|
||||
\xfc\x89\xc8\x00\x1b\xa8\xdbW\x06\xe0wF\x94\xa4\x06U\
|
||||
;\xbb?\xdc\xc0L\xa4~\x05\x95I@\x96\xa9\xae\xd1\
|
||||
\x06\xa4\x01\xf86\x8f\xdc\x0f\xdd\x00\xc2\x99\x04\x87Hd\
|
||||
}\x11\xbb\x1c\x03\x90\x01\x92<\xa0\xf5\xfd\x91\xf5\xdd\xbb\
|
||||
k`\x15t\x91\xda\x7f\x15\xb2\xbe\x88\xc3\x17t\x17l\
|
||||
x\x0dt\x0f\xf2\xc8\x09\xe8\xea\x22v{\x07\xa2#l\
|
||||
\x90\x01\x06\xb2\xd7\xb8=\x02\xf8\xc1\x0dD\x0a\xc5\x98\x10\
|
||||
\x89y\xa1*\x22&\x08\xd5\xdd#\xe2x:X0\xc8\
|
||||
\x9eq^vw\xe6B\xb2\x85\x11q4\xfa\x9c\x9d0\
|
||||
\x13\x22\x84\xc7\xe2\xc0$;\x15\xb3\x93\xa0{]\x8a\x11\
|
||||
\xf6\xc2d\x7f\x10\x22p\xce\xed\x12\x9cm\x1f\xf0 \x88\
|
||||
\xb4\x1f''\x09y\xa0\xea\xbf\x04\xa3\x9f\x1d\x0c\x9d\x08\
|
||||
W\xe1\x22\xe5\x04\xbb\xf0\xcf(\xd7c\x19\xee\x05\x9c\xc7\
|
||||
\xa8\xef\xe6\x9c\xb9\xf4\x15\xc5\xa5\xbb9\xe8XK8'\
|
||||
y\xee\xb2\xf5\x00Ba\x0d {\xd0v\xa3\xec\xb3N\
|
||||
\xcan\xb4\xdd9\x83\x88\x95\xc4n\x83\x08\x88\xfe\xfal\
|
||||
\xc0~W\xd4\xeb\xf3iq\x9a\x1b\x08\x0f\xac\xa7Z\xbe\
|
||||
\x14d`\x9b\x0c7\x97&\xcf~\xa9\x0cM\xdb]v\
|
||||
\xac\x1c\xf6\xc2dj\xce\x07 ^\xb3\x7f\xf7\xb3_\xda\
|
||||
)HY\xdb\x19f$}\x1a{c\xbc\xe6C\x183\
|
||||
\xed\xe8?\xf3\xc2N{\x94\xdd|\xc0\x08T\xc7\xce\x0c\
|
||||
=\x0c90\x86\xfb\x0b\x1e6\xcf\xde\x89\xa5\xa9\x9e\x0e\
|
||||
\xe4\xc3\xa9\x9e\x9a\x07u \xd0\x06\xc9\xf3\xf1\xfa;?\
|
||||
{x\xd7\x1a%\xaf>\x9a{8Q\x93\x03e%j\
|
||||
J\x9e\xfb\xc2.\x08e7/Y\x83h\xb8\xc9\xa9\x8b\
|
||||
\x01l g\x1c\xef\xad\xae\x98\xfb\xec. V\x07\x1f\
|
||||
\xfa\xf0n!5o\x1b\x98\xe1K9\xaf\x93d\xb0~\
|
||||
\x87\x03*B=omJx\xd6\xc0\x0dP#!t\
|
||||
@Wf\xa1\x88\xfa\xf3;\x22xxf\xdb\xb6\xd3\xfc\
|
||||
\xfc\xd8\xc9\xb0M\x1cKB#\xfa\xbaP\xafv\xd7>\
|
||||
\xd3\xff\xf2\x8e(n\xe4\xe5\xa1\x0eQ\x94\xa7\xf36\x89\
|
||||
'i\xc2\xa6Mwr\xc5\xb1\xab\xcd\xe0\x8b:\xa4+\
|
||||
\xaf\xaa\xb3`0TVVuP^\xf9\x1e\xc4\xc3\xdd\
|
||||
\x9b\xb9by\x03\xb5@S\x84\x95\xf3\xa8\x01\xe8\xe74\
|
||||
\x89\xd6\xcf\x9d\x8f\x9eKF\xa7EG{z\x0e\xed\x17\
|
||||
\x17\x173}z@\x80\x93\xd3\xe4\xbf\x02\x0c\x5cz\x1b\
|
||||
\xc6\xe3s\xe7m\x7f\xee\xcd}\x06\x92\xd9u\x1cZ\xe5\
|
||||
a\xd9x\x03$\x9f\xfb\xe8\x9a\xaa\xfa[\xda\xc1|`\
|
||||
r\xb5\x0ao\xe0\xd2^\x10\xe7\xaf\xa9T\x94\x03\xfaz\
|
||||
\x08\xec\xb7\x5cx\xfdMMp}\x95\xea\xe4O\x9d\xd7\
|
||||
o\xff\x01d`%\xde@}.H\xff\xa4\xca\xca\xc9\
|
||||
\xf3\xb6\xa2\x14Hr\xf7\xdcF,\x0fv\x00\xf8\xd6\xa6\
|
||||
O9xd)\xd9\xbb\xe3u\xc8\x1a\x17\xaf\xbf\xb1I\
|
||||
\x9c\xbe\xaa\xfa\xa7\xceBH\x1f,\x05\x18x\xb8\x0f\xcb\
|
||||
\x7f\xab\x19\xfa\xf7~\xb6\x16\x96<\x8b\xdf,pK\xc2\
|
||||
\xeb\x9f\xc1\xeb\x9fc\xe8\x9f\xbcw\xb9\xd4V>\x17\xbf\
|
||||
\xbe\xc0\xebo\xfd\xaa\x84\xc1\xbeG\xbbJx\x9cS1\
|
||||
\xf4\xaf\x5c>A\x7f\x80\x9f\x8b\x06o\xc1s\x87!U\
|
||||
v]u\xed<X\xbfd!N?h8^\xbf\xb0\
|
||||
\xed M\xc9\xf5\x0e\x8ds\x07\x99\x94\xfe\x9b\xa1\xff\xe5\
|
||||
\x95S'\x18\x9f\xcd\xc6\x19\x88\x004\xc0\x96\xa6R\x1b\
|
||||
\xfb.\xaa,\x9d}q/]Tz\x90\xab_\xca\x00\
|
||||
\xfb\x18\xbc\x22\xca\x00\xadU}\xfd1\xad\xf1#S\xff\
|
||||
k\xb6~\xe9\xefq?czO\x80\x9a:s\xd7E\
|
||||
\xd3\xa7\x9d0\xfa\xfa\xdaykQ\xc9E\x8e\xfe\xa7,\
|
||||
p\x06\x22\x05\xf4\xeb\xd4\xea*\xfa\xa2\xb0\xe9\xa8\x85\x82\
|
||||
\xc7\x0c-U\xf5\xb9\x8e\xb2}L\xfd\xab\x94\xfeQ6\
|
||||
8\x03\xf3\xb6\xa20W\xa9\x95\xbe\xd3\xcc\xf4u\xa7\x81\
|
||||
\xa3?_`:\xa0\x06\xc2\xd1\x1d,\xfd\xefy\xfaG\
|
||||
\x97c\x0c(\xcd\x08\xfd\x9aru\xb4\x9c\x98~\xdav\
|
||||
\xbd\xf1\xbb\x02+'~xz\x92\xe9\xe0\xfa\x8f\xd79\
|
||||
\xfa\x05\x1c\x0ef\xe0~SZ^\xc3\x95/\xacSO\
|
||||
\x1b\xd5\x91\xaa\xa4\xd3Eo\xd8\xe25_\xberU%\
|
||||
\xc0\xd5_\xb4<\xfd\x82\x02\xdc<\xe04M]W\xc8\
|
||||
n\xfeJ\xb5\xd2\xba\xa4\x0d\xb3}\xb0\xe5\x09\x1d\xb9\xf9\
|
||||
\xd4/g\xd1\xfag\x91\xfa\x05\xd8\x94\xc4I\xa9\xae2\
|
||||
\xb3\x9b?\xacs\x7f\x22\x8en\x1cs3\x1d\xb0X{\
|
||||
\xebi5J\xff\xb6v7B\xbf\x19\x9b\x10\xc8]}\
|
||||
\xd5j\xba\xb7\xb7\x9eV\x87\xf7\xa3_S\x95t\xf1\x7f\
|
||||
v\xdb8\xb1[{\xf9\xca\x97H}\x14\x7f\x84dD\
|
||||
\xa3\xa6\xa9+\xcd\x85\x16*\xd5>\x8c\x5c>\xb5\xd0\xc6\
|
||||
w\xcc\xa0\xcd\xa7\xbe?\x0b\xd4\xdf\xbd\x02v\x04\x19\xab\
|
||||
.\xaf\xa1dj\xaa\xd4S\xddX\x0bI\x9b\x81'\xcd\
|
||||
,\x07\xda[\x17X\xddp\xf6\x96\xf6\x00R\xbf\x19\x96\
|
||||
\x95\x12\x0aOj\xde\xa9\xacR\xab\xd9\x87\x96Nut\
|
||||
\x13\xfcz\x80I\xb1\xf6\xfe=F7\x5c\xb8\xa5->\
|
||||
\x80\xe4U\xf0o\x1c\x03|\xd4j\xf54\xce/\x02\x15\
|
||||
>]}\xc0Q\xa8et\xc3\x85\x1f\x84\xf4\x0fd\x10\
|
||||
`\x5cc<\xe3x\xc7E\xbef\x9b\x81\x1am1\x9b\
|
||||
f\xed\xed\xcen\xe8\xd0\x17@\x9b,\xe6g\xe6\xa8\xe1\
|
||||
y\x9an\x82\xdf\x8a8\xc1\xdf\xb7vC\xf5\x85\xfb\x82\
|
||||
\xfaE\xcb\x89n\xe2\xba\x8c6p\xb7\xe5c\x0e\xfbk\
|
||||
O}}\xf6\xea\xd3\xfb\xda\x8f\x85h\x99\xdb]\x03\xc4\
|
||||
Ts\xd7(\xa8\xdd\xcf\xa1\xb8V{\xeb\xf6e^\xb1\
|
||||
\x8d\xa2\xe6\x15\xdd\xd6'\xfau=\x07\xe6_\x8b\xf8\x1a\
|
||||
:\x9d\xa0\xfe\xfe\xa2W\xa3\xbao\xc0\x83\x9e\xa2\x0a\xcd\
|
||||
OZ\x8ax\x14\x17\x09\xd32\x84\x90\x00j\x9e.\xaf\
|
||||
\xaa\xaa\xb2l\xa6\xfc\xd6R$\x02]\x86\x14\xfa\x0a\xd7\
|
||||
hu\x17\xff\xd3\xbd\x0bF\x17(\xd5?\xc8\xba\x8d\x1e\
|
||||
\x15\xd7\x8f\x22nz@J \xd8\x81nA\x14!=\
|
||||
\xf2\xa8@\xdd' t\x0bR\x88\x1e!*\xb0\xf5\xd0\
|
||||
\xfbX\x0e\xb5\x04F\x11=\x84<\xc3X\x8b\xd3\xafm\
|
||||
]\xd1\x93\xff\x9e\xbchu\xcb\xe7\x87\xec\xd1\xf2\xa7E\
|
||||
D\x8f\x92\xb2\xdc\xa8\x13\x96\xd7\x19\x03\x93\x89\x9ef\xc8\
|
||||
\x02\xa3\xees$:\xe3\xe2!Do0$\xd0\xd8\xaa\
|
||||
;\xce\x16?\xaek5\x06.R\x10\xbdD\xf2\xc2\x05\
|
||||
Fck\x8b\xae\xf68E\xad\xae\xa5\xd5h\x5c\xbc0\
|
||||
\x99\xe8UR\x16e\x04.^m\xa4X\xbd80c\
|
||||
Q\x0aa\xe7\xee\xff\x0fO2\xbfP\x8b\xfe4z\x00\
|
||||
\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x05~\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00 \x00\x00\x00 \x08\x03\x00\x00\x00D\xa4\x8a\xc6\
|
||||
\x00\x00\x00\x09pHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\
|
||||
\x01\x95+\x0e\x1b\x00\x00\x02\xbePLTEGpL\
|
||||
\xd7`*\x17\x0a\x04\xa0G\x1fO#\x0f\x04\x01\x00\xf7\
|
||||
q4\xf6n0\xdfd,\x00\x00\x00M\x22\x0f\x14\x09\
|
||||
\x04\x02\x01\x00\x9eG\x1f\xd8a*\xddc+-\x14\x08\
|
||||
Q$\x10\xb5Q#\x22\x0f\x06\x06\x02\x01\x17\x0a\x04\xa1\
|
||||
H\x1f\xf7}E\xdbb+\xf6n0\xf5n0N#\
|
||||
\x0f\xafN\x22\x15\x09\x04_*\x12\x02\x01\x00\xa9L!\
|
||||
\xe4f-X'\x11}8\x182\x16\x09&\x11\x07\xe0\
|
||||
d,\x9fG\x1f\x09\x03\x01\xe3f-\x0c\x05\x02b,\
|
||||
\x13\xb8R$m1\x15Q$\x10b+\x13z7\x18\
|
||||
\x04\x01\x00W'\x11\xf8}E\xf4\x85S\xf7\x86R\xa1\
|
||||
H\x1f\xa7K!\xbeiB \x0e\x06)\x12\x08\xd5u\
|
||||
I\xd3_)\xd5`*W'\x11\xcdrH\xc6mD\
|
||||
\xf2\x82O\xf7~G\xa0G\x1f\xf7x>\xb6e@\xcc\
|
||||
\x5c(\x98D\x1e\xf8\x7fHK!\x0e\xe6|K\x97R\
|
||||
4\xf7w=\xcf])\xf6\x83O\xf7|C\xf7v;\
|
||||
\xf8\x80I\xee\x82Q\xf7|D\xe8\x7fO\xf7\x7fG\xd0\
|
||||
tI\xe4~O\xe8h.\xddxI\xf7u:\xdf{\
|
||||
N\xe9i.\xf7y@\xf7y@\xf7o1\xff\xff\xff\
|
||||
\xf8}E\xf6n0\xf5n0\xfe\xfe\xfe\xfe\xf0\xea\xea\
|
||||
i.\xf7p2\xfa\xf0\xec\xfc\xf6\xf3\xf7x>\xfe\xf3\
|
||||
\xee\xe3{K\xdfg0\xf8\x8d\x5c\xfb\xbd\xa1\xf6\x7fI\
|
||||
\xfc\xce\xb9\xf8\xeb\xe5\xfe\xfc\xfb\xf4m0\xe6\x83V\xe3\
|
||||
\x81T\xe3\x81U\xfb\xf5\xf2\xf9\x9cr\xe2\x89`\xfb\xf5\
|
||||
\xf3\xfb\xc9\xb2\xf1\xa8\x86\xefm1\xf0\xd4\xc7\xfc\xf5\xf1\
|
||||
\xe5r>\xfd\xf9\xf8\xf9\xf0\xeb\xe6\x82U\xe5\x90i\xfc\
|
||||
\xd8\xc8\xf6o1\xe5r=\xf6n1\xf1\xd6\xc9\xf9\x9a\
|
||||
n\xfd\xfa\xf8\xf1\xa5\x82\xe4\x91j\xf0k/\xe2\x89a\
|
||||
\xf0l1\xfb\xc7\xaf\xe9\xba\xa4\xf0l/\xe3\x97t\xe2\
|
||||
|M\xfc\xd6\xc5\xe4g/\xf7\xe8\xe1\xf8\xeb\xe6\xf9\xc2\
|
||||
\xa8\xe2}N\xf8\x81K\xe4\x9ay\xf7v<\xf8\x85Q\
|
||||
\xe9\xb8\xa2\xe4g.\xfe\xfa\xf8\xf7v;\xfe\xf7\xf4\xfc\
|
||||
\xf7\xf4\xf8\xbd\xa3\xf7\xe7\xe1\xfe\xfa\xf9\xe4\xaa\x8f\xf8\xec\
|
||||
\xe6\xfa\xae\x8c\xf7o2\xe5\xac\x92\xfe\xf9\xf7\xf7{C\
|
||||
\xfc\xcd\xb8\xfa\xa6\x80\xfa\xb7\x99\xfc\xd2\xbf\xf7zB\xf9\
|
||||
\xa3|\xfd\xea\xe1\xf5\xe3\xdb\xfd\xec\xe3\xfc\xd7\xc6\xfd\xdd\
|
||||
\xce\xfa\xaf\x8d\xfe\xf2\xed\xfc\xd9\xc9\xfa\xae\x8b\xe1\x96s\
|
||||
\xe3\xa9\x8e\xfe\xf2\xec\xdfxH\xfd\xfa\xf9\xe1\x97t\xdf\
|
||||
vF\xf6\xe7\xdf\xf9\xa4}\xe3\xa8\x8d\xfa\xf1\xed\xfe\xee\
|
||||
\xe7\xe2\x8bc\xfa\xb5\x96\xfe\xfc\xfc\xe2\x8ce\xfe\xed\xe6\
|
||||
\xfd\xfb\xfa\xf2\xad\x8d\xfd\xdf\xd1\xfe\xf8\xf6\xfe\xef\xe9\xe2\
|
||||
\xa5\x8a\xed\x95l\xf1\x82O\xf4\xc2\xab\xf9\x95g\xf8\x8c\
|
||||
Z\xed\x97p\xef\x98q\xe2\xa3\x87\xf8\x8c[\xe0\x89b\
|
||||
\xfd\xe0\xd2\xf1\x81N\xf2\xac\x8c\xf9\x96i\xf4\xc3\xad\xfc\
|
||||
\xcf\xba\xef\x99q\xfe\xf9\xf6\xe0\x89a\x19\x07\xe4\xa6\x00\
|
||||
\x00\x00_tRNS\x00\xe1$\xb1c\x08\xfc\xfe\xe9\
|
||||
\x12a#\x07\xaf\xe2\xe2\x03d\xa0\x02\x05%\xb2\xfe\xe1\
|
||||
\xfd\xfdb\x94$D\x08\x93\xebC\x89\x06\x05\xea\xb0\x02\
|
||||
\xea\x028\xa2CeB\x87\x056\xfeY[\x88\x89\x11\
|
||||
\x01\x01\x14\xd7\xd9-;\x13\x5c\xcc~\xe3\x10\xce|\xcf\
|
||||
+2\x01\xe4\xcf^\xc5\xf6\xcd9\xc37\xca<.\xe0\
|
||||
1\xf7-\xdf\xe8\xe7\xfe\x08\x05\xdf\x00\x00\x01\xfbID\
|
||||
AT8\xcbc`\xa0\x220\x11O\x84\x02cSl\
|
||||
\xf2!\x01\xf1p\x10\x15\x84.\xeb\x1a\x16\x1d\x17\x8f\x04\
|
||||
b\xc3\xed-\x90\xe5\x1d\x02\x97d\xc5\xa3\x80\xac\xf9\x1e\
|
||||
\xd6H\x0aB\xe3\xb1\x00'$\x051\xf1\xf1[\xb7\xef\
|
||||
\xdb\x9f\xb7mcvv\xe1\x89\xbc=\xbbwn\x88\x8f\
|
||||
\x8f@\xc8G\xb2\xc5\xc7oI@\x01\xbb\xd6\xc6\xb3y\
|
||||
\xc1\x15\x04\xc7\xc7o\x02\x0a\x16\xa7@e\x8f\x00\x19k\
|
||||
\xe2\xe3\xfd\xbd\xa1\xf2\x8e\xe9\xf1s\x97&\x1c?\x9a{\
|
||||
\xecdQBB\xd1\xc1\xc3\xb9\x07v$,^\x10\x9f\
|
||||
\xee\x07U\xe0\x12\x1f?\x1d\xa4\xf1P~\xc1\xe3\x84\x84\
|
||||
K\x05\xf9{A\xbc\xb6\xf8x\x1f\xa8\x02\xb7\xf8\xf8\x06\
|
||||
\x88\xd9\x0f\x0b\x13\x12\x9e]\x86\xb03\xdb\xe3}\xa1\x0a\
|
||||
\xdc\xe3\xe3[\x9b\xc0\x82O\xaf&$\xa4\xde\x043{\
|
||||
\xfa\xe2\xe3\x9d\xa1\x0a<\xd9\xe3\x93\xab*@\xa2wA\
|
||||
\xc4s\x10Q=!9\x9e\xdd\x0e\xaa\xc0\xd2&>>\
|
||||
\xa9\xac9!!\xe5\xc6\x83{\xd7^>:\x97\x90\x90\
|
||||
Q\x9e\x14\x1fok\x05\xf3\xa6\x190\xdc\x92Z\x80\xfa\
|
||||
R\xef\xc7\xc7\xdfN\x052J\x81\xf2\xf1\xe6\xf0p0\
|
||||
\x02\x85l7\xc8\xe4\x17On]\x01\xd1\x95 \x11m\
|
||||
\xb8\x02a\x09 w\x12H\xa2\xf8\xce\xf5\x8b \xba\x06\
|
||||
(\xc0/\x80\x08k\x05 \xbf\x17(~\xea|I\xc9\
|
||||
\x99\x0c \xa3\x16( \x83\x14Y\xfa@\xfe\xc4\x85\xab\
|
||||
\xd7\xe5\x9c\xbdpzs\xce\xaaY\x93\xeb\x80\x02\xbaH\
|
||||
\x0aT5\x81\x02S\x16-_\x0f2~\xe5\xec\x19\xfd\
|
||||
@\xae\xa2\x0ar\x8a\x11\x029*i\xde2p\x10N\
|
||||
\x05y!^\x07%\xc9)I\x81\xc4\x92\xe7\xacHH\
|
||||
\x989-\x19\xc4\x96\x94CQ\xa0a\xc0\x0e6\xa3\xa3\
|
||||
\xbe\xab\xb1\x13\xc4bW\xd6BM\xb5\x86,\x1c\xe0t\
|
||||
\x96\x96\x06\xa6\xd4\xf4D0\x92\xbd\xac:;,5\xb2\
|
||||
\x8b\x89b\xcb\x18\xf2\xd2\x10C\xe29\x04Yq\xe4-\
|
||||
n^\xa0!\xec\xccL\xb8s\x1f\x0f\x17#\x1f\x0b+\
|
||||
\xde\x0c\xca\xc9Ij\x96\x06\x00\xfd\x98\xe6y\x07\xbeQ\
|
||||
(\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x05}\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00 \x00\x00\x00 \x08\x03\x00\x00\x00D\xa4\x8a\xc6\
|
||||
\x00\x00\x02\xe2PLTEGpL\xfc\xceI\xfc\xce\
|
||||
I\xec\xc1D\xdcJ=\xdcJ=\x0f\x93S\xfc\xceI\
|
||||
\xfc\xceI\xfc\xceI\x9e5,\xdbI<\x0f\x98V\xf4\
|
||||
\xc7F\x0cvC\x96{+\xdcJ=\x0buB\xf6\xc9\
|
||||
G\x10\x9dY\xdbI<\xcf\xa9<\xdbI<\x10\x9dY\
|
||||
\xdcJ=\x9f\x82.\x0ac8\x86-%\x9e5,\x10\
|
||||
\x9dY\xbc\x996\xdcJ=\x0f\x99V\xbd\x9b7\x10\x9d\
|
||||
Y\x0d\x81I\x0f\x98V\x10\x9dY\xb7=2\xdcJ=\
|
||||
\x10\x9dY\xb7=2\xf5\xc8G\x09]5\xdbJ<\xdc\
|
||||
J=\xb6=2\xdbI<\xaa\x840\xf0\xa0D\xdaJ\
|
||||
<\xb6=2\x0bk=\x10\x9dY\xdcJ=\xc3A6\
|
||||
\x5c9(\xdcJ=\x10\x9dY\xc3A6\xdcJ=\x18\
|
||||
\x98W\x10\x9dY\xdcJ=\xdcJ=\xf1\xc5E\x0f\x9b\
|
||||
W\xf9\xcbH\x10\x9dY\x9d4+\x0ac8\xdcJ=\
|
||||
\xd9I<\xc3A6\xd9I<\xc3A6\x9d4+\x10\
|
||||
\x9dY\xfc\xceI\x0d\x82J\x0e\x91R\x0d\x80H\x0e\x90\
|
||||
R\x0e\x95T\x0ah;\x8f\xcc\xae\xeb\xed\xee\xfc\xceI\
|
||||
4\xa1hE~L\x13\x9bX\xa5\xa2\x8c}gC\xa0\
|
||||
\x83.`rG\xa8E3\xbdA3\x1d\x96V\xe8\xbd\
|
||||
C\xd1\xab<\xa7\x880\xcd\xa8;\xea\xbfC\x0f\x9cX\
|
||||
\x0f\x9cX\x94y+\x09\x5c4\xbf\x9c7\xdc\xb4?\x0c\
|
||||
wC\x10\x87L\x9a\xa6D\xd9\xb1?\xdcJ=F\x87\
|
||||
\xf3\xfc\xceI\x10\x9dY\xfb\xcdH\xc7\xd7\xf1\xe9\xb5\xb0\
|
||||
\xddTH\xde\xe5\xf1F\x87\xf2\x0e\x94T\x0f\x9bXG\
|
||||
\x85P\xed\x96?\xeb\x94<\xb9WA\xee\x9aB\xdbI\
|
||||
<\xf6\xde\x97\xf9\xc9D\xdaH;\xf6\xde\x99M\x8b\xf2\
|
||||
\x15\x9e\x5c\x9b]A\xb4\xda\xc8\xdfcX_\x96\xf2\xc1\
|
||||
\xd3\xf1\xf6\xc2>\xc0\xd2\xf1\xcf\xdc\xf1\xf8\xd9\x81\xef\xef\
|
||||
\xf0\xea\x91:\xe5\x96\x8f\xfb\xd0S\xf8\xd8|\x84\xc8\xa7\
|
||||
\x16\x9f]\xd9G:\xcc\xda\xf1/\x8eS\xe9\x908\xe8\
|
||||
\xae\xa9\xaa\xc5\xf1\xe8\x8e7\xf2\xba7\xe8\xb0\xa7`\x97\
|
||||
\xf2\xcdA5\xe1\xe7\xf1\xf1\xef\xea\xfb\xcfO\xe6\x91I\
|
||||
\xe7\x8b4\xad\xc6\xf1\xc2\xd4\xf1\xf0\xef\xed\xef\xd5\x9b\xee\
|
||||
\xb45\xe8\xa5\x86\xd8\xe2\xf1\xd0\xdd\xf1J\x89\xf2\xd8F\
|
||||
:\xdb\xe3\xf1\xf0\xb73\xf4\xbf>\xe7\x8c5U\x90\xf2\
|
||||
\xa1\xbf\xf1\xe6\x8a3\xed\xd2\xd0\xf6\xe0\x9f\xef\xb41\xf8\
|
||||
\xc7Bn\xa0\xf2x\xa6\xf2\xf0\xe0\xbd\xc66-\xd7E\
|
||||
9\xcc<1\xde\xa6\xa2[\x94\xf2\xd9\x8b\x86\xbf\xd2\xf1\
|
||||
G\x88\xf2h\x9c\xf2\xe7\xed\xea\xa0\xbf\xf1\xed\xee\xf0\x1e\
|
||||
\xa2b\xf1\xee\xe5\xd9^T\xdd\xa3\x9f\xc6\xd6\xf1\xc99\
|
||||
/\x91\xb4M\x1c\x95Q\x0e\x95T[\xb8\x8b\xe8\xc9I\
|
||||
\x15\x8cSy\xc3\x9f\x0f\x98V\x10\x8cN\xe1\xe9\xe5\xb7\
|
||||
\xcd\xf1{\xc5\xa1\xd2\xdd\xf1\x0d\x8bN\xdf\xe6\xf1L\x9b\
|
||||
L\xf2\xdc\x92Z\x93\xf2p\xb4\x93o\xa9M\x0f\x9cX\
|
||||
R\xa8~\xd2\xc2J\xb4\xcb\xf1~\xa9\xf2\xda\xe3\xf1\xb4\
|
||||
\xb8J\x0e\x8eP\xf7\xcdJ\xbf\xd3\xb3y\xa6\xf2\xf6\xcc\
|
||||
I1\x9fS\xb3\xbdMN\xa9TT\xbb$\xa6\x00\x00\
|
||||
\x00qtRNS\x00\xb4\xf6l\xde||\xa5\xcc\xdb\
|
||||
\x09aZQ\x11\x04\xbe\x10Q\xe6e\x14j\xd6\xe6\x04\
|
||||
\x09\x02\x0c\xd5\x0b\xeaa\x0b\xb0\x1be\xb4\x17\xb4\xe1\x11\
|
||||
U\x06\xbe\xc4\x17k\x06\xe1Z\x10\x0c\xea\xd6\x22\x02\xd5\
|
||||
\xc4\x1b\xb0k\xfc\xc9\xfcUk[\xc9\x11\x11\xfdkH\
|
||||
jE\x14\xfd\xb9HkEj[\x14\xfe\xfe\xf8\xfd\xfe\
|
||||
\xfe\xfe\xfe\x07\xfe\xfe\xfe\xfeZ8\x096[\xb9\xf8\x07\
|
||||
\x096l8\x5cmZFt#+\x00\x00\x01\xd9I\
|
||||
DAT8\xcbc` \x05\xf8xz\xb0\xb2zy\
|
||||
\xbbb\x97\x95\xd1\xb7w/\x04\x03\x07]\x11.\x0ci\
|
||||
i1\xf9B$ \xc1\x8d&\xaf&P\x88\x06\xd45\
|
||||
\x91\xe5\xcdY\x0a1\x80\x995B^\x0f\x8b|a\xa1\
|
||||
\xa51L\xdeB\xa7\x10+\xb0\x81)\xb0mh\xc2\xae\
|
||||
\xc2\x08\x22obZ\xdb\x0a\x15\xe9\x9e2w\xe1\xba\xe5\
|
||||
;Wl\x9b?gr}]\xa3\xa1\x01X\x81SI\
|
||||
I\xe7L\x90tE\xf9\xa4\xb2y+7\xac^\xb3v\
|
||||
\xcf\x96\x05}-\xa5\xa5\xc5\xce`\x05.%%\xb3\x17\
|
||||
o\x02\xca\xef*\xeb)\x02\x82}\xeb\x0fl\xdd[Z\
|
||||
\x0c\x02~ \xf9\x10\xdf\x92\x92\x92\x98\xc3\x87\x0a\xcb/\
|
||||
n,\x02\x83\xed\x9bw\x17C@x\x0aP\x81?P\
|
||||
\xbe$1\xe1\xd2\xf93G\x8b\xa0`\xff\xd2\xa9P\x15\
|
||||
\xc9@\x05\x01 \x05%q\x07\x8f\x1d\x87\xc9\x17UU\
|
||||
N\x84*H\x03*`\x03+\x88\x8a?r\x1a\xae\xa0\
|
||||
\xa8\xbf\x0d\xaa\x80\x19\xae\xa0$\xf2\xc2\x0e\x84\x82\xdef\
|
||||
$\x05A\x10\x05%\xd1\xab\xe0\xf2'\x96L\x83*H\
|
||||
\x02*\x08\x84*\x88\x08[\x04S\xf0\xfa\xdc2\xa8\x82\
|
||||
T\xa0\x027;\xa8\x8a\xd0Y\xed\x10\xf9'\x0f\x1fC\
|
||||
\xe5\x99bA\x01a\x05U0\xa3k\xc2I\x90\x17>\
|
||||
V~x\x0fU\xc0\x08\x0eI\x15\xa8\x82\x92\x8e\xe9\xa7\
|
||||
\xee\xbf\xba\xfb\xe6\xce\xd3\xb7P\xf9b-\xb0\x02)a\
|
||||
\x98\x8a\x92\xb3\xd7\xee\xdd|q\xe3\x11L\x9eS\x12\x12\
|
||||
\x9d\x0a%H\xe0\xd6\x83\xdb/a\x0a\x84`\x09B\x15\
|
||||
I\xc1\xd5w\xcf`\xf2\xec\xf0$\xc7'\x8ePP}\
|
||||
\xe5:T\x9eC\x11\x91(\x95ea\xf2\xcf\xab/\xc3\
|
||||
\xe4E\x91\x93\xb5\xa0\x12TA\xcd\xe7OP\xf3\xe5\xd0\
|
||||
r\x06\x8f\x06D\xc1\x17\x88\xfby1\xb3\x9evpz\
|
||||
FI\xc9WP\xf81:\xf2c\xcf\x9eY\xb9y\xf9\
|
||||
9\x05\xd9\x99$\xe5x\x00\xfe\xa1E\xc6\x0b}\xa2(\
|
||||
\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x06v\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00 \x00\x00\x00 \x08\x03\x00\x00\x00D\xa4\x8a\xc6\
|
||||
\x00\x00\x00\x09pHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\
|
||||
\x01\x95+\x0e\x1b\x00\x00\x03\x00PLTEGpL\
|
||||
C\x96\xbbs\xdeB'W\x9e#[\xa70\x81\xd51\
|
||||
\x84\xdb!U\x9f9tr%H\x8c)E\x8c*G\
|
||||
\x92`\xbb>'H\x8f.~\xda(E\x87f\xc5D\
|
||||
1\x83\xdb5\x83\xbcP\xb1\xe20WuV\xc0\xf03\
|
||||
}\xb0 J\x8a N\x94s\xdeBS\xa4Rs\xde\
|
||||
C/\x80\xd2,z\xdar\xdaD,E\x901\x84\xd8\
|
||||
\x22C\x86o\xd7K+s\xcfX\xc2\xe4,x\xdaY\
|
||||
\xb9\x95h\xce{h\xd2\x83d\xcd\x97g\xd0\x8b-y\
|
||||
\xc9Z\xbe\xad#O\x96'L\x95P\xb3\xe4$K\x92\
|
||||
p\xdaU_\xc8\xbeZ\xb0NU\xa48e\xc39$\
|
||||
O\x99d\xcd\x9e,y\xcd0\x81\xdbq\xdaG\x22Q\
|
||||
\x9at\xdeAt\xdfBq\xdbO^\xbaZo\xd9Z\
|
||||
`\xc2yk\xd4v'K\x92b\xcb\xab-y\xd1V\
|
||||
\xbf\xf3(P\x9f!J\x8a)>~t\xdeA(G\
|
||||
\x8bV\xbf\xf2'I\x8c+P\x9b*r\xc8\x22J\x8c\
|
||||
)H\x91#Q\x9d)H\x91/}\xd1'g\xa6&\
|
||||
d\xb4/|\xd1(i\xb8a\xc5~-M\x9f&c\
|
||||
\xb1,v\xce0J\xa26\x85\xba&U\xa2^\xc7\xc4\
|
||||
!\x5c\xa2V\xbf\xf1?\x90\xa1c\xca\x8e+t\xc1+\
|
||||
u\xc1\x5c\xc3\xb9E\x9b\xafV\xbf\xef^\xc3\x9c.|\
|
||||
\xdaF\xa1\xcb1j\xb7=\x90\xba-v\xb8V\xbf\xf2\
|
||||
K\xa6\xba<\x8e\xc1m\xd6jU\xbb\xdcK\xa8\xd3W\
|
||||
\xc0\xed.w\xbaX\xc1\xe71}\xbb,x\xd4W\xc0\
|
||||
\xeb>\x93\xc6V\xbc\xe0Q\xb4\xd9 P\x960{\xba\
|
||||
T\xb4\xbc$a\xa0n\xd7cY\xc1\xe3%h\xbaS\
|
||||
\xb8\xd9[\xc4\xd47\x87\xbfq\xdbM-/qp\xda\
|
||||
ST\xbb\xe9Z\xc3\xd83\x86\xdaW\xc0\xecW\xc0\xe9\
|
||||
Y\xc2\xdcP\xae\xc3L\xab\xcc;\x8e\xc00~\xc74\
|
||||
\x82\xbf,y\xda%Z\xa9(m\xc4!S\x9a'g\
|
||||
\xbe'h\xbe+x\xda*r\xd1+x\xda+w\xd9\
|
||||
2\x86\xdb3\x88\xdb0\x83\xdb3\x87\xdb\x1fK\x8c2\
|
||||
\x85\xdb\x1fN\x93/\x80\xdb\x1fQ\x99t\xdfA L\
|
||||
\x8f M\x91\x1fI\x8a\x1fO\x95q\xdbPX\xc1\xe5\
|
||||
J\x8b1\x84\xdb\x1fP\x97.\x7f\xda R\x9an\
|
||||
\xd8dq\xdcLo\xd9\x5cp\xdaV-|\xdar\xdc\
|
||||
J\x1fK\x8dY\xc2\xe1Y\xc3\xddk\xd5tl\xd6n\
|
||||
g\xd1\x8aa\xcb\xb0b\xcc\xabd\xce\x9d_\xc9\xb8i\
|
||||
\xd3\x82h\xd2\x86m\xd6jc\xcd\xa5`\xca\xb4s\xdd\
|
||||
Cp\xd9Y3\x88\xdap\xdaSs\xddFX\xc1\xe9\
|
||||
/}\xc8,z\xda3\x87\xdak\xd5rj\xd3|j\
|
||||
\xd4xi\xd3~W\xc0\xeff\xd0\x91g\xd0\x8e_\xc8\
|
||||
\xbcf\xcf\x94m\xd7ge\xcf\x96c\xcd\xa1s\xdcG\
|
||||
^\xc7\xc1n\xd8an\xd8_W\xc0\xea]\xc7\xc7\x5c\
|
||||
\xc6\xcc]\xc6\xc9W\xc0\xec\x5c\xc5\xd1\x5c\xc5\xce[\xc4\
|
||||
\xd53\x86\xd5Z\xc3\xda[\xc4\xd3/~\xceZ\xc4\xd7\
|
||||
1\x83\xd12\x85\xd8e\xce\x99s\xddD R\x9ca\
|
||||
\xcb\xa7_\xc8\xbf0\x80\xcd,w\xc4^\xc7\xc4/|\
|
||||
\xc5-x\xc01\x83\xd53\x88\xd9.{\xc2|7\x1f\
|
||||
\xe1\x00\x00\x00\xa1tRNS\x00\x16\xc1\x1a\xfd\xfe\xfe\
|
||||
\xfd\x04\xc12\x8d3\x80\xf8I.\xb7\xfd7\x09\xfd\xfd\
|
||||
\xfe\xfc\xcd\x0d\xa8g\xa0\xd9g\xbe\x0fX\xfd\xc9\xfe%\
|
||||
H\xfcXhG;\xd8\x9f?\xe3\xf7\xa5B\x1b$\xcc\
|
||||
\xf4B\xect\xe7\xb0\xec\xf4P\x84\x91\xe7\xae\xb8N\xac\
|
||||
\x91\xf5)\x80_\xe4v[:\xf4\x9a\xdf\x87\xfdF\xfd\
|
||||
\xfd\xfc\xf7o\xfc\xfdh\xfd\xbb\xf3\x12\xf2\xf9\xfd\xa2`\
|
||||
\xfd\xfc\xec\xfd\xe3\xfdm\xfd\xfd\xb6\xfd\xfd\x89\xfd#\xcd\
|
||||
\xfe\xf3\xfd\xfdz\xfc\xfe\xfd\xfd\xfdC\x1f\xf0\x86.\xe8\
|
||||
y\xfc\xca\x1f\x9b_\xa2\x9d\x93\xb6\xbdG\x8a\xbc\xcf\xd5\
|
||||
\xf5\x99Q\xfd\xba\xfc\xb2\xc9\x8b\xc8u|\x80\xe1\x00\x00\
|
||||
\x02oIDAT8\xcbc` \x010\xf6\xf4O\
|
||||
PQ\x99\xd8\xdb\xde(\x82MZ\xbf@\xf4\xc5\x85\x0d\
|
||||
{\xf7}x\xf7\xeay\x82\x91\x0e\x86n7\xd1\x1bO\
|
||||
\x80\xd2o_\xbf|\xf6\xf4\xe1\xed\xa3\x87\x5c\xd4P\xe4\
|
||||
\x85}n\xc0\xb5\xff\xfa~\xf4\xd4\xa1\xc3'\xcd5\x91\
|
||||
\xe5\x93P\xb5\x1f>y\xff\xc8\xbd\x9bZp\xf9\xd2L\
|
||||
t\xedG>\xdf\xb9uPC\x1d*\xcfQ\x86\xac\xfd\
|
||||
\xd4\xe1o \xed\x07\x8f\x1f\xbb\xea$\x05QP}a\
|
||||
CIN\x91\x90P\x5c^a:H\xfb}\x90\xf6c\
|
||||
\xd7\xaf\xed?P\x0c\xf1@EmM\xf9\xff\x8b?~\
|
||||
\xfc\xb8\xf8'?\x03\xae\xfd\xda\x95\x03w\x9b\xc1F\xb4\
|
||||
\xd4U\xfe\xfe\xf8\xe6\xdf\xbf\xb3\xff>\xfd}\xffS,\
|
||||
\x0b\xa8\xfd8H\xfb\x89m\x8fv(\x81\x5c\xd0\xd6p\
|
||||
\xf1\xcd\xd9E@\xb0d\xc9\xc2O\xac?\xc5R\xa0\xda\
|
||||
\xb7=>\xb3\xb3\x1b\xa8`R\xc7o\xb8\xfcBA\x99\
|
||||
\xb4\xd4d\xb0\xf6\xbb\x8fv\x9c9\xb7\xbe\x13\xa8`\xf2\
|
||||
\x9f\x8fg\xa1\xd2\xac\xb9 +C#\xc1\xdaw\xec<\
|
||||
\xb7~\xb7\x1c\x90?\xe5\xe2'\xa8|H<\xc4W\x8e\
|
||||
`\xed;\xd7o\xdf}^\x12\xc8\x9d\xf6\xe3\x1fXz\
|
||||
\x19+{\x14D\x81=D\xfb\xf6\x07_NK\x03\xb9\
|
||||
S?^\x06\xcb/\x8b\xde\x1a\x0bQ`\x0c\xd1\xfe\xe0\
|
||||
\xcb\xaaU _(|\x02Ko\x0a_\xf9\xd5\x1d\xa2\
|
||||
\xc0\x01\xa4}\xf7\xf9\xd3\xabV\xc9q\x00\xb9}\x0b\xc1\
|
||||
\xf2\x8b#V~\x0d\x02\xf1\x19\x04\x0c\xc1\xda\x81\xf2\xb6\
|
||||
\x02 \xbe\x0cHz\xd3\xe2\xc5,[\xb7n\xf5\x03\x09\
|
||||
\xf0\xd8A\xb4\xafb2\x05\x1b\xd8\xc4\x06\xd4\x0e\x04\xec\
|
||||
+\xb7n\xb5f\x06\x89\x98XI3I\xdax\xf1\xc0\
|
||||
\x22[\x01,\xbf\x02\xa8`\xe5J3E,\x89Q\x9b\
|
||||
\x0d(\xbdb\x05\xcb\xe6\x95+7o\xd6\xe5\xc2\xa2B\
|
||||
\x10$\xbf%l\xdd\xe6\xcd\x9b\xd7\xad\x93\xd0\x83)\x11\
|
||||
\x91\xe7\x86\xb2\x98-\x81\xf2[b\xd6\xac\x03\x82\xe5\xcb\
|
||||
\xd7\xac1p\xe6\xe6\x0d\x0e\xe4\x0c\x10\xe7\x87\x19a\xc1\
|
||||
\xb7e\xcb\x96],k\x96/\x07\xc9\xafY\xbdz\xcf\
|
||||
\x9e\xa5\x1b7\x8a\xfb\x22,q\xe5\xdb\xb5kWU\xfd\
|
||||
\x9a5\xcbA\xd2\xab\xf7,\x05\xc9\xf3\x22;\xc3?\x1b\
|
||||
\xa8BYb\xcf\xea\xd5P\xed\x1b=\xbcQ\x1d\x9a(\
|
||||
\xbb\xeb\xd2%e\xf6\x8dKA\x00\xa8\x9d\xd3\x13\xc3/\
|
||||
\xad\xb2\xd3UU\xe7\xcc\x12_\xbbv\xad8'?\xd6\
|
||||
\xcc\xab8s\xfe\xdc\x05\xf3f\xcf\x90\xef\x22%\xcb\x03\
|
||||
\x00/\xab\x8c\xa71\xf3a\xb4\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
"
|
||||
|
||||
qt_resource_name = b"\
|
||||
\x00\x06\
|
||||
\x07\x03}\xc3\
|
||||
\x00i\
|
||||
\x00m\x00a\x00g\x00e\x00s\
|
||||
\x00\x0d\
|
||||
\x06\x0b\xf4\xa7\
|
||||
\x00b\
|
||||
\x00l\x00a\x00n\x00k\x00_\x001\x002\x008\x00.\x00p\x00n\x00g\
|
||||
\x00\x12\
|
||||
\x0d,\x1a\xc7\
|
||||
\x00d\
|
||||
\x00a\x00i\x00l\x00y\x00c\x00h\x00e\x00c\x00k\x00_\x001\x002\x008\x00.\x00p\x00n\
|
||||
\x00g\
|
||||
\x00\x08\
|
||||
\x09n\x91\xb3\
|
||||
\x00b\
|
||||
\x00r\x00o\x00w\x00s\x00e\x00r\x00s\
|
||||
\x00\x0c\
|
||||
\x0fTN'\
|
||||
\x00b\
|
||||
\x00r\x00a\x00v\x00e\x00_\x003\x002\x00.\x00p\x00n\x00g\
|
||||
\x00\x0d\
|
||||
\x01\x97\x91\x87\
|
||||
\x00c\
|
||||
\x00h\x00r\x00o\x00m\x00e\x00_\x003\x002\x00.\x00p\x00n\x00g\
|
||||
\x00\x0b\
|
||||
\x02\xb2\x0c'\
|
||||
\x00e\
|
||||
\x00d\x00g\x00e\x00_\x003\x002\x00.\x00p\x00n\x00g\
|
||||
"
|
||||
|
||||
qt_resource_struct = b"\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x01\x8db\xb8\xc9\xc8\
|
||||
\x00\x00\x00\x5c\x00\x02\x00\x00\x00\x03\x00\x00\x00\x05\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x002\x00\x00\x00\x00\x00\x01\x00\x00\x01\xbc\
|
||||
\x00\x00\x01\x8df\xb35\xbe\
|
||||
\x00\x00\x00\x90\x00\x00\x00\x00\x00\x01\x00\x00\x15\xae\
|
||||
\x00\x00\x01\x8dd\xf15i\
|
||||
\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x1b/\
|
||||
\x00\x00\x01\x8dd\xf1K\xbc\
|
||||
\x00\x00\x00r\x00\x00\x00\x00\x00\x01\x00\x00\x10,\
|
||||
\x00\x00\x01\x8dd\xf1\x5c\xbe\
|
||||
"
|
||||
|
||||
def qInitResources():
|
||||
QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
|
||||
|
||||
def qCleanupResources():
|
||||
QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
|
||||
|
||||
qInitResources()
|
||||
BIN
dailycheck.icns
Normal file
BIN
dailycheck.icns
Normal file
Binary file not shown.
BIN
dailycheck.ico
Normal file
BIN
dailycheck.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 173 KiB |
42
global_vars.py
Normal file
42
global_vars.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# coding: utf8
|
||||
import requests
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
SoftwareStatusRole = 0x0101
|
||||
ExtensionStatusRole = 0x0102
|
||||
ExtensionIdRole = 0x0103
|
||||
|
||||
|
||||
def accept_warning(widget: QtWidgets.QWidget, condition: bool,
|
||||
caption: str = "Warning", text: str = "Are you sure to continue?") -> bool:
|
||||
if condition:
|
||||
b = QtWidgets.QMessageBox.question(widget, caption, text)
|
||||
if b == QtWidgets.QMessageBox.StandardButton.No:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_with_chained_keys(dic: dict, keys: list, default=None):
|
||||
"""
|
||||
调用 get_with_chained_keys(d, ["a", "b", "c"])
|
||||
等同于 d["a"]["b"]["c"] ,
|
||||
只不过中间任意一次索引如果找不到键,则返回 default
|
||||
|
||||
:param dic: 目标字典
|
||||
:param keys: 键列表
|
||||
:param default: 找不到键时的默认返回值
|
||||
:return:
|
||||
"""
|
||||
k = keys[0]
|
||||
if k not in dic:
|
||||
return default
|
||||
if len(keys) == 1:
|
||||
return dic[k]
|
||||
return get_with_chained_keys(dic[k], keys[1:], default)
|
||||
|
||||
|
||||
def request_content(url: str) -> bytes:
|
||||
req = requests.get(url)
|
||||
if req.status_code == 200:
|
||||
return req.content
|
||||
return b""
|
||||
BIN
images/blank_128.png
Normal file
BIN
images/blank_128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 440 B |
BIN
images/browsers/brave_32.png
Normal file
BIN
images/browsers/brave_32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
images/browsers/chrome_32.png
Normal file
BIN
images/browsers/chrome_32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
images/browsers/edge_32.png
Normal file
BIN
images/browsers/edge_32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
images/dailycheck_128.png
Normal file
BIN
images/dailycheck_128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
74
main.py
Normal file
74
main.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# coding: utf8
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
from mw_dailycheck import MwDailyCheck
|
||||
|
||||
import daily_check_rc
|
||||
|
||||
version = (1, 0, 0)
|
||||
|
||||
ORG_NAME = "JnPrograms"
|
||||
APP_NAME = "DailyCheck"
|
||||
|
||||
|
||||
def set_default_settings():
|
||||
plat = sys.platform
|
||||
user_path = os.path.expanduser("~")
|
||||
user_data_path_map = {
|
||||
"win32": {
|
||||
"Chrome": str(Path(user_path, r"AppData\Local\Google\Chrome\User Data")),
|
||||
"Edge": str(Path(user_path, r"AppData\Local\Microsoft\Edge\User Data")),
|
||||
"Brave": str(Path(user_path, r"AppData\Local\BraveSoftware\Brave-Browser\User Data")),
|
||||
},
|
||||
"darwin": {
|
||||
"Chrome": str(Path(user_path, "Library/Application Support/Google/Chrome")),
|
||||
"Edge": str(Path(user_path, "Library/Application Support/Microsoft Edge")),
|
||||
"Brave": str(Path(user_path, "Library/Application Support/BraveSoftware/Brave-Browser")),
|
||||
},
|
||||
}
|
||||
exec_path_map = {
|
||||
"win32": {
|
||||
"Chrome": r"C:\Program Files\Google\Chrome\Application\chrome.exe",
|
||||
"Edge": r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
|
||||
"Brave": r"C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe",
|
||||
},
|
||||
"darwin": {
|
||||
"Chrome": r"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||
"Edge": r"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
||||
"Brave": r"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
||||
},
|
||||
}
|
||||
user_data_path = user_data_path_map[plat]
|
||||
exec_path = exec_path_map[plat]
|
||||
settings_map = {
|
||||
"ChromeExec": exec_path["Chrome"],
|
||||
"EdgeExec": exec_path["Edge"],
|
||||
"BraveExec": exec_path["Brave"],
|
||||
"ChromeData": user_data_path["Chrome"],
|
||||
"EdgeData": user_data_path["Edge"],
|
||||
"BraveData": user_data_path["Brave"],
|
||||
}
|
||||
us = QtCore.QSettings()
|
||||
exist_keys = us.childKeys()
|
||||
for s in settings_map:
|
||||
if s not in exist_keys:
|
||||
us.setValue(s, settings_map[s])
|
||||
# print(f"Add key {s}")
|
||||
|
||||
|
||||
def main():
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
app.setOrganizationName(ORG_NAME)
|
||||
app.setApplicationName(APP_NAME)
|
||||
|
||||
set_default_settings()
|
||||
|
||||
win = MwDailyCheck(version)
|
||||
win.show()
|
||||
return app.exec()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
101
mw_dailycheck.py
Normal file
101
mw_dailycheck.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# coding: utf8
|
||||
import json
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from PySide6 import QtWidgets, QtGui, QtCore
|
||||
from wg_basic import WgBasic
|
||||
from wg_software import WgSoftware
|
||||
from wg_extensions import WgExtensions
|
||||
from global_vars import (
|
||||
request_content,
|
||||
accept_warning,
|
||||
)
|
||||
|
||||
|
||||
class UiMwDailyCheck(object):
|
||||
|
||||
def __init__(self, window: QtWidgets.QMainWindow):
|
||||
|
||||
self.cw = QtWidgets.QWidget(window)
|
||||
window.setCentralWidget(self.cw)
|
||||
|
||||
self.vly_m = QtWidgets.QVBoxLayout()
|
||||
self.cw.setLayout(self.vly_m)
|
||||
self.tw_m = QtWidgets.QTabWidget(self.cw)
|
||||
self.vly_m.addWidget(self.tw_m)
|
||||
self.wg_basic = WgBasic(self.cw)
|
||||
self.wg_software = WgSoftware(self.cw)
|
||||
self.wg_extensions = WgExtensions(self.cw)
|
||||
self.tw_m.addTab(self.wg_basic, "基本信息")
|
||||
self.tw_m.addTab(self.wg_software, "已安装软件")
|
||||
self.tw_m.addTab(self.wg_extensions, "已安装插件")
|
||||
|
||||
self.menu_bar = window.menuBar()
|
||||
self.menu_help = self.menu_bar.addMenu("帮助")
|
||||
self.menu_about = self.menu_bar.addMenu("关于")
|
||||
|
||||
self.act_update_safe = QtGui.QAction("更新安全标注", window)
|
||||
self.act_export_unknown = QtGui.QAction("导出未知", window)
|
||||
|
||||
self.act_about = QtGui.QAction("关于", window)
|
||||
self.act_about_qt = QtGui.QAction("关于 Qt", window)
|
||||
|
||||
self.menu_help.addActions([self.act_update_safe, self.act_export_unknown])
|
||||
self.menu_about.addActions([self.act_about, self.act_about_qt])
|
||||
|
||||
|
||||
class MwDailyCheck(QtWidgets.QMainWindow):
|
||||
|
||||
def __init__(self, version: tuple, parent=None):
|
||||
super().__init__(parent)
|
||||
self.version = version
|
||||
self.setWindowTitle("日常检查工具")
|
||||
self.setWindowIcon(QtGui.QIcon(":/images/dailycheck_128.png"))
|
||||
self.ui = UiMwDailyCheck(self)
|
||||
|
||||
self.ui.act_update_safe.triggered.connect(self.on_act_update_safe_triggered)
|
||||
self.ui.act_export_unknown.triggered.connect(self.on_act_export_unknown_triggered)
|
||||
self.ui.act_about.triggered.connect(self.on_act_about_triggered)
|
||||
self.ui.act_about_qt.triggered.connect(self.on_act_about_qt_triggered)
|
||||
|
||||
def sizeHint(self):
|
||||
return QtCore.QSize(800, 600)
|
||||
|
||||
def on_act_about_triggered(self):
|
||||
msg = f"日常检查工具\n\n适用于 Windows 平台。版本:{self.version[0]}.{self.version[1]}.{self.version[-1]}"
|
||||
QtWidgets.QMessageBox.about(self, "关于", msg)
|
||||
|
||||
def on_act_about_qt_triggered(self):
|
||||
QtWidgets.QMessageBox.aboutQt(self, "关于 Qt")
|
||||
|
||||
def on_act_update_safe_triggered(self):
|
||||
root_url = "https://julianfreeman.github.io/dailycheckutils"
|
||||
marks_all = request_content(f"{root_url}/marks_all.json").decode("utf8")
|
||||
if len(marks_all) == 0:
|
||||
return
|
||||
marks_all_d = json.loads(marks_all)
|
||||
self.ui.wg_software.update_safe(marks_all_d["software_win"])
|
||||
self.ui.wg_extensions.update_safe(marks_all_d["extensions"])
|
||||
self.ui.wg_basic.update_safe(marks_all_d["isp"], marks_all_d["manufacturer"])
|
||||
|
||||
def on_act_export_unknown_triggered(self):
|
||||
dirname = QtWidgets.QFileDialog.getExistingDirectory(self, "导出未知")
|
||||
if len(dirname) == 0:
|
||||
return
|
||||
now = datetime.strftime(datetime.now(), "%y%m%d%H%M")
|
||||
ex_file = Path(dirname, f"未知信息{now}.json")
|
||||
if accept_warning(self, ex_file.exists(), "警告", "文件已存在,确认覆盖吗?"):
|
||||
return
|
||||
|
||||
unknown_software = self.ui.wg_software.export_unknown()
|
||||
unknown_ext = self.ui.wg_extensions.export_unknown()
|
||||
unknown_isp_manu = self.ui.wg_basic.export_unknown()
|
||||
|
||||
unknown_all = {
|
||||
"software_win": unknown_software,
|
||||
"extensions": unknown_ext,
|
||||
}
|
||||
unknown_all.update(unknown_isp_manu)
|
||||
|
||||
ex_file.write_text(json.dumps(unknown_all, indent=4, ensure_ascii=False), "utf8")
|
||||
QtWidgets.QMessageBox.information(self, "提示", f"已导出到 {ex_file}")
|
||||
237
util_ext.py
Normal file
237
util_ext.py
Normal file
@@ -0,0 +1,237 @@
|
||||
# coding: utf8
|
||||
import os
|
||||
import json
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
# from typing import Callable
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
from dataclasses import dataclass, field
|
||||
from global_vars import get_with_chained_keys
|
||||
|
||||
|
||||
@dataclass
|
||||
class ProfileNode(object):
|
||||
gaia_given_name: str
|
||||
gaia_name: str
|
||||
name: str
|
||||
shortcut_name: str
|
||||
user_name: str
|
||||
|
||||
|
||||
ProfilesData = dict[str, ProfileNode]
|
||||
|
||||
|
||||
def scan_profiles(user_data_path: str) -> ProfilesData:
|
||||
local_state_path = Path(user_data_path, "Local State")
|
||||
if not local_state_path.exists():
|
||||
return {}
|
||||
|
||||
local_state_data: dict = json.loads(local_state_path.read_text(encoding="utf8"))
|
||||
info_cache_data: dict = get_with_chained_keys(local_state_data, ["profile", "info_cache"])
|
||||
|
||||
profiles_data: ProfilesData = {}
|
||||
|
||||
for profile_id in info_cache_data:
|
||||
profile_data: dict = info_cache_data[profile_id]
|
||||
profile_node = ProfileNode(
|
||||
gaia_given_name=profile_data.get("gaia_given_name", ""),
|
||||
gaia_name=profile_data.get("gaia_name", ""),
|
||||
name=profile_data.get("name", ""),
|
||||
shortcut_name=profile_data.get("shortcut_name", ""),
|
||||
user_name=profile_data.get("user_name"),
|
||||
)
|
||||
profiles_data[profile_id] = profile_node
|
||||
|
||||
return profiles_data
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExtensionNode(object):
|
||||
# ids: str
|
||||
icon: str
|
||||
name: str
|
||||
# path: str
|
||||
profiles: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
ExtensionsData = dict[str, ExtensionNode]
|
||||
|
||||
|
||||
def get_extension_icon_path(ext_icons: dict[str, str], ext_path: str, profile_path: Path) -> str:
|
||||
if len(ext_icons) == 0:
|
||||
return ""
|
||||
|
||||
if "128" in ext_icons:
|
||||
icon_file = ext_icons["128"]
|
||||
else:
|
||||
icon_file = ext_icons[str(max(map(lambda x: int(x), ext_icons.keys())))]
|
||||
# 如果路径以 / 开头,会被认为是根而忽略其他,因此需要检查一下
|
||||
if icon_file.startswith("/"):
|
||||
icon_file = icon_file[1:]
|
||||
|
||||
full_path = Path(profile_path, "Extensions", ext_path, icon_file)
|
||||
if not full_path.exists():
|
||||
return ""
|
||||
return str(full_path)
|
||||
|
||||
|
||||
def scan_extensions(browser: str, is_compat=False) -> tuple[ExtensionsData, ProfilesData]:
|
||||
us = QtCore.QSettings()
|
||||
user_data_path = str(us.value(f"{browser}Data", ""))
|
||||
if len(user_data_path) == 0 or not Path(user_data_path).exists():
|
||||
return {}, {}
|
||||
|
||||
profile_data = scan_profiles(user_data_path)
|
||||
extensions_data: ExtensionsData = {}
|
||||
|
||||
if is_compat:
|
||||
pref_file = "Preferences"
|
||||
else:
|
||||
pref_file = "Secure Preferences"
|
||||
|
||||
# print(pref_file)
|
||||
|
||||
for profile_id in profile_data:
|
||||
profile_path = Path(user_data_path, profile_id)
|
||||
secure_pref_path = Path(profile_path, pref_file)
|
||||
secure_pref_data: dict = json.loads(secure_pref_path.read_text(encoding="utf8"))
|
||||
ext_settings_data: dict = get_with_chained_keys(secure_pref_data, ["extensions", "settings"], dict())
|
||||
|
||||
for ext_id in ext_settings_data:
|
||||
ext_data: dict = ext_settings_data[ext_id]
|
||||
# 这里插件有几种情况
|
||||
ext_path: str = ext_data.get("path", "")
|
||||
if len(ext_path) == 0:
|
||||
# 如果 path 是空,则该插件可能是一些内部插件,不予收集
|
||||
continue
|
||||
elif not (ext_path.startswith(ext_id) or os.path.exists(ext_path)):
|
||||
# 如果 path 以插件 ID 开头,则为商店安装的插件,
|
||||
# 如果不是以插件 ID 开头,但是是一个存在的路径,则为离线安装的插件
|
||||
# 否则,可能也是内部插件,不予收集
|
||||
continue
|
||||
ext_manifest: dict = ext_data.get("manifest", {})
|
||||
if len(ext_manifest) == 0:
|
||||
# 如果 manifest 为空,则该插件可能是离线插件,需要额外找它的 manifest
|
||||
ext_manifest_path = Path(ext_path, "manifest.json")
|
||||
ext_manifest: dict = json.loads(ext_manifest_path.read_text(encoding="utf8"))
|
||||
|
||||
if ext_id not in extensions_data:
|
||||
ext_node = ExtensionNode(
|
||||
# ids=ext_id,
|
||||
icon=get_extension_icon_path(ext_manifest.get("icons", {}), ext_path, profile_path),
|
||||
name=ext_manifest.get("name", ""),
|
||||
# path=ext_data.get("path", ""),
|
||||
profiles=[profile_id],
|
||||
)
|
||||
extensions_data[ext_id] = ext_node
|
||||
else:
|
||||
ext_node = extensions_data[ext_id]
|
||||
ext_node.profiles += [profile_id]
|
||||
|
||||
return extensions_data, profile_data
|
||||
|
||||
|
||||
# ================== Deletion ====================
|
||||
|
||||
|
||||
def delete_extensions(profile_path: str, pref_name: str, ext_ids: list[str]) -> tuple[int, int]:
|
||||
total = len(ext_ids)
|
||||
|
||||
e_pref_path = Path(profile_path, pref_name)
|
||||
e_pref_data = json.loads(e_pref_path.read_text("utf8")) # type: dict
|
||||
ext_set_data = get_with_chained_keys(e_pref_data, ["extensions", "settings"]) # type: dict
|
||||
if ext_set_data is None:
|
||||
return 0, total
|
||||
|
||||
s_pref_path = Path(profile_path, "Secure Preferences")
|
||||
pref_path = Path(profile_path, "Preferences")
|
||||
s_pref_data = json.loads(s_pref_path.read_text("utf8")) # type: dict
|
||||
pref_data = json.loads(pref_path.read_text("utf8")) # type: dict
|
||||
|
||||
macs = get_with_chained_keys(s_pref_data, ["protection", "macs", "extensions", "settings"]) # type: dict
|
||||
if macs is None:
|
||||
return 0, total
|
||||
|
||||
success = 0
|
||||
for ids in ext_ids:
|
||||
c1 = ext_set_data.pop(ids, None)
|
||||
c2 = macs.pop(ids, None)
|
||||
if None not in (c1, c2):
|
||||
success += 1
|
||||
|
||||
pinned_ext = get_with_chained_keys(pref_data, ["extensions", "pinned_extensions"]) # type: list
|
||||
if pinned_ext is not None:
|
||||
for ids in ext_ids:
|
||||
if ids in pinned_ext:
|
||||
pinned_ext.remove(ids)
|
||||
|
||||
s_pref_path.write_text(json.dumps(s_pref_data, ensure_ascii=False), "utf8")
|
||||
pref_path.write_text(json.dumps(pref_data, ensure_ascii=False), "utf8")
|
||||
|
||||
extensions_path_d = Path(profile_path, "Extensions")
|
||||
for ids in ext_ids:
|
||||
# 对于离线安装的插件,目录可能不在这个位置,所以就不删了
|
||||
ext_folder_path = Path(extensions_path_d, ids)
|
||||
if ext_folder_path.exists():
|
||||
shutil.rmtree(ext_folder_path, ignore_errors=True)
|
||||
|
||||
return success, total
|
||||
|
||||
|
||||
class DeleteThread(QtCore.QThread):
|
||||
|
||||
deleted = QtCore.Signal(int, int)
|
||||
|
||||
def __init__(self,
|
||||
profile_path: str,
|
||||
pref_name: str,
|
||||
ext_ids: list[str],
|
||||
parent: QtCore.QObject = None):
|
||||
super().__init__(parent)
|
||||
self.profile_path = profile_path
|
||||
self.pref_name = pref_name
|
||||
self.ext_ids = ext_ids
|
||||
self.finished.connect(self.deleteLater)
|
||||
|
||||
def run(self):
|
||||
success, total = delete_extensions(self.profile_path, self.pref_name, self.ext_ids)
|
||||
self.deleted.emit(success, total)
|
||||
|
||||
|
||||
class DeleteThreadManager(QtCore.QObject):
|
||||
|
||||
def __init__(self, total: int, progress_bar: QtWidgets.QProgressBar, parent: QtWidgets.QDialog):
|
||||
super().__init__(parent)
|
||||
self.deletion_progress = 0
|
||||
self.success_deletion = 0
|
||||
self.fail_deletion = 0
|
||||
self.total_for_deletion = total
|
||||
self.deletion_info = "成功:{success} 个;失败:{fail} 个;总共 {total} 个。"
|
||||
self.progress_bar = progress_bar
|
||||
self.parent = parent
|
||||
|
||||
self.progress_bar.setMaximum(total)
|
||||
self.progress_bar.setValue(0)
|
||||
|
||||
self.progress_bar.valueChanged.connect(self.on_pgb_del_value_changed)
|
||||
|
||||
def start(self, thread: DeleteThread):
|
||||
thread.deleted.connect(self.on_del_thd_deleted)
|
||||
thread.start()
|
||||
|
||||
def on_del_thd_deleted(self, success: int, total: int):
|
||||
self.success_deletion += success
|
||||
self.deletion_progress += total
|
||||
self.fail_deletion += total - success
|
||||
self.progress_bar.setValue(self.deletion_progress)
|
||||
|
||||
def on_pgb_del_value_changed(self, value: int):
|
||||
if value == self.total_for_deletion:
|
||||
QtWidgets.QMessageBox.information(
|
||||
self.parent, "删除结果", self.deletion_info.format(
|
||||
success=self.success_deletion,
|
||||
fail=self.fail_deletion,
|
||||
total=self.total_for_deletion
|
||||
)
|
||||
)
|
||||
self.parent.accept()
|
||||
82
util_func.py
Normal file
82
util_func.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# coding: utf8
|
||||
import os
|
||||
import wmi
|
||||
import json
|
||||
import win32gui
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from PySide6.QtCore import QDir, QSettings
|
||||
from PySide6.QtGui import QImage, QIcon, QPixmap
|
||||
from global_vars import request_content
|
||||
|
||||
|
||||
def get_isp_name() -> str:
|
||||
try:
|
||||
data = json.loads(request_content("https://ipinfo.io/"))
|
||||
return data.get("org", "[Not found]")
|
||||
except json.JSONDecodeError:
|
||||
return "[Decode Error]"
|
||||
|
||||
|
||||
def get_win_manufacturer() -> list[str]:
|
||||
w = wmi.WMI()
|
||||
cs_ls = w.Win32_ComputerSystem()
|
||||
if len(cs_ls) > 0:
|
||||
cs = cs_ls[0]
|
||||
return [cs.model, cs.Manufacturer]
|
||||
return []
|
||||
|
||||
|
||||
def get_win_license() -> str:
|
||||
temp_out = str(Path(QDir.tempPath(), "lic.txt"))
|
||||
return_code = subprocess.call(
|
||||
rf'cscript /Nologo /U "C:\Windows\System32\slmgr.vbs" /dlv >{temp_out}',
|
||||
shell=True,
|
||||
)
|
||||
if return_code == 0:
|
||||
with open(temp_out, "r", encoding="utf-16-le") as f:
|
||||
return f.read()
|
||||
return ""
|
||||
|
||||
|
||||
def get_win_installed_software() -> dict[str, str]:
|
||||
|
||||
def gather_software(path: str, software_dict: dict[str, str]):
|
||||
reg = QSettings(path, QSettings.Format.NativeFormat)
|
||||
soft = reg.childGroups()
|
||||
for s in soft:
|
||||
c = QSettings(path + "\\" + s.split("/")[0], QSettings.Format.NativeFormat)
|
||||
name = str(c.value("DisplayName"))
|
||||
if name == "None":
|
||||
continue
|
||||
icon = str(c.value("DisplayIcon"))
|
||||
software_dict[name] = icon
|
||||
|
||||
all_software = {}
|
||||
gather_software(r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", all_software)
|
||||
gather_software(r"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", all_software)
|
||||
gather_software(r"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall", all_software)
|
||||
|
||||
return all_software
|
||||
|
||||
|
||||
def extract_win_icon_from_file(icon: str, default: QIcon) -> QIcon:
|
||||
if icon == "None":
|
||||
return default
|
||||
if icon.endswith(",0"):
|
||||
icon = icon[:-2]
|
||||
icon = icon.replace('"', "")
|
||||
icon = icon.replace("'", "")
|
||||
if not os.path.exists(icon):
|
||||
return default
|
||||
if not icon.endswith(("exe", "EXE", "dll", "DLL")):
|
||||
return QIcon(icon)
|
||||
|
||||
large, small = win32gui.ExtractIconEx(icon, 0)
|
||||
if len(small) > 0:
|
||||
win32gui.DestroyIcon(small[0])
|
||||
if len(large) > 0:
|
||||
image = QImage.fromHICON(large[0])
|
||||
return QIcon(QPixmap(image))
|
||||
else:
|
||||
return default
|
||||
92
wg_basic.py
Normal file
92
wg_basic.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# coding: utf8
|
||||
from PySide6 import QtWidgets, QtCore, QtGui
|
||||
from util_func import (
|
||||
get_isp_name,
|
||||
get_win_manufacturer,
|
||||
get_win_license,
|
||||
)
|
||||
|
||||
|
||||
class UiWgBasic(object):
|
||||
|
||||
def __init__(self, window: QtWidgets.QWidget):
|
||||
self.vly_m = QtWidgets.QVBoxLayout()
|
||||
window.setLayout(self.vly_m)
|
||||
|
||||
self.gbx_isp = QtWidgets.QGroupBox("网络运营商", window)
|
||||
self.vly_m.addWidget(self.gbx_isp)
|
||||
self.vly_gbx_isp = QtWidgets.QVBoxLayout()
|
||||
self.gbx_isp.setLayout(self.vly_gbx_isp)
|
||||
|
||||
self.lne_isp = QtWidgets.QLineEdit(self.gbx_isp)
|
||||
self.lne_isp.setReadOnly(True)
|
||||
self.vly_gbx_isp.addWidget(self.lne_isp)
|
||||
|
||||
self.gbx_manu = QtWidgets.QGroupBox("系统制造商", window)
|
||||
self.vly_m.addWidget(self.gbx_manu)
|
||||
self.hly_gbx_manu = QtWidgets.QHBoxLayout()
|
||||
self.gbx_manu.setLayout(self.hly_gbx_manu)
|
||||
self.lb_model = QtWidgets.QLabel("型号:", self.gbx_manu)
|
||||
self.lne_model = QtWidgets.QLineEdit(self.gbx_manu)
|
||||
self.lne_model.setReadOnly(True)
|
||||
self.lb_manufacturer = QtWidgets.QLabel("制造商:", self.gbx_manu)
|
||||
self.lne_manufacturer = QtWidgets.QLineEdit(self.gbx_manu)
|
||||
self.lne_manufacturer.setReadOnly(True)
|
||||
self.hly_gbx_manu.addWidget(self.lb_model)
|
||||
self.hly_gbx_manu.addWidget(self.lne_model)
|
||||
self.hly_gbx_manu.addWidget(self.lb_manufacturer)
|
||||
self.hly_gbx_manu.addWidget(self.lne_manufacturer)
|
||||
|
||||
self.gbx_license = QtWidgets.QGroupBox("许可证", window)
|
||||
self.vly_m.addWidget(self.gbx_license)
|
||||
self.vly_gbx_license = QtWidgets.QVBoxLayout()
|
||||
self.gbx_license.setLayout(self.vly_gbx_license)
|
||||
self.pte_license = QtWidgets.QPlainTextEdit(self.gbx_license)
|
||||
self.vly_gbx_license.addWidget(self.pte_license)
|
||||
self.pte_license.setReadOnly(True)
|
||||
|
||||
|
||||
class WgBasic(QtWidgets.QWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.ui = UiWgBasic(self)
|
||||
|
||||
self.ui.lne_isp.setText(get_isp_name())
|
||||
model, manufacturer = get_win_manufacturer()
|
||||
self.ui.lne_model.setText(model)
|
||||
self.ui.lne_manufacturer.setText(manufacturer)
|
||||
|
||||
self.ui.pte_license.setPlainText(get_win_license())
|
||||
|
||||
def update_safe(self, isp_safe_info: dict, manu_safe_info: dict):
|
||||
|
||||
def set_palette(is_safe: bool | None, lne_w: QtWidgets.QLineEdit):
|
||||
pal = lne_w.palette()
|
||||
if is_safe is True:
|
||||
pal.setColor(QtGui.QPalette.ColorRole.Text, QtCore.Qt.GlobalColor.blue)
|
||||
elif is_safe is False:
|
||||
pal.setColor(QtGui.QPalette.ColorRole.Text, QtCore.Qt.GlobalColor.red)
|
||||
else:
|
||||
pal.setColor(QtGui.QPalette.ColorRole.Text, QtCore.Qt.GlobalColor.black)
|
||||
lne_w.setPalette(pal)
|
||||
|
||||
isp_text = self.ui.lne_isp.text()
|
||||
if isp_text in isp_safe_info:
|
||||
is_isp_safe = isp_safe_info[isp_text]["safe"]
|
||||
set_palette(is_isp_safe, self.ui.lne_isp)
|
||||
|
||||
manu_text = self.ui.lne_manufacturer.text()
|
||||
if manu_text in manu_safe_info:
|
||||
is_manu_safe = manu_safe_info[manu_text]["safe"]
|
||||
set_palette(is_manu_safe, self.ui.lne_manufacturer)
|
||||
|
||||
def export_unknown(self) -> dict:
|
||||
unknown = {}
|
||||
text_role = QtGui.QPalette.ColorRole.Text
|
||||
black = QtCore.Qt.GlobalColor.black
|
||||
if self.ui.lne_isp.palette().color(text_role) == black:
|
||||
unknown["isp"] = self.ui.lne_isp.text()
|
||||
if self.ui.lne_manufacturer.palette().color(text_role) == black:
|
||||
unknown["manufacturer"] = self.ui.lne_manufacturer.text()
|
||||
return unknown
|
||||
277
wg_extensions.py
Normal file
277
wg_extensions.py
Normal file
@@ -0,0 +1,277 @@
|
||||
# coding: utf8
|
||||
from PySide6 import QtWidgets, QtCore, QtGui
|
||||
from util_ext import scan_extensions, ExtensionsData, ProfilesData
|
||||
from global_vars import (
|
||||
ExtensionStatusRole,
|
||||
ExtensionIdRole,
|
||||
)
|
||||
from da_ext_settings import DaExtSettings
|
||||
from da_show_profiles import DaShowProfiles
|
||||
|
||||
|
||||
class UiWgExtensions(object):
|
||||
|
||||
def __init__(self, window: QtWidgets.QWidget):
|
||||
self.vly_m = QtWidgets.QVBoxLayout()
|
||||
window.setLayout(self.vly_m)
|
||||
|
||||
self.hly_top = QtWidgets.QHBoxLayout()
|
||||
self.vly_m.addLayout(self.hly_top)
|
||||
self.cmbx_browsers = QtWidgets.QComboBox(window)
|
||||
self.cbx_safe = QtWidgets.QCheckBox("安全", window)
|
||||
self.cbx_unsafe = QtWidgets.QCheckBox("不安全", window)
|
||||
self.cbx_unknown = QtWidgets.QCheckBox("未知", window)
|
||||
self.cbx_safe.setChecked(True)
|
||||
self.cbx_unsafe.setChecked(True)
|
||||
self.cbx_unknown.setChecked(True)
|
||||
self.cbx_compat = QtWidgets.QCheckBox("兼容模式", window)
|
||||
self.pbn_update = QtWidgets.QPushButton("更新", window)
|
||||
self.pbn_settings = QtWidgets.QPushButton("设置", window)
|
||||
self.hly_top.addWidget(self.cmbx_browsers)
|
||||
self.hly_top.addWidget(self.cbx_safe)
|
||||
self.hly_top.addWidget(self.cbx_unsafe)
|
||||
self.hly_top.addWidget(self.cbx_unknown)
|
||||
self.hly_top.addStretch(1)
|
||||
self.hly_top.addWidget(self.cbx_compat)
|
||||
self.hly_top.addWidget(self.pbn_update)
|
||||
self.hly_top.addWidget(self.pbn_settings)
|
||||
|
||||
self.lv_extensions = QtWidgets.QListView(window)
|
||||
self.vly_m.addWidget(self.lv_extensions)
|
||||
|
||||
|
||||
class BaseExtensionsListModel(QtCore.QAbstractListModel):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.all_profiles = {} # type: ProfilesData
|
||||
self.all_extensions = {} # type: ExtensionsData
|
||||
self.names = [] # type: list[tuple[str, str]]
|
||||
self.icons = {} # type: dict[str, QtGui.QIcon]
|
||||
self.safe_info = {} # type: dict[str, dict]
|
||||
self.blank_icon = QtGui.QIcon(":/images/blank_128.png")
|
||||
|
||||
self.last_is_compat = False
|
||||
|
||||
def update(self, is_compat=False):
|
||||
raise NotImplementedError
|
||||
|
||||
def update_ext(self, browser: str, is_compat=False):
|
||||
"""内部用"""
|
||||
self.all_profiles.clear()
|
||||
self.all_extensions.clear()
|
||||
self.names.clear()
|
||||
self.icons.clear()
|
||||
|
||||
self.all_extensions, self.all_profiles = scan_extensions(browser, is_compat)
|
||||
self.last_is_compat = is_compat
|
||||
for ext_id in self.all_extensions:
|
||||
name = self.all_extensions[ext_id].name
|
||||
icon = self.all_extensions[ext_id].icon
|
||||
self.names.append((ext_id, name))
|
||||
if len(icon) == 0:
|
||||
self.icons[ext_id] = self.blank_icon
|
||||
else:
|
||||
self.icons[ext_id] = QtGui.QIcon(icon)
|
||||
self.names.sort(key=lambda x: x[1].lower())
|
||||
|
||||
# print("updated " + browser)
|
||||
|
||||
def update_safe_info(self, safe_info: dict):
|
||||
self.safe_info.clear()
|
||||
self.safe_info = safe_info
|
||||
|
||||
def rowCount(self, parent: QtCore.QModelIndex = ...):
|
||||
return len(self.names)
|
||||
|
||||
def data(self, index: QtCore.QModelIndex, role: int = ...):
|
||||
row = index.row()
|
||||
ext_id, name = self.names[row]
|
||||
|
||||
if role == QtCore.Qt.ItemDataRole.DisplayRole:
|
||||
return name
|
||||
if role == QtCore.Qt.ItemDataRole.DecorationRole:
|
||||
return self.icons[ext_id]
|
||||
if role == QtCore.Qt.ItemDataRole.BackgroundRole:
|
||||
is_safe = self.data(index, ExtensionStatusRole)
|
||||
if is_safe is True:
|
||||
return QtGui.QBrush(QtGui.QColor("lightgreen"))
|
||||
elif is_safe is False:
|
||||
return QtGui.QBrush(QtGui.QColor("lightpink"))
|
||||
else:
|
||||
return QtGui.QBrush(QtCore.Qt.BrushStyle.NoBrush)
|
||||
if role == ExtensionStatusRole:
|
||||
if ext_id not in self.safe_info:
|
||||
return None
|
||||
else:
|
||||
return self.safe_info[ext_id]["safe"]
|
||||
if role == ExtensionIdRole:
|
||||
return ext_id
|
||||
|
||||
|
||||
class ChromeExtensionsListModel(BaseExtensionsListModel):
|
||||
|
||||
def update(self, is_compat=False):
|
||||
super().update_ext("Chrome", is_compat)
|
||||
|
||||
|
||||
class EdgeExtensionsListModel(BaseExtensionsListModel):
|
||||
|
||||
def update(self, is_compat=False):
|
||||
super().update_ext("Edge", is_compat)
|
||||
|
||||
|
||||
class BraveExtensionsListModel(BaseExtensionsListModel):
|
||||
|
||||
def update(self, is_compat=False):
|
||||
super().update_ext("Brave", is_compat)
|
||||
|
||||
|
||||
class BrowsersListModel(QtCore.QAbstractListModel):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.browsers = ["Chrome", "Edge", "Brave"]
|
||||
self.icons = [
|
||||
QtGui.QIcon(":/images/browsers/chrome_32.png"),
|
||||
QtGui.QIcon(":/images/browsers/edge_32.png"),
|
||||
QtGui.QIcon(":/images/browsers/brave_32.png"),
|
||||
]
|
||||
|
||||
def rowCount(self, parent: QtCore.QModelIndex = ...):
|
||||
return len(self.browsers)
|
||||
|
||||
def data(self, index: QtCore.QModelIndex, role: int = ...):
|
||||
row = index.row()
|
||||
|
||||
if role == QtCore.Qt.ItemDataRole.DisplayRole:
|
||||
return self.browsers[row]
|
||||
if role == QtCore.Qt.ItemDataRole.DecorationRole:
|
||||
return self.icons[row]
|
||||
|
||||
|
||||
class WgExtensions(QtWidgets.QWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.ui = UiWgExtensions(self)
|
||||
|
||||
self.browsers_list_model = BrowsersListModel(self)
|
||||
self.ui.cmbx_browsers.setModel(self.browsers_list_model)
|
||||
|
||||
self.ext_list_models: dict[str, BaseExtensionsListModel] = {
|
||||
"Chrome": ChromeExtensionsListModel(self),
|
||||
"Edge": EdgeExtensionsListModel(self),
|
||||
"Brave": BraveExtensionsListModel(self),
|
||||
}
|
||||
self.model_is_initial = {
|
||||
"Chrome": True,
|
||||
"Edge": True,
|
||||
"Brave": True,
|
||||
}
|
||||
self.switch_model(self.get_current_browser())
|
||||
|
||||
self.ui.cbx_compat.clicked.connect(self.on_cbx_compat_clicked)
|
||||
self.ui.cmbx_browsers.currentTextChanged.connect(self.on_cmbx_browsers_current_text_changed)
|
||||
self.ui.cbx_safe.clicked.connect(self.on_cbx_safe_clicked)
|
||||
self.ui.cbx_unsafe.clicked.connect(self.on_cbx_unsafe_clicked)
|
||||
self.ui.cbx_unknown.clicked.connect(self.on_cbx_unknown_clicked)
|
||||
self.ui.pbn_update.clicked.connect(self.on_pbn_update_clicked)
|
||||
self.ui.pbn_settings.clicked.connect(self.on_pbn_settings_clicked)
|
||||
self.ui.lv_extensions.doubleClicked.connect(self.on_lv_extensions_double_clicked)
|
||||
|
||||
def on_pbn_settings_clicked(self):
|
||||
da_es = DaExtSettings(self)
|
||||
da_es.exec()
|
||||
|
||||
def get_current_browser(self) -> str:
|
||||
return self.ui.cmbx_browsers.currentData(QtCore.Qt.ItemDataRole.DisplayRole)
|
||||
|
||||
def show_all_rows(self):
|
||||
# 在 update 之前调用
|
||||
self.filters_clicked(True, True)
|
||||
self.filters_clicked(False, True)
|
||||
self.filters_clicked(None, True)
|
||||
|
||||
def apply_rows_hidden(self):
|
||||
# 在 update 之后调用
|
||||
self.filters_clicked(True, self.ui.cbx_safe.isChecked())
|
||||
self.filters_clicked(False, self.ui.cbx_unsafe.isChecked())
|
||||
self.filters_clicked(None, self.ui.cbx_unknown.isChecked())
|
||||
|
||||
def update_model(self, browser: str):
|
||||
model = self.ext_list_models[browser]
|
||||
self.show_all_rows()
|
||||
model.update(is_compat=self.ui.cbx_compat.isChecked())
|
||||
self.apply_rows_hidden()
|
||||
|
||||
def switch_model(self, browser: str):
|
||||
if self.model_is_initial[browser] is True:
|
||||
self.update_model(browser)
|
||||
self.model_is_initial[browser] = False
|
||||
|
||||
model = self.ext_list_models[browser]
|
||||
self.show_all_rows()
|
||||
self.ui.lv_extensions.setModel(model)
|
||||
self.apply_rows_hidden()
|
||||
# 单纯的切换浏览器不一定会导致更新数据,所以需要同步兼容模式的设置
|
||||
self.ui.cbx_compat.setChecked(model.last_is_compat)
|
||||
|
||||
def on_cbx_compat_clicked(self):
|
||||
self.update_model(self.get_current_browser())
|
||||
|
||||
def on_cmbx_browsers_current_text_changed(self, text: str):
|
||||
self.switch_model(text)
|
||||
|
||||
def filters_clicked(self, safe_mark: bool | None, checked: bool):
|
||||
model = self.ext_list_models[self.get_current_browser()]
|
||||
for row in range(model.rowCount()):
|
||||
idx = model.index(row)
|
||||
is_safe = model.data(idx, ExtensionStatusRole)
|
||||
if is_safe is safe_mark:
|
||||
self.ui.lv_extensions.setRowHidden(row, not checked)
|
||||
|
||||
def on_cbx_safe_clicked(self, checked: bool):
|
||||
self.filters_clicked(True, checked)
|
||||
|
||||
def on_cbx_unsafe_clicked(self, checked: bool):
|
||||
self.filters_clicked(False, checked)
|
||||
|
||||
def on_cbx_unknown_clicked(self, checked: bool):
|
||||
self.filters_clicked(None, checked)
|
||||
|
||||
def on_pbn_update_clicked(self):
|
||||
self.update_model(self.get_current_browser())
|
||||
|
||||
def on_lv_extensions_double_clicked(self, index: QtCore.QModelIndex):
|
||||
model = self.ext_list_models[self.get_current_browser()]
|
||||
ext_id = model.data(index, ExtensionIdRole)
|
||||
node = model.all_extensions[ext_id]
|
||||
da_sp = DaShowProfiles(
|
||||
self.get_current_browser(),
|
||||
self.ui.cbx_compat.isChecked(),
|
||||
model.all_profiles,
|
||||
ext_id,
|
||||
node.name,
|
||||
model.icons[ext_id],
|
||||
node.profiles,
|
||||
self
|
||||
)
|
||||
da_sp.exec()
|
||||
|
||||
def update_safe(self, safe_info: dict):
|
||||
for browser in self.ext_list_models:
|
||||
self.ext_list_models[browser].update_safe_info(safe_info)
|
||||
|
||||
def export_unknown(self) -> dict[str, dict]:
|
||||
unknown_ext = {}
|
||||
for browser in self.ext_list_models:
|
||||
model = self.ext_list_models[browser]
|
||||
for row in range(model.rowCount()):
|
||||
idx = model.index(row)
|
||||
is_safe = model.data(idx, ExtensionStatusRole)
|
||||
if is_safe is None:
|
||||
ext_id = model.data(idx, ExtensionIdRole)
|
||||
name = model.data(idx, QtCore.Qt.ItemDataRole.DisplayRole)
|
||||
unknown_ext[ext_id] = {"name": name}
|
||||
return unknown_ext
|
||||
131
wg_software.py
Normal file
131
wg_software.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# coding: utf8
|
||||
from PySide6 import QtWidgets, QtCore, QtGui
|
||||
from util_func import (
|
||||
get_win_installed_software,
|
||||
extract_win_icon_from_file,
|
||||
)
|
||||
from global_vars import (
|
||||
SoftwareStatusRole,
|
||||
)
|
||||
|
||||
|
||||
class UiWgSoftware(object):
|
||||
|
||||
def __init__(self, window: QtWidgets.QWidget):
|
||||
self.vly_m = QtWidgets.QVBoxLayout()
|
||||
window.setLayout(self.vly_m)
|
||||
|
||||
self.hly_top = QtWidgets.QHBoxLayout()
|
||||
self.vly_m.addLayout(self.hly_top)
|
||||
self.cbx_safe = QtWidgets.QCheckBox("安全", window)
|
||||
self.cbx_unsafe = QtWidgets.QCheckBox("不安全", window)
|
||||
self.cbx_unknown = QtWidgets.QCheckBox("未知", window)
|
||||
self.cbx_safe.setChecked(True)
|
||||
self.cbx_unsafe.setChecked(True)
|
||||
self.cbx_unknown.setChecked(True)
|
||||
self.hly_top.addWidget(self.cbx_safe)
|
||||
self.hly_top.addWidget(self.cbx_unsafe)
|
||||
self.hly_top.addWidget(self.cbx_unknown)
|
||||
self.hly_top.addStretch(1)
|
||||
|
||||
self.lv_software = QtWidgets.QListView(window)
|
||||
self.vly_m.addWidget(self.lv_software)
|
||||
|
||||
|
||||
class SoftwareListModel(QtCore.QAbstractListModel):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.all_software = {} # type: dict[str, str]
|
||||
self.names = [] # type: list[str]
|
||||
self.icons = {} # type: dict[str, QtGui.QIcon]
|
||||
self.safe_info = {}
|
||||
self.blank_icon = QtGui.QIcon(":/images/blank_128.png")
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
self.all_software.clear()
|
||||
self.names.clear()
|
||||
self.icons.clear()
|
||||
|
||||
self.all_software = get_win_installed_software()
|
||||
for s in self.all_software:
|
||||
self.names.append(s)
|
||||
self.icons[s] = extract_win_icon_from_file(
|
||||
self.all_software[s],
|
||||
self.blank_icon
|
||||
)
|
||||
self.names.sort(key=lambda x: x.lower())
|
||||
|
||||
def update_safe_info(self, safe_info: dict):
|
||||
self.safe_info.clear()
|
||||
self.safe_info = safe_info
|
||||
|
||||
def rowCount(self, parent: QtCore.QModelIndex = ...):
|
||||
return len(self.names)
|
||||
|
||||
def data(self, index: QtCore.QModelIndex, role: int = ...):
|
||||
row = index.row()
|
||||
name = self.names[row]
|
||||
|
||||
if role == QtCore.Qt.ItemDataRole.DisplayRole:
|
||||
return name
|
||||
if role == QtCore.Qt.ItemDataRole.DecorationRole:
|
||||
return self.icons[name]
|
||||
if role == QtCore.Qt.ItemDataRole.BackgroundRole:
|
||||
is_safe = self.data(index, SoftwareStatusRole)
|
||||
if is_safe is True:
|
||||
return QtGui.QBrush(QtGui.QColor("lightgreen"))
|
||||
elif is_safe is False:
|
||||
return QtGui.QBrush(QtGui.QColor("lightpink"))
|
||||
else:
|
||||
return QtGui.QBrush(QtCore.Qt.BrushStyle.NoBrush)
|
||||
if role == SoftwareStatusRole:
|
||||
if name not in self.safe_info:
|
||||
return None
|
||||
else:
|
||||
return self.safe_info[name]["safe"]
|
||||
|
||||
|
||||
class WgSoftware(QtWidgets.QWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.ui = UiWgSoftware(self)
|
||||
|
||||
self.software_list_model = SoftwareListModel(self)
|
||||
self.ui.lv_software.setModel(self.software_list_model)
|
||||
|
||||
self.ui.cbx_safe.clicked.connect(self.on_cbx_safe_clicked)
|
||||
self.ui.cbx_unsafe.clicked.connect(self.on_cbx_unsafe_clicked)
|
||||
self.ui.cbx_unknown.clicked.connect(self.on_cbx_unknown_clicked)
|
||||
|
||||
def filters_clicked(self, safe_mark: bool | None, checked: bool):
|
||||
for row in range(self.software_list_model.rowCount()):
|
||||
idx = self.software_list_model.index(row)
|
||||
is_safe = self.software_list_model.data(idx, SoftwareStatusRole)
|
||||
if is_safe is safe_mark:
|
||||
self.ui.lv_software.setRowHidden(row, not checked)
|
||||
|
||||
def on_cbx_safe_clicked(self, checked: bool):
|
||||
self.filters_clicked(True, checked)
|
||||
|
||||
def on_cbx_unsafe_clicked(self, checked: bool):
|
||||
self.filters_clicked(False, checked)
|
||||
|
||||
def on_cbx_unknown_clicked(self, checked: bool):
|
||||
self.filters_clicked(None, checked)
|
||||
|
||||
def update_safe(self, safe_info: dict):
|
||||
self.software_list_model.update_safe_info(safe_info)
|
||||
|
||||
def export_unknown(self) -> list[str]:
|
||||
unknown_software = []
|
||||
for row in range(self.software_list_model.rowCount()):
|
||||
idx = self.software_list_model.index(row)
|
||||
is_safe = self.software_list_model.data(idx, SoftwareStatusRole)
|
||||
if is_safe is None:
|
||||
name = self.software_list_model.data(idx, QtCore.Qt.ItemDataRole.DisplayRole)
|
||||
unknown_software.append(name)
|
||||
|
||||
return unknown_software
|
||||
Reference in New Issue
Block a user