From 606f9528615e08dc7af4dd72f3555f110fb054a5 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 14:47:17 +0000 Subject: [PATCH] update build scripts and addindg extra stuf --- PepDeb32/PepProPixMaps | 1 + PepDeb32/PepProTools | 1 + PepDeb32/pepapplication | 1 + PepDeb32/pepbld.sh | 137 +- PepDeb32/pepconf | 1 + PepDeb32/pepdatabase | 1 + PepDeb32/pepissue | 1 - PepDeb32/pmostools | 1 + PepDeb32/polkit | 1 + PepDeb32/pylibraries | 1 + PepDeb64/PepProPixMaps | 1 + PepDeb64/PepProTools | 1 + PepDeb64/pepapplication | 1 + PepDeb64/pepbld.sh | 136 +- PepDeb64/pepconf | 1 + PepDeb64/pepdatabase | 1 + PepDeb64/pepissue | 1 - PepDeb64/pmostools | 1 + PepDeb64/polkit | 1 + PepDeb64/pylibraries | 1 + PepDeb_arm64/PepProPixMaps | 1 + PepDeb_arm64/PepProTools | 1 + PepDeb_arm64/pepapplication | 1 + PepDeb_arm64/pepbld.sh | 131 +- PepDeb_arm64/pepconf | 1 + PepDeb_arm64/pepdatabase | 1 + PepDeb_arm64/pepissue | 1 - PepDeb_arm64/pmostools | 1 + PepDeb_arm64/polkit | 1 + PepDeb_arm64/pylibraries | 1 + PepDev32/PepProPixMaps | 1 + PepDev32/PepProTools | 1 + PepDev32/pepapplication | 1 + PepDev32/pepbld.sh | 133 +- PepDev32/pepconf | 1 + PepDev32/pepdatabase | 1 + PepDev32/pepissue | 1 - PepDev32/pepscripts/copy-files-to-chroot.sh | 6 +- PepDev32/pmostools | 1 + PepDev32/polkit | 1 + PepDev32/pylibraries | 1 + PepDev64/PepProPixMaps | 1 + PepDev64/PepProTools | 1 + PepDev64/pepapplication | 1 + PepDev64/pepbld.sh | 133 +- PepDev64/pepconf | 1 + PepDev64/pepdatabase | 1 + PepDev64/pepissue | 1 - PepDev64/pepscripts/copy-files-to-chroot.sh | 6 +- PepDev64/pmostools | 1 + PepDev64/polkit | 1 + PepDev64/pylibraries | 1 + PepDev_arm64/PepProPixMaps | 1 + PepDev_arm64/PepProTools | 1 + PepDev_arm64/pepapplication | 1 + PepDev_arm64/pepbld.sh | 132 +- PepDev_arm64/pepconf | 1 + PepDev_arm64/pepdatabase | 1 + PepDev_arm64/pepissue | 1 - .../pepscripts/copy-files-to-chroot.sh | 6 +- PepDev_arm64/pmostools | 1 + PepDev_arm64/polkit | 1 + PepDev_arm64/pylibraries | 1 + PepProPixMaps/ai.png | Bin 0 -> 3654 bytes PepProPixMaps/applications-system.png | Bin 0 -> 2902 bytes PepProPixMaps/battery.png | Bin 0 -> 2785 bytes PepProPixMaps/ckm.png | Bin 0 -> 674 bytes PepProPixMaps/cs-default-applications.png | Bin 0 -> 3384 bytes PepProPixMaps/cs-notifications.png | Bin 0 -> 2762 bytes PepProPixMaps/dconf-editor.png | Bin 0 -> 2465 bytes PepProPixMaps/drive-harddisk-system.png | Bin 0 -> 2594 bytes PepProPixMaps/ff.png | Bin 0 -> 5824 bytes PepProPixMaps/flat.png | Bin 0 -> 9848 bytes PepProPixMaps/glade.png | Bin 0 -> 3593 bytes PepProPixMaps/gnome-dev-printer.png | Bin 0 -> 2196 bytes PepProPixMaps/gnome-session.png | Bin 0 -> 3758 bytes PepProPixMaps/gnome-software.png | Bin 0 -> 2963 bytes PepProPixMaps/hblock.png | Bin 0 -> 10886 bytes PepProPixMaps/hblockon.png | Bin 0 -> 10790 bytes PepProPixMaps/install-debian.png | Bin 0 -> 6292 bytes PepProPixMaps/keyboard.png | Bin 0 -> 2111 bytes PepProPixMaps/kumo.png | Bin 0 -> 12475 bytes PepProPixMaps/logo.png | Bin 0 -> 7252 bytes PepProPixMaps/luakit.png | Bin 0 -> 12475 bytes PepProPixMaps/mouse.png | Bin 0 -> 2531 bytes PepProPixMaps/network-wired.png | Bin 0 -> 1505 bytes PepProPixMaps/panel-applets.png | Bin 0 -> 2287 bytes PepProPixMaps/peppermint-48.png | Bin 0 -> 14647 bytes PepProPixMaps/peppermint-fm-20.png | Bin 0 -> 13612 bytes PepProPixMaps/peppermint-hub-128.png | Bin 0 -> 17813 bytes PepProPixMaps/peppermint-hub.png | Bin 0 -> 14485 bytes PepProPixMaps/peppermint-inst-48.png | Bin 0 -> 2610 bytes PepProPixMaps/peppermint-inst.png | Bin 0 -> 6292 bytes PepProPixMaps/peppermint-old.png | Bin 0 -> 18648 bytes PepProPixMaps/peppermint-rim-128.png | Bin 0 -> 18648 bytes PepProPixMaps/peppermint-rim-48.png | Bin 0 -> 14645 bytes PepProPixMaps/peppermint.png | Bin 0 -> 18649 bytes .../preferences-desktop-accessibility.png | Bin 0 -> 2755 bytes PepProPixMaps/snap.png | Bin 0 -> 13428 bytes PepProPixMaps/software-properties.png | Bin 0 -> 3433 bytes PepProPixMaps/stock_music-library.png | Bin 0 -> 4887 bytes PepProPixMaps/stock_people.png | Bin 0 -> 1904 bytes PepProPixMaps/synaptic.png | Bin 0 -> 3269 bytes PepProPixMaps/update-manager.png | Bin 0 -> 3429 bytes PepProPixMaps/video-display.png | Bin 0 -> 2301 bytes PepProPixMaps/xd.png | Bin 0 -> 1485 bytes PepProTools/hub | 4 + PepProTools/kumo | 3 + PepProTools/welcome | 3 + PepProTools/xDaily | 211 + pepapplication/Pephub.desktop | 11 + pepapplication/Welcome.desktop | 11 + pepapplication/gdebi.desktop | 164 + pepapplication/kumo.desktop | 10 + pepapplication/plank.desktop | 67 + pepconf/hostname | 1 + pepconf/issue | 2 + pepconf/issue.net | 1 + pepconf/os-release | 7 + pepconf/sources.list | 24 + pepdatabase/welval.db | Bin 0 -> 12288 bytes pepinstaller/preseed/preseed.cfg | 2 +- pepissue/issue | 2 - pepissue/issue.net | 1 - .../boot/grub/live-theme/background.png | Bin 16859 -> 34608 bytes peploadersplash/boot/grub/splash.png | Bin 16859 -> 34608 bytes peploadersplash/isolinux/splash.png | Bin 16859 -> 34608 bytes pepscripts/copy-files-to-chroot.sh | 46 +- pmostools/peptools/actions.py | 135 + pmostools/peptools/bsconf.py | 6 + pmostools/peptools/hub.py | 563 ++ pmostools/peptools/hubconf.py | 68 + pmostools/peptools/images/cb.png | Bin 0 -> 1013 bytes pmostools/peptools/images/kumosm.png | Bin 0 -> 19404 bytes pmostools/peptools/images/mn.png | Bin 0 -> 9349 bytes pmostools/peptools/images/mt.png | Bin 0 -> 744 bytes pmostools/peptools/images/peppermint-hub.png | Bin 0 -> 14485 bytes .../peptools/images/peppermint-inst-48.png | Bin 0 -> 2610 bytes .../peptools/images/peppermint-rimbw-48.png | Bin 0 -> 12513 bytes .../peptools/images/peppermint-word-white.png | Bin 0 -> 18163 bytes pmostools/peptools/images/rd.png | Bin 0 -> 12549 bytes pmostools/peptools/images/sf.png | Bin 0 -> 1057 bytes pmostools/peptools/images/xd.png | Bin 0 -> 1485 bytes pmostools/peptools/kumo.py | 170 + pmostools/peptools/refresh.py | 15 + pmostools/peptools/suggested.py | 273 + pmostools/peptools/ttkcreator/__init__.py | 0 pmostools/peptools/ttkcreator/__main__.py | 472 ++ pmostools/peptools/welcome.py | 257 + pmostools/peptools/welconf.py | 48 + pmostools/peptools/welfunc.py | 62 + polkit/org.freedesktop.pepkumo.policy | 19 + polkit/org.freedesktop.peppackages.policy | 19 + polkit/org.freedesktop.pepu.policy | 19 + polkit/org.freedesktop.python3.policy | 18 + polkit/org.freedesktop.ttkcreator.policy | 19 + pylibraries/tendo-0.3.0.dist-info/INSTALLER | 1 + pylibraries/tendo-0.3.0.dist-info/LICENSE.txt | 48 + pylibraries/tendo-0.3.0.dist-info/METADATA | 70 + pylibraries/tendo-0.3.0.dist-info/RECORD | 45 + pylibraries/tendo-0.3.0.dist-info/REQUESTED | 0 pylibraries/tendo-0.3.0.dist-info/WHEEL | 5 + .../tendo-0.3.0.dist-info/top_level.txt | 1 + pylibraries/tendo/__init__.py | 18 + .../tendo/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 637 bytes .../tendo/__pycache__/_version.cpython-39.pyc | Bin 0 -> 282 bytes .../tendo/__pycache__/ansiterm.cpython-39.pyc | Bin 0 -> 8276 bytes .../tendo/__pycache__/colorer.cpython-39.pyc | Bin 0 -> 3171 bytes .../__pycache__/execfile2.cpython-39.pyc | Bin 0 -> 1879 bytes .../__pycache__/singleton.cpython-39.pyc | Bin 0 -> 2988 bytes .../tendo/__pycache__/tee.cpython-39.pyc | Bin 0 -> 6232 bytes .../tendo/__pycache__/unicode.cpython-39.pyc | Bin 0 -> 2326 bytes pylibraries/tendo/_version.py | 5 + pylibraries/tendo/ansiterm.py | 273 + pylibraries/tendo/colorer.py | 142 + pylibraries/tendo/execfile2.py | 56 + pylibraries/tendo/py.typed | 0 pylibraries/tendo/singleton.py | 92 + pylibraries/tendo/tee.py | 277 + pylibraries/tendo/tests/__init__.py | 0 .../tests/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 193 bytes .../__pycache__/test_colorer.cpython-39.pyc | Bin 0 -> 1225 bytes .../__pycache__/test_execfile2.cpython-39.pyc | Bin 0 -> 1773 bytes .../__pycache__/test_singleton.cpython-39.pyc | Bin 0 -> 1648 bytes .../tests/__pycache__/test_tee.cpython-39.pyc | Bin 0 -> 1541 bytes .../__pycache__/test_unicode.cpython-39.pyc | Bin 0 -> 1650 bytes .../tests/assets/full_sample_utf8_bom.txt | 4 + .../tendo/tests/assets/sample_ucs2_be.txt | Bin 0 -> 86 bytes .../tendo/tests/assets/sample_ucs2_le.txt | Bin 0 -> 86 bytes .../tendo/tests/assets/sample_utf8.txt | 1 + .../tendo/tests/assets/sample_utf8_bom.txt | 3 + .../tendo/tests/assets/utf8-after-append.txt | 1 + .../tendo/tests/assets/utf8-invalid.txt | Bin 0 -> 20334 bytes pylibraries/tendo/tests/assets/utf8.txt | 1 + pylibraries/tendo/tests/py.typed | 0 pylibraries/tendo/tests/test_colorer.py | 37 + pylibraries/tendo/tests/test_execfile2.py | 52 + pylibraries/tendo/tests/test_singleton.py | 58 + pylibraries/tendo/tests/test_tee.py | 48 + pylibraries/tendo/tests/test_unicode.py | 49 + pylibraries/tendo/unicode.py | 81 + .../ttkbootstrap-1.10.1.dist-info/INSTALLER | 1 + .../ttkbootstrap-1.10.1.dist-info/LICENSE | 21 + .../ttkbootstrap-1.10.1.dist-info/METADATA | 81 + .../ttkbootstrap-1.10.1.dist-info/RECORD | 61 + .../ttkbootstrap-1.10.1.dist-info/REQUESTED | 0 .../ttkbootstrap-1.10.1.dist-info/WHEEL | 5 + .../top_level.txt | 2 + pylibraries/ttkbootstrap/__init__.py | 11 + pylibraries/ttkbootstrap/__main__.py | 301 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 680 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 0 -> 6794 bytes .../__pycache__/colorutils.cpython-39.pyc | Bin 0 -> 5293 bytes .../__pycache__/constants.cpython-39.pyc | Bin 0 -> 914 bytes .../__pycache__/icons.cpython-39.pyc | Bin 0 -> 76414 bytes .../__pycache__/publisher.cpython-39.pyc | Bin 0 -> 3771 bytes .../__pycache__/scrolled.cpython-39.pyc | Bin 0 -> 14575 bytes .../__pycache__/style.cpython-39.pyc | Bin 0 -> 98295 bytes .../__pycache__/tableview.cpython-39.pyc | Bin 0 -> 77970 bytes .../__pycache__/toast.cpython-39.pyc | Bin 0 -> 6679 bytes .../__pycache__/tooltip.cpython-39.pyc | Bin 0 -> 4957 bytes .../__pycache__/utility.cpython-39.pyc | Bin 0 -> 3845 bytes .../__pycache__/validation.cpython-39.pyc | Bin 0 -> 10268 bytes .../__pycache__/widgets.cpython-39.pyc | Bin 0 -> 31302 bytes .../__pycache__/window.cpython-39.pyc | Bin 0 -> 15107 bytes pylibraries/ttkbootstrap/colorutils.py | 204 + pylibraries/ttkbootstrap/constants.py | 43 + pylibraries/ttkbootstrap/dialogs/__init__.py | 1 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 247 bytes .../__pycache__/colorchooser.cpython-39.pyc | Bin 0 -> 18242 bytes .../__pycache__/colordropper.cpython-39.pyc | Bin 0 -> 6207 bytes .../__pycache__/dialogs.cpython-39.pyc | Bin 0 -> 53722 bytes .../ttkbootstrap/dialogs/colorchooser.py | 570 ++ .../ttkbootstrap/dialogs/colordropper.py | 170 + pylibraries/ttkbootstrap/dialogs/dialogs.py | 1879 ++++++ pylibraries/ttkbootstrap/icons.py | 2130 +++++++ .../ttkbootstrap/localization/__init__.py | 18 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 895 bytes .../__pycache__/msgcat.cpython-39.pyc | Bin 0 -> 5981 bytes .../__pycache__/msgs.cpython-39.pyc | Bin 0 -> 11554 bytes .../ttkbootstrap/localization/msgcat.py | 179 + pylibraries/ttkbootstrap/localization/msgs.py | 431 ++ pylibraries/ttkbootstrap/publisher.py | 118 + pylibraries/ttkbootstrap/scrolled.py | 476 ++ pylibraries/ttkbootstrap/style.py | 5179 +++++++++++++++++ pylibraries/ttkbootstrap/tableview.py | 2657 +++++++++ pylibraries/ttkbootstrap/themes/__init__.py | 0 .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 201 bytes .../__pycache__/standard.cpython-39.pyc | Bin 0 -> 3576 bytes .../themes/__pycache__/user.cpython-39.pyc | Bin 0 -> 211 bytes pylibraries/ttkbootstrap/themes/standard.py | 380 ++ pylibraries/ttkbootstrap/themes/user.py | 1 + pylibraries/ttkbootstrap/toast.py | 243 + pylibraries/ttkbootstrap/tooltip.py | 175 + pylibraries/ttkbootstrap/utility.py | 100 + pylibraries/ttkbootstrap/validation.py | 331 ++ pylibraries/ttkbootstrap/widgets.py | 1162 ++++ pylibraries/ttkbootstrap/window.py | 504 ++ pylibraries/ttkcreator/__init__.py | 0 pylibraries/ttkcreator/__main__.py | 472 ++ .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 192 bytes .../__pycache__/__main__.cpython-39.pyc | Bin 0 -> 13471 bytes 262 files changed, 22778 insertions(+), 103 deletions(-) create mode 120000 PepDeb32/PepProPixMaps create mode 120000 PepDeb32/PepProTools create mode 120000 PepDeb32/pepapplication create mode 120000 PepDeb32/pepconf create mode 120000 PepDeb32/pepdatabase delete mode 120000 PepDeb32/pepissue create mode 120000 PepDeb32/pmostools create mode 120000 PepDeb32/polkit create mode 120000 PepDeb32/pylibraries create mode 120000 PepDeb64/PepProPixMaps create mode 120000 PepDeb64/PepProTools create mode 120000 PepDeb64/pepapplication create mode 120000 PepDeb64/pepconf create mode 120000 PepDeb64/pepdatabase delete mode 120000 PepDeb64/pepissue create mode 120000 PepDeb64/pmostools create mode 120000 PepDeb64/polkit create mode 120000 PepDeb64/pylibraries create mode 120000 PepDeb_arm64/PepProPixMaps create mode 120000 PepDeb_arm64/PepProTools create mode 120000 PepDeb_arm64/pepapplication create mode 120000 PepDeb_arm64/pepconf create mode 120000 PepDeb_arm64/pepdatabase delete mode 120000 PepDeb_arm64/pepissue create mode 120000 PepDeb_arm64/pmostools create mode 120000 PepDeb_arm64/polkit create mode 120000 PepDeb_arm64/pylibraries create mode 120000 PepDev32/PepProPixMaps create mode 120000 PepDev32/PepProTools create mode 120000 PepDev32/pepapplication create mode 120000 PepDev32/pepconf create mode 120000 PepDev32/pepdatabase delete mode 120000 PepDev32/pepissue create mode 120000 PepDev32/pmostools create mode 120000 PepDev32/polkit create mode 120000 PepDev32/pylibraries create mode 120000 PepDev64/PepProPixMaps create mode 120000 PepDev64/PepProTools create mode 120000 PepDev64/pepapplication create mode 120000 PepDev64/pepconf create mode 120000 PepDev64/pepdatabase delete mode 120000 PepDev64/pepissue create mode 120000 PepDev64/pmostools create mode 120000 PepDev64/polkit create mode 120000 PepDev64/pylibraries create mode 120000 PepDev_arm64/PepProPixMaps create mode 120000 PepDev_arm64/PepProTools create mode 120000 PepDev_arm64/pepapplication create mode 120000 PepDev_arm64/pepconf create mode 120000 PepDev_arm64/pepdatabase delete mode 120000 PepDev_arm64/pepissue create mode 120000 PepDev_arm64/pmostools create mode 120000 PepDev_arm64/polkit create mode 120000 PepDev_arm64/pylibraries create mode 100644 PepProPixMaps/ai.png create mode 100644 PepProPixMaps/applications-system.png create mode 100644 PepProPixMaps/battery.png create mode 100644 PepProPixMaps/ckm.png create mode 100644 PepProPixMaps/cs-default-applications.png create mode 100644 PepProPixMaps/cs-notifications.png create mode 100644 PepProPixMaps/dconf-editor.png create mode 100644 PepProPixMaps/drive-harddisk-system.png create mode 100644 PepProPixMaps/ff.png create mode 100644 PepProPixMaps/flat.png create mode 100644 PepProPixMaps/glade.png create mode 100644 PepProPixMaps/gnome-dev-printer.png create mode 100644 PepProPixMaps/gnome-session.png create mode 100644 PepProPixMaps/gnome-software.png create mode 100644 PepProPixMaps/hblock.png create mode 100644 PepProPixMaps/hblockon.png create mode 100644 PepProPixMaps/install-debian.png create mode 100644 PepProPixMaps/keyboard.png create mode 100644 PepProPixMaps/kumo.png create mode 100644 PepProPixMaps/logo.png create mode 100644 PepProPixMaps/luakit.png create mode 100644 PepProPixMaps/mouse.png create mode 100644 PepProPixMaps/network-wired.png create mode 100644 PepProPixMaps/panel-applets.png create mode 100644 PepProPixMaps/peppermint-48.png create mode 100644 PepProPixMaps/peppermint-fm-20.png create mode 100644 PepProPixMaps/peppermint-hub-128.png create mode 100644 PepProPixMaps/peppermint-hub.png create mode 100644 PepProPixMaps/peppermint-inst-48.png create mode 100644 PepProPixMaps/peppermint-inst.png create mode 100644 PepProPixMaps/peppermint-old.png create mode 100644 PepProPixMaps/peppermint-rim-128.png create mode 100644 PepProPixMaps/peppermint-rim-48.png create mode 100644 PepProPixMaps/peppermint.png create mode 100644 PepProPixMaps/preferences-desktop-accessibility.png create mode 100644 PepProPixMaps/snap.png create mode 100644 PepProPixMaps/software-properties.png create mode 100644 PepProPixMaps/stock_music-library.png create mode 100644 PepProPixMaps/stock_people.png create mode 100644 PepProPixMaps/synaptic.png create mode 100644 PepProPixMaps/update-manager.png create mode 100644 PepProPixMaps/video-display.png create mode 100644 PepProPixMaps/xd.png create mode 100755 PepProTools/hub create mode 100755 PepProTools/kumo create mode 100755 PepProTools/welcome create mode 100755 PepProTools/xDaily create mode 100755 pepapplication/Pephub.desktop create mode 100755 pepapplication/Welcome.desktop create mode 100755 pepapplication/gdebi.desktop create mode 100755 pepapplication/kumo.desktop create mode 100755 pepapplication/plank.desktop create mode 100644 pepconf/hostname create mode 100644 pepconf/issue create mode 100644 pepconf/issue.net create mode 100644 pepconf/os-release create mode 100644 pepconf/sources.list create mode 100644 pepdatabase/welval.db delete mode 100644 pepissue/issue delete mode 100644 pepissue/issue.net create mode 100644 pmostools/peptools/actions.py create mode 100644 pmostools/peptools/bsconf.py create mode 100755 pmostools/peptools/hub.py create mode 100644 pmostools/peptools/hubconf.py create mode 100644 pmostools/peptools/images/cb.png create mode 100644 pmostools/peptools/images/kumosm.png create mode 100644 pmostools/peptools/images/mn.png create mode 100644 pmostools/peptools/images/mt.png create mode 100644 pmostools/peptools/images/peppermint-hub.png create mode 100644 pmostools/peptools/images/peppermint-inst-48.png create mode 100644 pmostools/peptools/images/peppermint-rimbw-48.png create mode 100644 pmostools/peptools/images/peppermint-word-white.png create mode 100644 pmostools/peptools/images/rd.png create mode 100644 pmostools/peptools/images/sf.png create mode 100644 pmostools/peptools/images/xd.png create mode 100644 pmostools/peptools/kumo.py create mode 100644 pmostools/peptools/refresh.py create mode 100644 pmostools/peptools/suggested.py create mode 100644 pmostools/peptools/ttkcreator/__init__.py create mode 100644 pmostools/peptools/ttkcreator/__main__.py create mode 100755 pmostools/peptools/welcome.py create mode 100644 pmostools/peptools/welconf.py create mode 100644 pmostools/peptools/welfunc.py create mode 100644 polkit/org.freedesktop.pepkumo.policy create mode 100644 polkit/org.freedesktop.peppackages.policy create mode 100644 polkit/org.freedesktop.pepu.policy create mode 100755 polkit/org.freedesktop.python3.policy create mode 100644 polkit/org.freedesktop.ttkcreator.policy create mode 100644 pylibraries/tendo-0.3.0.dist-info/INSTALLER create mode 100644 pylibraries/tendo-0.3.0.dist-info/LICENSE.txt create mode 100644 pylibraries/tendo-0.3.0.dist-info/METADATA create mode 100644 pylibraries/tendo-0.3.0.dist-info/RECORD create mode 100644 pylibraries/tendo-0.3.0.dist-info/REQUESTED create mode 100644 pylibraries/tendo-0.3.0.dist-info/WHEEL create mode 100644 pylibraries/tendo-0.3.0.dist-info/top_level.txt create mode 100644 pylibraries/tendo/__init__.py create mode 100644 pylibraries/tendo/__pycache__/__init__.cpython-39.pyc create mode 100644 pylibraries/tendo/__pycache__/_version.cpython-39.pyc create mode 100644 pylibraries/tendo/__pycache__/ansiterm.cpython-39.pyc create mode 100644 pylibraries/tendo/__pycache__/colorer.cpython-39.pyc create mode 100644 pylibraries/tendo/__pycache__/execfile2.cpython-39.pyc create mode 100644 pylibraries/tendo/__pycache__/singleton.cpython-39.pyc create mode 100644 pylibraries/tendo/__pycache__/tee.cpython-39.pyc create mode 100644 pylibraries/tendo/__pycache__/unicode.cpython-39.pyc create mode 100644 pylibraries/tendo/_version.py create mode 100644 pylibraries/tendo/ansiterm.py create mode 100644 pylibraries/tendo/colorer.py create mode 100644 pylibraries/tendo/execfile2.py create mode 100644 pylibraries/tendo/py.typed create mode 100644 pylibraries/tendo/singleton.py create mode 100644 pylibraries/tendo/tee.py create mode 100644 pylibraries/tendo/tests/__init__.py create mode 100644 pylibraries/tendo/tests/__pycache__/__init__.cpython-39.pyc create mode 100644 pylibraries/tendo/tests/__pycache__/test_colorer.cpython-39.pyc create mode 100644 pylibraries/tendo/tests/__pycache__/test_execfile2.cpython-39.pyc create mode 100644 pylibraries/tendo/tests/__pycache__/test_singleton.cpython-39.pyc create mode 100644 pylibraries/tendo/tests/__pycache__/test_tee.cpython-39.pyc create mode 100644 pylibraries/tendo/tests/__pycache__/test_unicode.cpython-39.pyc create mode 100644 pylibraries/tendo/tests/assets/full_sample_utf8_bom.txt create mode 100644 pylibraries/tendo/tests/assets/sample_ucs2_be.txt create mode 100644 pylibraries/tendo/tests/assets/sample_ucs2_le.txt create mode 100644 pylibraries/tendo/tests/assets/sample_utf8.txt create mode 100644 pylibraries/tendo/tests/assets/sample_utf8_bom.txt create mode 100644 pylibraries/tendo/tests/assets/utf8-after-append.txt create mode 100644 pylibraries/tendo/tests/assets/utf8-invalid.txt create mode 100644 pylibraries/tendo/tests/assets/utf8.txt create mode 100644 pylibraries/tendo/tests/py.typed create mode 100644 pylibraries/tendo/tests/test_colorer.py create mode 100644 pylibraries/tendo/tests/test_execfile2.py create mode 100644 pylibraries/tendo/tests/test_singleton.py create mode 100644 pylibraries/tendo/tests/test_tee.py create mode 100644 pylibraries/tendo/tests/test_unicode.py create mode 100644 pylibraries/tendo/unicode.py create mode 100644 pylibraries/ttkbootstrap-1.10.1.dist-info/INSTALLER create mode 100644 pylibraries/ttkbootstrap-1.10.1.dist-info/LICENSE create mode 100644 pylibraries/ttkbootstrap-1.10.1.dist-info/METADATA create mode 100644 pylibraries/ttkbootstrap-1.10.1.dist-info/RECORD create mode 100644 pylibraries/ttkbootstrap-1.10.1.dist-info/REQUESTED create mode 100644 pylibraries/ttkbootstrap-1.10.1.dist-info/WHEEL create mode 100644 pylibraries/ttkbootstrap-1.10.1.dist-info/top_level.txt create mode 100644 pylibraries/ttkbootstrap/__init__.py create mode 100644 pylibraries/ttkbootstrap/__main__.py create mode 100644 pylibraries/ttkbootstrap/__pycache__/__init__.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/__main__.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/colorutils.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/constants.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/icons.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/publisher.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/scrolled.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/style.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/tableview.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/toast.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/tooltip.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/utility.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/validation.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/widgets.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/__pycache__/window.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/colorutils.py create mode 100644 pylibraries/ttkbootstrap/constants.py create mode 100644 pylibraries/ttkbootstrap/dialogs/__init__.py create mode 100644 pylibraries/ttkbootstrap/dialogs/__pycache__/__init__.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/dialogs/__pycache__/colorchooser.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/dialogs/__pycache__/colordropper.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/dialogs/__pycache__/dialogs.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/dialogs/colorchooser.py create mode 100644 pylibraries/ttkbootstrap/dialogs/colordropper.py create mode 100644 pylibraries/ttkbootstrap/dialogs/dialogs.py create mode 100644 pylibraries/ttkbootstrap/icons.py create mode 100644 pylibraries/ttkbootstrap/localization/__init__.py create mode 100644 pylibraries/ttkbootstrap/localization/__pycache__/__init__.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/localization/__pycache__/msgcat.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/localization/__pycache__/msgs.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/localization/msgcat.py create mode 100644 pylibraries/ttkbootstrap/localization/msgs.py create mode 100644 pylibraries/ttkbootstrap/publisher.py create mode 100644 pylibraries/ttkbootstrap/scrolled.py create mode 100644 pylibraries/ttkbootstrap/style.py create mode 100644 pylibraries/ttkbootstrap/tableview.py create mode 100644 pylibraries/ttkbootstrap/themes/__init__.py create mode 100644 pylibraries/ttkbootstrap/themes/__pycache__/__init__.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/themes/__pycache__/standard.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/themes/__pycache__/user.cpython-39.pyc create mode 100644 pylibraries/ttkbootstrap/themes/standard.py create mode 100644 pylibraries/ttkbootstrap/themes/user.py create mode 100644 pylibraries/ttkbootstrap/toast.py create mode 100644 pylibraries/ttkbootstrap/tooltip.py create mode 100644 pylibraries/ttkbootstrap/utility.py create mode 100644 pylibraries/ttkbootstrap/validation.py create mode 100644 pylibraries/ttkbootstrap/widgets.py create mode 100644 pylibraries/ttkbootstrap/window.py create mode 100644 pylibraries/ttkcreator/__init__.py create mode 100644 pylibraries/ttkcreator/__main__.py create mode 100644 pylibraries/ttkcreator/__pycache__/__init__.cpython-39.pyc create mode 100644 pylibraries/ttkcreator/__pycache__/__main__.cpython-39.pyc diff --git a/PepDeb32/PepProPixMaps b/PepDeb32/PepProPixMaps new file mode 120000 index 0000000..6bc2f9d --- /dev/null +++ b/PepDeb32/PepProPixMaps @@ -0,0 +1 @@ +../PepProPixMaps \ No newline at end of file diff --git a/PepDeb32/PepProTools b/PepDeb32/PepProTools new file mode 120000 index 0000000..5004ced --- /dev/null +++ b/PepDeb32/PepProTools @@ -0,0 +1 @@ +../PepProTools \ No newline at end of file diff --git a/PepDeb32/pepapplication b/PepDeb32/pepapplication new file mode 120000 index 0000000..95cfb79 --- /dev/null +++ b/PepDeb32/pepapplication @@ -0,0 +1 @@ +../pepapplication \ No newline at end of file diff --git a/PepDeb32/pepbld.sh b/PepDeb32/pepbld.sh index f14fb07..5c8237a 100755 --- a/PepDeb32/pepbld.sh +++ b/PepDeb32/pepbld.sh @@ -12,7 +12,7 @@ uchinanchu="$(pwd)" [ -e fusato ] && [ ! -d fusato ] && rm -f fusato || [ ! -e fusato ] && mkdir fusato cd fusato umount $(mount | grep "${PWD}/chroot" | tac | cut -f3 -d" ") 2>/dev/null -for i in ./* ./.build ; do [ $i = ./cache ] && continue || rm -rf $i ; done +for i in * .build ; do [ $i = cache ] && continue || rm -rf $i ; done # Defines Live Build settings @@ -20,7 +20,6 @@ for i in ./* ./.build ; do [ $i = ./cache ] && continue || rm -rf $i ; done lb config noauto \ --binary-images iso-hybrid \ --architectures i386 \ - --linux-flavours 686 \ --distribution bookworm \ --archive-areas "main contrib non-free non-free-firmware" \ --firmware-chroot false \ @@ -37,33 +36,145 @@ lb config noauto \ --win32-loader false \ --debian-installer-preseedfile preseed.cfg \ - "${@}" + "${@}" + +# Packages to be stored in /pool but not installed in the OS . +echo "# These packages are available to the installer. +linux-image-686 +linux-image-686-pae +hdmi2usb-fx2-firmware +firmware-ath9k-htc +amd64-microcode +atmel-firmware +bluez-firmware +dahdi-firmware-nonfree +firmware-ast +firmware-amd-graphics +firmware-atheros +firmware-bnx2 +firmware-bnx2x +firmware-brcm80211 +firmware-cavium +firmware-intel-sound +firmware-ipw2x00 +firmware-ivtv +firmware-iwlwifi +firmware-libertas +firmware-misc-nonfree +firmware-myricom +firmware-netronome +firmware-qcom-soc +firmware-qlogic +firmware-realtek +firmware-samsung +firmware-siano +firmware-ti-connectivity +firmware-sof-signed +intel-microcode +firmware-nvidia-tesla-gsp +firmware-nvidia-tesla-gsp +firmware-nvidia-gsp +firmware-nvidia-gsp +raspi-firmware +firmware-realtek-rtl8723cs-bt +firmware-zd1211 +accountsservice +acpi +acpid +adduser +alsa-utils +apt +apt-utils +at-spi2-core +avahi-daemon +base-files +base-passwd +bash +bind9-host +bsdmainutils +bsdutils +busybox +bzip2 +ca-certificates +console-setup +consolekit +coreutils +cpio +cron +curl +dash +dbus +debconf +debconf-i18n +debian-archive-keyring +debianutils +desktop-file-utils +dhcp-client +dhcp-common +dialog +diffutils +dmidecode +dmsetup +dosfstools +dpkg +e2fsprogs +eject +exim4-base +exim4-config +exim4-daemon-light +file +findutils +fontconfig-config + +" > $uchinanchu/fusato/config/package-lists/installer.list.binary + # Setup the installer structure mkdir -p $uchinanchu/fusato/config/includes.installer mkdir -p $uchinanchu/fusato/config/includes.installer/etc mkdir -p $uchinanchu/fusato/config/includes.installer/preseed +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/repos +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/scripts +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/sources-final +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/keyrings +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/apps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/database +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/tools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/protools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/polkit +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/conf +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/py +mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/includes.binary mkdir -p $uchinanchu/fusato/config/includes.binary/install -mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/hooks/normal #cp $uchinanchu/peprepo/* $uchinanchu/fusato/config/archives cp $uchinanchu/pepinstaller/preseed/preseed.cfg $uchinanchu/fusato/config/includes.installer -cp $uchinanchu/pepissue/* $uchinanchu/fusato/config/includes.installer/etc cp $uchinanchu/pephooks/normal/* $uchinanchu/fusato/config/hooks/normal -cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed +cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed/scripts +cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed/sources-final +cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed/keyrings +cp $uchinanchu/polkit/* $uchinanchu/fusato/config/includes.installer/preseed/polkit +cp $uchinanchu/pepapplication/* $uchinanchu/fusato/config/includes.installer/preseed/apps +cp $uchinanchu/pepdatabase/* $uchinanchu/fusato/config/includes.installer/preseed/database +cp $uchinanchu/PepProPixMaps/* $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +cp $uchinanchu/pepconf/* $uchinanchu/fusato/config/includes.installer/preseed/conf +cp $uchinanchu/pmostools/* $uchinanchu/fusato/config/includes.installer/preseed/tools +cp $uchinanchu/PepProTools/* $uchinanchu/fusato/config/includes.installer/preseed/protools -# Copy recursive files and sub-directories, containing symlinks. +# Copy recursive files and sub-directories cp -r $uchinanchu/peploadersplash/boot $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/peploadersplash/isolinux $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/pepinstaller/graphics $uchinanchu/fusato/config/includes.installer/usr/share cp -r $uchinanchu/pepinstaller/themes $uchinanchu/fusato/config/includes.installer/usr/share -cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed -cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed +cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed/grub +cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +cp -r $uchinanchu/pylibraries/* $uchinanchu/fusato/config/includes.installer/preseed/py lb build diff --git a/PepDeb32/pepconf b/PepDeb32/pepconf new file mode 120000 index 0000000..fd5e87f --- /dev/null +++ b/PepDeb32/pepconf @@ -0,0 +1 @@ +../pepconf \ No newline at end of file diff --git a/PepDeb32/pepdatabase b/PepDeb32/pepdatabase new file mode 120000 index 0000000..c1bb31f --- /dev/null +++ b/PepDeb32/pepdatabase @@ -0,0 +1 @@ +../pepdatabase \ No newline at end of file diff --git a/PepDeb32/pepissue b/PepDeb32/pepissue deleted file mode 120000 index f386cca..0000000 --- a/PepDeb32/pepissue +++ /dev/null @@ -1 +0,0 @@ -../pepissue \ No newline at end of file diff --git a/PepDeb32/pmostools b/PepDeb32/pmostools new file mode 120000 index 0000000..2141b89 --- /dev/null +++ b/PepDeb32/pmostools @@ -0,0 +1 @@ +../pmostools \ No newline at end of file diff --git a/PepDeb32/polkit b/PepDeb32/polkit new file mode 120000 index 0000000..7ae742c --- /dev/null +++ b/PepDeb32/polkit @@ -0,0 +1 @@ +../polkit \ No newline at end of file diff --git a/PepDeb32/pylibraries b/PepDeb32/pylibraries new file mode 120000 index 0000000..694e1d7 --- /dev/null +++ b/PepDeb32/pylibraries @@ -0,0 +1 @@ +../pylibraries \ No newline at end of file diff --git a/PepDeb64/PepProPixMaps b/PepDeb64/PepProPixMaps new file mode 120000 index 0000000..6bc2f9d --- /dev/null +++ b/PepDeb64/PepProPixMaps @@ -0,0 +1 @@ +../PepProPixMaps \ No newline at end of file diff --git a/PepDeb64/PepProTools b/PepDeb64/PepProTools new file mode 120000 index 0000000..5004ced --- /dev/null +++ b/PepDeb64/PepProTools @@ -0,0 +1 @@ +../PepProTools \ No newline at end of file diff --git a/PepDeb64/pepapplication b/PepDeb64/pepapplication new file mode 120000 index 0000000..95cfb79 --- /dev/null +++ b/PepDeb64/pepapplication @@ -0,0 +1 @@ +../pepapplication \ No newline at end of file diff --git a/PepDeb64/pepbld.sh b/PepDeb64/pepbld.sh index 43801fb..44e4e4b 100755 --- a/PepDeb64/pepbld.sh +++ b/PepDeb64/pepbld.sh @@ -12,7 +12,7 @@ uchinanchu="$(pwd)" [ -e fusato ] && [ ! -d fusato ] && rm -f fusato || [ ! -e fusato ] && mkdir fusato cd fusato umount $(mount | grep "${PWD}/chroot" | tac | cut -f3 -d" ") 2>/dev/null -for i in ./* ./.build ; do [ $i = ./cache ] && continue || rm -rf $i ; done +for i in * .build ; do [ $i = cache ] && continue || rm -rf $i ; done # Defines Live Build settings @@ -20,7 +20,6 @@ for i in ./* ./.build ; do [ $i = ./cache ] && continue || rm -rf $i ; done lb config noauto \ --binary-images iso-hybrid \ --architectures amd64 \ - --linux-flavours amd64 \ --distribution bookworm \ --archive-areas "main contrib non-free non-free-firmware" \ --firmware-chroot false \ @@ -37,33 +36,144 @@ lb config noauto \ --win32-loader false \ --debian-installer-preseedfile preseed.cfg \ - "${@}" + "${@}" + +# Packages to be stored in /pool but not installed in the OS . +echo "# These packages are available to the installer. +linux-image-amd64 +hdmi2usb-fx2-firmware +firmware-ath9k-htc +amd64-microcode +atmel-firmware +bluez-firmware +dahdi-firmware-nonfree +firmware-ast +firmware-amd-graphics +firmware-atheros +firmware-bnx2 +firmware-bnx2x +firmware-brcm80211 +firmware-cavium +firmware-intel-sound +firmware-ipw2x00 +firmware-ivtv +firmware-iwlwifi +firmware-libertas +firmware-misc-nonfree +firmware-myricom +firmware-netronome +firmware-qcom-soc +firmware-qlogic +firmware-realtek +firmware-samsung +firmware-siano +firmware-ti-connectivity +firmware-sof-signed +intel-microcode +firmware-nvidia-tesla-gsp +firmware-nvidia-tesla-gsp +firmware-nvidia-gsp +firmware-nvidia-gsp +raspi-firmware +firmware-realtek-rtl8723cs-bt +firmware-zd1211 +accountsservice +acpi +acpid +adduser +alsa-utils +apt +apt-utils +at-spi2-core +avahi-daemon +base-files +base-passwd +bash +bind9-host +bsdmainutils +bsdutils +busybox +bzip2 +ca-certificates +console-setup +consolekit +coreutils +cpio +cron +curl +dash +dbus +debconf +debconf-i18n +debian-archive-keyring +debianutils +desktop-file-utils +dhcp-client +dhcp-common +dialog +diffutils +dmidecode +dmsetup +dosfstools +dpkg +e2fsprogs +eject +exim4-base +exim4-config +exim4-daemon-light +file +findutils +fontconfig-config + +" > $uchinanchu/fusato/config/package-lists/installer.list.binary + # Setup the installer structure mkdir -p $uchinanchu/fusato/config/includes.installer mkdir -p $uchinanchu/fusato/config/includes.installer/etc mkdir -p $uchinanchu/fusato/config/includes.installer/preseed +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/repos +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/scripts +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/sources-final +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/keyrings +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/apps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/database +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/tools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/protools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/polkit +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/conf +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/py +mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/includes.binary mkdir -p $uchinanchu/fusato/config/includes.binary/install -mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/hooks/normal #cp $uchinanchu/peprepo/* $uchinanchu/fusato/config/archives cp $uchinanchu/pepinstaller/preseed/preseed.cfg $uchinanchu/fusato/config/includes.installer -cp $uchinanchu/pepissue/* $uchinanchu/fusato/config/includes.installer/etc cp $uchinanchu/pephooks/normal/* $uchinanchu/fusato/config/hooks/normal -cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed +cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed/scripts +cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed/sources-final +cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed/keyrings +cp $uchinanchu/polkit/* $uchinanchu/fusato/config/includes.installer/preseed/polkit +cp $uchinanchu/pepapplication/* $uchinanchu/fusato/config/includes.installer/preseed/apps +cp $uchinanchu/pepdatabase/* $uchinanchu/fusato/config/includes.installer/preseed/database +cp $uchinanchu/PepProPixMaps/* $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +cp $uchinanchu/pepconf/* $uchinanchu/fusato/config/includes.installer/preseed/conf +cp $uchinanchu/pmostools/* $uchinanchu/fusato/config/includes.installer/preseed/tools +cp $uchinanchu/PepProTools/* $uchinanchu/fusato/config/includes.installer/preseed/protools -# Copy recursive files and sub-directories, containing symlinks. +# Copy recursive files and sub-directories cp -r $uchinanchu/peploadersplash/boot $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/peploadersplash/isolinux $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/pepinstaller/graphics $uchinanchu/fusato/config/includes.installer/usr/share cp -r $uchinanchu/pepinstaller/themes $uchinanchu/fusato/config/includes.installer/usr/share -cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed -cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed +cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed/grub +cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +cp -r $uchinanchu/pylibraries/* $uchinanchu/fusato/config/includes.installer/preseed/py lb build diff --git a/PepDeb64/pepconf b/PepDeb64/pepconf new file mode 120000 index 0000000..fd5e87f --- /dev/null +++ b/PepDeb64/pepconf @@ -0,0 +1 @@ +../pepconf \ No newline at end of file diff --git a/PepDeb64/pepdatabase b/PepDeb64/pepdatabase new file mode 120000 index 0000000..c1bb31f --- /dev/null +++ b/PepDeb64/pepdatabase @@ -0,0 +1 @@ +../pepdatabase \ No newline at end of file diff --git a/PepDeb64/pepissue b/PepDeb64/pepissue deleted file mode 120000 index f386cca..0000000 --- a/PepDeb64/pepissue +++ /dev/null @@ -1 +0,0 @@ -../pepissue \ No newline at end of file diff --git a/PepDeb64/pmostools b/PepDeb64/pmostools new file mode 120000 index 0000000..2141b89 --- /dev/null +++ b/PepDeb64/pmostools @@ -0,0 +1 @@ +../pmostools \ No newline at end of file diff --git a/PepDeb64/polkit b/PepDeb64/polkit new file mode 120000 index 0000000..7ae742c --- /dev/null +++ b/PepDeb64/polkit @@ -0,0 +1 @@ +../polkit \ No newline at end of file diff --git a/PepDeb64/pylibraries b/PepDeb64/pylibraries new file mode 120000 index 0000000..694e1d7 --- /dev/null +++ b/PepDeb64/pylibraries @@ -0,0 +1 @@ +../pylibraries \ No newline at end of file diff --git a/PepDeb_arm64/PepProPixMaps b/PepDeb_arm64/PepProPixMaps new file mode 120000 index 0000000..6bc2f9d --- /dev/null +++ b/PepDeb_arm64/PepProPixMaps @@ -0,0 +1 @@ +../PepProPixMaps \ No newline at end of file diff --git a/PepDeb_arm64/PepProTools b/PepDeb_arm64/PepProTools new file mode 120000 index 0000000..5004ced --- /dev/null +++ b/PepDeb_arm64/PepProTools @@ -0,0 +1 @@ +../PepProTools \ No newline at end of file diff --git a/PepDeb_arm64/pepapplication b/PepDeb_arm64/pepapplication new file mode 120000 index 0000000..95cfb79 --- /dev/null +++ b/PepDeb_arm64/pepapplication @@ -0,0 +1 @@ +../pepapplication \ No newline at end of file diff --git a/PepDeb_arm64/pepbld.sh b/PepDeb_arm64/pepbld.sh index d72e9e5..37441a7 100755 --- a/PepDeb_arm64/pepbld.sh +++ b/PepDeb_arm64/pepbld.sh @@ -41,31 +41,142 @@ lb config noauto \ "${@}" +# Packages to be stored in /pool but not installed in the OS . +echo "# These packages are available to the installer. +linux-image-arm64 +hdmi2usb-fx2-firmware +firmware-ath9k-htc +amd64-microcode +atmel-firmware +bluez-firmware +dahdi-firmware-nonfree +firmware-ast +firmware-amd-graphics +firmware-atheros +firmware-bnx2 +firmware-bnx2x +firmware-brcm80211 +firmware-cavium +firmware-intel-sound +firmware-ipw2x00 +firmware-ivtv +firmware-iwlwifi +firmware-libertas +firmware-misc-nonfree +firmware-myricom +firmware-netronome +firmware-qcom-soc +firmware-qlogic +firmware-realtek +firmware-samsung +firmware-siano +firmware-ti-connectivity +firmware-sof-signed +intel-microcode +firmware-nvidia-tesla-gsp +firmware-nvidia-tesla-gsp +firmware-nvidia-gsp +firmware-nvidia-gsp +raspi-firmware +firmware-realtek-rtl8723cs-bt +firmware-zd1211 +accountsservice +acpi +acpid +adduser +alsa-utils +apt +apt-utils +at-spi2-core +avahi-daemon +base-files +base-passwd +bash +bind9-host +bsdmainutils +bsdutils +busybox +bzip2 +ca-certificates +console-setup +consolekit +coreutils +cpio +cron +curl +dash +dbus +debconf +debconf-i18n +debian-archive-keyring +debianutils +desktop-file-utils +dhcp-client +dhcp-common +dialog +diffutils +dmidecode +dmsetup +dosfstools +dpkg +e2fsprogs +eject +exim4-base +exim4-config +exim4-daemon-light +file +findutils +fontconfig-config + +" > $uchinanchu/fusato/config/package-lists/installer.list.binary + + # Setup the installer structure mkdir -p $uchinanchu/fusato/config/includes.installer mkdir -p $uchinanchu/fusato/config/includes.installer/etc mkdir -p $uchinanchu/fusato/config/includes.installer/preseed +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/repos +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/scripts +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/sources-final +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/keyrings +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/apps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/database +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/tools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/protools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/polkit +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/conf +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/py +mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/includes.binary mkdir -p $uchinanchu/fusato/config/includes.binary/install -mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/hooks/normal #cp $uchinanchu/peprepo/* $uchinanchu/fusato/config/archives cp $uchinanchu/pepinstaller/preseed/preseed.cfg $uchinanchu/fusato/config/includes.installer -cp $uchinanchu/pepissue/* $uchinanchu/fusato/config/includes.installer/etc cp $uchinanchu/pephooks/normal/* $uchinanchu/fusato/config/hooks/normal -cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed +cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed/scripts +cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed/sources-final +cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed/keyrings +cp $uchinanchu/polkit/* $uchinanchu/fusato/config/includes.installer/preseed/polkit +cp $uchinanchu/pepapplication/* $uchinanchu/fusato/config/includes.installer/preseed/apps +cp $uchinanchu/pepdatabase/* $uchinanchu/fusato/config/includes.installer/preseed/database +cp $uchinanchu/PepProPixMaps/* $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +cp $uchinanchu/pepconf/* $uchinanchu/fusato/config/includes.installer/preseed/conf +cp $uchinanchu/pmostools/* $uchinanchu/fusato/config/includes.installer/preseed/tools +cp $uchinanchu/PepProTools/* $uchinanchu/fusato/config/includes.installer/preseed/protools -# Copy recursive files and sub-directories, containing symlinks. +# Copy recursive files and sub-directories cp -r $uchinanchu/peploadersplash/boot $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/peploadersplash/isolinux $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/pepinstaller/graphics $uchinanchu/fusato/config/includes.installer/usr/share cp -r $uchinanchu/pepinstaller/themes $uchinanchu/fusato/config/includes.installer/usr/share -cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed -cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed +cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed/grub +cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +cp -r $uchinanchu/pylibraries/* $uchinanchu/fusato/config/includes.installer/preseed/py lb build diff --git a/PepDeb_arm64/pepconf b/PepDeb_arm64/pepconf new file mode 120000 index 0000000..fd5e87f --- /dev/null +++ b/PepDeb_arm64/pepconf @@ -0,0 +1 @@ +../pepconf \ No newline at end of file diff --git a/PepDeb_arm64/pepdatabase b/PepDeb_arm64/pepdatabase new file mode 120000 index 0000000..c1bb31f --- /dev/null +++ b/PepDeb_arm64/pepdatabase @@ -0,0 +1 @@ +../pepdatabase \ No newline at end of file diff --git a/PepDeb_arm64/pepissue b/PepDeb_arm64/pepissue deleted file mode 120000 index f386cca..0000000 --- a/PepDeb_arm64/pepissue +++ /dev/null @@ -1 +0,0 @@ -../pepissue \ No newline at end of file diff --git a/PepDeb_arm64/pmostools b/PepDeb_arm64/pmostools new file mode 120000 index 0000000..2141b89 --- /dev/null +++ b/PepDeb_arm64/pmostools @@ -0,0 +1 @@ +../pmostools \ No newline at end of file diff --git a/PepDeb_arm64/polkit b/PepDeb_arm64/polkit new file mode 120000 index 0000000..7ae742c --- /dev/null +++ b/PepDeb_arm64/polkit @@ -0,0 +1 @@ +../polkit \ No newline at end of file diff --git a/PepDeb_arm64/pylibraries b/PepDeb_arm64/pylibraries new file mode 120000 index 0000000..694e1d7 --- /dev/null +++ b/PepDeb_arm64/pylibraries @@ -0,0 +1 @@ +../pylibraries \ No newline at end of file diff --git a/PepDev32/PepProPixMaps b/PepDev32/PepProPixMaps new file mode 120000 index 0000000..6bc2f9d --- /dev/null +++ b/PepDev32/PepProPixMaps @@ -0,0 +1 @@ +../PepProPixMaps \ No newline at end of file diff --git a/PepDev32/PepProTools b/PepDev32/PepProTools new file mode 120000 index 0000000..5004ced --- /dev/null +++ b/PepDev32/PepProTools @@ -0,0 +1 @@ +../PepProTools \ No newline at end of file diff --git a/PepDev32/pepapplication b/PepDev32/pepapplication new file mode 120000 index 0000000..95cfb79 --- /dev/null +++ b/PepDev32/pepapplication @@ -0,0 +1 @@ +../pepapplication \ No newline at end of file diff --git a/PepDev32/pepbld.sh b/PepDev32/pepbld.sh index 15a6454..294de90 100755 --- a/PepDev32/pepbld.sh +++ b/PepDev32/pepbld.sh @@ -51,32 +51,143 @@ lb config noauto \ "${@}" +# Packages to be stored in /pool but not installed in the OS . +echo "# These packages are available to the installer. +linux-image-686 +linux-image-686-pae +hdmi2usb-fx2-firmware +firmware-ath9k-htc +amd64-microcode +atmel-firmware +bluez-firmware +dahdi-firmware-nonfree +firmware-ast +firmware-amd-graphics +firmware-atheros +firmware-bnx2 +firmware-bnx2x +firmware-brcm80211 +firmware-cavium +firmware-intel-sound +firmware-ipw2x00 +firmware-ivtv +firmware-iwlwifi +firmware-libertas +firmware-misc-nonfree +firmware-myricom +firmware-netronome +firmware-qcom-soc +firmware-qlogic +firmware-realtek +firmware-samsung +firmware-siano +firmware-ti-connectivity +firmware-sof-signed +intel-microcode +firmware-nvidia-tesla-gsp +firmware-nvidia-tesla-gsp +firmware-nvidia-gsp +firmware-nvidia-gsp +raspi-firmware +firmware-realtek-rtl8723cs-bt +firmware-zd1211 +accountsservice +acpi +acpid +adduser +alsa-utils +apt +apt-utils +at-spi2-core +avahi-daemon +base-files +base-passwd +bash +bind9-host +bsdmainutils +bsdutils +busybox +bzip2 +ca-certificates +console-setup +consolekit +coreutils +cpio +cron +curl +dash +dbus +debconf +debconf-i18n +debian-archive-keyring +debianutils +desktop-file-utils +dhcp-client +dhcp-common +dialog +diffutils +dmidecode +dmsetup +dosfstools +dpkg +e2fsprogs +eject +exim4-base +exim4-config +exim4-daemon-light +file +findutils +fontconfig-config + +" > $uchinanchu/fusato/config/package-lists/installer.list.binary + + # Setup the installer structure mkdir -p $uchinanchu/fusato/config/includes.installer mkdir -p $uchinanchu/fusato/config/includes.installer/etc mkdir -p $uchinanchu/fusato/config/includes.installer/preseed +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/repos +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/scripts +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/sources-final +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/keyrings +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/apps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/database +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/tools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/protools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/polkit +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/conf +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/py +mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/includes.binary mkdir -p $uchinanchu/fusato/config/includes.binary/install -mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/hooks/normal #cp $uchinanchu/peprepo/* $uchinanchu/fusato/config/archives cp $uchinanchu/pepinstaller/preseed/preseed.cfg $uchinanchu/fusato/config/includes.installer -cp $uchinanchu/pepissue/* $uchinanchu/fusato/config/includes.installer/etc cp $uchinanchu/pephooks/normal/* $uchinanchu/fusato/config/hooks/normal -cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepinfo/* $uchinanchu/fusato/config/includes.installer/preseed +cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed/scripts +cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed/sources-final +cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed/keyrings +cp $uchinanchu/polkit/* $uchinanchu/fusato/config/includes.installer/preseed/polkit +cp $uchinanchu/pepapplication/* $uchinanchu/fusato/config/includes.installer/preseed/apps +cp $uchinanchu/pepdatabase/* $uchinanchu/fusato/config/includes.installer/preseed/database +cp $uchinanchu/PepProPixMaps/* $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +cp $uchinanchu/pepconf/* $uchinanchu/fusato/config/includes.installer/preseed/conf +cp $uchinanchu/pmostools/* $uchinanchu/fusato/config/includes.installer/preseed/tools +cp $uchinanchu/PepProTools/* $uchinanchu/fusato/config/includes.installer/preseed/protools -# Copy recursive files and sub-directories, containing symlinks. +# Copy recursive files and sub-directories cp -r $uchinanchu/peploadersplash/boot $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/peploadersplash/isolinux $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/pepinstaller/graphics $uchinanchu/fusato/config/includes.installer/usr/share cp -r $uchinanchu/pepinstaller/themes $uchinanchu/fusato/config/includes.installer/usr/share -cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed -cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed +cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed/grub +cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +cp -r $uchinanchu/pylibraries/* $uchinanchu/fusato/config/includes.installer/preseed/py lb build diff --git a/PepDev32/pepconf b/PepDev32/pepconf new file mode 120000 index 0000000..fd5e87f --- /dev/null +++ b/PepDev32/pepconf @@ -0,0 +1 @@ +../pepconf \ No newline at end of file diff --git a/PepDev32/pepdatabase b/PepDev32/pepdatabase new file mode 120000 index 0000000..c1bb31f --- /dev/null +++ b/PepDev32/pepdatabase @@ -0,0 +1 @@ +../pepdatabase \ No newline at end of file diff --git a/PepDev32/pepissue b/PepDev32/pepissue deleted file mode 120000 index f386cca..0000000 --- a/PepDev32/pepissue +++ /dev/null @@ -1 +0,0 @@ -../pepissue \ No newline at end of file diff --git a/PepDev32/pepscripts/copy-files-to-chroot.sh b/PepDev32/pepscripts/copy-files-to-chroot.sh index 1482350..5d6e7ad 100755 --- a/PepDev32/pepscripts/copy-files-to-chroot.sh +++ b/PepDev32/pepscripts/copy-files-to-chroot.sh @@ -4,11 +4,11 @@ # # SPDX-FileCopyrightText: 2023 PeppemrintOS Team (peppermintosteam@proton.me) -# This script copies a file to target and runs commands during the Debian installation process. +# This script copies a file to the chroot and runs commands during the Debian installation process. # Copy files to the chroot directory -echo "Copying files to Target..." +echo "Copying files to chroot..." cp /preseed/grub /target/etc/default cp /preseed/multimedia.list /target/etc/apt/sources.list.d cp /preseed/peppermint.list /target/etc/apt/sources.list.d @@ -17,8 +17,6 @@ cp /preseed/deb-multimedia-keyring.gpg /target/etc/apt/trusted.gpg.d cp /preseed/peppermint-keyring.gpg /target/etc/apt/trusted.gpg.d cp /preseed/deb-multimedia-keyring.gpg /target/usr/share/keyrings cp /preseed/peppermint-keyring.gpg /target/usr/share/keyrings -cp /preseed/Devuan.info /target//usr/share/python-apt/templates -cp /preseed/Devuan.mirrors /target//usr/share/python-apt/templates cp -r /preseed/themes /target/boot/grub # Run a commands in the chroot diff --git a/PepDev32/pmostools b/PepDev32/pmostools new file mode 120000 index 0000000..2141b89 --- /dev/null +++ b/PepDev32/pmostools @@ -0,0 +1 @@ +../pmostools \ No newline at end of file diff --git a/PepDev32/polkit b/PepDev32/polkit new file mode 120000 index 0000000..7ae742c --- /dev/null +++ b/PepDev32/polkit @@ -0,0 +1 @@ +../polkit \ No newline at end of file diff --git a/PepDev32/pylibraries b/PepDev32/pylibraries new file mode 120000 index 0000000..694e1d7 --- /dev/null +++ b/PepDev32/pylibraries @@ -0,0 +1 @@ +../pylibraries \ No newline at end of file diff --git a/PepDev64/PepProPixMaps b/PepDev64/PepProPixMaps new file mode 120000 index 0000000..6bc2f9d --- /dev/null +++ b/PepDev64/PepProPixMaps @@ -0,0 +1 @@ +../PepProPixMaps \ No newline at end of file diff --git a/PepDev64/PepProTools b/PepDev64/PepProTools new file mode 120000 index 0000000..5004ced --- /dev/null +++ b/PepDev64/PepProTools @@ -0,0 +1 @@ +../PepProTools \ No newline at end of file diff --git a/PepDev64/pepapplication b/PepDev64/pepapplication new file mode 120000 index 0000000..95cfb79 --- /dev/null +++ b/PepDev64/pepapplication @@ -0,0 +1 @@ +../pepapplication \ No newline at end of file diff --git a/PepDev64/pepbld.sh b/PepDev64/pepbld.sh index 7af7d23..e01ada7 100755 --- a/PepDev64/pepbld.sh +++ b/PepDev64/pepbld.sh @@ -50,31 +50,142 @@ lb config noauto \ "${@}" +# Packages to be stored in /pool but not installed in the OS . +echo "# These packages are available to the installer. +linux-image-amd64 +hdmi2usb-fx2-firmware +firmware-ath9k-htc +amd64-microcode +atmel-firmware +bluez-firmware +dahdi-firmware-nonfree +firmware-ast +firmware-amd-graphics +firmware-atheros +firmware-bnx2 +firmware-bnx2x +firmware-brcm80211 +firmware-cavium +firmware-intel-sound +firmware-ipw2x00 +firmware-ivtv +firmware-iwlwifi +firmware-libertas +firmware-misc-nonfree +firmware-myricom +firmware-netronome +firmware-qcom-soc +firmware-qlogic +firmware-realtek +firmware-samsung +firmware-siano +firmware-ti-connectivity +firmware-sof-signed +intel-microcode +firmware-nvidia-tesla-gsp +firmware-nvidia-tesla-gsp +firmware-nvidia-gsp +firmware-nvidia-gsp +raspi-firmware +firmware-realtek-rtl8723cs-bt +firmware-zd1211 +accountsservice +acpi +acpid +adduser +alsa-utils +apt +apt-utils +at-spi2-core +avahi-daemon +base-files +base-passwd +bash +bind9-host +bsdmainutils +bsdutils +busybox +bzip2 +ca-certificates +console-setup +consolekit +coreutils +cpio +cron +curl +dash +dbus +debconf +debconf-i18n +debian-archive-keyring +debianutils +desktop-file-utils +dhcp-client +dhcp-common +dialog +diffutils +dmidecode +dmsetup +dosfstools +dpkg +e2fsprogs +eject +exim4-base +exim4-config +exim4-daemon-light +file +findutils +fontconfig-config + +" > $uchinanchu/fusato/config/package-lists/installer.list.binary + + # Setup the installer structure mkdir -p $uchinanchu/fusato/config/includes.installer mkdir -p $uchinanchu/fusato/config/includes.installer/etc mkdir -p $uchinanchu/fusato/config/includes.installer/preseed +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/repos +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/scripts +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/sources-final +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/keyrings +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/apps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/database +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/tools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/protools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/polkit +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/conf +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/py +mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/includes.binary mkdir -p $uchinanchu/fusato/config/includes.binary/install -mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/hooks/normal #cp $uchinanchu/peprepo/* $uchinanchu/fusato/config/archives cp $uchinanchu/pepinstaller/preseed/preseed.cfg $uchinanchu/fusato/config/includes.installer -cp $uchinanchu/pepissue/* $uchinanchu/fusato/config/includes.installer/etc cp $uchinanchu/pephooks/normal/* $uchinanchu/fusato/config/hooks/normal -cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepinfo/* $uchinanchu/fusato/config/includes.installer/preseed +cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed/scripts +cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed/sources-final +cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed/keyrings +cp $uchinanchu/polkit/* $uchinanchu/fusato/config/includes.installer/preseed/polkit +cp $uchinanchu/pepapplication/* $uchinanchu/fusato/config/includes.installer/preseed/apps +cp $uchinanchu/pepdatabase/* $uchinanchu/fusato/config/includes.installer/preseed/database +cp $uchinanchu/PepProPixMaps/* $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +cp $uchinanchu/pepconf/* $uchinanchu/fusato/config/includes.installer/preseed/conf +cp $uchinanchu/pmostools/* $uchinanchu/fusato/config/includes.installer/preseed/tools +cp $uchinanchu/PepProTools/* $uchinanchu/fusato/config/includes.installer/preseed/protools -# Copy recursive files and sub-directories, containing symlinks. +# Copy recursive files and sub-directories cp -r $uchinanchu/peploadersplash/boot $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/peploadersplash/isolinux $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/pepinstaller/graphics $uchinanchu/fusato/config/includes.installer/usr/share cp -r $uchinanchu/pepinstaller/themes $uchinanchu/fusato/config/includes.installer/usr/share -cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed -cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed +cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed/grub +cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +cp -r $uchinanchu/pylibraries/* $uchinanchu/fusato/config/includes.installer/preseed/py + lb build diff --git a/PepDev64/pepconf b/PepDev64/pepconf new file mode 120000 index 0000000..fd5e87f --- /dev/null +++ b/PepDev64/pepconf @@ -0,0 +1 @@ +../pepconf \ No newline at end of file diff --git a/PepDev64/pepdatabase b/PepDev64/pepdatabase new file mode 120000 index 0000000..c1bb31f --- /dev/null +++ b/PepDev64/pepdatabase @@ -0,0 +1 @@ +../pepdatabase \ No newline at end of file diff --git a/PepDev64/pepissue b/PepDev64/pepissue deleted file mode 120000 index f386cca..0000000 --- a/PepDev64/pepissue +++ /dev/null @@ -1 +0,0 @@ -../pepissue \ No newline at end of file diff --git a/PepDev64/pepscripts/copy-files-to-chroot.sh b/PepDev64/pepscripts/copy-files-to-chroot.sh index 1482350..5d6e7ad 100755 --- a/PepDev64/pepscripts/copy-files-to-chroot.sh +++ b/PepDev64/pepscripts/copy-files-to-chroot.sh @@ -4,11 +4,11 @@ # # SPDX-FileCopyrightText: 2023 PeppemrintOS Team (peppermintosteam@proton.me) -# This script copies a file to target and runs commands during the Debian installation process. +# This script copies a file to the chroot and runs commands during the Debian installation process. # Copy files to the chroot directory -echo "Copying files to Target..." +echo "Copying files to chroot..." cp /preseed/grub /target/etc/default cp /preseed/multimedia.list /target/etc/apt/sources.list.d cp /preseed/peppermint.list /target/etc/apt/sources.list.d @@ -17,8 +17,6 @@ cp /preseed/deb-multimedia-keyring.gpg /target/etc/apt/trusted.gpg.d cp /preseed/peppermint-keyring.gpg /target/etc/apt/trusted.gpg.d cp /preseed/deb-multimedia-keyring.gpg /target/usr/share/keyrings cp /preseed/peppermint-keyring.gpg /target/usr/share/keyrings -cp /preseed/Devuan.info /target//usr/share/python-apt/templates -cp /preseed/Devuan.mirrors /target//usr/share/python-apt/templates cp -r /preseed/themes /target/boot/grub # Run a commands in the chroot diff --git a/PepDev64/pmostools b/PepDev64/pmostools new file mode 120000 index 0000000..2141b89 --- /dev/null +++ b/PepDev64/pmostools @@ -0,0 +1 @@ +../pmostools \ No newline at end of file diff --git a/PepDev64/polkit b/PepDev64/polkit new file mode 120000 index 0000000..7ae742c --- /dev/null +++ b/PepDev64/polkit @@ -0,0 +1 @@ +../polkit \ No newline at end of file diff --git a/PepDev64/pylibraries b/PepDev64/pylibraries new file mode 120000 index 0000000..694e1d7 --- /dev/null +++ b/PepDev64/pylibraries @@ -0,0 +1 @@ +../pylibraries \ No newline at end of file diff --git a/PepDev_arm64/PepProPixMaps b/PepDev_arm64/PepProPixMaps new file mode 120000 index 0000000..6bc2f9d --- /dev/null +++ b/PepDev_arm64/PepProPixMaps @@ -0,0 +1 @@ +../PepProPixMaps \ No newline at end of file diff --git a/PepDev_arm64/PepProTools b/PepDev_arm64/PepProTools new file mode 120000 index 0000000..5004ced --- /dev/null +++ b/PepDev_arm64/PepProTools @@ -0,0 +1 @@ +../PepProTools \ No newline at end of file diff --git a/PepDev_arm64/pepapplication b/PepDev_arm64/pepapplication new file mode 120000 index 0000000..95cfb79 --- /dev/null +++ b/PepDev_arm64/pepapplication @@ -0,0 +1 @@ +../pepapplication \ No newline at end of file diff --git a/PepDev_arm64/pepbld.sh b/PepDev_arm64/pepbld.sh index 7b0cac0..c849040 100755 --- a/PepDev_arm64/pepbld.sh +++ b/PepDev_arm64/pepbld.sh @@ -54,32 +54,142 @@ lb config noauto \ "${@}" +# Packages to be stored in /pool but not installed in the OS . +echo "# These packages are available to the installer. +linux-image-arm64 +hdmi2usb-fx2-firmware +firmware-ath9k-htc +amd64-microcode +atmel-firmware +bluez-firmware +dahdi-firmware-nonfree +firmware-ast +firmware-amd-graphics +firmware-atheros +firmware-bnx2 +firmware-bnx2x +firmware-brcm80211 +firmware-cavium +firmware-intel-sound +firmware-ipw2x00 +firmware-ivtv +firmware-iwlwifi +firmware-libertas +firmware-misc-nonfree +firmware-myricom +firmware-netronome +firmware-qcom-soc +firmware-qlogic +firmware-realtek +firmware-samsung +firmware-siano +firmware-ti-connectivity +firmware-sof-signed +intel-microcode +firmware-nvidia-tesla-gsp +firmware-nvidia-tesla-gsp +firmware-nvidia-gsp +firmware-nvidia-gsp +raspi-firmware +firmware-realtek-rtl8723cs-bt +firmware-zd1211 +accountsservice +acpi +acpid +adduser +alsa-utils +apt +apt-utils +at-spi2-core +avahi-daemon +base-files +base-passwd +bash +bind9-host +bsdmainutils +bsdutils +busybox +bzip2 +ca-certificates +console-setup +consolekit +coreutils +cpio +cron +curl +dash +dbus +debconf +debconf-i18n +debian-archive-keyring +debianutils +desktop-file-utils +dhcp-client +dhcp-common +dialog +diffutils +dmidecode +dmsetup +dosfstools +dpkg +e2fsprogs +eject +exim4-base +exim4-config +exim4-daemon-light +file +findutils +fontconfig-config + +" > $uchinanchu/fusato/config/package-lists/installer.list.binary + + # Setup the installer structure mkdir -p $uchinanchu/fusato/config/includes.installer mkdir -p $uchinanchu/fusato/config/includes.installer/etc mkdir -p $uchinanchu/fusato/config/includes.installer/preseed +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/repos +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/scripts +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/sources-final +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/keyrings +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/apps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/database +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/tools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/protools +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/polkit +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/conf +mkdir -p $uchinanchu/fusato/config/includes.installer/preseed/py +mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/includes.binary mkdir -p $uchinanchu/fusato/config/includes.binary/install -mkdir -p $uchinanchu/fusato/config/includes.installer/usr/share mkdir -p $uchinanchu/fusato/config/hooks/normal #cp $uchinanchu/peprepo/* $uchinanchu/fusato/config/archives cp $uchinanchu/pepinstaller/preseed/preseed.cfg $uchinanchu/fusato/config/includes.installer -cp $uchinanchu/pepissue/* $uchinanchu/fusato/config/includes.installer/etc cp $uchinanchu/pephooks/normal/* $uchinanchu/fusato/config/hooks/normal -cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed -cp $uchinanchu/pepinfo/* $uchinanchu/fusato/config/includes.installer/preseed +cp $uchinanchu/pepscripts/* $uchinanchu/fusato/config/includes.installer/preseed/scripts +cp $uchinanchu/pepsources/multimedia.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/peppermint.list $uchinanchu/fusato/config/includes.installer/preseed/repos +cp $uchinanchu/pepsources/sources.list $uchinanchu/fusato/config/includes.installer/preseed/sources-final +cp $uchinanchu/pepkeyrings/* $uchinanchu/fusato/config/includes.installer/preseed/keyrings +cp $uchinanchu/polkit/* $uchinanchu/fusato/config/includes.installer/preseed/polkit +cp $uchinanchu/pepapplication/* $uchinanchu/fusato/config/includes.installer/preseed/apps +cp $uchinanchu/pepdatabase/* $uchinanchu/fusato/config/includes.installer/preseed/database +cp $uchinanchu/PepProPixMaps/* $uchinanchu/fusato/config/includes.installer/preseed/pixmaps +cp $uchinanchu/pepconf/* $uchinanchu/fusato/config/includes.installer/preseed/conf +cp $uchinanchu/pmostools/* $uchinanchu/fusato/config/includes.installer/preseed/tools +cp $uchinanchu/PepProTools/* $uchinanchu/fusato/config/includes.installer/preseed/protools -# Copy recursive files and sub-directories, containing symlinks. +# Copy recursive files and sub-directories cp -r $uchinanchu/peploadersplash/boot $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/peploadersplash/isolinux $uchinanchu/fusato/config/includes.binary cp -r $uchinanchu/pepinstaller/graphics $uchinanchu/fusato/config/includes.installer/usr/share cp -r $uchinanchu/pepinstaller/themes $uchinanchu/fusato/config/includes.installer/usr/share -cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed -cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed +cp -r $uchinanchu/pepgrub/* $uchinanchu/fusato/config/includes.installer/preseed/grub +cp -r $uchinanchu/pepgrub/themes $uchinanchu/fusato/config/includes.installer/preseed/grub-themes +cp -r $uchinanchu/pylibraries/* $uchinanchu/fusato/config/includes.installer/preseed/py lb build diff --git a/PepDev_arm64/pepconf b/PepDev_arm64/pepconf new file mode 120000 index 0000000..fd5e87f --- /dev/null +++ b/PepDev_arm64/pepconf @@ -0,0 +1 @@ +../pepconf \ No newline at end of file diff --git a/PepDev_arm64/pepdatabase b/PepDev_arm64/pepdatabase new file mode 120000 index 0000000..c1bb31f --- /dev/null +++ b/PepDev_arm64/pepdatabase @@ -0,0 +1 @@ +../pepdatabase \ No newline at end of file diff --git a/PepDev_arm64/pepissue b/PepDev_arm64/pepissue deleted file mode 120000 index f386cca..0000000 --- a/PepDev_arm64/pepissue +++ /dev/null @@ -1 +0,0 @@ -../pepissue \ No newline at end of file diff --git a/PepDev_arm64/pepscripts/copy-files-to-chroot.sh b/PepDev_arm64/pepscripts/copy-files-to-chroot.sh index 1482350..5d6e7ad 100755 --- a/PepDev_arm64/pepscripts/copy-files-to-chroot.sh +++ b/PepDev_arm64/pepscripts/copy-files-to-chroot.sh @@ -4,11 +4,11 @@ # # SPDX-FileCopyrightText: 2023 PeppemrintOS Team (peppermintosteam@proton.me) -# This script copies a file to target and runs commands during the Debian installation process. +# This script copies a file to the chroot and runs commands during the Debian installation process. # Copy files to the chroot directory -echo "Copying files to Target..." +echo "Copying files to chroot..." cp /preseed/grub /target/etc/default cp /preseed/multimedia.list /target/etc/apt/sources.list.d cp /preseed/peppermint.list /target/etc/apt/sources.list.d @@ -17,8 +17,6 @@ cp /preseed/deb-multimedia-keyring.gpg /target/etc/apt/trusted.gpg.d cp /preseed/peppermint-keyring.gpg /target/etc/apt/trusted.gpg.d cp /preseed/deb-multimedia-keyring.gpg /target/usr/share/keyrings cp /preseed/peppermint-keyring.gpg /target/usr/share/keyrings -cp /preseed/Devuan.info /target//usr/share/python-apt/templates -cp /preseed/Devuan.mirrors /target//usr/share/python-apt/templates cp -r /preseed/themes /target/boot/grub # Run a commands in the chroot diff --git a/PepDev_arm64/pmostools b/PepDev_arm64/pmostools new file mode 120000 index 0000000..2141b89 --- /dev/null +++ b/PepDev_arm64/pmostools @@ -0,0 +1 @@ +../pmostools \ No newline at end of file diff --git a/PepDev_arm64/polkit b/PepDev_arm64/polkit new file mode 120000 index 0000000..7ae742c --- /dev/null +++ b/PepDev_arm64/polkit @@ -0,0 +1 @@ +../polkit \ No newline at end of file diff --git a/PepDev_arm64/pylibraries b/PepDev_arm64/pylibraries new file mode 120000 index 0000000..694e1d7 --- /dev/null +++ b/PepDev_arm64/pylibraries @@ -0,0 +1 @@ +../pylibraries \ No newline at end of file diff --git a/PepProPixMaps/ai.png b/PepProPixMaps/ai.png new file mode 100644 index 0000000000000000000000000000000000000000..55f99eee3b020827e9a2dbdb16492c24090d880c GIT binary patch literal 3654 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 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/dconf-editor.png b/PepProPixMaps/dconf-editor.png new file mode 100644 index 0000000000000000000000000000000000000000..9f4916f5d63587bff9272ba803aa8388c279bfa5 GIT binary patch literal 2465 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 KMNUMnLSTY#3L&@v literal 0 HcmV?d00001 diff --git a/PepProPixMaps/flat.png b/PepProPixMaps/flat.png new file mode 100644 index 0000000000000000000000000000000000000000..fdb2c030e7a3cca982b5b753b3ba6bcfff50746e GIT binary patch literal 9848 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 Wc|_I}^E>?j0000fgI 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 YZ=OLFt2(x;IsgCw07*qoM6N<$f~`g^0ssI2 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/gnome-software.png b/PepProPixMaps/gnome-software.png new file mode 100644 index 0000000000000000000000000000000000000000..a46ba6c04a31ba9744078fa832ed56981d8fb942 GIT binary patch literal 2963 zcmV;E3vBd>P)Z>@L2ncfBzf48|oS1{)G55YjkxiI5@)Nt8-WDg}w~5=f#b zR3TDSq|gV1cmUBx;XUmO5L&5;O2AOU-CjnTxqI8CWfgTPyV_KPd%Va;# zWQ?t{M8{h3u&4r zs;a6MFeuAvAP}hP?*97Unx_3-4Ml!GolF{2qoc3@$~h;JP#nj;AP|sOH?79?Z?8j=Bu7~HIUxj`b1=rhIY;BFdJquYyg2}i0}HY&*pxzG z#fo|?uTS9nn*&G_3PqM7=RDz6KnMZLve5LiCTN<5NF>6_j)AVOuA1vNuJ`ue>aUJQ zqeT@ELV$At(=xH9sR6PqOMpMLu34`~7sHv$busWp_lu}rX z<&@xftZh9eCTB4)Ft{KTl|bo_mNiWiro{jVJl3`j6BDzz)j!~P!3Ezu1tupaF)%QI zR4O$zJ3IT%xpU_}@l${RAeS#+{zGqX?<0({;Oy*dZ6FW`*45P+lu{d*giTOtP=9~FCP~t*R4S!!-n_YI$BrF)a`JFnfhd54uIpa|(0s8* z3elEIr7$-)i^<6msA?Focmm;22%4sWQtC|%Fbo58ckW;&IRkxe5|N5HqSZBsMyvf2 z35v+UHBDOwU<|;F?DD{J#u#-A>OOZ*XkX`?V|;uF1VBY)6#}a2z`-kk5CW1UK~+_R zLt&)vq%b`-j!-D+r@Um7Js2$I8~^MXC8ece5luV_HS z%0^UGR-wAO8j7O$3hy4Oq5uG%*!(1}T)B*O&CMk(?mf@VPrX4L1F$KTO8r%mB*km> zVz2@LRaKpgQ6}+oGMS<%$Ye5$3h#jyk3=Fn0dxVFbVJDTfZJ_;!n?go>vFTT$A`J;1{@FtCuTuAP~UKn>WEZ zFPQWF_ut2=RjaUL#}2f%w&KKz6HbxzuH~~H@#8`SfFy+Qb)y?je(BUt_?KUP85b{J zgr;e@di5%@*(@YULPtjjbX`X@8g-t(aNz>>@89plgBv#X;enCt#!_Ak++`;QAp{gf z!KqWHkW3~aguvk7Ag*1zhFB~HMNx3=+BE>c$&)8x7zX4VeXx&>8#f}G&Hhil4D}PJ z3=8BHmcxe+#7i%|gkUg;WHJd&(;RHq*482r2;l75vretB57RWUckf;YYNgh8 z{U=@zI6SA6wf&T!lp>u@L(?=c#&GGQ5-yY5MyIwn4h1=%9Shg zskFFBd9Gq};!?wY6w%ZAD*SA7*A| zKq*Bm7DGH9ho)(`aNzo|P)FgiLq@XRyMKvh+oJb4ml&z=RP6qS{gek2TxF%Uv<`t<1n z{^W;yKMzz_SMLJwPXJ?iblN+Sw{W?yX_|(kM~}iZO$-eU;mtSSL^vEqO-)V7eT|}8X6k##v5-qRrsTiK0;Sl7b+?$Kq8H`z*_lTzIOk|?0nJAc`vwrpA6oRLTbd-m)p=z$k&pxlP@YiLNZhNf6v z_CCiL!-^Fv@b=qpqp7J0hG95gZWv=Q3Dt;_tr!a}x8T{qnMfq^-d}A0w2WpR z(&=bwN6T~ANXKLGp-!1n;MZb{T! zYuNxG4nV>G*D!OI@6UZ+%KB}kkeuMEUtRc#2g@m8+V}^6{{rvRGV+qn?jryI002ov JPDHLkV1h>waAp7i literal 0 HcmV?d00001 diff --git a/PepProPixMaps/hblock.png b/PepProPixMaps/hblock.png new file mode 100644 index 0000000000000000000000000000000000000000..bbe750fabb96ccfc7ee1a7cde1cd6ee6e89817dc GIT binary patch literal 10886 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 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/hblockon.png b/PepProPixMaps/hblockon.png new file mode 100644 index 0000000000000000000000000000000000000000..395bd127d5b620970259af2f67f62abe5987b816 GIT binary patch literal 10790 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 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/install-debian.png b/PepProPixMaps/install-debian.png new file mode 100644 index 0000000000000000000000000000000000000000..6361d2d7a74879414748fc3152fe9395c4bf30a6 GIT binary patch literal 6292 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 KMNUMnLSTa0=NqvA literal 0 HcmV?d00001 diff --git a/PepProPixMaps/keyboard.png b/PepProPixMaps/keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..9cb1cb501cbfc1495f121200eb2ff655023777eb GIT binary patch literal 2111 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 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/logo.png b/PepProPixMaps/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c5d5c79ac2b98ba9a4cb41f44b50e618d3febe60 GIT binary patch literal 7252 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 JsanA*;(w(m=&1kz literal 0 HcmV?d00001 diff --git a/PepProPixMaps/luakit.png b/PepProPixMaps/luakit.png new file mode 100644 index 0000000000000000000000000000000000000000..ae6c53b15b06608bb271994f31a8208cc8f31444 GIT binary patch literal 12475 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 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/mouse.png b/PepProPixMaps/mouse.png new file mode 100644 index 0000000000000000000000000000000000000000..a0f50f7279bb3eef93d2d49896fb5cbaa1322a8e GIT binary patch literal 2531 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 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/network-wired.png b/PepProPixMaps/network-wired.png new file mode 100644 index 0000000000000000000000000000000000000000..e849106638096f71cd5df35bb8af27c0fa5cfb88 GIT binary patch literal 1505 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%0DN)xP$Pli* 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%0D}4-mQ(uZ+rC8$!&BZV|h|p(DrnE7}S069L#GX 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 Xk#>NE?R+t^tTqla$ literal 0 HcmV?d00001 diff --git a/PepProPixMaps/peppermint-hub-128.png b/PepProPixMaps/peppermint-hub-128.png new file mode 100644 index 0000000000000000000000000000000000000000..85b0ee6da87508a1deac86fdc008ed6e0705d2c0 GIT binary patch literal 17813 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 JS~26G{{^g(hj{=1 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/peppermint-hub.png b/PepProPixMaps/peppermint-hub.png new file mode 100644 index 0000000000000000000000000000000000000000..f984e7226228cfec494c6e74c9547ca7d58594c5 GIT binary patch literal 14485 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 KMNUMnLSTY4njBLA literal 0 HcmV?d00001 diff --git a/PepProPixMaps/peppermint-old.png b/PepProPixMaps/peppermint-old.png new file mode 100644 index 0000000000000000000000000000000000000000..4934a1c3889394df650617ccab3483e66e9e09c8 GIT binary patch literal 18648 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 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/peppermint-rim-128.png b/PepProPixMaps/peppermint-rim-128.png new file mode 100644 index 0000000000000000000000000000000000000000..ce08aebd6f99bd66aa29dd1025f214b5771eb378 GIT binary patch literal 18648 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 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/peppermint.png b/PepProPixMaps/peppermint.png new file mode 100644 index 0000000000000000000000000000000000000000..094e9f5dce0dd3a79507387b6ecca814d7d78980 GIT binary patch literal 18649 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йW0eyhPnID^{ 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 JPDHLkV1krPPL2Qo literal 0 HcmV?d00001 diff --git a/PepProPixMaps/snap.png b/PepProPixMaps/snap.png new file mode 100644 index 0000000000000000000000000000000000000000..19f88ce09a30360d0acdf519d401f4794fef141c GIT binary patch literal 13428 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 WQO3)9YP5s^0000X84_&Sj)@#( z3veVWN+3d!kPuR$M9M!vn1O^CASMhLGl3b(fJ7MtAxnr!@I%IlF^OY4w!1rS?C$F7 z?y9c3b?ctT-fJ;9=f3LEwp;OlE$!Op?(@eAZtY}$adBa}mv{SV#&YW2lEW=IY{@&8%ydaa7fo$Z2#c8X7tVa< zLGj*K#Yh&GSE-7jnTBxdotNHKvmL>08!}MSftuSvWGAW(LUe;*aZr{v7>?r3&fZ|K zzP>Yf;kCC1|M}Cm3ZMcCGm8fRh)Dh?Uw!8MUw-#n-(Ox{eMCgWG@`M_VkBz7VnAa+ zHDDTuF*b7=F%6hTq8TpD0BEe!;e0CiG}TytF7Y05;GBTVaLyy*n$i1abo1jk|M*$z zx+>Qb={DYroWJl#fBx-1=>GV>e)%mCxnX8; z>;qk3ZT0wx3w1F-W8K_oikUS)%#N7T=^X9c5zVA=2W$VDH8hQ%JsGj){CJ!>f8V)# ze;-(DWu7>33LF>bMMHH!Kx=P4e9q?Pq8<%E^wGfqv(Q-p&fu;!Kc3Tg2Ok& zCbk0_{p4)i!G!L{Z1!C@<>DX!83QoW_JKNi$b8s5nhm`}9>Bs>h>z*K`_H#wdQzUu zISZq3RdTg;Vp*pvWc)W0)M3Q9=Hy!es~>bqR^}fNnP%;a_Tf1 zBca{{aX2TKB(3vXYiKrA#_WsC2yRH$TgEvDE+gyq@kxqCC0|-4U+e*b2nYfqh&T{w z?h%K$<~IUn()x+ueM)RH{hJNVl*02jGFn;sGToM zic!H}FyuSm0Ph=ehZ`3G2vPZ$AHB$8uS+LS$vPQ%mXc=~d7jhB6Y@N7jyoCMPEOwG z(CcN*l)B-Vi#B2g6ykt`~nNGFXf zi4t=m^il?$E-uM%em==NBKMtIpuf-|NfMm*B#FZ(hW7#QjReKl1z18A5^UtDcN0c~ z4Njl`8~}q`H|I=$j)A%?Fja1^U&dlhmS$v`OzJ)nL1avOGh%|cHY5VuJ41YuP8b+B z6UP%@c$}5x#YRc3ZJQoZ_h6H%!D3`<{W7P{KSEgy4`o0A7wVD_Dyp);%!o0tadn-# zY!gu{xQJu*%xU^7$3TQ}b~g+)!6hlq`+W=qu<0hnj7zlvfF=yL5vnQ6ZDRj*Sr9@+ zT^3^o4rF36RYFw~s}fUX2v0Ahl+ z5(^Dz8wUkzA23dAs_oBlfDmF`5vpqJy$5O`092K_D2Sm#RjJB??m~|bFaL_Vs!%gN zxO9oSu1V5#UtKuWtWQ#WlFfDJ(}^&Xb=f$gO2dM-iLmL-CP8zogb=8U(Scp2SrZad zrLKx5#v_;+RZ;N4#Y@z6#Xa}k&;P#jE}^RNKAF%jCYUt(j7H1Ae3Ex+P0E-Q4$WU} zpMhDMCmMK@)z>7)KwTA0sGV7n&6$v@5@T#vRSq+wu4=?NVu*zCaf~?cQMJ~Ytuce~ z_K0(c+h;<~GtOhJ{a^+$8qpL}!&z*yyDDl;RonQAwb#bxeTU=$83Ry_lVzcqk##ya z@98Zqk##%xBw?Z7ryPy2$>OD93z2r;h=a|*w23+~D*e;HNq_ADdMj)6R!)$(gsL2} zyS2gkrJr)^-Is7Jt78DFVHUsv2Arv)Av8|Z0+=4_v=%Yu#wO&o1`(B4UwMtHs;J9~ zs;Y=pP1MN45B>(~Q|^EAIhMP}i9SQp42>l!LT6!##pUDN^T4AFpZXdvfB)~$(alMj ztsx+8el0&|sH%#_c?NV+1JlylIGeQ&=iFH1P0H_dG8W?qpXd0M9*K8&?-3EYCm-Uo zPkkOrW3;=DSb{9{akfNsiq{Sr4ez15bc|>I^sjmK`F~{h%1;16V{FQgnKlQ&RM9wP zpmD6Ry($)4o78au7tuK08f+fi$x=F5dccB;m~!^XFET8*am6j1CZw?oF~yPM@rWci z=TWT*b>oMRKl=@S{Jr;(VhfD{IU)+d#EHX%0iUMq4R`tMlTUy+RFy}6=ZlCqK6vjv zRCQ96S(}Ov+e@$O$l`B*g&Rf0szA@WEJ#3Qk8-OZ4u{a`((5mCY~=)^5seUPBbwvk zXTHfB|MvI97~rFvcp8O_{;?IdH@DcnwTXzEln+ik$4;Ma0JL{Yo6Yw7ubELVoFnws zxVE!RE?pL#XSdqLhI=fS$2t)-Qi-xN*kg5NgiB`TZl@scn$`_V(bxpU>9R+C(w;4o40Oo zYvU@W#_4nCIQ`H=+`L(Fauu+QP#YmsbXPABuD?2w^>7n1BZe9^oywwyNV^ubaq71w z){7wOn%F(0n4q<=w6w~lQ88Ql9F~b~#)*Z^ ztpR(ZiaIpTn&%nk&Yr*+ubyaDQ0)WkK#+q`SyF}}X(qT-$g+%XbeQYlMCf)hPMumq zF}mFjqk4~eSkMar#6$2nO9(+J%8Hbu8E~pf3;~S_B6RbVJWWUvj|n8=*xMU5QAZqE zp5R301>c82P_AFQ$UXNx28kglUw|%htr(F zjSZZP%fKTxw#*LZ0o6cRHa?+=Lu0!GAwufAXzRce5c}h_DO=YsbMO6+H9q5@-|thD zBi0^xjO!Qwhwa-#z{nQ5_>*f4h%BTz-W8-t05z`Nyh7@jl;ryvXicbPpaq~BsiTs( zCfJl^jdLC_@+>2UHZC7l`-~rI#`P<2@YvH|$N46bWm(4B*|Xffy~Vjlo~GO@sfx&` zh-|Zq_29Mzf(BMxNg_4xUwoT03p!4ihwnre005SIo;P0n9uGhI6upHW zKJg@pCx*ykZ;8z3R9Q{A6Ntl-R8!1Sc8Tbq=I1Z|2j`D>&=^?X+S{R7MgY@!A$}s%#DRxVl;L_3!)@XU{!AIVveeCFQ83Dr@Rd zpxTLy>d0G{U*O7r|A2dscUbNwtZxnnFT8Q}{eOS${eP{3{s{QFnGMDO1UL=+7VtQ5 z0a!l78h#xu0s7DW-j^N*CTX@$=|B>>QTB3-2USiKDN+xeYV&=If;%zm@2 z+H>o`mbJ^l34n1Ek%UG|pZ0&UjGLqV=1U{+Q6Je92xd0-4eS2^`fPfoDcnR7>q$S$dZw5 zK~~brD`_vYyR$Ppm+t=m|C~H@&uVqCU-AGsRb4&P(=+{@|J=TF{vi>;|7TIZX#oFM zF>Butkp@r%5aG8^IR=mdh(#m}F0Lv-M1_L~4_^1xuYUETTeoigy(o${hzJ7^03sqF zqSf0keT^u8PisHR?;Tu|HLfaa&XRwy_8Qhai}GivG)?EHr>B4LiBEjuX(E~vkpuuJ zzgYtqJ9_l!pYGhb^Uh|oIoWQv8{T^tLI}AcL`0-6iC%jqqQNmx>MD^-!Xm=Cge%TG zP>P6@)><25Y^_!s8y+5h+nFC;iS+of)|ibLh#-P?|ld%419Cddqze^#zsd+CypOK9zXr` z({A6*?>O+#!+*AGq&8A<{gl#vA8-NvmP4<-0HZZbK^F?9P^gU!)g~r(Zg}s#@7=%D zTFBS&@P#iNyYIgH`nTP7Te;iqwz}PJmzf0si-?2}gop-sLI@!RP|h*0Dljl- z7)09|eoUk@&T$U1)D%IkLKlIUe{?SK2 z_P!^cc%raPMf#zfZI;;vo0Zf`Ui zL;vvk&oABgCm(qLo7P?TO7kM-eIFeaaOUK5n7%lPR4I5-5Yb?$?B|t&CJizKV>GO> zFp)tNSwsa3Z8W@h*tuip*k6D8Z$9{qfBnYr1c^x@%sBP)Z=uy) zf>#7jfDi!$C?b$nFxo&{4Q(`xG1*}athIvIJnt+gd0gG;sLDg_7tDW$YA##n3Z$jHb>YwhMQ{^J)fe&Az&aPLf*E-d>Qc(Veh zSCHBsT$BI=aBw*GlYc`T5-`O zP`U#$9f<9KZ66{65kcHt#?cr49bPe50Rjam4MPS>DQKfWN4g`qzu)a=C7diIh@nEugL$#JLL@t+g2%8X7%$@?^TM9&KMgim}jZ zAPPMcxrc~W5K#w4ccE+-A_5ix3$Qddg_mFc0Xzh(G7C^1#6*_^Un>H_fKhlCux0bs zv5DTP(%_R5TI)zDWv|4n)|^$Avomsb{P=OTYxBklS?M8)3FW>*xlgFZz`6ve zCjqt8VThB=g_u!@Ja*l(54u!A-0$LrpM4*1OrOD-vy+&X0NU(j?6kh_lx&^#}tiuwbZMA4*eKSy@>jA{w~RY8XlOu*UwiE} zIBK#0%aAzfCxhb@U7*rxwPrEiCb@&UYI(&`@)3_*T+fU z6^q65!^5@Ht)Bhxa~H#s>uF;R9!JzyZt*VS#6+eS7w8fBp5>n}-h{ zmbDq^npp^dnx396j0_F0XBB}}39KprY9Q2L!Vn^^qUb6przKQEVb!NMMI*o3I=1Oz zrBZPZKm713M3ipaxN&9g-o5jsQpuO9rSqL``}M~B99qpL(j-L^CrFb7sdI2?njcwA z1P%xxpkAx5KX>k&6%o3!hhJ-fK^;&@k|<-M0^1TqX(+Z3@nARr0R#^XhGj+xSulzK z6XS22uGMNuwOWlA7Z*pBQk`SRj;-%>I-QA$iFxN-8bbK>%=rtO(q25WY2&7hH8qu_X_{DTi)$DDvYt5CP9;gAqC(lSDS(uLum?*3QOI0K0u(W{ zVp#Ec7J@XJO}k;khMB3Usj)_*@v>6NE-Wm(G&(vOlu|d2kB`q?xNu=Sa5&#yUK|-) zw+_O{7l0rFaNbE~sWx+oVxd^*cDp1Z>dh=bc@`R2Ko=_2NC*HMKr93yF52Kx9Ew$}9jtYpsZg zhK7dFYPD!&WTY`MF|jnbMnt5wRsvA)Jp|u@NCCtOBr4-h&`_iS)(}#FA|NmZy>`x+ zubG7gZltQ!D!Oq$tq;{oS%wJ6`Urs{15yS=0;Ls7QE@O{fOF0;GmVaps(!z}ar5TQ zivTb(GO}rObW|}jIp<8LwQ!w6A4bLCkU)C{5)DlnLIxrlqD+2hbXPh_wOZvoh%fV@ z%Q~_F(=*2K@^UMVbW~ckS#DBN1eFy$XhUGhO23FusZ>fjoz8HrR%`6uz59k8J9a2z z3@fG3?RFcTPG`7ODkYtGX>+|yNU;LT0tE#n8srTK1_~CevO{!)xZ6(3I=OM>o*L8RtG&9lhkUJ5*wpI>JqnV%+77oTF;7zV&(;8LjnMx zm6es@PN!2ob>`I#iH%2!V|Da(2M_@XprApbp~QmN0u({1A_52WdMk0IQjs+lxXOhH z03}J%3nBO&J9en^7pG@$*!A`;tE;Xq2X1WEkPJwrh@if1^z93cMG(=2cDubjilP7j zX_{)Sb^O}d|Jg9_n|te{Rrsn#C&Xaw!HR)2V+ht65Jga21i%0R=jSfW?%cW4TDXn0%hPEba3$1~%7RDMNXO$3O^QO&eaba=a z!hCbwIX7b>-3TJmTW}BQRqIE4qu+03mp=c9{}R0iHnn;Wk{6~>kbm-k!lCWDLe~;VX*TkB?jUN#%mBf zW|x*%hDS$}b?ertd{z|JV!l!V$tQbq^UXKAS6)3nIkA1mL1Q$c$iPMsq9|9u8fdM- z!mDglDTPX@f{ASt=#Iu%Y_4FXm!J>Aig>K36ur=cGXbIk5*3hG4<{bZGlC(o7O)o- z9H0Z?<#W@g4;?y$d=x2HP9-i2;(W{}%p49KI^>>s;)!z$i;X?wIIA1p1Hk~I0-R!aVgwC{31CeS zXCTI)J)d^ZHri8vc=)hxx7$fRE9Ey0;*7$wIOo_}>+ZSdp7aMl{{Cy*wr;=e=+A$O z`no!{Y}tZtw~NNYJbrod6|`C{hzPXSxg~OCLS`9aK(Rmv149br2}FQ5f}j~*3z!6= z6^L9;_9FlV{N&i=$%h_#$fjx9O4Br0YbhVqUOR}_xDh8wl5XF=T|f2IQ*WET_y+dx z-;aX_4`O_L94AhkfcGBD%gb0h?2;Nq#y4C$f9{{8z4Q&Ur|d@3hvv0mjuxi1SdOSjuqU;gr!-)oGi zRVo!kQ3L>JHk)X*TIlt9h~pUE`%8h$jNr4v7Q_R>P!MP#m>{r1h@cU9f=dZn1MIQ| z6=NVs5Ja#%6Sq&kc>d>){@tS^jYi|NbIvQJ)TPP$wOwd6O*!ZIo8SEAZJkc1RxB2= zw6uiD$w@3OE*g!fhIr`V3}Y&5W&C^0U8hm zkW#R)Fys)Lo%q-@KYQV^M<2T`j^m4sMq>%tL>x*f$t&?H<|{ig0DyB2larG-06-ka zSXfv_AcBGq8Bx?O0_W7EXbk+^ zO#78@|M;h${q$$nS1Og>$&)9iLI|9L8w{P_)B=i#bY8BBne9NKBuS7{f=dCDSLT7d z^U&>CqwWJF2w1XS86?moP$aNQp^#7Pl@Lr&Krm1aX#M*1?1f*v_>0Fr_vnUdwHm+v z`s=@R&PfPClu|UnmCr}7NvARBi2+clRGRO0yW0j}-%56D-+_1Eb1yhw{on&Q-}+KS zIe0)+R`3V}C4z{7?$T0k^5y4Vt6FJ%?r%Rg636lE$&)9i^35U619x?=EXTJhm$^_r zQqjg3y6djHo+T&i>)eE&lRf>N3~^3KDDoPG-+6teNU7HAek03a-# z=6v_`)bX>+i|5{W@WBVozJ2?OjYi|_;^N|x_ny7?vW9uC4&9Ve8rN_kAR8-MV~mZW z$lr3yElmLb@%;18AMEvd8=1Mp%vLFtYOT{srP99j)?3e3DwXB$|LDKv+u!?E>Be0* zZXVyXb*xw{7Yb3KP%4zI1fbPjaotWo>GrzG#kuqIXQofhjE)Y+x7>0I9Xj+uO4GDG zH8s_8&UswgOdO0+V68PaoA6q#wS6-Mcz%993=a=S%*;eYhYug_+;r1T&&|xtl$Ms3 zN@<$fdcE$)#>U*njT>F5RH7(~v{H)N?RM|gS6^+PIQ8;muh&Dr-=}W3s{o)iHQmSp8G$^^?IF{Sz4`D-+Lbh-P*vF2NhRqt&A}`%4L3jJ|NpOzD$7xK&%uq+g(LgKUBS5SGV7OyWDZd9d7LkT_G~4 z8EKjZ5#bECRz$=^q?J;f-v}Z?zRhZ)D2fV&Lh;CvBXa;+Ip$RbdH|-LeDcZJG)-#{ zJn%qa)22<8e5-Syts0niurIv!oDW_HTWhOBf>qSOU<;TBdtLbjUV9#p$+gc_N~xKd z8TZUH&-B0cwXe+rn94D)Hu41EIsk76a5I1j0QEOn#ov^)kO4mq;8g%~gBA@y=63*w z0c=DDKKjkzd{ar0DKQJ6ng4qLSB>iOT@Zuc&Odv25%Fs;{x9cs9+7W13?l#l002ov JPDHLkV1gOX`0oG! literal 0 HcmV?d00001 diff --git a/PepProPixMaps/stock_people.png b/PepProPixMaps/stock_people.png new file mode 100644 index 0000000000000000000000000000000000000000..395135b955416743785fc898e13e81c87ca522d6 GIT binary patch literal 1904 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 DfTTe6 literal 0 HcmV?d00001 diff --git a/PepProPixMaps/update-manager.png b/PepProPixMaps/update-manager.png new file mode 100644 index 0000000000000000000000000000000000000000..ab481abe25f3ccf6bdae0c4b19230e58b3e7387d GIT binary patch literal 3429 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 /dev/null && + echo -e " $_msg - Completed.\n" || + echo -e "\r $_msg - Skipped.\n" ) +} + +run_it() { + [ "$_interactive" != "yes" ] && [ "$_quiet" != "yes" ] && ( + echo -e "\n $_msg ." && + do_it && + echo -e " $_msg - Completed." ) +} + +# Begin xDaily command functions +_update() { + _msg="Check apt repositories for Updates" + do_it() { + [ "$_quiet" != "yes" ] && + $PkgMgr update || + $PkgMgr update 2>&1 >/dev/null +} + see_it + no_see + run_it +} + +_upgradable() { + _msg="See upgradable packages" + do_it() { + [ "$_quiet" != "yes" ] && + $PkgMgr list --upgradable || + $PkgMgr list --upgradable 2>&1 >/dev/null +} + see_it +# no_see +# run_it +} + +_upgrade() { + _msg="Install available updated packages" + do_it() { + [ "$_quiet" != "yes" ] && + $PkgMgr upgrade || + $PkgMgr upgrade 2>&1 >/dev/null +} + see_it + no_see + run_it +} + +_apt_clean() { + _msg="Remove unneccessary packages from APT cache" + do_it() { + [ "$_quiet" != "yes" ] && + $PkgMgr clean || + $PkgMgr clean 2>&1 >/dev/null +} + see_it + no_see + run_it +} + +_autoclean () { + _msg="Remove unavailable entries from APT cache" + do_it() { + [ "$_quiet" != "yes" ] && + $PkgMgr autoclean || + $PkgMgr autoclean 2>&1 >/dev/null +} + see_it + no_see + run_it +} + +_autoremove() { + _msg="Remove old dependencies not required by the system" + do_it() { + [ "$_quiet" != "yes" ] && + $PkgMgr autoremove || + $PkgMgr autoremove 2>&1 >/dev/null +} + see_it + no_see + run_it +} + +_clear_thumbnails() { + _msg="Clear browser thumbnail caches" + do_it() { + for i in ".thumbnails" ".cache/thumbnails" ; do + for j in "*/*.png" "*/*/*.png" ; do + [ "$_quiet" != "yes" ] && + rm -v /home/${SUDO_USER}/${i}/${j} 2>/dev/null || + rm /home/${SUDO_USER}/${i}/${j} 2>/dev/null + done + done ; true + } + see_it + no_see + run_it +} + +_clear_recents() { + _msg="Clear the \"Recently Used\" list in FireFox" + do_it() { + dd bs=1 count=1 status=none if=/dev/null of=/home/${SUDO_USER}/.local/share/recently-used.xbel + chown ${SUDO_USER} /home/${SUDO_USER}/.local/share/recently-used.xbel +} + see_it + no_see + run_it +} + +_rbranding() { + _msg="Reconfirm Peppermint Branding in os-release" + do_it() { + diff -q /opt/pepconf/os-release /usr/lib/os-release || cp /opt/pepconf/os-release /usr/lib/os-release + diff -q /opt/pepconf/os-release /etc/os-release || cp /opt/pepconf/os-release /etc/os-release + } + see_it + no_see + run_it +} + +_ssd_trimfs() { + _msg="For SSDs: trim eligible ext2/3/4 filesystems" + do_it() { + for mnt in $(grep -E "(ext2|ext3|ext4)" /etc/mtab | cut -f2 -d" ") + do fstrim ${mnt} &>/dev/null && + echo -e " Completed fstrim for \"${mnt}\"" || + echo -e " No fstrim required for \"${mnt}\"" + done + } + see_it + no_see + run_it +} + + +_udcache() { + _msg="Caching icons at /usr/share/icons/" + do_it() { update-icon-caches /usr/share/icons/* ;} + see_it + no_see + run_it +} + + +_update +_upgradable +_upgrade +_apt_clean +if [ "$PkgMgr" = "apt" ]; then _autoclean ; fi +_autoremove +_clear_thumbnails +_clear_recents +_udcache +_rbranding +_ssd_trimfs +_ptools + +[ "$_interactive" = "yes" ] && + read -n1 -p " Press any key to continue ... " answ +echo + diff --git a/pepapplication/Pephub.desktop b/pepapplication/Pephub.desktop new file mode 100755 index 0000000..55a4e0b --- /dev/null +++ b/pepapplication/Pephub.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Peppermint Hub +Exec=hub +Icon=/usr/share/pixmaps/peppermint-hub.png +Terminal=false +Categories=Settings +StartupNotify=True +Comment=Use the Hub to configure your system + diff --git a/pepapplication/Welcome.desktop b/pepapplication/Welcome.desktop new file mode 100755 index 0000000..a669bc5 --- /dev/null +++ b/pepapplication/Welcome.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Welcome to Peppermint +Exec=welcome +Icon=/usr/share/pixmaps/peppermint.png +Terminal=false +Categories=Settings +StartupNotify=True +Name[en_US]=Welcome to Peppermint + diff --git a/pepapplication/gdebi.desktop b/pepapplication/gdebi.desktop new file mode 100755 index 0000000..9bc2eba --- /dev/null +++ b/pepapplication/gdebi.desktop @@ -0,0 +1,164 @@ +[Desktop Entry] +Name=GDebi Package Installer +Name[ar]=مثبت الحزم GDebi +Name[ast]=Instalador de Paquetes GDebi +Name[bg]=Инсталатор на пакет +Name[bn]=প্যাকেজ ইনস্টলার +Name[bs]=GDebi Paketni Instaler +Name[ca]=Instal·lador de paquets GDebi +Name[ca@valencia]=Instal·lador de paquets GDebi +Name[cs]=Instalátor balíků GDebi +Name[da]=GDebi pakkeinstalleringsprogram +Name[de]=GDebi-Paket-Installationsprogramm +Name[el]=Εγκατάσταση πακέτων GDebi +Name[en_AU]=GDebi Package Installer +Name[en_CA]=GDebi Package Installer +Name[en_GB]=GDebi Package Installer +Name[eo]=Instalilo de GDebi-pakaĵoj +Name[es]=Instalador de paquetes GDebi +Name[et]=GDebi Paketipaigaldus +Name[eu]=GDebi pakete-instalatzailea +Name[fi]=GDebi-paketinasentaja +Name[fo]=GDebi Pakka innleggjari +Name[fr]=Installateur de paquets GDebi +Name[gl]=Instalador de paquetes GDebi +Name[he]=מתקין החבילות GDebi +Name[hu]=GDebi csomagtelepítő +Name[id]=Pemasang Paket GDebi +Name[it]=Installatore pacchetto GDebi +Name[ja]=GDebi パッケージインストーラー +Name[ko]=GDebi 패키지 설치 프로그램 +Name[lt]=„GDebi“ paketų diegyklė +Name[ms]=Pemasang Pakej GDebi +Name[nb]=GDebi pakkeinstallerer +Name[nl]=GDebi pakketinstalleerder +Name[oc]=Installador de paquets GDebi +Name[pl]=Instalator pakietów GDebi +Name[pt]=Instalador de Pacotes +Name[pt_BR]=Instalador de pacotes GDebi +Name[ro]=GDebi instalator de pachete +Name[ru]=Программа установки пакетов GDebi +Name[sk]=Inštalátor balíkov GDebi +Name[sl]=Namestilnik paketov GDebi +Name[sr]=ГДеби — Инсталер пакета +Name[sv]=Paketinstalleraren GDebi +Name[te]=GDebi ప్యాకేజీ స్థాపకం +Name[tr]=GDebi Paket Kurucu +Name[uk]=Встановлювач пакунків GDebi +Name[ur]=جی ڈیبی پیکج انسٹالر +Name[zh_CN]=GDebi 软件包安装程序 +Name[zh_HK]=GDebi 套件安裝程式 +Name[zh_TW]=GDebi 套件安裝程式 +GenericName=Package Installer +GenericName[ar]=مثبت الحزم +GenericName[ast]=Instalador de paquetes +GenericName[bg]=Инсталатор на пакети +GenericName[bn]=প্যাকেজ ইনস্টলার +GenericName[bs]=Paketni instaler +GenericName[ca]=Instal·lador de paquets +GenericName[ca@valencia]=Instal·lador de paquets +GenericName[cs]=Instalátor balíků +GenericName[da]=Pakkeinstalleringsprogram +GenericName[de]=Paket-Installationsprogramm +GenericName[el]=Εγκατάσταση πακέτων +GenericName[en_AU]=Package Installer +GenericName[en_CA]=Package Installer +GenericName[en_GB]=Package Installer +GenericName[eo]=Instalilo de pakaĵoj +GenericName[es]=Instalador de paquetes +GenericName[et]=Paketti paigaldus +GenericName[eu]=Pakete-instalatzailea +GenericName[fi]=Paketinasentaja +GenericName[fo]=Pakkainnleggjari +GenericName[fr]=Installateur de paquets +GenericName[gl]=Instalador de paquetes +GenericName[he]=מתקין החבילות +GenericName[hr]=Paketni instaler +GenericName[hu]=Csomagtelepítő +GenericName[id]=Pemasang Paket +GenericName[it]=Installatore pacchetto +GenericName[ja]=パッケージインストーラー +GenericName[ko]=패키지 설치 프로그램 +GenericName[ku]=Sazgera Paketan +GenericName[lt]=Paketų diegyklė +GenericName[ms]=Pemasang Pakej +GenericName[nb]=Pakkeinstallerer +GenericName[nl]=Pakketinstalleerder +GenericName[oc]=Installador de paquets +GenericName[pl]=Instalator pakietów +GenericName[pt]=Instalador de Pacotes +GenericName[pt_BR]=Instalador de Pacotes +GenericName[ro]=Instalator pachete +GenericName[ru]=Программа установки пакетов +GenericName[sk]=Inštalátor balíkov +GenericName[sl]=Namestilnik paketov +GenericName[sr]=Инсталер пакета +GenericName[sv]=Paketinstallerare +GenericName[te]=ప్యాకేజీ స్థాపకం +GenericName[tr]=Paket Kurucu +GenericName[uk]=Встановлювач пакунків +GenericName[ur]=پیکج تنصیب کار +GenericName[zh_CN]=软件包安装程序 +GenericName[zh_HK]=套件安裝程式 +GenericName[zh_TW]=套件安裝程式 +Comment=Install and view software packages +Comment[ar]=تثبيت و عرض حزم البرامج +Comment[ast]=Instalar y ver paquetes de software +Comment[bg]=Инсталиране и преглед на пакети +Comment[bn]=সফটওয়্যার প্যাকেজ ইনস্টল করুন এবং দেখুন +Comment[bs]=Instaliraj i vidi softverski paket +Comment[ca]=Instal·la i visualitza paquets de programari +Comment[ca@valencia]=Instal·la i visualitza paquets de programari +Comment[cs]=Instalovat a prohlížet balíky +Comment[da]=Installer og vis softwarepakker +Comment[de]=Software-Pakete installieren und betrachten +Comment[el]=Εγκαταστήστε και εμφανίστε πακέτα λογισμικού +Comment[en_AU]=Install and view software packages +Comment[en_CA]=Install and view software packages +Comment[en_GB]=Install and view software packages +Comment[eo]=Instali kaj vidi pakaĵojn de programaroj +Comment[es]=Instala y muestra paquetes de software +Comment[eu]=Instalatu eta ikusi software paketeak +Comment[fi]=Asenna ja tarkastele ohjelmapaketteja +Comment[fo]=Legg inn og vís ritbúnaðarpakkar +Comment[fr]=Installer et lister les paquets logiciels +Comment[gl]=Instalar e ver paquetes de software +Comment[he]=התקנה וצפייה של חבילות תכנה +Comment[hr]=Instaliraj i pregledaj pakete +Comment[hu]=Szoftvercsomagok telepítése és megjelenítése +Comment[id]=Pasang dan tinjau paket-paket piranti lunak +Comment[it]=Installa e visualizza pacchetti software +Comment[ja]=ソフトウェアパッケージのインストールと表示を行います +Comment[ko]=소프트웨어 패키지를 설치하고 봅니다 +Comment[ku]=Paketên nivîsbariyê saz bike û lê binêre +Comment[lt]=Įdiegti ir peržiūrėti programinės įrangos paketus +Comment[ms]=Pasang dan papar pakej perisian +Comment[nb]=Installer og vis programvarepakker +Comment[nl]=Programmapakketten installeren en bekijken +Comment[oc]=Installar e far la lista dels paquets logicials +Comment[pl]=Instaluje i wyświetla informacje o pakietach oprogramowania +Comment[pt]=Instalar e ver pacotes de software +Comment[pt_BR]=Instalar e visualizar pacotes de programa +Comment[ro]=Instalare şi vizualizare pachete software +Comment[ru]=Программа установки и просмотра пакетов программ +Comment[sk]=Inštalácia a zobrazenie balíkov softvéru +Comment[sl]=Nameščanje in ogled paketov programske opreme +Comment[sr]=Инсталирајте и погледајте софтверске пакете +Comment[sv]=Installera och visa programpaket +Comment[te]=సాఫ్ట్‍వేర్ ప్యాకేజీలను స్థాపించు మరియు చూడు +Comment[tr]=Yazılım paketlerini kur ve izle +Comment[uk]=Встановити та оглянути програмні пакунки +Comment[ur]=اردو ترجمہ از محمد علی مکی +Comment[zh_CN]=安装和查看软件包 +Comment[zh_HK]=安裝和檢視軟件套件 +Comment[zh_TW]=安裝和檢視軟體套件 +Exec=sh -c "gdebi-gtk %f" +Icon=gnome-mime-application-x-deb +Terminal=false +Type=Application +Categories=System; +MimeType=application/vnd.debian.binary-package; +NotShowIn=KDE; +X-Ubuntu-Gettext-Domain=gdebi +StartupNotify=true +Keywords=package;apt;dpkg;install diff --git a/pepapplication/kumo.desktop b/pepapplication/kumo.desktop new file mode 100755 index 0000000..ec67327 --- /dev/null +++ b/pepapplication/kumo.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=Kumo +GenericName=Kumo +Comment=Simple SSB Launcher +Categories=Network; +Type=Application +Exec=kumo +Icon=/usr/share/pixmaps/kumo.png +Terminal=false +NoDisplay=false diff --git a/pepapplication/plank.desktop b/pepapplication/plank.desktop new file mode 100755 index 0000000..2a34df4 --- /dev/null +++ b/pepapplication/plank.desktop @@ -0,0 +1,67 @@ +[Desktop Entry] +Name=Plank +GenericName=Dock +Comment[am]=በጣም ቀላል +Comment[ar]=بسيط بغباء. +Comment[bg]=Пределно прост. +Comment[bs]=Glupavo jednostavan. +Comment[ca]=Estúpidament simple. +Comment[cs]=Stupidně jednoduchý. +Comment[da]=Super simpel. +Comment[de]=Lächerlich einfach. +Comment[el]=Βλακωδώς απλό. +Comment[en_AU]=Stupidly simple. +Comment[en_CA]=Stupidly simple. +Comment[en_GB]=Stupidly simple. +Comment[eo]=Stulte simple. +Comment[es]=Estúpidamente simple. +Comment[et]=Hämmastavalt lihtne. +Comment[eu]=Erraza baino errazagoa. +Comment[fi]=Todella yksinkertainen. +Comment[fr]=Stupidement simple. +Comment[ga]=Simplíocht shimplí. +Comment[gd]=Cho furasta 's a ghabhas. +Comment[gl]=Estupidamente simple. +Comment[he]=טפשי עד כמה שזה פשוט +Comment[hr]=Neviđeno jednostavan +Comment[hu]=Nagyszerűen egyszerű. +Comment[id]=Begitu sederhana. +Comment[it]=Stupidamente semplice. +Comment[ja]=超シンプル +Comment[ka]=ძალიან მარტივი აი ძალიან +Comment[ko]=어처구니없으리 만치 단순한. +Comment[lt]=Kvailai paprastas. +Comment[lv]=Muļķīgi vienkārši. +Comment[ml]=അനായാസം. +Comment[ms]=Ringkas la sangat. +Comment[nb]=Uforstandig enkelt. +Comment[ne]=एकदमै सरल +Comment[nl]=Belachelijk eenvoudig. +Comment[nn]=Idiotsikkert +Comment[pl]=Idiotycznie prosty. +Comment[pt]=Estupidamente simples. +Comment[pt_BR]=Estupidamente simples. +Comment[ro]=Stupid de simplu. +Comment[ru]=До безумного прост. +Comment[sk]=Primitívne jednoduchý. +Comment[sl]=Bedasto preprost. +Comment[sma]=dle dan aelhkies. +Comment[sr]=Шашаво једноставно. +Comment[sr@latin]=Glupavo jenostavan. +Comment[sv]=Galet enkelt. +Comment[ta]=மிகவும் எளிது +Comment[te]=చాలా సరళమైనది. +Comment[th]=ง่ายเหี้ยๆ +Comment[tr]=Son derece basit. +Comment[uk]=Просто легкий. +Comment[uz]=Ahmoqona darajada sodda. +Comment[vi]=Cực kì đơn giản. +Comment[zh_CN]=简单得无语。 +Comment[zh_TW]=極簡。 +Comment=Stupidly simple. +Categories=Utility; +Type=Application +Exec=plank +Icon=plank +Terminal=false +NoDisplay=false diff --git a/pepconf/hostname b/pepconf/hostname new file mode 100644 index 0000000..ac3dd28 --- /dev/null +++ b/pepconf/hostname @@ -0,0 +1 @@ +PepOS-Live diff --git a/pepconf/issue b/pepconf/issue new file mode 100644 index 0000000..115706f --- /dev/null +++ b/pepconf/issue @@ -0,0 +1,2 @@ +Peppermint OS GNU/Linux \n \l + diff --git a/pepconf/issue.net b/pepconf/issue.net new file mode 100644 index 0000000..c95a2cb --- /dev/null +++ b/pepconf/issue.net @@ -0,0 +1 @@ +Peppermint OS diff --git a/pepconf/os-release b/pepconf/os-release new file mode 100644 index 0000000..af53259 --- /dev/null +++ b/pepconf/os-release @@ -0,0 +1,7 @@ +PRETTY_NAME="PeppermintOS" +NAME="Peppermint" +ID=peppermint +VERSION_CODENAME="bookworm" +HOME_URL="https://peppermintos.com" +SUPPORT_URL="https://sourceforge.net/p/peppermintos/pepos/" +BUG_REPORT_URL="https://sourceforge.net/p/peppermintos/pepos/" \ No newline at end of file diff --git a/pepconf/sources.list b/pepconf/sources.list new file mode 100644 index 0000000..696c7b9 --- /dev/null +++ b/pepconf/sources.list @@ -0,0 +1,24 @@ +# This system was installed using PeppermintOS removable media +# (e.g. netinst, live or single CD). The matching "deb cdrom" +# entries were removed at the end of the installation process. +# For information about how to configure apt package sources, +# see the sources.list(5) manual. + +# Main Repo - main contrib non-free +deb http://deb.debian.org/debian/ bookworm main contrib non-free +deb-src http://deb.debian.org/debian/ bookworm main contrib non-free + +# Security Repo - main contrib non-free +#deb http://security.debian.org/ bookworm-security main contrib non-free +#deb-src http://security.debian.org/ bookworm-security main contrib non-free + +# Updates Repo - main contrib non-free +#deb http://deb.debian.org/debian bookworm-updates main contrib non-free +#deb-src http://deb.debian.org/debian bookworm-updates main +#deb http://deb.debian.org/debian/ bookworm-proposed-updates main contrib non-free +#deb-src http://deb.debian.org/debian/ bookworm-proposed-updates main contrib non-free + +# bookworm-backports, previously on backports.debian.org +#deb http://deb.debian.org/debian/ bookworm-backports main contrib non-free +#deb-src http://deb.debian.org/debian/ bookworm-backports main contrib non-free + diff --git a/pepdatabase/welval.db b/pepdatabase/welval.db new file mode 100644 index 0000000000000000000000000000000000000000..743b99975bd977e84e60b8328ba42c4877fe0c1a GIT binary patch literal 12288 zcmeI&y-ve06oz3tsX!f2*s6%-jICO+a03LCq3RDMgdr1zVjv<-)g}m5E`b~1F4=L2 zI?#og>iuL}wq<*Gwj=rKa-3C09+vAxTFHqzR9dT35v5eicGvbzw4Ky@o7C&S(o)Bx zj{|$k@y5#gv;#o^0R#|0009ILKmY**5J2Fc3(TCoPQR~{D!t21xyo%}eN&p%iz(*Y zOZPkqgE*8pI2(tub+%ii3p21cd$)SOgK#FB)j8x{W>eaQmE&2%p2q1s}0tg_000IagfB*sr p?65#x|7-6{*+vjR009ILKmY**5I_I{1Q0-Amjv3{_1n##fp3$UNGSjS literal 0 HcmV?d00001 diff --git a/pepinstaller/preseed/preseed.cfg b/pepinstaller/preseed/preseed.cfg index 2dc0dbb..f65728e 100644 --- a/pepinstaller/preseed/preseed.cfg +++ b/pepinstaller/preseed/preseed.cfg @@ -2,7 +2,7 @@ # # SPDX-FileCopyrightText: 2023 PeppermintOS Team (peppermintosteam@proton.me) -#This preseed file includes configuration settings for a custom repository with the 'contrib' and 'non-free' components #enabled, as well as disabling the automatic response to the Debian popularity contest. In addition, it configures #various options for the Grub bootloader, including the timeout, command line parameters, and splash screen settings etc. +#This preseed file includes configuration settings for a custom repository with the 'contrib' and 'non-free' components #enabled, as well as disabling the automatic response to the Debian popularity contest. In addition, it configures #various options for the Grub bootloader, including the timeout, command line parameters, and splash screen settings #etc. # Add your custom repositories d-i apt-setup/local0/repository string \ diff --git a/pepissue/issue b/pepissue/issue deleted file mode 100644 index 7b2661f..0000000 --- a/pepissue/issue +++ /dev/null @@ -1,2 +0,0 @@ -PepOS GNU/Linux \n \l - diff --git a/pepissue/issue.net b/pepissue/issue.net deleted file mode 100644 index 4a909c7..0000000 --- a/pepissue/issue.net +++ /dev/null @@ -1 +0,0 @@ -PepOS diff --git a/peploadersplash/boot/grub/live-theme/background.png b/peploadersplash/boot/grub/live-theme/background.png index 696538781a4b028813af26952bc853bd14b201b2..bf8fd87b076374d2cb9027d715a9b6965724e02d 100644 GIT binary patch literal 34608 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 literal 0 HcmV?d00001 diff --git a/pmostools/peptools/images/kumosm.png b/pmostools/peptools/images/kumosm.png new file mode 100644 index 0000000000000000000000000000000000000000..fa73f763e1fe0b2b893acc60fc356639092a6af9 GIT binary patch literal 19404 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 D#2^_7 literal 0 HcmV?d00001 diff --git a/pmostools/peptools/images/mn.png b/pmostools/peptools/images/mn.png new file mode 100644 index 0000000000000000000000000000000000000000..d773152a448af4c59c333d0402c5a7fcf59c1f69 GIT binary patch literal 9349 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 E3o4Mi_W%F@ literal 0 HcmV?d00001 diff --git a/pmostools/peptools/images/mt.png b/pmostools/peptools/images/mt.png new file mode 100644 index 0000000000000000000000000000000000000000..6191add7e49e8b595b4f3c827abbd03cf4c591ad GIT binary patch literal 744 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 literal 0 HcmV?d00001 diff --git a/pmostools/peptools/images/rd.png b/pmostools/peptools/images/rd.png new file mode 100644 index 0000000000000000000000000000000000000000..d7473492d465193aa0cd852bd3ff5ed59edc5c41 GIT binary patch literal 12549 zcmeHsXH-+&wsz=6nt(JZp-6{>7D6C&klv*T0wD>#hR{2rbg2R=(mM)B5fG#)9TWrv zQF;~WAkD%JzVCUD=ey@S_d8>J_ut7F$=++NXFhYTXU(~G_E@n7de3!IbHOP5qn9NCURF-yy z0Q@_nb6M41k=${+Z|Fz9?78El-N4~c1$TLCPu6|8vu_TX)yM5gACu@lMfcR5tf-<} zt=H#l%Bn9l?JucTUkZ5Iw})!eGqK^@-)`HrMzl13ExNfsX}SJIzD+)CxI>}P#;y0@ z*^~|6>7HMoxrchy(qqfnv*@`uEiHiRus0_?X@Q}#vyP`u^7}>yv+Zt?N}&#=Yl9U- z`qja5bz#t-uLR_0mhKYFB|ea64-5L#Gk0~4l`gEgmV?9Wr*E+T$FSKq-{xlPvR+QU zY+<0~)vbPXR&y_BS6z?x`>FXN-vMY}E;n~TUrcqUW95KZf6Zaana%wJWXkE$jPK)K z1c^3vj>=|eub~`q==t~W`k`O_!R!)@?ltfq zIR)3h#ffyMDBL@072Egs9$r0#;qfj`r*zYN?Ue67b4&}V_5N^k(|lKdNwxcQ9wu5N z_M%_#)~@_Q_9E6zK5Z+YI)0m-xXx4cu;0N-C2*kFKd`okw-^lE6!`F#p7))$j6mu4Jp8QY1Co?)hhskUH|^!n^Oz zCs^UG6De0lpAGP@=?En)bk;nH{}_6*J?1_4-Cp@b;dJ;V++q2}IrUrS=56YnZxbHv zd($Qln3AM>RkT!?zwQ-rcsN(>Dy%GL_F6y7ToQ)Y|EQlRSgDoum0&~%0=yb*b+1mR zeh3OPCuUgE{q}i+x|%P^rn#YY%^m$^=bdGLiq*psU;UFSvRxER_)i{-tk^qL2_?}V z9yt?8x0%g-U)`a(P8mQDhWJ9u;cfs+ROObOW^qVo~CYv+#xH1Qn_HBpCwO!-oEt>pd9nwJL;rLyNTr)!5N>(LD*>n<~sYp)SOKR z57$9gIUEhYHGXzQ?EUrJeT7PVp*Rv&4VTO_jb5${VbLb!6~AnQ8A-?&fj*m88&TX} zX7Lmz>6cK){8C0BDGrYFh95Y>sQ?27~8cRiM-*cCldF(Dl-F@)>h@8c}(WsDha9@Q0mrp)lo=U=}fI&ut6#?9N#Igw8vwR~(RO zJF_XqqIcC=@5(8e%Oj4O-;KTG*emIjIxo`<6(CX(QVs97y2`hxt`5vD!a_r+JQkWyu7QhPq09%AEpFM^b3F1Vg#`E@*4f@<{2kLOmI z!TPuzv3rYoKT-5!DF;#GNAi-V^MaGSys@)rQr|pl>7~AKf370F?OH>O`S9WRYZ;L; zHX)+hq#X;0BR#C)6^+dT?ML`jXOqJ_#T7JLU$$pT?kWk)41C9TcL@#-N>87f>{$pY zPCwi>Bbj=G26q)Cizd`e6Y=&rp1|*h$lra@RrZMH0k0uEgqJD5{LwmFk?ZVB&-FVt!7mSY|i z6bAb#oLLY#5D0nK@RTr9JK!yl<+d_*qy5}tUwj;68KqN8;U54Q@2h&L^0?`Ie&?(g zF^-XX&-Nrw?jCI{wdeelc%=Ooi)Gcu)4)rn3)Re*E}=qp#;C#XrufN6%7VR8ztq{b zcWhBME%Q=qfW#B&JQN_IFo3dvGat468sg?a6Q>d~yGLe-6SWu6-5w-51S|{GTIerrJk)Xub6!KXivPns=^qEZ7qLDaa%|v!IH_=>%0V;eZ97D zzSSW}YXhQ+Uap6$E{IPtMpX0<7AhX|(-pWtYd{#_1eur$n#v9`U#d3sjfkpCy(FFB z*?J}o3bcVt-}j7&tRXpFp!C(b3SuHZl;^-NKFZX}Tm zxW{uK#;Vd;_w7b~>ZcGIdjA2w3a)amfy<_xsZFVIF_O+%uN{V-D-(dNIfm)%^8?#c zA9cT-0BT1ga;N98X1qRn*WC@s`LoG}@T=yfMj9E-JRy3A)U6BF#_(*JC2 zo~7d?4pA?Y9i`c&&llscf_y8`LBis={6;!{Oap}}OU@O=jPqIVcceac*^8nqKJbuQ zKbD;SZpr>bn#Jv>NYc-%iIB|eAAgu7?*0(#QqSw8esHLm{8WndAkdq&M zOOmuCki$l3rcelogaC9OJCgJZyF8p>)72(Y6Z5_I2KzSpfMz@`o>zku#kh(;>OwkT z{dP|ept#6RW}@6i&=B4Z%bG- zC38FePio#WGi1?61{AhV3k(4AJPL(s&hw58-quyCyo>^6BJU}%Sr|QV zd9fkzvi*T|Xd~q*S7mfb6pN8+P=_t&(~-qbfktnyvaIklcSz9$Szas3KJhR5Qz~i2NOP)JSeByDiSY?^j;Iiar zXHZk(Fop>|yrat=HAi{d*8aqXHh9m&Obkk8?i+257?ASwWEOEtv+ulO9U};+gg_%X z9crPg+!)2KPywa9#YPrF$Es*s^GY@^IZj()w)^7A7NtJ5U)&xKnSCiTsN?t!=Wm1( zZ}=%=g}jPaljTLyhA&-CFc2uJdzO|o!?FTDj`ft~V4(uH^Eaj%i0&+}*h~n_7v%~T z!{qcHi?1hDz721%FT`gudXUD~)WkAQ;9lo3wzhPzqd!M|FltU=fVx~o2&53tj~40w zKx4c{xqB-dNomL78&OoGRV`_{%%W^t&>1oC*14xZ2eJH-ghtMb7;9)QRu5}ZHnpIJ zUXppp!&M{i5pyM>*FmP;+`GOcgSuY%RwL*69o~7elkYTYCs?;w)z`OWx(rhQ5#U}@ z=X-{?T!=Uku-9bf(xzVpefmDqy0rOisz-)?zPBPtR_LN1_r@>nQMKteJ51PCSGk~g zl+k>(Y?v4CwJ%KvSeMzMC4G6BHLA%+C~DN?*IVUw%|E4p3(1a>6;D}|(kYz{bF%tY zH>pvdOgB<)-SgrNw;q*|@mOU!`BDz^%{00`{o;n@ z>IcPFZERq{fU|V#K%9|F4{5;w^hD7K|!T%c;|FdW!xa`;-lWn-ttw>Q*8O* z>->d|=mD9iw$HWOW6CA=Q4Nws65Q~NVE#Hm$u7=M@Hq%caR03}VljDto0b_~RW6W} ze7?PwIwxu=C8W8tjhg<*w<8jctO39Q0@Pl)pLrOG-PefW;f0|?L21WugL&Ouc0oc~ zQt-9iR*#zFY;}szI9aLtm5HN@ej2p%_!|EA;YNgP;67(i^H%GkeZ{*nHFo$)YvcvA(gAMa2Ys#dFE9vG zp~nuz%G>V@t5x~3eKQR%?-G+S$lmiA|EXh}^4!9n^SP3yvx$=KJp^CTd{%@>$NABR zVt^>YL*(W8(I)_M6YUkBZLbWhrKhd4kc!(O{OtX}bJ~;UVPZG|~O5O3QeKUW({NJ<6VF_wb1`EAMDNXOMFg zp=f}JM$9;mA)Cdz*jU*uUCU*eNN=9Ys!ZpFan-u_NP04Unw;J;qALPSTrGRDJC-H4 zmiKOI@FAIZjpXgt($DcVta*m`nZOGDG}1V8O+;CV7>wl0t8yu)utz3aD^t-i*$o!2xdY?dla-=4990<6F4A=Iz?)9suU;_ievLBo^%Dm}&t^-5b@_k1%hJt(YDM?~aPf0@zGclH< zFJAj?{q#E8K2f+tPN2<~HY!Y53A-7o3~jt7Ix(iePs2wGAoOhsB3pU(mfiYoFDV{h zVPXTKc20fWo?_$LPx7bb)p^5dqR+!BMBgj<34QXR`n1DOZFrjpMcem|5ZDXtenLQ7 zu_*4qHRq~FHqsqiaxkQ26lAdUz2Pk!oOL{iWKn+ zBuo?L_Ahsq65Uj#O0a`spxR~e?Uz7A4T@OzkqG&!98>h00X#y_&~$>G{WRnpQHa?n z9qA^CN4blFsmDi0Kf2}cr<+lkLDw(c%_T#Bka7HA#zyB2yD z7S0mL$yMILE}T9-0?6ySEwvak@{+=r#QF_%w1JW#%c4-I`Uif)tC+%B6+ptQ9J&7NQ}%6r2(F*{-J-FpEIJ zQp#;ackp$D5^^}lAQeKE9TjTX*P_#xX;Qfew}|k2=NZ@?UNXt5U6eHiYjc3jPro7b4yo`mh>|yx}4eN+1nz@f>KF0avIT0%Q8UZVU z3?GmABL`$r)rGoTZN$K9pR{@uFy9gK5uev@6lmy!A~G$1=>yf!xXYrp$3%AoXjZ$P za?7@U8cUaW~x>FLM` z`JD*GHYg#nQxcPT(5K+;fMem_zoXrCD~RV`w*LQ?@h zd<6Z$a}RGO6_TZ4wCmnmr=+|3s;qMLQJ+ygh*avKNNI0d#{=w9PcHeQi?JUptKCu~ zU5jl0(=pSROfSw%mQA93bvgTeEqDe)SIXjpN12Hm5C>D<;lZbPM57FGBt;zgD-Can zZ*Y|$>Btog@Mh}XKW$(zp^I1RWTN45DxJhf_U(kMK1lArterC9nO|B?Gbh9Inu6=+ znTrB{VG=_5U{Def>{hv|;980AyQoJM?^IwJ*{5x&gio4LxJ%)lCskoKPqJJAV&x2$3x z);60p=9)D&v5OvVO5MxAu!l^fZ$;W-f3gvE@GTH^9PM*IdZqvBhn@5W!Gcgd&%?J} zdou>Arc4U~jRD58O`GqV6w{n%L5-CTy6X$Rx>ef-&k?D4C@+3#y%TO?YFrlg+sVJd`Ux>B{(ion#&%J$qzcunk}Dk;yLbs(kiA=c8RdM zJNmD{4L0N-QNuuf!)@0*2Iy0gq*s_0CHjy>y~NPf|2lU1C%Yl$6ZfIZELm}Z z6BW2F*W(FF@`1^f2Agl+<2Q<5#2&lKr(JIE^V+r1X!%ZA^aB*VRiS~wde^B(KJQ6wpJYwQvvHI+ zoQYFSv_?X9TkP=m$^(5My?Cf)ulL*j@6wrsT_L3dDT%8A!UDv-tMJ{|MR$+Ny?Iy~ zOcE)?e2c&h-3ltNT$GTa#%k3jA(rKVD%16x>OmRk!JVVdq=1?MDgAc13~e28|nhaG_dfEog?(s--2U zMyt{58jH`W>cK9>WTZgW;@ zy+ytZ3tas|Q}_+`Dyyl07<$zXM6Vl3bNPo|AS^_8>ppy1oXoyVfzKd!zUK2+Y{1}i=HD0lDb6KsSIPsYANS}ueq3$V=L2wUr2uU@o2Va9Q)-{mN8vtIu~)Oy@{VcvZ_uGu9jRYRBY-PXqX`r z&Zgq(dc0yZ^n#vhqdZw{W~!1pMJ#^af3|R-r7S4+Z;QmWxAWB8N+F z>4)pv$oP|=P>%onNI6~?CMfLSwwTGk;oH_wBZ)*qVX3g@>ikmb*kzTIJ*O?)7_y z#){Srev`MjogNnA8xyKl$JQS~1n}O(Z^88#6%rAl(g@GqH|JJRDb&g(J^j8k2~_vf z8IT=HC?vQo#_*h%bsqK5Da`V*dFI4$^oO_R*y6~~vcfg^W|GAA7VWE7p&#kqa|Hq# zvsE8(uJ;yImj{BmzT*d4KFX=7IVaBkz8ERHLg|1zT15ZTGesv)Eb`(^5w6uw*#V|F z3yQlpA0C8a73wc+mw(Y7_pSm+AU+i^Gn^4Sm2e&CjHTWqF?e{p#f}!sHnQTg@e$H+ zTTQ2`L^Bmj*~W;V{yNNPS##It8UTQI$4N!SKvPBKZ-;uK6+ zH-Q_;`7jHb`-^2a_?7P%2-R=cVm@<7I%ybLu@X&2-d2deLBi;8i_MBiX_w^nXusyD zn$~wwf%=%edrR!I-)1vgb^!U-_75k-@El*bmC$;LrQN#HVm(R_u?n^muS<~8)avpJ zp5g8|ZsC8sU;WMduv{h{4==?(gSkYvRf+XuJ>{EkuMwu{Hx|7Yz0yQAzW4cbFG;^4 z$|TBRhK!X5EP|hzJ0gn{Ek?&_2cr3L^Xr{M32K)Rr@Ks&U9lRKPir=^_> z=!AlEnMvt_bv;$kj!qhZ7_@Pqo(VG01u28#QjjN?^M~OG+|XDA(BI9~-5cf)=laD9 z!`)wWgSdddAXpbTm$|M1P{jj-21<%bih@Pd{GEKox#Y=#au}37%t%%JPYPTM&gF>3 zdcr^;KR-WFKM7F}i~~qaMn(n%76*xoi{KC<-U04dgujTpH}?g_9~`P^ZzKkn&rTlh zzza@X+x=ZLVwT8vJE*A)|wno98c!3-s?u6!I^e zrw_*U7Y2m{p4bv)x<%N5?ZsrkND+G|NY@>?Q0(BxTTGgoLEMGztR# zjY1#egsTLE>+exrP@!;C5U8CDSV|0Mi;|GRQQ@)@Dvg#9kwV!cA(E1kP#FmN7uCf& zz?2L$;auXPe~|y`F>pm-?L9DVa4sDucOU=1Lrt9A(8gHAg=k_Bh&UK3EhZ%{B@K~; zivJyCipF^3lKg^G3@j@C#|R1uQ^zqPaLMN6hHyZGJl!3BnOrOj3^y5^u!xJ=z%l>o z$IS(%f4(-U^*ViUxvTHXw*f${aJC!2nW!wrX1*B zf&Uwmv7?8d`~Mry-=Tl8C}FUE9vByWjJ};S8j1baJpT;*7n2cguk*%Y0yO`JN&O!< zxj)iX183`j3HZ&wG1}{o)*l;^tJ5#3fWTjy01ScrLw;|BFB~{g=F193?=0)Zi`*?b~I-xQDD9qmy<^O{F&Hm4%{GZH!hy7u#;^7&9D_TdazMuQQ zbpKC)e=z7cA<^#M9{(!z-ywg<^4oI<7xN!|xaS$}@d*0s`S@pATvX2g;^)tF`(N~c zqyCSRe@oy0$n_t&{w)RmE%1M`>pyb+TMGPJ;QwUT|2MhF|Ne%JcE|nE^TWMAzbwO> z#=UzJ+i71{1+)UL0yv&gb5P-W$UHSHya51Y>BSfC{Hy#7u9F0-sjEh^N`{Xw3FN0a zPX_?-i#1i1OrV1e_uuN%Dbe?~*Nt)1)Z1yKykcNT-yihfV2^oLUm`TCaYC?+8p+C{ zxt9ZtnbkN{WLYA6HR?a<=EyTTh!GF(1nM{z634aIOmWCAp&NyZ=o9uH^X{yK*v}3H z&2d6|DAd>hU~)x*5%-oxKo-DyUFNxwP|`TyD?t`fvSP>}ps8?eYU7!rilGNU125SN zCIo1Dx;7=z%gGio%tmt101>6rT6O9eTxULKC(ZFs_h#+LejEg-AJmzU)hgZ$(0R1t zYrS}&>!wQvA9UB0KO3PcbyfcwRmXS#vx|#5`gk&#-{Lqlcq+u5f5YGDV62HP!;LuR z$HT#|wvt%;M`1rsHOv=(lKBN1)>;-q_}3Z7^>u!R?BJ301#Ix~Ql6=?&6o9VOFRO; zjHc0!(ozY}+VkVr2en&Gd0#7L+rRlQiFxIc==KUNr zQ6<;@_VW+;0MF4zCXQ@Rd`yx!egr>gAJN+zAIyYr*nFMYD>&Y)p3HM0z4h9MIx+zN zGmx0W%&2;4WPfK9E0vy^G#N?h?fy>xQbGM2-nFM$UxVIlx%o{`O5{q3_Wi!AW52Mp7Sx^KiDd^%|||AHR7wT4WMV*b)t z+^niR=IwfaoFd$mTXLvrHMjLTtwrkb8=m9lN*e*xyRY~KI? literal 0 HcmV?d00001 diff --git a/pmostools/peptools/images/sf.png b/pmostools/peptools/images/sf.png new file mode 100644 index 0000000000000000000000000000000000000000..cee328b1413043d0fe50746fc2a3b6e3d6ad1952 GIT binary patch literal 1057 zcmV++1m63JP)EX>4Tx04R}tkv&MmKp2MKriwpQ9PA+KkfAzR5S8MnRVYG*P%E_RU~=gnG-*gu zTpR`0f`dPcRR6lU)_NUeN=A<}hM1vy3@OO2T)1-6O#FyC~1{ulsZKsX2=Q0g-r?8KzCVK^)t( z4bJ<-VOEq?;&bA0lP*a7$aTfzH_io@1)do;)2VslFtJ!@W2KE*(bR~ii6g3}Q@)V# zSmnIMSu0mr^Pc>Lp`5<5%yn8LNMI35kRU=q6(y8mBTBnYiiH%N$9?=mu3sXTLaq`R zITlcX2HEw4|H1FxTKTC-FDVoUI$s>;V-)Dx1sXNS`95}>#t9I72Cnp$zfuQgK1r{& zw8#u6ccHp5ycZNK>zpH^9Lm zFjk=Kb)R>4xA*Penr8og09mDSo$1>vf&c&j24YJ`L;$=18vq}%4<8Ny000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j&R^11u<|5{1YB00JFJL_t(I%e~akixyNA#_`Yl z;*Sd5ghkU`T%<56aG|(KMIEl-5KRmzAw?}(w5W}HSMK@)3=u>`QT!Pg)xw)3ft$cx z3n3UUaZ5wUg=oDAwmAbYuVXS1-R(K&%el{a&f!0PgMSLvl;6b(EW@?Z+s1zltSP^P zQ&^54F^zS2sq}ViNx+)&EY4yDu46l%!&R)p$ECN!i`5Pnfz6D;e!{fD;1+H~r*NpI z{LvGEHRYZ7B4G2_UV8h*!-vQ4B6i`2_}Sr_@`sNF)|97l5i8NKt@PGw%2SxbXQj70 z0lgNrM{3INJ)&Vvc^kgL)A$)%OK+{FJjC~SBl4$8Z#QeoYw#`B;{o0;y`6?(O?eBx z4p@)QS^z#BgTdyS@}AP$ZR`w9m*IH0!VqWh%mSeFHa`Z~J0Z~v*i}HL+r-gC^%SCet8TqH33|SSHnU|Z#QuuVk>ZTSbDpP8QhCWepgdo8%eh&fcvls zzlZjla4=%`@b+MA%8v{N8*pI)`g!T?lbZ4dT*9-skD1cjmBEA$r|}h@3hQjej_7PP z&S5`Ziz%>OGvrZTuP=lkgYzmfp@U`M{2Wug5JM;e&-W b`(NJQp&7S6!kvlV00000NkvXXu0mjf^L6Ia literal 0 HcmV?d00001 diff --git a/pmostools/peptools/images/xd.png b/pmostools/peptools/images/xd.png new file mode 100644 index 0000000000000000000000000000000000000000..fe742071c9df64d0dacc84df68cd827d924de339 GIT binary patch literal 1485 zcmV;;1v2`HP)EX>4Tx04R}tkv&MmKpe$iQ>7vm2Rn!q$xwB%AS&XhRVYG*P%E_RU~=h)(4-+r zad8w}3l4rPRvlcNb#-tR1i=pwM<*vm7b)?7NufoI2gm(*ckglc4iM^Prdb_hfTr7K zI++l&xfL<=iV&g*V+4aTvy53uO2K!0-6O!)yExDCKlkV8QS%l90wVDYGfbO!op@@~ zHaPDSM_5T#iO-2gO}ZfQBi9v=-#8at7IZLB~v4wB#xu00006VoOIv051S9052jEKP~_O010qNS#tmY zE+YT{E+YYWr9XB6000McNliru<_8=MD;2hphVB3W02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00X5-L_t(&-tAdUYvV>3er86J9XqmRNaEOzWhGmhu$NFM zJ(Pt#wDb@3)*gCl13mWMztBse>_VZ}o_B9$3q7QV{R7LwHV`SMv7>C{SZiCBBTX+( zu&j}r?b@UUJvYDeYVbQwcilJHWJ@I;pJ=B^3M0s+7{2MYoy08{|$0PX?!7{C{l;(0!g zN7*cFLY6OtvSEmau8T*GgHMBCd2^t6b%pO72cHQ@+*t@6t*qccRRI7x=g>LF2cr=_ z^gPst!2McE4FF1T17sepKER$CLG4v zXf!*C*Vosfjw{49T-U|L#RbmK&(WVsq(Y&9wY9amC-*Btgvl&(j>0Sggb>v0bx;~z zZZ?}ZJw1)c7-QJl+L}w;i!h?%LRk&N*=1CgWf+F>uP-nR1F|eHti&o~L#OfgbQKao zRaKPB<;3;P%}uPXt}e{@_c;gST4_z=5aR9$h^oK+O-EJ4QZATMI zQDU9iwrydjeit10K0Z&_^Tos%kvNU|zK`SM;c+fEdz`+Gdp?!@l}ZJ<@x$eEITAXYb6A#zot>Rn??}SE z!2r(^VqAErl;S5%i;U935VCC>ytyU|hr0aU*WGSU+G#W9zcDoJNb?0XIfHC}`s`#c>", self.change_base_theme) + + ## color options + self.color_rows = [] + for color in self.style.colors.label_iter(): + row = ColorRow(self.configure_frame, color, self.style) + self.color_rows.append(row) + row.pack(fill=BOTH, expand=YES) + row.bind("<>", self.create_temp_theme) + + def create_temp_theme(self, *_): + """Creates a temp theme using the current configure settings and + changes the theme in tkinter to that new theme. + """ + themename = "temp_" + str(uuid4()).replace("-", "")[:10] + colors = {} + for row in self.color_rows: + colors[row.label["text"]] = row.color_value + definition = ThemeDefinition(themename, colors, self.style.theme.type) + self.style.register_theme(definition) + self.style.theme_use(themename) + self.update_color_patches() + + def change_base_theme(self, *_): + """Sets the initial colors used in the color configuration""" + themename = self.base_theme.get() + self.style.theme_use(themename) + self.update_color_patches() + + def update_color_patches(self): + """Updates the color patches next to the color code entry.""" + for row in self.color_rows: + row.color_value = self.style.colors.get(row.label["text"]) + row.update_patch_color() + + def export_user_themes(self): + """Export user themes saved in the user.py file""" + inpath = Path(user.__file__) + outpath = asksaveasfilename( + initialdir="/", + initialfile="user.py", + filetypes=[("python", "*.py")], + ) + if outpath: + shutil.copyfile(inpath, outpath) + Messagebox.ok( + parent=self, + title="Export", + message="User themes have been exported.", + ) + + def import_user_themes(self): + """Import user themes into the user.py file. Any existing data + in the user.py file will be overwritten.""" + outpath = Path(user.__file__) + inpath = askopenfilename( + initialdir="/", + initialfile="user.py", + filetypes=[("python", "*.py")], + ) + confirm = Messagebox.okcancel( + title="Import", + message="This import will overwrite the existing user themes. Ok to import?", + ) + if confirm == "OK" and inpath: + shutil.copyfile(inpath, outpath) + Messagebox.ok( + parent=self, + title="Export", + message="User themes have been imported.", + ) + + def save_theme(self): + """Save the current settings as a new theme. Warn using if + saving will overwrite existing theme.""" + name = self.theme_name.get().lower().replace(" ", "") + + if name in user.USER_THEMES: + result = Messagebox.okcancel( + title="Save Theme", + alert=True, + message=f"Overwrite existing theme {name}?", + ) + if result == "Cancel": + return + + colors = {} + for row in self.color_rows: + colors[row.label["text"]] = row.color_value + + theme = {name: {"type": self.style.theme.type, "colors": colors}} + user.USER_THEMES.update(theme) + standard.STANDARD_THEMES[name] = theme[name] + + # save user themes to file + formatted = json.dumps(user.USER_THEMES, indent=4) + out = 'USER_THEMES = ' + formatted + filepath = user.__file__ + with open(filepath, 'w', encoding='utf-8') as f: + f.write(out) + + definition = ThemeDefinition(name, colors, self.style.theme.type) + self.style.register_theme(definition) + self.style.theme_use(name) + new_themes = [] + for themename in self.style.theme_names(): + if not themename.startswith("temp"): + new_themes.append(themename) + self.base_theme.configure(values=new_themes) + Messagebox.ok(f"The theme {name} has been created", "Save theme") + + +class ColorRow(ttk.Frame): + def __init__(self, master, color, style): + super().__init__(master, padding=(5, 2)) + self.colorname = color + self.style = style + + self.label = ttk.Label(self, text=color, width=12) + self.label.pack(side=LEFT) + self.patch = Frame( + master=self, background=self.style.colors.get(color), width=15 + ) + self.patch.pack(side=LEFT, fill=BOTH, padx=2) + self.entry = ttk.Entry(self, width=12) + self.entry.pack(side=LEFT, fill=X, expand=YES) + self.entry.bind("", self.enter_color) + self.color_picker = ttk.Button( + master=self, + text="...", + bootstyle=SECONDARY, + command=self.pick_color, + ) + self.color_picker.pack(side=LEFT, padx=2) + + # set initial color value and patch color + self.color_value = self.style.colors.get(color) + self.update_patch_color() + + def pick_color(self): + """Callback for when a color is selected from the color chooser""" + color = askcolor(color=self.color_value) + if color[1]: + self.color_value = color[1] + self.update_patch_color() + self.event_generate("<>") + + def enter_color(self, *_): + """Callback for when a color is typed into the entry""" + try: + self.color_value = self.entry.get().lower() + self.update_patch_color() + except: + self.color_value = self.style.colors.get(self.label["text"]) + self.update_patch_color() + self.event_generate("<>") + + def update_patch_color(self): + """Update the color patch frame with the color value stored in + the entry widget.""" + self.entry.delete(0, END) + self.entry.insert(END, self.color_value) + self.patch.configure(background=self.color_value) + + +class DemoWidgets(ttk.Frame): + """Builds a frame containing an example of most ttkbootstrap widgets + with various styles and states applied. + """ + + ZEN = """Beautiful is better than ugly. + Explicit is better than implicit. + Simple is better than complex. + Complex is better than complicated. + Flat is better than nested. + Sparse is better than dense. + Readability counts. + Special cases aren't special enough to break the rules. + Although practicality beats purity. + Errors should never pass silently. + Unless explicitly silenced. + In the face of ambiguity, refuse the temptation to guess. + There should be one-- and preferably only one --obvious way to do it. + Although that way may not be obvious at first unless you're Dutch. + Now is better than never. + Although never is often better than *right* now. + If the implementation is hard to explain, it's a bad idea. + If the implementation is easy to explain, it may be a good idea. + Namespaces are one honking great idea -- let's do more of those!""" + + def __init__(self, master, style): + super().__init__(master) + + self.style: ttk.Style = style + self.create_left_frame() + self.create_right_frame() + + def create_right_frame(self): + container = ttk.Frame(self) + container.pack(side=RIGHT, fill=BOTH, expand=YES, padx=5) + + # demonstrates various button styles + btn_group = ttk.Labelframe( + master=container, text="Buttons", padding=(10, 5) + ) + btn_group.pack(fill=X) + + menu = ttk.Menu(self) + for i, t in enumerate(self.style.theme_names()): + menu.add_radiobutton(label=t, value=i) + + default = ttk.Button(master=btn_group, text="solid button") + default.pack(fill=X, pady=5) + default.focus_set() + + mb = ttk.Menubutton( + master=btn_group, + text="solid menubutton", + bootstyle=SECONDARY, + menu=menu, + ) + mb.pack(fill=X, pady=5) + + cb = ttk.Checkbutton( + master=btn_group, + text="solid toolbutton", + bootstyle=(SUCCESS, TOOLBUTTON), + ) + cb.invoke() + cb.pack(fill=X, pady=5) + + ob = ttk.Button( + master=btn_group, text="outline button", bootstyle=(INFO, OUTLINE) + ) + ob.pack(fill=X, pady=5) + + mb = ttk.Menubutton( + master=btn_group, + text="outline menubutton", + bootstyle=(WARNING, OUTLINE), + menu=menu, + ) + mb.pack(fill=X, pady=5) + + cb = ttk.Checkbutton( + master=btn_group, + text="outline toolbutton", + bootstyle="success-outline-toolbutton", + ) + cb.pack(fill=X, pady=5) + + lb = ttk.Button(master=btn_group, text="link button", bootstyle=LINK) + lb.pack(fill=X, pady=5) + + cb1 = ttk.Checkbutton( + master=btn_group, + text="rounded toggle", + bootstyle=(SUCCESS, ROUND, TOGGLE), + ) + cb1.invoke() + cb1.pack(fill=X, pady=5) + + cb2 = ttk.Checkbutton( + master=btn_group, text="squared toggle", bootstyle=(SQUARE, TOGGLE) + ) + cb2.pack(fill=X, pady=5) + cb2.invoke() + + input_group = ttk.Labelframe( + master=container, text="Other input widgets", padding=10 + ) + input_group.pack(fill=BOTH, pady=(10, 5), expand=YES) + entry = ttk.Entry(input_group) + entry.pack(fill=X) + entry.insert(END, "entry widget") + + password = ttk.Entry(master=input_group, show="•") + password.pack(fill=X, pady=5) + password.insert(END, "password") + + spinbox = ttk.Spinbox(master=input_group, from_=0, to=100) + spinbox.pack(fill=X) + spinbox.set(45) + + cbo = ttk.Combobox( + master=input_group, + text=self.style.theme.name, + values=self.style.theme_names(), + ) + cbo.pack(fill=X, pady=5) + cbo.current(self.style.theme_names().index(self.style.theme.name)) + + de = ttk.DateEntry(input_group) + de.pack(fill=X) + + def create_left_frame(self): + """Create all the left frame widgets""" + container = ttk.Frame(self) + container.pack(side=LEFT, fill=BOTH, expand=YES, padx=5) + + # demonstrates all color options inside a label + color_group = ttk.Labelframe( + master=container, text="Theme color options", padding=10 + ) + color_group.pack(fill=X, side=TOP) + for color in self.style.colors: + cb = ttk.Button(color_group, text=color, bootstyle=color) + cb.pack(side=LEFT, expand=YES, padx=5, fill=X) + + # demonstrates all radiobutton widgets active and disabled + cr_group = ttk.Labelframe( + master=container, text="Checkbuttons & radiobuttons", padding=10 + ) + cr_group.pack(fill=X, pady=10, side=TOP) + cr1 = ttk.Checkbutton(cr_group, text="selected") + cr1.pack(side=LEFT, expand=YES, padx=5) + cr1.invoke() + cr2 = ttk.Checkbutton(cr_group, text="deselected") + cr2.pack(side=LEFT, expand=YES, padx=5) + cr3 = ttk.Checkbutton(cr_group, text="disabled", state=DISABLED) + cr3.pack(side=LEFT, expand=YES, padx=5) + cr4 = ttk.Radiobutton(cr_group, text="selected", value=1) + cr4.pack(side=LEFT, expand=YES, padx=5) + cr4.invoke() + cr5 = ttk.Radiobutton(cr_group, text="deselected", value=2) + cr5.pack(side=LEFT, expand=YES, padx=5) + cr6 = ttk.Radiobutton( + cr_group, text="disabled", value=3, state=DISABLED + ) + cr6.pack(side=LEFT, expand=YES, padx=5) + + # demonstrates the treeview and notebook widgets + ttframe = ttk.Frame(container) + ttframe.pack(pady=5, fill=X, side=TOP) + table_data = [ + ("South Island, New Zealand", 1), + ("Paris", 2), + ("Bora Bora", 3), + ("Maui", 4), + ("Tahiti", 5), + ] + tv = ttk.Treeview( + master=ttframe, columns=[0, 1], show="headings", height=5 + ) + for row in table_data: + tv.insert("", END, values=row) + tv.selection_set("I001") + tv.heading(0, text="City") + tv.heading(1, text="Rank") + tv.column(0, width=300) + tv.column(1, width=70, anchor=CENTER) + tv.pack(side=LEFT, anchor=NE, fill=X) + + nb = ttk.Notebook(ttframe) + nb.pack(side=LEFT, padx=(10, 0), expand=YES, fill=BOTH) + nb_text = ( + "This is a notebook tab.\nYou can put any widget you want here." + ) + nb.add(ttk.Label(nb, text=nb_text), text="Tab 1", sticky=NW) + nb.add( + child=ttk.Label(nb, text="A notebook tab."), + text="Tab 2", + sticky=NW, + ) + nb.add(ttk.Frame(nb), text="Tab 3") + nb.add(ttk.Frame(nb), text="Tab 4") + nb.add(ttk.Frame(nb), text="Tab 5") + + # text widget + txt = ttk.Text(master=container, height=5, width=50, wrap="none") + txt.insert(END, DemoWidgets.ZEN) + txt.pack(side=LEFT, anchor=NW, pady=5, fill=BOTH, expand=YES) + + # demonstrates scale, progressbar, and meter, and scrollbar widgets + lframe_inner = ttk.Frame(container) + lframe_inner.pack(fill=BOTH, expand=YES, padx=10) + scale = ttk.Scale( + master=lframe_inner, orient=HORIZONTAL, value=75, from_=100, to=0 + ) + scale.pack(fill=X, pady=5, expand=YES) + + ttk.Progressbar( + master=lframe_inner, + orient=HORIZONTAL, + value=50, + ).pack(fill=X, pady=5, expand=YES) + + ttk.Progressbar( + master=lframe_inner, + orient=HORIZONTAL, + value=75, + bootstyle="success-striped", + ).pack(fill=X, pady=5, expand=YES) + + m = ttk.Meter( + master=lframe_inner, + metersize=150, + amountused=45, + subtext="meter widget", + bootstyle="info", + interactive=True, + ) + m.pack(pady=10) + + sb = ttk.Scrollbar( + master=lframe_inner, + orient=HORIZONTAL, + ) + sb.set(0.1, 0.9) + sb.pack(fill=X, pady=5, expand=YES) + + sb = ttk.Scrollbar( + master=lframe_inner, orient=HORIZONTAL, bootstyle="danger-round" + ) + sb.set(0.1, 0.9) + sb.pack(fill=X, pady=5, expand=YES) + + +if __name__ == "__main__": + + creator = ThemeCreator() + creator.mainloop() diff --git a/pmostools/peptools/welcome.py b/pmostools/peptools/welcome.py new file mode 100755 index 0000000..efa3d05 --- /dev/null +++ b/pmostools/peptools/welcome.py @@ -0,0 +1,257 @@ +""" +* Author: "PepDebian(peppermintosteam@proton.me) +* +* License: SPDX-License-Identifier: GPL-3.0-or-later +* +* This script is used to welcome the user to the system +""" + +import os +import tkinter as tk +import ttkbootstrap as ttk +import welconf +import welfunc + + +# setting up window +pewel = welconf.bbstyle +pewel.resizable(False, False) +WINDOW_HEIGHT = 360 +WINDOW_WIDTH = 720 + + +def wbase(): + """Check to see what base is being loaded""" + if os.path.exists("/etc/devuan_version"): + pewel.title(welconf.DEVUAN_TITLE) + lpath = welconf.devcandy + l_g = tk.Label(pewel, image=lpath, width=128, height=128, borderwidth=0) + l_g.grid( + columnspan=2, + row=0, + column=0, + ipadx=5, + ipady=5, + padx=5, + pady=5) + elif os.path.exists("/etc/debian_version"): + pewel.title(welconf.DEBIAN_TITLE) + +def center_screen(): + """ gets the coordinates of the center of the screen """ + screen_width = pewel.winfo_screenwidth() + screen_height = pewel.winfo_screenheight() + # Coordinates of the upper left corner of the window to make the window + # appear in the center + x_cordinate = int((screen_width / 2) - (WINDOW_WIDTH / 2)) + y_cordinate = int((screen_height / 2) - (WINDOW_HEIGHT / 2)) + pewel.geometry("{}x{}+{}+{}".format(WINDOW_WIDTH, + WINDOW_HEIGHT, x_cordinate, y_cordinate)) + + +def kon(): + """ This feature will is the disabled the welcome screen""" + # This will get the logged in user + gusr = tk.StringVar() + gusr.set(os.getlogin()) + # set it in to as entry box + txtcappuser = ttk.Entry(pewel, bootstyle="success", width=10, text=gusr) + # set that as a variable + usr = txtcappuser.get() + # Create path variable + spath = "/home/" + usr + "/.config/autostart/" + if os.path.isfile(spath + "Welcome_auto.desktop"): + cbas = ttk.Checkbutton( + pewel, + text="Disable Auto start", + bootstyle="light-round-toggle", + command=kon_del) + cbas.grid(row=2, column=6) + else: + cbas = ttk.Checkbutton( + pewel, + text="Disable Auto start", + bootstyle="light-round-toggle") + + +def kon_del(): + """ This feature will let the user disable the welcome screen""" + # This will get the logged in user + gusr = tk.StringVar() + gusr.set(os.getlogin()) + # set it in to as entry box + txtcappuser = ttk.Entry(pewel, bootstyle="danger", width=10, text=gusr) + # set that as a variable + usr = txtcappuser.get() + # Create path variable + spath = "/home/" + usr + "/.config/autostart/" + if os.path.isfile(spath + "Welcome_auto.desktop"): + os.remove(spath + "Welcome_auto.desktop") + print("sym deleted") + else: + print("no welcome sym to delete") + + +# Set the window icon +pewel.tk.call('wm', 'iconphoto', pewel._w, tk.PhotoImage( + file='/usr/share/pixmaps/peppermint-old.png')) + +# logo for the splash +lpath2 = welconf.peppertxt +lg2 = tk.Label(pewel, image=lpath2, width=600, height=80, borderwidth=0) +lg2.grid(row=0, column=1, columnspan=2, ipadx=5, ipady=5, padx=5, pady=5) + +# Frame that managed the Welcome To Peppermint Section +fsw = ttk.Frame(pewel, width=200) +fsw.grid(row=1, column=0, columnspan=4) + +# Frame title +# Frame that manages the Community buttons +fs = ttk.Frame(fsw) +fs.grid(row=0, column=0, ipadx=5, ipady=5, padx=5, pady=5, sticky='e') + +wms = ttk.Labelframe(fs, bootstyle="warning", text="Welcome to Peppermint OS") +wms.grid(row=0, column=0, ipadx=0, ipady=0, padx=10, pady=10, sticky='e') + + +# Frame Buttons +btnsaw = ttk.Button( + wms, + text="Suggested", + cursor="hand2", + style="danger-outline", + command=welfunc.suggested_packages) +btnsaw.grid(row=0, column=0, ipadx=5, ipady=5, padx=5, pady=5, sticky='ew') + +btnph = ttk.Button( + wms, + text="Peppermint Hub", + cursor="hand2", + bootstyle="danger-outline", + command=welfunc.hub) +btnph.grid(row=1, column=0, ipadx=5, ipady=5, padx=5, pady=5, sticky='ew') + +btnpdocs = ttk.Button( + wms, + text="Pep Docs", + cursor="hand2", + bootstyle="danger-outline", + command=welfunc.pep_docs) +btnpdocs.grid(row=2, column=0, ipadx=5, ipady=5, padx=5, pady=5, sticky='ew') + +btnbdl = ttk.Button( + wms, + text="Build Log", + cursor="hand2", + bootstyle="danger-outline", + command=welfunc.build_date) +btnbdl.grid(row=3, column=0, ipadx=5, ipady=5, padx=5, pady=5, sticky='ew') + +# Frame labels +lblsaw = ttk.Label(wms, text=" - Select Packages and Web Browsers ", + wraplength=300) +lblsaw.grid(row=0, column=1, sticky='ew') + +lblph = ttk.Label(wms, text=" - System changes, and customizations ", + wraplength=300) +lblph.grid(row=1, column=1, sticky='ew') + +lblpdocs = ttk.Label(wms, text=" - Peppermint Online Documentation", + wraplength=300) +lblpdocs.grid(row=2, column=1, sticky='ew') + +lblbdl = ttk.Label(wms, text=" - Review the build log", + wraplength=300) +lblbdl.grid(row=3, column=1, sticky='ew') + +# Frame Title +soc = ttk.Labelframe(fs, bootstyle="warning", text="The Peppermint Community") +soc.grid(row=0, column=1, ipadx=5, ipady=5, padx=5, pady=5, sticky='e') +#soc.grid(row=5, column=0) + +# Frame message +lblqt = ttk.Label(soc, text=welconf.MESSAGE_TEXT, + wraplength=250) +lblqt.grid(row=3, column=0, columnspan=3,sticky='ew') + +# Social Icons +icotms = welconf.tms +icotfm = welconf.fm +icotmat = welconf.mat +icotcb = welconf.cb + +# Social buttons +btncf = ttk.Button( + soc, + text="Forums", + cursor="hand2", + bootstyle="dark-outline", + image=icotfm, + command=welfunc.source_forge) +btncf.grid( + row=0, + column=0, + ipadx=10, + ipady=10, + padx=10, + pady=10, + sticky='nesw' + ) +btnmt = ttk.Button( + soc, + text="-matrix-", + cursor="hand2", + bootstyle="dark-outline", + image=icotmat, + command=welfunc.matrix) +btnmt.grid( + row=0, + column=1, + ipadx=10, + ipady=10, + padx=10, + pady=10, + sticky='nesw' + ) +btnmas = ttk.Button( + soc, + text="Mastodon", + cursor="hand2", + bootstyle="dark-outline", + image=icotms, + command=welfunc.mastodon) +btnmas.grid( + row=1, + column=1, + ipadx=10, + ipady=10, + padx=10, + pady=10, + sticky='nesw' + ) +btncb = ttk.Button( + soc, + text="CodeBerg", + cursor="hand2", + bootstyle="dark-outline", + image=icotcb, + command=welfunc.code_berg) +btncb.grid( + row=1, + column=0, + ipadx=10, + ipady=10, + padx=10, + pady=10, + sticky='nesw' + ) + + +# set the correct build branding +wbase() +kon() + +# call Center screen +center_screen() +# run the application +pewel.mainloop() diff --git a/pmostools/peptools/welconf.py b/pmostools/peptools/welconf.py new file mode 100644 index 0000000..56c815e --- /dev/null +++ b/pmostools/peptools/welconf.py @@ -0,0 +1,48 @@ +""" +* Author: "PepDebian(peppermintosteam@proton.me) +* +* License: SPDX-License-Identifier: GPL-3.0-or-later +* +* this files should be used for the over all style and design of +* the Welcome Screen +""" +from tkinter import PhotoImage +import ttkbootstrap as ttk +import os + +# This will set the style to used for boostrap +# just change the name to what is needed for the +# the system +bbstyle = ttk.Window(themename="darkly") + +# set the title of the window +DEBIAN_TITLE = "Welcome to Peppermint - (Debian)" +DEVUAN_TITLE = "Welcome to Peppermint - (Devuan)" + +# set the logo for the screen +#debcandy = PhotoImage(file='/home/tommy/Developer/bubbles/iso_configs/pmostools/peptools/images/' +# 'pep-logo-deb.png' + # ) +#devcandy = PhotoImage(file='/home/tommy/Developer/bubbles/iso_configs/pmostools/peptools/images/' +# 'pep-logo-dev.png' +# ) + +# This will get the logged in user +gusr = os.getlogin() +spath = "/home/" + gusr + "/.local/share/pmostools/peptools" + +# add the logo text +peppertxt = PhotoImage(file=spath + '/images/peppermint-word-white.png' + ) + + + +# set the icons that are used for the community buttons +tms = PhotoImage(file=spath + '/images/mn.png') +fm = PhotoImage(file=spath + '/images/sf.png') +mat = PhotoImage(file=spath + '/images/mt.png') +cb = PhotoImage(file=spath + '/images/cb.png') + +# set the message for the community section +MESSAGE_TEXT = ("Come join the conversation about" + " PeppermintOS in the community.") diff --git a/pmostools/peptools/welfunc.py b/pmostools/peptools/welfunc.py new file mode 100644 index 0000000..808037e --- /dev/null +++ b/pmostools/peptools/welfunc.py @@ -0,0 +1,62 @@ +""" +* Author: "PepDebian(peppermintosteam@proton.me) +* +* License: SPDX-License-Identifier: GPL-3.0-or-later +* +* This file is used to keep functions that are used for the Welcome +* Screen +""" +import os.path + + +# This will get the logged in user +gusr = os.getlogin() +spath = "/home/" + gusr + "/.local/share/pmostools/peptools" + +def suggested_packages(): + """ Open the suggested packages """ + os.system('python3 ' + spath + '/suggested.py') + +def hub(): + """Open the Hub""" + os.system('hub') + + +##SSB's### + +def pep_docs(): + """Open Pep Docs""" + os.system('luakit -U https://peppermint_os.codeberg.page/html/ &' + ) + +# Community Section + + +def mastodon(): + """Open Mastodon""" + os.system('luakit -U https://fosstodon.org/@peppermintos &' + ) + + +def code_berg(): + """Open Codeberg""" + os.system('luakit -U https://codeberg.org/Peppermint_OS & ' + ) + + +def matrix(): + """Open Matrix""" + os.system('luakit -U https://matrix.to/#/!JhPtEbNexzFaoOkvGp:matrix.org?via=matrix.org & ' + ) + + +def source_forge(): + """Open Sourceforge""" + os.system('luakit -U https://sourceforge.net/p/peppermintos/pepos/ & ' + ) + + +def build_date(): + """Open BuildDate wiki""" + os.system('luakit -U https://sourceforge.net/p/peppermintos/pepwiki/BuildDate/ & ' + ) diff --git a/polkit/org.freedesktop.pepkumo.policy b/polkit/org.freedesktop.pepkumo.policy new file mode 100644 index 0000000..329e97b --- /dev/null +++ b/polkit/org.freedesktop.pepkumo.policy @@ -0,0 +1,19 @@ + + + + + Run PepKumo SSB creater + Authentication to run Kumo is required + accessories-text-editor + + auth_admin + auth_admin + auth_admin + + /opt/pypep/pepkumo.py + true + + + diff --git a/polkit/org.freedesktop.peppackages.policy b/polkit/org.freedesktop.peppackages.policy new file mode 100644 index 0000000..f95e9ab --- /dev/null +++ b/polkit/org.freedesktop.peppackages.policy @@ -0,0 +1,19 @@ + + + + + Run Package Selection program + Authentication to run PepPackages is required + accessories-text-editor + + auth_admin + auth_admin + auth_admin + + /opt/pypep/peppackages.py + true + + + diff --git a/polkit/org.freedesktop.pepu.policy b/polkit/org.freedesktop.pepu.policy new file mode 100644 index 0000000..e2a6c8e --- /dev/null +++ b/polkit/org.freedesktop.pepu.policy @@ -0,0 +1,19 @@ + + + + + Run Peppermint Update Manager + Authentication to run Update Manager is required + accessories-text-editor + + auth_admin + auth_admin + auth_admin + + /opt/pypep/pepu.py + true + + + diff --git a/polkit/org.freedesktop.python3.policy b/polkit/org.freedesktop.python3.policy new file mode 100755 index 0000000..ad312c8 --- /dev/null +++ b/polkit/org.freedesktop.python3.policy @@ -0,0 +1,18 @@ + + + + + Run python3 program + Authentication is required to run the python3 + accessories-text-editor + + auth_admin + auth_admin + auth_admin + + /usr/bin/python3 + true + + diff --git a/polkit/org.freedesktop.ttkcreator.policy b/polkit/org.freedesktop.ttkcreator.policy new file mode 100644 index 0000000..4ca43ae --- /dev/null +++ b/polkit/org.freedesktop.ttkcreator.policy @@ -0,0 +1,19 @@ + + + + + Run TTK-Creator program + Authentication to run TTK-Creater is required + accessories-text-editor + + auth_admin + auth_admin + auth_admin + + /usr/bin/python3 /usr/local/lib/python3.9/dist-packages/ttkcreator/__main__.py + true + + + diff --git a/pylibraries/tendo-0.3.0.dist-info/INSTALLER b/pylibraries/tendo-0.3.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/pylibraries/tendo-0.3.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/pylibraries/tendo-0.3.0.dist-info/LICENSE.txt b/pylibraries/tendo-0.3.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..0496023 --- /dev/null +++ b/pylibraries/tendo-0.3.0.dist-info/LICENSE.txt @@ -0,0 +1,48 @@ +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python +alone or in any derivative version, provided, however, that PSF's +License Agreement and PSF's notice of copyright, i.e., "Copyright (c) +2001, 2002, 2003, 2004 Python Software Foundation; All Rights Reserved" +are retained in Python alone or in any derivative version prepared +by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. diff --git a/pylibraries/tendo-0.3.0.dist-info/METADATA b/pylibraries/tendo-0.3.0.dist-info/METADATA new file mode 100644 index 0000000..a4ce7d6 --- /dev/null +++ b/pylibraries/tendo-0.3.0.dist-info/METADATA @@ -0,0 +1,70 @@ +Metadata-Version: 2.1 +Name: tendo +Version: 0.3.0 +Summary: A Python library that extends some core functionality +Home-page: https://github.com/pycontribs/tendo +Author: Sorin Sbarnea +Author-email: sorin.sbarnea@gmail.com +Maintainer: Sorin Sbarnea +Maintainer-email: sorin.sbarnea@gmail.com +Keywords: tendo,tee,unicode,colorer,singleton +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Other Environment +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE.txt +Requires-Dist: six +Provides-Extra: docs +Requires-Dist: Sphinx (~=5.2.1) ; extra == 'docs' +Requires-Dist: docutils (~=0.19) ; extra == 'docs' +Requires-Dist: MarkupSafe (~=2.1.1) ; extra == 'docs' +Provides-Extra: test +Requires-Dist: coverage[toml] (>=6.5.0) ; extra == 'test' +Requires-Dist: coveralls (~=3.3.1) ; extra == 'test' +Requires-Dist: pre-commit (~=2.20.0) ; extra == 'test' +Requires-Dist: pytest-cache (~=1.0) ; extra == 'test' +Requires-Dist: pytest-cov (~=3.0.0) ; extra == 'test' +Requires-Dist: pytest-html (~=3.1.1) ; extra == 'test' +Requires-Dist: pytest-instafail (~=0.4.2) ; extra == 'test' +Requires-Dist: pytest-xdist (~=2.5.0) ; extra == 'test' +Requires-Dist: pytest (~=7.1.3) ; extra == 'test' +Requires-Dist: wheel (~=0.37.1) ; extra == 'test' + +[![](https://img.shields.io/pypi/v/tendo.svg?colorB=green)](https://pypi.python.org/pypi/tendo/) +[![](https://img.shields.io/codecov/c/github/pycontribs/tendo/main.svg)](https://codecov.io/gh/pycontribs/tendo) +[![](https://readthedocs.org/projects/tendo/badge/?version=latest)](http://tendo.readthedocs.io) + +# tendo + +Tendo is a python module that adds basic functionality that is +not provided by Python. Read the [documentation](https://tendo.readthedocs.org/en/latest/) for more info. + +- [transparent Unicode support for text file operations (BOM detection)](https://tendo.readthedocs.org/en/latest/#module-tendo.singleton) +- [console logging coloring](https://tendo.readthedocs.org/en/latest/#module-tendo.colorer) +- enable you to use symlinks under windows +- [python tee implementation](https://tendo.readthedocs.org/en/latest/#module-tendo.colorer) for executing external programs and redirecting their output to both console/file +- [improved execfile](https://tendo.readthedocs.org/en/latest/#module-tendo.execfile2) + +## Requirements and compatibility + +- python 3.6 or newer +- tox for running tests + +## Related projects and packages + +- jaraco - http://pypi.python.org/pypi/jaraco.util +- pexpect (maybe) diff --git a/pylibraries/tendo-0.3.0.dist-info/RECORD b/pylibraries/tendo-0.3.0.dist-info/RECORD new file mode 100644 index 0000000..ffe7090 --- /dev/null +++ b/pylibraries/tendo-0.3.0.dist-info/RECORD @@ -0,0 +1,45 @@ +tendo-0.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +tendo-0.3.0.dist-info/LICENSE.txt,sha256=OxeG9HHdLhJfWm6Hf6fUu2LVhQIKMdkOI7aD8NDwnGk,2382 +tendo-0.3.0.dist-info/METADATA,sha256=3ND7DyJNzL42x9jdLXg65BuaiJe2bkSdWy-R0E3MJF8,3222 +tendo-0.3.0.dist-info/RECORD,, +tendo-0.3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +tendo-0.3.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +tendo-0.3.0.dist-info/top_level.txt,sha256=mmQBS5KFPLIkGpS8uCmFiJxGLhsSSAInN47ST1KPjmc,6 +tendo/__init__.py,sha256=LiZKhNQTguXDtwYRvv3wOPrlXQ3tFXd17Mqc_uxaISU,569 +tendo/__pycache__/__init__.cpython-39.pyc,, +tendo/__pycache__/_version.cpython-39.pyc,, +tendo/__pycache__/ansiterm.cpython-39.pyc,, +tendo/__pycache__/colorer.cpython-39.pyc,, +tendo/__pycache__/execfile2.cpython-39.pyc,, +tendo/__pycache__/singleton.cpython-39.pyc,, +tendo/__pycache__/tee.cpython-39.pyc,, +tendo/__pycache__/unicode.cpython-39.pyc,, +tendo/_version.py,sha256=_UJonOENX4r-qwHEWuAPWP39KNmboa9_GnJ2v2Bb0P8,176 +tendo/ansiterm.py,sha256=SDX3yhayfzAWDU3n992kdAYKiFS8gRzTpINyjdv9q30,10910 +tendo/colorer.py,sha256=jsl_BEMwPND5fwPHPK3-Wdkx8TpvrtkLorK5iwIQDLY,5438 +tendo/execfile2.py,sha256=r1RWOnTwUGf7G009sXz1LdXp6VQ2fWJlGncajqkMPYM,1917 +tendo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +tendo/singleton.py,sha256=uth-bsBUymT8UseXVSZ9iUrdaY7U5Qac-2-h9HBYE38,3265 +tendo/tee.py,sha256=gJYRskfTJCrYpNqWuONKAns5etVmbtl-z-rktN5e03E,8754 +tendo/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +tendo/tests/__pycache__/__init__.cpython-39.pyc,, +tendo/tests/__pycache__/test_colorer.cpython-39.pyc,, +tendo/tests/__pycache__/test_execfile2.cpython-39.pyc,, +tendo/tests/__pycache__/test_singleton.cpython-39.pyc,, +tendo/tests/__pycache__/test_tee.cpython-39.pyc,, +tendo/tests/__pycache__/test_unicode.cpython-39.pyc,, +tendo/tests/assets/full_sample_utf8_bom.txt,sha256=KkNqDwebtlS3xhAfplqf3tHcOObMmouytKn_4MroI0Q,49 +tendo/tests/assets/sample_ucs2_be.txt,sha256=__3Aog-FfxCuKo6NKHunfeRXGSpuUlXnARO3UBVP4VA,86 +tendo/tests/assets/sample_ucs2_le.txt,sha256=uJwfDIb4NO1UWXuwG02bCmcvB-4UIM4Z7VhOfKaEN-w,86 +tendo/tests/assets/sample_utf8.txt,sha256=C6RMLCgPWRPxEugZTJy3l4LTUVPq6nmR-cOt6aBvC9Q,9 +tendo/tests/assets/sample_utf8_bom.txt,sha256=1wfcaw-EIPdBGFtm8UFbI9WrzVbq0sXN8JQXauJIUEI,57 +tendo/tests/assets/utf8-after-append.txt,sha256=cpkiZTGrK8QbpxjMn2YZ1vhzhs4wLN5iyqXAuLKuZ78,79 +tendo/tests/assets/utf8-invalid.txt,sha256=2RYQGQO5gNv5DuyEk4huGwQ6tzxjT-Gz_3NcbyOXufQ,20334 +tendo/tests/assets/utf8.txt,sha256=EBPTlbkWEzl0touJx-LcPMYwApkP432FqDxX-6YOmdU,17 +tendo/tests/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +tendo/tests/test_colorer.py,sha256=qWk_-ntdZtL3bLV_Q1tWUuK3-UwTGgL7DdUwEpdG2i0,1170 +tendo/tests/test_execfile2.py,sha256=SXYLMnOX-HB_5pjpd_0D_HOnbn6E-yBqURup8CB0Zn4,1171 +tendo/tests/test_singleton.py,sha256=8d8ujn7JMxzSsDImJS2MGNMPXcdS_BMpR2GZK4s5BeU,1624 +tendo/tests/test_tee.py,sha256=RFVdrFXYqQuE8VXRxLunjnhoXYc2zd0lxuqz4L1MhpI,1283 +tendo/tests/test_unicode.py,sha256=96ZV5MkMgXu8qw7ojQ20zUWD6bIJb_akyLGXoZ-5hQs,1373 +tendo/unicode.py,sha256=oxBES0O3NmqkSJ9_nPI_dxCw3a9lR95-EjrTBRNBavs,3124 diff --git a/pylibraries/tendo-0.3.0.dist-info/REQUESTED b/pylibraries/tendo-0.3.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/pylibraries/tendo-0.3.0.dist-info/WHEEL b/pylibraries/tendo-0.3.0.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/pylibraries/tendo-0.3.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/pylibraries/tendo-0.3.0.dist-info/top_level.txt b/pylibraries/tendo-0.3.0.dist-info/top_level.txt new file mode 100644 index 0000000..e187ff8 --- /dev/null +++ b/pylibraries/tendo-0.3.0.dist-info/top_level.txt @@ -0,0 +1 @@ +tendo diff --git a/pylibraries/tendo/__init__.py b/pylibraries/tendo/__init__.py new file mode 100644 index 0000000..60d03ed --- /dev/null +++ b/pylibraries/tendo/__init__.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# Licensed to PSF under a Contributor Agreement. +# See http://www.python.org/psf/license for licensing details. +from __future__ import absolute_import +import sys + +from ._version import __version__ + +__author__ = "Sorin Sbarnea" +__copyright__ = "Copyright 2010-2022, Sorin Sbarnea" +__email__ = "sorin.sbarnea@gmail.com" +__status__ = "Production" +__all__ = ('tee', 'colorer', 'unicode', + 'execfile2', 'singleton', 'ansiterm', '__version__') + + +if sys.hexversion < 0x02050000: + sys.exit("Python 2.5 or newer is required by tendo module.") diff --git a/pylibraries/tendo/__pycache__/__init__.cpython-39.pyc b/pylibraries/tendo/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..68d13226eaf272ebd4ba7c9de1a6b1ee049ea6fd GIT binary patch literal 637 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 R6TMBF*=GJvj7KBve*vNO&jkPg literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/__pycache__/colorer.cpython-39.pyc b/pylibraries/tendo/__pycache__/colorer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d58f00d6fa3f05d0fed2856c9a3080eff7c01c5 GIT binary patch literal 3171 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 literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/__pycache__/singleton.cpython-39.pyc b/pylibraries/tendo/__pycache__/singleton.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..199d3b078ff8138baed24179fb05d20d2d5bea6c GIT binary patch literal 2988 zcmZt|O>Y~=b!K?*t^1meNB*gomUsMx5DWY()M116tb-=dr|uN?GQI_eiSx$VHBV#5d;+g z;YeudjVDn$jKzbrP(d1s`}?8DRg|UMZCiW3p9YEWeO>qcB;%79*A3r)H3{PKw* z`CYe&l(?A(;Z86V1rVk@0~{)-vb2*=r47PX(G&}SN6r36_Tr_xcRKBSK(FgLQOX>dLcLg_FHjK6ikf*#lj$EeZeB*BozfpaF(U<6hJ5#n8K1E97U<< z)awks2Lm?ECTuTA6;m0@CHMlCfRn5UWt1y6kXgdyBu&9OV9fGB25~H6Hl7sfO-byc zYQJo=5Ebj7kO=h?Qm`P+aB)fpIt8&50iT{rP;4)XW4I16U;%r1k?S(-FHbGl+}vbQ zl4lYOz$h5AI-L$n1pEDuR4T20Is$z%lRc=}u8>nUDa2qBv#4~)DA*OJ4k(z|7a=4C zXZ;_Cl$(dgti$o-Su+v0Ep}f6J?i3{p5h!$Kz~U_2 zVS^|ZV6z$l0@s+YtREC6bm%q)ipAh}xB)Rj#93(I_Ng2GEVJE+ms|&Nu$xIg;-x2p z$uPm78ct{YjXcEUh7e>esct|_;tUFOXmSHYh7ckpSTc!Kl!LO%L|=xnDAUB~hU%jo z9cKItrbEi8D4fKBgiO5XXp?zGF2ZOKgI7X6Xd+ARJ?U-O|5@H>l zeOTolj4c?2f{w!J0rAO!CD#rtSkd%_vc~q@F|eZCag|$F$$~B@&T~_F3kzmwWM-~% zXJK*gfL1eusy<&?Km*($2QU^WhpNVWmDdl*m?DM};NRpcJJ$D9uFbwM zbeHgSLFGS`HE)$`xM$7Z!8P1igK_yDV8-kB$O3i#*)x*<(IM)}_jKB%1b@n%Z!E|Q z`_MV89J&XU@y~f<$CCd8`B(X>k$mlld`H#0cyW8_KG})Ccdw9HRu-xWfxO84difF zSEb0~AQak5GnteGN+h{qQXdM18y-pR#@TQvq^@w$pA5AJRB8Yu+7u)= zwyb)&2ENuF_8n|M+ELS7XbK)v{qvVoVrAiTi{AD94`Ek42SBJx9qL)vsdM6-4vV@c z7PU?s%cac|*KW}Uwd^K9hpwO0ttM^KI;_hZb&8lewdgg#m)HiKz4hvM&LoGJTxTEV z3Z|DS`G3O#QkWEVOvKp>MaLGWP#SlXeL%Q%WJ#`^c?Iscrc9i971kcE(_g9D9E*2F z)y54>edUZ-=gsom&@JymQTz#t;?neAlpF%>?6zvjzpECMF%&oOYN$1CW7ELmQ*?eA z+6b(kK^bzO_$y68L!57sTV>HywoTg0Pr-!Q`nM^jR?vrr3rUd4JM5F<^$C3Kb@>k5 zkQ)d-0HCX*pa_(b@-pr!VVuFg7vv%_LMF7bgR3rr8vss| zpkFIc;wtLSDZ3gf)esqZ2W^sMokUcCoZaqby>ZM2*9$0AgQtFA#qcxd`S f>9(IxNqb+Ee@r)#yl4UNsQKA->e(%)_09hQsLVME literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/__pycache__/tee.cpython-39.pyc b/pylibraries/tendo/__pycache__/tee.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a0d052d04002cd759b97c8cd678e661beebbbc3 GIT binary patch literal 6232 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 Cs5RXH literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/__pycache__/unicode.cpython-39.pyc b/pylibraries/tendo/__pycache__/unicode.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1695e9272ca648510844145ec1402c24903882fb GIT binary patch literal 2326 zcmZuz&5zqe6!$pJ=YDi|`#~#J)lgcsDcOX!2o(udsHK%ar6Af0kxSxu#)-R*ZDz*1 zn<{%M94qz2iNl5)C;kAWR_gu_9Oep_{SOclD({WGZVKzH$9^;K{pP**`(?83(o(~M z>+V1AviBD)>kl;MuM!xaz%8%Az$`{WtD8MZml)gb+U8m6mblX`vnngI%J-z}Fmm5& z)(#-fx~GXacmdT1ojsd5GxCJ&mf^iLvxxPL6`ge~J$=V)gSCL z){L0-+Kg=4;zw26t52;dVf9&Q*8#)^6w#R3fKq$3Fm-y1&q#EkKvJXCnPKsa^;!m>g5xj=B{GFZtGx&^{&f#4K595V?0BN5hegr5q8 z<3}K@7=&|&KGRa+)0h!ru?ntFo%(us3r=aJ=npPg2P$wUq zVke)1g`DE*lM+N#KrE-+r=Yh7qd-!@lhF0Jq+df?*K3A@$S9Qvu9u2Wmv! zhz8?C#9N%vEiNQ3sFABEKs<~BFJ_#&+pgg4MuWojV8m&}w`o64cyzhhr!0m&ypbCX zIUVqkyA{N#pm7vVs2>Ze_>Q7}5OUd~QLN|yauAao>Oh=qd9A{MvPtOI7lcDqe|`Y|9 z894Uo+-_K$EXt7B;*9GSGBN@aq{e-pdkTnntqbg@03PE=<~!d(wPfmq9%MNTIMnAd z3Q7fcs0i>1d7?HTvBKHtjMKyg9`n|s40HI;eA6=)&q-jCCGLjd!1XqJJc2Glms^MJ z0iJV8-b@Fw=s$dLXZ=PW+6fwi3}M5*E4?7-w~Yvd4X3guuC~SC08edg-8;Y^g|q_0 z{;w~8d-+1(ynut-IzaCf&HWeq&vhLS`>P-HLY~b%-+xie4S0qCegJAbuU|t?#D)06 z7iI_i@zq;U-JEjTM#mRr%ltK=0q~_CyC5@8w?!Nc+fAY!lt=R|T|?(y95P)VOca;e zK{j;R3uDQ3{q~N>6Qod=WU!;jKrdw6_tYfex`LYYAP7;kBRSvHHDId;t-GO1@TM1Y z6KcJCd)~VF+JZXf*1&?bx)N~6A!)iA#zQ#ahPn#JRfI3~LY| zRH2bxvm#F8`*|EL;&2iN%t5T+fHltz>tO2IN6@-7`_. + +Under Windows, where the escapes are not supported it does use the Windows API. + +The colored output is generated only when the console is a terminal supporting it, so if you redirect the output to a log file you will not see the escape codes in the file. + +>>> import colorer, logging +... logging.error("red line") +... logging.warn("yellow line") +... logging.info("gray line") +... logging.debug("magenta line") +""" +import copy +import logging +import os +import six +import sys + + +if (hasattr(sys.stderr, "isatty") and sys.stderr.isatty()) or \ + ('TERM' in os.environ.keys() and os.environ['TERM'] in ['linux']) or \ + ('PYCHARM_HOSTED' in os.environ.keys()): + + # Why stderr and not stdout? - because python logging module does output to stderr by default and not stdout. + # now we patch Python code to add color support to logging.StreamHandler + def add_coloring_to_emit_windows(fn): + # add methods we need to the class + def _out_handle(self): + import ctypes + return ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE) + + def _set_color(self, code): + import ctypes + # Constants from the Windows API + self.STD_OUTPUT_HANDLE = -11 + hdl = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE) + ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, code) + + setattr(logging.StreamHandler, '_set_color', _set_color) + + def new(*args): + FOREGROUND_BLUE = 0x0001 # text color contains blue. + FOREGROUND_GREEN = 0x0002 # text color contains green. + FOREGROUND_RED = 0x0004 # text color contains red. + FOREGROUND_INTENSITY = 0x0008 # text color is intensified. + FOREGROUND_WHITE = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED + # winbase.h + # STD_INPUT_HANDLE = -10 + # STD_OUTPUT_HANDLE = -11 + # STD_ERROR_HANDLE = -12 + + # wincon.h + # FOREGROUND_BLACK = 0x0000 + FOREGROUND_BLUE = 0x0001 + FOREGROUND_GREEN = 0x0002 + # FOREGROUND_CYAN = 0x0003 + FOREGROUND_RED = 0x0004 + FOREGROUND_MAGENTA = 0x0005 + FOREGROUND_YELLOW = 0x0006 + # FOREGROUND_GREY = 0x0007 + FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified. + + # BACKGROUND_BLACK = 0x0000 + # BACKGROUND_BLUE = 0x0010 + # BACKGROUND_GREEN = 0x0020 + # BACKGROUND_CYAN = 0x0030 + # BACKGROUND_RED = 0x0040 + # BACKGROUND_MAGENTA = 0x0050 + BACKGROUND_YELLOW = 0x0060 + # BACKGROUND_GREY = 0x0070 + BACKGROUND_INTENSITY = 0x0080 # background color is intensified. + + levelno = args[1].levelno + if levelno >= 50: + color = BACKGROUND_YELLOW | FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY + elif levelno >= 40: + color = FOREGROUND_RED | FOREGROUND_INTENSITY + elif levelno >= 30: + color = FOREGROUND_YELLOW | FOREGROUND_INTENSITY + elif levelno >= 20: + color = FOREGROUND_GREEN + elif levelno >= 10: + color = FOREGROUND_MAGENTA + else: + color = FOREGROUND_WHITE + args[0]._set_color(color) + + ret = fn(*args) + args[0]._set_color(FOREGROUND_WHITE) + # print "after" + return ret + return new + + def add_coloring_to_emit_ansi(fn): + # add methods we need to the class + def new(*args): + # new_args = args + if len(args) == 2: + new_args = (args[0], copy.copy(args[1])) + else: + new_args = (args[0], copy.copy(args[1]), args[2:]) + if hasattr(args[0], 'baseFilename'): + return fn(*args) + levelno = new_args[1].levelno + if levelno >= 50: + color = '\x1b[31m' # red + elif levelno >= 40: + color = '\x1b[31m' # red + elif levelno >= 30: + color = '\x1b[33m' # yellow + elif levelno >= 20: + color = '\x1b[32m' # green + elif levelno >= 10: + color = '\x1b[35m' # pink + else: + color = '\x1b[0m' # normal + try: + new_args[ + 1].msg = color + six.text_type(new_args[1].msg) + '\x1b[0m' # normal + except Exception as e: + raise e + return fn(*new_args) + return new + + import platform + if platform.system() == 'Windows': + # Windows does not support ANSI escapes and we are using API calls to + # set the console color + logging.StreamHandler.emit = add_coloring_to_emit_windows( + logging.StreamHandler.emit) + else: + # all non-Windows platforms are supporting ANSI escapes so we use them + logging.StreamHandler.emit = add_coloring_to_emit_ansi( + logging.StreamHandler.emit) + # log = logging.getLogger() + # log.addFilter(log_filter()) + # //hdlr = logging.StreamHandler() + # //hdlr.setFormatter(formatter()) diff --git a/pylibraries/tendo/execfile2.py b/pylibraries/tendo/execfile2.py new file mode 100644 index 0000000..bd413fa --- /dev/null +++ b/pylibraries/tendo/execfile2.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +import shlex +import sys + +if sys.hexversion > 0x03000000: + def execfile(file, globals=globals(), locals=locals()): + fh = open(file, "r") + if not fh: + raise Exception("Unable to open %s." % file) + exec(fh.read() + "\n", globals, locals) + + +def execfile2(filename, _globals=dict(), _locals=dict(), cmd=None, quiet=False): + """Execute a Python script using :py:func:`execfile`. + + In addition to Python :py:func:`execfile` this method can temporary change the argv params. + + This enables you to call an external python script that requires + command line arguments without leaving current python interpretor. + + `cmd` can be a string with command line arguments or a list or arguments + + The return value is a numeric exit code similar to the one used for command line tools: + + - 0 - if succesfull; this applies if script receives SystemExit with error code 0 + - 1 - if SystemExit does not contain an error code or if other Exception is received. + - x - the SystemExit error code (if present) + """ + _globals['__name__'] = '__main__' + saved_argv = sys.argv # we save sys.argv + if cmd: + sys.argv = list([filename]) + if isinstance(cmd, list): + sys.argv.append(cmd) + else: + sys.argv.extend(shlex.split(cmd)) + exit_code = 0 + try: + exec( + compile(open(filename).read(), filename, 'exec'), _globals, _locals) + + except SystemExit: + type, e, tb = sys.exc_info() + if isinstance(e.code, int): + exit_code = e.code # this could be 0 if you do sys.exit(0) + else: + exit_code = 1 + except Exception: + if not quiet: + import traceback + traceback.print_exc(file=sys.stderr) + exit_code = 1 + finally: + if cmd: + sys.argv = saved_argv # we restore sys.argv + return exit_code diff --git a/pylibraries/tendo/py.typed b/pylibraries/tendo/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pylibraries/tendo/singleton.py b/pylibraries/tendo/singleton.py new file mode 100644 index 0000000..7668ced --- /dev/null +++ b/pylibraries/tendo/singleton.py @@ -0,0 +1,92 @@ +#! /usr/bin/env python + +import logging +import os +import sys +import tempfile + + +if sys.platform != "win32": + import fcntl + + +class SingleInstanceException(BaseException): + pass + + +class SingleInstance(object): + + """Class that can be instantiated only once per machine. + + If you want to prevent your script from running in parallel just instantiate SingleInstance() class. If is there another instance already running it will throw a `SingleInstanceException`. + + >>> import tendo + ... me = SingleInstance() + + This option is very useful if you have scripts executed by crontab at small amounts of time. + + Remember that this works by creating a lock file with a filename based on the full path to the script file. + + Providing a flavor_id will augment the filename with the provided flavor_id, allowing you to create multiple singleton instances from the same file. This is particularly useful if you want specific functions to have their own singleton instances. + """ + + def __init__(self, flavor_id="", lockfile=""): + self.initialized = False + if lockfile: + self.lockfile = lockfile + else: + basename = os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace( + "/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock' + self.lockfile = os.path.normpath( + tempfile.gettempdir() + '/' + basename) + + logger.debug("SingleInstance lockfile: " + self.lockfile) + if sys.platform == 'win32': + try: + # file already exists, we try to remove (in case previous + # execution was interrupted) + if os.path.exists(self.lockfile): + os.unlink(self.lockfile) + self.fd = os.open( + self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR) + except OSError: + type, e, tb = sys.exc_info() + if e.errno == 13: + logger.error( + "Another instance is already running, quitting.") + raise SingleInstanceException() + print(e.errno) + raise + else: # non Windows + self.fp = open(self.lockfile, 'w') + self.fp.flush() + try: + fcntl.lockf(self.fp, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + logger.warning( + "Another instance is already running, quitting.") + raise SingleInstanceException() + self.initialized = True + + def __del__(self): + if not self.initialized: + return + try: + if sys.platform == 'win32': + if hasattr(self, 'fd'): + os.close(self.fd) + os.unlink(self.lockfile) + else: + fcntl.lockf(self.fp, fcntl.LOCK_UN) + # os.close(self.fp) + if os.path.isfile(self.lockfile): + os.unlink(self.lockfile) + except Exception as e: + if logger: + logger.warning(e) + else: + print("Unloggable error: %s" % e) + sys.exit(-1) + + +logger = logging.getLogger("tendo.singleton") diff --git a/pylibraries/tendo/tee.py b/pylibraries/tendo/tee.py new file mode 100644 index 0000000..6660f8c --- /dev/null +++ b/pylibraries/tendo/tee.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python +# encoding: utf-8 +import codecs +import logging +import os +import pipes +from six import string_types +import subprocess +import sys +import time +import types +import unittest + +global logger +global stdout +global stderr +global timing +global log_command + +logger = None +stdout = False +stderr = False +# print execution time of each command in the log, just after the return code +timing = True +# outputs the command being executed to the log (before command output) +log_command = True +_sentinel = object() + + +def quote_command(cmd): + """This function does assure that the command line is entirely quoted. + + This is required in order to prevent getting "The input line is too long" error message. + """ + if not (os.name == "nt" or os.name == "dos"): + # the escaping is required only on Windows platforms, in fact it will + # break cmd line on others + return cmd + if '"' in cmd[1:-1]: + cmd = '"' + cmd + '"' + return cmd + + +def system2(cmd, cwd=None, logger=_sentinel, stdout=_sentinel, log_command=_sentinel, timing=_sentinel): + # def tee(cmd, cwd=None, logger=tee_logger, console=tee_console): + """Works exactly like :func:`system` but it returns both the exit code and the output as a list of lines. + + This method returns a tuple: (return_code, output_lines_as_list). The return code of 0 means success. + """ + # if isinstance(cmd, collections.Iterable): # -- this line was replaced + # because collections.Iterable seems to be missing on Debian Python 2.5.5 + # (but not on OS X 10.8 with Python 2.5.6) + if hasattr(cmd, '__iter__'): + cmd = " ".join(pipes.quote(s) for s in cmd) + + t = time.process_time() + output = [] + if log_command is _sentinel: + log_command = globals().get('log_command') + if timing is _sentinel: + timing = globals().get('timing') + + # default to python native logger if logger parameter is not used + if logger is _sentinel: + logger = globals().get('logger') + if stdout is _sentinel: + stdout = globals().get('stdout') + + # logging.debug("logger=%s stdout=%s" % (logger, stdout)) + + f = sys.stdout + if not f.encoding or f.encoding == 'ascii': + # `ascii` is not a valid encoding by our standards, it's better to output to UTF-8 because it can encoding any Unicode text + encoding = 'utf_8' + else: + encoding = f.encoding + + def filelogger(msg): + try: + # we'll use the same endline on all platforms, you like it or not + msg += '\n' + try: + f.write(msg) + except TypeError: + f.write(msg.encode("utf-8")) + except Exception: + sys.exc_info()[1] + import traceback + print(' ****** ERROR: Exception: %s\nencoding = %s' % + (e, encoding)) + traceback.print_exc(file=sys.stderr) + sys.exit(-1) + pass + + def nop(msg): + pass + + if not logger: + mylogger = nop + elif isinstance(logger, string_types): + f = codecs.open(logger, "a+b", 'utf_8') + mylogger = filelogger + elif isinstance(logger, (types.FunctionType, types.MethodType, types.BuiltinFunctionType)): + mylogger = logger + else: + method_write = getattr(logger, "write", None) + # if we can call write() we'll aceppt it :D + # this should work for filehandles + if hasattr(method_write, '__call__'): + f = logger + mylogger = filelogger + else: + sys.exit("tee() does not support this type of logger=%s" % + type(logger)) + + if cwd is not None and not os.path.isdir(cwd): + os.makedirs(cwd) # this throws exception if fails + + cmd = quote_command(cmd) # to prevent _popen() bug + p = subprocess.Popen( + cmd, cwd=cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if log_command: + mylogger("Running: %s" % cmd) + while True: + line = "" + try: + line = p.stdout.readline() + line = line.decode(encoding) + except Exception: + e = sys.exc_info()[1] + logging.error(e) + logging.error("The output of the command could not be decoded as %s\ncmd: %s\n line ignored: %s" % + (encoding, cmd, repr(line))) + pass + + output.append(line) + if not line: + break + line = line.rstrip('\n\r') + mylogger(line) # they are added by logging anyway + if stdout: + print(line) + returncode = p.wait() + if log_command: + if timing: + def secondsToStr(t): + return time.strftime('%H:%M:%S', time.gmtime(t)) + mylogger("Returned: %d (execution time %s)\n" % + (returncode, secondsToStr(time.process_time() - t))) + else: + mylogger("Returned: %d\n" % returncode) + + # running a tool that returns non-zero? this deserves a warning + if not returncode == 0: + logging.warning("Returned: %d from: %s\nOutput %s" % + (returncode, cmd, '\n'.join(output))) + + return returncode, output + + +def system(cmd, cwd=None, logger=None, stdout=None, log_command=_sentinel, timing=_sentinel): + """This works similar to :py:func:`os.system` but add some useful optional parameters. + + * ``cmd`` - command to be executed + * ``cwd`` - optional working directory to be set before running cmd + * ``logger`` - None, a filename, handle or a function like print or :py:meth:`logging.Logger.warning` + + Returns the exit code reported by the execution of the command, 0 means success. + + >>> import os, logging + ... import tendo.tee + ... tee.system("echo test", logger=logging.error) # output using python logging + ... tee.system("echo test", logger="log.txt") # output to a file + ... f = open("log.txt", "w") + ... tee.system("echo test", logger=f) # output to a filehandle + ... tee.system("echo test", logger=print) # use the print() function for output + """ + (returncode, output) = system2(cmd, cwd=cwd, logger=logger, + stdout=stdout, log_command=log_command, timing=timing) + return returncode + + +class testTee(unittest.TestCase): + + def test_1(self): + """No CMD os.system() + + 1 sort /? ok ok + 2 "sort" /? ok ok + 3 sort "/?" ok ok + 4 "sort" "/?" ok [bad] + 5 ""sort /?"" ok [bad] + 6 "sort /?" [bad] ok + 7 "sort "/?"" [bad] ok + 8 ""sort" "/?"" [bad] ok + """ + + quotes = { + 'dir >nul': 'dir >nul', + 'cd /D "C:\\Program Files\\"': '"cd /D "C:\\Program Files\\""', + 'python -c "import os" dummy': '"python -c "import os" dummy"', + 'sort': 'sort', + } + + # we fake the os name because we want to run the test on any platform + save = os.name + os.name = 'nt' + + for key, value in quotes.items(): + resulted_value = quote_command(key) + self.assertEqual( + value, resulted_value, "Returned <%s>, expected <%s>" % (resulted_value, value)) + # ret = os.system(resulted_value) + # if not ret==0: + # print("failed") + os.name = save + + def test_2(self): + self.assertEqual(system(['python', '-V']), 0) + + def test_3(self): + self.assertEqual(system2(['python', '-V'])[0], 0) + + def test_4(self): + self.assertEqual(system(['python', '-c', "print('c c')"]), 0) + + +if __name__ == '__main__': + + # unittest.main() + import pytest + pytest.main([__file__]) + + # import pytest + # pytest.main(['--pyargs', __name__]) + """ + import colorer + import tempfile, os + + logging.basicConfig(level=logging.NOTSET, + format='%(message)s') + + # default (stdout) + print("#1") + system("python --version") + + # function/method + print("#2") + system("python --version", logger=logging.error) + + # function (this is the same as default) + print("#3") + system("python --version", logger=print) + + # handler + print("#4") + f = tempfile.NamedTemporaryFile() + system("python --version", logger=f) + f.close() + + # test with string (filename) + print("#5") + (f, fname) = tempfile.mkstemp() + system("python --version", logger=fname) + os.close(f) + os.unlink(fname) + + print("#6") + stdout = False + logger = None + system("echo test") + + print("#7") + stdout = True + system("echo test2") + +""" diff --git a/pylibraries/tendo/tests/__init__.py b/pylibraries/tendo/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pylibraries/tendo/tests/__pycache__/__init__.cpython-39.pyc b/pylibraries/tendo/tests/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ca146d3d47ff0f72b20386d43cb40a1329f944c GIT binary patch literal 193 zcmYe~<>g`k0-wLyDIoeWh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o10PKO;XkRlg)Z zH@8yXCABOyC%+)INWUbtxJ19Ov?MjDD8IZID4EFQ_cZ$j>v@ zv(zunEJ@WZNKDR7OiwKaYRpT?2Wv0ZkB`sH%PfhH*DI*J#bJ}1pHiBWY6r6aGY~TX E036LUHUIzs literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/tests/__pycache__/test_colorer.cpython-39.pyc b/pylibraries/tendo/tests/__pycache__/test_colorer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3dc2a9a28ee5ad2f3d1c76e0c83ba1d17df19b4f GIT binary patch literal 1225 zcmY*ZTTc@~6rS1LZnsNYxhN`6RUx4#^##d39G-@59GSeu15+%$*c8CU+Fgv*+WC^=7LkyDnjyw52hTTGf zhq$jL+O-&SP=s!JqW{O;ASOf_5ILI|sut{z(YVW8qNhDZJ?9c_?9G$x7%dQMjBb+A zdCbAB5o#e~|HS zQX~i_Tc}CX<&F@&c#-#bg$qI%tBk19%hC#;@?lr}$L9|lp%*gBDqh$sV`=hkv+172 zODn4${X)IK@oAZ_l~z`L%DK}5C(b#gQ-=j^(28vy`m_YiLKf#z>;}!SY{UznUhmbm zYwy-d(_lNE-`v~V-CHYNCE4-pc5Q2SOsQNup4-@b^AQrKX^`oZiQ8`}& zvE%^UkU6Zkr8q+kK+Y)q-U)~YB?|k3QOBRSxvh}-a3I*pL-8xmb_kgSWyb@PnRHgt zP*P=v(4%fk8ev2OnS-Lla|6oDRw}1%2|qeaPo_27Qfstjt~u#dFVeG!HUuSyX%2{m z)WaZ@h62A3y9X2b)2X)M164)n`@QOh(ha>>RZuRfCtX30Sa`~Ts6;e^4Ijo=_1vRs z)D!J6cwBj2<*uNQBByZ--=0HaK*BU#VJ@b(C+f-kb;5-1{23U84V=d|&TEtREG{Y^ zTPbe?F5;r5-s)#T{4Acw>;dGK4W|Bun5xE#$|yFKcFo)ePFgSgkaRuzN?jAK5-g}? G>Hh!%cvoQn literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/tests/__pycache__/test_execfile2.cpython-39.pyc b/pylibraries/tendo/tests/__pycache__/test_execfile2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59701b4092bb4f9c5e765f546f9abaaff11f1f7e GIT binary patch literal 1773 zcmcIkOHUL*5bmDG?yxNK7K{?n%Q8_Iz;N)f#zedrvll&$n$7HVFHD#hJ-xt^-HUMb z<^fT5O2j0y)5j z*$BuI8)ai42N``&?1^KzhtzDE5AYrCH-nHb`x=g&wE{>J{@3tlHRvQcpoip>*44VY z)H*t&ds^4%7zcWX98yX^GY$zQ((LFR$~2~b)w`xF92gx9=RqrWbhXk107fmR+aGK# z*Zqj)=4LD4(zbF-@+d_#c`@3^(8!G><#BFqi$L<+^ur|M0+HrglI4X~90u`*UC0eT zVO&6LiEA`vl)3}W*o$}_)SNca%|O<1%@GkJuoZ7T>C+@B!`Ld&!SpG9veDaK&T$BEyKf`Z8(KE;mV5z% zKt%OgP6RGLqUsnB!(b_APrFet^GSs*PDJE|3SvtJNjwfy83vC47I7QowNbccBjn!t zF+vd&*iB-G?p}Z~q+m2WrUHVF^{z+j+Sl(+1yPy^Ih(b!=h!Xa%g-&^DmIq`8diW{ zJp=`qvN+QR>CS(Ev|=A)hFuxb%ELZLcW(q~=C4tzsHdygV(Op3#`nR991WieVnx!;j5u>%wi5&1bshfF`m-&%Ti6paza8GH(C7(5+*E%Jq6#RU) zAjxPEZQ%+3PZG67LvYX?(G)GvOJYGRf?gJF(E)u;&|MNNy@UuzKcH3P2UQHD)&Z+J zyGcGw<#w)3oDXER_Im%}@j#X)De}pxk+~>(+Dm8f--2K7fG}i4 zPpBa?W}FdZLdm3KyeVMtf}XISNxtq9-bm6ysRnO0m&vtz9W4nVLK-}-U;@%Gj?I|>6-SdYrl zV_muOI5B}!xp|z)$}w3PIhouzBWfAuyh_98MJ7X2WZ5`;fO{6DR3WAv{&HyKGgbVm z!O<&a2?JS@n=nnDh2_{hFY?Xam!VFKyj8}7gLo)4ECl@QaZnR|EEz}7%z2&4sJs8z789FH*CRzWay>cS=U?b@B(Dhvxws=K*ex}R?3qAQQ(;1F+UFu54mRF?+=zoz~UiH8>fO&_d`@2%Z%-GOttfn@tEKEPHAz{&e(*TdoWh$I zExqx+U3x8=z{wr^%%}$L(u#%nKF&o7?@oKyC>dw-u4>}A_u%2TKYeGZYA=uFnuW;* ma|vRbfxUbDURH?1RNluD>+2vm;>?y=3m*c9FL0l8zVa_tA!eTd literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/tests/__pycache__/test_tee.cpython-39.pyc b/pylibraries/tendo/tests/__pycache__/test_tee.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58e10cbfcb4e4b05a69bb482c56310701ea09cc8 GIT binary patch literal 1541 zcmaJ>OK;Oa5Z<*N#|^X;DA1}!T6}4(s;;f=W4>qc%vJ=BWcRPp4=Bq(ku`@~-^`E9k&8 z)9JHjB3U_1;y9?&)D|rvS)As3awAl@K?7b$^bCoOuXm7$ zVvpL2S11gy>x77eOXBYgcC<`tqh(SXqBjU}72F-iPe5<>ZM*)CJ2FQmptpv$$M6~S zyOV$(54k)9ye0!&+>!kZo{TFz>N`;Q5gm;gE;F${9>5iyp~6J?lRg`lWbxy00exzyHA9nM5> z&Qi0+T50Y)h?=Z4!vB4T}5LfHckE$nrkLYIcsV{)QS|bQ+L4^rWtkXoRtGSS6$yl+`N^QMZS22jY zFrXO&>+g^4*w{8^v4dwUeiKf{>QO;W=(--~VIp-!R-n?T2~{?ys*IvTbYUH&c8^ut zSbNK%($EFwN>@jk8D?RxPiSKq=0YV8VSWe3$ym z-bX`*$CjVraS!I+6n8+Brt}pS;okJ4>Sa&|r7lM&i7Vb*DP8Hnc^)UU8L@4ZSE2s- N4xTfe2ae@f{{YVLLizvz literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/tests/__pycache__/test_unicode.cpython-39.pyc b/pylibraries/tendo/tests/__pycache__/test_unicode.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a30ef23abb0d74616876131ca8dc0b958e43f3c4 GIT binary patch literal 1650 zcmZWp&5ImG6tAkT{+N&bFvg803gST+WOt%o6ha8NdJtIzH%J<#>7AJ>182-+v{p{Fr*V-_>#z1VY}$J}{8_MH#nK+;|) znDB0r9U4c13;!mGYa$RK__~Nh4SYk;_eiV$0L~#x9ki2A2!l7^)#t$&Qc**Ctn!@Y z#vfB`6lPpBs6rD|(U@QqcBM2^E8L!7He?){gW+S@8 zw%LeP!I)42K146LzVJ4=0Tcs#{*q`*sj3qpy5jjQ&TfwuAP}cXFkiXru|GHO09+sfNY>L4pf?HK;qQp zLrxbZGW(`dg;G!8MyJp`iRKtbbsC3Hp_xN-2Fq_4!d*k=@E9#3G~!+ z5dRN$D3}0Vhusb6Aa-N&0mx;W8LTwk^Q!>Rq97SIW`JloVuFEqypdN$rdHKKPOb`J z+(7(Hvp03Tj^jGvi{KM)n~%5%heY>KVun|Z(P8!SY8v*2I(FDbOA5KSG#y?9pRRQ7 zeR1!riYF-&rXt)c8yf?DU@UG7<#KkZK&7Me!tjQZq3-p$U*7KJ{+uT9N0jw z4Nca!QKu-kaOxyMNSgIG6x6b#($b;h%ZB)M2e$Cg1j8JPVZG(MB8Yj{49d9H#UD`u zUG)rX69*+`D^9X}&av!{NQyj>o0&FQzUruc5!Pv}2|}B6j@F?>*gpY_D5taFn-Ebg zm~Ax`7KuV>sOQl*-8r@49_j@!G32~yhDy35U8yF18(n~E9`@x${1s`` QDHiE*^Ej>hQ6p;n3y1rg{{R30 literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/tests/assets/full_sample_utf8_bom.txt b/pylibraries/tendo/tests/assets/full_sample_utf8_bom.txt new file mode 100644 index 0000000..73dc094 --- /dev/null +++ b/pylibraries/tendo/tests/assets/full_sample_utf8_bom.txt @@ -0,0 +1,4 @@ +NFD aaâaa +NFC aaâaa +NFKD aaâaa +NFKC aaâaa \ No newline at end of file diff --git a/pylibraries/tendo/tests/assets/sample_ucs2_be.txt b/pylibraries/tendo/tests/assets/sample_ucs2_be.txt new file mode 100644 index 0000000000000000000000000000000000000000..1443f60da23113dea15f6d7741c1a944476fc12e GIT binary patch literal 86 rcmezOpCOTvnc)$G0z)yAB#=yGNMvRM(#i~m3|u(njPS@AGjIU_afA#T literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/tests/assets/sample_ucs2_le.txt b/pylibraries/tendo/tests/assets/sample_ucs2_le.txt new file mode 100644 index 0000000000000000000000000000000000000000..94badbc4a9100a979c1710d716b37b353bc2d35d GIT binary patch literal 86 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 a&2b9W0wR0mw+p!$IoY3(SqK`l>;D0Y6_gYJ literal 0 HcmV?d00001 diff --git a/pylibraries/tendo/tests/assets/utf8.txt b/pylibraries/tendo/tests/assets/utf8.txt new file mode 100644 index 0000000..996259b --- /dev/null +++ b/pylibraries/tendo/tests/assets/utf8.txt @@ -0,0 +1 @@ +This is a test \ No newline at end of file diff --git a/pylibraries/tendo/tests/py.typed b/pylibraries/tendo/tests/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pylibraries/tendo/tests/test_colorer.py b/pylibraries/tendo/tests/test_colorer.py new file mode 100644 index 0000000..c5208c5 --- /dev/null +++ b/pylibraries/tendo/tests/test_colorer.py @@ -0,0 +1,37 @@ +import logging +import sys +import tempfile + + +def test_colorer(): + isatty = None + if hasattr(sys.stderr, "isatty"): + isatty = sys.stderr.isatty() + print("sys.stderr.isatty = %s" % isatty) + + isatty = None + if hasattr(sys.stdout, "isatty"): + isatty = sys.stdout.isatty() + print("sys.stdout.isatty = %s" % isatty) + + logging.getLogger().setLevel(logging.NOTSET) + tmp_file = tempfile.NamedTemporaryFile(suffix='_colorer.log').name + fh = logging.FileHandler(tmp_file) + fh.setLevel(logging.NOTSET) + ch = logging.StreamHandler() + ch.setLevel(logging.NOTSET) + formatter = logging.Formatter('%(levelname)s: %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logging.getLogger().addHandler(ch) + logging.getLogger().addHandler(fh) + + logging.warning("a warning") + logging.error("some error") + logging.info("some info") + logging.debug("some info") + expected_lines = ['WARNING: a warning\n', 'ERROR: some error\n', 'INFO: some info\n', 'DEBUG: some info\n'] + line_no = 0 + for line in open(tmp_file).readlines(): + assert(line == expected_lines[line_no]) + line_no += 1 diff --git a/pylibraries/tendo/tests/test_execfile2.py b/pylibraries/tendo/tests/test_execfile2.py new file mode 100644 index 0000000..20e6412 --- /dev/null +++ b/pylibraries/tendo/tests/test_execfile2.py @@ -0,0 +1,52 @@ + +import os +import tempfile + +from tendo.execfile2 import execfile2 + + +def exec_py_code(code, cmd=None): + (ftmp, fname_tmp) = tempfile.mkstemp() + f = open(fname_tmp, "w") # encoding not specified, should use utf-8 + f.write(code) + f.close() + exit_code = execfile2(fname_tmp, cmd=cmd, quiet=True) + os.close(ftmp) + os.unlink(fname_tmp) + return exit_code + + +def test_normal_execution(): + exit_code = exec_py_code("") + assert exit_code == 0 + + +def test_bad_code(): + exit_code = exec_py_code("bleah") + assert exit_code == 1 + + +def test_sys_exit_0(): + exit_code = exec_py_code("import sys; sys.exit(0)") + assert exit_code == 0 + + +def test_sys_exit_5(): + exit_code = exec_py_code("import sys; sys.exit(5)") + assert exit_code == 5 + + +def test_sys_exit_text(): + exit_code = exec_py_code("import sys; sys.exit('bleah')") + assert exit_code == 1 + + +def test_raised_exception(): + exit_code = exec_py_code("raise Exception('bleah')") + assert exit_code == 1 + + +def test_command_line(): + exit_code = exec_py_code( + "import sys\nif len(sys.argv)==2 and sys.argv[1]=='doh!': sys.exit(-1)", cmd="doh!") + assert exit_code == -1 diff --git a/pylibraries/tendo/tests/test_singleton.py b/pylibraries/tendo/tests/test_singleton.py new file mode 100644 index 0000000..6423482 --- /dev/null +++ b/pylibraries/tendo/tests/test_singleton.py @@ -0,0 +1,58 @@ +import logging +from multiprocessing import Process +import sys + +from tendo.singleton import SingleInstance, SingleInstanceException + +logger = logging.getLogger("tendo.singleton.test") +logger.addHandler(logging.StreamHandler()) +logger.setLevel(logging.DEBUG) + + +def f(name): + tmp = logger.level + logger.setLevel(logging.CRITICAL) # we do not want to see the warning + try: + me2 = SingleInstance(flavor_id=name) # noqa + except SingleInstanceException: + sys.exit(-1) + logger.setLevel(tmp) + pass + + +def test_1(): + me = SingleInstance(flavor_id="test-1") + del me # now the lock should be removed + assert True + + +def test_2(): + p = Process(target=f, args=("test-2",)) + p.start() + p.join() + # the called function should succeed + assert p.exitcode == 0, "%s != 0" % p.exitcode + + +def test_3(): + me = SingleInstance(flavor_id="test-3") # noqa -- me should still kept + p = Process(target=f, args=("test-3",)) + p.start() + p.join() + # the called function should fail because we already have another + # instance running + assert p.exitcode != 0, "%s != 0 (2nd execution)" % p.exitcode + # note, we return -1 but this translates to 255 meanwhile we'll + # consider that anything different from 0 is good + p = Process(target=f, args=("test-3",)) + p.start() + p.join() + # the called function should fail because we already have another + # instance running + assert p.exitcode != 0, "%s != 0 (3rd execution)" % p.exitcode + + +def test_4(): + lockfile = '/tmp/foo.lock' + me = SingleInstance(lockfile=lockfile) + assert me.lockfile == lockfile diff --git a/pylibraries/tendo/tests/test_tee.py b/pylibraries/tendo/tests/test_tee.py new file mode 100644 index 0000000..f1ad9de --- /dev/null +++ b/pylibraries/tendo/tests/test_tee.py @@ -0,0 +1,48 @@ + +import os +from tendo.tee import quote_command, system, system2 + + +def test_1(): + """No CMD os.system() + + 1 sort /? ok ok + 2 "sort" /? ok ok + 3 sort "/?" ok ok + 4 "sort" "/?" ok [bad] + 5 ""sort /?"" ok [bad] + 6 "sort /?" [bad] ok + 7 "sort "/?"" [bad] ok + 8 ""sort" "/?"" [bad] ok + """ + + quotes = { + 'dir >nul': 'dir >nul', + 'cd /D "C:\\Program Files\\"': '"cd /D "C:\\Program Files\\""', + 'python -c "import os" dummy': '"python -c "import os" dummy"', + 'sort': 'sort', + } + + # we fake the os name because we want to run the test on any platform + save = os.name + os.name = 'nt' + + for key, value in quotes.items(): + resulted_value = quote_command(key) + assert value == resulted_value + # ret = os.system(resulted_value) + # if not ret==0: + # print("failed") + os.name = save + + +def test_2(): + assert system(['python', '-V']) == 0 + + +def test_3(): + assert system2(['python', '-V'])[0] == 0 + + +def test_4(): + assert system(['python', '-c', "print('c c')"]) == 0 diff --git a/pylibraries/tendo/tests/test_unicode.py b/pylibraries/tendo/tests/test_unicode.py new file mode 100644 index 0000000..12ce1be --- /dev/null +++ b/pylibraries/tendo/tests/test_unicode.py @@ -0,0 +1,49 @@ +import inspect +import pytest +import tempfile +import six +import os +import filecmp +import shutil + + +@pytest.fixture +def dir(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + return os.path.dirname(inspect.getfile(inspect.currentframe())) + + +def test_read_utf8(dir): + if six.PY2: + mode = "rU" + else: + mode = "r" + f = open(os.path.join(dir, "assets/utf8.txt"), mode) + f.readlines() + f.close() + assert True + + +def test_read_invalid_utf8(dir): + with pytest.raises(UnicodeDecodeError): + if six.PY2: + mode = "rU" + else: + mode = "r" + f = open(os.path.join(dir, "assets/utf8-invalid.txt"), mode) + f.readlines() + f.close() + + +def test_write_on_existing_utf8(dir): + (ftmp, fname_tmp) = tempfile.mkstemp() + shutil.copyfile(os.path.join(dir, "assets/utf8.txt"), fname_tmp) + f = open(fname_tmp, "a") # encoding not specified, should use utf-8 + f.write(six.u( + "\u0061\u0062\u0063\u0219\u021B\u005F\u1E69\u0073\u0323\u0307\u0073\u0307\u0323\u005F\u0431\u0434\u0436\u005F\u03B1\u03B2\u03CE\u005F\u0648\u062A\u005F\u05D0\u05E1\u05DC\u005F\u6C38\U0002A6A5\u9EB5\U00020000")) + f.close() + passed = filecmp.cmp( + os.path.join(dir, "assets/utf8-after-append.txt"), fname_tmp, shallow=False) + assert passed is True + os.close(ftmp) + os.unlink(fname_tmp) diff --git a/pylibraries/tendo/unicode.py b/pylibraries/tendo/unicode.py new file mode 100644 index 0000000..8c21df1 --- /dev/null +++ b/pylibraries/tendo/unicode.py @@ -0,0 +1,81 @@ +#!/usr/bin/python +import codecs +import logging +import sys + +import six +""" +Author: Sorin Sbarnea + +This file does add some additional Unicode support to Python, like: +* auto-detect BOM on text-file open: open(file, "r") and open(file, "rU") + +""" +# we save the file function handler because we want to override it +open_old = open + + +def open(filename, mode='r', bufsize=-1, fallback_encoding='utf_8'): + """This replaces Python original function with an improved version that is Unicode aware. + + The new `open()` does change behaviour only for text files, not binary. + + * mode is by default 'r' if not specified and text mode + * negative bufsize makes it use the default system one (same as not specified) + + >>> import tendo.unicode + ... f = open("file-with-unicode-content.txt") + ... content = f.read() # Unicode content of the file, without BOM + + Shortly by importing unicode, you will repair code that previously was broken when the input files were Unicode. + + This will not change the behavior of code that reads the files as binary, it has an effect on text file operations. + + Files with BOM will be read properly as Unicode and the BOM will not be part of the text. + + If you do not specify the fallback_encoding, files without BOM will be read as `UTF-8` instead of `ascii`. + """ + # Do not assign None to bufsize or mode because calling original open will + # fail + + # we read the first 4 bytes just to be sure we use the right encoding + # we are interested of detecting the mode only for read text + if "r" in mode or "a" in mode: + try: + f = open_old(filename, "rb") + aBuf = bytes(f.read(4)) + f.close() + except Exception: + aBuf = six.b('') + if six.binary_type(aBuf[:3]) == six.b('\xEF\xBB\xBF'): + f = codecs.open(filename, mode, "utf_8") + f.seek(3, 0) + f.BOM = codecs.BOM_UTF8 + elif six.binary_type(aBuf[:2]) == six.b('\xFF\xFE'): + f = codecs.open(filename, mode, "utf_16_le") + f.seek(2, 0) + f.BOM = codecs.BOM_UTF16_LE + elif six.binary_type(aBuf[:2]) == six.b('\xFE\xFF'): + f = codecs.open(filename, mode, "utf_16_be") + f.seek(2, 0) + f.BOM = codecs.BOM_UTF16_BE + elif six.binary_type(aBuf[:4]) == six.b('\xFF\xFE\x00\x00'): + f = codecs.open(filename, mode, "utf_32_le") + f.seek(4, 0) + f.BOM = codecs.BOM_UTF32_LE + elif six.binary_type(aBuf[:4]) == six.b('\x00\x00\xFE\xFF'): + f = codecs.open(filename, mode, "utf_32_be") + f.seek(4, 0) + f.BOM = codecs.BOM_UTF32_BE + else: # we assume that if there is no BOM, the encoding is UTF-8 + f = codecs.open(filename, mode, fallback_encoding) + f.seek(0) + f.BOM = None + return f + else: + import traceback + logging.warning( + "Calling unicode.open(%s,%s,%s) that may be wrong." % (filename, mode, bufsize)) + traceback.print_exc(file=sys.stderr) + + return open_old(filename, mode, bufsize) diff --git a/pylibraries/ttkbootstrap-1.10.1.dist-info/INSTALLER b/pylibraries/ttkbootstrap-1.10.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/pylibraries/ttkbootstrap-1.10.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/pylibraries/ttkbootstrap-1.10.1.dist-info/LICENSE b/pylibraries/ttkbootstrap-1.10.1.dist-info/LICENSE new file mode 100644 index 0000000..8b4db1f --- /dev/null +++ b/pylibraries/ttkbootstrap-1.10.1.dist-info/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Israel Dryer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pylibraries/ttkbootstrap-1.10.1.dist-info/METADATA b/pylibraries/ttkbootstrap-1.10.1.dist-info/METADATA new file mode 100644 index 0000000..1f6dba1 --- /dev/null +++ b/pylibraries/ttkbootstrap-1.10.1.dist-info/METADATA @@ -0,0 +1,81 @@ +Metadata-Version: 2.1 +Name: ttkbootstrap +Version: 1.10.1 +Summary: A supercharged theme extension for tkinter that enables on-demand modern flat style themes inspired by Bootstrap. +Home-page: https://github.com/israel-dryer/ttkbootstrap +Author: Israel Dryer +Author-email: israel.dryer@gmail.com +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: pillow (>=8.2.0) + + +![](https://img.shields.io/github/release/israel-dryer/ttkbootstrap.svg) +[![Downloads](https://pepy.tech/badge/ttkbootstrap)](https://pepy.tech/project/ttkbootstrap) +[![Downloads](https://pepy.tech/badge/ttkbootstrap/month)](https://pepy.tech/project/ttkbootstrap) +![](https://img.shields.io/github/issues/israel-dryer/ttkbootstrap.svg) +![](https://img.shields.io/github/issues-closed/israel-dryer/ttkbootstrap.svg) +![](https://img.shields.io/github/license/israel-dryer/ttkbootstrap.svg) +![](https://img.shields.io/github/stars/israel-dryer/ttkbootstrap.svg) +![](https://img.shields.io/github/forks/israel-dryer/ttkbootstrap.svg) + +A supercharged theme extension for tkinter that enables on-demand modern flat style themes inspired by Bootstrap. + +👀 Check out the [documentation](https://ttkbootstrap.readthedocs.io/en/latest/). + +![](https://raw.githubusercontent.com/israel-dryer/ttkbootstrap/master/docs/assets/themes/themes.gif) + +## Features + +✔️ [**Built-in Themes**](https://ttkbootstrap.readthedocs.io/en/latest/themes/) +Over a dozen curated dark and light themes. + +✔️ [**Pre-defined Styles:**](https://ttkbootstrap.readthedocs.io/en/latest/styleguide/) +Loads of beautiful pre-defined widget styles such as **outline** and **round toggle** buttons. + +✔️ [**Simple keyword API:**](https://ttkbootstrap.readthedocs.io/en/latest/gettingstarted/tutorial/#use-themed-widgets) +Apply colors and types using simple keywords such as **primary** and **striped** instead of the legacy approach of **primary.Striped.Horizontal.TProgressbar**. If you've used Bootstrap for web development, you are already familiar with this approach using css classes. + +✔️ [**Lots of new Widgets:**](https://ttkbootstrap.readthedocs.io/en/latest/api/widgets/dateentry/) +ttkbootstrap comes with several new beautifully designed widgets such as **Meter**, **DateEntry**, and **Floodgauge**. Additionally, **dialogs** are now themed and fully customizable. + +✔️ [**Built-in Theme Creator:**](https://ttkbootstrap.readthedocs.io/en/latest/themes/themecreator/) +Want to create your own theme? Easy! ttkboostrap includes a built-in **theme creator** that enables you to easily build, load, expore, and apply your own custom themes. + +## Installation + +```python +python -m pip install ttkbootstrap +``` + +## Simple Usage +Instead of using long, complicated ttk style classes, you can use simple keywords with the "bootstyle" parameter. + +```python +import ttkbootstrap as ttk +from ttkbootstrap.constants import * + +root = ttk.Window(themename="superhero") + +b1 = ttk.Button(root, text="Submit", bootstyle="success") +b1.pack(side=LEFT, padx=5, pady=10) + +b2 = ttk.Button(root, text="Submit", bootstyle="info-outline") +b2.pack(side=LEFT, padx=5, pady=10) + +root.mainloop() +``` + +The new keyword API is very flexible. The following examples all produce the same result: +- `bootstyle="info-outline"` +- `bootstyle="info outline"` +- `bootstyle=("info", "outline")` +- `bootstyle=(INFO, OUTLINE)` + +## Links +- **Documentation:** https://ttkbootstrap.readthedocs.io/en/latest/ +- **GitHub:** https://github.com/israel-dryer/ttkbootstrap diff --git a/pylibraries/ttkbootstrap-1.10.1.dist-info/RECORD b/pylibraries/ttkbootstrap-1.10.1.dist-info/RECORD new file mode 100644 index 0000000..38d39c1 --- /dev/null +++ b/pylibraries/ttkbootstrap-1.10.1.dist-info/RECORD @@ -0,0 +1,61 @@ +ttkbootstrap-1.10.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ttkbootstrap-1.10.1.dist-info/LICENSE,sha256=5GxHfz2ouGVSpF_de2vLpQ334PjztTwgUXTzB6YrQRk,1090 +ttkbootstrap-1.10.1.dist-info/METADATA,sha256=01CDyDVcyPAdh5gNebxKlumHGh1OFOaOt2pJ5vfGdGc,3757 +ttkbootstrap-1.10.1.dist-info/RECORD,, +ttkbootstrap-1.10.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ttkbootstrap-1.10.1.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +ttkbootstrap-1.10.1.dist-info/top_level.txt,sha256=kiB4so53dBImGejI1oNn1TsM0rRR9MF-okIQV-tgGWc,24 +ttkbootstrap/__init__.py,sha256=XKPis2k-N7nVBjtKmQf0iH7n8_mDJOQt7jLYi4JP7cM,396 +ttkbootstrap/__main__.py,sha256=LvK6Wp_GlZhCFXny5Hm-NFpKIklqLW6fyc_cKpOUL48,8926 +ttkbootstrap/__pycache__/__init__.cpython-39.pyc,, +ttkbootstrap/__pycache__/__main__.cpython-39.pyc,, +ttkbootstrap/__pycache__/colorutils.cpython-39.pyc,, +ttkbootstrap/__pycache__/constants.cpython-39.pyc,, +ttkbootstrap/__pycache__/icons.cpython-39.pyc,, +ttkbootstrap/__pycache__/publisher.cpython-39.pyc,, +ttkbootstrap/__pycache__/scrolled.cpython-39.pyc,, +ttkbootstrap/__pycache__/style.cpython-39.pyc,, +ttkbootstrap/__pycache__/tableview.cpython-39.pyc,, +ttkbootstrap/__pycache__/toast.cpython-39.pyc,, +ttkbootstrap/__pycache__/tooltip.cpython-39.pyc,, +ttkbootstrap/__pycache__/utility.cpython-39.pyc,, +ttkbootstrap/__pycache__/validation.cpython-39.pyc,, +ttkbootstrap/__pycache__/widgets.cpython-39.pyc,, +ttkbootstrap/__pycache__/window.cpython-39.pyc,, +ttkbootstrap/colorutils.py,sha256=8BLNwPi5Y3hnIJLYWedT7f4qOrpuuZ8hudoEkb6RqQ8,5255 +ttkbootstrap/constants.py,sha256=d1GutsvjEDbuAe079R-4r4hsRpXCjAz0T9fk5vNT2QA,775 +ttkbootstrap/dialogs/__init__.py,sha256=j_U9tYIjdQNmakvsvJT_OZRu5VYA-bazR7Ut2WcZJjI,42 +ttkbootstrap/dialogs/__pycache__/__init__.cpython-39.pyc,, +ttkbootstrap/dialogs/__pycache__/colorchooser.cpython-39.pyc,, +ttkbootstrap/dialogs/__pycache__/colordropper.cpython-39.pyc,, +ttkbootstrap/dialogs/__pycache__/dialogs.cpython-39.pyc,, +ttkbootstrap/dialogs/colorchooser.py,sha256=NPC94V9bYdlYepAt7cYhpGxmu-nRa-zqTg3W09tB3n4,22700 +ttkbootstrap/dialogs/colordropper.py,sha256=1hVj3iqA8OA1bM5bBp5mMNaIjY04hhDEixbRQh9qiyA,6885 +ttkbootstrap/dialogs/dialogs.py,sha256=O6bD4RogB2Sj7BmqlsdZCCKW80EvtHxXaxz8rqZ3wQk,64245 +ttkbootstrap/icons.py,sha256=dA2NB413LgENzuhBQ3JLSRS98ZGY4yTVVfrQomnvfM0,118811 +ttkbootstrap/localization/__init__.py,sha256=QvDooxyknlC7Iw7sAlRAGbXM9UC6FxJf3UT4iGYdiew,675 +ttkbootstrap/localization/__pycache__/__init__.cpython-39.pyc,, +ttkbootstrap/localization/__pycache__/msgcat.cpython-39.pyc,, +ttkbootstrap/localization/__pycache__/msgs.cpython-39.pyc,, +ttkbootstrap/localization/msgcat.py,sha256=BEEXbDOm-WdvRGXMA4g7kgQXMus-t3_ibEoXv1wudI4,5594 +ttkbootstrap/localization/msgs.py,sha256=Pc5dKO4JdNKqaP6tUOXItXAITliXgVPdus_PjjnIzdg,15106 +ttkbootstrap/publisher.py,sha256=NAiNAwOMSqa8nymObagYXhQf7vHBX2lGzOimh7seinw,2879 +ttkbootstrap/scrolled.py,sha256=sQCxqXDuT3ddyozNfrG5x7UnlIz3naAhHbQgWBeaPus,15839 +ttkbootstrap/style.py,sha256=YCC661XblVvHSxukSqoZm8lPfrjMckGjbg3E2rYUF64,172335 +ttkbootstrap/tableview.py,sha256=OHYxxBmtsslAPn4zOKLI3NKo6YIT49sZct48HZ-p1Gw,92575 +ttkbootstrap/themes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ttkbootstrap/themes/__pycache__/__init__.cpython-39.pyc,, +ttkbootstrap/themes/__pycache__/standard.cpython-39.pyc,, +ttkbootstrap/themes/__pycache__/user.cpython-39.pyc,, +ttkbootstrap/themes/standard.py,sha256=4Uv-FuiG_dbo46_AaTvyXUE22BO-KZzN6Yh_eg6Uk18,11286 +ttkbootstrap/themes/user.py,sha256=G7lg2FIAc8oijPHic6BwWH0jq1dXCKPCqMbb7hxPViA,14 +ttkbootstrap/toast.py,sha256=K1uzggaf61YHK6VNTVq-hVSULsYuooHfIuUTFmGKPvk,8389 +ttkbootstrap/tooltip.py,sha256=6YOndpT6GwwvTkThCFQk3nxQZZFXBscbDpvTz6Oj19Q,5139 +ttkbootstrap/utility.py,sha256=oQlyHG1_XNgcqzWybEqdPmKvoHmxlHGhITTbY6ViE_o,3555 +ttkbootstrap/validation.py,sha256=go7iVecKNJdaPelf6Mkx7CjLhfVhO2B-CGHJdAMi5ts,10200 +ttkbootstrap/widgets.py,sha256=FhfJVQBL1jAWgi_Tks0hRSs2ZEa8PfPqm3SyVgQwvrc,40690 +ttkbootstrap/window.py,sha256=nOaFAj5PX1l8Oo3d-HpAw7w42qGObpRGqGzc5FjfYuw,18186 +ttkcreator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ttkcreator/__main__.py,sha256=DUPwQng17rIssQzyudFpgmo-Kt77W7yxtE7SxyU5K10,17276 +ttkcreator/__pycache__/__init__.cpython-39.pyc,, +ttkcreator/__pycache__/__main__.cpython-39.pyc,, diff --git a/pylibraries/ttkbootstrap-1.10.1.dist-info/REQUESTED b/pylibraries/ttkbootstrap-1.10.1.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/pylibraries/ttkbootstrap-1.10.1.dist-info/WHEEL b/pylibraries/ttkbootstrap-1.10.1.dist-info/WHEEL new file mode 100644 index 0000000..57e3d84 --- /dev/null +++ b/pylibraries/ttkbootstrap-1.10.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/pylibraries/ttkbootstrap-1.10.1.dist-info/top_level.txt b/pylibraries/ttkbootstrap-1.10.1.dist-info/top_level.txt new file mode 100644 index 0000000..fc93cf8 --- /dev/null +++ b/pylibraries/ttkbootstrap-1.10.1.dist-info/top_level.txt @@ -0,0 +1,2 @@ +ttkbootstrap +ttkcreator diff --git a/pylibraries/ttkbootstrap/__init__.py b/pylibraries/ttkbootstrap/__init__.py new file mode 100644 index 0000000..90f1d87 --- /dev/null +++ b/pylibraries/ttkbootstrap/__init__.py @@ -0,0 +1,11 @@ +from ttkbootstrap.style import Style +from ttkbootstrap.style import Bootstyle +from ttkbootstrap.widgets import * +from ttkbootstrap.window import Window, Toplevel + +from tkinter.scrolledtext import ScrolledText +from tkinter import Variable, StringVar, IntVar, BooleanVar, DoubleVar +from tkinter import Canvas, Menu, Text +from tkinter import PhotoImage + +Bootstyle.setup_ttkbootstap_api() diff --git a/pylibraries/ttkbootstrap/__main__.py b/pylibraries/ttkbootstrap/__main__.py new file mode 100644 index 0000000..4859986 --- /dev/null +++ b/pylibraries/ttkbootstrap/__main__.py @@ -0,0 +1,301 @@ +""" + ttkbootstrap demo + + ISSUES: + - the legacy tk widgets do not update after DateDialog is used. +""" +import ttkbootstrap as ttk +from ttkbootstrap.constants import * +from ttkbootstrap.dialogs import Messagebox +from ttkbootstrap.scrolled import ScrolledText + + +def setup_demo(master): + + ZEN = """Beautiful is better than ugly. +Explicit is better than implicit. +Simple is better than complex. +Complex is better than complicated. +Flat is better than nested. +Sparse is better than dense. +Readability counts. +Special cases aren't special enough to break the rules. +Although practicality beats purity. +Errors should never pass silently. +Unless explicitly silenced. +In the face of ambiguity, refuse the temptation to guess. +There should be one-- and preferably only one --obvious way to do it. +Although that way may not be obvious at first unless you're Dutch. +Now is better than never. +Although never is often better than *right* now. +If the implementation is hard to explain, it's a bad idea. +If the implementation is easy to explain, it may be a good idea. +Namespaces are one honking great idea -- let's do more of those!""" + + root = ttk.Frame(master, padding=10) + style = ttk.Style() + theme_names = style.theme_names() + + theme_selection = ttk.Frame(root, padding=(10, 10, 10, 0)) + theme_selection.pack(fill=X, expand=YES) + + theme_selected = ttk.Label( + master=theme_selection, text="litera", font="-size 24 -weight bold" + ) + theme_selected.pack(side=LEFT) + + lbl = ttk.Label(theme_selection, text="Select a theme:") + theme_cbo = ttk.Combobox( + master=theme_selection, + text=style.theme.name, + values=theme_names, + ) + theme_cbo.pack(padx=10, side=RIGHT) + theme_cbo.current(theme_names.index(style.theme.name)) + lbl.pack(side=RIGHT) + + ttk.Separator(root).pack(fill=X, pady=10, padx=10) + + def change_theme(e): + t = cbo.get() + style.theme_use(t) + theme_selected.configure(text=t) + theme_cbo.selection_clear() + default.focus_set() + + theme_cbo.bind("<>", change_theme) + + lframe = ttk.Frame(root, padding=5) + lframe.pack(side=LEFT, fill=BOTH, expand=YES) + + rframe = ttk.Frame(root, padding=5) + rframe.pack(side=RIGHT, fill=BOTH, expand=YES) + + color_group = ttk.Labelframe( + master=lframe, text="Theme color options", padding=10 + ) + color_group.pack(fill=X, side=TOP) + + for color in style.colors: + cb = ttk.Button(color_group, text=color, bootstyle=color) + cb.pack(side=LEFT, expand=YES, padx=5, fill=X) + + rb_group = ttk.Labelframe( + lframe, text="Checkbuttons & radiobuttons", padding=10 + ) + rb_group.pack(fill=X, pady=10, side=TOP) + + check1 = ttk.Checkbutton(rb_group, text="selected") + check1.pack(side=LEFT, expand=YES, padx=5) + check1.invoke() + + check2 = ttk.Checkbutton(rb_group, text="alternate") + check2.pack(side=LEFT, expand=YES, padx=5) + + check4 = ttk.Checkbutton(rb_group, text="deselected") + check4.pack(side=LEFT, expand=YES, padx=5) + check4.invoke() + check4.invoke() + + check3 = ttk.Checkbutton(rb_group, text="disabled", state=DISABLED) + check3.pack(side=LEFT, expand=YES, padx=5) + + radio1 = ttk.Radiobutton(rb_group, text="selected", value=1) + radio1.pack(side=LEFT, expand=YES, padx=5) + radio1.invoke() + + radio2 = ttk.Radiobutton(rb_group, text="deselected", value=2) + radio2.pack(side=LEFT, expand=YES, padx=5) + + radio3 = ttk.Radiobutton( + master=rb_group, text="disabled", value=3, state=DISABLED + ) + radio3.pack(side=LEFT, expand=YES, padx=5) + + ttframe = ttk.Frame(lframe) + ttframe.pack(pady=5, fill=X, side=TOP) + + table_data = [ + ("South Island, New Zealand", 1), + ("Paris", 2), + ("Bora Bora", 3), + ("Maui", 4), + ("Tahiti", 5), + ] + + tv = ttk.Treeview(master=ttframe, columns=[0, 1], show=HEADINGS, height=5) + for row in table_data: + tv.insert("", END, values=row) + + tv.selection_set("I001") + tv.heading(0, text="City") + tv.heading(1, text="Rank") + tv.column(0, width=300) + tv.column(1, width=70, anchor=CENTER) + tv.pack(side=LEFT, anchor=NE, fill=X) + + # # notebook with table and text tabs + nb = ttk.Notebook(ttframe) + nb.pack(side=LEFT, padx=(10, 0), expand=YES, fill=BOTH) + nb_text = "This is a notebook tab.\nYou can put any widget you want here." + nb.add(ttk.Label(nb, text=nb_text), text="Tab 1", sticky=NW) + nb.add( + child=ttk.Label(nb, text="A notebook tab."), text="Tab 2", sticky=NW + ) + nb.add(ttk.Frame(nb), text="Tab 3") + nb.add(ttk.Frame(nb), text="Tab 4") + nb.add(ttk.Frame(nb), text="Tab 5") + + # text widget + txt = ScrolledText(master=lframe, height=5, width=50, autohide=True) + txt.insert(END, ZEN) + txt.pack(side=LEFT, anchor=NW, pady=5, fill=BOTH, expand=YES) + lframe_inner = ttk.Frame(lframe) + lframe_inner.pack(fill=BOTH, expand=YES, padx=10) + s1 = ttk.Scale( + master=lframe_inner, orient=HORIZONTAL, value=75, from_=100, to=0 + ) + s1.pack(fill=X, pady=5, expand=YES) + + ttk.Progressbar( + master=lframe_inner, + orient=HORIZONTAL, + value=50, + ).pack(fill=X, pady=5, expand=YES) + + ttk.Progressbar( + master=lframe_inner, + orient=HORIZONTAL, + value=75, + bootstyle=(SUCCESS, STRIPED), + ).pack(fill=X, pady=5, expand=YES) + + m = ttk.Meter( + master=lframe_inner, + metersize=150, + amountused=45, + subtext="meter widget", + bootstyle=INFO, + interactive=True, + ) + m.pack(pady=10) + + sb = ttk.Scrollbar( + master=lframe_inner, + orient=HORIZONTAL, + ) + sb.set(0.1, 0.9) + sb.pack(fill=X, pady=5, expand=YES) + + sb = ttk.Scrollbar( + master=lframe_inner, orient=HORIZONTAL, bootstyle=(DANGER, ROUND) + ) + sb.set(0.1, 0.9) + sb.pack(fill=X, pady=5, expand=YES) + + btn_group = ttk.Labelframe(master=rframe, text="Buttons", padding=(10, 5)) + btn_group.pack(fill=X) + + menu = ttk.Menu(root) + for i, t in enumerate(style.theme_names()): + menu.add_radiobutton(label=t, value=i) + + default = ttk.Button(master=btn_group, text="solid button") + default.pack(fill=X, pady=5) + default.focus_set() + + mb = ttk.Menubutton( + master=btn_group, + text="solid menubutton", + bootstyle=SECONDARY, + menu=menu, + ) + mb.pack(fill=X, pady=5) + + cb = ttk.Checkbutton( + master=btn_group, + text="solid toolbutton", + bootstyle=(SUCCESS, TOOLBUTTON), + ) + cb.invoke() + cb.pack(fill=X, pady=5) + + ob = ttk.Button( + master=btn_group, + text="outline button", + bootstyle=(INFO, OUTLINE), + command=lambda: Messagebox.ok("You pushed an outline button"), + ) + ob.pack(fill=X, pady=5) + + mb = ttk.Menubutton( + master=btn_group, + text="outline menubutton", + bootstyle=(WARNING, OUTLINE), + menu=menu, + ) + mb.pack(fill=X, pady=5) + + cb = ttk.Checkbutton( + master=btn_group, + text="outline toolbutton", + bootstyle=(SUCCESS, OUTLINE, TOOLBUTTON), + ) + cb.pack(fill=X, pady=5) + + lb = ttk.Button(master=btn_group, text="link button", bootstyle=LINK) + lb.pack(fill=X, pady=5) + + cb1 = ttk.Checkbutton( + master=btn_group, + text="rounded toggle", + bootstyle=(SUCCESS, ROUND, TOGGLE), + ) + cb1.invoke() + cb1.pack(fill=X, pady=5) + + cb2 = ttk.Checkbutton( + master=btn_group, text="squared toggle", bootstyle=(SQUARE, TOGGLE) + ) + cb2.pack(fill=X, pady=5) + cb2.invoke() + + input_group = ttk.Labelframe( + master=rframe, text="Other input widgets", padding=10 + ) + input_group.pack(fill=BOTH, pady=(10, 5), expand=YES) + entry = ttk.Entry(input_group) + entry.pack(fill=X) + entry.insert(END, "entry widget") + + password = ttk.Entry(master=input_group, show="•") + password.pack(fill=X, pady=5) + password.insert(END, "password") + + spinbox = ttk.Spinbox(master=input_group, from_=0, to=100) + spinbox.pack(fill=X) + spinbox.set(45) + + cbo = ttk.Combobox( + master=input_group, + text=style.theme.name, + values=theme_names, + exportselection=False, + ) + cbo.pack(fill=X, pady=5) + cbo.current(theme_names.index(style.theme.name)) + + de = ttk.DateEntry(input_group) + de.pack(fill=X) + + return root + + +if __name__ == "__main__": + + app = ttk.Window("ttkbootstrap widget demo") + + bagel = setup_demo(app) + bagel.pack(fill=BOTH, expand=YES) + + app.mainloop() diff --git a/pylibraries/ttkbootstrap/__pycache__/__init__.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b47dd14b4a34801744ace15ff676eafb82d4c9ee GIT binary patch literal 680 zcmZ8f!H&}~5Vg}LP12<8cFT<)2#Dms32B88>}7>ONUcEfC2}`b8`X}D?QH3tBOd`r ze#uu(`~ngZuW7JXU&FvvPJ0P_f)rZ%b82oR=3>_Vi#R?)~_iX$rJb20{o@|nX2+}{=l`j` zejWNv(e0)~+gYYdbN31huh0{)+{E5mI`-L#2mej=)I;VW_b~G?_pksU4-B)ts_$>5 zs-~*_j`Y%}c=)xe_ENCQN+C|wrEjBDOjni}TYc;;+iH0@pfCOlikbi zY5A-&tz}OXy*@x=Om%-3bfv26wrg!&m+Iu=3n81Hv*+*!BIC=;aE4>N2r~Q-uW%Op E0Vx!|2mk;8 literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/__pycache__/__main__.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/__main__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7ac73a9868d573e3bcd7262ae7032eb9684f147 GIT binary patch literal 6794 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 R*}qd=s$Q=yFWz2w<$sf7Q>_31 literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/__pycache__/constants.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/constants.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1cb2af1cee78f3025452c84952257d9909ec1e38 GIT binary patch literal 914 zcmYjPOK;Oa5Ox|TPLrnZ_XD^VM19}{LI~5isa3}=wgc*1DmSxDtTkdT-_TXk3a?f1>>YG!sPZnv8njk7`CB74&hLu!{G@OG9 zR$&fxsA3It(lx9@9StzB0S%mod2B)xThPJpapGEJ2(*d(D07`Z1&7E1>0Bzq#2kr&A%E~YRq*nnq9R2iZi444p9 zPMi%{p^Uq@$dYWR^d!6HP)n2HNK(;pF;)gIWtwD6>4aldmwY%(85l`+%L+k6$Rf!Z zLQ`^{UX@bvOw|frW(ak0Q^o~dl?7wa7_k^Biul%^=@$=DH41X@ES-9X&tOH4lZ?ta zqHKkXGbxnOu}`eN8>#j^xr|Qjo(+vCI=eh}tsWRNX1NixX5{`9=qG*GReEUm9B6gy z$PRjrXGJ!&9q*|$&I6}s1s5e6<44@w!TPyb~X4 zuG2k@K&QiJVEFyWbvzq%!ZWBxez)t|U^w0vI|zw{QQ(~09cV_r?|$k>k?%n*@cUjz z)x*zyE1;`UU{gt_w$-61AuP=FqiO_X_dWOGhgNJ+gZzWvBXh(j%#?gGnVKDT%Tk`R z!jw!%^QM&Ss^E8m3`aT3$w3_=%`~|(^Qj#1?9I_zQ&3~Q&f~#29x`D{Ilkgt(vQZu q`S|`v`BXLDPdJt-`%t0a;vGR_POse0D(w&J!8Y4xwx{#IuKop6BL?&U literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/__pycache__/icons.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/icons.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1b74879935391bb259293fa32a2f069c402beee GIT binary patch literal 76414 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 literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/__pycache__/publisher.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/publisher.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39b22e17ea8ec8ca6e6f5d7e8b7d45b6f7bb0022 GIT binary patch literal 3771 zcmcInTW=dh6rR~#JC5Tzy$D*`mjxlHLAALEAy6reQUs_~R5g(ROK7v+nK;|5cioxU zRU|mP>t}C3bC){pD zcwkm3RyOy;G?mHd&ket?vV0h)eSa@g{{6g{#Cl&UU*|m?skkSVZu?)RB91~Mwf|&a z8d^qE_fek2qA!gfYd`c8*$<;be|LN5&aQ6`rfXmu`q~^OQuw<&J9l>{3+=k)uwe|2 z$-(^Na)Q``g@66s*2k8DCaByi`J00{HMl}~w4Sgb0xNkSmQ646fm6ae6Sri0a7gy}MIPn=%z(LvkzTer; z2C`$a!QilSM?RKGHk7J^$C=I}ys@XUCmIv&p&VjifR}WVxYrpTn*A(&zx`21$41^5 zhS5RTm%3xjK`+aUHYyx;hEwIVhlh%ARzcNOG&SxuJZ|SsVS-+Fn+Q*1Lby*Ea8Y8S zzCF|UDB2+Xm+JRLXhJ_qLaqH=lO!`}GgH!!Q$S`Anm9}Su$SeA^gNVNycb7SadRHn z1H|~dGkoo~BYMHKP@gWkiK<^lGv;F!u(5-dk6pBYF`(;&m!PsuB>_e{ZFc1a6xm~Cpb^@kJpmJeYB@P6bXfn&gk z#0vwp^2cP9rF(HdSJoKxsh7>5O>#2K@Q2PZ!I_iV+(f485*CWf!R&THFh`(1OQ^;9 zIYAI*d1~G#(a}lF8C{(*YA=&TB<5)$g;3o@)uemF9`YmC!0upy&aq3jcN0;}+TI7S zy%`?J4}@tctOZ2>XX`6P{idFJ>W+yLYmy7h|CI%9SR*}ce?xdMigV$_66F%(iN*Y# z9nRw*d9&$;Sp=oYxwsLT=)P2-W~*WK5=3ml-}06$5@rfZThO$%%hegX4@pC$Dz;8$ zd4_%?3TD$nIH1U275fG{R^p#nJ|6^!tXT&N~w&j}9NmLH78hcb)sd z|3kf{jFzNSxa6pvQc9QcfMwCF<>^c~Ut(R`yll0~9o{6*_0n8K3zR2>M3ykE+Q)!| z^po>5JL2E5JubL#p4P_v4#aijj2+m3JJJuN$rS>gk3`A%2p6+kC_R@`Nwpk-C}}&H z)6m;#mKYt#!yAvoB$vL)hsngb5W$H{lV%AA5A|nC9QblWZUOSLL-@a70Pk7Lx5+yQJ3%8u*8pQ>>w1`+ur_r&@9bKDa=@i{ot8r#pLq5A` zXoJ`uA+Oxegra0dQ9i9GW-2Sr*QrOI_N;4J?|%b}HVdZ|$a5TCcTq2(HfxRAy2CBi zR%@m3B);+#-MOfmL;5;X1Z|t(+wf6FlQVLyqIR?Va=As(KvRa+;0^cuh34hI0QcO2 A5dZ)H literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/__pycache__/scrolled.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/scrolled.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25e976ab6c7ac2635763418c98afc18e12279abc GIT binary patch literal 14575 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 literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/__pycache__/style.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/style.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79d13bf06498c6bdc8c6278c07b6122ec3752ddb GIT binary patch literal 98295 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 SoA^GCdJP*P1%Cgr^Zx_%c=6!? literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/__pycache__/tooltip.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/tooltip.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea5f64f987933032be7340f2c59ed6b48589447e GIT binary patch literal 4957 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 QiQB|~nyPX0=Ei&f2LWeED*ylh literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/__pycache__/validation.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/validation.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e620a33c4e9d5651cec243c78b55864aeef7a5e8 GIT binary patch literal 10268 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 literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/__pycache__/widgets.cpython-39.pyc b/pylibraries/ttkbootstrap/__pycache__/widgets.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17c2b15009aaa7b63e806180d60a33474c864dc5 GIT binary patch literal 31302 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 Yi9)4NS@?nS3a(3q6yiPU3MBD=094IB0RR91 literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/colorutils.py b/pylibraries/ttkbootstrap/colorutils.py new file mode 100644 index 0000000..b878b0b --- /dev/null +++ b/pylibraries/ttkbootstrap/colorutils.py @@ -0,0 +1,204 @@ +from PIL import ImageColor +from colorsys import rgb_to_hls + +RGB = 'rgb' +HSL = 'hsl' +HEX = 'hex' +NAME = 'name' + +HUE = 360 +SAT = 100 +LUM = 100 + + +def color_to_rgb(color, model=HEX): + """Convert color value to rgb. + + The color and model parameters represent the color to be converted. + The value is expected to be a string for "name" and "hex" models and + a Tuple or List for "rgb" and "hsl" models. + + Parameters: + + color (Any): + The color values for the model being converted. + + model (str): + The color model being converted. + + Returns: + + Tuple[int, int, int]: + The rgb color values. + """ + color_ = conform_color_model(color, model) + try: + return ImageColor.getrgb(color_) + except: + print('this') + +def color_to_hex(color, model=RGB): + """Convert color value to hex. + + The color and model parameters represent the color to be converted. + The value is expected to be a string for "name" and "hex" models and + a Tuple or List for "rgb" and "hsl" models. + + Parameters: + + color (Any): + The color values for the model being converted. + + model (str): + The color model being converted. + + Returns: + + str: + The hexadecimal color value. + """ + r, g, b = color_to_rgb(color, model) + return f'#{r:02x}{g:02x}{b:02x}' + +def color_to_hsl(color, model=HEX): + """Convert color value to hsl. + + The color and model parameters represent the color to be converted. + The value is expected to be a string for "name" and "hex" models and + a Tuple or List for "rgb" and "hsl" models. + + Parameters: + + color (Any): + The color values for the model being converted. + + model (str): + The color model being converted. + + Returns: + + Tuple[int, int, int]: + The hsl color values. + """ + r, g, b = color_to_rgb(color, model) + hls = rgb_to_hls(r/255, g/255, b/255) + h = int(hls[0]*HUE) + l = int(hls[1]*LUM) + s = int(hls[2]*SAT) + return h, s, l + +def update_hsl_value(color, hue=None, sat=None, lum=None, inmodel=HSL, outmodel=HSL): + """Change hue, saturation, or lumenosity of the color based on the + hue, sat, lum parameters provided. + + Parameters: + + color (Any): + The color + + hue (int): + A number between 0 and 360. + + sat (int): + A number between 0 and 100. + + lum (int): + A number between 0 and 100. + + inmodel (str): + The color model used by the color to be changed. One of + hsl, rgb, hex, name. + + outmodel (str): + The color value model to be returned when the color is + changed. One of hsl, rgb, hex. + + Returns: + + Union[Tuple[int, int, int], str]: + The color value based on the selected color model. + """ + h, s, l = color_to_hsl(color, inmodel) + if hue is not None: + h = hue + if sat is not None: + s = sat + if lum is not None: + l = lum + if outmodel == RGB: + return color_to_rgb([h, s, l], HSL) + elif outmodel == HEX: + return color_to_hex([h, s, l], HSL) + else: + return h, s, l + + +""" +https://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color + +""" + +def contrast_color(color, model=RGB, darkcolor='#000', lightcolor='#fff'): + """Returns the best matching contrasting light or dark color for + the given color. + + Parameters: + + color (Any): + The color value to evaluate. + + model (str): + The model of the color value to be evaluated. 'rgb' by + default. + + darkcolor (Any): + The color value to be returned when the constrasting color + should be dark. + + lightcolor (Any): + The color value to be returned when the constrasting color + should be light. + + Returns: + + str: + The matching color value. + """ + if model != RGB: + r, g, b = color_to_rgb(color, model) + else: + r, g, b = color + + luminance = ((0.299 * r) + (0.587 * g) + (0.114 * b))/255 + if luminance > 0.5: + return darkcolor + else: + return lightcolor + + +def conform_color_model(color, model): + """Conform the color values to a string that can be interpreted + by the `PIL.ImageColor.getrgb method`. + + Parameters: + + color (Union[Tuple[int, int, int], str]): + The color value to conform. + + model (str): + One of 'HSL', 'RGB', or 'HEX' + + Returns: + + str: + A color value string that can be used as a parameter in the + PIL.ImageColor.getrgb method. + """ + if model == HSL: + h, s, l = color + return f'hsl({h},{s}%,{l}%)' + elif model == RGB: + r, g, b = color + return f'rgb({r},{g},{b})' + else: + return color diff --git a/pylibraries/ttkbootstrap/constants.py b/pylibraries/ttkbootstrap/constants.py new file mode 100644 index 0000000..d2cd63a --- /dev/null +++ b/pylibraries/ttkbootstrap/constants.py @@ -0,0 +1,43 @@ +from tkinter.constants import * + +DEFAULT = 'default' +DEFAULT_THEME = 'litera' +TTK_CLAM = 'clam' +TTK_ALT = 'alt' +TTK_DEFAULT = 'default' + +# meter constants +FULL = 'full' +SEMI = 'semi' + +# progressbar constant +DETERMINATE = 'determinate' +INDETERMINATE = 'indeterminate' + +# bootstyle colors +PRIMARY = 'primary' +SECONDARY = 'secondary' +SUCCESS = 'success' +DANGER = 'danger' +WARNING = 'warning' +INFO = 'info' +LIGHT = 'light' +DARK = 'dark' + +# bootstyle types +OUTLINE = 'outline' +LINK = 'link' +TOGGLE = 'toggle' +INVERSE = 'inverse' +STRIPED = 'striped' +TOOLBUTTON = 'toolbutton' +ROUND = 'round' +SQUARE = 'square' + +# treeview constants +TREE = 'tree' +HEADINGS = 'headings' +TREEHEADINGS = 'tree headings' + +# state constants +READONLY = 'readonly' \ No newline at end of file diff --git a/pylibraries/ttkbootstrap/dialogs/__init__.py b/pylibraries/ttkbootstrap/dialogs/__init__.py new file mode 100644 index 0000000..1edf819 --- /dev/null +++ b/pylibraries/ttkbootstrap/dialogs/__init__.py @@ -0,0 +1 @@ +from ttkbootstrap.dialogs.dialogs import * \ No newline at end of file diff --git a/pylibraries/ttkbootstrap/dialogs/__pycache__/__init__.cpython-39.pyc b/pylibraries/ttkbootstrap/dialogs/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb97337a0135e4a0270f4c5e2b1c96717ebc44af GIT binary patch literal 247 zcmYjLF^U2~5S-CO1Y7aI zg07;f8@f!d*Hwb~?b8lK&kysCmYAtb`Vg|pYOj8jZGEe(eUU_E`lJVoOfUP)+meVA z*CyuH8Bgo*GKC+80JS| literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/dialogs/__pycache__/colorchooser.cpython-39.pyc b/pylibraries/ttkbootstrap/dialogs/__pycache__/colorchooser.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6625d388901cb74a8668707e4ca828e3d3bf3d17 GIT binary patch literal 18242 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 KVrdm8T>N@ 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 jgJ9adG&b_V$RNHC@H_Ou$Oj|uL+*o-U6TJ`", self.on_spectrum_interaction, add="+") + canvas.bind("", self.on_spectrum_interaction, add="+") + + # add color points + for x, colorx in enumerate(range(0, width, xf)): + for y, colory in enumerate(range(0, height, yf)): + values = self.color_from_coords(colorx, colory) + fill = values.hex + bbox = [x*xf, y*yf, (x*xf)+xf, (y*yf)+yf] + canvas.create_rectangle(*bbox, fill=fill, width=0) + return canvas + + def create_spectrum_indicator(self): + """Create a square indicator that displays in the position of + the selected color""" + s = utility.scale_size(self, 10) + width = utility.scale_size(self, 2) + values = self.get_variables() + x1, y1 = self.coords_from_color(values.hex) + colorutils.contrast_color(values.hex, 'hex') + tag = ['spectrum-indicator'] + self.color_spectrum.create_rectangle( + x1, y1, x1+s, y1+s, width=width, tags=[tag]) + self.color_spectrum.tag_lower('spectrum-indicator') + + # widget builder methods + def create_swatches(self, master, colors): + """Create a grid of color swatches""" + boxpadx = 2 + boxpady = 0 + padxtotal = (boxpadx*15) + boxwidth = int((self.spectrum_width-padxtotal)) / len(STD_COLORS) + boxheight = int((self.spectrum_height-boxpady) / (len(STD_SHADES)+1)) + container = ttk.Frame(master) + + # create color combinations + color_rows = [colors] + lastcol = len(colors)-1 + for l in STD_SHADES: + lum = int(l*LUM) + row = [] + for color in colors: + color = colorutils.update_hsl_value( + color=color, + lum=lum, + inmodel='hex', + outmodel='hex' + ) + row.append(color) + color_rows.append(row) + + # themed colors - regular colors + for row in color_rows: + rowframe = ttk.Frame(container) + for j, color in enumerate(row): + swatch = tkFrame( + master=rowframe, + bg=color, + width=boxwidth, + height=boxheight, + autostyle=False + ) + swatch.bind('', self.on_press_swatch) + if j == 0: + swatch.pack(side=LEFT, padx=(0, boxpadx)) + elif j == lastcol: + swatch.pack(side=LEFT, padx=(boxpadx, 0)) + else: + swatch.pack(side=LEFT, padx=boxpadx) + rowframe.pack(fill=X, expand=YES) + + return container + + def create_preview(self, master): + """Create the preview frame for original and new colors""" + nbstyle = self.notebook.cget('style') + # set the border color to match the notebook border color + bordercolor = self.style.lookup(nbstyle, 'bordercolor') + container = ttk.Frame(master) + + # the frame and label for the original color (current) + old = tkFrame( + master=container, + relief=FLAT, + bd=2, + highlightthickness=1, + highlightbackground=bordercolor, + bg=self.initialcolor, + autostyle=False + ) + old.pack(side=LEFT, fill=BOTH, expand=YES, padx=(0, 2)) + contrastfg = colorutils.contrast_color( + color=self.initialcolor, + model='hex', + ) + tkLabel( + master=old, + text=MessageCatalog.translate('Current'), + background=self.initialcolor, + foreground=contrastfg, + autostyle=False, + width=7 + ).pack(anchor=NW) + + # the frame and label for the new color + self.preview = tkFrame( + master=container, + relief=FLAT, + bd=2, + highlightthickness=1, + highlightbackground=bordercolor, + bg=self.initialcolor, + autostyle=False + ) + self.preview.pack(side=LEFT, fill=BOTH, expand=YES, padx=(2, 0)) + self.preview_lbl = tkLabel( + master=self.preview, + text=MessageCatalog.translate('New'), + background=self.initialcolor, + foreground=contrastfg, + autostyle=False, + width=7 + ) + self.preview_lbl.pack(anchor=NW) + + return container + + def create_value_inputs(self, master): + """Create color value input widgets""" + container = ttk.Frame(master) + for x in range(4): + container.columnconfigure(x, weight=1) + + # value labels + lbl_cnf = {'master': container, 'anchor': E} + ttk.Label(**lbl_cnf, text=f'''{MessageCatalog.translate('Hue')}:''').grid(row=0, column=0, sticky=E) + ttk.Label(**lbl_cnf, text=f'''{MessageCatalog.translate('Sat')}:''').grid(row=1, column=0, sticky=E) + ttk.Label(**lbl_cnf, text=f'''{MessageCatalog.translate('Lum')}:''').grid(row=2, column=0, sticky=E) + ttk.Label(**lbl_cnf, text=f'''{MessageCatalog.translate('Hex')}:''').grid(row=3, column=0, sticky=E) + ttk.Label(**lbl_cnf, text=f'''{MessageCatalog.translate('Red')}:''').grid(row=0, column=2, sticky=E) + ttk.Label(**lbl_cnf, text=f'''{MessageCatalog.translate('Green')}:''').grid(row=1, column=2, sticky=E) + ttk.Label(**lbl_cnf, text=f'''{MessageCatalog.translate('Blue')}:''').grid(row=2, column=2, sticky=E) + + # value spinners and entry widgets + rgb_cnf = {'master': container, 'from_': 0, 'to': 255, 'width': 3} + sl_cnf = {'master': container, 'from_': 0, 'to': 100, 'width': 3} + hue_cnf = {'master': container, 'from_': 0, 'to': 360, 'width': 3} + sb_hue = ttk.Spinbox(**hue_cnf, textvariable=self.hue) + sb_hue.grid(row=0, column=1, padx=4, pady=2, sticky=EW) + sb_sat = ttk.Spinbox(**sl_cnf, textvariable=self.sat) + sb_sat.grid(row=1, column=1, padx=4, pady=2, sticky=EW) + sb_lum = ttk.Spinbox(**sl_cnf, textvariable=self.lum) + sb_lum.grid(row=2, column=1, padx=4, pady=2, sticky=EW) + sb_red = ttk.Spinbox(**rgb_cnf, textvariable=self.red) + sb_red.grid(row=0, column=3, padx=4, pady=2, sticky=EW) + sb_grn = ttk.Spinbox(**rgb_cnf, textvariable=self.grn) + sb_grn.grid(row=1, column=3, padx=4, pady=2, sticky=EW) + sb_blu = ttk.Spinbox(**rgb_cnf, textvariable=self.blu) + sb_blu.grid(row=2, column=3, padx=4, pady=2, sticky=EW) + ent_hex = ttk.Entry(container, textvariable=self.hex) + ent_hex.grid(row=3, column=1, padx=4, columnspan=3, pady=2, sticky=EW) + + # add input validation + add_validation(ent_hex, validate_color) + add_range_validation(sb_hue, 0, 360) + for sb in [sb_sat, sb_lum]: + add_range_validation(sb, 0, 100) + for sb in [sb_red, sb_grn, sb_blu]: + add_range_validation(sb, 0, 255) + + # event binding for updating colors on value change + for sb in [sb_hue, sb_sat, sb_lum]: + for sequence in ['<>', '<>', '', '']: + sb.bind( + sequence=sequence, + func=lambda _, w=sb: self.on_entry_value_change( + w, HSL), + add="+" + ) + for sb in [sb_red, sb_grn, sb_blu]: + for sequence in ['<>', '<>', '', '']: + sb.bind( + sequence=sequence, + func=lambda _, w=sb: self.on_entry_value_change( + w, RGB), + add="+" + ) + for sequence in ['', '']: + ent_hex.bind( + sequence=sequence, + func=lambda _, w=ent_hex: self.on_entry_value_change( + w, HEX), + add="+" + ) + + return container + + def create_luminance_scale(self, master): + """Create the color luminance canvas""" + # widget dimensions + height = xf = self.spectrum_point + width = self.spectrum_width + + values = self.get_variables() + canvas = ttk.Canvas(master, height=height, width=width) + + # add color points to scale + for x, l in enumerate(range(0, width, xf)): + lum = l/width*LUM + fill = colorutils.update_hsl_value( + color=values.hex, + lum=lum, + inmodel='hex', + outmodel='hex' + ) + bbox = [x*xf, 0, (x*xf)+xf, height] + tag = f'color{x}' + canvas.create_rectangle(*bbox, fill=fill, width=0, tags=[tag]) + canvas.bind("", self.on_luminance_interaction, add="+") + canvas.bind("", self.on_luminance_interaction, add="+") + return canvas + + def create_luminance_indicator(self): + """Create an indicator that displays in the position of the + luminance value""" + lum = 50 + x1 = int(lum / LUM * self.spectrum_width) - \ + ((self.spectrum_point - 2)//2) + y1 = 0 + x2 = x1 + self.spectrum_point + y2 = self.spectrum_point - 3 + tag = 'luminance-indicator' + bbox = [x1, y1, x2, y2] + self.luminance_scale.create_rectangle( + *bbox, fill='white', outline='black', tags=[tag]) + self.luminance_scale.tag_lower(tag) + + def coords_from_color(self, hexcolor): + """Get the coordinates on the color spectrum from the color + value""" + h, s, _ = colorutils.color_to_hsl(hexcolor) + x = (h / HUE) * self.spectrum_width + y = (1-(s / SAT)) * self.spectrum_height + return x, y + + def color_from_coords(self, x, y): + """Get the color value from the mouse position in the color + spectrum""" + HEIGHT = self.spectrum_height + WIDTH = self.spectrum_width + h = int(min(HUE, max(0, (HUE/WIDTH) * x))) + s = int(min(SAT, max(0, SAT - ((SAT/HEIGHT) * y)))) + l = 50 + hx = colorutils.color_to_hex([h, s, l], 'hsl') + r, g, b = colorutils.color_to_rgb(hx) + return ColorValues(h, s, l, r, g, b, hx) + + def set_variables(self, h, s, l, r, g, b, hx): + """Update the color value variables""" + self.hue.set(h) + self.sat.set(s) + self.lum.set(l) + self.red.set(r) + self.grn.set(g) + self.blu.set(b) + self.hex.set(hx) + + def get_variables(self): + """Get the values of all color models and return a + tuple of color values""" + h = self.hue.get() + s = self.sat.get() + l = self.lum.get() + r = self.red.get() + g = self.grn.get() + b = self.blu.get() + hx = self.hex.get() + return ColorValues(h, s, l, r, g, b, hx) + + def update_preview(self): + """Update the color in the preview frame""" + hx = self.hex.get() + fg = colorutils.contrast_color( + color=hx, + model='hex', + ) + self.preview.configure(bg=hx) + self.preview_lbl.configure(bg=hx, fg=fg) + + def update_luminance_scale(self): + """Update the luminance scale with the change in hue and saturation""" + values = self.get_variables() + width = self.spectrum_width + xf = self.spectrum_point + for x, l in enumerate(range(0, width, xf)): + lum = l/width*LUM + fill = colorutils.update_hsl_value( + color=values.hex, + lum=lum, + inmodel='hex', + outmodel='hex' + ) + tag = f'color{x}' + self.luminance_scale.itemconfig(tag, fill=fill) + + def update_luminance_indicator(self): + """Update the position of the luminance indicator""" + lum = self.lum.get() + x = int(lum / LUM * self.spectrum_width) - \ + ((self.spectrum_point - 2)//2) + self.luminance_scale.moveto('luminance-indicator', x, 0) + self.luminance_scale.tag_raise('luminance-indicator') + + def update_spectrum_indicator(self): + """Move the spectrum indicator to a new location""" + values = self.get_variables() + x, y = self.coords_from_color(values.hex) + # move to the new color location + self.color_spectrum.moveto('spectrum-indicator', x, y) + self.color_spectrum.tag_raise('spectrum-indicator') + # adjust the outline color based on contrast of background + color = colorutils.contrast_color(values.hex, 'hex') + self.color_spectrum.itemconfig('spectrum-indicator', outline=color) + + # color events + def sync_color_values(self, model): + """Callback for when a color value changes. A change in one + value will automatically update the other values so that all + color models remain in sync.""" + values = self.get_variables() + if model == HEX: + hx = values.hex + r, g, b = colorutils.color_to_rgb(hx) + h, s, l = colorutils.color_to_hsl(hx) + elif model == RGB: + r, g, b = values.r, values.g, values.b + h, s, l = colorutils.color_to_hsl([r, g, b], 'rgb') + hx = colorutils.color_to_hex([r, g, b]) + elif model == HSL: + h, s, l = values.h, values.s, values.l + r, g, b = colorutils.color_to_rgb([h, s, l], 'hsl') + hx = colorutils.color_to_hex([h, s, l], 'hsl') + self.set_variables(h, s, l, r, g, b, hx) + self.update_preview() + self.update_luminance_indicator() + + def on_entry_value_change(self, widget: ttk.Spinbox, model): + """Update the widget colors when the color value input is + changed""" + is_valid = widget.validate() + if is_valid: + self.sync_color_values(model) + self.update_luminance_scale() + self.update_spectrum_indicator() + + def on_press_swatch(self, event): + """Update the widget colors when a color swatch is clicked.""" + button: tkFrame = self.nametowidget(event.widget) + color = button.cget('background') + self.hex.set(color) + self.sync_color_values(HEX) + self.update_luminance_scale() + self.update_spectrum_indicator() + + def on_spectrum_interaction(self, event): + """Update the widget colors when the color spectrum canvas is + pressed""" + values = self.color_from_coords(event.x, event.y) + self.hue.set(values.h) + self.sat.set(values.s) + self.lum.set(values.l) + self.sync_color_values(HSL) + self.update_luminance_scale() + self.update_spectrum_indicator() + + def on_luminance_interaction(self, event): + """Update the widget colors when the color luminance scale is + pressed""" + l = max(0, min(LUM, int((event.x / self.spectrum_width) * LUM))) + self.lum.set(l) + self.sync_color_values(HSL) + + +from ttkbootstrap.dialogs import Dialog + +class ColorChooserDialog(Dialog): + """A class which displays a color chooser dialog. When a color + option is selected and the "OK" button is pressed, the dialog will + return a namedtuple that contains the color values for rgb, hsl, and + hex. These values can be accessed by indexing the tuple or by using + the named fields. + + ![](../../assets/dialogs/querybox-get-color.png) + + Examples: + + ```python + >>> cd = ColorChooserDialog() + >>> cd.show() + >>> colors = cd.result + >>> colors.hex + '#5fb04f' + >>> colors[2] + '#5fb04f + >>> colors.rgb + (95, 176, 79) + >>> colors[0] + (95, 176, 79) + ``` + """ + + def __init__(self, parent=None, title="Color Chooser", initialcolor=None): + title = MessageCatalog.translate(title) + super().__init__(parent=parent, title=title) + self.initialcolor = initialcolor + self.dropper = ColorDropperDialog() + self.dropper.result.trace_add('write', self.trace_dropper_color) + + def create_body(self, master): + self.colorchooser = ColorChooser(master, self.initialcolor) + self.colorchooser.pack(fill=BOTH, expand=YES) + + def create_buttonbox(self, master): + frame = ttk.Frame(master, padding=(5, 5)) + + # OK button + ok = ttk.Button(frame, bootstyle=PRIMARY, width=6, text=MessageCatalog.translate('OK')) + ok.bind("", lambda _: ok.invoke()) + ok.configure(command=lambda b=ok: self.on_button_press(b)) + ok.pack(padx=2, side=RIGHT) + + # Cancel button + cancel = ttk.Button(frame, bootstyle=SECONDARY, width=6, text=MessageCatalog.translate('Cancel')) + cancel.bind("", lambda _: cancel.invoke()) + cancel.configure(command=lambda b=cancel: self.on_button_press(b)) + cancel.pack(padx=2, side=RIGHT) + + # color dropper (not supported on Mac OS) + if self._toplevel.winsys != 'aqua': + dropper = ttk.Label(frame, text=PEN, font=('-size 16')) + ToolTip(dropper, MessageCatalog.translate('color dropper')) # add tooltip + dropper.pack(side=RIGHT, padx=2) + dropper.bind("", self.on_show_colordropper) + + frame.pack(side=BOTTOM, fill=X, anchor=S) + + def on_show_colordropper(self, event): + self.dropper.show() + + def trace_dropper_color(self, *_): + values = self.dropper.result.get() + self.colorchooser.hex.set(values[2]) + self.colorchooser.sync_color_values('hex') + + def on_button_press(self, button): + if button.cget('text') == 'OK': + values = self.colorchooser.get_variables() + self._result = ColorChoice( + rgb=(values.r, values.g, values.b), + hsl=(values.h, values.s, values.l), + hex=values.hex + ) + self._toplevel.destroy() + self._toplevel.destroy() diff --git a/pylibraries/ttkbootstrap/dialogs/colordropper.py b/pylibraries/ttkbootstrap/dialogs/colordropper.py new file mode 100644 index 0000000..67e137f --- /dev/null +++ b/pylibraries/ttkbootstrap/dialogs/colordropper.py @@ -0,0 +1,170 @@ +""" + NOTE: https://stackoverflow.com/questions/25467288/pils-imagegrab-is-capturing-at-the-wrong-resolution + + !! This widget is not currently supported on Mac OS +""" +import tkinter as tk +import ttkbootstrap as ttk +from ttkbootstrap.constants import * +from ttkbootstrap import colorutils, utility +from PIL import ImageGrab, ImageTk, Image +from collections import namedtuple + +ColorChoice = namedtuple('ColorChoice', 'rgb hsl hex') + + +class ColorDropperDialog: + """A widget that displays an indicator and a zoom window for + selecting a color on the screen. + + Left-click the mouse button to select a color. The result is + stored in the `result` property as a `ColorChoice` tuple which + contains named fields for rgb, hsl, and hex color models. + + Zoom in and out on the zoom window by using the mouse wheel. + + This widget is implemented for **Windows** and **Linux** only. + + ![](../../assets/dialogs/color-dropper.png) + + !!! warning "high resolution displays" + This widget may not function properly on high resolution + displays if you are not using the application in high + resolution mode. This is enabled automatically on Windows. + """ + + def __init__(self): + self.toplevel: ttk.Toplevel = None + self.result = ttk.Variable() + + def build_screenshot_canvas(self): + """Build the screenshot canvas""" + self.screenshot_canvas = ttk.Canvas( + self.toplevel, cursor='tcross', autostyle=False) + self.screenshot_data = ImageGrab.grab() + self.screenshot_image = ImageTk.PhotoImage(self.screenshot_data) + self.screenshot_canvas.create_image( + 0, 0, image=self.screenshot_image, anchor=NW) + self.screenshot_canvas.pack(fill=BOTH, expand=YES) + + def build_zoom_toplevel(self, master): + """Build the toplevel widget that shows the zoomed version of + the pixels underneath the mouse cursor.""" + height = utility.scale_size(self.toplevel, 100) + width = utility.scale_size(self.toplevel, 100) + text_xoffset = utility.scale_size(self.toplevel, 50) + text_yoffset = utility.scale_size(self.toplevel, 50) + toplevel = ttk.Toplevel(master) + toplevel.transient(master) + if self.toplevel.winsys == 'x11': + toplevel.attributes('-type', 'tooltip') + else: + toplevel.overrideredirect(True) + toplevel.geometry(f'{width}x{height}') + toplevel.lift() + self.zoom_canvas = ttk.Canvas( + toplevel, borderwidth=1, height=self.zoom_height, width=self.zoom_width) + self.zoom_canvas.create_image(0, 0, tags=['image'], anchor=NW) + self.zoom_canvas.create_text( + text_xoffset, text_yoffset, text="+", fill="white", tags=['indicator']) + self.zoom_canvas.pack(fill=BOTH, expand=YES) + self.zoom_toplevel = toplevel + + def on_mouse_wheel(self, event: tk.Event): + """Zoom in and out on the image underneath the mouse + TODO Cross platform testing needed + """ + if self.toplevel.winsys.lower() == 'win32': + delta = -int(event.delta / 120) + elif self.toplevel.winsys.lower() == 'aqua': + delta = -event.delta + elif event.num == 4: + delta = -1 + elif event.num == 5: + delta = 1 + self.zoom_level += delta + self.on_mouse_motion() + + def on_left_click(self, _): + """Capture the color underneath the mouse cursor and destroy + the toplevel widget""" + # add logic here to capture the image color + hx = self.get_hover_color() + hsl = colorutils.color_to_hsl(hx) + rgb = colorutils.color_to_rgb(hx) + self.result.set(ColorChoice(rgb, hsl, hx)) + self.toplevel.destroy() + self.zoom_toplevel.destroy() + self.toplevel.grab_release() + return self.result.get() + + def on_right_click(self, _): + """Close the color dropper without saving any color information""" + self.zoom_toplevel.destroy() + self.toplevel.grab_release() + self.toplevel.destroy() + + def on_mouse_motion(self, event=None): + """Callback for mouse motion""" + if event is None: + x, y = self.toplevel.winfo_pointerxy() + else: + x = event.x + y = event.y + # move snip window + self.zoom_toplevel.geometry( + f'+{x+self.zoom_xoffset}+{y+self.zoom_yoffset}') + # update the snip image + bbox = (x-self.zoom_level, y-self.zoom_level, + x+self.zoom_level+1, y+self.zoom_level+1) + size = (self.zoom_width, self.zoom_height) + self.zoom_data = self.screenshot_data.crop( + bbox).resize(size, Image.BOX) + self.zoom_image = ImageTk.PhotoImage(self.zoom_data) + self.zoom_canvas.itemconfig('image', image=self.zoom_image) + hover_color = self.get_hover_color() + contrast_color = colorutils.contrast_color(hover_color, 'hex') + self.zoom_canvas.itemconfig('indicator', fill=contrast_color) + + def get_hover_color(self): + """Get the color that is hovered over by the mouse cursor.""" + x1, y1, x2, y2 = self.zoom_canvas.bbox('indicator') + x = x1 + (x2-x1)//2 + y = y1 + (y2-y2)//2 + r, g, b = self.zoom_data.getpixel((x, y)) + hx = colorutils.color_to_hex((r, g, b)) + return hx + + def show(self): + """Show the toplevel window""" + self.toplevel = ttk.Toplevel(alpha=1) + self.toplevel.wm_attributes('-fullscreen', True) + self.build_screenshot_canvas() + + # event binding + self.toplevel.bind("", self.on_mouse_motion, "+") + self.toplevel.bind("", self.on_left_click, "+") + self.toplevel.bind("", self.on_right_click, "+") + + if self.toplevel.winsys.lower() == 'x11': + self.toplevel.bind("", self.on_mouse_wheel, "+") + self.toplevel.bind("", self.on_mouse_wheel, "+") + else: + self.toplevel.bind("", self.on_mouse_wheel, "+") + + # initial snip setup + self.zoom_level = 2 + self.zoom_toplevel: ttk.Toplevel = None + self.zoom_data = None + self.zoom_image = None + self.zoom_height = utility.scale_size(self.toplevel, 100) + self.zoom_width = utility.scale_size(self.toplevel, 100) + self.zoom_xoffset = utility.scale_size(self.toplevel, 10) + self.zoom_yoffset = utility.scale_size(self.toplevel, 10) + + self.build_zoom_toplevel(self.toplevel) + self.toplevel.grab_set() + self.toplevel.lift('.') + self.zoom_toplevel.lift(self.toplevel) + + self.on_mouse_motion() diff --git a/pylibraries/ttkbootstrap/dialogs/dialogs.py b/pylibraries/ttkbootstrap/dialogs/dialogs.py new file mode 100644 index 0000000..6f6999e --- /dev/null +++ b/pylibraries/ttkbootstrap/dialogs/dialogs.py @@ -0,0 +1,1879 @@ +""" + This module contains various base dialog base classes that can be + used to create custom dialogs for the end user. + + These classes serve as the basis for the pre-defined static helper + methods in the `Messagebox`, and `Querybox` container classes. +""" + +import calendar +import textwrap +import locale +from datetime import datetime +from tkinter import font +import ttkbootstrap as ttk +from ttkbootstrap import utility +from ttkbootstrap.icons import Icon +from ttkbootstrap.constants import * +from tkinter import BaseWidget +from ttkbootstrap.localization import MessageCatalog + + +class Dialog(BaseWidget): + """A simple dialog base class.""" + + def __init__(self, parent=None, title="", alert=False): + """ + Parameters: + + parent (Widget): + Makes the window the logical parent of the message box. + The messagebox is displayed on top of its parent window. + + title (str): + The string displayed as the title of the message box. + This option is ignored on Mac OS X, where platform + guidelines forbid the use of a title on this kind of + dialog. + + alert (bool): + Ring the display's bell when the dialog is shown. + """ + BaseWidget._setup(self, parent, {}) + self._winsys = self.master.tk.call("tk", "windowingsystem") + self._parent = parent + self._toplevel = None + self._title = title or " " + self._result = None + self._alert = alert + self._initial_focus = None + + def _locate(self): + toplevel = self._toplevel + if self._parent is None: + master = toplevel.master + else: + master = self._parent + x = master.winfo_rootx() + y = master.winfo_rooty() + toplevel.geometry(f"+{x}+{y}") + + def show(self, position=None): + """Show the popup dialog + Parameters: + + position: Tuple[int, int] + The x and y coordinates used to position the dialog. By + default the dialog will anchor at the NW corner of the + parent window. + """ + self._result = None + self.build() + + if position is None: + self._locate() + else: + try: + x, y = position + self._toplevel.geometry(f'+{x}+{y}') + except: + self._locate() + + self._toplevel.deiconify() + if self._alert: + self._toplevel.bell() + + if self._initial_focus: + self._initial_focus.focus_force() + + self._toplevel.grab_set() + self._toplevel.wait_window() + + def create_body(self, master): + """Create the dialog body. + + This method should be overridden and is called by the `build` + method. Set the `self._initial_focus` for the widget that + should receive the initial focus. + + Parameters: + + master (Widget): + The parent widget. + """ + raise NotImplementedError + + def create_buttonbox(self, master): + """Create the dialog button box. + + This method should be overridden and is called by the `build` + method. Set the `self._initial_focus` for the button that + should receive the intial focus. + + Parameters: + + master (Widget): + The parent widget. + """ + raise NotImplementedError + + def build(self): + """Build the dialog from settings""" + + # setup toplevel based on widowing system + if self._winsys == "win32": + self._toplevel = ttk.Toplevel( + transient=self.master, + title=self._title, + resizable=(0, 0), + minsize=(250, 15), + iconify=True, + ) + else: + self._toplevel = ttk.Toplevel( + transient=self.master, + title=self._title, + resizable=(0, 0), + windowtype="dialog", + iconify=True, + ) + + self._toplevel.withdraw() # reset the iconify state + + # bind event to window close + self._toplevel.bind("", lambda _: self._toplevel.destroy()) + + # set position of popup from parent window + # self._locate() + + # create widgets + self.create_body(self._toplevel) + self.create_buttonbox(self._toplevel) + + # update the window before showing + self._toplevel.update_idletasks() + + @property + def result(self): + """Returns the result of the dialog.""" + return self._result + + +class MessageDialog(Dialog): + """A simple modal dialog class that can be used to build simple + message dialogs. + + Displays a message and a set of buttons. Each of the buttons in the + message window is identified by a unique symbolic name. After the + message window is popped up, the message box awaits for the user to + select one of the buttons. Then it returns the symbolic name of the + selected button. Use a `Toplevel` widget for more advanced modal + dialog designs. + """ + + def __init__( + self, + message, + title=" ", + buttons=None, + command=None, + width=50, + parent=None, + alert=False, + default=None, + padding=(20, 20), + icon=None, + **kwargs, + ): + """ + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the message box. + This option is ignored on Mac OS X, where platform + guidelines forbid the use of a title on this kind of + dialog. + + buttons (List[str]): + A list of buttons to appear at the bottom of the popup + messagebox. The buttons can be a list of strings which + will define the symbolic name and the button text. + `['OK', 'Cancel']`. Alternatively, you can assign a + bootstyle to each button by using the colon to separate the + button text and the bootstyle. If no colon is found, then + the style is set to 'primary' by default. + `['OK:success','Cancel:danger']`. + + command (Tuple[Callable, str]): + The function to invoke when the user closes the dialog. + The actual command is a tuple that consists of the + function to call and the symbolic name of the button that + closes the dialog. + + width (int): + The maximum number of characters per line in the message. + If the text stretches beyond the limit, the line will break + at the word. + + parent (Widget): + Makes the window the logical parent of the message box. + The messagebox is displayed on top of its parent window. + + alert (bool): + Ring the display's bell when the dialog is shown. + + default (str): + The symbolic name of the default button. The default + button is invoked when the the key is pressed. + If no default is provided, the right-most button in the + button list will be set as the default., + + padding (Union[int, Tuple[int]]): + The amount of space between the border and the widget + contents. + + icon (str): + An image path, path-like object or image data to be + displayed to the left of the text. + + **kwargs (Dict): + Other optional keyword arguments. + + Example: + + ```python + root = tk.Tk() + + md = MessageDialog("Displays a message with buttons.") + md.show() + ``` + """ + super().__init__(parent, title, alert) + self._message = message + self._command = command + self._width = width + self._alert = alert + self._default = (default,) + self._padding = padding + self._icon = icon + self._localize = kwargs.get("localize") + + if buttons is None: + self._buttons = [ + f"{MessageCatalog.translate('Cancel')}:secondary", + f"{MessageCatalog.translate('OK')}:primary", + ] + else: + self._buttons = buttons + + def create_body(self, master): + """Overrides the parent method; adds the message section.""" + container = ttk.Frame(master, padding=self._padding) + if self._icon: + try: + # assume this is image data + self._img = ttk.PhotoImage(data=self._icon) + icon_lbl = ttk.Label(container, image=self._img) + icon_lbl.pack(side=LEFT, padx=5) + except: + try: + # assume this is a file path + self._img = ttk.PhotoImage(file=self._icon) + icon_lbl = ttk.Label(container, image=self._img) + icon_lbl.pack(side=LEFT, padx=5) + except: + # icon is neither data nor a valid file path + print("MessageDialog icon is invalid") + + if self._message: + for msg in self._message.split("\n"): + message = "\n".join(textwrap.wrap(msg, width=self._width)) + message_label = ttk.Label(container, text=message) + message_label.pack(pady=(0, 3), fill=X, anchor=N) + container.pack(fill=X, expand=True) + + def create_buttonbox(self, master): + """Overrides the parent method; adds the message buttonbox""" + frame = ttk.Frame(master, padding=(5, 5)) + + button_list = [] + + for i, button in enumerate(self._buttons[::-1]): + cnf = button.split(":") + if len(cnf) == 2: + text, bootstyle = cnf + else: + text = cnf[0] + bootstyle = "secondary" + + if self._localize == True: + text = MessageCatalog.translate(text) + + btn = ttk.Button(frame, bootstyle=bootstyle, text=text) + btn.configure(command=lambda b=btn: self.on_button_press(b)) + btn.pack(padx=2, side=RIGHT) + btn.lower() # set focus traversal left-to-right + button_list.append(btn) + + if self._default is not None and text == self._default: + self._initial_focus = btn + elif self._default is None and i == 0: + self._initial_focus = btn + + # bind default button to return key press and set focus + self._toplevel.bind("", lambda _, b=btn: b.invoke()) + self._toplevel.bind("", lambda _, b=btn: b.invoke()) + + ttk.Separator(self._toplevel).pack(fill=X) + frame.pack(side=BOTTOM, fill=X, anchor=S) + + if not self._initial_focus: + self._initial_focus = button_list[0] + + def on_button_press(self, button): + """Save result, destroy the toplevel, and execute command.""" + self._result = button["text"] + command = self._command + if command is not None: + command() + self._toplevel.destroy() + + def show(self, position=None): + """Create and display the popup messagebox.""" + super().show(position) + + +class QueryDialog(Dialog): + """A simple modal dialog class that can be used to build simple + data input dialogs. Displays a prompt, and input box, and a set of + buttons. Additional data manipulation can be performed on the + user input post-hoc by overriding the `apply` method. + + Use a `Toplevel` widget for more advanced modal dialog designs. + """ + + def __init__( + self, + prompt, + title=" ", + initialvalue="", + minvalue=None, + maxvalue=None, + width=65, + datatype=str, + padding=(20, 20), + parent=None, + ): + """ + Parameters: + + prompt (str): + A message to display in the message box above the entry + widget. + + title (str): + The string displayed as the title of the message box. + This option is ignored on Mac OS X, where platform + guidelines forbid the use of a title on this kind of + dialog. + + initialvalue (Any): + The initial value in the entry widget. + + minvalue (Any): + The minimum allowed value. Only valid for int and float + data types. + + maxvalue (Any): + The maximum allowed value. Only valid for int and float + data types. + + width (int): + The maximum number of characters per line in the + message. If the text stretches beyond the limit, the + line will break at the word. + + parent (Widget): + Makes the window the logical parent of the message box. + The messagebox is displayed on top of its parent + window. + + padding (Union[int, Tuple[int]]): + The amount of space between the border and the widget + contents. + + datatype (Union[int, str, float]): + The data type used to validate the entry value. + """ + super().__init__(parent, title) + self._prompt = prompt + self._initialvalue = initialvalue + self._minvalue = minvalue + self._maxvalue = maxvalue + self._width = width + self._datatype = datatype + self._padding = padding + self._result = None + + def create_body(self, master): + """Overrides the parent method; adds the message and input + section.""" + frame = ttk.Frame(master, padding=self._padding) + if self._prompt: + for p in self._prompt.split("\n"): + prompt = "\n".join(textwrap.wrap(p, width=self._width)) + prompt_label = ttk.Label(frame, text=prompt) + prompt_label.pack(pady=(0, 5), fill=X, anchor=N) + + entry = ttk.Entry(master=frame) + entry.insert(END, self._initialvalue) + entry.pack(pady=(0, 5), fill=X) + entry.bind("", self.on_submit) + entry.bind("", self.on_submit) + entry.bind("", self.on_cancel) + frame.pack(fill=X, expand=True) + self._initial_focus = entry + + def create_buttonbox(self, master): + """Overrides the parent method; adds the message buttonbox""" + frame = ttk.Frame(master, padding=(5, 10)) + + submit = ttk.Button( + master=frame, + bootstyle="primary", + text=MessageCatalog.translate("Submit"), + command=self.on_submit, + ) + submit.pack(padx=5, side=RIGHT) + submit.lower() # set focus traversal left-to-right + + cancel = ttk.Button( + master=frame, + bootstyle="secondary", + text=MessageCatalog.translate("Cancel"), + command=self.on_cancel, + ) + cancel.pack(padx=5, side=RIGHT) + cancel.lower() # set focus traversal left-to-right + + ttk.Separator(self._toplevel).pack(fill=X) + frame.pack(side=BOTTOM, fill=X, anchor=S) + + def on_submit(self, *_): + """Save result, destroy the toplevel, and apply any post-hoc + data manipulations.""" + self._result = self._initial_focus.get() + valid_result = self.validate() + if not valid_result: + return # keep toplevel open for valid response + self._toplevel.destroy() + self.apply() + + def on_cancel(self, *_): + """Close the toplevel and return empty.""" + self._toplevel.destroy() + return + + def validate(self): + """Validate the data + + This method is called automatically to validate the data before + the dialog is destroyed. Can be subclassed and overridden. + """ + # no default checks required for string data types + if self._datatype not in [float, int, complex]: + return True + + # convert result to appropriate data type + try: + self._result = self._datatype(self._result) + except ValueError: + msg = MessageCatalog.translate("Should be of data type") + Messagebox.ok( + message=f"{msg} `{self._datatype}`", + title=MessageCatalog.translate("Invalid data type"), + parent=self._toplevel + ) + return False + + # max value range + if self._maxvalue is not None: + if self._result > self._maxvalue: + msg = MessageCatalog.translate("Number cannot be greater than") + Messagebox.ok( + message=f"{msg} {self._maxvalue}", + title=MessageCatalog.translate("Out of range"), + parent=self._toplevel + ) + return False + + # min value range + if self._minvalue is not None: + if self._result < self._minvalue: + msg = MessageCatalog.translate("Number cannot be less than") + Messagebox.ok( + message=f"{msg} {self._minvalue}", + title=MessageCatalog.translate("Out of range"), + parent=self._toplevel + ) + return False + + # valid result + return True + + def apply(self): + """Process the data. + + This method is called automatically to process the data after + the dialog is destroyed. By default, it does nothing. + """ + pass # override + + +class DatePickerDialog: + """A dialog that displays a calendar popup and returns the + selected date as a datetime object. + + The current date is displayed by default unless the `startdate` + parameter is provided. + + The month can be changed by clicking the chevrons to the left + and right of the month-year title. + + Left-click the arrow to move the calendar by one month. + Right-click the arrow to move the calendar by one year. + Right-click the title to reset the calendar to the start date. + + The starting weekday can be changed with the `firstweekday` + parameter for geographies that do not start the calendar on + Sunday, which is the default. + + The widget grabs focus and all screen events until released. + If you want to cancel a date selection, click the 'X' button + at the top-right corner of the widget. + + The bootstyle api may be used to change the style of the widget. + The available colors include -> primary, secondary, success, + info, warning, danger, light, dark. + + ![](../../assets/dialogs/date-picker-dialog.png) + + """ + + locale.setlocale(locale.LC_ALL, locale.setlocale(locale.LC_TIME, "")) + + def __init__( + self, + parent=None, + title=" ", + firstweekday=6, + startdate=None, + bootstyle=PRIMARY, + ): + """ + Parameters: + + parent (Widget): + The parent widget; the popup will appear to the + bottom-right of the parent widget. If no parent is + provided, the widget is centered on the screen. + + title (str): + The text that appears on the titlebar. + + firstweekday (int): + Specifies the first day of the week. 0=Monday, + 1=Tuesday, etc... + + startdate (datetime): + The date to be in focus when the widget is + displayed. + + bootstyle (str): + The following colors can be used to change the color of + the title and hover / pressed color -> primary, + secondary, info, warning, success, danger, light, dark. + """ + self.parent = parent + self.root = ttk.Toplevel( + title=title, + transient=self.parent, + resizable=(False, False), + topmost=True, + minsize=(226, 1), + iconify=True, + ) + self.firstweekday = firstweekday + self.startdate = startdate or datetime.today().date() + self.bootstyle = bootstyle or PRIMARY + + self.date_selected = self.startdate + self.date = startdate or self.date_selected + self.calendar = calendar.Calendar(firstweekday=firstweekday) + + self.titlevar = ttk.StringVar() + self.datevar = ttk.IntVar() + + self._setup_calendar() + self.root.grab_set() + self.root.wait_window() + + def _setup_calendar(self): + """Setup the calendar widget""" + # create the widget containers + self.frm_calendar = ttk.Frame( + master=self.root, padding=0, borderwidth=0, relief=FLAT + ) + self.frm_calendar.pack(fill=BOTH, expand=YES) + self.frm_title = ttk.Frame(self.frm_calendar, padding=(3, 3)) + self.frm_title.pack(fill=X) + self.frm_header = ttk.Frame(self.frm_calendar, bootstyle=SECONDARY) + self.frm_header.pack(fill=X) + + # setup the toplevel widget + self.root.withdraw() # reset the iconify state + self.frm_calendar.update_idletasks() # actualize geometry + + # create visual components + self._draw_titlebar() + self._draw_calendar() + + # make toplevel visible + self._set_window_position() + self.root.deiconify() + + def _update_widget_bootstyle(self): + self.frm_title.configure(bootstyle=self.bootstyle) + self.title.configure(bootstyle=f"{self.bootstyle}-inverse") + self.prev_period.configure(style=f"Chevron.{self.bootstyle}.TButton") + self.next_period.configure(style=f"Chevron.{self.bootstyle}.TButton") + + def _draw_calendar(self): + self._update_widget_bootstyle() + self._set_title() + self._current_month_days() + self.frm_dates = ttk.Frame(self.frm_calendar) + self.frm_dates.pack(fill=BOTH, expand=YES) + + for row, weekday_list in enumerate(self.monthdays): + for col, day in enumerate(weekday_list): + self.frm_dates.columnconfigure(col, weight=1) + if day == 0: + ttk.Label( + master=self.frm_dates, + text=self.monthdates[row][col].day, + anchor=CENTER, + padding=5, + bootstyle=SECONDARY, + ).grid(row=row, column=col, sticky=NSEW) + else: + if all( + [ + day == self.date_selected.day, + self.date.month == self.date_selected.month, + self.date.year == self.date_selected.year, + ] + ): + day_style = "secondary-toolbutton" + else: + day_style = f"{self.bootstyle}-calendar" + + def selected(x=row, y=col): + self._on_date_selected(x, y) + + btn = ttk.Radiobutton( + master=self.frm_dates, + variable=self.datevar, + value=day, + text=day, + bootstyle=day_style, + padding=5, + command=selected, + ) + btn.grid(row=row, column=col, sticky=NSEW) + + def _draw_titlebar(self): + """Draw the calendar title bar which includes the month title + and the buttons that increment and decrement the selected + month. + + In addition to the previous and next MONTH commands that are + assigned to the button press, a "right-click" event is assigned + to each button that causes the calendar to move to the previous + and next YEAR. + """ + # create and pack the title and action buttons + self.prev_period = ttk.Button( + master=self.frm_title, text="«", command=self.on_prev_month + ) + self.prev_period.pack(side=LEFT) + + self.title = ttk.Label( + master=self.frm_title, + textvariable=self.titlevar, + anchor=CENTER, + font="-weight bold", + ) + self.title.pack(side=LEFT, fill=X, expand=YES) + + self.next_period = ttk.Button( + master=self.frm_title, + text="»", + command=self.on_next_month, + ) + self.next_period.pack(side=LEFT) + + # bind "year" callbacks to action buttons + self.prev_period.bind("", self.on_prev_year, "+") + self.next_period.bind("", self.on_next_year, "+") + self.title.bind("", self.on_reset_date) + + # create and pack days of the week header + for col in self._header_columns(): + ttk.Label( + master=self.frm_header, + text=col, + anchor=CENTER, + padding=5, + bootstyle=(SECONDARY, INVERSE), + ).pack(side=LEFT, fill=X, expand=YES) + + def _set_title(self): + _titledate = f'{self.date.strftime("%B %Y")}' + self.titlevar.set(value=_titledate.capitalize()) + + def _current_month_days(self): + """Fetch the day numbers and dates for all days in the current + month. `monthdays` is a list of days as integers, and + `monthdates` is a list of `datetime` objects. + """ + self.monthdays = self.calendar.monthdayscalendar( + year=self.date.year, month=self.date.month + ) + self.monthdates = self.calendar.monthdatescalendar( + year=self.date.year, month=self.date.month + ) + + def _header_columns(self): + """Create and return a list of weekdays to be used as a header + in the calendar. The order of the weekdays is based on the + `firstweekday` property. + + Returns: + + List[str]: + A list of weekday column names for the calendar header. + """ + weekdays = [ + MessageCatalog.translate("Mo"), + MessageCatalog.translate("Tu"), + MessageCatalog.translate("We"), + MessageCatalog.translate("Th"), + MessageCatalog.translate("Fr"), + MessageCatalog.translate("Sa"), + MessageCatalog.translate("Su"), + ] + header = weekdays[self.firstweekday :] + weekdays[: self.firstweekday] + return header + + def _on_date_selected(self, row, col): + """Callback for selecting a date. + + An index is assigned to each date button that corresponds to + the dates in the `monthdates` matrix. When the user clicks a + button to select a date, the index from this button is used + to lookup the date value of the button based on the row and + column index reference. This value is saved in the + `date_selected` property and the `Toplevel` is destroyed. + + Parameters: + + index (Tuple[int, int]): + A row and column index of the date selected; to be + found in the `monthdates` matrix. + + Returns: + + datetime: + The date selected + """ + self.date_selected = self.monthdates[row][col] + self.root.destroy() + + def _selection_callback(func): + """Calls the decorated `func` and redraws the calendar.""" + + def inner(self, *args): + func(self, *args) + self.frm_dates.destroy() + self._draw_calendar() + + return inner + + @_selection_callback + def on_next_month(self): + """Increment the calendar data to the next month""" + year, month = self._nextmonth(self.date.year, self.date.month) + self.date = datetime(year=year, month=month, day=1).date() + + @_selection_callback + def on_next_year(self, *_): + """Increment the calendar data to the next year""" + year = self.date.year + 1 + month = self.date.month + self.date = datetime(year=year, month=month, day=1).date() + + @_selection_callback + def on_prev_month(self): + """Decrement the calendar to the previous year""" + year, month = self._prevmonth(self.date.year, self.date.month) + self.date = datetime(year=year, month=month, day=1).date() + + @_selection_callback + def on_prev_year(self, *_): + year = self.date.year - 1 + month = self.date.month + self.date = datetime(year=year, month=month, day=1).date() + + @_selection_callback + def on_reset_date(self, *_): + """Set the calendar to the start date""" + self.date = self.startdate + + def _set_window_position(self): + """Move the window the to bottom-right of the parent widget, or + the top-left corner of the master window if no parent is + provided. + """ + if self.parent: + xpos = self.parent.winfo_rootx() + self.parent.winfo_width() + ypos = self.parent.winfo_rooty() + self.parent.winfo_height() + self.root.geometry(f"+{xpos}+{ypos}") + else: + xpos = self.root.master.winfo_rootx() + ypos = self.root.master.winfo_rooty() + self.root.geometry(f"+{xpos}+{ypos}") + + @staticmethod + def _nextmonth(year, month): + if month == 12: + return year + 1, 1 + else: + return year, month + 1 + + @staticmethod + def _prevmonth(year, month): + if month == 1: + return year - 1, 12 + else: + return year, month - 1 + + +class FontDialog(Dialog): + + """A dialog that displays a variety of options for choosing a font. + + This dialog constructs and returns a `Font` object based on the + options selected by the user. The initial font is based on OS + settings and will vary. + + The font object is returned when the **Ok** button is pressed and + can be passed to any widget that accepts a _font_ configuration + option. + + ![](../../assets/dialogs/querybox-get-font.png) + """ + + def __init__(self, title="Font Selector", parent=None): + title = MessageCatalog.translate(title) + super().__init__(parent=parent, title=title) + self._style = ttk.Style() + self._default = font.nametofont("TkDefaultFont") + self._actual = self._default.actual() + self._size = ttk.Variable(value=self._actual["size"]) + self._family = ttk.Variable(value=self._actual["family"]) + self._slant = ttk.Variable(value=self._actual["slant"]) + self._weight = ttk.Variable(value=self._actual["weight"]) + self._overstrike = ttk.Variable(value=self._actual["overstrike"]) + self._underline = ttk.Variable(value=self._actual["underline"]) + self._preview_font = font.Font() + self._slant.trace_add("write", self._update_font_preview) + self._weight.trace_add("write", self._update_font_preview) + self._overstrike.trace_add("write", self._update_font_preview) + self._underline.trace_add("write", self._update_font_preview) + + _headingfont = font.nametofont("TkHeadingFont") + _headingfont.configure(weight="bold") + + self._update_font_preview() + self._families = set([self._family.get()]) + for f in font.families(): + if all([f, not f.startswith("@"), "emoji" not in f.lower()]): + self._families.add(f) + + def create_body(self, master): + width = utility.scale_size(master, 600) + height = utility.scale_size(master, 500) + self._toplevel.geometry(f"{width}x{height}") + + family_size_frame = ttk.Frame(master, padding=10) + family_size_frame.pack(fill=X, anchor=N) + self._initial_focus = self._font_families_selector(family_size_frame) + self._font_size_selector(family_size_frame) + self._font_options_selectors(master, padding=10) + self._font_preview(master, padding=10) + + def create_buttonbox(self, master): + container = ttk.Frame(master, padding=(5, 10)) + container.pack(fill=X) + + ok_btn = ttk.Button( + master=container, + bootstyle="primary", + text=MessageCatalog.translate("OK"), + command=self._on_submit, + ) + ok_btn.pack(side=RIGHT, padx=5) + ok_btn.bind("", lambda _: ok_btn.invoke()) + + cancel_btn = ttk.Button( + master=container, + bootstyle="secondary", + text=MessageCatalog.translate("Cancel"), + command=self._on_cancel, + ) + cancel_btn.pack(side=RIGHT, padx=5) + cancel_btn.bind("", lambda _: cancel_btn.invoke()) + + def _font_families_selector(self, master): + container = ttk.Frame(master) + container.pack(fill=BOTH, expand=YES, side=LEFT) + + header = ttk.Label( + container, + text=MessageCatalog.translate("Family"), + font="TkHeadingFont", + ) + header.pack(fill=X, pady=(0, 2), anchor=N) + + listbox = ttk.Treeview( + master=container, + height=5, + show="", + columns=[0], + ) + listbox.column(0, width=utility.scale_size(listbox, 250)) + listbox.pack(side=LEFT, fill=BOTH, expand=YES) + + listbox_vbar = ttk.Scrollbar( + container, + command=listbox.yview, + orient=VERTICAL, + bootstyle="rounded", + ) + listbox_vbar.pack(side=RIGHT, fill=Y) + listbox.configure(yscrollcommand=listbox_vbar.set) + + for f in self._families: + listbox.insert("", iid=f, index=END, tags=[f], values=[f]) + listbox.tag_configure(f, font=(f, self._size.get())) + + iid = self._family.get() + listbox.selection_set(iid) # select default value + listbox.see(iid) # ensure default is visible + listbox.bind( + "<>", lambda e: self._on_select_font_family(e) + ) + return listbox + + def _font_size_selector(self, master): + container = ttk.Frame(master) + container.pack(side=LEFT, fill=Y, padx=(10, 0)) + + header = ttk.Label( + container, + text=MessageCatalog.translate("Size"), + font="TkHeadingFont", + ) + header.pack(fill=X, pady=(0, 2), anchor=N) + + sizes_listbox = ttk.Treeview(container, height=7, columns=[0], show="") + sizes_listbox.column(0, width=utility.scale_size(sizes_listbox, 24)) + + sizes = [*range(8, 13), *range(13, 30, 2), 36, 48, 72] + for s in sizes: + sizes_listbox.insert("", iid=s, index=END, values=[s]) + + iid = self._size.get() + sizes_listbox.selection_set(iid) + sizes_listbox.see(iid) + sizes_listbox.bind( + "<>", lambda e: self._on_select_font_size(e) + ) + + sizes_listbox_vbar = ttk.Scrollbar( + master=container, + orient=VERTICAL, + command=sizes_listbox.yview, + bootstyle="round", + ) + sizes_listbox.configure(yscrollcommand=sizes_listbox_vbar.set) + sizes_listbox.pack(side=LEFT, fill=Y, expand=YES, anchor=N) + sizes_listbox_vbar.pack(side=LEFT, fill=Y, expand=YES) + + def _font_options_selectors(self, master, padding: int): + container = ttk.Frame(master, padding=padding) + container.pack(fill=X, padx=2, pady=2, anchor=N) + + weight_lframe = ttk.Labelframe( + container, text=MessageCatalog.translate("Weight"), padding=5 + ) + weight_lframe.pack(side=LEFT, fill=X, expand=YES) + opt_normal = ttk.Radiobutton( + master=weight_lframe, + text=MessageCatalog.translate("normal"), + value="normal", + variable=self._weight, + ) + opt_normal.invoke() + opt_normal.pack(side=LEFT, padx=5, pady=5) + opt_bold = ttk.Radiobutton( + master=weight_lframe, + text=MessageCatalog.translate("bold"), + value="bold", + variable=self._weight, + ) + opt_bold.pack(side=LEFT, padx=5, pady=5) + + slant_lframe = ttk.Labelframe( + container, text=MessageCatalog.translate("Slant"), padding=5 + ) + slant_lframe.pack(side=LEFT, fill=X, padx=10, expand=YES) + opt_roman = ttk.Radiobutton( + master=slant_lframe, + text=MessageCatalog.translate("roman"), + value="roman", + variable=self._slant, + ) + opt_roman.invoke() + opt_roman.pack(side=LEFT, padx=5, pady=5) + opt_italic = ttk.Radiobutton( + master=slant_lframe, + text=MessageCatalog.translate("italic"), + value="italic", + variable=self._slant, + ) + opt_italic.pack(side=LEFT, padx=5, pady=5) + + effects_lframe = ttk.Labelframe( + container, text=MessageCatalog.translate("Effects"), padding=5 + ) + effects_lframe.pack(side=LEFT, padx=(2, 0), fill=X, expand=YES) + opt_underline = ttk.Checkbutton( + master=effects_lframe, + text=MessageCatalog.translate("underline"), + variable=self._underline, + ) + opt_underline.pack(side=LEFT, padx=5, pady=5) + opt_overstrike = ttk.Checkbutton( + master=effects_lframe, + text=MessageCatalog.translate("overstrike"), + variable=self._overstrike, + ) + opt_overstrike.pack(side=LEFT, padx=5, pady=5) + + def _font_preview(self, master, padding: int): + container = ttk.Frame(master, padding=padding) + container.pack(fill=BOTH, expand=YES, anchor=N) + + header = ttk.Label( + container, + text=MessageCatalog.translate("Preview"), + font="TkHeadingFont", + ) + header.pack(fill=X, pady=2, anchor=N) + + content = MessageCatalog.translate( + "The quick brown fox jumps over the lazy dog." + ) + self._preview_text = ttk.Text( + master=container, + height=3, + font=self._preview_font, + highlightbackground=self._style.colors.primary, + ) + self._preview_text.insert(END, content) + self._preview_text.pack(fill=BOTH, expand=YES) + container.pack_propagate(False) + + def _on_select_font_family(self, e): + tree: ttk.Treeview = self._toplevel.nametowidget(e.widget) + fontfamily = tree.selection()[0] + self._family.set(value=fontfamily) + self._update_font_preview() + + def _on_select_font_size(self, e): + tree: ttk.Treeview = self._toplevel.nametowidget(e.widget) + fontsize = tree.selection()[0] + self._size.set(value=fontsize) + self._update_font_preview() + + def _on_submit(self) -> font.Font: + self._toplevel.destroy() + return self.result + + def _on_cancel(self): + self._toplevel.destroy() + + def _update_font_preview(self, *_): + family = self._family.get() + size = self._size.get() + slant = self._slant.get() + overstrike = self._overstrike.get() + underline = self._underline.get() + + self._preview_font.config( + family=family, + size=size, + slant=slant, + overstrike=overstrike, + underline=underline, + ) + try: + self._preview_text.configure(font=self._preview_font) + except: + pass + self._result = self._preview_font + + +class Messagebox: + """This class contains various static methods that show popups with + a message to the end user with various arrangments of buttons + and alert options.""" + + @staticmethod + def show_info(message, title=" ", parent=None, alert=False, **kwargs): + """Display a modal dialog box with an OK button and an INFO + icon. + + ![](../../assets/dialogs/messagebox-show-info.png) + + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the messagebox. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + parent (Union[Window, Toplevel]): + Makes the window the logical parent of the message box. The + message box is displayed on top of its parent window. + + alert (bool): + Specified whether to ring the display bell. + + **kwargs (Dict): + Other optional keyword arguments. + """ + dialog = MessageDialog( + message=message, + title=title, + alert=alert, + parent=parent, + buttons=["OK:primary"], + icon=Icon.info, + localize=True, + **kwargs + ) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + + @staticmethod + def show_warning(message, title=" ", parent=None, alert=True, **kwargs): + """Display a modal dialog box with an OK button and a + warning icon. Also will ring the display bell. + + ![](../../assets/dialogs/messagebox-show-warning.png) + + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the messagebox. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + parent (Union[Window, Toplevel]): + Makes the window the logical parent of the message box. The + message box is displayed on top of its parent window. + + alert (bool): + Specified whether to ring the display bell. + + **kwargs (Dict): + Other optional keyword arguments. + """ + dialog = MessageDialog( + message=message, + title=title, + parent=parent, + buttons=["OK:primary"], + icon=Icon.warning, + alert=alert, + localize=True, + **kwargs, + ) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + + @staticmethod + def show_error(message, title=" ", parent=None, alert=True, **kwargs): + """Display a modal dialog box with an OK button and an + error icon. Also will ring the display bell. + + ![](../../assets/dialogs/messagebox-show-error.png) + + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the messagebox. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + parent (Union[Window, Toplevel]): + Makes the window the logical parent of the message box. The + message box is displayed on top of its parent window. + + alert (bool): + Specified whether to ring the display bell. + + **kwargs (Dict): + Other optional keyword arguments. + """ + dialog = MessageDialog( + message=message, + title=title, + parent=parent, + buttons=["OK:primary"], + icon=Icon.error, + alert=alert, + localize=True, + **kwargs, + ) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + + @staticmethod + def show_question( + message, + title=" ", + parent=None, + buttons=["No:secondary", "Yes:primary"], + alert=True, + **kwargs, + ): + """Display a modal dialog box with yes, no buttons and a + question icon. Also will ring the display bell. You may also + change the button scheme using the `buttons` parameter. + + ![](../../assets/dialogs/messagebox-show-question.png) + + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the messagebox. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + parent (Union[Window, Toplevel]): + Makes the window the logical parent of the message box. The + message box is displayed on top of its parent window. + + buttons (List[str]): + A list of buttons to appear at the bottom of the popup + messagebox. The buttons can be a list of strings which + will define the symbolic name and the button text. + `['OK', 'Cancel']`. Alternatively, you can assign a + bootstyle to each button by using the colon to separate the + button text and the bootstyle. If no colon is found, then + the style is set to 'primary' by default. + `['Yes:success','No:danger']`. + + alert (bool): + Specified whether to ring the display bell. + + **kwargs (Dict): + Other optional keyword arguments. + + Returns: + + Union[str, None]: + The symbolic name of the button pressed, or None if the + window is closed without pressing a button. + """ + dialog = MessageDialog( + message=message, + title=title, + parent=parent, + buttons=buttons, + icon=Icon.question, + alert=alert, + localize=True, + **kwargs, + ) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + return dialog.result + + @staticmethod + def ok(message, title=" ", alert=False, parent=None, **kwargs): + """Display a modal dialog box with an OK button and and optional + bell alert. + + ![](../../assets/dialogs/messagebox-ok.png) + + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the messagebox. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + alert (bool): + Specified whether to ring the display bell. + + parent (Union[Window, Toplevel]): + Makes the window the logical parent of the message box. The + message box is displayed on top of its parent window. + + **kwargs (Dict): + Other optional keyword arguments. + """ + dialog = MessageDialog( + title=title, + message=message, + parent=parent, + alert=alert, + buttons=["OK:primary"], + localize=True, + **kwargs, + ) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + + @staticmethod + def okcancel(message, title=" ", alert=False, parent=None, **kwargs): + """Displays a modal dialog box with OK and Cancel buttons and + return the symbolic name of the button pressed. + + ![](../../assets/dialogs/messagebox-ok-cancel.png) + + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the messagebox. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + alert (bool): + Specified whether to ring the display bell. + + parent (Union[Window, Toplevel]): + Makes the window the logical parent of the message box. The + message box is displayed on top of its parent window. + + **kwargs (Dict): + Other optional keyword arguments. + + Returns: + + Union[str, None]: + The symbolic name of the button pressed, or None if the + window is closed without pressing a button. + """ + dialog = MessageDialog( + title=title, + message=message, + parent=parent, + alert=alert, + localize=True, + **kwargs, + ) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + return dialog.result + + @staticmethod + def yesno(message, title=" ", alert=False, parent=None, **kwargs): + """Display a modal dialog box with YES and NO buttons and return + the symbolic name of the button pressed. + + ![](../../assets/dialogs/messagebox-yes-no.png) + + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the messagebox. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + alert (bool): + Specified whether to ring the display bell. + + parent (Union[Window, Toplevel]): + Makes the window the logical parent of the message box. The + message box is displayed on top of its parent window. + + **kwargs (Dict): + Other optional keyword arguments. + + Returns: + + Union[str, None]: + The symbolic name of the button pressed, or None if the + window is closed without pressing a button. + """ + dialog = MessageDialog( + title=title, + message=message, + parent=parent, + buttons=["No", "Yes:primary"], + alert=alert, + localize=True, + **kwargs, + ) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + return dialog.result + + @staticmethod + def yesnocancel(message, title=" ", alert=False, parent=None, **kwargs): + """Display a modal dialog box with YES, NO, and Cancel buttons, + and return the symbolic name of the button pressed. + + ![](../../assets/dialogs/messagebox-yes-no-cancel.png) + + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the messagebox. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + alert (bool): + Specified whether to ring the display bell. + + parent (Union[Window, Toplevel]): + Makes the window the logical parent of the message box. The + message box is displayed on top of its parent window. + + **kwargs (Dict): + Optional keyword arguments. + + Returns: + + Union[str, None]: + The symbolic name of the button pressed, or None if the + window is closed without pressing a button. + """ + dialog = MessageDialog( + title=title, + message=message, + parent=parent, + alert=alert, + buttons=["Cancel", "No", "Yes:primary"], + localize=True, + **kwargs, + ) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + return dialog.result + + @staticmethod + def retrycancel(message, title=" ", alert=False, parent=None, **kwargs): + """Display a modal dialog box with RETRY and Cancel buttons; + returns the symbolic name of the button pressed. + + ![](../../assets/dialogs/messagebox-retry-cancel.png) + + Parameters: + + message (str): + A message to display in the message box. + + title (str): + The string displayed as the title of the messagebox. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + alert (bool): + Specified whether to ring the display bell. + + parent (Union[Window, Toplevel]): + Makes the window the logical parent of the message box. The + message box is displayed on top of its parent window. + + **kwargs (Dict): + Other optional keyword arguments. + + Returns: + + Union[str, None]: + The symbolic name of the button pressed, or None if the + window is closed without pressing a button. + """ + dialog = MessageDialog( + title=title, + message=message, + parent=parent, + alert=alert, + buttons=["Cancel", "Retry:primary"], + localize=True, + **kwargs, + ) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + return dialog.result + + +class Querybox: + """This class contains various static methods that request data + from the end user.""" + + @staticmethod + def get_color( + parent=None, title="Color Chooser", initialcolor=None, **kwargs + ): + """Show a color picker and return the select color when the + user pressed OK. + + ![](../../assets/dialogs/querybox-get-color.png) + + Parameters: + + parent (Widget): + The parent widget. + + title (str): + Optional text that appears on the titlebar. + + initialcolor (str): + The initial color to display in the 'Current' color + frame. + + Returns: + + Tuple[rgb, hsl, hex]: + The selected color in various colors models. + """ + from ttkbootstrap.dialogs.colorchooser import ColorChooserDialog + + dialog = ColorChooserDialog(parent, title, initialcolor) + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog.show(position) + return dialog.result + + @staticmethod + def get_date( + parent=None, + title=" ", + firstweekday=6, + startdate=None, + bootstyle="primary", + ): + """Shows a calendar popup and returns the selection. + + ![](../../assets/dialogs/querybox-get-date.png) + + Parameters: + + parent (Widget): + The parent widget; the popup will appear to the + bottom-right of the parent widget. If no parent is + provided, the widget is centered on the screen. + + title (str): + The text that appears on the popup titlebar. + + firstweekday (int): + Specifies the first day of the week. `0` is Monday, `6` is + Sunday (the default). + + startdate (datetime): + The date to be in focus when the widget is displayed; + + bootstyle (str): + The following colors can be used to change the color of the + title and hover / pressed color -> primary, secondary, info, + warning, success, danger, light, dark. + + Returns: + + datetime: + The date selected; the current date if no date is selected. + """ + chooser = DatePickerDialog( + parent=parent, + title=title, + firstweekday=firstweekday, + startdate=startdate, + bootstyle=bootstyle, + ) + return chooser.date_selected + + @staticmethod + def get_string( + prompt="", title=" ", initialvalue=None, parent=None, **kwargs + ): + """Request a string type input from the user. + + ![](../../assets/dialogs/querybox-get-string.png) + + Parameters: + + prompt (str): + A message to display in the message box above the entry + widget. + + title (str): + The string displayed as the title of the message box. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + initialvalue (Any): + The initial value in the entry widget. + + parent (Widget): + Makes the window the logical parent of the message box. The + messagebox is displayed on top of its parent window. + + **kwargs (Dict): + Other optional keyword arguments. + + Returns: + + str: + The string value of the entry widget. + """ + initialvalue = initialvalue or "" + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog = QueryDialog( + prompt, title, initialvalue, parent=parent, **kwargs + ) + dialog.show(position) + return dialog._result + + @staticmethod + def get_integer( + prompt="", + title=" ", + initialvalue=None, + minvalue=None, + maxvalue=None, + parent=None, + **kwargs, + ): + """Request an integer type input from the user. + + ![](../../assets/dialogs/querybox-get-integer.png) + + Parameters: + + prompt (str): + A message to display in the message box above the entry + widget. + + title (str): + The string displayed as the title of the message box. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + initialvalue (int): + The initial value in the entry widget. + + minvalue (int): + The minimum allowed value. + + maxvalue (int): + The maximum allowed value. + + parent (Widget): + Makes the window the logical parent of the message box. The + messagebox is displayed on top of its parent window. + + **kwargs (Dict): + Other optional keyword arguments. + + Returns: + + int: + The integer value of the entry widget. + """ + initialvalue = initialvalue or "" + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog = QueryDialog( + prompt, + title, + initialvalue, + minvalue, + maxvalue, + datatype=int, + parent=parent, + **kwargs, + ) + dialog.show(position) + return dialog._result + + @staticmethod + def get_float( + prompt="", + title=" ", + initialvalue=None, + minvalue=None, + maxvalue=None, + parent=None, + **kwargs, + ): + """Request a float type input from the user. + + ![](../../assets/dialogs/querybox-get-float.png) + + Parameters: + + prompt (str): + A message to display in the message box above the entry + widget. + + title (str): + The string displayed as the title of the message box. This + option is ignored on Mac OS X, where platform guidelines + forbid the use of a title on this kind of dialog. + + initialvalue (float): + The initial value in the entry widget. + + minvalue (float): + The minimum allowed value. + + maxvalue (float): + The maximum allowed value. + + parent (Widget): + Makes the window the logical parent of the message box. The + messagebox is displayed on top of its parent window. + + **kwargs (Dict): + Other optional keyword arguments. + + Returns: + + float: + The float value of the entry widget. + """ + initialvalue = initialvalue or "" + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog = QueryDialog( + prompt, + title, + initialvalue, + minvalue, + maxvalue, + datatype=float, + parent=parent, + **kwargs, + ) + dialog.show(position) + return dialog._result + + @staticmethod + def get_font(parent=None, **kwargs): + """Request a customized font + + ![](../../assets/dialogs/querybox-get-font.png) + + Parameters: + + parent (Widget): + Makes the window the logical parent of the dialog box. The + dialog is displayed on top of its parent window. + + **kwargs (Dict): + Other keyword arguments. + + Returns: + + Font: + A font object. + """ + if "position" in kwargs: + position = kwargs.pop("position") + else: + position = None + dialog = FontDialog(parent=parent, **kwargs) + dialog.show(position) + return dialog.result diff --git a/pylibraries/ttkbootstrap/icons.py b/pylibraries/ttkbootstrap/icons.py new file mode 100644 index 0000000..c1a2f30 --- /dev/null +++ b/pylibraries/ttkbootstrap/icons.py @@ -0,0 +1,2130 @@ +""" + A module various classes that can be used either in text as `Emoji` + or in the tkinter.PhotoImage class as in `Icon`. +""" + + +class Icon: + """A container class that contains base64 image attributes that can + be used in the `PhotoImage` class using the `data` parameter. + + Attributes: + + icon (str): The ttkbootstrap icon. + error (str): An error image. + warning (str): A warning image. + question (str): A question image. + info (str): An info image. + + Examples: + + ```python + img = tk.PhotoImage(data=Icon.warning) + ``` + """ + + icon = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFxEAABcRAcom8z8AAAT/SURBVFhHzZd9TNR1HMff/O64BwKOp+NRnkIScJI2n7ZqFmqjVhkrHExrrDFZLnPkqmEyW2WtlQ1SQawWPSAYiJLNGvK0VhJOaDgVBDPCw0PwEOUQfsfd/fp+v/cFbt0Bd6yxXvfP5+F79/l+P9+H+3w8wMnO3r40I+P5vOCQ4I1ymVzrQeCu/wSJYDabB/sHbp45XlnzQUnJgcvcBRQUHEjX6/vH6aCFQK/XjxcWHnyBxpbRle/Ysf3n0NAQJTWMmiRUnB/FNy1GNHaNYdwsIS7IE4JdPqxWKxoam3D0aAVq6+rR39+P2NhYKBQKPsJGx3Abyq8dxGldBbrvXkSoVyR8Pf3g7e0tj46O2mQ0Gqs96urqv1u/PmUL/cLfQ2a8/O0t9A2b2Q9MsjpGicOZQfBSeEAURbyZtxstLee410awVovCgk8RGxPD9NLuT1B6dT+TJ/EUFMhL/gwpYZuYXl/fUCbQPaeKVQJyqwwOwSnnekR8VDvM5JIjnzsEpwwMDmL32/mwWCxoHqhzCE6ZsJrw4YWduHGvh+k0tkAPHFXadSZc1k8whzNOtt+DcWwCJ2p+4BZH/urpwfnWNpzs/YpbHJmwivjxehmTaWxh8rTT9M+GSM7CpZ5BjI2NcYtzdH069PEVzsSkn8YWmETQek+JTpER9/3h/pDL5dzinKDAQAQqg7nmHHv/VNRV5KCFaWRcc2RdvBpajRopjz/GLY4EBARgzerVeCKc3TCneJDPxohp/9QEFDIPfJwWwE76v1nkL8fep/yYnLvzNURFRTLZHqVSib35e6BSqfDkokysC32ae6ahwbPidyFRs4JbiK2jo1NKSFjCVaDHYMaRX0fQ3mcikwIeiVMh+2EfaNTTW2Q0jqKsvBxnzzaza5mYmICXXtw6dQUpVslK7n85ztyogkEcQIRXLNKis7BWu4GPADo7rzhOYCGZ9wQmxi249NN19P5xCxaTFdo4XyQ/Gw3fEDUf4RrzmoA4MoFT77RiqNfILTY8VTKk5i1HWJI/t8wNncDsd88JzV93OQSn0Kw0FF5kGXEHtyZgJj/+5283uebI6JAI3QUD11zDrQmIIyZYzLOvcNQgcsk13JqASqOAXDnzY0XxcfMgujUBmVzAkpRwrjmiCfNCxLIArrmG24dwzZbFTk+6mmRnw+vLIJAX1R3m9Q5IpHjo/kWP3jYDOZgW9g4sTY2EyseTj3CN/8VL6PYWMO70A1W7gHeTgfzFwBcZpBpp4U7OuAl4jxQm8ZvJ/pB/0IeygC9P0fKYD7DhfgaGeoFDzwAjA9zAEcjtyDwEPEjqPZFUVqm5QFMbd9rxShpQ9AYT55eBmj2OwSlWC1D9Fln5XaC42nlwSvEJoLGVK+7eApE8wVcauOKEsTtAVxPwfT03zMCxab9AGwUuz834iG2lszF6GzCQLMzGkM1PYwu0XWKaK/iQAlqt4coMhDwAJE0XJk7hfhpboL0a01xBIAXpo9u44oRIUmrFriV1G7kV9q2UPb73AdueYyKNLfNSazpXrlyRTdslZp2LmFXAbR2gn+4tGSHkJmWVkgz5AtGhpOsgT3ItaWBIGzeFnw9QuQ9YHk/bObG4qGgrs9NGkTaMdE9c5trvknR6nyTV5EtS23HS+pq4w46rOkl6v1SSXt0vSQXHJGlwmJltzWkhK42n8pSTk5OUnr5598K055WkPS8hKQT+AVyRrtzM5URAAAAAAElFTkSuQmCC" + error = "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAEc0lEQVRIDcVXbU8bRxCee7MxkGBTSIJt0UAjRES+tKDKUfoFm5I0UVuFUKr+h/ZTf0Wr/pK2BFlKKkUmqOqHJKC8NFKiJKqJAEGNgRIIAcy97PWZde8EfgGnQcLy7M0+MzvP7e3ezB7REf2UWnnvDAx8qKrqkKZpl1zXPSWECJPrkqppa6Qoi2Tbv1lEYxcymT9ribkvsUukTKZSXyua9pNmGMcj8Xhd04kThh4Mkm4YMr5tWWTv7NDrfN5cXVgwhW2vOY7zfWJ8/BcERwjpVtbAVoZJAIRdrqalA6FQPN7T03istVXiBzWvl5dp4enTN2ahMOdY1hcXJiamK42pSDyZTCZJ19PR7u76lo4OtdLAg7Clly/F4osXW1iSz8+Pj/9OJb8y4nupVD8e7c2Ovr5QrbMsiel3efYz9+9v27Z9CTP/wzdA2UM81d/f4+r65AeJRENDczPM7/7fXF2l7NTUBgnRm8hk/vIiqp6CXaBQMPhz7Ny50GGRcmyOFcOS4W34VXIwCPGJ7w4MfBOsr29/r73dx2A/lH/L6dOaEQp13hscHPECShK+E9zRD21nzzZ6htKrduYMKXV1pbDfV0IhYh8fKFE4tqYoP3qwJL6TSvXhPW061tLi4XuuWlcXGVeuUODaNWKCPUZ0GGMb+7AvoLL/cbyOqmFE7iaTH7FREuua9lUkFqs6HTE7S2JpiRQMDoyMkNLQwGOlsC4x3DT7OPCVhgpNpK0tiDdmmE2SGJ1PMdtiKmK0RFxkJvP6dRL5PCmRCAWGhyW5JGUdGNvYh+BbMtzv4vUM4MkOMqBzg61+ChlKqlUbBOTAgaEhUk+eJJ4l+ypNTfKG2LYfKftig5ErRJR1OWN0Ipx/GdhX/iMXSItMyOJCr4WU4xrYnK7jNLMuiVmpVRQUBxbfn/t68cH5WA2KJFZUdZUrzEH+SmNjcX3DYbnZeDMp0L01P2i8hScGrn/YzyPOW9vb3K8qe0gXF8kcHZUioPsbDjdWNQAM1tYWYSPnoJIkRv28sYZ6ykAlKSMdGyO5kTADE3qt5KjZBdTrG8xRJHbd0fVczmSgkqjRKHkbyUqni6SeI8gZc1dWpA/7eqbS66tcznGEGGVcVidOmVMXL8519PbG8a4xXiacDsX8PLmFQpmNAc5eaixGTjbL3TLhEjn74MHcx7duvc9GOWOwu45tfzf/5MkbnKcYLxMOWI2UnV3sEfZhvVQ45vzjx5s4kXzr2SQxd87fvp22TXNueWZGcP8wZWV62hGOM52YmJDry7F9Yp61ZZqXc8+ebawjNbLxMGQduz6Xza7vOM5nzOHF9IkZwPEE1UBcnX34cHsDGYmxdxG5ro8ebeMkevWTTObv3bFwE7u7RV0e9jRtrK27u661szNQRN+uxWHPzD1/XsD6flnTYc8LfyTHW4/c3XWgV3U9HI5GAyjoRgD12KtmJrLRzuYm4bFanAtQBF4hIf3/A71H7l13fcJcRjWLYpe2sA2fMCv4tFlARrppvcUnDI89EvkXuxHzVm+w/WUAAAAASUVORK5CYII=" + warning = "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAADO0lEQVRIDe1V3UtTYRh/P87HPo815+zoNlHb5tzUbTmnQiUU6QZGBN4UBBURIgR1E9TNkkIi8SIw2BKi226iG5cfMKiLoIT+hO4qRSIIg3Tb6TxHtpbbzs4RIogO5znvc37P7/n9zns+3oPQX9rwfnyz8yOWvMl8nyAs7dDtm6MXlrf06jB6G4BfMAtPmhziOEIS2tz42CxjE3Lo2okutkxeWkgcIwTHg9EhPhAd5gmhccDkkq5dl3EymSQMJQu+3n4ToRRROSAHDGp6nHUZDzvfXjJbG0TR3VZ6NyA3WQVxyPXu4h8xXkmdbJBv8aw/PGBBCJd5YNQdjlkYgmeBU1ZQTTXPmLLGaYfTzQkHbRWCgDW1unjCGZNI46bJeDWd8EpYuuLrCRtr6e7WpKvArcUpxzUZE54+6vAFOY7/5Rs7+wxBFMWg1uELcIRn5ouY2ljXeCU9NkopG3N7/VRNCGpubzelDDMIPXCuFqrGa6kjLGGZtD/UbyFElap4AKerL2qBnmxyRHVxUlX7yjZPCgdstqYWlyKs5eBocSLoybnMk2r8msbZ1LgdY3LXH4nJn4+aRGXNHxmwEIzvrT490VhZ3UVqGhd4NCO621mzVdhl6jjKiwwS29pZjIwztdqqGq+kEj3y+n/ucCBkqNVYD1d6C/i8olWFXNWYGuhjT7DPwHJclRZtEPR6gr0GytN0tY4K4+X02GmWMwScHd6KWjUBNczZ4SMszweXFuLje3nliy5afBjnOYH5EB48Ltoc8JvdS9d//mVjHb1/k/20/a3QnriW+VFU+G1WnAXfsDXaBS2mnze/I4iiUK0RtGyNDoG34uvlnJLxy9SoiBG51RUaMJcTquVgODG1iCamMmhdvoBqnHKsKyxrSuQ2eBTxkjFn4GZdnV7WaNbw2UpSsV9++UtpzQQ0nZ0eluP5B0WS8oxX02NRynPZo/EzZsqwxZrqCLMGwiG7CYa6kc/toFeZ51u5XH7k1OXFNWXGhOPmPMGQUaspuIAhBORaArS9gYiRpcwc8BXjQj4fsYutSg7gnwrwyBdyEdBX/iCUkOnXmRfTUqGw/xUD1OoEJmSbEuZOHdr/8j9yB34CUBepV8n7RlcAAAAASUVORK5CYII=" + question = "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAEvElEQVRIDcVX208cVRj/5sxlB1j2Aiyy2lpgEZVaUvXBeIvERCOwC1Qlvhj/gcYnE1PAR1tsU598bOK7sV6A3ZVEX9pqE32yFzDKdlkS6ILslUXYmT0z4zlnmbLtzt76oJPznXO+y+/7zsy5fQPwPz1cvXEnpkMnDZ4/JSA0AobRpem6i2J5hDKAuLiGtTAu4O+C5wM3qLwW1QhscIHp8KTAo88FQXR2dnps7rY2SRIFEAWB+S5gDGoBQzqVVLf+TigYa2kDax/NzQ5/A8AZzMiiqhh47MxiHxKNOZvNdsTn8zncLqcFvFyUTmcgEo3m1Ly6RgY1HvrMHy23IkOyEk58sjDEceL33d3HWr1eL+IqDs8KTWUGbGzEtVhsbdfQtMDcrP8alZZSmcvAVPg1XkTzJ5453upobS3Tl4Jr9bM7OWNpaXkHa7p/4ezwz6X29zk+NRXqB4H/dXDwhMtubym1e+j+7u4u3Lh5K40N/ELw08CK6UgwOwAGB/zi1729PS3VgjpkBENPy3DUXYSupzFc+TMPmT390FVJz263Q09Pt31tNfYVADxHiBXEalKNTYfea25ufryr6xGRsJallQT94GU79BOTJokDSk+Q/vsv2oHqLEFE+KjXK9pkuXd8KvwuYVk5CGxwCAkXen09bG8yjUX1OnlTWeQguo3h0pUcXLqaY30qG3pKtkAcinx9PifiuYumhAUemwk/L0mCgzym3LI91l78vD8u7UN2X4cs+bw/Le8z2+6Ooo4xFpXT4QBRFF1jZ8LPUjULjBA36fF4mqmgGklCcS3m8ofzWdAOzoiDphre0+lpQgI3SW3YMBEnvOFyuyrOLTWkdHExS5v76ORRifFbOY211Sqn0yltxjffJDbT7I3B0L02SSJ8Y8XXKcJLfcW5/S2q1ATLsg3It/JSQxaYHPhuSbJRvm5qa0EwOtgE9FS7HslDLIFrYiXycrqmtVNDFph2GqVX+2Wgc35rXYXrEaUBeHGdsMCI51Oq2ggY4GgbWx5w9a983UEVRQHEoyQFFANzsKWoKuXrJrp3qfG+WsdypoaEFEUFEjBOusCGXcB6MJlIDbjIqqPCeshqhdfCpVKpPFlPQWpHBgCgY3w5kdhu7JUpukHaTmxrBV27TGEscPC8/6amGelMtnyfUiMr6rDzQMlKZyWjCYKO9UTonP821bPAJB8wsI4/jKxEdow6poxuJXpZUHKTbUUdVScDViJ3cpqunTbtDgIDLJwdmVcVNba1tVkwlfW0xc1R3fJufLOgFdTI/Dl/yLRki6vIcIamzg1H76z+Tq6wDrfLVdFn6h8dvryWY7BK9zBTkiqZShmrq7Es0rhRwt4rZc5Z6sNzoYHjA80kwSvT30PW0UlnMsby0h975B55q2rqY/oKzPzwCrmI5rpJ5kCSPYkei6auvtaA9Y14PrZGkj1sTMzPjvzyII57UGDyb38cPgIy+laUpCcfJr3FamEJ5/PvzF8Yv2v6LG0rBjaNAjOhcQEJX/A8cne0t5OE3iXKsgyUqI2i5GF/Lw+pTEZNJhIFklEmyV/F6YXZ0SDVV6KagU2g+Qsj8tyorhmPkROogySIwCM+QVKaDUxOv0Z+YUy//3n7L6y2u/Lkn4gSAAAAAElFTkSuQmCC" + info = "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAADzElEQVRIDcWXzXPbRBTAn7SSrJbEH8Vp4rjFjjPDUCgz0PbMDMwAwRknE4aP4dpe4QSHEigHSDNDp/9C+wfQr6RNTLgxvXGDoaEpxDSJmzYhtpSPUluydpd9InJE4yTqpCYaPe3Te2/fb3dlrZ8A9uiQgnL7TudfVQh/V1LUHuC8gzIWxb5ElpdBkhYcxvISpddGhnt/RvtOsgOYS32D4x8SQs4TRQ23H4zrsQPPqpqqgKoobu6a40DNroFhmvbi4l82pXTZofTTG8PZSwASd4MaXLYED3w+/jwoZFTT1EPd3d0tsVi0QffNJtM0oVC4+9CyrTmxMn0jQ9nC5igxpEbG3Jf5NxSQR1Pp1P5kskPeIqxR13Ubh/n5+2x2pviIQy03MpT7ER47Ns24b3DsdSKT8SMvHtkXdJaP5azf4uxv/zZVoZz1XD/be7PuEMp/wLkzEy8Rzn86+vLRZyLhVuHe/bm6sgq3JifXatw5PjaU+8PLKHsKAJcUCb7LZLr2BYGGdRlQNvo31sKRMKS60vs1Wb2MDC+qDu4/k/9ID+nPJRL4TD134xaBp15rhZNCWsUAGkdtWJOJBAmF9Ez/F99/4FnXwVySgXybyaRbPMe2re8B+dRtu3R1pVokIp/zglxw7vTECaIokWg02CuzWmFw4eYaXBSyWmVerm3bWCwm3k4llhu8cQwDXbCssPfbD7bpaAgqCEQJGo9x7W1tISIr76Hubj+KrL4ppquiIYh81hOph52fWKnrOymRaERbWFh4S8QNujPmnHXoIU3cBzufBObPqOshYJx3os0Fiw0/pmkhvG+qaJoGjPEDCHHBqPzf4oJlQgzbtprOtiwLZCKXEfQvWIJFy7bxvqliWTYI4AOEiBaAOmzMKBlNJxumURW/p7E6GDi7slQqNR28tLREucSu1MEjw9lfsHIwzWW0NUVMwwTKeHn0m95fEeAutfij547DPpmeLjzkfMtqBeNd8W8gft11NrhgzunCn39zyj723L49nksDX/1wK3X48AvJQ8n1AXlhu2uL9+bpvWJx8urXb7+Ck8RsPoDELUqzM7Nza4ZYFnQ+DSmXDSjOFVecqvWOB8W8PjBA/mzvLGN0YOr275Wn8byx9LkzdadCRc7r5/rvI9AT31J7JgAs9sSIrmXSab0zmdDESDecgTQs9h7Yd2dmqhLQ/kDFnpd3T8pbD441Ur2gJyQaj8c1UXmquq4DCsZZVhUqj6qioF+ulcslUdAzc1cFPSb1y8YnjJLljHeKHSiOfvEJU5KJNF+jfPxJPmGw757IPxpVi5HvZ9PZAAAAAElFTkSuQmCC" + + +class EmojiItem: + + """A container for an emoji character used by the Emoji class""" + + def __init__(self, name, category, subcategory, char): + """ + Parameters: + + name (str): + The name of the emoji character. + + category (str): + The major category of the emoji character. + + subcategory (str): + The subcategory of the emoji character. + + char (str): + The unicode character. + """ + self.name = name + self.category = category + self.subcategory = subcategory + self.char = char + + def __repr__(self) -> str: + return self.char + + +class Emoji: + """A class that contains emoji characters that can be used in the + `text` parameter in any tkinter widget with the option. + """ + + _ITEMS = [ + EmojiItem("KNOT", "activities", "arts & crafts", "🪢"), + EmojiItem("SEWING NEEDLE", "activities", "arts & crafts", "🪡"), + EmojiItem("BALL OF YARN", "activities", "arts & crafts", "🧶"), + EmojiItem("SPOOL OF THREAD", "activities", "arts & crafts", "🧵"), + EmojiItem("FRAME WITH PICTURE", "activities", "arts & crafts", "🖼"), + EmojiItem("PERFORMING ARTS", "activities", "arts & crafts", "🎭"), + EmojiItem("ARTIST PALETTE", "activities", "arts & crafts", "🎨"), + EmojiItem("THIRD PLACE MEDAL", "activities", "award-medal", "🥉"), + EmojiItem("SECOND PLACE MEDAL", "activities", "award-medal", "🥈"), + EmojiItem("FIRST PLACE MEDAL", "activities", "award-medal", "🥇"), + EmojiItem("TROPHY", "activities", "award-medal", "🏆"), + EmojiItem("SPORTS MEDAL", "activities", "award-medal", "🏅"), + EmojiItem("MILITARY MEDAL", "activities", "award-medal", "🎖"), + EmojiItem("SPARKLES", "activities", "event", "✨"), + EmojiItem("FIRECRACKER", "activities", "event", "🧨"), + EmojiItem("RED GIFT ENVELOPE", "activities", "event", "🧧"), + EmojiItem("TICKET", "activities", "event", "🎫"), + EmojiItem("ADMISSION TICKETS", "activities", "event", "🎟"), + EmojiItem("REMINDER RIBBON", "activities", "event", "🎗"), + EmojiItem("MOON VIEWING CEREMONY", "activities", "event", "🎑"), + EmojiItem("WIND CHIME", "activities", "event", "🎐"), + EmojiItem("CARP STREAMER", "activities", "event", "🎏"), + EmojiItem("JAPANESE DOLLS", "activities", "event", "🎎"), + EmojiItem("PINE DECORATION", "activities", "event", "🎍"), + EmojiItem("TANABATA TREE", "activities", "event", "🎋"), + EmojiItem("CONFETTI BALL", "activities", "event", "🎊"), + EmojiItem("PARTY POPPER", "activities", "event", "🎉"), + EmojiItem("BALLOON", "activities", "event", "🎈"), + EmojiItem("FIREWORK SPARKLER", "activities", "event", "🎇"), + EmojiItem("FIREWORKS", "activities", "event", "🎆"), + EmojiItem("CHRISTMAS TREE", "activities", "event", "🎄"), + EmojiItem("JACK-O-LANTERN", "activities", "event", "🎃"), + EmojiItem("WRAPPED PRESENT", "activities", "event", "🎁"), + EmojiItem("RIBBON", "activities", "event", "🎀"), + EmojiItem("BLACK CHESS PAWN", "activities", "game", "♟"), + EmojiItem("BLACK DIAMOND SUIT", "activities", "game", "♦"), + EmojiItem("BLACK HEART SUIT", "activities", "game", "♥"), + EmojiItem("BLACK CLUB SUIT", "activities", "game", "♣"), + EmojiItem("BLACK SPADE SUIT", "activities", "game", "♠"), + EmojiItem("NESTING DOLLS", "activities", "game", "🪆"), + EmojiItem("PINATA", "activities", "game", "🪅"), + EmojiItem("MAGIC WAND", "activities", "game", "🪄"), + EmojiItem("KITE", "activities", "game", "🪁"), + EmojiItem("YO-YO", "activities", "game", "🪀"), + EmojiItem("NAZAR AMULET", "activities", "game", "🧿"), + EmojiItem("TEDDY BEAR", "activities", "game", "🧸"), + EmojiItem("JIGSAW PUZZLE PIECE", "activities", "game", "🧩"), + EmojiItem("JOYSTICK", "activities", "game", "🕹"), + EmojiItem("CRYSTAL BALL", "activities", "game", "🔮"), + EmojiItem("FLOWER PLAYING CARDS", "activities", "game", "🎴"), + EmojiItem("GAME DIE", "activities", "game", "🎲"), + EmojiItem("BILLIARDS", "activities", "game", "🎱"), + EmojiItem("SLOT MACHINE", "activities", "game", "🎰"), + EmojiItem("DIRECT HIT", "activities", "game", "🎯"), + EmojiItem("VIDEO GAME", "activities", "game", "🎮"), + EmojiItem("PLAYING CARD BLACK JOKER", "activities", "game", "🃏"), + EmojiItem("MAHJONG TILE RED DRAGON", "activities", "game", "🀄"), + EmojiItem("ICE SKATE", "activities", "sport", "⛸"), + EmojiItem("FLAG IN HOLE", "activities", "sport", "⛳"), + EmojiItem("BASEBALL", "activities", "sport", "⚾"), + EmojiItem("SOCCER BALL", "activities", "sport", "⚽"), + EmojiItem("FLYING DISC", "activities", "sport", "🥏"), + EmojiItem("SOFTBALL", "activities", "sport", "🥎"), + EmojiItem("LACROSSE STICK AND BALL", "activities", "sport", "🥍"), + EmojiItem("CURLING STONE", "activities", "sport", "🥌"), + EmojiItem("MARTIAL ARTS UNIFORM", "activities", "sport", "🥋"), + EmojiItem("BOXING GLOVE", "activities", "sport", "🥊"), + EmojiItem("GOAL NET", "activities", "sport", "🥅"), + EmojiItem("DIVING MASK", "activities", "sport", "🤿"), + EmojiItem("SLED", "activities", "sport", "🛷"), + EmojiItem( + "BADMINTON RACQUET AND SHUTTLECOCK", "activities", "sport", "🏸" + ), + EmojiItem("TABLE TENNIS PADDLE AND BALL", "activities", "sport", "🏓"), + EmojiItem("ICE HOCKEY STICK AND PUCK", "activities", "sport", "🏒"), + EmojiItem("FIELD HOCKEY STICK AND BALL", "activities", "sport", "🏑"), + EmojiItem("VOLLEYBALL", "activities", "sport", "🏐"), + EmojiItem("CRICKET BAT AND BALL", "activities", "sport", "🏏"), + EmojiItem("RUGBY FOOTBALL", "activities", "sport", "🏉"), + EmojiItem("AMERICAN FOOTBALL", "activities", "sport", "🏈"), + EmojiItem("BASKETBALL AND HOOP", "activities", "sport", "🏀"), + EmojiItem("SKI AND SKI BOOT", "activities", "sport", "🎿"), + EmojiItem("TENNIS RACQUET AND BALL", "activities", "sport", "🎾"), + EmojiItem("RUNNING SHIRT WITH SASH", "activities", "sport", "🎽"), + EmojiItem("BOWLING", "activities", "sport", "🎳"), + EmojiItem("FISHING POLE AND FISH", "activities", "sport", "🎣"), + EmojiItem("FROG FACE", "animal-nature", "animal-amphibian", "🐸"), + EmojiItem("FEATHER", "animal-nature", "animal-bird", "🪶"), + EmojiItem("FLAMINGO", "animal-nature", "animal-bird", "🦩"), + EmojiItem("DODO", "animal-nature", "animal-bird", "🦤"), + EmojiItem("SWAN", "animal-nature", "animal-bird", "🦢"), + EmojiItem("PARROT", "animal-nature", "animal-bird", "🦜"), + EmojiItem("PEACOCK", "animal-nature", "animal-bird", "🦚"), + EmojiItem("OWL", "animal-nature", "animal-bird", "🦉"), + EmojiItem("DUCK", "animal-nature", "animal-bird", "🦆"), + EmojiItem("EAGLE", "animal-nature", "animal-bird", "🦅"), + EmojiItem("TURKEY", "animal-nature", "animal-bird", "🦃"), + EmojiItem("DOVE OF PEACE", "animal-nature", "animal-bird", "🕊"), + EmojiItem("PENGUIN", "animal-nature", "animal-bird", "🐧"), + EmojiItem("BIRD", "animal-nature", "animal-bird", "🐦"), + EmojiItem( + "FRONT-FACING BABY CHICK", "animal-nature", "animal-bird", "🐥" + ), + EmojiItem("BABY CHICK", "animal-nature", "animal-bird", "🐤"), + EmojiItem("HATCHING CHICK", "animal-nature", "animal-bird", "🐣"), + EmojiItem("CHICKEN", "animal-nature", "animal-bird", "🐔"), + EmojiItem("ROOSTER", "animal-nature", "animal-bird", "🐓"), + EmojiItem("COCKROACH", "animal-nature", "animal-bug", "🪳"), + EmojiItem("BEETLE", "animal-nature", "animal-bug", "🪲"), + EmojiItem("WORM", "animal-nature", "animal-bug", "🪱"), + EmojiItem("FLY", "animal-nature", "animal-bug", "🪰"), + EmojiItem("MICROBE", "animal-nature", "animal-bug", "🦠"), + EmojiItem("MOSQUITO", "animal-nature", "animal-bug", "🦟"), + EmojiItem("CRICKET", "animal-nature", "animal-bug", "🦗"), + EmojiItem("BUTTERFLY", "animal-nature", "animal-bug", "🦋"), + EmojiItem("SCORPION", "animal-nature", "animal-bug", "🦂"), + EmojiItem("SPIDER WEB", "animal-nature", "animal-bug", "🕸"), + EmojiItem("SPIDER", "animal-nature", "animal-bug", "🕷"), + EmojiItem("LADY BEETLE", "animal-nature", "animal-bug", "🐞"), + EmojiItem("HONEYBEE", "animal-nature", "animal-bug", "🐝"), + EmojiItem("ANT", "animal-nature", "animal-bug", "🐜"), + EmojiItem("BUG", "animal-nature", "animal-bug", "🐛"), + EmojiItem("SNAIL", "animal-nature", "animal-bug", "🐌"), + EmojiItem("GUIDE DOG", "animal-nature", "animal-mammal", "🦮"), + EmojiItem("BISON", "animal-nature", "animal-mammal", "🦬"), + EmojiItem("BEAVER", "animal-nature", "animal-mammal", "🦫"), + EmojiItem("SKUNK", "animal-nature", "animal-mammal", "🦨"), + EmojiItem("ORANGUTAN", "animal-nature", "animal-mammal", "🦧"), + EmojiItem("OTTER", "animal-nature", "animal-mammal", "🦦"), + EmojiItem("SLOTH", "animal-nature", "animal-mammal", "🦥"), + EmojiItem("MAMMOTH", "animal-nature", "animal-mammal", "🦣"), + EmojiItem("BADGER", "animal-nature", "animal-mammal", "🦡"), + EmojiItem("RACCOON", "animal-nature", "animal-mammal", "🦝"), + EmojiItem("HIPPOPOTAMUS", "animal-nature", "animal-mammal", "🦛"), + EmojiItem("LLAMA", "animal-nature", "animal-mammal", "🦙"), + EmojiItem("KANGAROO", "animal-nature", "animal-mammal", "🦘"), + EmojiItem("HEDGEHOG", "animal-nature", "animal-mammal", "🦔"), + EmojiItem("ZEBRA FACE", "animal-nature", "animal-mammal", "🦓"), + EmojiItem("GIRAFFE FACE", "animal-nature", "animal-mammal", "🦒"), + EmojiItem("RHINOCEROS", "animal-nature", "animal-mammal", "🦏"), + EmojiItem("GORILLA", "animal-nature", "animal-mammal", "🦍"), + EmojiItem("DEER", "animal-nature", "animal-mammal", "🦌"), + EmojiItem("FOX FACE", "animal-nature", "animal-mammal", "🦊"), + EmojiItem("BAT", "animal-nature", "animal-mammal", "🦇"), + EmojiItem("UNICORN FACE", "animal-nature", "animal-mammal", "🦄"), + EmojiItem("LION FACE", "animal-nature", "animal-mammal", "🦁"), + EmojiItem("CHIPMUNK", "animal-nature", "animal-mammal", "🐿"), + EmojiItem("PAW PRINTS", "animal-nature", "animal-mammal", "🐾"), + EmojiItem("PIG NOSE", "animal-nature", "animal-mammal", "🐽"), + EmojiItem("PANDA FACE", "animal-nature", "animal-mammal", "🐼"), + EmojiItem("BEAR FACE", "animal-nature", "animal-mammal", "🐻"), + EmojiItem("WOLF FACE", "animal-nature", "animal-mammal", "🐺"), + EmojiItem("HAMSTER FACE", "animal-nature", "animal-mammal", "🐹"), + EmojiItem("PIG FACE", "animal-nature", "animal-mammal", "🐷"), + EmojiItem("DOG FACE", "animal-nature", "animal-mammal", "🐶"), + EmojiItem("MONKEY FACE", "animal-nature", "animal-mammal", "🐵"), + EmojiItem("HORSE FACE", "animal-nature", "animal-mammal", "🐴"), + EmojiItem("CAT FACE", "animal-nature", "animal-mammal", "🐱"), + EmojiItem("RABBIT FACE", "animal-nature", "animal-mammal", "🐰"), + EmojiItem("TIGER FACE", "animal-nature", "animal-mammal", "🐯"), + EmojiItem("COW FACE", "animal-nature", "animal-mammal", "🐮"), + EmojiItem("MOUSE FACE", "animal-nature", "animal-mammal", "🐭"), + EmojiItem("BACTRIAN CAMEL", "animal-nature", "animal-mammal", "🐫"), + EmojiItem("DROMEDARY CAMEL", "animal-nature", "animal-mammal", "🐪"), + EmojiItem("POODLE", "animal-nature", "animal-mammal", "🐩"), + EmojiItem("KOALA", "animal-nature", "animal-mammal", "🐨"), + EmojiItem("ELEPHANT", "animal-nature", "animal-mammal", "🐘"), + EmojiItem("BOAR", "animal-nature", "animal-mammal", "🐗"), + EmojiItem("PIG", "animal-nature", "animal-mammal", "🐖"), + EmojiItem("DOG", "animal-nature", "animal-mammal", "🐕"), + EmojiItem("MONKEY", "animal-nature", "animal-mammal", "🐒"), + EmojiItem("SHEEP", "animal-nature", "animal-mammal", "🐑"), + EmojiItem("GOAT", "animal-nature", "animal-mammal", "🐐"), + EmojiItem("RAM", "animal-nature", "animal-mammal", "🐏"), + EmojiItem("HORSE", "animal-nature", "animal-mammal", "🐎"), + EmojiItem("CAT", "animal-nature", "animal-mammal", "🐈"), + EmojiItem("RABBIT", "animal-nature", "animal-mammal", "🐇"), + EmojiItem("LEOPARD", "animal-nature", "animal-mammal", "🐆"), + EmojiItem("TIGER", "animal-nature", "animal-mammal", "🐅"), + EmojiItem("COW", "animal-nature", "animal-mammal", "🐄"), + EmojiItem("WATER BUFFALO", "animal-nature", "animal-mammal", "🐃"), + EmojiItem("OX", "animal-nature", "animal-mammal", "🐂"), + EmojiItem("MOUSE", "animal-nature", "animal-mammal", "🐁"), + EmojiItem("RAT", "animal-nature", "animal-mammal", "🐀"), + EmojiItem("SEAL", "animal-nature", "animal-marine", "🦭"), + EmojiItem("SHARK", "animal-nature", "animal-marine", "🦈"), + EmojiItem("SPOUTING WHALE", "animal-nature", "animal-marine", "🐳"), + EmojiItem("DOLPHIN", "animal-nature", "animal-marine", "🐬"), + EmojiItem("BLOWFISH", "animal-nature", "animal-marine", "🐡"), + EmojiItem("TROPICAL FISH", "animal-nature", "animal-marine", "🐠"), + EmojiItem("FISH", "animal-nature", "animal-marine", "🐟"), + EmojiItem("SPIRAL SHELL", "animal-nature", "animal-marine", "🐚"), + EmojiItem("OCTOPUS", "animal-nature", "animal-marine", "🐙"), + EmojiItem("WHALE", "animal-nature", "animal-marine", "🐋"), + EmojiItem("T-REX", "animal-nature", "animal-reptile", "🦖"), + EmojiItem("SAUROPOD", "animal-nature", "animal-reptile", "🦕"), + EmojiItem("LIZARD", "animal-nature", "animal-reptile", "🦎"), + EmojiItem("DRAGON FACE", "animal-nature", "animal-reptile", "🐲"), + EmojiItem("TURTLE", "animal-nature", "animal-reptile", "🐢"), + EmojiItem("SNAKE", "animal-nature", "animal-reptile", "🐍"), + EmojiItem("CROCODILE", "animal-nature", "animal-reptile", "🐊"), + EmojiItem("DRAGON", "animal-nature", "animal-reptile", "🐉"), + EmojiItem("WILTED FLOWER", "animal-nature", "plant-flower", "🥀"), + EmojiItem("WHITE FLOWER", "animal-nature", "plant-flower", "💮"), + EmojiItem("BOUQUET", "animal-nature", "plant-flower", "💐"), + EmojiItem("ROSETTE", "animal-nature", "plant-flower", "🏵"), + EmojiItem("BLOSSOM", "animal-nature", "plant-flower", "🌼"), + EmojiItem("SUNFLOWER", "animal-nature", "plant-flower", "🌻"), + EmojiItem("HIBISCUS", "animal-nature", "plant-flower", "🌺"), + EmojiItem("ROSE", "animal-nature", "plant-flower", "🌹"), + EmojiItem("CHERRY BLOSSOM", "animal-nature", "plant-flower", "🌸"), + EmojiItem("TULIP", "animal-nature", "plant-flower", "🌷"), + EmojiItem("SHAMROCK", "animal-nature", "plant-other", "☘"), + EmojiItem("POTTED PLANT", "animal-nature", "plant-other", "🪴"), + EmojiItem( + "LEAF FLUTTERING IN WIND", "animal-nature", "plant-other", "🍃" + ), + EmojiItem("FALLEN LEAF", "animal-nature", "plant-other", "🍂"), + EmojiItem("MAPLE LEAF", "animal-nature", "plant-other", "🍁"), + EmojiItem("FOUR LEAF CLOVER", "animal-nature", "plant-other", "🍀"), + EmojiItem("HERB", "animal-nature", "plant-other", "🌿"), + EmojiItem("EAR OF RICE", "animal-nature", "plant-other", "🌾"), + EmojiItem("CACTUS", "animal-nature", "plant-other", "🌵"), + EmojiItem("PALM TREE", "animal-nature", "plant-other", "🌴"), + EmojiItem("DECIDUOUS TREE", "animal-nature", "plant-other", "🌳"), + EmojiItem("EVERGREEN TREE", "animal-nature", "plant-other", "🌲"), + EmojiItem("SEEDLING", "animal-nature", "plant-other", "🌱"), + EmojiItem( + "EMOJI COMPONENT WHITE HAIR", "component", "hair-style", "🦳" + ), + EmojiItem("EMOJI COMPONENT BALD", "component", "hair-style", "🦲"), + EmojiItem( + "EMOJI COMPONENT CURLY HAIR", "component", "hair-style", "🦱" + ), + EmojiItem("EMOJI COMPONENT RED HAIR", "component", "hair-style", "🦰"), + EmojiItem( + "EMOJI MODIFIER FITZPATRICK TYPE-6", "component", "skin-tone", "🏿" + ), + EmojiItem( + "EMOJI MODIFIER FITZPATRICK TYPE-5", "component", "skin-tone", "🏾" + ), + EmojiItem( + "EMOJI MODIFIER FITZPATRICK TYPE-4", "component", "skin-tone", "🏽" + ), + EmojiItem( + "EMOJI MODIFIER FITZPATRICK TYPE-3", "component", "skin-tone", "🏼" + ), + EmojiItem( + "EMOJI MODIFIER FITZPATRICK TYPE-1-2", + "component", + "skin-tone", + "🏻", + ), + EmojiItem("TRIANGULAR FLAG ON POST", "flags", "flag", "🚩"), + EmojiItem("WAVING BLACK FLAG", "flags", "flag", "🏴"), + EmojiItem("WAVING WHITE FLAG", "flags", "flag", "🏳"), + EmojiItem("CHEQUERED FLAG", "flags", "flag", "🏁"), + EmojiItem("CROSSED FLAGS", "flags", "flag", "🎌"), + EmojiItem("CHOPSTICKS", "food-drink", "dishware", "🥢"), + EmojiItem("SPOON", "food-drink", "dishware", "🥄"), + EmojiItem("HOCHO", "food-drink", "dishware", "🔪"), + EmojiItem("AMPHORA", "food-drink", "dishware", "🏺"), + EmojiItem("FORK AND KNIFE WITH PLATE", "food-drink", "dishware", "🍽"), + EmojiItem("FORK AND KNIFE", "food-drink", "dishware", "🍴"), + EmojiItem("HOT BEVERAGE", "food-drink", "drink", "☕"), + EmojiItem("TEAPOT", "food-drink", "drink", "🫖"), + EmojiItem("BUBBLE TEA", "food-drink", "drink", "🧋"), + EmojiItem("ICE CUBE", "food-drink", "drink", "🧊"), + EmojiItem("MATE DRINK", "food-drink", "drink", "🧉"), + EmojiItem("BEVERAGE BOX", "food-drink", "drink", "🧃"), + EmojiItem("CUP WITH STRAW", "food-drink", "drink", "🥤"), + EmojiItem("GLASS OF MILK", "food-drink", "drink", "🥛"), + EmojiItem("TUMBLER GLASS", "food-drink", "drink", "🥃"), + EmojiItem("CLINKING GLASSES", "food-drink", "drink", "🥂"), + EmojiItem("BOTTLE WITH POPPING CORK", "food-drink", "drink", "🍾"), + EmojiItem("BABY BOTTLE", "food-drink", "drink", "🍼"), + EmojiItem("CLINKING BEER MUGS", "food-drink", "drink", "🍻"), + EmojiItem("BEER MUG", "food-drink", "drink", "🍺"), + EmojiItem("TROPICAL DRINK", "food-drink", "drink", "🍹"), + EmojiItem("COCKTAIL GLASS", "food-drink", "drink", "🍸"), + EmojiItem("WINE GLASS", "food-drink", "drink", "🍷"), + EmojiItem("SAKE BOTTLE AND CUP", "food-drink", "drink", "🍶"), + EmojiItem("TEACUP WITHOUT HANDLE", "food-drink", "drink", "🍵"), + EmojiItem("MOON CAKE", "food-drink", "food-asian", "🥮"), + EmojiItem("TAKEOUT BOX", "food-drink", "food-asian", "🥡"), + EmojiItem("FORTUNE COOKIE", "food-drink", "food-asian", "🥠"), + EmojiItem("DUMPLING", "food-drink", "food-asian", "🥟"), + EmojiItem("BENTO BOX", "food-drink", "food-asian", "🍱"), + EmojiItem( + "FISH CAKE WITH SWIRL DESIGN", "food-drink", "food-asian", "🍥" + ), + EmojiItem("FRIED SHRIMP", "food-drink", "food-asian", "🍤"), + EmojiItem("SUSHI", "food-drink", "food-asian", "🍣"), + EmojiItem("ODEN", "food-drink", "food-asian", "🍢"), + EmojiItem("DANGO", "food-drink", "food-asian", "🍡"), + EmojiItem("ROASTED SWEET POTATO", "food-drink", "food-asian", "🍠"), + EmojiItem("SPAGHETTI", "food-drink", "food-asian", "🍝"), + EmojiItem("STEAMING BOWL", "food-drink", "food-asian", "🍜"), + EmojiItem("CURRY AND RICE", "food-drink", "food-asian", "🍛"), + EmojiItem("COOKED RICE", "food-drink", "food-asian", "🍚"), + EmojiItem("RICE BALL", "food-drink", "food-asian", "🍙"), + EmojiItem("RICE CRACKER", "food-drink", "food-asian", "🍘"), + EmojiItem("OLIVE", "food-drink", "food-fruit", "🫒"), + EmojiItem("BLUEBERRIES", "food-drink", "food-fruit", "🫐"), + EmojiItem("MANGO", "food-drink", "food-fruit", "🥭"), + EmojiItem("COCONUT", "food-drink", "food-fruit", "🥥"), + EmojiItem("KIWIFRUIT", "food-drink", "food-fruit", "🥝"), + EmojiItem("STRAWBERRY", "food-drink", "food-fruit", "🍓"), + EmojiItem("CHERRIES", "food-drink", "food-fruit", "🍒"), + EmojiItem("PEACH", "food-drink", "food-fruit", "🍑"), + EmojiItem("PEAR", "food-drink", "food-fruit", "🍐"), + EmojiItem("GREEN APPLE", "food-drink", "food-fruit", "🍏"), + EmojiItem("RED APPLE", "food-drink", "food-fruit", "🍎"), + EmojiItem("PINEAPPLE", "food-drink", "food-fruit", "🍍"), + EmojiItem("BANANA", "food-drink", "food-fruit", "🍌"), + EmojiItem("LEMON", "food-drink", "food-fruit", "🍋"), + EmojiItem("TANGERINE", "food-drink", "food-fruit", "🍊"), + EmojiItem("WATERMELON", "food-drink", "food-fruit", "🍉"), + EmojiItem("MELON", "food-drink", "food-fruit", "🍈"), + EmojiItem("GRAPES", "food-drink", "food-fruit", "🍇"), + EmojiItem("TOMATO", "food-drink", "food-fruit", "🍅"), + EmojiItem("OYSTER", "food-drink", "food-marine", "🦪"), + EmojiItem("LOBSTER", "food-drink", "food-marine", "🦞"), + EmojiItem("SQUID", "food-drink", "food-marine", "🦑"), + EmojiItem("SHRIMP", "food-drink", "food-marine", "🦐"), + EmojiItem("CRAB", "food-drink", "food-marine", "🦀"), + EmojiItem("FONDUE", "food-drink", "food-prepared", "🫕"), + EmojiItem("TAMALE", "food-drink", "food-prepared", "🫔"), + EmojiItem("FLATBREAD", "food-drink", "food-prepared", "🫓"), + EmojiItem("BUTTER", "food-drink", "food-prepared", "🧈"), + EmojiItem("WAFFLE", "food-drink", "food-prepared", "🧇"), + EmojiItem("FALAFEL", "food-drink", "food-prepared", "🧆"), + EmojiItem("SALT SHAKER", "food-drink", "food-prepared", "🧂"), + EmojiItem("CHEESE WEDGE", "food-drink", "food-prepared", "🧀"), + EmojiItem("BAGEL", "food-drink", "food-prepared", "🥯"), + EmojiItem("CANNED FOOD", "food-drink", "food-prepared", "🥫"), + EmojiItem("SANDWICH", "food-drink", "food-prepared", "🥪"), + EmojiItem("CUT OF MEAT", "food-drink", "food-prepared", "🥩"), + EmojiItem("PRETZEL", "food-drink", "food-prepared", "🥨"), + EmojiItem("BOWL WITH SPOON", "food-drink", "food-prepared", "🥣"), + EmojiItem("PANCAKES", "food-drink", "food-prepared", "🥞"), + EmojiItem("EGG", "food-drink", "food-prepared", "🥚"), + EmojiItem("STUFFED FLATBREAD", "food-drink", "food-prepared", "🥙"), + EmojiItem("SHALLOW PAN OF FOOD", "food-drink", "food-prepared", "🥘"), + EmojiItem("GREEN SALAD", "food-drink", "food-prepared", "🥗"), + EmojiItem("BAGUETTE BREAD", "food-drink", "food-prepared", "🥖"), + EmojiItem("BACON", "food-drink", "food-prepared", "🥓"), + EmojiItem("CROISSANT", "food-drink", "food-prepared", "🥐"), + EmojiItem("POPCORN", "food-drink", "food-prepared", "🍿"), + EmojiItem("COOKING", "food-drink", "food-prepared", "🍳"), + EmojiItem("POT OF FOOD", "food-drink", "food-prepared", "🍲"), + EmojiItem("FRENCH FRIES", "food-drink", "food-prepared", "🍟"), + EmojiItem("BREAD", "food-drink", "food-prepared", "🍞"), + EmojiItem("POULTRY LEG", "food-drink", "food-prepared", "🍗"), + EmojiItem("MEAT ON BONE", "food-drink", "food-prepared", "🍖"), + EmojiItem("SLICE OF PIZZA", "food-drink", "food-prepared", "🍕"), + EmojiItem("HAMBURGER", "food-drink", "food-prepared", "🍔"), + EmojiItem("BURRITO", "food-drink", "food-prepared", "🌯"), + EmojiItem("TACO", "food-drink", "food-prepared", "🌮"), + EmojiItem("HOT DOG", "food-drink", "food-prepared", "🌭"), + EmojiItem("CUPCAKE", "food-drink", "food-sweet", "🧁"), + EmojiItem("PIE", "food-drink", "food-sweet", "🥧"), + EmojiItem("BIRTHDAY CAKE", "food-drink", "food-sweet", "🎂"), + EmojiItem("SHORTCAKE", "food-drink", "food-sweet", "🍰"), + EmojiItem("HONEY POT", "food-drink", "food-sweet", "🍯"), + EmojiItem("CUSTARD", "food-drink", "food-sweet", "🍮"), + EmojiItem("LOLLIPOP", "food-drink", "food-sweet", "🍭"), + EmojiItem("CANDY", "food-drink", "food-sweet", "🍬"), + EmojiItem("CHOCOLATE BAR", "food-drink", "food-sweet", "🍫"), + EmojiItem("COOKIE", "food-drink", "food-sweet", "🍪"), + EmojiItem("DOUGHNUT", "food-drink", "food-sweet", "🍩"), + EmojiItem("ICE CREAM", "food-drink", "food-sweet", "🍨"), + EmojiItem("SHAVED ICE", "food-drink", "food-sweet", "🍧"), + EmojiItem("SOFT ICE CREAM", "food-drink", "food-sweet", "🍦"), + EmojiItem("BELL PEPPER", "food-drink", "food-vegetable", "🫑"), + EmojiItem("ONION", "food-drink", "food-vegetable", "🧅"), + EmojiItem("GARLIC", "food-drink", "food-vegetable", "🧄"), + EmojiItem("LEAFY GREEN", "food-drink", "food-vegetable", "🥬"), + EmojiItem("BROCCOLI", "food-drink", "food-vegetable", "🥦"), + EmojiItem("PEANUTS", "food-drink", "food-vegetable", "🥜"), + EmojiItem("CARROT", "food-drink", "food-vegetable", "🥕"), + EmojiItem("POTATO", "food-drink", "food-vegetable", "🥔"), + EmojiItem("CUCUMBER", "food-drink", "food-vegetable", "🥒"), + EmojiItem("AVOCADO", "food-drink", "food-vegetable", "🥑"), + EmojiItem("AUBERGINE", "food-drink", "food-vegetable", "🍆"), + EmojiItem("MUSHROOM", "food-drink", "food-vegetable", "🍄"), + EmojiItem("EAR OF MAIZE", "food-drink", "food-vegetable", "🌽"), + EmojiItem("HOT PEPPER", "food-drink", "food-vegetable", "🌶"), + EmojiItem("CHESTNUT", "food-drink", "food-vegetable", "🌰"), + EmojiItem("ROLLED-UP NEWSPAPER", "objects", "book-paper", "🗞"), + EmojiItem("BOOKMARK", "objects", "book-paper", "🔖"), + EmojiItem("NEWSPAPER", "objects", "book-paper", "📰"), + EmojiItem("SCROLL", "objects", "book-paper", "📜"), + EmojiItem("BOOKS", "objects", "book-paper", "📚"), + EmojiItem("ORANGE BOOK", "objects", "book-paper", "📙"), + EmojiItem("BLUE BOOK", "objects", "book-paper", "📘"), + EmojiItem("GREEN BOOK", "objects", "book-paper", "📗"), + EmojiItem("OPEN BOOK", "objects", "book-paper", "📖"), + EmojiItem("CLOSED BOOK", "objects", "book-paper", "📕"), + EmojiItem( + "NOTEBOOK WITH DECORATIVE COVER", "objects", "book-paper", "📔" + ), + EmojiItem("NOTEBOOK", "objects", "book-paper", "📓"), + EmojiItem("LEDGER", "objects", "book-paper", "📒"), + EmojiItem("BOOKMARK TABS", "objects", "book-paper", "📑"), + EmojiItem("PAGE FACING UP", "objects", "book-paper", "📄"), + EmojiItem("PAGE WITH CURL", "objects", "book-paper", "📃"), + EmojiItem("LABEL", "objects", "book-paper", "🏷"), + EmojiItem("HELMET WITH WHITE CROSS", "objects", "clothing", "⛑"), + EmojiItem("MILITARY HELMET", "objects", "clothing", "🪖"), + EmojiItem("THONG SANDAL", "objects", "clothing", "🩴"), + EmojiItem("SHORTS", "objects", "clothing", "🩳"), + EmojiItem("BRIEFS", "objects", "clothing", "🩲"), + EmojiItem("ONE-PIECE SWIMSUIT", "objects", "clothing", "🩱"), + EmojiItem("BALLET SHOES", "objects", "clothing", "🩰"), + EmojiItem("SOCKS", "objects", "clothing", "🧦"), + EmojiItem("COAT", "objects", "clothing", "🧥"), + EmojiItem("GLOVES", "objects", "clothing", "🧤"), + EmojiItem("SCARF", "objects", "clothing", "🧣"), + EmojiItem("BILLED CAP", "objects", "clothing", "🧢"), + EmojiItem("SAFETY VEST", "objects", "clothing", "🦺"), + EmojiItem("FLAT SHOE", "objects", "clothing", "🥿"), + EmojiItem("HIKING BOOT", "objects", "clothing", "🥾"), + EmojiItem("GOGGLES", "objects", "clothing", "🥽"), + EmojiItem("LAB COAT", "objects", "clothing", "🥼"), + EmojiItem("SARI", "objects", "clothing", "🥻"), + EmojiItem("SHOPPING BAGS", "objects", "clothing", "🛍"), + EmojiItem("DARK SUNGLASSES", "objects", "clothing", "🕶"), + EmojiItem("PRAYER BEADS", "objects", "clothing", "📿"), + EmojiItem("GEM STONE", "objects", "clothing", "💎"), + EmojiItem("RING", "objects", "clothing", "💍"), + EmojiItem("LIPSTICK", "objects", "clothing", "💄"), + EmojiItem("WOMANS BOOTS", "objects", "clothing", "👢"), + EmojiItem("WOMANS SANDAL", "objects", "clothing", "👡"), + EmojiItem("HIGH-HEELED SHOE", "objects", "clothing", "👠"), + EmojiItem("ATHLETIC SHOE", "objects", "clothing", "👟"), + EmojiItem("MANS SHOE", "objects", "clothing", "👞"), + EmojiItem("POUCH", "objects", "clothing", "👝"), + EmojiItem("HANDBAG", "objects", "clothing", "👜"), + EmojiItem("PURSE", "objects", "clothing", "👛"), + EmojiItem("WOMANS CLOTHES", "objects", "clothing", "👚"), + EmojiItem("BIKINI", "objects", "clothing", "👙"), + EmojiItem("KIMONO", "objects", "clothing", "👘"), + EmojiItem("DRESS", "objects", "clothing", "👗"), + EmojiItem("JEANS", "objects", "clothing", "👖"), + EmojiItem("T-SHIRT", "objects", "clothing", "👕"), + EmojiItem("NECKTIE", "objects", "clothing", "👔"), + EmojiItem("EYEGLASSES", "objects", "clothing", "👓"), + EmojiItem("WOMANS HAT", "objects", "clothing", "👒"), + EmojiItem("CROWN", "objects", "clothing", "👑"), + EmojiItem("TOP HAT", "objects", "clothing", "🎩"), + EmojiItem("GRADUATION CAP", "objects", "clothing", "🎓"), + EmojiItem("SCHOOL SATCHEL", "objects", "clothing", "🎒"), + EmojiItem("KEYBOARD", "objects", "computer", "⌨"), + EmojiItem("ABACUS", "objects", "computer", "🧮"), + EmojiItem("TRACKBALL", "objects", "computer", "🖲"), + EmojiItem("THREE BUTTON MOUSE", "objects", "computer", "🖱"), + EmojiItem("PRINTER", "objects", "computer", "🖨"), + EmojiItem("DESKTOP COMPUTER", "objects", "computer", "🖥"), + EmojiItem("ELECTRIC PLUG", "objects", "computer", "🔌"), + EmojiItem("BATTERY", "objects", "computer", "🔋"), + EmojiItem("DVD", "objects", "computer", "📀"), + EmojiItem("OPTICAL DISC", "objects", "computer", "💿"), + EmojiItem("FLOPPY DISK", "objects", "computer", "💾"), + EmojiItem("MINIDISC", "objects", "computer", "💽"), + EmojiItem("PERSONAL COMPUTER", "objects", "computer", "💻"), + EmojiItem("TOOTHBRUSH", "objects", "household", "🪥"), + EmojiItem("MOUSE TRAP", "objects", "household", "🪤"), + EmojiItem("BUCKET", "objects", "household", "🪣"), + EmojiItem("PLUNGER", "objects", "household", "🪠"), + EmojiItem("WINDOW", "objects", "household", "🪟"), + EmojiItem("MIRROR", "objects", "household", "🪞"), + EmojiItem("RAZOR", "objects", "household", "🪒"), + EmojiItem("CHAIR", "objects", "household", "🪑"), + EmojiItem("SPONGE", "objects", "household", "🧽"), + EmojiItem("BAR OF SOAP", "objects", "household", "🧼"), + EmojiItem("ROLL OF PAPER", "objects", "household", "🧻"), + EmojiItem("BASKET", "objects", "household", "🧺"), + EmojiItem("BROOM", "objects", "household", "🧹"), + EmojiItem("SAFETY PIN", "objects", "household", "🧷"), + EmojiItem("LOTION BOTTLE", "objects", "household", "🧴"), + EmojiItem("FIRE EXTINGUISHER", "objects", "household", "🧯"), + EmojiItem("ELEVATOR", "objects", "household", "🛗"), + EmojiItem("SHOPPING TROLLEY", "objects", "household", "🛒"), + EmojiItem("BED", "objects", "household", "🛏"), + EmojiItem("COUCH AND LAMP", "objects", "household", "🛋"), + EmojiItem("BATHTUB", "objects", "household", "🛁"), + EmojiItem("SHOWER", "objects", "household", "🚿"), + EmojiItem("TOILET", "objects", "household", "🚽"), + EmojiItem("DOOR", "objects", "household", "🚪"), + EmojiItem("DIYA LAMP", "objects", "light & video", "🪔"), + EmojiItem("CANDLE", "objects", "light & video", "🕯"), + EmojiItem("ELECTRIC TORCH", "objects", "light & video", "🔦"), + EmojiItem( + "RIGHT-POINTING MAGNIFYING GLASS", "objects", "light & video", "🔎" + ), + EmojiItem( + "LEFT-POINTING MAGNIFYING GLASS", "objects", "light & video", "🔍" + ), + EmojiItem("FILM PROJECTOR", "objects", "light & video", "📽"), + EmojiItem("VIDEOCASSETTE", "objects", "light & video", "📼"), + EmojiItem("TELEVISION", "objects", "light & video", "📺"), + EmojiItem("VIDEO CAMERA", "objects", "light & video", "📹"), + EmojiItem("CAMERA WITH FLASH", "objects", "light & video", "📸"), + EmojiItem("CAMERA", "objects", "light & video", "📷"), + EmojiItem("ELECTRIC LIGHT BULB", "objects", "light & video", "💡"), + EmojiItem("IZAKAYA LANTERN", "objects", "light & video", "🏮"), + EmojiItem("CLAPPER BOARD", "objects", "light & video", "🎬"), + EmojiItem("MOVIE CAMERA", "objects", "light & video", "🎥"), + EmojiItem("FILM FRAMES", "objects", "light & video", "🎞"), + EmojiItem("OLD KEY", "objects", "lock", "🗝"), + EmojiItem("OPEN LOCK", "objects", "lock", "🔓"), + EmojiItem("LOCK", "objects", "lock", "🔒"), + EmojiItem("KEY", "objects", "lock", "🔑"), + EmojiItem("CLOSED LOCK WITH KEY", "objects", "lock", "🔐"), + EmojiItem("LOCK WITH INK PEN", "objects", "lock", "🔏"), + EmojiItem("ENVELOPE", "objects", "mail", "✉"), + EmojiItem("BALLOT BOX WITH BALLOT", "objects", "mail", "🗳"), + EmojiItem("POSTBOX", "objects", "mail", "📮"), + EmojiItem("OPEN MAILBOX WITH LOWERED FLAG", "objects", "mail", "📭"), + EmojiItem("OPEN MAILBOX WITH RAISED FLAG", "objects", "mail", "📬"), + EmojiItem("CLOSED MAILBOX WITH RAISED FLAG", "objects", "mail", "📫"), + EmojiItem("CLOSED MAILBOX WITH LOWERED FLAG", "objects", "mail", "📪"), + EmojiItem( + "ENVELOPE WITH DOWNWARDS ARROW ABOVE", "objects", "mail", "📩" + ), + EmojiItem("INCOMING ENVELOPE", "objects", "mail", "📨"), + EmojiItem("E-MAIL SYMBOL", "objects", "mail", "📧"), + EmojiItem("PACKAGE", "objects", "mail", "📦"), + EmojiItem("INBOX TRAY", "objects", "mail", "📥"), + EmojiItem("OUTBOX TRAY", "objects", "mail", "📤"), + EmojiItem("STETHOSCOPE", "objects", "medical", "🩺"), + EmojiItem("ADHESIVE BANDAGE", "objects", "medical", "🩹"), + EmojiItem("DROP OF BLOOD", "objects", "medical", "🩸"), + EmojiItem("PILL", "objects", "medical", "💊"), + EmojiItem("SYRINGE", "objects", "medical", "💉"), + EmojiItem("COIN", "objects", "money", "🪙"), + EmojiItem("RECEIPT", "objects", "money", "🧾"), + EmojiItem( + "CHART WITH UPWARDS TREND AND YEN SIGN", "objects", "money", "💹" + ), + EmojiItem("MONEY WITH WINGS", "objects", "money", "💸"), + EmojiItem("BANKNOTE WITH POUND SIGN", "objects", "money", "💷"), + EmojiItem("BANKNOTE WITH EURO SIGN", "objects", "money", "💶"), + EmojiItem("BANKNOTE WITH DOLLAR SIGN", "objects", "money", "💵"), + EmojiItem("BANKNOTE WITH YEN SIGN", "objects", "money", "💴"), + EmojiItem("CREDIT CARD", "objects", "money", "💳"), + EmojiItem("MONEY BAG", "objects", "money", "💰"), + EmojiItem("RADIO", "objects", "music", "📻"), + EmojiItem("MUSICAL SCORE", "objects", "music", "🎼"), + EmojiItem("MULTIPLE MUSICAL NOTES", "objects", "music", "🎶"), + EmojiItem("MUSICAL NOTE", "objects", "music", "🎵"), + EmojiItem("HEADPHONE", "objects", "music", "🎧"), + EmojiItem("MICROPHONE", "objects", "music", "🎤"), + EmojiItem("CONTROL KNOBS", "objects", "music", "🎛"), + EmojiItem("LEVEL SLIDER", "objects", "music", "🎚"), + EmojiItem("STUDIO MICROPHONE", "objects", "music", "🎙"), + EmojiItem("LONG DRUM", "objects", "musical-instrument", "🪘"), + EmojiItem("ACCORDION", "objects", "musical-instrument", "🪗"), + EmojiItem("BANJO", "objects", "musical-instrument", "🪕"), + EmojiItem( + "DRUM WITH DRUMSTICKS", "objects", "musical-instrument", "🥁" + ), + EmojiItem("VIOLIN", "objects", "musical-instrument", "🎻"), + EmojiItem("TRUMPET", "objects", "musical-instrument", "🎺"), + EmojiItem("MUSICAL KEYBOARD", "objects", "musical-instrument", "🎹"), + EmojiItem("GUITAR", "objects", "musical-instrument", "🎸"), + EmojiItem("SAXOPHONE", "objects", "musical-instrument", "🎷"), + EmojiItem("BLACK SCISSORS", "objects", "office", "✂"), + EmojiItem("SPIRAL CALENDAR PAD", "objects", "office", "🗓"), + EmojiItem("SPIRAL NOTE PAD", "objects", "office", "🗒"), + EmojiItem("WASTEBASKET", "objects", "office", "🗑"), + EmojiItem("FILE CABINET", "objects", "office", "🗄"), + EmojiItem("CARD FILE BOX", "objects", "office", "🗃"), + EmojiItem("CARD INDEX DIVIDERS", "objects", "office", "🗂"), + EmojiItem("LINKED PAPERCLIPS", "objects", "office", "🖇"), + EmojiItem("TRIANGULAR RULER", "objects", "office", "📐"), + EmojiItem("STRAIGHT RULER", "objects", "office", "📏"), + EmojiItem("PAPERCLIP", "objects", "office", "📎"), + EmojiItem("ROUND PUSHPIN", "objects", "office", "📍"), + EmojiItem("PUSHPIN", "objects", "office", "📌"), + EmojiItem("CLIPBOARD", "objects", "office", "📋"), + EmojiItem("BAR CHART", "objects", "office", "📊"), + EmojiItem("CHART WITH DOWNWARDS TREND", "objects", "office", "📉"), + EmojiItem("CHART WITH UPWARDS TREND", "objects", "office", "📈"), + EmojiItem("CARD INDEX", "objects", "office", "📇"), + EmojiItem("TEAR-OFF CALENDAR", "objects", "office", "📆"), + EmojiItem("CALENDAR", "objects", "office", "📅"), + EmojiItem("OPEN FILE FOLDER", "objects", "office", "📂"), + EmojiItem("FILE FOLDER", "objects", "office", "📁"), + EmojiItem("BRIEFCASE", "objects", "office", "💼"), + EmojiItem("FUNERAL URN", "objects", "other-object", "⚱"), + EmojiItem("COFFIN", "objects", "other-object", "⚰"), + EmojiItem("PLACARD", "objects", "other-object", "🪧"), + EmojiItem("HEADSTONE", "objects", "other-object", "🪦"), + EmojiItem("SMOKING SYMBOL", "objects", "other-object", "🚬"), + EmojiItem("MOYAI", "objects", "other-object", "🗿"), + EmojiItem("BLACK TELEPHONE", "objects", "phone", "☎"), + EmojiItem( + "MOBILE PHONE WITH RIGHTWARDS ARROW AT LEFT", + "objects", + "phone", + "📲", + ), + EmojiItem("MOBILE PHONE", "objects", "phone", "📱"), + EmojiItem("FAX MACHINE", "objects", "phone", "📠"), + EmojiItem("PAGER", "objects", "phone", "📟"), + EmojiItem("TELEPHONE RECEIVER", "objects", "phone", "📞"), + EmojiItem("ALEMBIC", "objects", "science", "⚗"), + EmojiItem("DNA DOUBLE HELIX", "objects", "science", "🧬"), + EmojiItem("PETRI DISH", "objects", "science", "🧫"), + EmojiItem("TEST TUBE", "objects", "science", "🧪"), + EmojiItem("TELESCOPE", "objects", "science", "🔭"), + EmojiItem("MICROSCOPE", "objects", "science", "🔬"), + EmojiItem("SATELLITE ANTENNA", "objects", "science", "📡"), + EmojiItem("BELL WITH CANCELLATION STROKE", "objects", "sound", "🔕"), + EmojiItem("BELL", "objects", "sound", "🔔"), + EmojiItem("SPEAKER WITH THREE SOUND WAVES", "objects", "sound", "🔊"), + EmojiItem("SPEAKER WITH ONE SOUND WAVE", "objects", "sound", "🔉"), + EmojiItem("SPEAKER", "objects", "sound", "🔈"), + EmojiItem("SPEAKER WITH CANCELLATION STROKE", "objects", "sound", "🔇"), + EmojiItem("POSTAL HORN", "objects", "sound", "📯"), + EmojiItem("CHEERING MEGAPHONE", "objects", "sound", "📣"), + EmojiItem("PUBLIC ADDRESS LOUDSPEAKER", "objects", "sound", "📢"), + EmojiItem("CHAINS", "objects", "tool", "⛓"), + EmojiItem("PICK", "objects", "tool", "⛏"), + EmojiItem("GEAR", "objects", "tool", "⚙"), + EmojiItem("SCALES", "objects", "tool", "⚖"), + EmojiItem("CROSSED SWORDS", "objects", "tool", "⚔"), + EmojiItem("HAMMER AND PICK", "objects", "tool", "⚒"), + EmojiItem("HOOK", "objects", "tool", "🪝"), + EmojiItem("LADDER", "objects", "tool", "🪜"), + EmojiItem("SCREWDRIVER", "objects", "tool", "🪛"), + EmojiItem("CARPENTRY SAW", "objects", "tool", "🪚"), + EmojiItem("AXE", "objects", "tool", "🪓"), + EmojiItem("BOOMERANG", "objects", "tool", "🪃"), + EmojiItem("MAGNET", "objects", "tool", "🧲"), + EmojiItem("TOOLBOX", "objects", "tool", "🧰"), + EmojiItem("PROBING CANE", "objects", "tool", "🦯"), + EmojiItem("SHIELD", "objects", "tool", "🛡"), + EmojiItem("HAMMER AND WRENCH", "objects", "tool", "🛠"), + EmojiItem("DAGGER KNIFE", "objects", "tool", "🗡"), + EmojiItem("COMPRESSION", "objects", "tool", "🗜"), + EmojiItem("PISTOL", "objects", "tool", "🔫"), + EmojiItem("NUT AND BOLT", "objects", "tool", "🔩"), + EmojiItem("HAMMER", "objects", "tool", "🔨"), + EmojiItem("WRENCH", "objects", "tool", "🔧"), + EmojiItem("LINK SYMBOL", "objects", "tool", "🔗"), + EmojiItem("BOW AND ARROW", "objects", "tool", "🏹"), + EmojiItem("PENCIL", "objects", "writing", "✏"), + EmojiItem("BLACK NIB", "objects", "writing", "✒"), + EmojiItem("LOWER LEFT CRAYON", "objects", "writing", "🖍"), + EmojiItem("LOWER LEFT PAINTBRUSH", "objects", "writing", "🖌"), + EmojiItem("LOWER LEFT FOUNTAIN PEN", "objects", "writing", "🖋"), + EmojiItem("LOWER LEFT BALLPOINT PEN", "objects", "writing", "🖊"), + EmojiItem("MEMO", "objects", "writing", "📝"), + EmojiItem("LUNGS", "people-body", "body-parts", "🫁"), + EmojiItem("ANATOMICAL HEART", "people-body", "body-parts", "🫀"), + EmojiItem("BRAIN", "people-body", "body-parts", "🧠"), + EmojiItem("MECHANICAL LEG", "people-body", "body-parts", "🦿"), + EmojiItem("MECHANICAL ARM", "people-body", "body-parts", "🦾"), + EmojiItem("EAR WITH HEARING AID", "people-body", "body-parts", "🦻"), + EmojiItem("TOOTH", "people-body", "body-parts", "🦷"), + EmojiItem("FOOT", "people-body", "body-parts", "🦶"), + EmojiItem("LEG", "people-body", "body-parts", "🦵"), + EmojiItem("BONE", "people-body", "body-parts", "🦴"), + EmojiItem("FLEXED BICEPS", "people-body", "body-parts", "💪"), + EmojiItem("TONGUE", "people-body", "body-parts", "👅"), + EmojiItem("MOUTH", "people-body", "body-parts", "👄"), + EmojiItem("NOSE", "people-body", "body-parts", "👃"), + EmojiItem("EAR", "people-body", "body-parts", "👂"), + EmojiItem("EYE", "people-body", "body-parts", "👁"), + EmojiItem("EYES", "people-body", "body-parts", "👀"), + EmojiItem("COUPLE WITH HEART", "people-body", "family", "💑"), + EmojiItem("KISS", "people-body", "family", "💏"), + EmojiItem("TWO WOMEN HOLDING HANDS", "people-body", "family", "👭"), + EmojiItem("TWO MEN HOLDING HANDS", "people-body", "family", "👬"), + EmojiItem("MAN AND WOMAN HOLDING HANDS", "people-body", "family", "👫"), + EmojiItem("FAMILY", "people-body", "family", "👪"), + EmojiItem("RAISED FIST", "people-body", "hand-fingers-closed", "✊"), + EmojiItem( + "RIGHT-FACING FIST", "people-body", "hand-fingers-closed", "🤜" + ), + EmojiItem( + "LEFT-FACING FIST", "people-body", "hand-fingers-closed", "🤛" + ), + EmojiItem( + "THUMBS DOWN SIGN", "people-body", "hand-fingers-closed", "👎" + ), + EmojiItem("THUMBS UP SIGN", "people-body", "hand-fingers-closed", "👍"), + EmojiItem( + "FISTED HAND SIGN", "people-body", "hand-fingers-closed", "👊" + ), + EmojiItem("RAISED HAND", "people-body", "hand-fingers-open", "✋"), + EmojiItem( + "RAISED BACK OF HAND", "people-body", "hand-fingers-open", "🤚" + ), + EmojiItem( + "RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS", + "people-body", + "hand-fingers-open", + "🖖", + ), + EmojiItem( + "RAISED HAND WITH FINGERS SPLAYED", + "people-body", + "hand-fingers-open", + "🖐", + ), + EmojiItem("WAVING HAND SIGN", "people-body", "hand-fingers-open", "👋"), + EmojiItem("VICTORY HAND", "people-body", "hand-fingers-partial", "✌"), + EmojiItem( + "I LOVE YOU HAND SIGN", "people-body", "hand-fingers-partial", "🤟" + ), + EmojiItem( + "HAND WITH INDEX AND MIDDLE FINGERS CROSSED", + "people-body", + "hand-fingers-partial", + "🤞", + ), + EmojiItem("CALL ME HAND", "people-body", "hand-fingers-partial", "🤙"), + EmojiItem( + "SIGN OF THE HORNS", "people-body", "hand-fingers-partial", "🤘" + ), + EmojiItem("PINCHING HAND", "people-body", "hand-fingers-partial", "🤏"), + EmojiItem( + "PINCHED FINGERS", "people-body", "hand-fingers-partial", "🤌" + ), + EmojiItem("OK HAND SIGN", "people-body", "hand-fingers-partial", "👌"), + EmojiItem("WRITING HAND", "people-body", "hand-prop", "✍"), + EmojiItem("SELFIE", "people-body", "hand-prop", "🤳"), + EmojiItem("NAIL POLISH", "people-body", "hand-prop", "💅"), + EmojiItem("PALMS UP TOGETHER", "people-body", "hands", "🤲"), + EmojiItem("HANDSHAKE", "people-body", "hands", "🤝"), + EmojiItem("PERSON WITH FOLDED HANDS", "people-body", "hands", "🙏"), + EmojiItem( + "PERSON RAISING BOTH HANDS IN CELEBRATION", + "people-body", + "hands", + "🙌", + ), + EmojiItem("OPEN HANDS SIGN", "people-body", "hands", "👐"), + EmojiItem("CLAPPING HANDS SIGN", "people-body", "hands", "👏"), + EmojiItem( + "WHITE UP POINTING INDEX", "people-body", "hand-single-finger", "☝" + ), + EmojiItem( + "REVERSED HAND WITH MIDDLE FINGER EXTENDED", + "people-body", + "hand-single-finger", + "🖕", + ), + EmojiItem( + "WHITE RIGHT POINTING BACKHAND INDEX", + "people-body", + "hand-single-finger", + "👉", + ), + EmojiItem( + "WHITE LEFT POINTING BACKHAND INDEX", + "people-body", + "hand-single-finger", + "👈", + ), + EmojiItem( + "WHITE DOWN POINTING BACKHAND INDEX", + "people-body", + "hand-single-finger", + "👇", + ), + EmojiItem( + "WHITE UP POINTING BACKHAND INDEX", + "people-body", + "hand-single-finger", + "👆", + ), + EmojiItem("BEARDED PERSON", "people-body", "person", "🧔"), + EmojiItem("OLDER ADULT", "people-body", "person", "🧓"), + EmojiItem("CHILD", "people-body", "person", "🧒"), + EmojiItem("ADULT", "people-body", "person", "🧑"), + EmojiItem("BABY", "people-body", "person", "👶"), + EmojiItem("OLDER WOMAN", "people-body", "person", "👵"), + EmojiItem("OLDER MAN", "people-body", "person", "👴"), + EmojiItem("PERSON WITH BLOND HAIR", "people-body", "person", "👱"), + EmojiItem("WOMAN", "people-body", "person", "👩"), + EmojiItem("MAN", "people-body", "person", "👨"), + EmojiItem("GIRL", "people-body", "person", "👧"), + EmojiItem("BOY", "people-body", "person", "👦"), + EmojiItem("PERSON CLIMBING", "people-body", "person-activity", "🧗"), + EmojiItem( + "PERSON IN STEAMY ROOM", "people-body", "person-activity", "🧖" + ), + EmojiItem("KNEELING PERSON", "people-body", "person-activity", "🧎"), + EmojiItem("STANDING PERSON", "people-body", "person-activity", "🧍"), + EmojiItem("PEDESTRIAN", "people-body", "person-activity", "🚶"), + EmojiItem("MAN DANCING", "people-body", "person-activity", "🕺"), + EmojiItem( + "MAN IN BUSINESS SUIT LEVITATING", + "people-body", + "person-activity", + "🕴", + ), + EmojiItem("HAIRCUT", "people-body", "person-activity", "💇"), + EmojiItem("FACE MASSAGE", "people-body", "person-activity", "💆"), + EmojiItem("DANCER", "people-body", "person-activity", "💃"), + EmojiItem( + "WOMAN WITH BUNNY EARS", "people-body", "person-activity", "👯" + ), + EmojiItem("RUNNER", "people-body", "person-activity", "🏃"), + EmojiItem("ZOMBIE", "people-body", "person-fantasy", "🧟"), + EmojiItem("GENIE", "people-body", "person-fantasy", "🧞"), + EmojiItem("ELF", "people-body", "person-fantasy", "🧝"), + EmojiItem("MERPERSON", "people-body", "person-fantasy", "🧜"), + EmojiItem("VAMPIRE", "people-body", "person-fantasy", "🧛"), + EmojiItem("FAIRY", "people-body", "person-fantasy", "🧚"), + EmojiItem("MAGE", "people-body", "person-fantasy", "🧙"), + EmojiItem("SUPERVILLAIN", "people-body", "person-fantasy", "🦹"), + EmojiItem("SUPERHERO", "people-body", "person-fantasy", "🦸"), + EmojiItem("MOTHER CHRISTMAS", "people-body", "person-fantasy", "🤶"), + EmojiItem("BABY ANGEL", "people-body", "person-fantasy", "👼"), + EmojiItem("FATHER CHRISTMAS", "people-body", "person-fantasy", "🎅"), + EmojiItem("DEAF PERSON", "people-body", "person-gesture", "🧏"), + EmojiItem("SHRUG", "people-body", "person-gesture", "🤷"), + EmojiItem("FACE PALM", "people-body", "person-gesture", "🤦"), + EmojiItem( + "PERSON WITH POUTING FACE", "people-body", "person-gesture", "🙎" + ), + EmojiItem("PERSON FROWNING", "people-body", "person-gesture", "🙍"), + EmojiItem( + "HAPPY PERSON RAISING ONE HAND", + "people-body", + "person-gesture", + "🙋", + ), + EmojiItem( + "PERSON BOWING DEEPLY", "people-body", "person-gesture", "🙇" + ), + EmojiItem( + "FACE WITH OK GESTURE", "people-body", "person-gesture", "🙆" + ), + EmojiItem( + "FACE WITH NO GOOD GESTURE", "people-body", "person-gesture", "🙅" + ), + EmojiItem( + "INFORMATION DESK PERSON", "people-body", "person-gesture", "💁" + ), + EmojiItem( + "PERSON IN LOTUS POSITION", "people-body", "person-resting", "🧘" + ), + EmojiItem( + "SLEEPING ACCOMMODATION", "people-body", "person-resting", "🛌" + ), + EmojiItem("BATH", "people-body", "person-resting", "🛀"), + EmojiItem("PERSON WITH HEADSCARF", "people-body", "person-role", "🧕"), + EmojiItem("NINJA", "people-body", "person-role", "🥷"), + EmojiItem("MAN IN TUXEDO", "people-body", "person-role", "🤵"), + EmojiItem("PRINCE", "people-body", "person-role", "🤴"), + EmojiItem("BREAST-FEEDING", "people-body", "person-role", "🤱"), + EmojiItem("PREGNANT WOMAN", "people-body", "person-role", "🤰"), + EmojiItem("SLEUTH OR SPY", "people-body", "person-role", "🕵"), + EmojiItem("GUARDSMAN", "people-body", "person-role", "💂"), + EmojiItem("PRINCESS", "people-body", "person-role", "👸"), + EmojiItem("CONSTRUCTION WORKER", "people-body", "person-role", "👷"), + EmojiItem("MAN WITH TURBAN", "people-body", "person-role", "👳"), + EmojiItem("MAN WITH GUA PI MAO", "people-body", "person-role", "👲"), + EmojiItem("BRIDE WITH VEIL", "people-body", "person-role", "👰"), + EmojiItem("POLICE OFFICER", "people-body", "person-role", "👮"), + EmojiItem("PERSON WITH BALL", "people-body", "person-sport", "⛹"), + EmojiItem("SKIER", "people-body", "person-sport", "⛷"), + EmojiItem("HANDBALL", "people-body", "person-sport", "🤾"), + EmojiItem("WATER POLO", "people-body", "person-sport", "🤽"), + EmojiItem("WRESTLERS", "people-body", "person-sport", "🤼"), + EmojiItem("FENCER", "people-body", "person-sport", "🤺"), + EmojiItem("JUGGLING", "people-body", "person-sport", "🤹"), + EmojiItem( + "PERSON DOING CARTWHEEL", "people-body", "person-sport", "🤸" + ), + EmojiItem("MOUNTAIN BICYCLIST", "people-body", "person-sport", "🚵"), + EmojiItem("BICYCLIST", "people-body", "person-sport", "🚴"), + EmojiItem("ROWBOAT", "people-body", "person-sport", "🚣"), + EmojiItem("GOLFER", "people-body", "person-sport", "🏌"), + EmojiItem("WEIGHT LIFTER", "people-body", "person-sport", "🏋"), + EmojiItem("SWIMMER", "people-body", "person-sport", "🏊"), + EmojiItem("HORSE RACING", "people-body", "person-sport", "🏇"), + EmojiItem("SURFER", "people-body", "person-sport", "🏄"), + EmojiItem("SNOWBOARDER", "people-body", "person-sport", "🏂"), + EmojiItem("PEOPLE HUGGING", "people-body", "person-symbol", "🫂"), + EmojiItem( + "SPEAKING HEAD IN SILHOUETTE", "people-body", "person-symbol", "🗣" + ), + EmojiItem("BUSTS IN SILHOUETTE", "people-body", "person-symbol", "👥"), + EmojiItem("BUST IN SILHOUETTE", "people-body", "person-symbol", "👤"), + EmojiItem("FOOTPRINTS", "people-body", "person-symbol", "👣"), + EmojiItem( + "CIRCLED LATIN CAPITAL LETTER M", "symbols", "alphanum", "Ⓜ" + ), + EmojiItem("CIRCLED IDEOGRAPH SECRET", "symbols", "alphanum", "㊙"), + EmojiItem( + "CIRCLED IDEOGRAPH CONGRATULATION", "symbols", "alphanum", "㊗" + ), + EmojiItem("INFORMATION SOURCE", "symbols", "alphanum", "ℹ"), + EmojiItem( + "INPUT SYMBOL FOR LATIN LETTERS", "symbols", "alphanum", "🔤" + ), + EmojiItem("INPUT SYMBOL FOR SYMBOLS", "symbols", "alphanum", "🔣"), + EmojiItem("INPUT SYMBOL FOR NUMBERS", "symbols", "alphanum", "🔢"), + EmojiItem( + "INPUT SYMBOL FOR LATIN SMALL LETTERS", "symbols", "alphanum", "🔡" + ), + EmojiItem( + "INPUT SYMBOL FOR LATIN CAPITAL LETTERS", + "symbols", + "alphanum", + "🔠", + ), + EmojiItem("CIRCLED IDEOGRAPH ACCEPT", "symbols", "alphanum", "🉑"), + EmojiItem("CIRCLED IDEOGRAPH ADVANTAGE", "symbols", "alphanum", "🉐"), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-55B6", "symbols", "alphanum", "🈺" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-5272", "symbols", "alphanum", "🈹" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-7533", "symbols", "alphanum", "🈸" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-6708", "symbols", "alphanum", "🈷" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-6709", "symbols", "alphanum", "🈶" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-6E80", "symbols", "alphanum", "🈵" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-5408", "symbols", "alphanum", "🈴" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-7A7A", "symbols", "alphanum", "🈳" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-7981", "symbols", "alphanum", "🈲" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-6307", "symbols", "alphanum", "🈯" + ), + EmojiItem( + "SQUARED CJK UNIFIED IDEOGRAPH-7121", "symbols", "alphanum", "🈚" + ), + EmojiItem("SQUARED KATAKANA SA", "symbols", "alphanum", "🈂"), + EmojiItem("SQUARED KATAKANA KOKO", "symbols", "alphanum", "🈁"), + EmojiItem("SQUARED VS", "symbols", "alphanum", "🆚"), + EmojiItem( + "SQUARED UP WITH EXCLAMATION MARK", "symbols", "alphanum", "🆙" + ), + EmojiItem("SQUARED SOS", "symbols", "alphanum", "🆘"), + EmojiItem("SQUARED OK", "symbols", "alphanum", "🆗"), + EmojiItem("SQUARED NG", "symbols", "alphanum", "🆖"), + EmojiItem("SQUARED NEW", "symbols", "alphanum", "🆕"), + EmojiItem("SQUARED ID", "symbols", "alphanum", "🆔"), + EmojiItem("SQUARED FREE", "symbols", "alphanum", "🆓"), + EmojiItem("SQUARED COOL", "symbols", "alphanum", "🆒"), + EmojiItem("SQUARED CL", "symbols", "alphanum", "🆑"), + EmojiItem("NEGATIVE SQUARED AB", "symbols", "alphanum", "🆎"), + EmojiItem( + "NEGATIVE SQUARED LATIN CAPITAL LETTER P", + "symbols", + "alphanum", + "🅿", + ), + EmojiItem( + "NEGATIVE SQUARED LATIN CAPITAL LETTER O", + "symbols", + "alphanum", + "🅾", + ), + EmojiItem( + "NEGATIVE SQUARED LATIN CAPITAL LETTER B", + "symbols", + "alphanum", + "🅱", + ), + EmojiItem( + "NEGATIVE SQUARED LATIN CAPITAL LETTER A", + "symbols", + "alphanum", + "🅰", + ), + EmojiItem("DOWNWARDS BLACK ARROW", "symbols", "arrow", "⬇"), + EmojiItem("UPWARDS BLACK ARROW", "symbols", "arrow", "⬆"), + EmojiItem("LEFTWARDS BLACK ARROW", "symbols", "arrow", "⬅"), + EmojiItem("BLACK RIGHTWARDS ARROW", "symbols", "arrow", "➡"), + EmojiItem("RIGHTWARDS ARROW WITH HOOK", "symbols", "arrow", "↪"), + EmojiItem("LEFTWARDS ARROW WITH HOOK", "symbols", "arrow", "↩"), + EmojiItem( + "ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS", + "symbols", + "arrow", + "⤵", + ), + EmojiItem( + "ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS", + "symbols", + "arrow", + "⤴", + ), + EmojiItem("SOUTH WEST ARROW", "symbols", "arrow", "↙"), + EmojiItem("SOUTH EAST ARROW", "symbols", "arrow", "↘"), + EmojiItem("NORTH EAST ARROW", "symbols", "arrow", "↗"), + EmojiItem("NORTH WEST ARROW", "symbols", "arrow", "↖"), + EmojiItem("UP DOWN ARROW", "symbols", "arrow", "↕"), + EmojiItem("LEFT RIGHT ARROW", "symbols", "arrow", "↔"), + EmojiItem("TOP WITH UPWARDS ARROW ABOVE", "symbols", "arrow", "🔝"), + EmojiItem("SOON WITH RIGHTWARDS ARROW ABOVE", "symbols", "arrow", "🔜"), + EmojiItem( + "ON WITH EXCLAMATION MARK WITH LEFT RIGHT ARROW ABOVE", + "symbols", + "arrow", + "🔛", + ), + EmojiItem("END WITH LEFTWARDS ARROW ABOVE", "symbols", "arrow", "🔚"), + EmojiItem("BACK WITH LEFTWARDS ARROW ABOVE", "symbols", "arrow", "🔙"), + EmojiItem( + "ANTICLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS", + "symbols", + "arrow", + "🔄", + ), + EmojiItem( + "CLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS", + "symbols", + "arrow", + "🔃", + ), + EmojiItem("BLACK LEFT-POINTING TRIANGLE", "symbols", "av-symbol", "◀"), + EmojiItem( + "BLACK RIGHT-POINTING TRIANGLE", "symbols", "av-symbol", "▶" + ), + EmojiItem("BLACK CIRCLE FOR RECORD", "symbols", "av-symbol", "⏺"), + EmojiItem("BLACK SQUARE FOR STOP", "symbols", "av-symbol", "⏹"), + EmojiItem("DOUBLE VERTICAL BAR", "symbols", "av-symbol", "⏸"), + EmojiItem( + "BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR", + "symbols", + "av-symbol", + "⏯", + ), + EmojiItem( + "BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR", + "symbols", + "av-symbol", + "⏮", + ), + EmojiItem( + "BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR", + "symbols", + "av-symbol", + "⏭", + ), + EmojiItem( + "BLACK DOWN-POINTING DOUBLE TRIANGLE", "symbols", "av-symbol", "⏬" + ), + EmojiItem( + "BLACK UP-POINTING DOUBLE TRIANGLE", "symbols", "av-symbol", "⏫" + ), + EmojiItem( + "BLACK LEFT-POINTING DOUBLE TRIANGLE", "symbols", "av-symbol", "⏪" + ), + EmojiItem("EJECT SYMBOL", "symbols", "av-symbol", "⏏"), + EmojiItem( + "DOWN-POINTING SMALL RED TRIANGLE", "symbols", "av-symbol", "🔽" + ), + EmojiItem( + "UP-POINTING SMALL RED TRIANGLE", "symbols", "av-symbol", "🔼" + ), + EmojiItem("HIGH BRIGHTNESS SYMBOL", "symbols", "av-symbol", "🔆"), + EmojiItem("LOW BRIGHTNESS SYMBOL", "symbols", "av-symbol", "🔅"), + EmojiItem( + "CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY", + "symbols", + "av-symbol", + "🔂", + ), + EmojiItem( + "CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS", + "symbols", + "av-symbol", + "🔁", + ), + EmojiItem("TWISTED RIGHTWARDS ARROWS", "symbols", "av-symbol", "🔀"), + EmojiItem("ANTENNA WITH BARS", "symbols", "av-symbol", "📶"), + EmojiItem("MOBILE PHONE OFF", "symbols", "av-symbol", "📴"), + EmojiItem("VIBRATION MODE", "symbols", "av-symbol", "📳"), + EmojiItem("CINEMA", "symbols", "av-symbol", "🎦"), + EmojiItem("HEAVY DOLLAR SIGN", "symbols", "currency", "💲"), + EmojiItem("CURRENCY EXCHANGE", "symbols", "currency", "💱"), + EmojiItem( + "MALE WITH STROKE AND MALE AND FEMALE SIGN", + "symbols", + "gender", + "⚧", + ), + EmojiItem("MALE SIGN", "symbols", "gender", "♂"), + EmojiItem("FEMALE SIGN", "symbols", "gender", "♀"), + EmojiItem("WHITE LARGE SQUARE", "symbols", "geometric", "⬜"), + EmojiItem("BLACK LARGE SQUARE", "symbols", "geometric", "⬛"), + EmojiItem("MEDIUM BLACK CIRCLE", "symbols", "geometric", "⚫"), + EmojiItem("MEDIUM WHITE CIRCLE", "symbols", "geometric", "⚪"), + EmojiItem("BLACK MEDIUM SMALL SQUARE", "symbols", "geometric", "◾"), + EmojiItem("WHITE MEDIUM SMALL SQUARE", "symbols", "geometric", "◽"), + EmojiItem("BLACK MEDIUM SQUARE", "symbols", "geometric", "◼"), + EmojiItem("WHITE MEDIUM SQUARE", "symbols", "geometric", "◻"), + EmojiItem("WHITE SMALL SQUARE", "symbols", "geometric", "▫"), + EmojiItem("BLACK SMALL SQUARE", "symbols", "geometric", "▪"), + EmojiItem("LARGE BROWN SQUARE", "symbols", "geometric", "🟫"), + EmojiItem("LARGE PURPLE SQUARE", "symbols", "geometric", "🟪"), + EmojiItem("LARGE GREEN SQUARE", "symbols", "geometric", "🟩"), + EmojiItem("LARGE YELLOW SQUARE", "symbols", "geometric", "🟨"), + EmojiItem("LARGE ORANGE SQUARE", "symbols", "geometric", "🟧"), + EmojiItem("LARGE BLUE SQUARE", "symbols", "geometric", "🟦"), + EmojiItem("LARGE RED SQUARE", "symbols", "geometric", "🟥"), + EmojiItem("LARGE BROWN CIRCLE", "symbols", "geometric", "🟤"), + EmojiItem("LARGE PURPLE CIRCLE", "symbols", "geometric", "🟣"), + EmojiItem("LARGE GREEN CIRCLE", "symbols", "geometric", "🟢"), + EmojiItem("LARGE YELLOW CIRCLE", "symbols", "geometric", "🟡"), + EmojiItem("LARGE ORANGE CIRCLE", "symbols", "geometric", "🟠"), + EmojiItem("DOWN-POINTING RED TRIANGLE", "symbols", "geometric", "🔻"), + EmojiItem("UP-POINTING RED TRIANGLE", "symbols", "geometric", "🔺"), + EmojiItem("SMALL BLUE DIAMOND", "symbols", "geometric", "🔹"), + EmojiItem("SMALL ORANGE DIAMOND", "symbols", "geometric", "🔸"), + EmojiItem("LARGE BLUE DIAMOND", "symbols", "geometric", "🔷"), + EmojiItem("LARGE ORANGE DIAMOND", "symbols", "geometric", "🔶"), + EmojiItem("LARGE BLUE CIRCLE", "symbols", "geometric", "🔵"), + EmojiItem("LARGE RED CIRCLE", "symbols", "geometric", "🔴"), + EmojiItem("WHITE SQUARE BUTTON", "symbols", "geometric", "🔳"), + EmojiItem("BLACK SQUARE BUTTON", "symbols", "geometric", "🔲"), + EmojiItem("RADIO BUTTON", "symbols", "geometric", "🔘"), + EmojiItem( + "DIAMOND SHAPE WITH A DOT INSIDE", "symbols", "geometric", "💠" + ), + EmojiItem("KEYCAP TEN", "symbols", "keycap", "🔟"), + EmojiItem("PERMANENT PAPER SIGN", "symbols", "math", "♾"), + EmojiItem("HEAVY DIVISION SIGN", "symbols", "math", "➗"), + EmojiItem("HEAVY MINUS SIGN", "symbols", "math", "➖"), + EmojiItem("HEAVY PLUS SIGN", "symbols", "math", "➕"), + EmojiItem("HEAVY MULTIPLICATION X", "symbols", "math", "✖"), + EmojiItem("PART ALTERNATION MARK", "symbols", "other-symbol", "〽"), + EmojiItem("HEAVY LARGE CIRCLE", "symbols", "other-symbol", "⭕"), + EmojiItem("DOUBLE CURLY LOOP", "symbols", "other-symbol", "➿"), + EmojiItem("CURLY LOOP", "symbols", "other-symbol", "➰"), + EmojiItem( + "NEGATIVE SQUARED CROSS MARK", "symbols", "other-symbol", "❎" + ), + EmojiItem("CROSS MARK", "symbols", "other-symbol", "❌"), + EmojiItem("FLEUR-DE-LIS", "symbols", "other-symbol", "⚜"), + EmojiItem( + "BLACK UNIVERSAL RECYCLING SYMBOL", "symbols", "other-symbol", "♻" + ), + EmojiItem("REGISTERED SIGN", "symbols", "other-symbol", "®"), + EmojiItem("COPYRIGHT SIGN", "symbols", "other-symbol", "©"), + EmojiItem("SPARKLE", "symbols", "other-symbol", "❇"), + EmojiItem("EIGHT POINTED BLACK STAR", "symbols", "other-symbol", "✴"), + EmojiItem("EIGHT SPOKED ASTERISK", "symbols", "other-symbol", "✳"), + EmojiItem("HEAVY CHECK MARK", "symbols", "other-symbol", "✔"), + EmojiItem("WHITE HEAVY CHECK MARK", "symbols", "other-symbol", "✅"), + EmojiItem("STAFF OF AESCULAPIUS", "symbols", "other-symbol", "⚕"), + EmojiItem("BALLOT BOX WITH CHECK", "symbols", "other-symbol", "☑"), + EmojiItem("TRADE MARK SIGN", "symbols", "other-symbol", "™"), + EmojiItem("TRIDENT EMBLEM", "symbols", "other-symbol", "🔱"), + EmojiItem( + "JAPANESE SYMBOL FOR BEGINNER", "symbols", "other-symbol", "🔰" + ), + EmojiItem("NAME BADGE", "symbols", "other-symbol", "📛"), + EmojiItem("DOUBLE EXCLAMATION MARK", "symbols", "punctuation", "‼"), + EmojiItem("WAVY DASH", "symbols", "punctuation", "〰"), + EmojiItem( + "HEAVY EXCLAMATION MARK SYMBOL", "symbols", "punctuation", "❗" + ), + EmojiItem( + "WHITE EXCLAMATION MARK ORNAMENT", "symbols", "punctuation", "❕" + ), + EmojiItem( + "WHITE QUESTION MARK ORNAMENT", "symbols", "punctuation", "❔" + ), + EmojiItem( + "BLACK QUESTION MARK ORNAMENT", "symbols", "punctuation", "❓" + ), + EmojiItem("EXCLAMATION QUESTION MARK", "symbols", "punctuation", "⁉"), + EmojiItem("LATIN CROSS", "symbols", "religion", "✝"), + EmojiItem("ATOM SYMBOL", "symbols", "religion", "⚛"), + EmojiItem("YIN YANG", "symbols", "religion", "☯"), + EmojiItem("PEACE SYMBOL", "symbols", "religion", "☮"), + EmojiItem("STAR AND CRESCENT", "symbols", "religion", "☪"), + EmojiItem("STAR OF DAVID", "symbols", "religion", "✡"), + EmojiItem("WHEEL OF DHARMA", "symbols", "religion", "☸"), + EmojiItem("ORTHODOX CROSS", "symbols", "religion", "☦"), + EmojiItem("PLACE OF WORSHIP", "symbols", "religion", "🛐"), + EmojiItem("MENORAH WITH NINE BRANCHES", "symbols", "religion", "🕎"), + EmojiItem("OM SYMBOL", "symbols", "religion", "🕉"), + EmojiItem( + "SIX POINTED STAR WITH MIDDLE DOT", "symbols", "religion", "🔯" + ), + EmojiItem("WHEELCHAIR SYMBOL", "symbols", "transport-sign", "♿"), + EmojiItem("LEFT LUGGAGE", "symbols", "transport-sign", "🛅"), + EmojiItem("BAGGAGE CLAIM", "symbols", "transport-sign", "🛄"), + EmojiItem("CUSTOMS", "symbols", "transport-sign", "🛃"), + EmojiItem("PASSPORT CONTROL", "symbols", "transport-sign", "🛂"), + EmojiItem("WATER CLOSET", "symbols", "transport-sign", "🚾"), + EmojiItem("BABY SYMBOL", "symbols", "transport-sign", "🚼"), + EmojiItem("RESTROOM", "symbols", "transport-sign", "🚻"), + EmojiItem("WOMENS SYMBOL", "symbols", "transport-sign", "🚺"), + EmojiItem("MENS SYMBOL", "symbols", "transport-sign", "🚹"), + EmojiItem("POTABLE WATER SYMBOL", "symbols", "transport-sign", "🚰"), + EmojiItem( + "PUT LITTER IN ITS PLACE SYMBOL", "symbols", "transport-sign", "🚮" + ), + EmojiItem( + "AUTOMATED TELLER MACHINE", "symbols", "transport-sign", "🏧" + ), + EmojiItem("NO ENTRY", "symbols", "warning", "⛔"), + EmojiItem("WARNING SIGN", "symbols", "warning", "⚠"), + EmojiItem("BIOHAZARD SIGN", "symbols", "warning", "☣"), + EmojiItem("RADIOACTIVE SIGN", "symbols", "warning", "☢"), + EmojiItem("CHILDREN CROSSING", "symbols", "warning", "🚸"), + EmojiItem("NO PEDESTRIANS", "symbols", "warning", "🚷"), + EmojiItem("NO BICYCLES", "symbols", "warning", "🚳"), + EmojiItem("NON-POTABLE WATER SYMBOL", "symbols", "warning", "🚱"), + EmojiItem("DO NOT LITTER SYMBOL", "symbols", "warning", "🚯"), + EmojiItem("NO SMOKING SYMBOL", "symbols", "warning", "🚭"), + EmojiItem("NO ENTRY SIGN", "symbols", "warning", "🚫"), + EmojiItem("NO ONE UNDER EIGHTEEN SYMBOL", "symbols", "warning", "🔞"), + EmojiItem("NO MOBILE PHONES", "symbols", "warning", "📵"), + EmojiItem("OPHIUCHUS", "symbols", "zodiac", "⛎"), + EmojiItem("SCORPIUS", "symbols", "zodiac", "♏"), + EmojiItem("LIBRA", "symbols", "zodiac", "♎"), + EmojiItem("VIRGO", "symbols", "zodiac", "♍"), + EmojiItem("LEO", "symbols", "zodiac", "♌"), + EmojiItem("CANCER", "symbols", "zodiac", "♋"), + EmojiItem("GEMINI", "symbols", "zodiac", "♊"), + EmojiItem("PISCES", "symbols", "zodiac", "♓"), + EmojiItem("AQUARIUS", "symbols", "zodiac", "♒"), + EmojiItem("CAPRICORN", "symbols", "zodiac", "♑"), + EmojiItem("SAGITTARIUS", "symbols", "zodiac", "♐"), + EmojiItem("TAURUS", "symbols", "zodiac", "♉"), + EmojiItem("ARIES", "symbols", "zodiac", "♈"), + EmojiItem("LUGGAGE", "travel-places", "hotel", "🧳"), + EmojiItem("BELLHOP BELL", "travel-places", "hotel", "🛎"), + EmojiItem("WOOD", "travel-places", "place-building", "🪵"), + EmojiItem("ROCK", "travel-places", "place-building", "🪨"), + EmojiItem("BRICK", "travel-places", "place-building", "🧱"), + EmojiItem("HUT", "travel-places", "place-building", "🛖"), + EmojiItem("STATUE OF LIBERTY", "travel-places", "place-building", "🗽"), + EmojiItem("TOKYO TOWER", "travel-places", "place-building", "🗼"), + EmojiItem("WEDDING", "travel-places", "place-building", "💒"), + EmojiItem("EUROPEAN CASTLE", "travel-places", "place-building", "🏰"), + EmojiItem("JAPANESE CASTLE", "travel-places", "place-building", "🏯"), + EmojiItem("FACTORY", "travel-places", "place-building", "🏭"), + EmojiItem("DEPARTMENT STORE", "travel-places", "place-building", "🏬"), + EmojiItem("SCHOOL", "travel-places", "place-building", "🏫"), + EmojiItem("CONVENIENCE STORE", "travel-places", "place-building", "🏪"), + EmojiItem("LOVE HOTEL", "travel-places", "place-building", "🏩"), + EmojiItem("HOTEL", "travel-places", "place-building", "🏨"), + EmojiItem("BANK", "travel-places", "place-building", "🏦"), + EmojiItem("HOSPITAL", "travel-places", "place-building", "🏥"), + EmojiItem( + "EUROPEAN POST OFFICE", "travel-places", "place-building", "🏤" + ), + EmojiItem( + "JAPANESE POST OFFICE", "travel-places", "place-building", "🏣" + ), + EmojiItem("OFFICE BUILDING", "travel-places", "place-building", "🏢"), + EmojiItem("HOUSE WITH GARDEN", "travel-places", "place-building", "🏡"), + EmojiItem("HOUSE BUILDING", "travel-places", "place-building", "🏠"), + EmojiItem("STADIUM", "travel-places", "place-building", "🏟"), + EmojiItem( + "CLASSICAL BUILDING", "travel-places", "place-building", "🏛" + ), + EmojiItem( + "DERELICT HOUSE BUILDING", "travel-places", "place-building", "🏚" + ), + EmojiItem("HOUSE BUILDINGS", "travel-places", "place-building", "🏘"), + EmojiItem( + "BUILDING CONSTRUCTION", "travel-places", "place-building", "🏗" + ), + EmojiItem("MOUNTAIN", "travel-places", "place-geographic", "⛰"), + EmojiItem("MOUNT FUJI", "travel-places", "place-geographic", "🗻"), + EmojiItem("NATIONAL PARK", "travel-places", "place-geographic", "🏞"), + EmojiItem("DESERT ISLAND", "travel-places", "place-geographic", "🏝"), + EmojiItem("DESERT", "travel-places", "place-geographic", "🏜"), + EmojiItem( + "BEACH WITH UMBRELLA", "travel-places", "place-geographic", "🏖" + ), + EmojiItem("CAMPING", "travel-places", "place-geographic", "🏕"), + EmojiItem( + "SNOW CAPPED MOUNTAIN", "travel-places", "place-geographic", "🏔" + ), + EmojiItem("VOLCANO", "travel-places", "place-geographic", "🌋"), + EmojiItem("COMPASS", "travel-places", "place-map", "🧭"), + EmojiItem("SILHOUETTE OF JAPAN", "travel-places", "place-map", "🗾"), + EmojiItem("WORLD MAP", "travel-places", "place-map", "🗺"), + EmojiItem("GLOBE WITH MERIDIANS", "travel-places", "place-map", "🌐"), + EmojiItem( + "EARTH GLOBE ASIA-AUSTRALIA", "travel-places", "place-map", "🌏" + ), + EmojiItem("EARTH GLOBE AMERICAS", "travel-places", "place-map", "🌎"), + EmojiItem( + "EARTH GLOBE EUROPE-AFRICA", "travel-places", "place-map", "🌍" + ), + EmojiItem("TENT", "travel-places", "place-other", "⛺"), + EmojiItem("FOUNTAIN", "travel-places", "place-other", "⛲"), + EmojiItem("HOT SPRINGS", "travel-places", "place-other", "♨"), + EmojiItem("BARBER POLE", "travel-places", "place-other", "💈"), + EmojiItem("CITYSCAPE", "travel-places", "place-other", "🏙"), + EmojiItem("CIRCUS TENT", "travel-places", "place-other", "🎪"), + EmojiItem("ROLLER COASTER", "travel-places", "place-other", "🎢"), + EmojiItem("FERRIS WHEEL", "travel-places", "place-other", "🎡"), + EmojiItem("CAROUSEL HORSE", "travel-places", "place-other", "🎠"), + EmojiItem("BRIDGE AT NIGHT", "travel-places", "place-other", "🌉"), + EmojiItem( + "SUNSET OVER BUILDINGS", "travel-places", "place-other", "🌇" + ), + EmojiItem("CITYSCAPE AT DUSK", "travel-places", "place-other", "🌆"), + EmojiItem("SUNRISE", "travel-places", "place-other", "🌅"), + EmojiItem( + "SUNRISE OVER MOUNTAINS", "travel-places", "place-other", "🌄" + ), + EmojiItem("NIGHT WITH STARS", "travel-places", "place-other", "🌃"), + EmojiItem("FOGGY", "travel-places", "place-other", "🌁"), + EmojiItem("CHURCH", "travel-places", "place-religious", "⛪"), + EmojiItem("HINDU TEMPLE", "travel-places", "place-religious", "🛕"), + EmojiItem("SYNAGOGUE", "travel-places", "place-religious", "🕍"), + EmojiItem("MOSQUE", "travel-places", "place-religious", "🕌"), + EmojiItem("KAABA", "travel-places", "place-religious", "🕋"), + EmojiItem("WHITE MEDIUM STAR", "travel-places", "sky & weather", "⭐"), + EmojiItem("UMBRELLA ON GROUND", "travel-places", "sky & weather", "⛱"), + EmojiItem( + "THUNDER CLOUD AND RAIN", "travel-places", "sky & weather", "⛈" + ), + EmojiItem("SUN BEHIND CLOUD", "travel-places", "sky & weather", "⛅"), + EmojiItem( + "SNOWMAN WITHOUT SNOW", "travel-places", "sky & weather", "⛄" + ), + EmojiItem("HIGH VOLTAGE SIGN", "travel-places", "sky & weather", "⚡"), + EmojiItem("SNOWFLAKE", "travel-places", "sky & weather", "❄"), + EmojiItem( + "UMBRELLA WITH RAIN DROPS", "travel-places", "sky & weather", "☔" + ), + EmojiItem("COMET", "travel-places", "sky & weather", "☄"), + EmojiItem("SNOWMAN", "travel-places", "sky & weather", "☃"), + EmojiItem("UMBRELLA", "travel-places", "sky & weather", "☂"), + EmojiItem("CLOUD", "travel-places", "sky & weather", "☁"), + EmojiItem( + "BLACK SUN WITH RAYS", "travel-places", "sky & weather", "☀" + ), + EmojiItem("RINGED PLANET", "travel-places", "sky & weather", "🪐"), + EmojiItem("FIRE", "travel-places", "sky & weather", "🔥"), + EmojiItem("DROPLET", "travel-places", "sky & weather", "💧"), + EmojiItem("WIND BLOWING FACE", "travel-places", "sky & weather", "🌬"), + EmojiItem("FOG", "travel-places", "sky & weather", "🌫"), + EmojiItem("CLOUD WITH TORNADO", "travel-places", "sky & weather", "🌪"), + EmojiItem( + "CLOUD WITH LIGHTNING", "travel-places", "sky & weather", "🌩" + ), + EmojiItem("CLOUD WITH SNOW", "travel-places", "sky & weather", "🌨"), + EmojiItem("CLOUD WITH RAIN", "travel-places", "sky & weather", "🌧"), + EmojiItem( + "WHITE SUN BEHIND CLOUD WITH RAIN", + "travel-places", + "sky & weather", + "🌦", + ), + EmojiItem( + "WHITE SUN BEHIND CLOUD", "travel-places", "sky & weather", "🌥" + ), + EmojiItem( + "WHITE SUN WITH SMALL CLOUD", "travel-places", "sky & weather", "🌤" + ), + EmojiItem("THERMOMETER", "travel-places", "sky & weather", "🌡"), + EmojiItem("SHOOTING STAR", "travel-places", "sky & weather", "🌠"), + EmojiItem("GLOWING STAR", "travel-places", "sky & weather", "🌟"), + EmojiItem("SUN WITH FACE", "travel-places", "sky & weather", "🌞"), + EmojiItem( + "FULL MOON WITH FACE", "travel-places", "sky & weather", "🌝" + ), + EmojiItem( + "LAST QUARTER MOON WITH FACE", + "travel-places", + "sky & weather", + "🌜", + ), + EmojiItem( + "FIRST QUARTER MOON WITH FACE", + "travel-places", + "sky & weather", + "🌛", + ), + EmojiItem("NEW MOON WITH FACE", "travel-places", "sky & weather", "🌚"), + EmojiItem("CRESCENT MOON", "travel-places", "sky & weather", "🌙"), + EmojiItem( + "WANING CRESCENT MOON SYMBOL", + "travel-places", + "sky & weather", + "🌘", + ), + EmojiItem( + "LAST QUARTER MOON SYMBOL", "travel-places", "sky & weather", "🌗" + ), + EmojiItem( + "WANING GIBBOUS MOON SYMBOL", "travel-places", "sky & weather", "🌖" + ), + EmojiItem("FULL MOON SYMBOL", "travel-places", "sky & weather", "🌕"), + EmojiItem( + "WAXING GIBBOUS MOON SYMBOL", "travel-places", "sky & weather", "🌔" + ), + EmojiItem( + "FIRST QUARTER MOON SYMBOL", "travel-places", "sky & weather", "🌓" + ), + EmojiItem( + "WAXING CRESCENT MOON SYMBOL", + "travel-places", + "sky & weather", + "🌒", + ), + EmojiItem("NEW MOON SYMBOL", "travel-places", "sky & weather", "🌑"), + EmojiItem("MILKY WAY", "travel-places", "sky & weather", "🌌"), + EmojiItem("WATER WAVE", "travel-places", "sky & weather", "🌊"), + EmojiItem("RAINBOW", "travel-places", "sky & weather", "🌈"), + EmojiItem("CLOSED UMBRELLA", "travel-places", "sky & weather", "🌂"), + EmojiItem("CYCLONE", "travel-places", "sky & weather", "🌀"), + EmojiItem("HOURGLASS WITH FLOWING SAND", "travel-places", "time", "⏳"), + EmojiItem("TIMER CLOCK", "travel-places", "time", "⏲"), + EmojiItem("STOPWATCH", "travel-places", "time", "⏱"), + EmojiItem("ALARM CLOCK", "travel-places", "time", "⏰"), + EmojiItem("HOURGLASS", "travel-places", "time", "⌛"), + EmojiItem("WATCH", "travel-places", "time", "⌚"), + EmojiItem("MANTELPIECE CLOCK", "travel-places", "time", "🕰"), + EmojiItem("CLOCK FACE TWELVE-THIRTY", "travel-places", "time", "🕧"), + EmojiItem("CLOCK FACE ELEVEN-THIRTY", "travel-places", "time", "🕦"), + EmojiItem("CLOCK FACE TEN-THIRTY", "travel-places", "time", "🕥"), + EmojiItem("CLOCK FACE NINE-THIRTY", "travel-places", "time", "🕤"), + EmojiItem("CLOCK FACE EIGHT-THIRTY", "travel-places", "time", "🕣"), + EmojiItem("CLOCK FACE SEVEN-THIRTY", "travel-places", "time", "🕢"), + EmojiItem("CLOCK FACE SIX-THIRTY", "travel-places", "time", "🕡"), + EmojiItem("CLOCK FACE FIVE-THIRTY", "travel-places", "time", "🕠"), + EmojiItem("CLOCK FACE FOUR-THIRTY", "travel-places", "time", "🕟"), + EmojiItem("CLOCK FACE THREE-THIRTY", "travel-places", "time", "🕞"), + EmojiItem("CLOCK FACE TWO-THIRTY", "travel-places", "time", "🕝"), + EmojiItem("CLOCK FACE ONE-THIRTY", "travel-places", "time", "🕜"), + EmojiItem("CLOCK FACE TWELVE OCLOCK", "travel-places", "time", "🕛"), + EmojiItem("CLOCK FACE ELEVEN OCLOCK", "travel-places", "time", "🕚"), + EmojiItem("CLOCK FACE TEN OCLOCK", "travel-places", "time", "🕙"), + EmojiItem("CLOCK FACE NINE OCLOCK", "travel-places", "time", "🕘"), + EmojiItem("CLOCK FACE EIGHT OCLOCK", "travel-places", "time", "🕗"), + EmojiItem("CLOCK FACE SEVEN OCLOCK", "travel-places", "time", "🕖"), + EmojiItem("CLOCK FACE SIX OCLOCK", "travel-places", "time", "🕕"), + EmojiItem("CLOCK FACE FIVE OCLOCK", "travel-places", "time", "🕔"), + EmojiItem("CLOCK FACE FOUR OCLOCK", "travel-places", "time", "🕓"), + EmojiItem("CLOCK FACE THREE OCLOCK", "travel-places", "time", "🕒"), + EmojiItem("CLOCK FACE TWO OCLOCK", "travel-places", "time", "🕑"), + EmojiItem("CLOCK FACE ONE OCLOCK", "travel-places", "time", "🕐"), + EmojiItem("AIRPLANE", "travel-places", "transport-air", "✈"), + EmojiItem("PARACHUTE", "travel-places", "transport-air", "🪂"), + EmojiItem("FLYING SAUCER", "travel-places", "transport-air", "🛸"), + EmojiItem("SATELLITE", "travel-places", "transport-air", "🛰"), + EmojiItem("AIRPLANE ARRIVING", "travel-places", "transport-air", "🛬"), + EmojiItem("AIRPLANE DEPARTURE", "travel-places", "transport-air", "🛫"), + EmojiItem("SMALL AIRPLANE", "travel-places", "transport-air", "🛩"), + EmojiItem("AERIAL TRAMWAY", "travel-places", "transport-air", "🚡"), + EmojiItem("MOUNTAIN CABLEWAY", "travel-places", "transport-air", "🚠"), + EmojiItem("SUSPENSION RAILWAY", "travel-places", "transport-air", "🚟"), + EmojiItem("HELICOPTER", "travel-places", "transport-air", "🚁"), + EmojiItem("ROCKET", "travel-places", "transport-air", "🚀"), + EmojiItem("SEAT", "travel-places", "transport-air", "💺"), + EmojiItem("FUEL PUMP", "travel-places", "transport-ground", "⛽"), + EmojiItem( + "MANUAL WHEELCHAIR", "travel-places", "transport-ground", "🦽" + ), + EmojiItem( + "MOTORIZED WHEELCHAIR", "travel-places", "transport-ground", "🦼" + ), + EmojiItem("ROLLER SKATE", "travel-places", "transport-ground", "🛼"), + EmojiItem("PICKUP TRUCK", "travel-places", "transport-ground", "🛻"), + EmojiItem("AUTO RICKSHAW", "travel-places", "transport-ground", "🛺"), + EmojiItem("SKATEBOARD", "travel-places", "transport-ground", "🛹"), + EmojiItem("MOTOR SCOOTER", "travel-places", "transport-ground", "🛵"), + EmojiItem("SCOOTER", "travel-places", "transport-ground", "🛴"), + EmojiItem("RAILWAY TRACK", "travel-places", "transport-ground", "🛤"), + EmojiItem("MOTORWAY", "travel-places", "transport-ground", "🛣"), + EmojiItem("OIL DRUM", "travel-places", "transport-ground", "🛢"), + EmojiItem("OCTAGONAL SIGN", "travel-places", "transport-ground", "🛑"), + EmojiItem("BICYCLE", "travel-places", "transport-ground", "🚲"), + EmojiItem( + "POLICE CARS REVOLVING LIGHT", + "travel-places", + "transport-ground", + "🚨", + ), + EmojiItem( + "CONSTRUCTION SIGN", "travel-places", "transport-ground", "🚧" + ), + EmojiItem( + "VERTICAL TRAFFIC LIGHT", "travel-places", "transport-ground", "🚦" + ), + EmojiItem( + "HORIZONTAL TRAFFIC LIGHT", + "travel-places", + "transport-ground", + "🚥", + ), + EmojiItem( + "MOUNTAIN RAILWAY", "travel-places", "transport-ground", "🚞" + ), + EmojiItem("MONORAIL", "travel-places", "transport-ground", "🚝"), + EmojiItem("TRACTOR", "travel-places", "transport-ground", "🚜"), + EmojiItem( + "ARTICULATED LORRY", "travel-places", "transport-ground", "🚛" + ), + EmojiItem("DELIVERY TRUCK", "travel-places", "transport-ground", "🚚"), + EmojiItem( + "RECREATIONAL VEHICLE", "travel-places", "transport-ground", "🚙" + ), + EmojiItem( + "ONCOMING AUTOMOBILE", "travel-places", "transport-ground", "🚘" + ), + EmojiItem("AUTOMOBILE", "travel-places", "transport-ground", "🚗"), + EmojiItem("ONCOMING TAXI", "travel-places", "transport-ground", "🚖"), + EmojiItem("TAXI", "travel-places", "transport-ground", "🚕"), + EmojiItem( + "ONCOMING POLICE CAR", "travel-places", "transport-ground", "🚔" + ), + EmojiItem("POLICE CAR", "travel-places", "transport-ground", "🚓"), + EmojiItem("FIRE ENGINE", "travel-places", "transport-ground", "🚒"), + EmojiItem("AMBULANCE", "travel-places", "transport-ground", "🚑"), + EmojiItem("MINIBUS", "travel-places", "transport-ground", "🚐"), + EmojiItem("BUS STOP", "travel-places", "transport-ground", "🚏"), + EmojiItem("TROLLEYBUS", "travel-places", "transport-ground", "🚎"), + EmojiItem("ONCOMING BUS", "travel-places", "transport-ground", "🚍"), + EmojiItem("BUS", "travel-places", "transport-ground", "🚌"), + EmojiItem("TRAM CAR", "travel-places", "transport-ground", "🚋"), + EmojiItem("TRAM", "travel-places", "transport-ground", "🚊"), + EmojiItem("STATION", "travel-places", "transport-ground", "🚉"), + EmojiItem("LIGHT RAIL", "travel-places", "transport-ground", "🚈"), + EmojiItem("METRO", "travel-places", "transport-ground", "🚇"), + EmojiItem("TRAIN", "travel-places", "transport-ground", "🚆"), + EmojiItem( + "HIGH-SPEED TRAIN WITH BULLET NOSE", + "travel-places", + "transport-ground", + "🚅", + ), + EmojiItem( + "HIGH-SPEED TRAIN", "travel-places", "transport-ground", "🚄" + ), + EmojiItem("RAILWAY CAR", "travel-places", "transport-ground", "🚃"), + EmojiItem( + "STEAM LOCOMOTIVE", "travel-places", "transport-ground", "🚂" + ), + EmojiItem("RACING CAR", "travel-places", "transport-ground", "🏎"), + EmojiItem( + "RACING MOTORCYCLE", "travel-places", "transport-ground", "🏍" + ), + EmojiItem("SAILBOAT", "travel-places", "transport-water", "⛵"), + EmojiItem("FERRY", "travel-places", "transport-water", "⛴"), + EmojiItem("ANCHOR", "travel-places", "transport-water", "⚓"), + EmojiItem("CANOE", "travel-places", "transport-water", "🛶"), + EmojiItem("PASSENGER SHIP", "travel-places", "transport-water", "🛳"), + EmojiItem("MOTOR BOAT", "travel-places", "transport-water", "🛥"), + EmojiItem("SPEEDBOAT", "travel-places", "transport-water", "🚤"), + EmojiItem("SHIP", "travel-places", "transport-water", "🚢"), + EmojiItem("WEARY CAT FACE", "smiley-emotion", "cat-face", "🙀"), + EmojiItem("CRYING CAT FACE", "smiley-emotion", "cat-face", "😿"), + EmojiItem("POUTING CAT FACE", "smiley-emotion", "cat-face", "😾"), + EmojiItem( + "KISSING CAT FACE WITH CLOSED EYES", + "smiley-emotion", + "cat-face", + "😽", + ), + EmojiItem( + "CAT FACE WITH WRY SMILE", "smiley-emotion", "cat-face", "😼" + ), + EmojiItem( + "SMILING CAT FACE WITH HEART-SHAPED EYES", + "smiley-emotion", + "cat-face", + "😻", + ), + EmojiItem( + "SMILING CAT FACE WITH OPEN MOUTH", + "smiley-emotion", + "cat-face", + "😺", + ), + EmojiItem( + "CAT FACE WITH TEARS OF JOY", "smiley-emotion", "cat-face", "😹" + ), + EmojiItem( + "GRINNING CAT FACE WITH SMILING EYES", + "smiley-emotion", + "cat-face", + "😸", + ), + EmojiItem("HEAVY BLACK HEART", "smiley-emotion", "emotion", "❤"), + EmojiItem( + "HEAVY HEART EXCLAMATION MARK ORNAMENT", + "smiley-emotion", + "emotion", + "❣", + ), + EmojiItem("ORANGE HEART", "smiley-emotion", "emotion", "🧡"), + EmojiItem("BROWN HEART", "smiley-emotion", "emotion", "🤎"), + EmojiItem("WHITE HEART", "smiley-emotion", "emotion", "🤍"), + EmojiItem("RIGHT ANGER BUBBLE", "smiley-emotion", "emotion", "🗯"), + EmojiItem("LEFT SPEECH BUBBLE", "smiley-emotion", "emotion", "🗨"), + EmojiItem("BLACK HEART", "smiley-emotion", "emotion", "🖤"), + EmojiItem("HOLE", "smiley-emotion", "emotion", "🕳"), + EmojiItem("HUNDRED POINTS SYMBOL", "smiley-emotion", "emotion", "💯"), + EmojiItem("THOUGHT BALLOON", "smiley-emotion", "emotion", "💭"), + EmojiItem("SPEECH BALLOON", "smiley-emotion", "emotion", "💬"), + EmojiItem("DIZZY SYMBOL", "smiley-emotion", "emotion", "💫"), + EmojiItem("DASH SYMBOL", "smiley-emotion", "emotion", "💨"), + EmojiItem("SPLASHING SWEAT SYMBOL", "smiley-emotion", "emotion", "💦"), + EmojiItem("COLLISION SYMBOL", "smiley-emotion", "emotion", "💥"), + EmojiItem("SLEEPING SYMBOL", "smiley-emotion", "emotion", "💤"), + EmojiItem("BOMB", "smiley-emotion", "emotion", "💣"), + EmojiItem("ANGER SYMBOL", "smiley-emotion", "emotion", "💢"), + EmojiItem("HEART DECORATION", "smiley-emotion", "emotion", "💟"), + EmojiItem("REVOLVING HEARTS", "smiley-emotion", "emotion", "💞"), + EmojiItem("HEART WITH RIBBON", "smiley-emotion", "emotion", "💝"), + EmojiItem("PURPLE HEART", "smiley-emotion", "emotion", "💜"), + EmojiItem("YELLOW HEART", "smiley-emotion", "emotion", "💛"), + EmojiItem("GREEN HEART", "smiley-emotion", "emotion", "💚"), + EmojiItem("BLUE HEART", "smiley-emotion", "emotion", "💙"), + EmojiItem("HEART WITH ARROW", "smiley-emotion", "emotion", "💘"), + EmojiItem("GROWING HEART", "smiley-emotion", "emotion", "💗"), + EmojiItem("SPARKLING HEART", "smiley-emotion", "emotion", "💖"), + EmojiItem("TWO HEARTS", "smiley-emotion", "emotion", "💕"), + EmojiItem("BROKEN HEART", "smiley-emotion", "emotion", "💔"), + EmojiItem("BEATING HEART", "smiley-emotion", "emotion", "💓"), + EmojiItem("LOVE LETTER", "smiley-emotion", "emotion", "💌"), + EmojiItem("KISS MARK", "smiley-emotion", "emotion", "💋"), + EmojiItem( + "WHITE SMILING FACE", "smiley-emotion", "face-affection", "☺" + ), + EmojiItem( + "SMILING FACE WITH TEAR", "smiley-emotion", "face-affection", "🥲" + ), + EmojiItem( + "SMILING FACE WITH SMILING EYES AND THREE HEARTS", + "smiley-emotion", + "face-affection", + "🥰", + ), + EmojiItem( + "GRINNING FACE WITH STAR EYES", + "smiley-emotion", + "face-affection", + "🤩", + ), + EmojiItem( + "KISSING FACE WITH CLOSED EYES", + "smiley-emotion", + "face-affection", + "😚", + ), + EmojiItem( + "KISSING FACE WITH SMILING EYES", + "smiley-emotion", + "face-affection", + "😙", + ), + EmojiItem( + "FACE THROWING A KISS", "smiley-emotion", "face-affection", "😘" + ), + EmojiItem("KISSING FACE", "smiley-emotion", "face-affection", "😗"), + EmojiItem( + "SMILING FACE WITH HEART-SHAPED EYES", + "smiley-emotion", + "face-affection", + "😍", + ), + EmojiItem( + "WHITE FROWNING FACE", "smiley-emotion", "face-concerned", "☹" + ), + EmojiItem( + "FACE WITH PLEADING EYES", "smiley-emotion", "face-concerned", "🥺" + ), + EmojiItem("YAWNING FACE", "smiley-emotion", "face-concerned", "🥱"), + EmojiItem( + "SLIGHTLY FROWNING FACE", "smiley-emotion", "face-concerned", "🙁" + ), + EmojiItem("FLUSHED FACE", "smiley-emotion", "face-concerned", "😳"), + EmojiItem("ASTONISHED FACE", "smiley-emotion", "face-concerned", "😲"), + EmojiItem( + "FACE SCREAMING IN FEAR", "smiley-emotion", "face-concerned", "😱" + ), + EmojiItem( + "FACE WITH OPEN MOUTH AND COLD SWEAT", + "smiley-emotion", + "face-concerned", + "😰", + ), + EmojiItem("HUSHED FACE", "smiley-emotion", "face-concerned", "😯"), + EmojiItem( + "FACE WITH OPEN MOUTH", "smiley-emotion", "face-concerned", "😮" + ), + EmojiItem( + "LOUDLY CRYING FACE", "smiley-emotion", "face-concerned", "😭" + ), + EmojiItem("TIRED FACE", "smiley-emotion", "face-concerned", "😫"), + EmojiItem("WEARY FACE", "smiley-emotion", "face-concerned", "😩"), + EmojiItem("FEARFUL FACE", "smiley-emotion", "face-concerned", "😨"), + EmojiItem("ANGUISHED FACE", "smiley-emotion", "face-concerned", "😧"), + EmojiItem( + "FROWNING FACE WITH OPEN MOUTH", + "smiley-emotion", + "face-concerned", + "😦", + ), + EmojiItem( + "DISAPPOINTED BUT RELIEVED FACE", + "smiley-emotion", + "face-concerned", + "😥", + ), + EmojiItem("PERSEVERING FACE", "smiley-emotion", "face-concerned", "😣"), + EmojiItem("CRYING FACE", "smiley-emotion", "face-concerned", "😢"), + EmojiItem("WORRIED FACE", "smiley-emotion", "face-concerned", "😟"), + EmojiItem( + "DISAPPOINTED FACE", "smiley-emotion", "face-concerned", "😞" + ), + EmojiItem("CONFOUNDED FACE", "smiley-emotion", "face-concerned", "😖"), + EmojiItem("CONFUSED FACE", "smiley-emotion", "face-concerned", "😕"), + EmojiItem( + "FACE WITH COLD SWEAT", "smiley-emotion", "face-concerned", "😓" + ), + EmojiItem("CLOWN FACE", "smiley-emotion", "face-costume", "🤡"), + EmojiItem("ROBOT FACE", "smiley-emotion", "face-costume", "🤖"), + EmojiItem("PILE OF POO", "smiley-emotion", "face-costume", "💩"), + EmojiItem("ALIEN MONSTER", "smiley-emotion", "face-costume", "👾"), + EmojiItem( + "EXTRATERRESTRIAL ALIEN", "smiley-emotion", "face-costume", "👽" + ), + EmojiItem("GHOST", "smiley-emotion", "face-costume", "👻"), + EmojiItem("JAPANESE GOBLIN", "smiley-emotion", "face-costume", "👺"), + EmojiItem("JAPANESE OGRE", "smiley-emotion", "face-costume", "👹"), + EmojiItem("FACE WITH MONOCLE", "smiley-emotion", "face-glasses", "🧐"), + EmojiItem("NERD FACE", "smiley-emotion", "face-glasses", "🤓"), + EmojiItem( + "SMILING FACE WITH SUNGLASSES", + "smiley-emotion", + "face-glasses", + "😎", + ), + EmojiItem( + "SMILING FACE WITH SMILING EYES AND HAND COVERING MOUTH", + "smiley-emotion", + "face-hand", + "🤭", + ), + EmojiItem( + "FACE WITH FINGER COVERING CLOSED LIPS", + "smiley-emotion", + "face-hand", + "🤫", + ), + EmojiItem("HUGGING FACE", "smiley-emotion", "face-hand", "🤗"), + EmojiItem("THINKING FACE", "smiley-emotion", "face-hand", "🤔"), + EmojiItem("DISGUISED FACE", "smiley-emotion", "face-hat", "🥸"), + EmojiItem( + "FACE WITH PARTY HORN AND PARTY HAT", + "smiley-emotion", + "face-hat", + "🥳", + ), + EmojiItem("FACE WITH COWBOY HAT", "smiley-emotion", "face-hat", "🤠"), + EmojiItem( + "SKULL AND CROSSBONES", "smiley-emotion", "face-negative", "☠" + ), + EmojiItem( + "SERIOUS FACE WITH SYMBOLS COVERING MOUTH", + "smiley-emotion", + "face-negative", + "🤬", + ), + EmojiItem( + "FACE WITH LOOK OF TRIUMPH", "smiley-emotion", "face-negative", "😤" + ), + EmojiItem("POUTING FACE", "smiley-emotion", "face-negative", "😡"), + EmojiItem("ANGRY FACE", "smiley-emotion", "face-negative", "😠"), + EmojiItem( + "SMILING FACE WITH HORNS", "smiley-emotion", "face-negative", "😈" + ), + EmojiItem("SKULL", "smiley-emotion", "face-negative", "💀"), + EmojiItem("IMP", "smiley-emotion", "face-negative", "👿"), + EmojiItem( + "FACE WITH ONE EYEBROW RAISED", + "smiley-emotion", + "face-neutral-skeptical", + "🤨", + ), + EmojiItem( + "LYING FACE", "smiley-emotion", "face-neutral-skeptical", "🤥" + ), + EmojiItem( + "ZIPPER-MOUTH FACE", + "smiley-emotion", + "face-neutral-skeptical", + "🤐", + ), + EmojiItem( + "FACE WITH ROLLING EYES", + "smiley-emotion", + "face-neutral-skeptical", + "🙄", + ), + EmojiItem( + "FACE WITHOUT MOUTH", + "smiley-emotion", + "face-neutral-skeptical", + "😶", + ), + EmojiItem( + "GRIMACING FACE", "smiley-emotion", "face-neutral-skeptical", "😬" + ), + EmojiItem( + "UNAMUSED FACE", "smiley-emotion", "face-neutral-skeptical", "😒" + ), + EmojiItem( + "EXPRESSIONLESS FACE", + "smiley-emotion", + "face-neutral-skeptical", + "😑", + ), + EmojiItem( + "NEUTRAL FACE", "smiley-emotion", "face-neutral-skeptical", "😐" + ), + EmojiItem( + "SMIRKING FACE", "smiley-emotion", "face-neutral-skeptical", "😏" + ), + EmojiItem("DROOLING FACE", "smiley-emotion", "face-sleepy", "🤤"), + EmojiItem("SLEEPING FACE", "smiley-emotion", "face-sleepy", "😴"), + EmojiItem("SLEEPY FACE", "smiley-emotion", "face-sleepy", "😪"), + EmojiItem("PENSIVE FACE", "smiley-emotion", "face-sleepy", "😔"), + EmojiItem("RELIEVED FACE", "smiley-emotion", "face-sleepy", "😌"), + EmojiItem( + "ROLLING ON THE FLOOR LAUGHING", + "smiley-emotion", + "face-smiling", + "🤣", + ), + EmojiItem("UPSIDE-DOWN FACE", "smiley-emotion", "face-smiling", "🙃"), + EmojiItem( + "SLIGHTLY SMILING FACE", "smiley-emotion", "face-smiling", "🙂" + ), + EmojiItem( + "SMILING FACE WITH SMILING EYES", + "smiley-emotion", + "face-smiling", + "😊", + ), + EmojiItem("WINKING FACE", "smiley-emotion", "face-smiling", "😉"), + EmojiItem( + "SMILING FACE WITH HALO", "smiley-emotion", "face-smiling", "😇" + ), + EmojiItem( + "SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES", + "smiley-emotion", + "face-smiling", + "😆", + ), + EmojiItem( + "SMILING FACE WITH OPEN MOUTH AND COLD SWEAT", + "smiley-emotion", + "face-smiling", + "😅", + ), + EmojiItem( + "SMILING FACE WITH OPEN MOUTH AND SMILING EYES", + "smiley-emotion", + "face-smiling", + "😄", + ), + EmojiItem( + "SMILING FACE WITH OPEN MOUTH", + "smiley-emotion", + "face-smiling", + "😃", + ), + EmojiItem( + "FACE WITH TEARS OF JOY", "smiley-emotion", "face-smiling", "😂" + ), + EmojiItem( + "GRINNING FACE WITH SMILING EYES", + "smiley-emotion", + "face-smiling", + "😁", + ), + EmojiItem("GRINNING FACE", "smiley-emotion", "face-smiling", "😀"), + EmojiItem( + "GRINNING FACE WITH ONE LARGE AND ONE SMALL EYE", + "smiley-emotion", + "face-tongue", + "🤪", + ), + EmojiItem("MONEY-MOUTH FACE", "smiley-emotion", "face-tongue", "🤑"), + EmojiItem( + "FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES", + "smiley-emotion", + "face-tongue", + "😝", + ), + EmojiItem( + "FACE WITH STUCK-OUT TONGUE AND WINKING EYE", + "smiley-emotion", + "face-tongue", + "😜", + ), + EmojiItem( + "FACE WITH STUCK-OUT TONGUE", "smiley-emotion", "face-tongue", "😛" + ), + EmojiItem( + "FACE SAVOURING DELICIOUS FOOD", + "smiley-emotion", + "face-tongue", + "😋", + ), + EmojiItem("FREEZING FACE", "smiley-emotion", "face-unwell", "🥶"), + EmojiItem("OVERHEATED FACE", "smiley-emotion", "face-unwell", "🥵"), + EmojiItem( + "FACE WITH UNEVEN EYES AND WAVY MOUTH", + "smiley-emotion", + "face-unwell", + "🥴", + ), + EmojiItem( + "SHOCKED FACE WITH EXPLODING HEAD", + "smiley-emotion", + "face-unwell", + "🤯", + ), + EmojiItem( + "FACE WITH OPEN MOUTH VOMITING", + "smiley-emotion", + "face-unwell", + "🤮", + ), + EmojiItem("SNEEZING FACE", "smiley-emotion", "face-unwell", "🤧"), + EmojiItem("NAUSEATED FACE", "smiley-emotion", "face-unwell", "🤢"), + EmojiItem( + "FACE WITH HEAD-BANDAGE", "smiley-emotion", "face-unwell", "🤕" + ), + EmojiItem( + "FACE WITH THERMOMETER", "smiley-emotion", "face-unwell", "🤒" + ), + EmojiItem( + "FACE WITH MEDICAL MASK", "smiley-emotion", "face-unwell", "😷" + ), + EmojiItem("DIZZY FACE", "smiley-emotion", "face-unwell", "😵"), + EmojiItem( + "SPEAK-NO-EVIL MONKEY", "smiley-emotion", "monkey-face", "🙊" + ), + EmojiItem("HEAR-NO-EVIL MONKEY", "smiley-emotion", "monkey-face", "🙉"), + EmojiItem("SEE-NO-EVIL MONKEY", "smiley-emotion", "monkey-face", "🙈"), + ] + + @staticmethod + def categories(): + """Get a set of categories. + + Returns: + + set: + Emoji categories. + + Examples: + + ```python + >>> Emoji.categories() + {'component', 'animal-nature', 'objects', 'symbols', 'flags', + 'people-body', 'smiley-emotion', 'activities', 'food-drink', + 'travel-places'} + ``` + """ + cat = set() + for e in Emoji._ITEMS: + cat.add(e.category) + return cat + + @staticmethod + def subcategories(category: str = None): + """Get a set of all subcategories or for a specific category. + + Parameters: + + category (str): + The name of the category to query. + + Returns: + + set: + All subcategories or categories for a specific category. + + Examples: + + ```python + >>> Emoji.subcategories('activities') + {'award-medal', 'arts & crafts', 'event', 'game', 'sport'} + ``` + """ + subcat = set() + for e in Emoji._ITEMS: + if not category: + subcat.add(e.subcategory) + else: + if e.category == category: + subcat.add(e.subcategory) + return subcat + + @staticmethod + def get(name: str): + """Lookup an emoji by name. + + Parameters: + + name (str): + The name of the emoji to lookup. + + Returns: + + Union[EmojiItem, None]: + The selected emoji or None if not found. + + Examples: + + ```python + >>> Emoji.get('winking face') + 😉 + + >>> face = Emoji.get('winking face') + >>> face.name + WINKING FACE + + >>> face.category + smiley-emotion + + >>> face.subcategory + face.smiling + + >>> face.char + 😉 + ``` + """ + for e in Emoji._ITEMS: + if e.name.lower() == name.lower(): + return e + + +if __name__ == "__main__": + print(Emoji.get("winking face")) + print(Emoji.categories()) + print(Emoji.subcategories()) diff --git a/pylibraries/ttkbootstrap/localization/__init__.py b/pylibraries/ttkbootstrap/localization/__init__.py new file mode 100644 index 0000000..b70c281 --- /dev/null +++ b/pylibraries/ttkbootstrap/localization/__init__.py @@ -0,0 +1,18 @@ +""" + A partial wrapper on the tcl/tk msgcat (Tcl message catalog) + + The MessageCatalog provides a set of functions that can be used to + manage multi-lingual user interfaces. Text strings are defined in a + “message catalog” which is independent from the application, and + which can be edited or localized without modifying the application + source code. New languages or locales may be provided by adding a + new file to the message catalog. + + https://www.tcl.tk/man/tcl/TclCmd/msgcat.html +""" +from ttkbootstrap.localization.msgs import initialize_localities +from ttkbootstrap.localization.msgcat import MessageCatalog + + + + diff --git a/pylibraries/ttkbootstrap/localization/__pycache__/__init__.cpython-39.pyc b/pylibraries/ttkbootstrap/localization/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08f6f1dfcd45b0fa6d1dbc51921fdaf4e2862046 GIT binary patch literal 895 zcmaJQW|~3n@&^hTLSBqu6A70 zDcFD$eH%*F%`42|3Og_|(c7>>f8ls(3(S9V&t!|y{*iV%wE|Ns29BgLa^T^= z{$4F+`+G&9d|4E$$3)w;_r#vaCO=5i3z_pfF{I91!GDuj_b}nK?^&4fub+OAsJ%r& z-rAjbhu3I4cc&nXBtEr->)<k$_cr#wy1&PIO#5q2Xx literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/localization/__pycache__/msgcat.cpython-39.pyc b/pylibraries/ttkbootstrap/localization/__pycache__/msgcat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a3e66d4d7cdba269ed4e1af02f373c845207dd2 GIT binary patch literal 5981 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 E0up&mv;Y7A literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/localization/__pycache__/msgs.cpython-39.pyc b/pylibraries/ttkbootstrap/localization/__pycache__/msgs.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d13457d0f48c7db4e30d9f915f52b658b862717c GIT binary patch literal 11554 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 On~M2AzU_|u_ 0: + return items[0:-1] + else: + return [] + + @staticmethod + def load(dirname): + """Searches the specified directory for files that match the + language specifications returned by `preferences`. Each file + located is sourced. + + Parameters: + + dirname (str or Pathlike object): + The directory path of the msg files. + + Returns: + + int: + Then number of message files which matched the + specification and were loaded. + """ + from pathlib import Path + msgs = Path(dirname).as_posix() # format path for tcl/tk + + root = get_default_root() + command = "::msgcat::mcload" + return int(root.tk.eval(f"{command} [list {msgs}]")) + + @staticmethod + def set(locale, src, translated=None): + """Sets the translation for 'src' to 'translated' in the + specified locale. If translated is not specified, src is used + for both. + + Parameters: + + locale (str): + The local code used when translating the src. + + src (str): + The original language string. + + translated (str): + The translated string. + """ + root = get_default_root() + command = "::msgcat::mcset" + root.tk.eval(f'{command} {locale} {src} {translated or ""}') + + @staticmethod + def set_many(locale, *args): + """Sets the translation for multiple source strings in *args in + the specified locale and the current namespace. Must be an even + number of args. + + Parameters: + + locale (str): + The local code used when translating the src. + + *args (str): + A series of src, translated pairs. + + Returns: + + int: + The number of translation sets. + """ + root = get_default_root() + command = "::msgcat::mcmset" + messages = " ".join([f'"{x}"' for x in args]) + out = f"{command} {locale} {{{messages}}}" + return int(root.tk.eval(out)) + + @staticmethod + def max(*src): + """Given several source strings, max returns the length of the + longest translated string. This is useful when designing localized + GUIs, which may require that all buttons, for example, be a fixed + width (which will be the width of the widest button). + + Parameters: + + *src (str): + A series of strings to compare + + Returns: + + int: + The length of the longest str. + """ + root = get_default_root() + command = "::msgcat::mcmax" + return int(root.tk.eval(f'{command} {" ".join(src)}')) + + +if __name__ == "__main__": + + # testing + from ttkbootstrap import localization + + localization.initialize_localities() + MessageCatalog.locale("zh_cn") + result = MessageCatalog.translate("Skip Messages") + print(result) + result = MessageCatalog.translate("yes") + print(result) + from ttkbootstrap.dialogs import Messagebox + + Messagebox.okcancel("this is my message") diff --git a/pylibraries/ttkbootstrap/localization/msgs.py b/pylibraries/ttkbootstrap/localization/msgs.py new file mode 100644 index 0000000..a529a98 --- /dev/null +++ b/pylibraries/ttkbootstrap/localization/msgs.py @@ -0,0 +1,431 @@ +from ttkbootstrap.localization.msgcat import MessageCatalog + +MESSAGES = [] + + +def initialize_localities(): + """Load all custom msg files.""" + for m in MESSAGES: + m.initialize() + + +class LocaleMsgs: + """A helper class to allow loading the library message catalog + without having to package library resources which can cause + problems with creating distributable applications with some + packagers.""" + + def __init__(self, locale, *msgs): + self.locale = locale + self.messages = msgs + + def initialize(self): + """Initialize this locale in the MessageCatalog""" + from itertools import chain + + messages = list(chain(*self.messages)) + MessageCatalog.set_many(self.locale, *messages) + + +MESSAGES.append( + # CZECH translation + LocaleMsgs( + "cs", + ("Continue", "Pokračovat"), + ("Retry", "Znovu"), + ("Delete", "Vymazat"), + ("Next", "Další"), + ("Prev", "Předchozí"), + ("Yes", "Ano"), + ("No", "Ne"), + ("Open", "Otevřený"), + ("Close", "Zavřít"), + ("Add", "Přidat"), + ("Remove", "Odstranit"), + ("Submit", "Podat"), + ("Family", "Rodina"), + ("Weight", "Hmotnost"), + ("Slant", "Sklonit"), + ("Effects", "Účinky"), + ("Preview", "Náhled"), + ("Size", "Velikost"), + ) +) +MESSAGES.append( + # DANISH translation + LocaleMsgs( + "da", + ("Continue", "Fortsætte"), + ("Retry", "Prøv"), + ("Delete", "Slette"), + ("Next", "Næste"), + ("Prev", "Forrige"), + ("Yes", "Ja"), + ("No", "Nej"), + ("Open", "Åben"), + ("Close", "Lukke"), + ("Add", "Tilføje"), + ("Remove", "Fjerne"), + ("Submit", "Indsende"), + ("Family", "Familie"), + ("Weight", "Vægt"), + ("Slant", "Drejning"), + ("Effects", "Effekter"), + ("Size", "Størrelse"), + ) +) +MESSAGES.append( + # SPANISH translation + LocaleMsgs( + "es", + ("Cancel", "Cancelar"), + ("Retry", "Reintentar"), + ("Delete", "Borrar"), + ("Next", "Próximo"), + ("Prev", "Anterior"), + ("Yes", "Sí"), + ("Open", "Abrir"), + ("Close", "Cerrar"), + ("Add", "Agregar"), + ("Remove", "Eliminar"), + ("Submit", "Enviar"), + ("Family", "Familia"), + ("Weight", "Peso"), + ("Slant", "Inclinación"), + ("Effects", "Efectos"), + ("Preview", "Vista previa"), + ("Size", "Tamaño"), + ) +) +MESSAGES.append( + # PORTUGUESE - BRAZIL translation + LocaleMsgs( + "pt_br", + ("Retry", "Repetir"), + ("Delete", "Excluir"), + ("Next", "Próximo"), + ("Prev", "Anterior"), + ("Yes", "Sim"), + ("No", "Não"), + ("Open", "Abrir"), + ("Close", "Fechar"), + ("Add", "Adicionar"), + ("Remove", "Remover"), + ("Submit", "Enviar"), + ("Family", "Família"), + ("Weight", "Espessura"), + ("Slant", "Estilo"), + ("Effects", "Efeitos"), + ("Preview", "Visualizar"), + ("Size", "Tamanho"), + ("Should be of data type", "Deve ser do tipo de dados"), + ("Invalid data type", "Tipo de dados inválido"), + ("Number cannot be greater than", "O número não deve ser maior que"), + ("Out of range", "Fora do limite"), + ("Previous", "Anterior"), + ( + "The quick brown fox jumps over the lazy dog.", + "A rápida raposa marrom pula sobre o cachorro preguiçoso.", + ), + ("Font Selector", "Seletor de Fontes"), + ("normal", "normal"), + ("bold", "negrito"), + ("roman", "romano"), + ("italic", "itálico"), + ("underline", "sublinhado"), + ("overstrike", "taxado"), + ("Color Chooser", "Seletor de Cores"), + ("Advanced", "Avançado"), + ("Themed", "Tema"), + ("Standard", "Básicas"), + ("Current", "Atual"), + ("New", "Nova"), + ("Hue", "Matiz"), + ("Sat", "Sat"), + ("Lum", "Lum"), + ("Hex", "Hex"), + ("Red", "Vermelho"), + ("Green", "Verde"), + ("Blue", "Azul"), + ("color dropper", "Selecionador de cores (conta-gotas)"), + ("Cancel", "Cancelar"), + ("Search", "Buscar"), + ("Page", "Página"), + ("of", "de"), + ("⎌", "↺"), + ("Reset table", "Resetar Tabela"), + ("Columns", "Colunas"), + ("Move", "Mover"), + ("Align", "Alinhar"), + ("Hide column", "Ocultar coluna"), + ("Delete column", "Excluir coluna"), + ("Show All", "Exibir todas"), + ("Move to left", "Mover para esquerda"), + ("Move to right", "Mover para direira"), + ("Move to first", "Mover para o início"), + ("Move to last", "Mover para o fim"), + ("Align left", "Alinhar à esquerda"), + ("Align center", "Alinhar ao centro"), + ("Align right", "Alinhar à direita"), + ("Sort", "Classificar"), + ("Filter", "Filtrar"), + ("Export", "Exportar"), + ("Delete selected rows", "Excluir linhas selecionadas"), + ("Sort Ascending", "Ordem crescente"), + ("Sort Descending", "Ordem decrescente"), + ("Clear filters", "Limpar filtros"), + ("Filter by cell's value", "Filtrar pelo valor da célula"), + ("Hide select rows", "Ocultar linha selecionada"), + ("Show only select rows", "Exibir somente as linhas selecionadas"), + ("Export all records", "Exportar todos os dados"), + ("Export current page", "Exportar página atual"), + ("Export current selection", "Exportar seleção atual"), + ("Export records in filter", "Exportar dados do filtro"), + ("Move up", "Mover para cima"), + ("Move down", "Mover para baixo"), + ("Move to top", "Mover para o início"), + ("Move to bottom", "Mover para o fim"), + ("Mo", "S"), + ("Tu", "T"), + ("We", "Q"), + ("Th", "Q"), + ("Fr", "S"), + ("Sa", "S"), + ("Su", "D"), + ) +) +MESSAGES.append( + # CHINESE - CHINA translation + LocaleMsgs( + "zh_cn", + ("&Abort", "&中止"), + ("&About...", "&关于……"), + ("All Files", "所有文件"), + ("Application Error", "应用程序错误"), + ("&Apply", "&添加"), + ("Bold", "粗体"), + ("Bold Italic", "加粗斜体"), + ("&Blue", "&蓝色"), + ("Cancel", "取消"), + ("&Cancel", "&取消"), + ( + 'Cannot change to the directory %1\$s.\nPermission denied.', + '无法更改目录 %1\$s。\n访问被拒绝。', + ), + ("Choose Directory", "选择文件夹"), + ("Cl&ear", "清&除"), + ("&Clear Console", "&清除终端"), + ("Color", "颜色"), + ("Console", "终端"), + ("&Copy", "&复制"), + ("Cu&t", "剪&切"), + ("&Delete", "&删除"), + ("Details >>", "详细信息 >>"), + ('Directory %1\$s does not exist.', '目录 %1\$s 不存在。'), + ("&Directory:", "&目录:"), + ("&Edit", "&编辑"), + ("Effects", "效果"), + ("Error: %1\$s", "错误: %1\$s"), + ("E&xit", "退&出"), + ("&File", "&文件"), + ( + 'File %1\$s already exists.\nDo you want to overwrite it?', + '文件 %1\$s 已经存在。\n您想要覆盖它吗?', + ), + ('File %1\$s already exists.\n\n', '文件 %1\$s 已经存在。\n\n'), + ('File %1\$s does not exist.', '文件 %1\$s 不存在。'), + ("File &name:", "文件&名:"), + ("File &names:", "文件&名:"), + ("Files of &type:", "文件&类型:"), + ("Fi&les:", "文&件:"), + ("&Filter", "&过滤"), + ("Fil&ter:", "过&滤:"), + ("Font", "字体"), + ("&Font:", "&字体:"), + ("Font st&yle:", "字体&样式:"), + ("&Green", "&绿色"), + ("&Help", "&帮助"), + ("Hi", "你好"), + ("&Hide Console", "&隐藏终端"), + ("&Ignore", "&忽略"), + ('Invalid file name %1\$s.', '无效的文件名 %1\$s。'), + ("Italic", "斜体"), + ("Log Files", "日志文件"), + ("&No", "&取消"), + ("No", "取消"), + ("&OK", "&确定"), + ("OK", "确定"), + ("Ok", "确定"), + ("Open", "打开"), + ("&Open", "&打开"), + ("Open Multiple Files", "打开多个文件"), + ("P&aste", "粘&贴"), + ("&Quit", "&退出"), + ("&Red", "红色"), + ("Regular", "规则"), + ("Replace existing file?", "替换已有文件?"), + ("&Retry", "&重试"), + ("Sample", "样式"), + ("&Save", "&保存"), + ("Save As", "另存为"), + ("Save To Log", "保存到日志"), + ("Select Log File", "选择日志文件"), + ("Select a file to source", "选择一个源文件"), + ("&Selection:", "&选择:"), + ("&Size:", "&大小:"), + ("Show &Hidden Directories", "显示&隐藏目录"), + ("Show &Hidden Files and Directories", "显示&隐藏文件和目录"), + ("Skip Messages", "跳过信息"), + ("&Source...", "&来源……"), + ("Stri&keout", "删&除线"), + ("Tcl Scripts", "Tcl脚本"), + ("Tcl for Windows", "适用于Windows的Tcl"), + ("Text Files", "文本文档"), + ("&Underline", "&下划线"), + ("&Yes", "&确定"), + ("abort", "中止"), + ("blue", "蓝色"), + ("cancel", "取消"), + ("extension", "拓展"), + ("extensions", "拓展"), + ("green", "绿色"), + ("ignore", "忽略"), + ("ok", "确定"), + ("red", "红色"), + ("retry", "重试"), + ("Retry", "重试"), + ("yes", "确认"), + ("Yes", "确认"), + ("Should be of data type", "应为数据类型"), + ("Invalid data type", "无效数据类型"), + ("Number cannot be greater than", "数字不能大于"), + ("Out of range", "超出范围"), + ("Submit", "提交"), + ("Delete", "删除"), + ("Next", "下一步"), + ("Previous", "以前的"), + ("Open", "打开"), + ("Close", "关闭"), + ("Add", "添加"), + ("Remove", "移除"), + ("Family", "组"), + ("Weight", "重量"), + ("Slant", "倾斜"), + ("Effects", "效果"), + ("Preview", "预览"), + ("Size", "大小"), + ("The quick brown fox jumps over the lazy dog.", "敏捷的棕色狐狸跳过懒惰的狗。"), + ("Print", "输出"), + ("Printer", "打印机"), + ("Letter ", "信 "), + ("Legal ", "合法的 "), + ("A4", "A4"), + ("Grayscale", "灰度"), + ("RGB", "RGB"), + ("Options", "设置"), + ("Copies", "复制"), + ("Paper", "纸"), + ("Scale", "规模"), + ("Orientation", "方向"), + ("Portrait", "竖向"), + ("Landscape", "横向"), + ("Output", "输出"), + ) +) + +MESSAGES.append( + LocaleMsgs( + # French + "fr", + ("OK", "OK"), + ("Ok", "Ok"), + ("Retry", "Recommencer"), + ("Delete", "Supprimer"), + ("Next", "Suivant"), + ("Prev", "Préc."), + ("Yes", "Oui"), + ("No", "Non"), + ("Open", "Ouvrir"), + ("Close", "Fermer"), + ("Add", "Ajouter"), + ("Remove", "Supprimer"), + ("Submit", "Envoyer"), + ("Family", "Famille"), + ("Weight", "Poids"), + ("Slant", "Italique"), + ("Effects", "Effets"), + ("Preview", "Prévisualiser"), + ("Size", "Taille"), + ("Should be a of data type", "Doit être du type de données"), + ("Invalid data type", "Type de données invalide"), + ("Number cannot be greater than", "Le nombre ne peut pas être plus grand que"), + ("Out of range", "Hors limites"), + ("Previous", "Précédent"), + ("The quick brown fox jumps over the lazy dog.", "The quick brown fox jumps over the lazy dog."), + ("Font Selector", "Sélecteur de Polices"), + ("normal", "normal"), + ("bold", "gras"), + ("roman", "roman"), + ("italic", "italique"), + ("underline", "souligné"), + ("overstrike", "barré"), + ("Color Chooser", "Sélecteur de couleur"), + ("Advanced", "Avancé"), + ("Themed", "Thème"), + ("Standard", "Standard"), + ("Current", "Courant"), + ("New", "Nouveau"), + ("Hue", "Teinte"), + ("Sat", "Sat"), + ("Lum", "Lum"), + ("Hex", "Hex"), + ("Red", "Rouge"), + ("Green", "Vert"), + ("Blue", "Bleu"), + ("color dropper", "Sélecteur de couleurs"), + ("Cancel", "Annuler"), + ("Search", "Chercher"), + ("Page", "Page"), + ("of", "de"), + ("⎌", "↺"), + ("Reset table", "Réinit. table"), + ("Columns", "Colonnes"), + ("Move", "Déplacer"), + ("Align", "Aligner"), + ("Hide column", "Cacher la colonne"), + ("Delete column", "Supprimer la colonne"), + ("Show All", "Afficher tout"), + ("Move to left", "Déplacer vers la gauche"), + ("Move to right", "Déplacer vers la droite"), + ("Move to first", "Déplacer en premier"), + ("Move to last", "Déplacer en dernier"), + ("Align left", "Aligner à gauche"), + ("Align center", "Aligner au centre"), + ("Align right", "Aligner à droite"), + ("Sort", "Trier"), + ("Filter", "Filtrer"), + ("Export", "Exporter"), + ("Delete selected rows", "Supprimer les lignes sélectionnées"), + ("Sort Ascending", "Tri ascendant"), + ("Sort Descending", "Tri descendant"), + ("Clear filters", "Effacer les filtres"), + ("Filter by cell's value", "Filtrer par valeur de cellules"), + ("Hide select rows", "Cacher les lignes sélectionnées"), + ("Show only select rows", "N’afficher que les lignes sélectionnées"), + ("Export all records", "Exporter tous les enregistrements"), + ("Export current page", "Exporter la page active"), + ("Export current selection", "Exporter la sélection"), + ("Export records in filter", "Exporter les enregistrements filtrés"), + ("Move up", "Déplacer vers le haut"), + ("Move down", "Déplacer vers le bas"), + ("Move to top", "Déplacer en premier"), + ("Move to bottom", "Déplacer en dernier"), + ("Mo", "Lu"), + ("Tu", "Ma"), + ("We", "Me"), + ("Th", "Je"), + ("Fr", "Ve"), + ("Sa", "Sa"), + ("Su", "Di"), + ) +) diff --git a/pylibraries/ttkbootstrap/publisher.py b/pylibraries/ttkbootstrap/publisher.py new file mode 100644 index 0000000..61e5f24 --- /dev/null +++ b/pylibraries/ttkbootstrap/publisher.py @@ -0,0 +1,118 @@ +from enum import Enum +from typing import List + + +class Channel(Enum): + """A grouping for Publisher subscribers. Indicates whether the + widget is a legacy `STD` tk widget or a styled `TTK` widget. + + Attributes: + + STD (1): + Legacy tkinter widgets. + + TTK (2): + Themed tkinter widgets. + """ + + STD = 1 + TTK = 2 + + +class Subscriber: + """A subcriber data class used to store information about a specific + subcriber to the `Publisher`.""" + + def __init__(self, name, func, channel): + """Create a subscriber. + + Parameters: + + name (str): + The name of the subscriber + + func (Callable): + The function to call when messaging. + + channel (Channel): + The subscription channel. + """ + self.name = name + self.func = func + self.channel = channel + + +class Publisher: + """A class used to publish events for widget updates for theme changes + or configurations""" + + __subscribers = {} + + @staticmethod + def subscriber_count(): + return len(Publisher.__subscribers) + + @staticmethod + def subscribe(name, func, channel): + """Subscribe to an event. + + Parameters: + + name (str): + The widget's tkinter/tcl name. + + func (Callable): + A function to call when passing a message. + + channel (Channel): + Indicates the channel grouping the subscribers. + """ + subs = Publisher.__subscribers + subs[name] = Subscriber(name, func, channel) + + @staticmethod + def unsubscribe(name): + """Remove a subscriber + + Parameters: + + name (str): + The widget's tkinter/tcl name. + """ + subs = Publisher.__subscribers + try: + del subs[str(name)] + except: + pass + + def get_subscribers(channel): + """Return a list of subscribers + + Returns: + + List: + List of key-value tuples + """ + subs = Publisher.__subscribers.values() + channel_subs = [s for s in subs if s.channel == channel] + return channel_subs + + def publish_message(channel, *args): + """Publish a message to all subscribers + + Parameters: + + channel (Channel): + The name of the channel to subscribe. + + **args: + optional arguments to pass to the subscribers. + """ + subs: List[Subscriber] = Publisher.get_subscribers(channel) + for sub in subs: + sub.func(*args) + + @staticmethod + def clear_subscribers(): + """Reset all subscriptions.""" + Publisher.__subscribers.clear() diff --git a/pylibraries/ttkbootstrap/scrolled.py b/pylibraries/ttkbootstrap/scrolled.py new file mode 100644 index 0000000..1a0384d --- /dev/null +++ b/pylibraries/ttkbootstrap/scrolled.py @@ -0,0 +1,476 @@ +""" + This module contains various custom scrolling widgets, including + `ScrolledText` and `ScrolledFrame`. +""" +import ttkbootstrap as ttk +from ttkbootstrap.constants import * +from tkinter import Pack, Place, Grid + + +class ScrolledText(ttk.Frame): + """A text widget with optional vertical and horizontal scrollbars. + Setting `autohide=True` will cause the scrollbars to hide when the + mouse is not over the widget. The vertical scrollbar is on by + default, but can be turned off. The horizontal scrollbar can be + enabled by setting `vbar=True`. + + This widget is identical in configuration to the `Text` widget other + than the scrolling frame. https://tcl.tk/man/tcl8.6/TkCmd/text.htm + + ![scrolled text](../../../assets/scrolled/scrolledtext.gif) + + Examples: + + ```python + import ttkbootstrap as ttk + from ttkbootstrap.constants import * + from ttkbootstrap.scrolled import ScrolledText + + app = ttk.Window() + + # scrolled text with autohide vertical scrollbar + st = ScrolledText(app, padding=5, height=10, autohide=True) + st.pack(fill=BOTH, expand=YES) + + # add text + st.insert(END, 'Insert your text here.') + + app.mainloop() + ``` + """ + + def __init__( + self, + master=None, + padding=2, + bootstyle=DEFAULT, + autohide=False, + vbar=True, + hbar=False, + **kwargs, + ): + """ + Parameters: + + master (Widget): + The parent widget. + + padding (int): + The amount of empty space to create on the outside of + the widget. + + bootstyle (str): + A style keyword used to set the color and style of the + vertical scrollbar. Available options include -> primary, + secondary, success, info, warning, danger, dark, light. + + vbar (bool): + A vertical scrollbar is shown when **True** (default). + + hbar (bool): + A horizontal scrollbar is shown when **True**. Turning + on this scrollbar will also set `wrap="none"`. This + scrollbar is _off_ by default. + + autohide (bool): + When **True**, the scrollbars will hide when the mouse + is not within the frame bbox. + + **kwargs (Dict[str, Any]): + Other keyword arguments passed to the `Text` widget. + """ + super().__init__(master, padding=padding) + + # setup text widget + self._text = ttk.Text(self, padx=50, **kwargs) + self._hbar = None + self._vbar = None + + # delegate text methods to frame + for method in vars(ttk.Text).keys(): + if any(["pack" in method, "grid" in method, "place" in method]): + pass + else: + setattr(self, method, getattr(self._text, method)) + + # setup scrollbars + if vbar: + self._vbar = ttk.Scrollbar( + master=self, + bootstyle=bootstyle, + command=self._text.yview, + orient=VERTICAL, + ) + self._vbar.place(relx=1.0, relheight=1.0, anchor=NE) + self._text.configure(yscrollcommand=self._vbar.set) + + if hbar: + self._hbar = ttk.Scrollbar( + master=self, + bootstyle=bootstyle, + command=self._text.xview, + orient=HORIZONTAL, + ) + self._hbar.place(rely=1.0, relwidth=1.0, anchor=SW) + self._text.configure(xscrollcommand=self._hbar.set, wrap="none") + + self._text.pack(side=LEFT, fill=BOTH, expand=YES) + + # position scrollbars + if self._hbar: + self.update_idletasks() + self._text_width = self.winfo_reqwidth() + self._scroll_width = self.winfo_reqwidth() + + self.bind("", self._on_configure) + + if autohide: + self.autohide_scrollbar() + self.hide_scrollbars() + + def _on_configure(self, *_): + """Callback for when the configure method is used""" + if self._hbar: + self.update_idletasks() + text_width = self.winfo_width() + vbar_width = self._vbar.winfo_width() + relx = (text_width - vbar_width) / text_width + self._hbar.place(rely=1.0, relwidth=relx) + + @property + def text(self): + """Returns the internal text object""" + return self._text + + @property + def hbar(self): + """Returns the internal horizontal scrollbar""" + return self._hbar + + @property + def vbar(self): + """Returns the internal vertical scrollbar""" + return self._vbar + + def hide_scrollbars(self, *_): + """Hide the scrollbars.""" + try: + self._vbar.lower(self._text) + except: + pass + try: + self._hbar.lower(self._text) + except: + pass + + def show_scrollbars(self, *_): + """Show the scrollbars.""" + try: + self._vbar.lift(self._text) + except: + pass + try: + self._hbar.lift(self._text) + except: + pass + + def autohide_scrollbar(self, *_): + """Show the scrollbars when the mouse enters the widget frame, + and hide when it leaves the frame.""" + self.bind("", self.show_scrollbars) + self.bind("", self.hide_scrollbars) + + +class ScrolledFrame(ttk.Frame): + """A widget container with a vertical scrollbar. + + The ScrolledFrame fills the width of its container. The height is + either set explicitly or is determined by the content frame's + contents. + + This widget behaves mostly like a normal frame other than the + exceptions stated already. Another exception is when packing it + into a Notebook or Panedwindow. In this case, you'll need to add + the container instead of the content frame. For example, + `mynotebook.add(myscrolledframe.container)`. + + The scrollbar has an autohide feature that can be turned on by + setting `autohide=True`. + + Examples: + + ```python + import ttkbootstrap as ttk + from ttkbootstrap.constants import * + from ttkbootstrap.scrolled import ScrolledFrame + + app = ttk.Window() + + sf = ScrolledFrame(app, autohide=True) + sf.pack(fill=BOTH, expand=YES, padx=10, pady=10) + + # add a large number of checkbuttons into the scrolled frame + for x in range(20): + ttk.Checkbutton(sf, text=f"Checkbutton {x}").pack(anchor=W) + + app.mainloop() + ```""" + + def __init__( + self, + master=None, + padding=2, + bootstyle=DEFAULT, + autohide=False, + height=200, + width=300, + scrollheight=None, + **kwargs, + ): + """ + Parameters: + + master (Widget): + The parent widget. + + padding (int): + The amount of empty space to create on the outside of + the widget. + + bootstyle (str): + A style keyword used to set the color and style of the + vertical scrollbar. Available options include -> primary, + secondary, success, info, warning, danger, dark, light. + + autohide (bool): + When **True**, the scrollbars will hide when the mouse + is not within the frame bbox. + + height (int): + The height of the container frame in screen units. + + width (int): + The width of the container frame in screen units. + + scrollheight (int): + The height of the content frame in screen units. If None, + the height is determined by the frame contents. + + **kwargs (Dict[str, Any]): + Other keyword arguments passed to the content frame. + """ + # content frame container + self.container = ttk.Frame( + master=master, + relief=FLAT, + borderwidth=0, + width=width, + height=height, + ) + self.container.bind("", lambda _: self.yview()) + self.container.propagate(0) + + # content frame + super().__init__( + master=self.container, + padding=padding, + bootstyle=bootstyle.replace('round', ''), + width=width, + height=height, + **kwargs, + ) + self.place(rely=0.0, relwidth=1.0, height=scrollheight) + + # vertical scrollbar + self.vscroll = ttk.Scrollbar( + master=self.container, + command=self.yview, + orient=VERTICAL, + bootstyle=bootstyle, + ) + self.vscroll.pack(side=RIGHT, fill=Y) + + self.winsys = self.tk.call("tk", "windowingsystem") + + # setup autohide scrollbar + self.autohide = autohide + if self.autohide: + self.hide_scrollbars() + + # widget event binding + self.container.bind("", self._on_enter, "+") + self.container.bind("", self._on_leave, "+") + self.container.bind("", self._on_map, "+") + self.bind("<>", self._on_map_child, "+") + + # delegate content geometry methods to container frame + _methods = vars(Pack).keys() | vars(Grid).keys() | vars(Place).keys() + for method in _methods: + if any(["pack" in method, "grid" in method, "place" in method]): + # prefix content frame methods with 'content_' + setattr(self, f"content_{method}", getattr(self, method)) + # overwrite content frame methods from container frame + setattr(self, method, getattr(self.container, method)) + + def yview(self, *args): + """Update the vertical position of the content frame within the + container. + + Parameters: + + *args (List[Any, ...]): + Optional arguments passed to yview in order to move the + content frame within the container frame. + """ + if not args: + first, _ = self.vscroll.get() + self.yview_moveto(fraction=first) + elif args[0] == "moveto": + self.yview_moveto(fraction=float(args[1])) + elif args[0] == "scroll": + self.yview_scroll(number=int(args[1]), what=args[2]) + else: + return + + def yview_moveto(self, fraction: float): + """Update the vertical position of the content frame within the + container. + + Parameters: + + fraction (float): + The relative position of the content frame within the + container. + """ + base, thumb = self._measures() + if fraction < 0: + first = 0.0 + elif (fraction + thumb) > 1: + first = 1 - thumb + else: + first = fraction + self.vscroll.set(first, first + thumb) + self.content_place(rely=-first * base) + + def yview_scroll(self, number: int, what: str): + """Update the vertical position of the content frame within the + container. + + Parameters: + + number (int): + The amount by which the content frame will be moved + within the container frame by 'what' units. + + what (str): + The type of units by which the number is to be interpeted. + This parameter is currently not used and is assumed to be + 'units'. + """ + first, _ = self.vscroll.get() + fraction = (number / 100) + first + self.yview_moveto(fraction) + + def _add_scroll_binding(self, parent): + """Recursive adding of scroll binding to all descendants.""" + children = parent.winfo_children() + for widget in [parent, *children]: + bindings = widget.bind() + if self.winsys.lower() == "x11": + if "" in bindings or "" in bindings: + continue + else: + widget.bind("", self._on_mousewheel, "+") + widget.bind("", self._on_mousewheel, "+") + else: + if "" not in bindings: + widget.bind("", self._on_mousewheel, "+") + if widget.winfo_children() and widget != parent: + self._add_scroll_binding(widget) + + + def _del_scroll_binding(self, parent): + """Recursive removal of scrolling binding for all descendants""" + children = parent.winfo_children() + for widget in [parent, *children]: + if self.winsys.lower() == "x11": + widget.unbind("") + widget.unbind("") + else: + widget.unbind("") + if widget.winfo_children() and widget != parent: + self._del_scroll_binding(widget) + + + def enable_scrolling(self): + """Enable mousewheel scrolling on the frame and all of its + children.""" + self._add_scroll_binding(self) + + + def disable_scrolling(self): + """Disable mousewheel scrolling on the frame and all of its + children.""" + self._del_scroll_binding(self) + + def hide_scrollbars(self): + """Hide the scrollbars.""" + self.vscroll.pack_forget() + + def show_scrollbars(self): + """Show the scrollbars.""" + self.vscroll.pack(side=RIGHT, fill=Y) + + def autohide_scrollbar(self): + """Toggle the autohide funtionality. Show the scrollbars when + the mouse enters the widget frame, and hide when it leaves the + frame.""" + self.autohide = not self.autohide + + def _measures(self): + """Measure the base size of the container and the thumb size + for use in the yview methods""" + outer = self.container.winfo_height() + inner = max([self.winfo_height(), outer]) + base = inner / outer + if inner == outer: + thumb = 1.0 + else: + thumb = outer / inner + return base, thumb + + def _on_map_child(self, event): + """Callback for when a widget is mapped to the content frame.""" + if self.container.winfo_ismapped(): + self.yview() + + def _on_enter(self, event): + """Callback for when the mouse enters the widget.""" + self.enable_scrolling() + if self.autohide: + self.show_scrollbars() + + def _on_leave(self, event): + """Callback for when the mouse leaves the widget.""" + self.disable_scrolling() + if self.autohide: + self.hide_scrollbars() + + def _on_configure(self, event): + """Callback for when the widget is configured""" + self.yview() + + def _on_map(self, event): + self.yview() + + def _on_mousewheel(self, event): + """Callback for when the mouse wheel is scrolled.""" + if self.winsys.lower() == "win32": + delta = -int(event.delta / 120) + elif self.winsys.lower() == "aqua": + delta = -event.delta + elif event.num == 4: + delta = -10 + elif event.num == 5: + delta = 10 + self.yview_scroll(delta, UNITS) diff --git a/pylibraries/ttkbootstrap/style.py b/pylibraries/ttkbootstrap/style.py new file mode 100644 index 0000000..dd127d6 --- /dev/null +++ b/pylibraries/ttkbootstrap/style.py @@ -0,0 +1,5179 @@ +import json +import re +import colorsys +import tkinter as tk +from tkinter import font +from math import ceil +from tkinter import TclError, ttk +from typing import Any, Callable +from PIL import ImageTk, ImageDraw, Image, ImageFont +from ttkbootstrap.constants import * +from ttkbootstrap.themes.standard import STANDARD_THEMES +from ttkbootstrap.publisher import Publisher, Channel +from ttkbootstrap import utility as util +from ttkbootstrap import colorutils +from PIL import ImageColor + + +try: + # prevent app from failing if user.py gets corrupted + from ttkbootstrap.themes.user import USER_THEMES +except (ImportError, ModuleNotFoundError): + USER_THEMES = {} + + +class Colors: + """A class that defines the color scheme for a theme as well as + provides several static methods for manipulating colors. + + A `Colors` object is attached to a `ThemeDefinition` and can also + be accessed through the `Style.colors` property for the + current theme. + + Examples: + + ```python + style = Style() + + # dot-notation + style.colors.primary + + # get method + style.colors.get('primary') + ``` + + This class is an iterator, so you can iterate over the main + style color labels (primary, secondary, success, info, warning, + danger): + + ```python + for color_label in style.colors: + color = style.colors.get(color_label) + print(color_label, color) + ``` + + If, for some reason, you need to iterate over all theme color + labels, then you can use the `Colors.label_iter` method. This + will include all theme colors. + + ```python + for color_label in style.colors.label_iter(): + color = Colors.get(color_label) + print(color_label, color) + ``` + + If you want to adjust the hsv values of an existing color by a + specific percentage (delta), you can use the `Colors.update_hsv` + method, which is static. In the example below, the "value delta" + or `vd` is increased by 15%, which will lighten the color: + + ```python + Colors.update_hsv("#9954bb", vd=0.15) + ``` + """ + + def __init__( + self, + primary, + secondary, + success, + info, + warning, + danger, + light, + dark, + bg, + fg, + selectbg, + selectfg, + border, + inputfg, + inputbg, + active, + ): + """ + Parameters: + + primary (str): + The primary theme color; used by default for all widgets. + + secondary (str): + An accent color; commonly of a `grey` hue. + + success (str): + An accent color; commonly of a `green` hue. + + info (str): + An accent color; commonly of a `blue` hue. + + warning (str): + An accent color; commonly of an `orange` hue. + + danger (str): + An accent color; commonly of a `red` hue. + + light (str): + An accent color. + + dark (str): + An accent color. + + bg (str): + Background color. + + fg (str): + Default text color. + + selectfg (str): + The color of selected text. + + selectbg (str): + The background color of selected text. + + border (str): + The color used for widget borders. + + inputfg (str): + The text color for input widgets. + + inputbg (str): + The text background color for input widgets. + + active (str): + An accent color. + """ + self.primary = primary + self.secondary = secondary + self.success = success + self.info = info + self.warning = warning + self.danger = danger + self.light = light + self.dark = dark + self.bg = bg + self.fg = fg + self.selectbg = selectbg + self.selectfg = selectfg + self.border = border + self.inputfg = inputfg + self.inputbg = inputbg + self.active = active + + @staticmethod + def make_transparent(alpha, foreground, background='#ffffff'): + """Simulate color transparency. + + Parameters: + + alpha (float): + The amount of transparency; a number between 0 and 1. + + foreground (str): + The foreground color. + + background (str): + The background color. + + Returns: + + str: + A hexadecimal color representing the "transparent" + version of the foreground color against the background + color. + """ + fg = ImageColor.getrgb(foreground) + bg = ImageColor.getrgb(background) + rgb_float = [alpha * c1 + (1 - alpha) * c2 for (c1, c2) in zip(fg, bg)] + rgb_int = [int(x) for x in rgb_float] + return '#{:02x}{:02x}{:02x}'.format(*rgb_int) + + @staticmethod + def rgb_to_hsv(r, g, b): + """Convert an rgb to hsv color value. + + Parameters: + r (float): + red + g (float): + green + b (float): + blue + + Returns: + Tuple[float, float, float]: The hsv color value. + """ + return colorsys.rgb_to_hsv(r, g, b) + + def get_foreground(self, color_label): + """Return the appropriate foreground color for the specified + color_label. + + Parameters: + + color_label (str): + A color label corresponding to a class property + """ + if color_label == LIGHT: + return self.dark + elif color_label == DARK: + return self.light + else: + return self.selectfg + + def get(self, color_label: str): + """Lookup a color value from the color name + + Parameters: + + color_label (str): + A color label corresponding to a class propery + + Returns: + + str: + A hexadecimal color value. + """ + return self.__dict__.get(color_label) + + def set(self, color_label: str, color_value: str): + """Set a color property value. This does not update any existing + widgets. Can also be used to create on-demand color properties + that can be used in your program after creation. + + Parameters: + + color_label (str): + The name of the color to be set (key) + + color_value (str): + A hexadecimal color value + """ + self.__dict__[color_label] = color_value + + def __iter__(self): + return iter( + [ + "primary", + "secondary", + "success", + "info", + "warning", + "danger", + "light", + "dark", + ] + ) + + def __repr__(self): + out = tuple(zip(self.__dict__.keys(), self.__dict__.values())) + return str(out) + + @staticmethod + def label_iter(): + """Iterate over all color label properties in the Color class + + Returns: + + iter: + An iterator for color label names + """ + return iter( + [ + "primary", + "secondary", + "success", + "info", + "warning", + "danger", + "light", + "dark", + "bg", + "fg", + "selectbg", + "selectfg", + "border", + "inputfg", + "inputbg", + "active", + ] + ) + + @staticmethod + def hex_to_rgb(color: str): + """Convert hexadecimal color to rgb color value + + Parameters: + + color (str): + A hexadecimal color value + + Returns: + + tuple[int, int, int]: + An rgb color value. + """ + r, g, b = colorutils.color_to_rgb(color) + return r/255, g/255, b/255 + + @staticmethod + def rgb_to_hex(r: int, g: int, b: int): + """Convert rgb to hexadecimal color value + + Parameters: + + r (int): + red + + g (int): + green + + b (int): + blue + + Returns: + + str: + A hexadecimal color value + """ + r_ = int(r * 255) + g_ = int(g * 255) + b_ = int(b * 255) + return colorutils.color_to_hex((r_, g_, b_)) + + @staticmethod + def update_hsv(color, hd=0, sd=0, vd=0): + """Modify the hue, saturation, and/or value of a given hex + color value by specifying the _delta_. + + Parameters: + + color (str): + A hexadecimal color value to adjust. + + hd (float): + % change in hue, _hue delta_. + + sd (float): + % change in saturation, _saturation delta_. + + vd (float): + % change in value, _value delta_. + + Returns: + + str: + The resulting hexadecimal color value + """ + r, g, b = Colors.hex_to_rgb(color) + h, s, v = colorsys.rgb_to_hsv(r, g, b) + + # hue + if h * (1 + hd) > 1: + h = 1 + elif h * (1 + hd) < 0: + h = 0 + else: + h *= 1 + hd + + # saturation + if s * (1 + sd) > 1: + s = 1 + elif s * (1 + sd) < 0: + s = 0 + else: + s *= 1 + sd + + # value + if v * (1 + vd) > 1: + v = 0.95 + elif v * (1 + vd) < 0.05: + v = 0.05 + else: + v *= 1 + vd + + r, g, b = colorsys.hsv_to_rgb(h, s, v) + return Colors.rgb_to_hex(r, g, b) + + +class ThemeDefinition: + """A class to provide defined name, colors, and font settings for a + ttkbootstrap theme.""" + + def __init__(self, name, colors, themetype=LIGHT): + """ + Parameters: + + name (str): + The name of the theme. + + colors (Colors): + An object that defines the color scheme for a theme. + + themetype (str): + Specifies whether the theme is **light** or **dark**. + """ + self.name = name + self.colors = Colors(**colors) + self.type = themetype + + def __repr__(self): + + return " ".join( + [ + f"name={self.name},", + f"type={self.type},", + f"colors={self.colors}", + ] + ) + + +class Style(ttk.Style): + """A singleton class for creating and managing the application + theme and widget styles. + + This class is meant to be a drop-in replacement for `ttk.Style` and + inherits all of it's methods and properties. However, in + ttkbootstrap, this class is implemented as a singleton. Subclassing + is not recommended and may have unintended consequences. + + Examples: + + ```python + # instantiate the style with default theme + style = Style() + + # instantiate the style with another theme + style = Style(theme='superhero') + + # check all available themes + for theme in style.theme_names(): + print(theme) + ``` + + See the [Python documentation](https://docs.python.org/3/library/tkinter.ttk.html#tkinter.ttk.Style) + on this class for more details. + """ + + instance = None + + def __new__(cls, theme=None): + if Style.instance is None: + return object.__new__(cls) + else: + return Style.instance + + def __init__(self, theme=DEFAULT_THEME): + """ + Parameters: + + theme (str): + The name of the theme to use when styling the widget. + """ + if Style.instance is not None: + if theme != DEFAULT_THEME: + Style.instance.theme_use(theme) + return + self._theme_objects = {} + self._theme_definitions = {} + self._style_registry = set() # all styles used + self._theme_styles = {} # styles used in theme + self._theme_names = set() + self._load_themes() + super().__init__() + + Style.instance = self + self.theme_use(theme) + + # apply localization + from ttkbootstrap import localization + localization.initialize_localities() + + @property + def colors(self): + """An object that contains the colors used for the current + theme. + + Returns: + + Colors: + The colors object for the current theme. + """ + theme = self.theme.name + if theme in list(self._theme_names): + definition = self._theme_definitions.get(theme) + if not definition: + return [] # TODO refactor this + else: + return definition.colors + else: + return [] # TODO refactor this + + def configure(self, style, query_opt: Any = None, **kw): + if query_opt: + return super().configure(style, query_opt=query_opt, **kw) + + if not self.style_exists_in_theme(style): + ttkstyle = Bootstyle.update_ttk_widget_style(None, style) + else: + ttkstyle = style + + if ttkstyle == style: + # configure an existing ttkbootrap theme + return super().configure(style, query_opt=query_opt, **kw) + else: + # subclass a ttkbootstrap theme + result = super().configure(style, query_opt=query_opt, **kw) + self._register_ttkstyle(style) + return result + + def theme_names(self): + """Return a list of all ttkbootstrap themes. + + Returns: + + List[str, ...]: + A list of theme names. + """ + return list(self._theme_definitions.keys()) + + def register_theme(self, definition): + """Register a theme definition for use by the `Style` + object. This makes the definition and name available at + run-time so that the assets and styles can be created when + needed. + + Parameters: + + definition (ThemeDefinition): + A `ThemeDefinition` object. + """ + theme = definition.name + self._theme_names.add(theme) + self._theme_definitions[theme] = definition + self._theme_styles[theme] = set() + + def theme_use(self, themename=None): + """Changes the theme used in rendering the application widgets. + + If themename is None, returns the theme in use, otherwise, set + the current theme to themename, refreshes all widgets and emits + a ``<>`` event. + + Only use this method if you are changing the theme *during* + runtime. Otherwise, pass the theme name into the Style + constructor to instantiate the style with a theme. + + Parameters: + + themename (str): + The name of the theme to apply when creating new widgets + + Returns: + + Union[str, None]: + The name of the current theme if `themename` is None + otherwise, `None`. + """ + if not themename: + # return current theme + return super().theme_use() + + # change to an existing theme + existing_themes = super().theme_names() + if themename in existing_themes: + self.theme = self._theme_definitions.get(themename) + super().theme_use(themename) + self._create_ttk_styles_on_theme_change() + Publisher.publish_message(Channel.STD) + # setup a new theme + elif themename in self._theme_names: + self.theme = self._theme_definitions.get(themename) + self._theme_objects[themename] = StyleBuilderTTK() + self._create_ttk_styles_on_theme_change() + Publisher.publish_message(Channel.STD) + else: + raise TclError(themename, "is not a valid theme.") + + def style_exists_in_theme(self, ttkstyle: str): + """Check if a style exists in the current theme. + + Parameters: + + ttkstyle (str): + The ttk style to check. + + Returns: + + bool: + `True` if the style exists, otherwise `False`. + """ + theme_styles = self._theme_styles.get(self.theme.name) + exists_in_theme = ttkstyle in theme_styles + exists_in_registry = ttkstyle in self._style_registry + return exists_in_theme and exists_in_registry + + @staticmethod + def get_instance(): + """Returns and instance of the style class""" + return Style.instance + + @staticmethod + def _get_builder(): + """Get the object that builds the widget styles for the current + theme. + + Returns: + + ThemeBuilderTTK: + The theme builder object that builds the ttk styles for + the current theme. + """ + style: Style = Style.get_instance() + theme_name = style.theme.name + return style._theme_objects[theme_name] + + @staticmethod + def _get_builder_tk(): + """Get the object that builds the widget styles for the current + theme. + + Returns: + + ThemeBuilderTK: + The theme builder object that builds the ttk styles for + the current theme. + """ + builder = Style._get_builder() + return builder.builder_tk + + def _build_configure(self, style, **kw): + """Calls configure of superclass; used by style builder classes.""" + super().configure(style, **kw) + + def _load_themes(self): + """Load all ttkbootstrap defined themes""" + # create a theme definition object for each theme, this will be + # used to generate the theme in tkinter along with any assets + # at run-time + if USER_THEMES: + STANDARD_THEMES.update(USER_THEMES) + theme_settings = {"themes": STANDARD_THEMES} + for name, definition in theme_settings["themes"].items(): + self.register_theme( + ThemeDefinition( + name=name, + themetype=definition["type"], + colors=definition["colors"], + ) + ) + + def _register_ttkstyle(self, ttkstyle): + """Register that a ttk style name. This ensures that the + builder will not attempt to build a style that has already + been created. + + Parameters: + + ttkstyle (str): + The name of the ttk style to register. + """ + self._style_registry.add(ttkstyle) + theme = self.theme.name + self._theme_styles[theme].add(ttkstyle) + + def _create_ttk_styles_on_theme_change(self): + """Create existing styles when the theme changes""" + for ttkstyle in self._style_registry: + if not self.style_exists_in_theme(ttkstyle): + color = Bootstyle.ttkstyle_widget_color(ttkstyle) + method_name = Bootstyle.ttkstyle_method_name(string=ttkstyle) + builder: StyleBuilderTTK = self._get_builder() + method: Callable = builder.name_to_method(method_name) + method(builder, color) + + def load_user_themes(self, file): + """Load user themes saved in json format""" + with open(file, encoding='utf-8') as f: + data = json.load(f) + themes = data['themes'] + for theme in themes: + for name, definition in theme.items(): + self.register_theme( + ThemeDefinition( + name=name, + themetype=definition["type"], + colors=definition["colors"], + ) + ) + + +class StyleBuilderTK: + """A class for styling legacy tkinter widgets (not ttk). + + The methods in this classed are used internally to update tk widget + style configurations and are not intended to be called by the end + user. + + All legacy tkinter widgets are updated with a callback whenever the + theme is changed. The color configuration of the widget is updated + to match the current theme. Legacy ttk widgets are not the primary + focus of this library, however, an attempt was made to make sure they + did not stick out amongst ttk widgets if used. + + Some ttk widgets contain legacy components that must be updated + such as the Combobox popdown, so this ensures they are styled + completely to match the current theme. + """ + + def __init__(self): + self.style = Style.get_instance() + self.master = self.style.master + + @property + def theme(self) -> ThemeDefinition: + """A reference to the `ThemeDefinition` object for the current + theme.""" + return self.style.theme + + @property + def colors(self) -> Colors: + """A reference to the `Colors` object for the current theme.""" + return self.style.colors + + @property + def is_light_theme(self) -> bool: + """Returns `True` if the theme is _light_, otherwise `False`.""" + return self.style.theme.type == LIGHT + + def update_tk_style(self, widget: tk.Tk): + """Update the window style. + + Parameters: + + widget (tkinter.Tk): + The tk object to update. + """ + widget.configure(background=self.colors.bg) + # add default initial font for text widget + widget.option_add('*Text*Font', 'TkDefaultFont') + + def update_toplevel_style(self, widget: tk.Toplevel): + """Update the toplevel style. + + Parameters: + + widget (tkinter.Toplevel): + The toplevel object to update. + """ + widget.configure(background=self.colors.bg) + + def update_canvas_style(self, widget: tk.Canvas): + """Update the canvas style. + + Parameters: + + widget (tkinter.Canvas): + The canvas object to update. + """ + # if self.is_light_theme: + # bordercolor = self.colors.border + # else: + # bordercolor = self.colors.selectbg + + widget.configure( + background=self.colors.bg, + highlightthickness=0, + # highlightbackground=bordercolor, + ) + + def update_button_style(self, widget: tk.Button): + """Update the button style. + + Parameters: + + widget (tkinter.Button): + The button object to update. + """ + background = self.colors.primary + foreground = self.colors.selectfg + activebackground = Colors.update_hsv(self.colors.primary, vd=-0.1) + + widget.configure( + background=background, + foreground=foreground, + relief=tk.FLAT, + borderwidth=0, + activebackground=activebackground, + highlightbackground=self.colors.selectfg, + ) + + def update_label_style(self, widget: tk.Label): + """Update the label style. + + Parameters: + + widget (tkinter.Label): + The label object to update. + """ + widget.configure(foreground=self.colors.fg, background=self.colors.bg) + + def update_frame_style(self, widget: tk.Frame): + """Update the frame style. + + Parameters: + + widget (tkinter.Frame): + The frame object to update. + """ + widget.configure(background=self.colors.bg) + + def update_checkbutton_style(self, widget: tk.Checkbutton): + """Update the checkbutton style. + + Parameters: + + widget (tkinter.Checkbutton): + The checkbutton object to update. + """ + widget.configure( + activebackground=self.colors.bg, + activeforeground=self.colors.primary, + background=self.colors.bg, + foreground=self.colors.fg, + selectcolor=self.colors.bg, + ) + + def update_radiobutton_style(self, widget: tk.Radiobutton): + """Update the radiobutton style. + + Parameters: + + widget (tkinter.Radiobutton): + The radiobutton object to update. + """ + widget.configure( + activebackground=self.colors.bg, + activeforeground=self.colors.primary, + background=self.colors.bg, + foreground=self.colors.fg, + selectcolor=self.colors.bg, + ) + + def update_entry_style(self, widget: tk.Entry): + """Update the entry style. + + Parameters: + + widget (tkinter.Entry): + The entry object to update. + """ + if self.is_light_theme: + bordercolor = self.colors.border + else: + bordercolor = self.colors.selectbg + + widget.configure( + relief=tk.FLAT, + highlightthickness=1, + foreground=self.colors.inputfg, + highlightbackground=bordercolor, + highlightcolor=self.colors.primary, + background=self.colors.inputbg, + insertbackground=self.colors.inputfg, + insertwidth=1, + ) + + def update_scale_style(self, widget: tk.Scale): + """Update the scale style. + + Parameters: + + widget (tkinter.scale): + The scale object to update. + """ + if self.is_light_theme: + bordercolor = self.colors.border + else: + bordercolor = self.colors.selectbg + + activecolor = Colors.update_hsv(self.colors.primary, vd=-0.2) + widget.configure( + background=self.colors.primary, + showvalue=False, + sliderrelief=tk.FLAT, + borderwidth=0, + activebackground=activecolor, + highlightthickness=1, + highlightcolor=bordercolor, + highlightbackground=bordercolor, + troughcolor=self.colors.inputbg, + ) + + def update_spinbox_style(self, widget: tk.Spinbox): + """Update the spinbox style. + + Parameters: + + widget (tkinter.Spinbox): + THe spinbox object to update. + """ + if self.is_light_theme: + bordercolor = self.colors.border + else: + bordercolor = self.colors.selectbg + + widget.configure( + relief=tk.FLAT, + highlightthickness=1, + foreground=self.colors.inputfg, + highlightbackground=bordercolor, + highlightcolor=self.colors.primary, + background=self.colors.inputbg, + buttonbackground=self.colors.inputbg, + insertbackground=self.colors.inputfg, + insertwidth=1, + # these options should work, but do not have any affect + buttonuprelief=tk.FLAT, + buttondownrelief=tk.SUNKEN, + ) + + def update_listbox_style(self, widget: tk.Listbox): + """Update the listbox style. + + Parameters: + + widget (tkinter.Listbox): + The listbox object to update. + """ + if self.is_light_theme: + bordercolor = self.colors.border + else: + bordercolor = self.colors.selectbg + + widget.configure( + foreground=self.colors.inputfg, + background=self.colors.inputbg, + selectbackground=self.colors.selectbg, + selectforeground=self.colors.selectfg, + highlightcolor=self.colors.primary, + highlightbackground=bordercolor, + highlightthickness=1, + activestyle="none", + relief=tk.FLAT, + ) + + def update_menubutton_style(self, widget: tk.Menubutton): + """Update the menubutton style. + + Parameters: + + widget (tkinter.Menubutton): + The menubutton object to update. + """ + activebackground = Colors.update_hsv(self.colors.primary, vd=-0.2) + widget.configure( + background=self.colors.primary, + foreground=self.colors.selectfg, + activebackground=activebackground, + activeforeground=self.colors.selectfg, + borderwidth=0, + ) + + def update_menu_style(self, widget: tk.Menu): + """Update the menu style. + + Parameters: + + widget (tkinter.Menu): + The menu object to update. + """ + widget.configure( + tearoff=False, + activebackground=self.colors.selectbg, + activeforeground=self.colors.selectfg, + foreground=self.colors.fg, + selectcolor=self.colors.primary, + background=self.colors.bg, + relief=tk.FLAT, + borderwidth=0, + ) + + def update_labelframe_style(self, widget: tk.LabelFrame): + """Update the labelframe style. + + Parameters: + + widget (tkinter.LabelFrame): + The labelframe object to update. + """ + if self.is_light_theme: + bordercolor = self.colors.border + else: + bordercolor = self.colors.selectbg + + widget.configure( + highlightcolor=bordercolor, + foreground=self.colors.fg, + borderwidth=1, + highlightthickness=0, + background=self.colors.bg, + ) + + def update_text_style(self, widget: tk.Text): + """Update the text style. + + Parameters: + + widget (tkinter.Text): + The text object to update. + """ + if self.is_light_theme: + bordercolor = self.colors.border + else: + bordercolor = self.colors.selectbg + + focuscolor = widget.cget("highlightbackground") + + if focuscolor in ["SystemButtonFace", bordercolor]: + focuscolor = bordercolor + + widget.configure( + background=self.colors.inputbg, + foreground=self.colors.inputfg, + highlightcolor=focuscolor, + highlightbackground=bordercolor, + insertbackground=self.colors.inputfg, + selectbackground=self.colors.selectbg, + selectforeground=self.colors.selectfg, + insertwidth=1, + highlightthickness=1, + relief=tk.FLAT, + padx=5, + pady=5, + #font="TkDefaultFont", + ) + + +class StyleBuilderTTK: + """A class containing methods for building new ttk widget styles on + demand. + + The methods in this classed are used internally to generate ttk + widget styles on-demand and are not intended to be called by the end + user. + """ + + def __init__(self): + self.style: Style = Style.get_instance() + self.theme_images = {} + self.builder_tk = StyleBuilderTK() + self.create_theme() + + @staticmethod + def name_to_method(method_name): + """Get a method by name. + + Parameters: + + method_name (str): + The name of the style builder method. + + Returns: + + Callable: + The method that is named by `method_name` + """ + func = getattr(StyleBuilderTTK, method_name) + return func + + @property + def colors(self) -> Colors: + """A reference to the `Colors` object of the current theme.""" + return self.style.theme.colors + + @property + def theme(self) -> ThemeDefinition: + """A reference to the `ThemeDefinition` object for the current + theme.""" + return self.style.theme + + @property + def is_light_theme(self) -> bool: + """If the current theme is _light_, returns `True`, otherwise + returns `False`.""" + return self.style.theme.type == LIGHT + + def scale_size(self, size): + """Scale the size of images and other assets based on the + scaling factor of ttk to ensure that the image matches the + screen resolution. + + Parameters: + + size (Union[int, List, Tuple]): + A single integer or an iterable of integers + """ + winsys = self.style.master.tk.call("tk", "windowingsystem") + if winsys == "aqua": + BASELINE = 1.000492368291482 + else: + BASELINE = 1.33398982438864281 + scaling = self.style.master.tk.call("tk", "scaling") + factor = scaling / BASELINE + + if isinstance(size, int) or isinstance(size, float): + return ceil(size * factor) + elif isinstance(size, tuple) or isinstance(size, list): + return [ceil(x * factor) for x in size] + + def create_theme(self): + """Create and style a new ttk theme. A wrapper around internal + style methods. + """ + self.style.theme_create(self.theme.name, TTK_CLAM) + ttk.Style.theme_use(self.style, self.theme.name) + self.update_ttk_theme_settings() + + def update_ttk_theme_settings(self): + """This method is called internally every time the theme is + changed to update various components included in the body of + the method.""" + self.create_default_style() + + def create_default_style(self): + """Setup the default widget style configuration for the root + ttk style "."; these defaults are applied to any widget that + contains the configuration options updated by this style. This + method should be called *first* before any other style is applied + during theme creation. + """ + self.style._build_configure( + style=".", + background=self.colors.bg, + darkcolor=self.colors.border, + foreground=self.colors.fg, + troughcolor=self.colors.bg, + selectbg=self.colors.selectbg, + selectfg=self.colors.selectfg, + selectforeground=self.colors.selectfg, + selectbackground=self.colors.selectbg, + fieldbg="white", + borderwidth=1, + focuscolor="", + ) + # this is general style applied to the tableview + self.create_link_button_style() + self.style.configure("symbol.Link.TButton", font="-size 16") + + def create_combobox_style(self, colorname=DEFAULT): + """Create a style for the ttk.Combobox widget. + + Parameters: + + colorname (str): + The color label to use as the primary widget color. + """ + STYLE = "TCombobox" + + if self.is_light_theme: + disabled_fg = self.colors.border + bordercolor = self.colors.border + readonly = self.colors.light + else: + disabled_fg = self.colors.selectbg + bordercolor = self.colors.selectbg + readonly = bordercolor + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + element = f"{ttkstyle.replace('TC','C')}" + focuscolor = self.colors.primary + else: + ttkstyle = f"{colorname}.{STYLE}" + element = f"{ttkstyle.replace('TC','C')}" + focuscolor = self.colors.get(colorname) + + self.style.element_create(f"{element}.downarrow", "from", TTK_DEFAULT) + self.style.element_create(f"{element}.padding", "from", TTK_CLAM) + self.style.element_create(f"{element}.textarea", "from", TTK_CLAM) + + if all([colorname, colorname != DEFAULT]): + bordercolor = focuscolor + + self.style._build_configure( + ttkstyle, + bordercolor=bordercolor, + darkcolor=self.colors.inputbg, + lightcolor=self.colors.inputbg, + arrowcolor=self.colors.inputfg, + foreground=self.colors.inputfg, + fieldbackground=self.colors.inputbg, + background=self.colors.inputbg, + insertcolor=self.colors.inputfg, + relief=tk.FLAT, + padding=5, + arrowsize=self.scale_size(12), + ) + self.style.map( + ttkstyle, + background=[("readonly", readonly)], + fieldbackground=[("readonly", readonly)], + foreground=[("disabled", disabled_fg)], + bordercolor=[ + ("invalid", self.colors.danger), + ("focus !disabled", focuscolor), + ("hover !disabled", focuscolor), + ], + lightcolor=[ + ("focus invalid", self.colors.danger), + ("focus !disabled", focuscolor), + ("pressed !disabled", focuscolor), + ("readonly", readonly), + ], + darkcolor=[ + ("focus invalid", self.colors.danger), + ("focus !disabled", focuscolor), + ("pressed !disabled", focuscolor), + ("readonly", readonly), + ], + arrowcolor=[ + ("disabled", disabled_fg), + ("pressed !disabled", focuscolor), + ("focus !disabled", focuscolor), + ("hover !disabled", focuscolor), + ], + ) + self.style.layout( + ttkstyle, + [ + ( + "combo.Spinbox.field", + { + "side": tk.TOP, + "sticky": tk.EW, + "children": [ + ( + "Combobox.downarrow", + {"side": tk.RIGHT, "sticky": tk.NS}, + ), + ( + "Combobox.padding", + { + "expand": "1", + "sticky": tk.NSEW, + "children": [ + ( + "Combobox.textarea", + {"sticky": tk.NSEW}, + ) + ], + }, + ), + ], + }, + ) + ], + ) + self.style._register_ttkstyle(ttkstyle) + + def create_separator_style(self, colorname=DEFAULT): + """Create a style for the ttk.Separator widget. + + Parameters: + + colorname (str): + The primary widget color. + """ + HSTYLE = "Horizontal.TSeparator" + VSTYLE = "Vertical.TSeparator" + + hsize = [40, 1] + vsize = [1, 40] + + # style colors + if self.is_light_theme: + default_color = self.colors.border + else: + default_color = self.colors.selectbg + + if any([colorname == DEFAULT, colorname == ""]): + background = default_color + h_ttkstyle = HSTYLE + v_ttkstyle = VSTYLE + else: + background = self.colors.get(colorname) + h_ttkstyle = f"{colorname}.{HSTYLE}" + v_ttkstyle = f"{colorname}.{VSTYLE}" + + # horizontal separator + h_element = h_ttkstyle.replace(".TS", ".S") + h_img = ImageTk.PhotoImage(Image.new("RGB", hsize, background)) + h_name = util.get_image_name(h_img) + self.theme_images[h_name] = h_img + + self.style.element_create(f"{h_element}.separator", "image", h_name) + self.style.layout( + h_ttkstyle, [(f"{h_element}.separator", {"sticky": tk.EW})] + ) + + # vertical separator + v_element = v_ttkstyle.replace(".TS", ".S") + v_img = ImageTk.PhotoImage(Image.new("RGB", vsize, background)) + v_name = util.get_image_name(v_img) + self.theme_images[v_name] = v_img + self.style.element_create(f"{v_element}.separator", "image", v_name) + self.style.layout( + v_ttkstyle, [(f"{v_element}.separator", {"sticky": tk.NS})] + ) + self.style._register_ttkstyle(h_ttkstyle) + self.style._register_ttkstyle(v_ttkstyle) + + def create_striped_progressbar_assets(self, thickness, colorname=DEFAULT): + """Create the striped progressbar image and return as a + `PhotoImage` + + Parameters: + + colorname (str): + The color label used to style the widget. + + Returns: + + Tuple[str]: + A list of photoimage names. + """ + if any([colorname == DEFAULT, colorname == ""]): + barcolor = self.colors.primary + else: + barcolor = self.colors.get(colorname) + + # calculate value of the light color + brightness = Colors.rgb_to_hsv(*Colors.hex_to_rgb(barcolor))[2] + if brightness < 0.4: + value_delta = 0.3 + elif brightness > 0.8: + value_delta = 0 + else: + value_delta = 0.1 + + barcolor_light = Colors.update_hsv(barcolor, sd=-0.2, vd=value_delta) + + # horizontal progressbar + img = Image.new("RGBA", (100, 100), barcolor_light) + draw = ImageDraw.Draw(img) + draw.polygon( + xy=[(0, 0), (48, 0), (100, 52), (100, 100)], + fill=barcolor, + ) + draw.polygon(xy=[(0, 52), (48, 100), (0, 100)], fill=barcolor) + + _resized = img.resize((thickness, thickness), Image.LANCZOS) + h_img = ImageTk.PhotoImage(_resized) + h_name = h_img._PhotoImage__photo.name + v_img = ImageTk.PhotoImage(_resized.rotate(90)) + v_name = v_img._PhotoImage__photo.name + + self.theme_images[h_name] = h_img + self.theme_images[v_name] = v_img + return h_name, v_name + + def create_striped_progressbar_style(self, colorname=DEFAULT): + """Create a striped style for the ttk.Progressbar widget. + + Parameters: + + colorname (str): + The primary widget color label. + """ + HSTYLE = "Striped.Horizontal.TProgressbar" + VSTYLE = "Striped.Vertical.TProgressbar" + + thickness = self.scale_size(12) + + if any([colorname == DEFAULT, colorname == ""]): + h_ttkstyle = HSTYLE + v_ttkstyle = VSTYLE + else: + h_ttkstyle = f"{colorname}.{HSTYLE}" + v_ttkstyle = f"{colorname}.{VSTYLE}" + + if self.is_light_theme: + if colorname == LIGHT: + troughcolor = self.colors.bg + bordercolor = self.colors.light + else: + troughcolor = self.colors.light + bordercolor = troughcolor + else: + troughcolor = Colors.update_hsv(self.colors.selectbg, vd=-0.2) + bordercolor = troughcolor + + # ( horizontal, vertical ) + images = self.create_striped_progressbar_assets(thickness, colorname) + + # horizontal progressbar + h_element = h_ttkstyle.replace(".TP", ".P") + self.style.element_create( + f"{h_element}.pbar", + "image", + images[0], + width=thickness, + sticky=tk.EW, + ) + self.style.layout( + h_ttkstyle, + [ + ( + f"{h_element}.trough", + { + "sticky": tk.NSEW, + "children": [ + ( + f"{h_element}.pbar", + {"side": tk.LEFT, "sticky": tk.NS}, + ) + ], + }, + ) + ], + ) + self.style._build_configure( + h_ttkstyle, + troughcolor=troughcolor, + thickness=thickness, + bordercolor=bordercolor, + borderwidth=1, + ) + + # vertical progressbar + v_element = v_ttkstyle.replace(".TP", ".P") + self.style.element_create( + f"{v_element}.pbar", + "image", + images[1], + width=thickness, + sticky=tk.NS, + ) + self.style.layout( + v_ttkstyle, + [ + ( + f"{v_element}.trough", + { + "sticky": tk.NSEW, + "children": [ + ( + f"{v_element}.pbar", + {"side": tk.BOTTOM, "sticky": tk.EW}, + ) + ], + }, + ) + ], + ) + self.style._build_configure( + v_ttkstyle, + troughcolor=troughcolor, + bordercolor=bordercolor, + thickness=thickness, + borderwidth=1, + ) + self.style._register_ttkstyle(h_ttkstyle) + self.style._register_ttkstyle(v_ttkstyle) + + def create_progressbar_style(self, colorname=DEFAULT): + """Create a solid ttk style for the ttk.Progressbar widget. + + Parameters: + + colorname (str): + The primary widget color. + """ + H_STYLE = "Horizontal.TProgressbar" + V_STYLE = "Vertical.TProgressbar" + + thickness = self.scale_size(10) + + if self.is_light_theme: + if colorname == LIGHT: + troughcolor = self.colors.bg + bordercolor = self.colors.light + else: + troughcolor = self.colors.light + bordercolor = troughcolor + else: + troughcolor = Colors.update_hsv(self.colors.selectbg, vd=-0.2) + bordercolor = troughcolor + + if any([colorname == DEFAULT, colorname == ""]): + background = self.colors.primary + h_ttkstyle = H_STYLE + v_ttkstyle = V_STYLE + else: + background = self.colors.get(colorname) + h_ttkstyle = f"{colorname}.{H_STYLE}" + v_ttkstyle = f"{colorname}.{V_STYLE}" + + self.style._build_configure( + h_ttkstyle, + thickness=thickness, + borderwidth=1, + bordercolor=bordercolor, + lightcolor=self.colors.border, + pbarrelief=tk.FLAT, + troughcolor=troughcolor, + ) + existing_elements = self.style.element_names() + + self.style._build_configure( + v_ttkstyle, + thickness=thickness, + borderwidth=1, + bordercolor=bordercolor, + lightcolor=self.colors.border, + pbarrelief=tk.FLAT, + troughcolor=troughcolor, + ) + existing_elements = self.style.element_names() + + # horizontal progressbar + h_element = h_ttkstyle.replace(".TP", ".P") + trough_element = f"{h_element}.trough" + pbar_element = f"{h_element}.pbar" + if trough_element not in existing_elements: + self.style.element_create(trough_element, "from", TTK_CLAM) + self.style.element_create(pbar_element, "from", TTK_DEFAULT) + + self.style.layout( + h_ttkstyle, + [ + ( + trough_element, + { + "sticky": "nswe", + "children": [ + (pbar_element, {"side": "left", "sticky": "ns"}) + ], + }, + ) + ], + ) + self.style._build_configure(h_ttkstyle, background=background) + + # vertical progressbar + v_element = v_ttkstyle.replace(".TP", ".P") + trough_element = f"{v_element}.trough" + pbar_element = f"{v_element}.pbar" + if trough_element not in existing_elements: + self.style.element_create(trough_element, "from", TTK_CLAM) + self.style.element_create(pbar_element, "from", TTK_DEFAULT) + self.style._build_configure(v_ttkstyle, background=background) + self.style.layout( + v_ttkstyle, + [ + ( + trough_element, + { + "sticky": "nswe", + "children": [ + (pbar_element, {"side": "bottom", "sticky": "we"}) + ], + }, + ) + ], + ) + + # register ttkstyles + self.style._register_ttkstyle(h_ttkstyle) + self.style._register_ttkstyle(v_ttkstyle) + + def create_scale_assets(self, colorname=DEFAULT, size=14): + """Create the assets used for the ttk.Scale widget. + + The slider handle is automatically adjusted to fit the + screen resolution. + + Parameters: + + colorname (str): + The color label. + + size (int): + The size diameter of the slider circle; default=16. + + Returns: + + Tuple[str]: + A tuple of PhotoImage names to be used in the image + layout when building the style. + """ + size = self.scale_size(size) + if self.is_light_theme: + disabled_color = self.colors.border + if colorname == LIGHT: + track_color = self.colors.bg + else: + track_color = self.colors.light + else: + disabled_color = self.colors.selectbg + track_color = Colors.update_hsv(self.colors.selectbg, vd=-0.2) + + if any([colorname == DEFAULT, colorname == ""]): + normal_color = self.colors.primary + else: + normal_color = self.colors.get(colorname) + + pressed_color = Colors.update_hsv(normal_color, vd=-0.1) + hover_color = Colors.update_hsv(normal_color, vd=0.1) + + # normal state + _normal = Image.new("RGBA", (100, 100)) + draw = ImageDraw.Draw(_normal) + draw.ellipse((0, 0, 95, 95), fill=normal_color) + normal_img = ImageTk.PhotoImage( + _normal.resize((size, size), Image.LANCZOS) + ) + normal_name = util.get_image_name(normal_img) + self.theme_images[normal_name] = normal_img + + # pressed state + _pressed = Image.new("RGBA", (100, 100)) + draw = ImageDraw.Draw(_pressed) + draw.ellipse((0, 0, 95, 95), fill=pressed_color) + pressed_img = ImageTk.PhotoImage( + _pressed.resize((size, size), Image.LANCZOS) + ) + pressed_name = util.get_image_name(pressed_img) + self.theme_images[pressed_name] = pressed_img + + # hover state + _hover = Image.new("RGBA", (100, 100)) + draw = ImageDraw.Draw(_hover) + draw.ellipse((0, 0, 95, 95), fill=hover_color) + hover_img = ImageTk.PhotoImage( + _hover.resize((size, size), Image.LANCZOS) + ) + hover_name = util.get_image_name(hover_img) + self.theme_images[hover_name] = hover_img + + # disabled state + _disabled = Image.new("RGBA", (100, 100)) + draw = ImageDraw.Draw(_disabled) + draw.ellipse((0, 0, 95, 95), fill=disabled_color) + disabled_img = ImageTk.PhotoImage( + _disabled.resize((size, size), Image.LANCZOS) + ) + disabled_name = util.get_image_name(disabled_img) + self.theme_images[disabled_name] = disabled_img + + # vertical track + h_track_img = ImageTk.PhotoImage( + Image.new("RGB", self.scale_size((40, 5)), track_color) + ) + h_track_name = util.get_image_name(h_track_img) + self.theme_images[h_track_name] = h_track_img + + # horizontal track + v_track_img = ImageTk.PhotoImage( + Image.new("RGB", self.scale_size((5, 40)), track_color) + ) + v_track_name = util.get_image_name(v_track_img) + self.theme_images[v_track_name] = v_track_img + + return ( + normal_name, + pressed_name, + hover_name, + disabled_name, + h_track_name, + v_track_name, + ) + + def create_scale_style(self, colorname=DEFAULT): + """Create a style for the ttk.Scale widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TScale" + + if any([colorname == DEFAULT, colorname == ""]): + h_ttkstyle = f"Horizontal.{STYLE}" + v_ttkstyle = f"Vertical.{STYLE}" + else: + h_ttkstyle = f"{colorname}.Horizontal.{STYLE}" + v_ttkstyle = f"{colorname}.Vertical.{STYLE}" + + # ( normal, pressed, hover, disabled, htrack, vtrack ) + images = self.create_scale_assets(colorname) + + # horizontal scale + h_element = h_ttkstyle.replace(".TS", ".S") + self.style.element_create( + f"{h_element}.slider", + "image", + images[0], + ("disabled", images[3]), + ("pressed", images[1]), + ("hover", images[2]), + ) + self.style.element_create(f"{h_element}.track", "image", images[4]) + self.style.layout( + h_ttkstyle, + [ + ( + f"{h_element}.focus", + { + "expand": "1", + "sticky": tk.NSEW, + "children": [ + (f"{h_element}.track", {"sticky": tk.EW}), + ( + f"{h_element}.slider", + {"side": tk.LEFT, "sticky": ""}, + ), + ], + }, + ) + ], + ) + # vertical scale + v_element = v_ttkstyle.replace(".TS", ".S") + self.style.element_create( + f"{v_element}.slider", + "image", + images[0], + ("disabled", images[3]), + ("pressed", images[1]), + ("hover", images[2]), + ) + self.style.element_create(f"{v_element}.track", "image", images[5]) + self.style.layout( + v_ttkstyle, + [ + ( + f"{v_element}.focus", + { + "expand": "1", + "sticky": tk.NSEW, + "children": [ + (f"{v_element}.track", {"sticky": tk.NS}), + ( + f"{v_element}.slider", + {"side": tk.TOP, "sticky": ""}, + ), + ], + }, + ) + ], + ) + # register ttkstyles + self.style._register_ttkstyle(h_ttkstyle) + self.style._register_ttkstyle(v_ttkstyle) + + def create_floodgauge_style(self, colorname=DEFAULT): + """Create a ttk style for the ttkbootstrap.widgets.Floodgauge + widget. This is a custom widget style that uses components of + the progressbar and label. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + HSTYLE = "Horizontal.TFloodgauge" + VSTYLE = "Vertical.TFloodgauge" + FLOOD_FONT = "-size 14" + + if any([colorname == DEFAULT, colorname == ""]): + h_ttkstyle = HSTYLE + v_ttkstyle = VSTYLE + background = self.colors.primary + else: + h_ttkstyle = f"{colorname}.{HSTYLE}" + v_ttkstyle = f"{colorname}.{VSTYLE}" + background = self.colors.get(colorname) + + if colorname == LIGHT: + foreground = self.colors.fg + troughcolor = self.colors.bg + else: + troughcolor = Colors.update_hsv(background, sd=-0.3, vd=0.8) + foreground = self.colors.selectfg + + # horizontal floodgauge + h_element = h_ttkstyle.replace(".TF", ".F") + self.style.element_create(f"{h_element}.trough", "from", TTK_CLAM) + self.style.element_create(f"{h_element}.pbar", "from", TTK_DEFAULT) + self.style.layout( + h_ttkstyle, + [ + ( + f"{h_element}.trough", + { + "children": [ + (f"{h_element}.pbar", {"sticky": tk.NS}), + ("Floodgauge.label", {"sticky": ""}), + ], + "sticky": tk.NSEW, + }, + ) + ], + ) + self.style._build_configure( + h_ttkstyle, + thickness=50, + borderwidth=1, + bordercolor=background, + lightcolor=background, + pbarrelief=tk.FLAT, + troughcolor=troughcolor, + background=background, + foreground=foreground, + justify=tk.CENTER, + anchor=tk.CENTER, + font=FLOOD_FONT, + ) + # vertical floodgauge + v_element = v_ttkstyle.replace(".TF", ".F") + self.style.element_create(f"{v_element}.trough", "from", TTK_CLAM) + self.style.element_create(f"{v_element}.pbar", "from", TTK_DEFAULT) + self.style.layout( + v_ttkstyle, + [ + ( + f"{v_element}.trough", + { + "children": [ + (f"{v_element}.pbar", {"sticky": tk.EW}), + ("Floodgauge.label", {"sticky": ""}), + ], + "sticky": tk.NSEW, + }, + ) + ], + ) + self.style._build_configure( + v_ttkstyle, + thickness=50, + borderwidth=1, + bordercolor=background, + lightcolor=background, + pbarrelief=tk.FLAT, + troughcolor=troughcolor, + background=background, + foreground=foreground, + justify=tk.CENTER, + anchor=tk.CENTER, + font=FLOOD_FONT, + ) + # register ttkstyles + self.style._register_ttkstyle(h_ttkstyle) + self.style._register_ttkstyle(v_ttkstyle) + + def create_arrow_assets(self, arrowcolor, pressed, active): + """Create arrow assets used for various widget buttons. + + !!! note + This method is currently not being utilized. + + Parameters: + + arrowcolor (str): + The color value to use as the arrow fill color. + + pressed (str): + The color value to use when the arrow is pressed. + + active (str): + The color value to use when the arrow is active or + hovered. + """ + + def draw_arrow(color: str): + + img = Image.new("RGBA", (11, 11)) + draw = ImageDraw.Draw(img) + size = self.scale_size([11, 11]) + + draw.line([2, 6, 2, 9], fill=color) + draw.line([3, 5, 3, 8], fill=color) + draw.line([4, 4, 4, 7], fill=color) + draw.line([5, 3, 5, 6], fill=color) + draw.line([6, 4, 6, 7], fill=color) + draw.line([7, 5, 7, 8], fill=color) + draw.line([8, 6, 8, 9], fill=color) + + img = img.resize(size, Image.BICUBIC) + + up_img = ImageTk.PhotoImage(img) + up_name = util.get_image_name(up_img) + self.theme_images[up_name] = up_img + + down_img = ImageTk.PhotoImage(img.rotate(180)) + down_name = util.get_image_name(down_img) + self.theme_images[down_name] = down_img + + left_img = ImageTk.PhotoImage(img.rotate(90)) + left_name = util.get_image_name(left_img) + self.theme_images[left_name] = left_img + + right_img = ImageTk.PhotoImage(img.rotate(-90)) + right_name = util.get_image_name(right_img) + self.theme_images[right_name] = right_img + + return up_name, down_name, left_name, right_name + + normal_names = draw_arrow(arrowcolor) + pressed_names = draw_arrow(pressed) + active_names = draw_arrow(active) + + return normal_names, pressed_names, active_names + + def create_round_scrollbar_assets(self, thumbcolor, pressed, active): + """Create image assets to be used when building the round + scrollbar style. + + Parameters: + + thumbcolor (str): + The color value of the thumb in normal state. + + pressed (str): + The color value to use when the thumb is pressed. + + active (str): + The color value to use when the thumb is active or + hovered. + """ + vsize = self.scale_size([9, 28]) + hsize = self.scale_size([28, 9]) + + def rounded_rect(size, fill): + x = size[0] * 10 + y = size[1] * 10 + img = Image.new("RGBA", (x, y)) + draw = ImageDraw.Draw(img) + radius = min([x, y]) // 2 + draw.rounded_rectangle([0, 0, x - 1, y - 1], radius, fill) + image = ImageTk.PhotoImage(img.resize(size, Image.BICUBIC)) + name = util.get_image_name(image) + self.theme_images[name] = image + return name + + # create images + h_normal_img = rounded_rect(hsize, thumbcolor) + h_pressed_img = rounded_rect(hsize, pressed) + h_active_img = rounded_rect(hsize, active) + + v_normal_img = rounded_rect(vsize, thumbcolor) + v_pressed_img = rounded_rect(vsize, pressed) + v_active_img = rounded_rect(vsize, active) + + return ( + h_normal_img, + h_pressed_img, + h_active_img, + v_normal_img, + v_pressed_img, + v_active_img, + ) + + def create_round_scrollbar_style(self, colorname=DEFAULT): + """Create a round style for the ttk.Scrollbar widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TScrollbar" + + if any([colorname == DEFAULT, colorname == ""]): + h_ttkstyle = f"Round.Horizontal.{STYLE}" + v_ttkstyle = f"Round.Vertical.{STYLE}" + + if self.is_light_theme: + background = self.colors.border + else: + background = self.colors.selectbg + + else: + h_ttkstyle = f"{colorname}.Round.Horizontal.{STYLE}" + v_ttkstyle = f"{colorname}.Round.Vertical.{STYLE}" + background = self.colors.get(colorname) + + if self.is_light_theme: + if colorname == LIGHT: + troughcolor = self.colors.bg + else: + troughcolor = self.colors.light + else: + troughcolor = Colors.update_hsv(self.colors.selectbg, vd=-0.2) + + pressed = Colors.update_hsv(background, vd=-0.05) + active = Colors.update_hsv(background, vd=0.05) + + scroll_images = self.create_round_scrollbar_assets( + background, pressed, active + ) + + # horizontal scrollbar + self.style._build_configure( + h_ttkstyle, + troughcolor=troughcolor, + darkcolor=troughcolor, + bordercolor=troughcolor, + lightcolor=troughcolor, + arrowcolor=background, + arrowsize=self.scale_size(11), + background=troughcolor, + relief=tk.FLAT, + borderwidth=0, + ) + self.style.element_create( + f"{h_ttkstyle}.thumb", + "image", + scroll_images[0], + ("pressed", scroll_images[1]), + ("active", scroll_images[2]), + border=self.scale_size(9), + padding=0, + sticky=tk.EW, + ) + self.style.layout( + h_ttkstyle, + [ + ( + "Horizontal.Scrollbar.trough", + { + "sticky": "we", + "children": [ + ( + "Horizontal.Scrollbar.leftarrow", + {"side": "left", "sticky": ""}, + ), + ( + "Horizontal.Scrollbar.rightarrow", + {"side": "right", "sticky": ""}, + ), + ( + f"{h_ttkstyle}.thumb", + {"expand": "1", "sticky": "nswe"}, + ), + ], + }, + ) + ], + ) + self.style._build_configure(h_ttkstyle, arrowcolor=background) + self.style.map( + h_ttkstyle, arrowcolor=[("pressed", pressed), ("active", active)] + ) + + # vertical scrollbar + self.style._build_configure( + v_ttkstyle, + troughcolor=troughcolor, + darkcolor=troughcolor, + bordercolor=troughcolor, + lightcolor=troughcolor, + arrowcolor=background, + arrowsize=self.scale_size(11), + background=troughcolor, + relief=tk.FLAT, + ) + self.style.element_create( + f"{v_ttkstyle}.thumb", + "image", + scroll_images[3], + ("pressed", scroll_images[4]), + ("active", scroll_images[5]), + border=self.scale_size(9), + padding=0, + sticky=tk.NS, + ) + self.style.layout( + v_ttkstyle, + [ + ( + "Vertical.Scrollbar.trough", + { + "sticky": "ns", + "children": [ + ( + "Vertical.Scrollbar.uparrow", + {"side": "top", "sticky": ""}, + ), + ( + "Vertical.Scrollbar.downarrow", + {"side": "bottom", "sticky": ""}, + ), + ( + f"{v_ttkstyle}.thumb", + {"expand": "1", "sticky": "nswe"}, + ), + ], + }, + ) + ], + ) + self.style._build_configure(v_ttkstyle, arrowcolor=background) + self.style.map( + v_ttkstyle, arrowcolor=[("pressed", pressed), ("active", active)] + ) + + # register ttkstyles + self.style._register_ttkstyle(h_ttkstyle) + self.style._register_ttkstyle(v_ttkstyle) + + def create_scrollbar_assets(self, thumbcolor, pressed, active): + """Create the image assets used to build the standard scrollbar + style. + + Parameters: + + thumbcolor (str): + The primary color value used to color the thumb. + + pressed (str): + The color value to use when the thumb is pressed. + + active (str): + The color value to use when the thumb is active or + hovered. + """ + vsize = self.scale_size([9, 28]) + hsize = self.scale_size([28, 9]) + + def draw_rect(size, fill): + x = size[0] * 10 + y = size[1] * 10 + img = Image.new("RGBA", (x, y), fill) + image = ImageTk.PhotoImage(img.resize(size), Image.BICUBIC) + name = util.get_image_name(image) + self.theme_images[name] = image + return name + + # create images + h_normal_img = draw_rect(hsize, thumbcolor) + h_pressed_img = draw_rect(hsize, pressed) + h_active_img = draw_rect(hsize, active) + + v_normal_img = draw_rect(vsize, thumbcolor) + v_pressed_img = draw_rect(vsize, pressed) + v_active_img = draw_rect(vsize, active) + + return ( + h_normal_img, + h_pressed_img, + h_active_img, + v_normal_img, + v_pressed_img, + v_active_img, + ) + + def create_scrollbar_style(self, colorname=DEFAULT): + """Create a standard style for the ttk.Scrollbar widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TScrollbar" + + if any([colorname == DEFAULT, colorname == ""]): + h_ttkstyle = f"Horizontal.{STYLE}" + v_ttkstyle = f"Vertical.{STYLE}" + + if self.is_light_theme: + background = self.colors.border + else: + background = self.colors.selectbg + + else: + h_ttkstyle = f"{colorname}.Horizontal.{STYLE}" + v_ttkstyle = f"{colorname}.Vertical.{STYLE}" + background = self.colors.get(colorname) + + if self.is_light_theme: + if colorname == LIGHT: + troughcolor = self.colors.bg + else: + troughcolor = self.colors.light + else: + troughcolor = Colors.update_hsv(self.colors.selectbg, vd=-0.2) + + pressed = Colors.update_hsv(background, vd=-0.05) + active = Colors.update_hsv(background, vd=0.05) + + scroll_images = self.create_scrollbar_assets( + background, pressed, active + ) + + # horizontal scrollbar + self.style._build_configure( + h_ttkstyle, + troughcolor=troughcolor, + darkcolor=troughcolor, + bordercolor=troughcolor, + lightcolor=troughcolor, + arrowcolor=background, + arrowsize=self.scale_size(11), + background=troughcolor, + relief=tk.FLAT, + borderwidth=0, + ) + self.style.element_create( + f"{h_ttkstyle}.thumb", + "image", + scroll_images[0], + ("pressed", scroll_images[1]), + ("active", scroll_images[2]), + border=(3, 0), + sticky=tk.NSEW, + ) + self.style.layout( + h_ttkstyle, + [ + ( + "Horizontal.Scrollbar.trough", + { + "sticky": "we", + "children": [ + ( + "Horizontal.Scrollbar.leftarrow", + {"side": "left", "sticky": ""}, + ), + ( + "Horizontal.Scrollbar.rightarrow", + {"side": "right", "sticky": ""}, + ), + ( + f"{h_ttkstyle}.thumb", + {"expand": "1", "sticky": "nswe"}, + ), + ], + }, + ) + ], + ) + self.style._build_configure(h_ttkstyle, arrowcolor=background) + self.style.map( + h_ttkstyle, arrowcolor=[("pressed", pressed), ("active", active)] + ) + + # vertical scrollbar + self.style._build_configure( + v_ttkstyle, + troughcolor=troughcolor, + darkcolor=troughcolor, + bordercolor=troughcolor, + lightcolor=troughcolor, + arrowcolor=background, + arrowsize=self.scale_size(11), + background=troughcolor, + relief=tk.FLAT, + borderwidth=0, + ) + self.style.element_create( + f"{v_ttkstyle}.thumb", + "image", + scroll_images[3], + ("pressed", scroll_images[4]), + ("active", scroll_images[5]), + border=(0, 3), + sticky=tk.NSEW, + ) + self.style.layout( + v_ttkstyle, + [ + ( + "Vertical.Scrollbar.trough", + { + "sticky": "ns", + "children": [ + ( + "Vertical.Scrollbar.uparrow", + {"side": "top", "sticky": ""}, + ), + ( + "Vertical.Scrollbar.downarrow", + {"side": "bottom", "sticky": ""}, + ), + ( + f"{v_ttkstyle}.thumb", + {"expand": "1", "sticky": "nswe"}, + ), + ], + }, + ) + ], + ) + self.style._build_configure(v_ttkstyle, arrowcolor=background) + self.style.map( + v_ttkstyle, arrowcolor=[("pressed", pressed), ("active", active)] + ) + + # register ttkstyles + self.style._register_ttkstyle(h_ttkstyle) + self.style._register_ttkstyle(v_ttkstyle) + + def create_spinbox_style(self, colorname=DEFAULT): + """Create a style for the ttk.Spinbox widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TSpinbox" + + if self.is_light_theme: + disabled_fg = self.colors.border + bordercolor = self.colors.border + readonly = self.colors.light + else: + disabled_fg = self.colors.selectbg + bordercolor = self.colors.selectbg + readonly = bordercolor + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + focuscolor = self.colors.primary + else: + ttkstyle = f"{colorname}.{STYLE}" + focuscolor = self.colors.get(colorname) + + if all([colorname, colorname != DEFAULT]): + bordercolor = focuscolor + + if colorname == "light": + arrowfocus = self.colors.fg + else: + arrowfocus = focuscolor + + element = ttkstyle.replace(".TS", ".S") + self.style.element_create(f"{element}.uparrow", "from", TTK_DEFAULT) + self.style.element_create(f"{element}.downarrow", "from", TTK_DEFAULT) + self.style.layout( + ttkstyle, + [ + ( + f"{element}.field", + { + "side": tk.TOP, + "sticky": tk.EW, + "children": [ + ( + "null", + { + "side": tk.RIGHT, + "sticky": "", + "children": [ + ( + f"{element}.uparrow", + {"side": tk.TOP, "sticky": tk.E}, + ), + ( + f"{element}.downarrow", + { + "side": tk.BOTTOM, + "sticky": tk.E, + }, + ), + ], + }, + ), + ( + f"{element}.padding", + { + "sticky": tk.NSEW, + "children": [ + ( + f"{element}.textarea", + {"sticky": tk.NSEW}, + ) + ], + }, + ), + ], + }, + ) + ], + ) + self.style._build_configure( + ttkstyle, + bordercolor=bordercolor, + darkcolor=self.colors.inputbg, + lightcolor=self.colors.inputbg, + fieldbackground=self.colors.inputbg, + foreground=self.colors.inputfg, + borderwidth=0, + background=self.colors.inputbg, + relief=tk.FLAT, + arrowcolor=self.colors.inputfg, + insertcolor=self.colors.inputfg, + arrowsize=self.scale_size(12), + padding=(10, 5), + ) + self.style.map( + ttkstyle, + foreground=[("disabled", disabled_fg)], + fieldbackground=[("readonly", readonly)], + background=[("readonly", readonly)], + lightcolor=[ + ("focus invalid", self.colors.danger), + ("focus !disabled", focuscolor), + ("readonly", readonly), + ], + darkcolor=[ + ("focus invalid", self.colors.danger), + ("focus !disabled", focuscolor), + ("readonly", readonly), + ], + bordercolor=[ + ("invalid", self.colors.danger), + ("focus !disabled", focuscolor), + ("hover !disabled", focuscolor), + ], + arrowcolor=[ + ("disabled !disabled", disabled_fg), + ("pressed !disabled", arrowfocus), + ("hover !disabled", arrowfocus), + ], + ) + # register ttkstyles + self.style._register_ttkstyle(ttkstyle) + + def create_table_treeview_style(self, colorname=DEFAULT): + """Create a style for the Tableview widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "Table.Treeview" + + f = font.nametofont("TkDefaultFont") + rowheight = f.metrics()["linespace"] + + if self.is_light_theme: + disabled_fg = Colors.update_hsv(self.colors.inputbg, vd=-0.2) + bordercolor = self.colors.border + hover = Colors.update_hsv(self.colors.light, vd=-0.1) + else: + disabled_fg = Colors.update_hsv(self.colors.inputbg, vd=-0.3) + bordercolor = self.colors.selectbg + hover = Colors.update_hsv(self.colors.dark, vd=0.1) + + if any([colorname == DEFAULT, colorname == ""]): + background = self.colors.inputbg + foreground = self.colors.inputfg + body_style = STYLE + header_style = f"{STYLE}.Heading" + elif colorname == LIGHT and self.is_light_theme: + background = self.colors.get(colorname) + foreground = self.colors.fg + body_style = f"{colorname}.{STYLE}" + header_style = f"{colorname}.{STYLE}.Heading" + hover = Colors.update_hsv(background, vd=-0.1) + else: + background = self.colors.get(colorname) + foreground = self.colors.selectfg + body_style = f"{colorname}.{STYLE}" + header_style = f"{colorname}.{STYLE}.Heading" + hover = Colors.update_hsv(background, vd=0.1) + + + # treeview header + self.style._build_configure( + header_style, + background=background, + foreground=foreground, + relief=RAISED, + borderwidth=1, + darkcolor=background, + bordercolor=bordercolor, + lightcolor=background, + padding=5, + ) + self.style.map( + header_style, + foreground=[("disabled", disabled_fg)], + background=[ + ("active !disabled", hover), + ], + darkcolor=[ + ("active !disabled", hover), + ], + lightcolor=[ + ("active !disabled", hover), + ], + ) + self.style._build_configure( + body_style, + background=self.colors.inputbg, + fieldbackground=self.colors.inputbg, + foreground=self.colors.inputfg, + bordercolor=bordercolor, + lightcolor=self.colors.inputbg, + darkcolor=self.colors.inputbg, + borderwidth=2, + padding=0, + rowheight=rowheight, + relief=tk.RAISED, + ) + self.style.map( + body_style, + background=[("selected", self.colors.selectbg)], + foreground=[ + ("disabled", disabled_fg), + ("selected", self.colors.selectfg), + ], + ) + self.style.layout( + body_style, + [ + ( + "Button.border", + { + "sticky": tk.NSEW, + "border": "1", + "children": [ + ( + "Treeview.padding", + { + "sticky": tk.NSEW, + "children": [ + ( + "Treeview.treearea", + {"sticky": tk.NSEW}, + ) + ], + }, + ) + ], + }, + ) + ], + ) + # register ttkstyles + self.style._register_ttkstyle(body_style) + + def create_treeview_style(self, colorname=DEFAULT): + """Create a style for the ttk.Treeview widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "Treeview" + + f = font.nametofont("TkDefaultFont") + rowheight = f.metrics()["linespace"] + + if self.is_light_theme: + disabled_fg = Colors.update_hsv(self.colors.inputbg, vd=-0.2) + bordercolor = self.colors.border + else: + disabled_fg = Colors.update_hsv(self.colors.inputbg, vd=-0.3) + bordercolor = self.colors.selectbg + + if any([colorname == DEFAULT, colorname == ""]): + background = self.colors.inputbg + foreground = self.colors.inputfg + body_style = STYLE + header_style = f"{STYLE}.Heading" + focuscolor = self.colors.primary + elif colorname == LIGHT and self.is_light_theme: + background = self.colors.get(colorname) + foreground = self.colors.fg + body_style = f"{colorname}.{STYLE}" + header_style = f"{colorname}.{STYLE}.Heading" + focuscolor = background + bordercolor = focuscolor + else: + background = self.colors.get(colorname) + foreground = self.colors.selectfg + body_style = f"{colorname}.{STYLE}" + header_style = f"{colorname}.{STYLE}.Heading" + focuscolor = background + bordercolor = focuscolor + + # treeview header + self.style._build_configure( + header_style, + background=background, + foreground=foreground, + relief=tk.FLAT, + padding=5, + ) + self.style.map( + header_style, + foreground=[("disabled", disabled_fg)], + bordercolor=[("focus !disabled", background)], + ) + # treeview body + self.style._build_configure( + body_style, + background=self.colors.inputbg, + fieldbackground=self.colors.inputbg, + foreground=self.colors.inputfg, + bordercolor=bordercolor, + lightcolor=self.colors.inputbg, + darkcolor=self.colors.inputbg, + borderwidth=2, + padding=0, + rowheight=rowheight, + relief=tk.RAISED, + ) + self.style.map( + body_style, + background=[("selected", self.colors.selectbg)], + foreground=[ + ("disabled", disabled_fg), + ("selected", self.colors.selectfg), + ], + bordercolor=[ + ("disabled", bordercolor), + ("focus", focuscolor), + ("pressed", focuscolor), + ("hover", focuscolor), + ], + lightcolor=[("focus", focuscolor)], + darkcolor=[("focus", focuscolor)], + ) + self.style.layout( + body_style, + [ + ( + "Button.border", + { + "sticky": tk.NSEW, + "border": "1", + "children": [ + ( + "Treeview.padding", + { + "sticky": tk.NSEW, + "children": [ + ( + "Treeview.treearea", + {"sticky": tk.NSEW}, + ) + ], + }, + ) + ], + }, + ) + ], + ) + + try: + self.style.element_create("Treeitem.indicator", "from", TTK_ALT) + except: + pass + + # register ttkstyles + self.style._register_ttkstyle(body_style) + + def create_frame_style(self, colorname=DEFAULT): + """Create a style for the ttk.Frame widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TFrame" + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + background = self.colors.bg + else: + ttkstyle = f"{colorname}.{STYLE}" + background = self.colors.get(colorname) + + self.style._build_configure(ttkstyle, background=background) + + # register style + self.style._register_ttkstyle(ttkstyle) + + def create_button_style(self, colorname=DEFAULT): + """Create a solid style for the ttk.Button widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TButton" + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + foreground = self.colors.get_foreground(PRIMARY) + background = self.colors.primary + else: + ttkstyle = f"{colorname}.{STYLE}" + foreground = self.colors.get_foreground(colorname) + background = self.colors.get(colorname) + + bordercolor = background + disabled_bg = Colors.make_transparent(0.10, self.colors.fg, self.colors.bg) + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + pressed = Colors.make_transparent(0.80, background, self.colors.bg) + hover = Colors.make_transparent(0.90, background, self.colors.bg) + + self.style._build_configure( + ttkstyle, + foreground=foreground, + background=background, + bordercolor=bordercolor, + darkcolor=background, + lightcolor=background, + relief=tk.RAISED, + focusthickness=0, + focuscolor=foreground, + padding=(10, 5), + anchor=tk.CENTER, + ) + self.style.map( + ttkstyle, + foreground=[("disabled", disabled_fg)], + background=[ + ("disabled", disabled_bg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + bordercolor=[("disabled", disabled_bg)], + darkcolor=[ + ("disabled", disabled_bg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + lightcolor=[ + ("disabled", disabled_bg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_outline_button_style(self, colorname=DEFAULT): + """Create an outline style for the ttk.Button widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "Outline.TButton" + + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + colorname = PRIMARY + else: + ttkstyle = f"{colorname}.{STYLE}" + + foreground = self.colors.get(colorname) + background = self.colors.get_foreground(colorname) + foreground_pressed = background + bordercolor = foreground + pressed = foreground + hover = foreground + + self.style._build_configure( + ttkstyle, + foreground=foreground, + background=self.colors.bg, + bordercolor=bordercolor, + darkcolor=self.colors.bg, + lightcolor=self.colors.bg, + relief=tk.RAISED, + focusthickness=0, + focuscolor=foreground, + padding=(10, 5), + anchor=tk.CENTER, + ) + self.style.map( + ttkstyle, + foreground=[ + ("disabled", disabled_fg), + ("pressed !disabled", foreground_pressed), + ("hover !disabled", foreground_pressed), + ], + background=[ + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + bordercolor=[ + ("disabled", disabled_fg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + focuscolor=[ + ("pressed !disabled", foreground_pressed), + ("hover !disabled", foreground_pressed), + ], + darkcolor=[ + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + lightcolor=[ + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_link_button_style(self, colorname=DEFAULT): + """Create a link button style for the ttk.Button widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "Link.TButton" + + pressed = self.colors.info + hover = self.colors.info + + if any([colorname == DEFAULT, colorname == ""]): + foreground = self.colors.fg + ttkstyle = STYLE + elif colorname == LIGHT: + foreground = self.colors.fg + ttkstyle = f"{colorname}.{STYLE}" + else: + foreground = self.colors.get(colorname) + ttkstyle = f"{colorname}.{STYLE}" + + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + + self.style._build_configure( + ttkstyle, + foreground=foreground, + background=self.colors.bg, + bordercolor=self.colors.bg, + darkcolor=self.colors.bg, + lightcolor=self.colors.bg, + relief=tk.RAISED, + focusthickness=0, + focuscolor=foreground, + anchor=tk.CENTER, + padding=(10, 5), + ) + self.style.map( + ttkstyle, + shiftrelief=[("pressed !disabled", -1)], + foreground=[ + ("disabled", disabled_fg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + focuscolor=[ + ("pressed !disabled", pressed), + ("hover !disabled", pressed), + ], + background=[ + ("disabled", self.colors.bg), + ("pressed !disabled", self.colors.bg), + ("hover !disabled", self.colors.bg), + ], + bordercolor=[ + ("disabled", self.colors.bg), + ("pressed !disabled", self.colors.bg), + ("hover !disabled", self.colors.bg), + ], + darkcolor=[ + ("disabled", self.colors.bg), + ("pressed !disabled", self.colors.bg), + ("hover !disabled", self.colors.bg), + ], + lightcolor=[ + ("disabled", self.colors.bg), + ("pressed !disabled", self.colors.bg), + ("hover !disabled", self.colors.bg), + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_square_toggle_assets(self, colorname=DEFAULT): + """Create the image assets used to build a square toggle + style. + + Parameters: + + colorname (str): + The color label used to style the widget. + + Returns: + + Tuple[str]: + A tuple of PhotoImage names. + """ + size = self.scale_size([24, 15]) + if any([colorname == DEFAULT, colorname == ""]): + colorname = PRIMARY + + # set default style color values + prime_color = self.colors.get(colorname) + on_border = prime_color + on_indicator = self.colors.selectfg + on_fill = prime_color + off_fill = self.colors.bg + disabled_fg = Colors.make_transparent(0.3, self.colors.fg, self.colors.bg) + off_border = Colors.make_transparent(0.4, self.colors.fg, self.colors.bg) + off_indicator = Colors.make_transparent(0.4, self.colors.fg, self.colors.bg) + + # override defaults for light and dark colors + if colorname == LIGHT: + on_border = self.colors.dark + on_indicator = on_border + elif colorname == DARK: + on_border = self.colors.light + on_indicator = on_border + + # toggle off + _off = Image.new("RGBA", (226, 130)) + draw = ImageDraw.Draw(_off) + draw.rectangle( + xy=[1, 1, 225, 129], outline=off_border, width=6, fill=off_fill + ) + draw.rectangle([18, 18, 110, 110], fill=off_indicator) + + off_img = ImageTk.PhotoImage(_off.resize(size, Image.LANCZOS)) + off_name = util.get_image_name(off_img) + self.theme_images[off_name] = off_img + + # toggle on + toggle_on = Image.new("RGBA", (226, 130)) + draw = ImageDraw.Draw(toggle_on) + draw.rectangle( + xy=[1, 1, 225, 129], outline=on_border, width=6, fill=on_fill + ) + draw.rectangle([18, 18, 110, 110], fill=on_indicator) + _on = toggle_on.transpose(Image.ROTATE_180) + on_img = ImageTk.PhotoImage(_on.resize(size, Image.LANCZOS)) + on_name = util.get_image_name(on_img) + self.theme_images[on_name] = on_img + + # toggle disabled + _disabled = Image.new("RGBA", (226, 130)) + draw = ImageDraw.Draw(_disabled) + draw.rectangle([1, 1, 225, 129], outline=disabled_fg, width=6) + draw.rectangle([18, 18, 110, 110], fill=disabled_fg) + disabled_img = ImageTk.PhotoImage( + _disabled.resize(size, Image.LANCZOS) + ) + disabled_name = util.get_image_name(disabled_img) + self.theme_images[disabled_name] = disabled_img + + # toggle on / disabled + toggle_on_disabled = Image.new("RGBA", (226, 130)) + draw = ImageDraw.Draw(toggle_on_disabled) + draw.rectangle( + xy=[1, 1, 225, 129], outline=disabled_fg, width=6, fill=off_fill + ) + draw.rectangle([18, 18, 110, 110], fill=disabled_fg) + _on_disabled = toggle_on_disabled.transpose(Image.ROTATE_180) + on_dis_img = ImageTk.PhotoImage(_on_disabled.resize(size, Image.LANCZOS)) + on_disabled_name = util.get_image_name(on_dis_img) + self.theme_images[on_disabled_name] = on_dis_img + + + return off_name, on_name, disabled_name, on_disabled_name + + def create_toggle_style(self, colorname=DEFAULT): + """Create a round toggle style for the ttk.Checkbutton widget. + + Parameters: + + colorname (str): + """ + self.create_round_toggle_style(colorname) + + def create_round_toggle_assets(self, colorname=DEFAULT): + """Create image assets for the round toggle style. + + Parameters: + + colorname (str): + The color label assigned to the colors property. + + Returns: + + Tuple[str]: + A tuple of PhotoImage names. + """ + size = self.scale_size([24, 15]) + + if any([colorname == DEFAULT, colorname == ""]): + colorname = PRIMARY + + # set default style color values + prime_color = self.colors.get(colorname) + on_border = prime_color + on_indicator = self.colors.selectfg + on_fill = prime_color + off_fill = self.colors.bg + + disabled_fg = Colors.make_transparent(0.3, self.colors.fg, self.colors.bg) + off_border = Colors.make_transparent(0.4, self.colors.fg, self.colors.bg) + off_indicator = Colors.make_transparent(0.4, self.colors.fg, self.colors.bg) + + # override defaults for light and dark colors + if colorname == LIGHT: + on_border = self.colors.dark + on_indicator = on_border + elif colorname == DARK: + on_border = self.colors.light + on_indicator = on_border + + # toggle off + _off = Image.new("RGBA", (226, 130)) + draw = ImageDraw.Draw(_off) + draw.rounded_rectangle( + xy=[1, 1, 225, 129], + radius=(128 / 2), + outline=off_border, + width=6, + fill=off_fill, + ) + draw.ellipse([20, 18, 112, 110], fill=off_indicator) + off_img = ImageTk.PhotoImage(_off.resize(size, Image.LANCZOS)) + off_name = util.get_image_name(off_img) + self.theme_images[off_name] = off_img + + # toggle on + _on = Image.new("RGBA", (226, 130)) + draw = ImageDraw.Draw(_on) + draw.rounded_rectangle( + xy=[1, 1, 225, 129], + radius=(128 / 2), + outline=on_border, + width=6, + fill=on_fill, + ) + draw.ellipse([20, 18, 112, 110], fill=on_indicator) + _on = _on.transpose(Image.ROTATE_180) + on_img = ImageTk.PhotoImage(_on.resize(size, Image.LANCZOS)) + on_name = util.get_image_name(on_img) + self.theme_images[on_name] = on_img + + # toggle on / disabled + _on_disabled = Image.new("RGBA", (226, 130)) + draw = ImageDraw.Draw(_on_disabled) + draw.rounded_rectangle( + xy=[1, 1, 225, 129], + radius=(128 / 2), + outline=disabled_fg, + width=6, + fill=off_fill, + ) + draw.ellipse([20, 18, 112, 110], fill=disabled_fg) + _on_disabled = _on_disabled.transpose(Image.ROTATE_180) + on_dis_img = ImageTk.PhotoImage(_on_disabled.resize(size, Image.LANCZOS)) + on_disabled_name = util.get_image_name(on_dis_img) + self.theme_images[on_disabled_name] = on_dis_img + + # toggle disabled + _disabled = Image.new("RGBA", (226, 130)) + draw = ImageDraw.Draw(_disabled) + draw.rounded_rectangle( + xy=[1, 1, 225, 129], radius=(128 / 2), outline=disabled_fg, width=6 + ) + draw.ellipse([20, 18, 112, 110], fill=disabled_fg) + disabled_img = ImageTk.PhotoImage( + _disabled.resize(size, Image.LANCZOS) + ) + disabled_name = util.get_image_name(disabled_img) + self.theme_images[disabled_name] = disabled_img + + return off_name, on_name, disabled_name, on_disabled_name + + def create_round_toggle_style(self, colorname=DEFAULT): + """Create a round toggle style for the ttk.Checkbutton widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "Round.Toggle" + + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + colorname = PRIMARY + else: + ttkstyle = f"{colorname}.{STYLE}" + + # ( off, on, disabled ) + images = self.create_round_toggle_assets(colorname) + + try: + width = self.scale_size(28) + borderpad = self.scale_size(4) + self.style.element_create( + f"{ttkstyle}.indicator", + "image", + images[1], + ("disabled selected", images[3]), + ("disabled", images[2]), + ("!selected", images[0]), + width=width, + border=borderpad, + sticky=tk.W, + ) + except: + """This method is used as the default Toggle style, so it + is neccessary to catch Tcl Errors when it tries to create + and element that was already created by the Toggle or + Round Toggle style""" + pass + + self.style._build_configure( + ttkstyle, + relief=tk.FLAT, + borderwidth=0, + padding=0, + foreground=self.colors.fg, + background=self.colors.bg, + ) + self.style.map( + ttkstyle, + foreground=[("disabled", disabled_fg)], + background=[("selected", self.colors.bg)], + ) + self.style.layout( + ttkstyle, + [ + ( + "Toolbutton.border", + { + "sticky": tk.NSEW, + "children": [ + ( + "Toolbutton.padding", + { + "sticky": tk.NSEW, + "children": [ + ( + f"{ttkstyle}.indicator", + {"side": tk.LEFT}, + ), + ( + "Toolbutton.label", + {"side": tk.LEFT}, + ), + ], + }, + ) + ], + }, + ) + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_square_toggle_style(self, colorname=DEFAULT): + """Create a square toggle style for the ttk.Checkbutton widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + + STYLE = "Square.Toggle" + + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + else: + ttkstyle = f"{colorname}.{STYLE}" + + # ( off, on, disabled ) + images = self.create_square_toggle_assets(colorname) + + width = self.scale_size(28) + borderpad = self.scale_size(4) + + self.style.element_create( + f"{ttkstyle}.indicator", + "image", + images[1], + ("disabled selected", images[3]), + ("disabled", images[2]), + ("!selected", images[0]), + width=width, + border=borderpad, + sticky=tk.W, + ) + self.style.layout( + ttkstyle, + [ + ( + "Toolbutton.border", + { + "sticky": tk.NSEW, + "children": [ + ( + "Toolbutton.padding", + { + "sticky": tk.NSEW, + "children": [ + ( + f"{ttkstyle}.indicator", + {"side": tk.LEFT}, + ), + ( + "Toolbutton.label", + {"side": tk.LEFT}, + ), + ], + }, + ) + ], + }, + ) + ], + ) + self.style._build_configure( + ttkstyle, relief=tk.FLAT, borderwidth=0, foreground=self.colors.fg + ) + self.style.map( + ttkstyle, + foreground=[("disabled", disabled_fg)], + background=[ + ("selected", self.colors.bg), + ("!selected", self.colors.bg), + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_toolbutton_style(self, colorname=DEFAULT): + """Create a solid toolbutton style for the ttk.Checkbutton + and ttk.Radiobutton widgets. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "Toolbutton" + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + toggle_on = self.colors.primary + else: + ttkstyle = f"{colorname}.{STYLE}" + toggle_on = self.colors.get(colorname) + + foreground = self.colors.get_foreground(colorname) + + if self.is_light_theme: + toggle_off = self.colors.border + else: + toggle_off = self.colors.selectbg + + disabled_bg = Colors.make_transparent(0.10, self.colors.fg, self.colors.bg) + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + + self.style._build_configure( + ttkstyle, + foreground=self.colors.selectfg, + background=toggle_off, + bordercolor=toggle_off, + darkcolor=toggle_off, + lightcolor=toggle_off, + relief=tk.RAISED, + focusthickness=0, + focuscolor="", + padding=(10, 5), + anchor=tk.CENTER, + ) + self.style.map( + ttkstyle, + foreground=[ + ("disabled", disabled_fg), + ("hover", foreground), + ("selected", foreground), + ], + background=[ + ("disabled", disabled_bg), + ("pressed !disabled", toggle_on), + ("selected !disabled", toggle_on), + ("hover !disabled", toggle_on), + ], + bordercolor=[ + ("disabled", disabled_bg), + ("pressed !disabled", toggle_on), + ("selected !disabled", toggle_on), + ("hover !disabled", toggle_on), + ], + darkcolor=[ + ("disabled", disabled_bg), + ("pressed !disabled", toggle_on), + ("selected !disabled", toggle_on), + ("hover !disabled", toggle_on), + ], + lightcolor=[ + ("disabled", disabled_bg), + ("pressed !disabled", toggle_on), + ("selected !disabled", toggle_on), + ("hover !disabled", toggle_on), + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_outline_toolbutton_style(self, colorname=DEFAULT): + """Create an outline toolbutton style for the ttk.Checkbutton + and ttk.Radiobutton widgets. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "Outline.Toolbutton" + + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + colorname = PRIMARY + else: + ttkstyle = f"{colorname}.{STYLE}" + + foreground = self.colors.get(colorname) + background = self.colors.get_foreground(colorname) + foreground_pressed = background + bordercolor = foreground + pressed = foreground + hover = foreground + + self.style._build_configure( + ttkstyle, + foreground=foreground, + background=self.colors.bg, + bordercolor=bordercolor, + darkcolor=self.colors.bg, + lightcolor=self.colors.bg, + relief=tk.RAISED, + focusthickness=0, + focuscolor=foreground, + padding=(10, 5), + anchor=tk.CENTER, + arrowcolor=foreground, + arrowpadding=(0, 0, 15, 0), + arrowsize=3, + ) + self.style.map( + ttkstyle, + foreground=[ + ("disabled", disabled_fg), + ("pressed !disabled", foreground_pressed), + ("selected !disabled", foreground_pressed), + ("hover !disabled", foreground_pressed), + ], + background=[ + ("pressed !disabled", pressed), + ("selected !disabled", pressed), + ("hover !disabled", hover), + ], + bordercolor=[ + ("disabled", disabled_fg), + ("pressed !disabled", pressed), + ("selected !disabled", pressed), + ("hover !disabled", hover), + ], + darkcolor=[ + ("disabled", self.colors.bg), + ("pressed !disabled", pressed), + ("selected !disabled", pressed), + ("hover !disabled", hover), + ], + lightcolor=[ + ("disabled", self.colors.bg), + ("pressed !disabled", pressed), + ("selected !disabled", pressed), + ("hover !disabled", hover), + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_entry_style(self, colorname=DEFAULT): + """Create a style for the ttk.Entry widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TEntry" + + # general default colors + if self.is_light_theme: + disabled_fg = self.colors.border + bordercolor = self.colors.border + readonly = self.colors.light + else: + disabled_fg = self.colors.selectbg + bordercolor = self.colors.selectbg + readonly = bordercolor + + if any([colorname == DEFAULT, not colorname]): + # default style + ttkstyle = STYLE + focuscolor = self.colors.primary + else: + # colored style + ttkstyle = f"{colorname}.{STYLE}" + focuscolor = self.colors.get(colorname) + bordercolor = focuscolor + + self.style._build_configure( + ttkstyle, + bordercolor=bordercolor, + darkcolor=self.colors.inputbg, + lightcolor=self.colors.inputbg, + fieldbackground=self.colors.inputbg, + foreground=self.colors.inputfg, + insertcolor=self.colors.inputfg, + padding=5, + ) + self.style.map( + ttkstyle, + foreground=[("disabled", disabled_fg)], + fieldbackground=[("readonly", readonly)], + bordercolor=[ + ("invalid", self.colors.danger), + ("focus !disabled", focuscolor), + ("hover !disabled", focuscolor), + ], + lightcolor=[ + ("focus invalid", self.colors.danger), + ("focus !disabled", focuscolor), + ("readonly", readonly), + ], + darkcolor=[ + ("focus invalid", self.colors.danger), + ("focus !disabled", focuscolor), + ("readonly", readonly), + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_radiobutton_assets(self, colorname=DEFAULT): + """Create the image assets used to build the radiobutton style. + + Parameters: + + colorname (str): + + Returns: + + Tuple[str]: + A tuple of PhotoImage names + """ + prime_color = self.colors.get(colorname) + on_fill = prime_color + off_fill = self.colors.bg + on_indicator = self.colors.selectfg + size = self.scale_size([14, 14]) + off_border = Colors.make_transparent(0.4, self.colors.fg, self.colors.bg) + disabled = Colors.make_transparent(0.3, self.colors.fg, self.colors.bg) + + if self.is_light_theme: + if colorname == LIGHT: + on_indicator = self.colors.dark + + # radio off + _off = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(_off) + draw.ellipse( + xy=[1, 1, 133, 133], outline=off_border, width=6, fill=off_fill + ) + off_img = ImageTk.PhotoImage(_off.resize(size, Image.LANCZOS)) + off_name = util.get_image_name(off_img) + self.theme_images[off_name] = off_img + + # radio on + _on = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(_on) + if colorname == LIGHT and self.is_light_theme: + draw.ellipse(xy=[1, 1, 133, 133], outline=off_border, width=6) + else: + draw.ellipse(xy=[1, 1, 133, 133], fill=on_fill) + draw.ellipse([40, 40, 94, 94], fill=on_indicator) + on_img = ImageTk.PhotoImage(_on.resize(size, Image.LANCZOS)) + on_name = util.get_image_name(on_img) + self.theme_images[on_name] = on_img + + # radio on/disabled + _on_dis = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(_on_dis) + if colorname == LIGHT and self.is_light_theme: + draw.ellipse(xy=[1, 1, 133, 133], outline=off_border, width=6) + else: + draw.ellipse(xy=[1, 1, 133, 133], fill=disabled) + draw.ellipse([40, 40, 94, 94], fill=off_fill) + on_dis_img = ImageTk.PhotoImage(_on_dis.resize(size, Image.LANCZOS)) + on_disabled_name = util.get_image_name(on_dis_img) + self.theme_images[on_disabled_name] = on_dis_img + + # radio disabled + _disabled = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(_disabled) + draw.ellipse( + xy=[1, 1, 133, 133], outline=disabled, width=3, fill=off_fill + ) + disabled_img = ImageTk.PhotoImage( + _disabled.resize(size, Image.LANCZOS) + ) + disabled_name = util.get_image_name(disabled_img) + self.theme_images[disabled_name] = disabled_img + + return off_name, on_name, disabled_name, on_disabled_name + + def create_radiobutton_style(self, colorname=DEFAULT): + """Create a style for the ttk.Radiobutton widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + + STYLE = "TRadiobutton" + + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + colorname = PRIMARY + else: + ttkstyle = f"{colorname}.{STYLE}" + + # ( off, on, disabled ) + images = self.create_radiobutton_assets(colorname) + width = self.scale_size(20) + borderpad = self.scale_size(4) + self.style.element_create( + f"{ttkstyle}.indicator", + "image", + images[1], + ("disabled selected", images[3]), + ("disabled", images[2]), + ("!selected", images[0]), + width=width, + border=borderpad, + sticky=tk.W, + ) + self.style.map(ttkstyle, foreground=[("disabled", disabled_fg)]) + self.style._build_configure(ttkstyle) + self.style.layout( + ttkstyle, + [ + ( + "Radiobutton.padding", + { + "children": [ + ( + f"{ttkstyle}.indicator", + {"side": tk.LEFT, "sticky": ""}, + ), + ( + "Radiobutton.focus", + { + "children": [ + ( + "Radiobutton.label", + {"sticky": tk.NSEW}, + ) + ], + "side": tk.LEFT, + "sticky": "", + }, + ), + ], + "sticky": tk.NSEW, + }, + ) + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_date_button_assets(self, foreground): + """Create the image assets used to build the date button + style. This button style applied to the button in the + ttkbootstrap.widgets.DateEntry. + + Parameters: + + foreground (str): + The color value used to draw the calendar image. + + Returns: + + str: + The PhotoImage name. + """ + fill = foreground + image = Image.new("RGBA", (210, 220)) + draw = ImageDraw.Draw(image) + + draw.rounded_rectangle( + [10, 30, 200, 210], radius=20, outline=fill, width=10 + ) + + calendar_image_coordinates = [ + # page spirals + [40, 10, 50, 50], + [100, 10, 110, 50], + [160, 10, 170, 50], + # row 1 + [70, 90, 90, 110], + [110, 90, 130, 110], + [150, 90, 170, 110], + # row 2 + [30, 130, 50, 150], + [70, 130, 90, 150], + [110, 130, 130, 150], + [150, 130, 170, 150], + # row 3 + [30, 170, 50, 190], + [70, 170, 90, 190], + [110, 170, 130, 190], + ] + for xy in calendar_image_coordinates: + draw.rectangle(xy=xy, fill=fill) + + size = self.scale_size([21, 22]) + tk_img = ImageTk.PhotoImage(image.resize(size, Image.LANCZOS)) + tk_name = util.get_image_name(tk_img) + self.theme_images[tk_name] = tk_img + return tk_name + + def create_date_button_style(self, colorname=DEFAULT): + """Create a date button style for the ttk.Button widget. + + Parameters: + + colorname (str): + The color label used to style widget. + """ + STYLE = "Date.TButton" + + if self.is_light_theme: + disabled_fg = self.colors.border + else: + disabled_fg = self.colors.selectbg + + btn_foreground = Colors.get_foreground(self.colors, colorname) + + img_normal = self.create_date_button_assets(btn_foreground) + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + foreground = self.colors.get_foreground(PRIMARY) + background = self.colors.primary + else: + ttkstyle = f"{colorname}.{STYLE}" + foreground = self.colors.get_foreground(colorname) + background = self.colors.get(colorname) + + pressed = Colors.update_hsv(background, vd=-0.1) + hover = Colors.update_hsv(background, vd=0.10) + + self.style._build_configure( + ttkstyle, + foreground=foreground, + background=background, + bordercolor=background, + darkcolor=background, + lightcolor=background, + relief=tk.RAISED, + focusthickness=0, + focuscolor=foreground, + padding=(2, 2), + anchor=tk.CENTER, + image=img_normal, + ) + self.style.map( + ttkstyle, + foreground=[("disabled", disabled_fg)], + background=[ + ("disabled", disabled_fg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + bordercolor=[("disabled", disabled_fg)], + darkcolor=[ + ("disabled", disabled_fg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + lightcolor=[ + ("disabled", disabled_fg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + ) + + self.style._register_ttkstyle(ttkstyle) + + def create_calendar_style(self, colorname=DEFAULT): + """Create a style for the + ttkbootstrap.dialogs.DatePickerPopup widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + + STYLE = "TCalendar" + + if any([colorname == DEFAULT, colorname == ""]): + prime_color = self.colors.primary + ttkstyle = STYLE + chevron_style = "Chevron.TButton" + else: + prime_color = self.colors.get(colorname) + ttkstyle = f"{colorname}.{STYLE}" + chevron_style = f"Chevron.{colorname}.TButton" + + if self.is_light_theme: + disabled_fg = Colors.update_hsv(self.colors.inputbg, vd=-0.2) + pressed = Colors.update_hsv(prime_color, vd=-0.1) + else: + disabled_fg = Colors.update_hsv(self.colors.inputbg, vd=-0.3) + pressed = Colors.update_hsv(prime_color, vd=0.1) + + self.style._build_configure( + ttkstyle, + foreground=self.colors.fg, + background=self.colors.bg, + bordercolor=self.colors.bg, + darkcolor=self.colors.bg, + lightcolor=self.colors.bg, + relief=tk.RAISED, + focusthickness=0, + focuscolor="", + borderwidth=1, + padding=(10, 5), + anchor=tk.CENTER, + ) + self.style.layout( + ttkstyle, + [ + ( + "Toolbutton.border", + { + "sticky": tk.NSEW, + "children": [ + ( + "Toolbutton.padding", + { + "sticky": tk.NSEW, + "children": [ + ( + "Toolbutton.label", + {"sticky": tk.NSEW}, + ) + ], + }, + ) + ], + }, + ) + ], + ) + self.style.map( + ttkstyle, + foreground=[ + ("disabled", disabled_fg), + ("pressed !disabled", self.colors.selectfg), + ("selected !disabled", self.colors.selectfg), + ("hover !disabled", self.colors.selectfg), + ], + background=[ + ("pressed !disabled", pressed), + ("selected !disabled", pressed), + ("hover !disabled", pressed), + ], + bordercolor=[ + ("disabled", disabled_fg), + ("pressed !disabled", pressed), + ("selected !disabled", pressed), + ("hover !disabled", pressed), + ], + darkcolor=[ + ("pressed !disabled", pressed), + ("selected !disabled", pressed), + ("hover !disabled", pressed), + ], + lightcolor=[ + ("pressed !disabled", pressed), + ("selected !disabled", pressed), + ("hover !disabled", pressed), + ], + ) + self.style._build_configure( + chevron_style, font="-size 14", focuscolor="" + ) + + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + self.style._register_ttkstyle(chevron_style) + + def create_metersubtxt_label_style(self, colorname=DEFAULT): + """Create a subtext label style for the + ttkbootstrap.widgets.Meter widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "Metersubtxt.TLabel" + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + if self.is_light_theme: + foreground = self.colors.secondary + else: + foreground = self.colors.light + else: + ttkstyle = f"{colorname}.{STYLE}" + foreground = self.colors.get(colorname) + + background = self.colors.bg + + self.style._build_configure( + ttkstyle, foreground=foreground, background=background + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_meter_label_style(self, colorname=DEFAULT): + """Create a label style for the + ttkbootstrap.widgets.Meter widget. This style also stores some + metadata that is called by the Meter class to lookup relevant + colors for the trough and bar when the new image is drawn. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + + STYLE = "Meter.TLabel" + + # text color = `foreground` + # trough color = `space` + + if self.is_light_theme: + if colorname == LIGHT: + troughcolor = self.colors.bg + else: + troughcolor = self.colors.light + else: + troughcolor = Colors.update_hsv(self.colors.selectbg, vd=-0.2) + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + background = self.colors.bg + textcolor = self.colors.primary + else: + ttkstyle = f"{colorname}.{STYLE}" + textcolor = self.colors.get(colorname) + background = self.colors.bg + + self.style._build_configure( + ttkstyle, + foreground=textcolor, + background=background, + space=troughcolor, + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_label_style(self, colorname=DEFAULT): + """Create a standard style for the ttk.Label widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TLabel" + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + foreground = self.colors.fg + background = self.colors.bg + else: + ttkstyle = f"{colorname}.{STYLE}" + foreground = self.colors.get(colorname) + background = self.colors.bg + + # standard label + self.style._build_configure( + ttkstyle, foreground=foreground, background=background + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_inverse_label_style(self, colorname=DEFAULT): + """Create an inverted style for the ttk.Label. + + The foreground and background are inverted versions of that + used in the standard label style. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE_INVERSE = "Inverse.TLabel" + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE_INVERSE + background = self.colors.fg + foreground = self.colors.bg + else: + ttkstyle = f"{colorname}.{STYLE_INVERSE}" + background = self.colors.get(colorname) + foreground = self.colors.get_foreground(colorname) + + self.style._build_configure( + ttkstyle, foreground=foreground, background=background + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_labelframe_style(self, colorname=DEFAULT): + """Create a style for the ttk.Labelframe widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TLabelframe" + + background = self.colors.bg + + if any([colorname == DEFAULT, colorname == ""]): + foreground = self.colors.fg + ttkstyle = STYLE + + if self.is_light_theme: + bordercolor = self.colors.border + else: + bordercolor = self.colors.selectbg + + else: + foreground = self.colors.get(colorname) + bordercolor = foreground + ttkstyle = f"{colorname}.{STYLE}" + + # create widget style + self.style._build_configure( + f"{ttkstyle}.Label", + foreground=foreground, + background=background, + ) + self.style._build_configure( + ttkstyle, + relief=tk.RAISED, + borderwidth=1, + bordercolor=bordercolor, + lightcolor=background, + darkcolor=background, + background=background, + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_checkbutton_style(self, colorname=DEFAULT): + """Create a standard style for the ttk.Checkbutton widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TCheckbutton" + + disabled_fg = Colors.make_transparent(0.3, self.colors.fg, self.colors.bg) + + if any([colorname == DEFAULT, colorname == ""]): + colorname = PRIMARY + ttkstyle = STYLE + else: + ttkstyle = f"{colorname}.TCheckbutton" + + # ( off, on, disabled ) + images = self.create_checkbutton_assets(colorname) + + element = ttkstyle.replace(".TC", ".C") + width = self.scale_size(20) + borderpad = self.scale_size(4) + self.style.element_create( + f"{element}.indicator", + "image", + images[1], + ("disabled selected", images[4]), + ("disabled alternate", images[5]), + ("disabled", images[2]), + ("alternate", images[3]), + ("!selected", images[0]), + width=width, + border=borderpad, + sticky=tk.W, + ) + self.style._build_configure(ttkstyle, foreground=self.colors.fg) + self.style.map(ttkstyle, foreground=[("disabled", disabled_fg)]) + self.style.layout( + ttkstyle, + [ + ( + "Checkbutton.padding", + { + "children": [ + ( + f"{element}.indicator", + {"side": tk.LEFT, "sticky": ""}, + ), + ( + "Checkbutton.focus", + { + "children": [ + ( + "Checkbutton.label", + {"sticky": tk.NSEW}, + ) + ], + "side": tk.LEFT, + "sticky": "", + }, + ), + ], + "sticky": tk.NSEW, + }, + ) + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_checkbutton_assets(self, colorname=DEFAULT): + """Create the image assets used to build the standard + checkbutton style. + + Parameters: + + colorname (str): + The color label used to style the widget. + + Returns: + + Tuple[str]: + A tuple of PhotoImage names. + """ + # set platform specific checkfont + winsys = self.style.tk.call("tk", "windowingsystem") + indicator = "✓" + if winsys == "win32": + # Windows font + fnt = ImageFont.truetype("seguisym.ttf", 120) + font_offset = -20 + # TODO consider using ImageFont.getsize for offsets + elif winsys == "x11": + # Linux fonts + try: + # this should be available on most Linux distros + fnt = ImageFont.truetype("FreeSerif.ttf", 130) + font_offset = 10 + except: + try: + # this should be available as a backup on Linux + # distros that don't have the FreeSerif.ttf file + fnt = ImageFont.truetype("DejaVuSans.ttf", 160) + font_offset = -15 + except: + # If all else fails, use the default ImageFont + # this won't actually show anything in practice + # because of how I'm scaling the image, but it + # will prevent the program from crashing. I need + # a better solution for a missing font + fnt = ImageFont.load_default() + font_offset = 0 + indicator = "x" + else: + # Mac OS font + fnt = ImageFont.truetype("LucidaGrande.ttc", 120) + font_offset = -10 + + prime_color = self.colors.get(colorname) + on_border = prime_color + on_fill = prime_color + off_fill = self.colors.bg + off_border = self.colors.selectbg + off_border = Colors.make_transparent(0.4, self.colors.fg, self.colors.bg) + disabled_fg = Colors.make_transparent(0.3, self.colors.fg, self.colors.bg) + + if colorname == LIGHT: + check_color = self.colors.dark + on_border = check_color + elif colorname == DARK: + check_color = self.colors.light + on_border = check_color + else: + check_color = self.colors.selectfg + + size = self.scale_size([14, 14]) + + # checkbutton off + checkbutton_off = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(checkbutton_off) + draw.rounded_rectangle( + [2, 2, 132, 132], + radius=16, + outline=off_border, + width=6, + fill=off_fill, + ) + off_img = ImageTk.PhotoImage( + checkbutton_off.resize(size, Image.LANCZOS) + ) + off_name = util.get_image_name(off_img) + self.theme_images[off_name] = off_img + + # checkbutton on + checkbutton_on = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(checkbutton_on) + draw.rounded_rectangle( + [2, 2, 132, 132], + radius=16, + fill=on_fill, + outline=on_border, + width=3, + ) + + draw.text((20, font_offset), indicator, font=fnt, fill=check_color) + on_img = ImageTk.PhotoImage(checkbutton_on.resize(size, Image.LANCZOS)) + on_name = util.get_image_name(on_img) + self.theme_images[on_name] = on_img + + # checkbutton on/disabled + checkbutton_on_disabled = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(checkbutton_on_disabled) + draw.rounded_rectangle( + [2, 2, 132, 132], + radius=16, + fill=disabled_fg, + outline=disabled_fg, + width=3, + ) + + draw.text((20, font_offset), indicator, font=fnt, fill=off_fill) + on_dis_img = ImageTk.PhotoImage(checkbutton_on_disabled.resize(size, Image.LANCZOS)) + on_dis_name = util.get_image_name(on_dis_img) + self.theme_images[on_dis_name] = on_dis_img + + # checkbutton alt + checkbutton_alt = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(checkbutton_alt) + draw.rounded_rectangle( + [2, 2, 132, 132], + radius=16, + fill=on_fill, + outline=on_border, + width=3, + ) + draw.line([36, 67, 100, 67], fill=check_color, width=12) + alt_img = ImageTk.PhotoImage( + checkbutton_alt.resize(size, Image.LANCZOS) + ) + alt_name = util.get_image_name(alt_img) + self.theme_images[alt_name] = alt_img + + # checkbutton alt/disabled + checkbutton_alt_disabled = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(checkbutton_alt_disabled) + draw.rounded_rectangle( + [2, 2, 132, 132], + radius=16, + fill=disabled_fg, + outline=disabled_fg, + width=3, + ) + draw.line([36, 67, 100, 67], fill=off_fill, width=12) + alt_dis_img = ImageTk.PhotoImage( + checkbutton_alt_disabled.resize(size, Image.LANCZOS) + ) + alt_dis_name = util.get_image_name(alt_dis_img) + self.theme_images[alt_dis_name] = alt_dis_img + + # checkbutton disabled + checkbutton_disabled = Image.new("RGBA", (134, 134)) + draw = ImageDraw.Draw(checkbutton_disabled) + draw.rounded_rectangle( + [2, 2, 132, 132], radius=16, outline=disabled_fg, width=3 + ) + disabled_img = ImageTk.PhotoImage( + checkbutton_disabled.resize(size, Image.LANCZOS) + ) + disabled_name = util.get_image_name(disabled_img) + self.theme_images[disabled_name] = disabled_img + + return off_name, on_name, disabled_name, alt_name, on_dis_name, alt_dis_name + + def create_menubutton_style(self, colorname=DEFAULT): + """Create a solid style for the ttk.Menubutton widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TMenubutton" + + foreground = self.colors.get_foreground(colorname) + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + background = self.colors.primary + else: + ttkstyle = f"{colorname}.{STYLE}" + background = self.colors.get(colorname) + + disabled_bg = Colors.make_transparent(0.10, self.colors.fg, self.colors.bg) + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + pressed = Colors.make_transparent(0.80, background, self.colors.bg) + hover = Colors.make_transparent(0.90, background, self.colors.bg) + + self.style._build_configure( + ttkstyle, + foreground=foreground, + background=background, + bordercolor=background, + darkcolor=background, + lightcolor=background, + arrowsize=self.scale_size(4), + arrowcolor=foreground, + arrowpadding=(0, 0, 15, 0), + relief=tk.RAISED, + focusthickness=0, + focuscolor=self.colors.selectfg, + padding=(10, 5), + ) + self.style.map( + ttkstyle, + arrowcolor=[("disabled", disabled_fg)], + foreground=[("disabled", disabled_fg)], + background=[ + ("disabled", disabled_bg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + bordercolor=[ + ("disabled", disabled_bg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + darkcolor=[ + ("disabled", disabled_bg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + lightcolor=[ + ("disabled", disabled_bg), + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_outline_menubutton_style(self, colorname=DEFAULT): + """Create an outline button style for the ttk.Menubutton widget + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "Outline.TMenubutton" + + disabled_fg = Colors.make_transparent(0.30, self.colors.fg, self.colors.bg) + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + colorname = PRIMARY + else: + ttkstyle = f"{colorname}.{STYLE}" + + foreground = self.colors.get(colorname) + background = self.colors.get_foreground(colorname) + foreground_pressed = background + bordercolor = foreground + pressed = foreground + hover = foreground + + self.style._build_configure( + ttkstyle, + foreground=foreground, + background=self.colors.bg, + bordercolor=bordercolor, + darkcolor=self.colors.bg, + lightcolor=self.colors.bg, + relief=tk.RAISED, + focusthickness=0, + focuscolor=foreground, + padding=(10, 5), + arrowcolor=foreground, + arrowpadding=(0, 0, 15, 0), + arrowsize=self.scale_size(4), + ) + self.style.map( + ttkstyle, + foreground=[ + ("disabled", disabled_fg), + ("pressed !disabled", foreground_pressed), + ("hover !disabled", foreground_pressed), + ], + background=[ + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + bordercolor=[ + ("disabled", disabled_fg), + ("pressed", pressed), + ("hover", hover), + ], + darkcolor=[ + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + lightcolor=[ + ("pressed !disabled", pressed), + ("hover !disabled", hover), + ], + arrowcolor=[ + ("disabled", disabled_fg), + ("pressed", foreground_pressed), + ("hover", foreground_pressed), + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_notebook_style(self, colorname=DEFAULT): + """Create a standard style for the ttk.Notebook widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TNotebook" + + if self.is_light_theme: + bordercolor = self.colors.border + foreground = self.colors.inputfg + else: + bordercolor = self.colors.selectbg + foreground = self.colors.selectfg + + if any([colorname == DEFAULT, colorname == ""]): + background = self.colors.inputbg + selectfg = self.colors.fg + ttkstyle = STYLE + else: + selectfg = self.colors.get_foreground(colorname) + background = self.colors.get(colorname) + ttkstyle = f"{colorname}.{STYLE}" + + ttkstyle_tab = f"{ttkstyle}.Tab" + + # create widget style + self.style._build_configure( + ttkstyle, + background=self.colors.bg, + bordercolor=bordercolor, + lightcolor=self.colors.bg, + darkcolor=self.colors.bg, + tabmargins=(0, 1, 1, 0), + ) + self.style._build_configure( + ttkstyle_tab, focuscolor="", foreground=foreground, padding=(6, 5) + ) + self.style.map( + ttkstyle_tab, + background=[ + ("selected", self.colors.bg), + ("!selected", background), + ], + lightcolor=[ + ("selected", self.colors.bg), + ("!selected", background), + ], + bordercolor=[ + ("selected", bordercolor), + ("!selected", bordercolor), + ], + padding=[("selected", (6, 5)), ("!selected", (6, 5))], + foreground=[("selected", foreground), ("!selected", selectfg)], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def create_panedwindow_style(self, colorname=DEFAULT): + """Create a standard style for the ttk.Panedwindow widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + H_STYLE = "Horizontal.TPanedwindow" + V_STYLE = "Vertical.TPanedwindow" + + if self.is_light_theme: + default_color = self.colors.border + else: + default_color = self.colors.selectbg + + if any([colorname == DEFAULT, colorname == ""]): + sashcolor = default_color + h_ttkstyle = H_STYLE + v_ttkstyle = V_STYLE + else: + sashcolor = self.colors.get(colorname) + h_ttkstyle = f"{colorname}.{H_STYLE}" + v_ttkstyle = f"{colorname}.{V_STYLE}" + + self.style._build_configure( + "Sash", gripcount=0, sashthickness=self.scale_size(2) + ) + self.style._build_configure(h_ttkstyle, background=sashcolor) + self.style._build_configure(v_ttkstyle, background=sashcolor) + + # register ttkstyle + self.style._register_ttkstyle(h_ttkstyle) + self.style._register_ttkstyle(v_ttkstyle) + + def create_sizegrip_assets(self, color): + """Create image assets used to build the sizegrip style. + + Parameters: + + color (str): + The color _value_ used to draw the image. + + Returns: + + str: + The PhotoImage name. + """ + from math import ceil + + box = self.scale_size(1) + pad = box * 2 + chunk = box + pad # 4 + + w = chunk * 3 + pad # 14 + h = chunk * 3 + pad # 14 + + size = [w, h] + + im = Image.new("RGBA", size) + draw = ImageDraw.Draw(im) + + draw.rectangle((chunk * 2 + pad, pad, chunk * 3, chunk), fill=color) + draw.rectangle( + (chunk * 2 + pad, chunk + pad, chunk * 3, chunk * 2), fill=color + ) + draw.rectangle( + (chunk * 2 + pad, chunk * 2 + pad, chunk * 3, chunk * 3), + fill=color, + ) + + draw.rectangle( + (chunk + pad, chunk + pad, chunk * 2, chunk * 2), fill=color + ) + draw.rectangle( + (chunk + pad, chunk * 2 + pad, chunk * 2, chunk * 3), fill=color + ) + + draw.rectangle((pad, chunk * 2 + pad, chunk, chunk * 3), fill=color) + + _img = ImageTk.PhotoImage(im) + _name = util.get_image_name(_img) + self.theme_images[_name] = _img + return _name + + def create_sizegrip_style(self, colorname=DEFAULT): + """Create a style for the ttk.Sizegrip widget. + + Parameters: + + colorname (str): + The color label used to style the widget. + """ + STYLE = "TSizegrip" + + if any([colorname == DEFAULT, colorname == ""]): + ttkstyle = STYLE + + if self.is_light_theme: + grip_color = self.colors.border + else: + grip_color = self.colors.inputbg + else: + ttkstyle = f"{colorname}.{STYLE}" + grip_color = self.colors.get(colorname) + + image = self.create_sizegrip_assets(grip_color) + + self.style.element_create( + f"{ttkstyle}.Sizegrip.sizegrip", "image", image + ) + self.style.layout( + ttkstyle, + [ + ( + f"{ttkstyle}.Sizegrip.sizegrip", + {"side": tk.BOTTOM, "sticky": tk.SE}, + ) + ], + ) + # register ttkstyle + self.style._register_ttkstyle(ttkstyle) + + def update_combobox_popdown_style(self, widget): + """Update the legacy ttk.Combobox elements. This method is + called every time the theme is changed in order to ensure + that the legacy tkinter components embedded in this ttk widget + are styled appropriate to the current theme. + + The ttk.Combobox contains several elements that are not styled + using the ttk theme engine. This includes the **popdownwindow** + and the **scrollbar**. Both of these widgets are configured + manually using calls to tcl/tk. + + Parameters: + + widget (ttk.Combobox): + The combobox element to be updated. + """ + if self.is_light_theme: + bordercolor = self.colors.border + else: + bordercolor = self.colors.selectbg + + tk_settings = [] + tk_settings.extend(["-borderwidth", 2]) + tk_settings.extend(["-highlightthickness", 1]) + tk_settings.extend(["-highlightcolor", bordercolor]) + tk_settings.extend(["-background", self.colors.inputbg]) + tk_settings.extend(["-foreground", self.colors.inputfg]) + tk_settings.extend(["-selectbackground", self.colors.selectbg]) + tk_settings.extend(["-selectforeground", self.colors.selectfg]) + + # set popdown style + popdown = widget.tk.eval(f"ttk::combobox::PopdownWindow {widget}") + widget.tk.call(f"{popdown}.f.l", "configure", *tk_settings) + + # set scrollbar style + sb_style = "TCombobox.Vertical.TScrollbar" + widget.tk.call(f"{popdown}.f.sb", "configure", "-style", sb_style) + + +class Keywords: + + # TODO possibly refactor the bootstyle keyword methods into this class? + # Leave for now. + + COLORS = [ + "primary", + "secondary", + "success", + "info", + "warning", + "danger", + "light", + "dark", + ] + ORIENTS = ["horizontal", "vertical"] + TYPES = [ + "outline", + "link", + "inverse", + "round", + "square", + "striped", + "focus", + "input", + "date", + "metersubtxt", + "meter", + "table" + ] + CLASSES = [ + "button", + "progressbar", + "checkbutton", + "combobox", + "entry", + "labelframe", + "label", + "frame", + "floodgauge", + "sizegrip", + "optionmenu", + "menubutton", + "menu", + "notebook", + "panedwindow", + "radiobutton", + "separator", + "scrollbar", + "spinbox", + "scale", + "text", + "toolbutton", + "treeview", + "toggle", + "tk", + "calendar", + "listbox", + "canvas", + "toplevel", + ] + COLOR_PATTERN = re.compile("|".join(COLORS)) + ORIENT_PATTERN = re.compile("|".join(ORIENTS)) + CLASS_PATTERN = re.compile("|".join(CLASSES)) + TYPE_PATTERN = re.compile("|".join(TYPES)) + + +class Bootstyle: + @staticmethod + def ttkstyle_widget_class(widget=None, string=""): + """Find and return the widget class + + Parameters: + + widget (Widget): + The widget object. + + string (str): + A keyword string to parse. + + Returns: + + str: + A widget class keyword. + """ + # find widget class from string pattern + match = re.search(Keywords.CLASS_PATTERN, string.lower()) + if match is not None: + widget_class = match.group(0) + return widget_class + + # find widget class from tkinter/tcl method + if widget is None: + return "" + _class = widget.winfo_class() + match = re.search(Keywords.CLASS_PATTERN, _class.lower()) + if match is not None: + widget_class = match.group(0) + return widget_class + else: + return "" + + @staticmethod + def ttkstyle_widget_type(string): + """Find and return the widget type. + + Parameters: + + string (str): + A keyword string to parse. + + Returns: + + str: + A widget type keyword. + """ + match = re.search(Keywords.TYPE_PATTERN, string.lower()) + if match is None: + return "" + else: + widget_type = match.group(0) + return widget_type + + @staticmethod + def ttkstyle_widget_orient(widget=None, string="", **kwargs): + """Find and return widget orient, or default orient for widget if + a widget with orientation. + + Parameters: + + widget (Widget): + The widget object. + + string (str): + A keyword string to parse. + + **kwargs: + Optional keyword arguments passed in the widget constructor. + + Returns: + + str: + A widget orientation keyword. + """ + # string method (priority) + match = re.search(Keywords.ORIENT_PATTERN, string) + widget_orient = "" + + if match is not None: + widget_orient = match.group(0) + return widget_orient + + # orient from kwargs + if "orient" in kwargs: + _orient = kwargs.pop("orient") + if _orient == "h": + widget_orient = "horizontal" + elif _orient == "v": + widget_orient = "vertical" + else: + widget_orient = _orient + return widget_orient + + # orient from settings + if widget is None: + return widget_orient + try: + widget_orient = str(widget.cget("orient")) + except: + pass + + return widget_orient + + @staticmethod + def ttkstyle_widget_color(string): + """Find and return widget color + + Parameters: + + string (str): + A keyword string to parse. + + Returns: + + str: + A color keyword. + """ + _color = re.search(Keywords.COLOR_PATTERN, string.lower()) + if _color is None: + return "" + else: + widget_color = _color.group(0) + return widget_color + + @staticmethod + def ttkstyle_name(widget=None, string="", **kwargs): + """Parse a string to build and return a ttkstyle name. + + Parameters: + + widget (Widget): + The widget object. + + string (str): + A keyword string to parse. + + **kwargs: + Other keyword arguments to parse widget orientation. + + Returns: + + str: + A ttk style name + """ + style_string = "".join(string).lower() + widget_color = Bootstyle.ttkstyle_widget_color(style_string) + widget_type = Bootstyle.ttkstyle_widget_type(style_string) + widget_orient = Bootstyle.ttkstyle_widget_orient( + widget, style_string, **kwargs + ) + widget_class = Bootstyle.ttkstyle_widget_class(widget, style_string) + + if widget_color: + widget_color = f"{widget_color}." + + if widget_type: + widget_type = f"{widget_type.title()}." + + if widget_orient: + widget_orient = f"{widget_orient.title()}." + + if widget_class.startswith("t"): + widget_class = widget_class.title() + else: + widget_class = f"T{widget_class.title()}" + + ttkstyle = f"{widget_color}{widget_type}{widget_orient}{widget_class}" + return ttkstyle + + @staticmethod + def ttkstyle_method_name(widget=None, string=""): + """Parse a string to build and return the name of the + `StyleBuilderTTK` method that creates the ttk style for the + target widget. + + Parameters: + + widget (Widget): + The widget object to lookup. + + string (str): + The keyword string to parse. + + Returns: + + str: + The name of the update method used to update the widget. + """ + style_string = "".join(string).lower() + widget_type = Bootstyle.ttkstyle_widget_type(style_string) + widget_class = Bootstyle.ttkstyle_widget_class(widget, style_string) + + if widget_type: + widget_type = f"_{widget_type}" + + if widget_class: + widget_class = f"_{widget_class}" + + if not widget_type and not widget_class: + return "" + else: + method_name = f"create{widget_type}{widget_class}_style" + return method_name + + @staticmethod + def tkupdate_method_name(widget): + """Lookup the tkinter style update method from the widget class + + Parameters: + + widget (Widget): + The widget object to lookup. + + Returns: + + str: + The name of the method used to update the widget object. + """ + widget_class = Bootstyle.ttkstyle_widget_class(widget) + + if widget_class: + widget_class = f"_{widget_class}" + + method_name = f"update{widget_class}_style" + return method_name + + @staticmethod + def override_ttk_widget_constructor(func): + """Override widget constructors with bootstyle api options. + + Parameters: + + func (Callable): + The widget class `__init__` method + """ + + def __init__(self, *args, **kwargs): + + # capture bootstyle and style arguments + if "bootstyle" in kwargs: + bootstyle = kwargs.pop("bootstyle") + else: + bootstyle = "" + + if "style" in kwargs: + style = kwargs.pop("style") or "" + else: + style = "" + + # instantiate the widget + func(self, *args, **kwargs) + + # must be called AFTER instantiation in order to use winfo_class + # in the `get_ttkstyle_name` method + + if style: + if Style.get_instance().style_exists_in_theme(style): + self.configure(style=style) + else: + ttkstyle = Bootstyle.update_ttk_widget_style( + self, style, **kwargs + ) + self.configure(style=ttkstyle) + elif bootstyle: + ttkstyle = Bootstyle.update_ttk_widget_style( + self, bootstyle, **kwargs + ) + self.configure(style=ttkstyle) + else: + ttkstyle = Bootstyle.update_ttk_widget_style( + self, "default", **kwargs + ) + self.configure(style=ttkstyle) + + return __init__ + + @staticmethod + def override_ttk_widget_configure(func): + """Overrides the configure method on a ttk widget. + + Parameters: + + func (Callable): + The widget class `configure` method + """ + + def configure(self, cnf=None, **kwargs): + # get configuration + if cnf in ("bootstyle", "style"): + return self.cget("style") + + if cnf is not None: + return func(self, cnf) + + # set configuration + if "bootstyle" in kwargs: + bootstyle = kwargs.pop("bootstyle") + else: + bootstyle = "" + + if "style" in kwargs: + style = kwargs.get("style") + ttkstyle = Bootstyle.update_ttk_widget_style( + self, style, **kwargs + ) + elif bootstyle: + ttkstyle = Bootstyle.update_ttk_widget_style( + self, bootstyle, **kwargs + ) + kwargs.update(style=ttkstyle) + + # update widget configuration + func(self, cnf, **kwargs) + + return configure + + @staticmethod + def update_ttk_widget_style( + widget: ttk.Widget = None, style_string: str = None, **kwargs + ): + """Update the ttk style or create if not existing. + + Parameters: + + widget (ttk.Widget): + The widget instance being updated. + + style_string (str): + The style string to evalulate. May be the `style`, `ttkstyle` + or `bootstyle` argument depending on the context and scenario. + + **kwargs: + Other optional keyword arguments. + + Returns: + + str: + The ttkstyle or empty string if there is none. + """ + style: Style = Style.get_instance() or Style() + + # get existing widget style if not provided + if style_string is None: + style_string = widget.cget("style") + + # do nothing if the style has not been set + if not style_string: + return "" + + if style_string == '.': + return '.' + + # build style if not existing (example: theme changed) + ttkstyle = Bootstyle.ttkstyle_name(widget, style_string, **kwargs) + if not style.style_exists_in_theme(ttkstyle): + widget_color = Bootstyle.ttkstyle_widget_color(ttkstyle) + method_name = Bootstyle.ttkstyle_method_name(widget, ttkstyle) + builder: StyleBuilderTTK = style._get_builder() + builder_method = builder.name_to_method(method_name) + builder_method(builder, widget_color) + + # subscribe popdown style to theme changes + try: + if widget.winfo_class() == "TCombobox": + builder: StyleBuilderTTK = style._get_builder() + winfo_id = hex(widget.winfo_id()) + winfo_pathname = widget.winfo_pathname(winfo_id) + Publisher.subscribe( + name=winfo_pathname, + func=lambda w=widget: builder.update_combobox_popdown_style( + w + ), + channel=Channel.STD, + ) + builder.update_combobox_popdown_style(widget) + except: + pass + + return ttkstyle + + @staticmethod + def setup_ttkbootstap_api(): + """Setup ttkbootstrap for use with tkinter and ttk. This method + is called when ttkbootstrap is imported to perform all of the + necessary method overrides that implement the bootstyle api.""" + from ttkbootstrap.widgets import TTK_WIDGETS + from ttkbootstrap.widgets import TK_WIDGETS + + # TTK WIDGETS + for widget in TTK_WIDGETS: + try: + # override widget constructor + _init = Bootstyle.override_ttk_widget_constructor( + widget.__init__ + ) + widget.__init__ = _init + + # override configure method + _configure = Bootstyle.override_ttk_widget_configure( + widget.configure + ) + widget.configure = _configure + widget.config = widget.configure + + # override get and set methods + _orig_getitem = widget.__getitem + _orig_setitem = widget.__setitem + + def __setitem(self, key, val): + if key in ("bootstyle", "style"): + return _configure(self, **{key: val}) + return _orig_setitem(key, val) + + def __getitem(self, key): + if key in ("bootstyle", "style"): + return _configure(self, cnf=key) + return _orig_getitem(key) + + if ( + widget.__name__ != "OptionMenu" + ): # this has it's own override + widget.__setitem__ = __setitem + widget.__getitem__ = __getitem + except: + # this may fail in python 3.6 for ttk widgets that do not exist + # in that version. + continue + + # TK WIDGETS + for widget in TK_WIDGETS: + # override widget constructor + _init = Bootstyle.override_tk_widget_constructor(widget.__init__) + widget.__init__ = _init + + @staticmethod + def update_tk_widget_style(widget): + """Lookup the widget name and call the appropriate update + method + + Parameters: + + widget (object): + The tcl/tk name given by `tkinter.Widget.winfo_name()` + """ + try: + style = Style.get_instance() + method_name = Bootstyle.tkupdate_method_name(widget) + builder = style._get_builder_tk() + builder_method = getattr(StyleBuilderTK, method_name) + builder_method(builder, widget) + except: + """Must pass here to prevent a failure when the user calls + the `Style`method BEFORE an instance of `Tk` is instantiated. + This will defer the update of the `Tk` background until the end + of the `BootStyle` object instantiation (created by the `Style` + method)""" + pass + + @staticmethod + def override_tk_widget_constructor(func): + """Override widget constructors to apply default style for tk + widgets. + + Parameters: + + func (Callable): + The `__init__` method for this widget. + """ + + def __init__wrapper(self, *args, **kwargs): + + # check for autostyle flag + if "autostyle" in kwargs: + autostyle = kwargs.pop("autostyle") + else: + autostyle = True + + # instantiate the widget + func(self, *args, **kwargs) + + if autostyle: + Publisher.subscribe( + name=str(self), + func=lambda w=self: Bootstyle.update_tk_widget_style(w), + channel=Channel.STD, + ) + Bootstyle.update_tk_widget_style(self) + + return __init__wrapper diff --git a/pylibraries/ttkbootstrap/tableview.py b/pylibraries/ttkbootstrap/tableview.py new file mode 100644 index 0000000..8e2feab --- /dev/null +++ b/pylibraries/ttkbootstrap/tableview.py @@ -0,0 +1,2657 @@ +import tkinter as tk +import ttkbootstrap as ttk +from ttkbootstrap.constants import * +from math import ceil +from datetime import datetime +from tkinter import font +from ttkbootstrap import utility +from typing import Any, Dict, List, Union +from ttkbootstrap.localization import MessageCatalog + +UPARROW = "⬆" +DOWNARROW = "⬇" +ASCENDING = 0 +DESCENDING = 1 + + +class TableColumn: + """Represents a column in a Tableview object""" + + def __init__( + self, + tableview, + cid, + text, + image="", + command="", + anchor=W, + width=200, + minwidth=20, + stretch=False, + ): + """ + Parameters: + + tableview (Tableview): + The parent tableview object. + + cid (str): + The column id. + + text (str): + The header text. + + image (PhotoImage): + An image that is displayed to the left of the header text. + + command (Callable): + A function called whenever the header button is clicked. + + anchor (str): + The position of the header text within the header. One + of "e", "w", "center". + + width (int): + Specifies the width of the column in pixels. + + minwidth (int): + Specifies the minimum width of the column in pixels. + + stretch (bool): + Specifies whether or not the column width should be + adjusted whenever the widget is resized or the user + drags the column separator. + """ + self._table = tableview + self._cid = cid + self._headertext = text + self._sort = ASCENDING + self._settings_column = {} + self._settings_heading = {} + + self.view: ttk.Treeview = tableview.view + self.view.column( + self._cid, + width=width, + minwidth=minwidth, + stretch=stretch, + anchor=anchor, + ) + self.view.heading( + self._cid, + text=text, + anchor=anchor, + image=image, + command=command, + ) + self._capture_settings() + self._table._cidmap[self._cid] = self + + @property + def headertext(self): + """The text on the header label""" + return self._headertext + + @property + def columnsort(self): + """Indicates how the column is to be sorted when the sorting + method is invoked.""" + return self._sort + + @columnsort.setter + def columnsort(self, value): + self._sort = value + + @property + def cid(self): + """A unique column identifier""" + return str(self._cid) + + @property + def tableindex(self): + """The index of the column as it is in the table configuration.""" + cols = self.view.cget("columns") + if cols is None: + return + try: + return cols.index(self.cid) + except IndexError: + return + + @property + def displayindex(self): + """The index of the column as it is displayed""" + cols = self.view.cget("displaycolumns") + if "#all" in cols: + return self.tableindex + else: + return cols.index(self.cid) + + def configure(self, opt=None, **kwargs): + """Configure the column. If opt is provided, the + current value is returned, otherwise, sets the widget + options specified in kwargs. See the documentation for + `Tableview.insert_column` for configurable options. + + Parameters: + + opt (str): + A configuration option to query. + + **kwargs (Dict): + Optional keyword arguments used to configure the + column and headers. + """ + # return queried options + if opt is not None: + if opt in ("anchor", "width", "minwidth", "stretch"): + return self.view.column(self.cid, opt) + elif opt in ("command", "text", "image"): + return self.view.heading(self.cid, opt) + else: + return + + # configure column and heading + for k, v in kwargs.items(): + if k in ("anchor", "width", "minwidth", "stretch"): + self._settings_column[k] = v + elif k in ("command", "text", "image"): + self._settings_heading[k] = v + self.view.column(self._cid, **self._settings_column) + self.view.heading(self._cid, **self._settings_heading) + if "text" in kwargs: + self._headertext = kwargs["text"] + + def show(self): + """Make the column visible in the tableview""" + displaycols = list(self.view.cget("displaycolumns")) + if "#all" in displaycols: + return + if self.cid in displaycols: + return + columns = list(self.view.cget("columns")) + index = columns.index(self.cid) + displaycols.insert(index, self.cid) + self.view.configure(displaycolumns=displaycols) + + def hide(self): + """Hide the column in the tableview""" + displaycols = list(self.view.cget("displaycolumns")) + cols = list(self.view.cget("columns")) + if "#all" in displaycols: + displaycols = cols + displaycols.remove(self.cid) + self.view.configure(displaycolumns=displaycols) + + def delete(self): + """Remove the column from the tableview permanently.""" + # update the tablerow columns + index = self.tableindex + if index is None: + return + + for row in self._table.tablerows: + row.values.pop(index) + row.refresh() + + # actual columns + cols = list(self.view.cget("columns")) + cols.remove(self.cid) + self._table.tablecolumns.remove(self) + + # visible columns + dcols = list(self.view.cget("displaycolumns")) + if "#all" in dcols: + dcols = cols + else: + dcols.remove(self.cid) + + # remove cid mapping + self._table.cidmap.pop(self._cid) + + # reconfigure the tableview column and displaycolumns + self.view.configure(columns=cols, displaycolumns=dcols) + + # remove the internal object references + for i, column in enumerate(self._table.tablecolumns): + if column.cid == self.cid: + self._table.tablecolumns.pop(i) + else: + column.restore_settings() + + def restore_settings(self): + """Update the configuration based on stored settings""" + self.view.column(self.cid, **self._settings_column) + self.view.heading(self.cid, **self._settings_heading) + + def _capture_settings(self): + """Update the stored settings for the column and heading. + This is required because the settings are erased whenever + the `columns` parameter is configured in the underlying + Treeview widget.""" + self._settings_heading = self.view.heading(self.cid) + self._settings_heading.pop("state") + self._settings_column = self.view.column(self.cid) + self._settings_column.pop("id") + + +class TableRow: + """Represents a row in a Tableview object""" + + _cnt = 0 + + def __init__(self, tableview, values): + """ + Parameters: + + tableview (Tableview): + The Tableview widget that contains this row + + values (List[Any, ...]): + A list of values to display in the row + """ + self.view: ttk.Treeview = tableview.view + self._values = list(values) + self._iid = None + self._sort = TableRow._cnt + 1 + self._table = tableview + + # increment cnt + TableRow._cnt += 1 + + @property + def values(self): + """The table row values""" + return self._values + + @values.setter + def values(self, values): + self._values = values + self.refresh() + + @property + def iid(self): + """A unique record identifier""" + return str(self._iid) + + def configure(self, opt=None, **kwargs): + """Configure the row. If opt is provided, the + current value is returned, otherwise, sets the widget + options specified in kwargs. See the documentation for + `Tableview.insert_row` for configurable options. + + Parameters: + + opt (str): + A configuration option to query. + + **kwargs { values, tags }: + Optional keyword arguments used to configure the + row. + """ + if self._iid is None: + self.build() + + if opt is not None: + return self.view.item(self.iid, opt) + elif 'values' in kwargs: + values = kwargs.pop('values') + self.values = values + else: + self.view.item(self.iid, **kwargs) + + def show(self, striped=False): + """Show the row in the data table view""" + if self._iid is None: + self.build() + self.view.reattach(self.iid, "", END) + + # remove existing stripes + tags = list(self.view.item(self.iid, "tags")) + try: + tags.remove("striped") + except ValueError: + pass + + # add stripes (if needed) + if striped: + tags.append("striped") + self.view.item(self.iid, tags=tags) + + def delete(self): + """Delete the row from the dataset""" + if self.iid: + self._table.iidmap.pop(self.iid) + self._table.tablerows_visible.remove(self) + self._table._tablerows.remove(self) + self._table.load_table_data() + self.view.delete(self.iid) + + def hide(self): + """Remove the row from the data table view""" + self.view.detach(self.iid) + + def refresh(self): + """Syncs the tableview values with the object values""" + if self._iid: + self.view.item(self.iid, values=self.values) + + def build(self): + """Create the row object in the `Treeview` and capture + the resulting item id (iid). + """ + if self._iid is None: + self._iid = self.view.insert("", END, values=self.values) + self._table.iidmap[self.iid] = self + + +class TableEvent: + """A container class for holding table event objects""" + + def __init__(self, column: TableColumn, row: TableRow): + self.column = column + self.row = row + + +class Tableview(ttk.Frame): + """A class built on the `ttk.Treeview` widget for arranging data in + rows and columns. The underlying Treeview object and its methods are + exposed in the `Tableview.view` property. + + A Tableview object contains various features such has striped rows, + pagination, and autosized and autoaligned columns. + + The pagination option is recommended when loading a lot of data as + the table records are inserted on-demand. Table records are only + created when requested to be in a page view. This allows the table + to be loaded very quickly even with hundreds of thousands of + records. + + All table columns are sortable. Clicking a column header will toggle + between sorting "ascending" and "descending". + + Columns are configurable by passing a simple list of header names or + by passing in a dictionary of column names with settings. You can + use both as well, as in the example below, where a column header + name is use for one column, and a dictionary of settings is used + for another. + + The object has a right-click menu on the header and the cells that + allow you to configure various settings. + + ![](../../assets/widgets/tableview-1.png) + ![](../../assets/widgets/tableview-2.png) + + Examples: + + Adding data with the constructor + ```python + import ttkbootstrap as ttk + from ttkbootstrap.tableview import Tableview + from ttkbootstrap.constants import * + + app = ttk.Window() + colors = app.style.colors + + coldata = [ + {"text": "LicenseNumber", "stretch": False}, + "CompanyName", + {"text": "UserCount", "stretch": False}, + ] + + rowdata = [ + ('A123', 'IzzyCo', 12), + ('A136', 'Kimdee Inc.', 45), + ('A158', 'Farmadding Co.', 36) + ] + + dt = Tableview( + master=app, + coldata=coldata, + rowdata=rowdata, + paginated=True, + searchable=True, + bootstyle=PRIMARY, + stripecolor=(colors.light, None), + ) + dt.pack(fill=BOTH, expand=YES, padx=10, pady=10) + + app.mainloop() + ``` + + Add data with methods + ```python + dt.insert_row('end', ['Marzale LLC', 26]) + ``` + """ + + def __init__( + self, + master=None, + bootstyle=DEFAULT, + coldata=[], + rowdata=[], + paginated=False, + searchable=False, + autofit=False, + autoalign=True, + stripecolor=None, + pagesize=10, + height=10, + delimiter=",", + ): + """ + Parameters: + + master (Widget): + The parent widget. + + bootstyle (str): + A style keyword used to set the focus color of the entry + and the background color of the date button. Available + options include -> primary, secondary, success, info, + warning, danger, dark, light. + + coldata (List[str | Dict]): + An iterable containing either the heading name or a + dictionary of column settings. Configurable settings + include >> text, image, command, anchor, width, minwidth, + maxwidth, stretch. Also see `Tableview.insert_column`. + + rowdata (List): + An iterable of row data. The lenth of each row of data + must match the number of columns. Also see + `Tableview.insert_row`. + + paginated (bool): + Specifies that the data is to be paginated. A pagination + frame will be created below the table with controls that + enable the user to page forward and backwards in the + data set. + + pagesize (int): + When `paginated=True`, this specifies the number of rows + to show per page. + + searchable (bool): + If `True`, a searchbar will be created above the table. + Press the key to initiate a search. Searching + with an empty string will reset the search criteria, or + pressing the reset button to the right of the search + bar. Currently, the search method looks for any row + that contains the search text. The filtered results + are displayed in the table view. + + autofit (bool): + If `True`, the table columns will be automatically sized + when loaded based on the records in the current view. + Also see `Tableview.autofit_columns`. + + autoalign (bool): + If `True`, the column headers and data are automatically + aligned. Numbers and number headers are right-aligned + and all other data types are left-aligned. The auto + align method evaluates the first record in each column + to determine the data type for alignment. Also see + `Tableview.autoalign_columns`. + + stripecolor (Tuple[str, str]): + If provided, even numbered rows will be color using the + (background, foreground) specified. You may specify one + or the other by passing in **None**. For example, + `stripecolor=('green', None)` will set the stripe + background as green, but the foreground will remain as + default. You may use standand color names, hexadecimal + color codes, or bootstyle color keywords. For example, + ('light', '#222') will set the background to the "light" + themed ttkbootstrap color and the foreground to the + specified hexadecimal color. Also see + `Tableview.apply_table_stripes`. + + height (int): + Specifies how many rows will appear in the table's viewport. + If the number of records extends beyond the table height, + the user may use the mousewheel or scrollbar to navigate + the data. + + delimiter (str): + The character to use as a delimiter when exporting data + to CSV. + """ + super().__init__(master) + self._tablecols = [] + self._tablerows = [] + self._tablerows_filtered = [] + self._viewdata = [] + self._rowindex = tk.IntVar(value=0) + self._pageindex = tk.IntVar(value=1) + self._pagelimit = tk.IntVar(value=0) + self._height = height + self._pagesize = tk.IntVar(value=pagesize) + self._paginated = paginated + self._searchable = searchable + self._stripecolor = stripecolor + self._autofit = autofit + self._autoalign = autoalign + self._filtered = False + self._sorted = False + self._searchcriteria = tk.StringVar() + self._rightclickmenu_cell = None + self._delimiter = delimiter + self._iidmap = {} # maps iid to row object + self._cidmap = {} # maps cid to col object + + self.view: ttk.Treeview = None + self._build_tableview_widget(coldata, rowdata, bootstyle) + + @property + def tablerows(self): + """A list of all tablerow objects""" + return self._tablerows + + @property + def tablerows_filtered(self): + """A list of filtered tablerow objects""" + return self._tablerows_filtered + + @property + def tablerows_visible(self): + """A list of visible tablerow objects""" + return self._viewdata + + @property + def tablecolumns(self): + """A list of table column objects""" + return self._tablecols + + @property + def tablecolumns_visible(self): + """A list of visible table column objects""" + cids = list(self.view.cget("displaycolumns")) + if "#all" in cids: + return self._tablecols + columns = [] + for cid in cids: + # the cidmap expects an integer + columns.append(self.cidmap.get(int(cid))) + return columns + + @property + def is_filtered(self): + """Indicates whether the table is currently filtered""" + return self._filtered + + @property + def searchcriteria(self): + """The criteria used to filter the records when the search + method is invoked""" + return self._searchcriteria.get() + + @searchcriteria.setter + def searchcriteria(self, value): + self._searchcriteria.set(value) + + @property + def pagesize(self): + """The number of records visible on a single page""" + return self._pagesize.get() + + @pagesize.setter + def pagesize(self, value): + self._pagesize.set(value) + + @property + def iidmap(self) -> Dict[str, TableRow]: + """A map of iid to tablerow object""" + return self._iidmap + + @property + def cidmap(self) -> Dict[str, TableColumn]: + """A map of cid to tablecolumn object""" + return self._cidmap + + def configure(self, cnf=None, **kwargs) -> Union[Any, None]: + """Configure the internal `Treeview` widget. If cnf is provided, + value of the option is return. Otherwise the widget is + configured via kwargs. + + Parameters: + + cnf (Any): + An option to query. + + **kwargs (Dict): + Optional keyword arguments used to configure the internal + Treeview widget. + + Returns: + + Union[Any, None]: + The value of cnf or None. + """ + try: + if "pagesize" in kwargs: + pagesize: int = kwargs.pop("pagesize") + self._pagesize.set(value=pagesize) + + self.view.configure(cnf, **kwargs) + except: + super().configure(cnf, **kwargs) + + # DATA HANDLING + + def build_table_data(self, coldata, rowdata): + """Insert the specified column and row data. + + The coldata can be either a string column name or a dictionary + of column settings that are passed to the `insert_column` + method. You may use a mixture of string and dictionary in + the list of coldata. + + !!!warning "Existing table data will be erased. + This method will completely rebuild the underlying table + with the new column and row data. Any existing data will + be lost. + + Parameters: + + coldata (List[Union[str, Dict]]): + An iterable of column names and/or settings. + + rowdata (List): + An iterable of row values. + """ + # destroy the existing data if existing + self.purge_table_data() + + # build the table columns + for i, col in enumerate(coldata): + if isinstance(col, str): + # just a column name + self.insert_column(i, col) + else: + # a dictionary of column settings + self.insert_column(i, **col) + + # build the table rows + for values in rowdata: + self.insert_row(values=values) + + # load the table data + self.load_table_data() + + # apply table formatting + if self._autofit: + self.autofit_columns() + + if self._autoalign: + self.autoalign_columns() + + if self._stripecolor is not None: + self.apply_table_stripes(self._stripecolor) + + self.goto_first_page() + + def insert_row(self, index=END, values=[]) -> TableRow: + """Insert a row into the tableview at index. + + You must call `Tableview.load_table_data()` to update the + current view. If the data is filtered, you will need to call + `Tableview.load_table_data(clear_filters=True)`. + + Parameters: + + index (Union[int, str]): + A numerical index that specifieds where to insert + the record in the dataset. You may also use the string + 'end' to append the record to the end of the data set. + If the index exceeds the record count, it will be + appended to the end of the dataset. + + values (Iterable): + An iterable of values to insert into the data set. + The number of columns implied by the list of values + must match the number of columns in the data set for + the values to be visible. + + Returns: + + TableRow: + A table row object. + """ + rowcount = len(self._tablerows) + + # validate the index + if len(values) == 0: + return + if index == END: + index = -1 + elif index > rowcount - 1: + index = -1 + + record = TableRow(self, values) + if rowcount == 0 or index == -1: + self._tablerows.append(record) + else: + self._tablerows.insert(index, record) + + return record + + def insert_rows(self, index, rowdata): + """Insert row after index for each row in *row. If index does + not exist then the records are appended to the end of the table. + You can also use the string 'end' to append records at the end + of the table. + + Parameters: + + index (Union[int, str]): + The location in the data set after where the records + will be inserted. You may use a numerical index or + the string 'end', which will append the records to the + end of the data set. + + rowdata (List[Any, List]): + A list of row values to be inserted into the table. + + Examples: + + ```python + Tableview.insert_rows('end', ['one', 1], ['two', 2]) + ``` + """ + if len(rowdata) == 0: + return + for values in reversed(rowdata): + self.insert_row(index, values) + + def delete_column(self, index=None, cid=None, visible=True): + """Delete the specified column based on the column index or the + unique cid. + + Unless otherwise specified, the index refers to the column index + as displayed in the tableview. + + If cid is provided, the column associated with the cid is deleted + regardless of whether it is in the visible data sets. + + Parameters: + + index (int): + The numerical index of the column. + + cid (str): + A unique column indentifier. + + visible (bool): + Specifies that the index should refer to the visible + columns. Otherwise, if False, the original column + position is used. + """ + if cid is not None: + column: TableColumn = self.cidmap(int(cid)) + column.delete() + + elif index is not None and visible: + self.tablecolumns_visible[int(index)].delete() + + elif index is None and not visible: + self.tablecolumns[int(index)].delete() + + def delete_columns(self, indices=None, cids=None, visible=True): + """Delete columns specified by indices or cids. + + Unless specified otherwise, the index refers to the position + of the columns in the table from left to right starting with + index 0. + + !!!Warning "Use this method with caution! + This method may or may not suffer performance issues. + Internally, this method calls the `delete_column` method + on each column specified in the list. The `delete_column` + method deletes the related column from each record in + the table data. So, if there are a lot of records this + could be problematic. It may be more beneficial to use + the `build_table_data` if you plan on changing the + structure of the table dramatically. + + Parameters: + + indices (List[int]): + A list of column indices to delete from the table. + + cids (List[str]): + A list of unique column identifiers to delete from the + table. + + visible (bool): + If True, the index refers to the visible position of the + column in the stable, from left to right starting at + index 0. + """ + if cids is not None: + for cid in cids: + self.delete_column(cid=cid) + elif indices is not None: + for index in indices: + self.delete_column(index=index, visible=visible) + + def delete_row(self, index=None, iid=None, visible=True): + """Delete a record from the data set. + + Unless specified otherwise, the index refers to the record + position within the visible data set from top to bottom + starting with index 0. + + If iid is provided, the record associated with the cid is deleted + regardless of whether it is in the visible data set. + + Parameters: + + index (int): + The numerical index of the record within the data set. + + iid (str): + A unique record identifier. + + visible (bool): + Indicates that the record index is relative to the current + records in view, otherwise, the original data set index is + used if False. + """ + # delete from iid + if iid is not None: + record: TableRow = self.iidmap.get(iid) + record.delete() + elif index is not None: + # visible index + if visible: + record = self.tablerows_visible[index] + record.delete() + # original index + else: + for record in self.tablerows: + if record._sort == index: + record.delete() + + def delete_rows(self, indices=None, iids=None, visible=True): + """Delete rows specified by indices or iids. + + If both indices and iids are None, then all records in the + table will be deleted. + """ + # remove records by iid + if iids is not None: + for iid in iids: + self.delete_row(iid=iid) + # remove records by index + elif indices is not None: + for index in indices: + self.delete_row(index=index, visible=visible) + # remove ALL records + else: + self._tablerows.clear() + self._tablerows_filtered.clear() + self._viewdata.clear() + self._iidmap.clear() + records = self.view.get_children() + self.view.delete(*records) + # route to new page if no records visible + if len(self._viewdata) == 0: + self.goto_page() + + def insert_column( + self, + index, + text="", + image="", + command="", + anchor=W, + width=200, + minwidth=20, + stretch=False, + ) -> TableColumn: + """ + Parameters: + + index (Union[int, str]): + A numerical index that specifieds where to insert + the column. You may also use the string 'end' to + insert the column in the right-most position. If the + index exceeds the column count, it will be inserted + at the right-most position. + + text (str): + The header text. + + image (PhotoImage): + An image that is displayed to the left of the header text. + + command (Callable): + A function called whenever the header button is clicked. + + anchor (str): + The position of the header text within the header. One + of "e", "w", "center". + + width (int): + Specifies the width of the column in pixels. + + minwidth (int): + Specifies the minimum width of the column in pixels. + + stretch (bool): + Specifies whether or not the column width should be + adjusted whenever the widget is resized or the user + drags the column separator. + + Returns: + + TableColumn: + A table column object. + """ + self.reset_table() + colcount = len(self.tablecolumns) + cid = colcount + if index == END: + index = -1 + elif index > colcount - 1: + index = -1 + + # actual columns + cols = self.view.cget("columns") + if len(cols) > 0: + cols = [int(x) for x in cols] + cols.append(cid) + else: + cols = [cid] + + # visible columns + dcols = self.view.cget("displaycolumns") + if "#all" in dcols: + dcols = cols + elif len(dcols) > 0: + dcols = [int(x) for x in dcols] + if index == -1: + dcols.append(cid) + else: + dcols.insert(index, cid) + else: + dcols = [cid] + + self.view.configure(columns=cols, displaycolumns=dcols) + + # configure new column + column = TableColumn( + tableview=self, + cid=cid, + text=text, + image=image, + command=command, + anchor=anchor, + width=width, + minwidth=minwidth, + stretch=stretch, + ) + self._tablecols.append(column) + # must be called to show the header after initially creating it + # ad hoc, not sure why this should be the case; + self._column_sort_header_reset() + + # update settings after they are erased when a column is + # inserted + for column in self._tablecols: + column.restore_settings() + + return column + + def purge_table_data(self): + """Erase all table and column data. + + This method will completely destroy the table data structure. + The table will need to be completely rebuilt after using this + method. + """ + self.delete_rows() + self.cidmap.clear() + self.tablecolumns.clear() + self.view.configure(columns=[], displaycolumns=[]) + + def unload_table_data(self): + """Unload all data from the table""" + for row in self.tablerows_visible: + row.hide() + self.tablerows_visible.clear() + + def load_table_data(self, clear_filters=False): + """Load records into the tableview. + + Parameters: + + clear_filters (bool): + Specifies that the table filters should be cleared + before loading the data into the view. + """ + if len(self.tablerows) == 0: + return + + if clear_filters: + self.reset_table() + + self.unload_table_data() + + if self._paginated: + page_start = self._rowindex.get() + page_end = self._rowindex.get() + self._pagesize.get() + else: + page_start = 0 + page_end = len(self._tablerows) + + if self._filtered: + rowdata = self._tablerows_filtered[page_start:page_end] + rowcount = len(self._tablerows_filtered) + else: + rowdata = self._tablerows[page_start:page_end] + rowcount = len(self._tablerows) + + self._pagelimit.set(ceil(rowcount / self._pagesize.get())) + + pageindex = ceil(page_end / self._pagesize.get()) + pagelimit = self._pagelimit.get() + self._pageindex.set(min([pagelimit, pageindex])) + + for i, row in enumerate(rowdata): + if self._stripecolor is not None and i % 2 == 0: + row.show(True) + else: + row.show(False) + self._viewdata.append(row) + + def fill_empty_columns(self, fillvalue=""): + """Fill empty columns with the fillvalue. + + This method can be used to fill in missing values when a column + column is inserted after data has already been inserted into + the tableview. + + Parameters: + + fillvalue (Any): + A value to insert into an empty column + """ + rowcount = len(self._tablerows) + if rowcount == 0: + return + colcount = len(self._tablecols) + for row in self._tablerows: + var = colcount - len(row._values) + if var <= 0: + return + else: + for _ in range(var): + row._values.append(fillvalue) + row.configure(values=row._values) + + # CONFIGURATION + + def get_columns(self) -> List[TableColumn]: + """Returns a list of all column objects. Same as using the + `Tableview.tablecolumns` property.""" + return self._tablecols + + def get_column( + self, index=None, visible=False, cid=None + ) -> TableColumn: + """Returns the `TableColumn` object from an index or a cid. + + If index is specified, the column index refers to the index + within the original, unless the visible flag is set, in which + case the index is relative to the visible columns in view. + + If cid is specified, the column associated with the cid is + return regardless of whether it is visible. + + Parameters: + + index (int): + The numerical index of the column. + + visible (bool): + Use the index of the visible columns as they appear + in the table. + + Returns: + + Union[TableColumn, None]: + The table column object if found, otherwise None. + """ + if cid is not None: + return self._cidmap.get(cid) + + if not visible: + # original column index + try: + return self._tablecols[index] + except IndexError: + return None + else: + # visible column index + cols = self.view.cget("columns") + if len(cols) > 0: + cols = [int(x) for x in cols] + else: + cols = [] + + dcols = self.view.cget("displaycolumns") + if "#all" in dcols: + dcols = cols + else: + try: + x = int(dcols[index]) + for c in self._tablecols: + if c.cid == x: + return c + except ValueError: + return None + + def get_rows(self, visible=False, filtered=False, selected=False) -> List[TableRow]: + """Return a list of TableRow objects. + + Return a subset of rows based on optional flags. Only ONE flag can be used + at a time. If more than one flag is set to `True`, then the first flag will + be used to return the data. + + Parameters: + + visible (bool): + If true, only records in the current view will be returned. + + filtered (bool): + If True, only rows in the filtered dataset will be returned. + + selected (bool): + If True, only rows that are currently selected will be returned. + + Returns: + + List[TableRow]: + A list of TableRow objects. + """ + if visible: + return self._viewdata + elif filtered: + return self._tablerows_filtered + elif selected: + return [row for row in self._viewdata if row.iid in self.view.selection()] + else: + return self._tablerows + + def get_row(self, index=None, visible=False, filtered=False, iid=None) -> TableRow: + """Returns the `TableRow` object from an index or the iid. + + If an index is specified, the row index refers to the index + within the original dataset. When choosing a subset of data, + the visible data takes priority over filtered if both flags + are set. + + If an iid is specified, the object attached to that iid is + returned regardless of whether or not it is visible or + filtered. + + Parameters: + + index (int): + The numerical index of the column. + + iid (str): + A unique column identifier. + + visible (bool): + Use the index of the visible rows as they appear + in the current table view. + + filtered (bool): + Use the index of the rows within the filtered data + set. + + Returns: + + Union[TableRow, None]: + The table column object if found, otherwise None + """ + if iid is not None: + return self.iidmap.get(iid) + + if visible: + try: + return self.tablerows_visible[index] + except IndexError: + return None + elif filtered: + try: + return self.tablerows_filtered[index] + except IndexError: + return None + else: + try: + return self.tablerows[index] + except IndexError: + return None + + # PAGE NAVIGATION + + def _select_first_visible_item(self): + try: + iid = self.tablerows_visible[0].iid + self.view.selection_set(iid) + # must force focus, sometimes just focus on iid doesn't work + self.view.focus_force() + # this sets the focus on the specific row item + self.view.focus(iid) + # make sure the row is visible + self.view.see(iid) + except: + pass + + def goto_first_page(self): + """Update table with first page of data""" + self._rowindex.set(0) + self.load_table_data() + self._select_first_visible_item() + + def goto_last_page(self): + """Update table with the last page of data""" + pagelimit = self._pagelimit.get() - 1 + self._rowindex.set(self.pagesize * pagelimit) + self.load_table_data() + self._select_first_visible_item() + + def goto_next_page(self): + """Update table with next page of data""" + if self._pageindex.get() >= self._pagelimit.get(): + return + rowindex = self._rowindex.get() + self._rowindex.set(rowindex + self.pagesize) + self.load_table_data() + self._select_first_visible_item() + + def goto_prev_page(self): + """Update table with prev page of data""" + if self._pageindex.get() <= 1: + return + rowindex = self._rowindex.get() + self._rowindex.set(rowindex - self.pagesize) + self.load_table_data() + self._select_first_visible_item() + + def goto_page(self, *_): + """Go to a specific page indicated by the page entry widget.""" + pagelimit = self._pagelimit.get() + pageindex = self._pageindex.get() + if pageindex > pagelimit: + pageindex = pagelimit + self._pageindex.set(pageindex) + elif pageindex <= 0: + pageindex = 1 + self._pageindex.set(pageindex) + rowindex = (pageindex * self.pagesize) - self.pagesize + self._rowindex.set(rowindex) + self.load_table_data() + self._select_first_visible_item() + + # COLUMN SORTING + + def sort_column_data(self, event=None, cid=None, sort=None): + """Sort the table rows by the specified column. This method + may be trigged by an event or manually. + + Parameters: + + event (Event): + A window event. + + cid (int): + A unique column identifier; typically the numerical + index of the column relative to the original data set. + + sort (int): + Determines the sort direction. 0 = ASCENDING. 1 = DESCENDING. + """ + if event is not None: + eo = self._get_event_objects(event) + column = eo.column + index = column.tableindex + elif cid is not None: + column: TableColumn = self.cidmap.get(int(cid)) + index = column.tableindex + else: + return + + # update table data + if self.is_filtered: + tablerows = self.tablerows_filtered + else: + tablerows = self.tablerows + + if sort is not None: + columnsort = sort + else: + columnsort = self.tablecolumns[index].columnsort + + if columnsort == ASCENDING: + self._tablecols[index].columnsort = DESCENDING + else: + self._tablecols[index].columnsort = ASCENDING + + try: + sortedrows = sorted( + tablerows, reverse=columnsort, key=lambda x: x.values[index] + ) + except: + # when data is missing, or sometimes with numbers + # this is still not right, but it works most of the time + # fix sometime down the road when I have time + self.fill_empty_columns() + sortedrows = sorted( + tablerows, reverse=columnsort, key=lambda x: int(x.values[index]) + ) + if self.is_filtered: + self._tablerows_filtered = sortedrows + else: + self._tablerows = sortedrows + + # update headers + self._column_sort_header_reset() + self._column_sort_header_update(column.cid) + + self.unload_table_data() + self.load_table_data() + self._select_first_visible_item() + + # DATA SEARCH & FILTERING + + def reset_row_filters(self): + """Remove all row level filters; unhide all rows.""" + self._filtered = False + self.searchcriteria = "" + self.unload_table_data() + self.load_table_data() + + def reset_column_filters(self): + """Remove all column level filters; unhide all columns.""" + cols = [col.cid for col in self.tablecolumns] + self.view.configure(displaycolumns=cols) + + def reset_row_sort(self): + """Display all table rows by original insert index""" + ... + + def reset_column_sort(self): + """Display all columns by original insert index""" + cols = sorted([col.cid for col in self.tablecolumns_visible], key=int) + self.view.configure(displaycolumns=cols) + + def reset_table(self): + """Remove all table data filters and column sorts""" + self._filtered = False + self.searchcriteria = "" + try: + sortedrows = sorted(self.tablerows, key=lambda x: x._sort) + except IndexError: + self.fill_empty_columns() + sortedrows = sorted(self.tablerows, key=lambda x: x._sort) + self._tablerows = sortedrows + self.unload_table_data() + + # reset the columns + self.reset_column_filters() + self.reset_column_sort() + + self._column_sort_header_reset() + self.goto_first_page() # needed? + + def filter_column_to_value(self, event=None, cid=None, value=None): + """Hide all records except for records where the current + column exactly matches the provided value. This method may + be triggered by a window event or by specifying the column id. + + Parameters: + + event (Event): + A window click event. + + cid (int): + A unique column identifier; typically the numerical + index of the column within the original dataset. + + value (Any): + The criteria used to filter the column. + """ + if event is not None: + eo = self._get_event_objects(event) + index = eo.column.tableindex + value = value or eo.row.values[index] + elif cid is not None: + column: TableColumn = self.cidmap.get(cid) + index = column.tableindex + else: + return + + self._filtered = True + self.tablerows_filtered.clear() + self.unload_table_data() + + for row in self.tablerows: + if row.values[index] == value: + self.tablerows_filtered.append(row) + + self._rowindex.set(0) + self.load_table_data() + + def filter_to_selected_rows(self): + """Hide all records except for the selected rows""" + criteria = self.view.selection() + if len(criteria) == 0: + return # nothing is selected + + if self.is_filtered: + for row in self.tablerows_visible: + if row.iid not in criteria: + row.hide() + self.tablerows_filtered.remove(row) + else: + self._filtered = True + self.tablerows_filtered.clear() + for row in self.tablerows_visible: + if row.iid in criteria: + self.tablerows_filtered.append(row) + self._rowindex.set(0) + self.load_table_data() + + def hide_selected_rows(self): + """Hide the currently selected rows""" + selected = self.view.selection() + view_cnt = len(self._viewdata) + hide_cnt = len(selected) + self.view.detach(*selected) + + tablerows = [] + for row in self.tablerows_visible: + if row.iid in selected: + tablerows.append(row) + + if not self.is_filtered: + self._filtered = True + self._tablerows_filtered = self.tablerows.copy() + + for row in tablerows: + if self.is_filtered: + self.tablerows_filtered.remove(row) + + if hide_cnt == view_cnt: + # assuming that if the count of the records on the page are + # selected for hiding, then need to go to the next page + # The call to `load_table_data` is duplicative, but currently + # this is the only way to get this to work until I've + # refactored this bit. + self.load_table_data() + self.goto_page() + else: + self.load_table_data() + + def hide_selected_column(self, event=None, cid=None): + """Detach the selected column from the tableview. This method + may be triggered by a window event or by specifying the column + id. + + Parameters: + + event (Event): + A window click event + + cid (int): + A unique column identifier; typically the numerical + index of the column within the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + column = eo.column.hide() + elif cid is not None: + column: TableColumn = self.cidmap.get(cid) + column.hide() + + def unhide_selected_column(self, event=None, cid=None): + """Attach the selected column to the tableview. This method + may be triggered by a window event or by specifying the column + id. The column is reinserted at the index in the original data + set. + + Parameters: + + event (Event): + An application click event + + cid (int): + A unique column identifier; typically the numerical + index of the column within the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + eo.column.show() + elif cid is not None: + column = self.cidmap.get(cid) + column.show() + + # DATA EXPORT + + def export_all_records(self): + """Export all records to a csv file""" + headers = [col.headertext for col in self.tablecolumns] + records = [row.values for row in self.tablerows] + self.save_data_to_csv(headers, records, self._delimiter) + + def export_current_page(self): + """Export records on current page to csv file""" + headers = [col.headertext for col in self.tablecolumns] + records = [row.values for row in self.tablerows_visible] + self.save_data_to_csv(headers, records, self._delimiter) + + def export_current_selection(self): + """Export rows currently selected to csv file""" + headers = [col.headertext for col in self.tablecolumns] + selected = self.view.selection() + records = [] + for iid in selected: + record: TableRow = self.iidmap.get(iid) + records.append(record.values) + self.save_data_to_csv(headers, records, self._delimiter) + + def export_records_in_filter(self): + """Export rows currently filtered to csv file""" + headers = [col.headertext for col in self.tablecolumns] + if not self.is_filtered: + return + records = [row.values for row in self.tablerows_filtered] + self.save_data_to_csv(headers, records, self._delimiter) + + def save_data_to_csv(self, headers, records, delimiter=","): + """Save data records to a csv file. + + Parameters: + + headers (List[str]): + A list of header labels. + + records (List[Tuple[...]]): + A list of table records. + + delimiter (str): + The character to use for delimiting the values. + """ + from tkinter.filedialog import asksaveasfilename + import csv + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + initialfile = f"tabledata_{timestamp}.csv" + filetypes = [ + ("CSV UTF-8 (Comma delimited)", "*.csv"), + ("All file types", "*.*"), + ] + filename = asksaveasfilename( + confirmoverwrite=True, + filetypes=filetypes, + defaultextension="csv", + initialfile=initialfile, + ) + if filename: + with open(filename, "w", encoding="utf-8", newline="") as f: + writer = csv.writer(f, delimiter=delimiter) + writer.writerow(headers) + writer.writerows(records) + + # ROW MOVEMENT + + def move_selected_rows_to_top(self): + """Move the selected rows to the top of the data set""" + selected = self.view.selection() + if len(selected) == 0: + return + + if self.is_filtered: + tablerows = self.tablerows_filtered.copy() + else: + tablerows = self.tablerows.copy() + + for i, iid in enumerate(selected): + row = self.iidmap.get(iid) + tablerows.remove(row) + tablerows.insert(i, row) + + if self.is_filtered: + self._tablerows_filtered = tablerows + else: + self._tablerows = tablerows + + # refresh the table data + self.unload_table_data() + self.load_table_data() + + def move_selected_rows_to_bottom(self): + """Move the selected rows to the bottom of the dataset""" + selected = self.view.selection() + if len(selected) == 0: + return + + if self.is_filtered: + tablerows = self.tablerows_filtered.copy() + else: + tablerows = self.tablerows.copy() + + for iid in selected: + row = self.iidmap.get(iid) + tablerows.remove(row) + tablerows.append(row) + + if self.is_filtered: + self._tablerows_filtered = tablerows + else: + self._tablerows = tablerows + + # refresh the table data + self.unload_table_data() + self.load_table_data() + + def move_selected_row_up(self): + """Move the selected rows up one position in the dataset""" + selected = self.view.selection() + if len(selected) == 0: + return + + if self.is_filtered: + tablerows = self._tablerows_filtered.copy() + else: + tablerows = self.tablerows.copy() + + for iid in selected: + row = self.iidmap.get(iid) + index = tablerows.index(row) - 1 + tablerows.remove(row) + tablerows.insert(index, row) + + if self.is_filtered: + self._tablerows_filtered = tablerows + else: + self._tablerows = tablerows + + # refresh the table data + self.unload_table_data() + self.load_table_data() + + def move_row_down(self): + """Move the selected rows down one position in the dataset""" + selected = self.view.selection() + if len(selected) == 0: + return + + if self._filtered: + tablerows = self._tablerows_filtered + else: + tablerows = self._tablerows + + for iid in selected: + row = self.iidmap.get(iid) + index = tablerows.index(row) + 1 + tablerows.remove(row) + tablerows.insert(index, row) + + if self._filtered: + self._tablerows_filtered = tablerows + else: + self._tablerows = tablerows + + # refresh the table data + self.unload_table_data() + self.load_table_data() + + # COLUMN MOVEMENT + + def move_column_left(self, event=None, cid=None): + """Move column one position to the left. This can be triggered + by either an event, or by passing in the `cid`, which is the + index of the column relative to the original data set. + + Parameters: + + event (Event): + An application click event. + + cid (int): + A unique column identifier; typically the index of the + column relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + column = eo.column + elif cid is not None: + column = self.cidmap.get(cid) + else: + return + + displaycols = [x.cid for x in self.tablecolumns_visible] + old_index = column.displayindex + if old_index == 0: + return + + new_index = column.displayindex - 1 + displaycols.insert(new_index, displaycols.pop(old_index)) + self.view.configure(displaycolumns=displaycols) + + def move_column_right(self, event=None, cid=None): + """Move column one position to the right. This can be triggered + by either an event, or by passing in the `cid`, which is the + index of the column relative to the original data set. + + Parameters: + + event (Event): + An application click event. + + cid (int): + A unique column identifier; typically the index of the + column relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + column = eo.column + elif cid is not None: + column = self.cidmap.get(cid) + else: + return + + displaycols = [x.cid for x in self.tablecolumns_visible] + old_index = column.displayindex + if old_index == len(displaycols) - 1: + return + + new_index = old_index + 1 + displaycols.insert(new_index, displaycols.pop(old_index)) + self.view.configure(displaycolumns=displaycols) + + def move_column_to_first(self, event=None, cid=None): + """Move column to leftmost position. This can be triggered by + either an event, or by passing in the `cid`, which is the index + of the column relative to the original data set. + + Parameters: + + event (Event): + An application click event. + + cid (int): + A unique column identifier; typically the index of the + column relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + column = eo.column + elif cid is not None: + column = self.cidmap.get(cid) + else: + return + + displaycols = [x.cid for x in self.tablecolumns_visible] + old_index = column.displayindex + if old_index == 0: + return + + displaycols.insert(0, displaycols.pop(old_index)) + self.view.configure(displaycolumns=displaycols) + + def move_column_to_last(self, event=None, cid=None): + """Move column to the rightmost position. This can be triggered + by either an event, or by passing in the `cid`, which is the + index of the column relative to the original data set. + + Parameters: + + event (Event): + An application click event. + + cid (int): + A unique column identifier; typically the index of the + column relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + column = eo.column + elif cid is not None: + column = self.cidmap.get(cid) + else: + return + + displaycols = [x.cid for x in self.tablecolumns_visible] + old_index = column.displayindex + if old_index == len(displaycols) - 1: + return + + new_index = len(displaycols) - 1 + displaycols.insert(new_index, displaycols.pop(old_index)) + self.view.configure(displaycolumns=displaycols) + + # OTHER FORMATTING + + def apply_table_stripes(self, stripecolor): + """Add stripes to even-numbered table rows as indicated by the + `stripecolor` of (background, foreground). Either element may be + specified as `None`, but both elements must be present. + + Parameters: + + stripecolor (Tuple[str, str]): + A tuple of colors to apply to the table stripe. The + tuple represents (background, foreground). + """ + style: ttk.Style = ttk.Style.get_instance() + colors = style.colors + if len(stripecolor) == 2: + self._stripecolor = stripecolor + bg, fg = stripecolor + kw = {} + if bg is None: + kw["background"] = colors.active + else: + kw["background"] = bg + if fg is None: + kw["foreground"] = colors.inputfg + else: + kw["foreground"] = fg + self.view.tag_configure("striped", **kw) + + def autofit_columns(self): + """Autofit all columns in the current view""" + f = font.nametofont("TkDefaultFont") + pad = utility.scale_size(self, 20) + col_widths = [] + + # measure header sizes + for col in self.tablecolumns: + width = f.measure(f"{col._headertext} {DOWNARROW}") + pad + col_widths.append(width) + + for row in self.tablerows_visible: + values = row.values + for i, value in enumerate(values): + old_width = col_widths[i] + new_width = f.measure(str(value)) + pad + width = max(old_width, new_width) + col_widths[i] = width + + for i, width in enumerate(col_widths): + self.view.column(i, width=width) + + # COLUMN AND HEADER ALIGNMENT + + def autoalign_columns(self): + """Align the columns and headers based on the data type of the + values. Text is left-aligned; numbers are right-aligned. This + method will have no effect if there is no data in the tables.""" + if len(self._tablerows) == 0: + return + + values = self._tablerows[0]._values + for i, value in enumerate(values): + if str(value).isnumeric(): + self.view.column(i, anchor=E) + self.view.heading(i, anchor=E) + else: + self.view.column(i, anchor=W) + self.view.heading(i, anchor=W) + + def align_column_left(self, event=None, cid=None): + """Left align the column text. This can be triggered by + either an event, or by passing in the `cid`, which is the index + of the column relative to the original data set. + + Parameters: + + event (Event): + An application click event. + + cid (int): + A unique column identifier; typically the index of the + column relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + self.view.column(eo.column.cid, anchor=W) + elif cid is not None: + self.view.column(cid, anchor=W) + + def align_column_right(self, event=None, cid=None): + """Right align the column text. This can be triggered by + either an event, or by passing in the `cid`, which is the index + of the column relative to the original data set. + + Parameters: + + event (Event): + An application event. + + cid (int): + A unique column identifier; typically the index of the + column relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + self.view.column(eo.column.cid, anchor=E) + elif cid is not None: + self.view.column(cid, anchor=E) + + def align_column_center(self, event=None, cid=None): + """Center align the column text. This can be triggered by + either an event, or by passing in the `cid`, which is the index + of the column relative to the original data set. + + Parameters: + + event (Event): + An application event. + + cid (int): + A unique column identifier; typically the index of the + column relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + self.view.column(eo.column.cid, anchor=CENTER) + elif cid is not None: + self.view.column(cid, anchor=CENTER) + + def align_heading_left(self, event=None, cid=None): + """Left align the heading text. This can be triggered by + either an event, or by passing in the `cid`, which is the index + of the heading relative to the original data set. + + Parameters: + + event (Event): + An application event. + + cid (int): + A unique heading identifier; typically the index of the + heading relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + self.view.heading(eo.column.cid, anchor=W) + elif cid is not None: + self.view.heading(cid, anchor=W) + + def align_heading_right(self, event=None, cid=None): + """Right align the heading text. This can be triggered by + either an event, or by passing in the `cid`, which is the index + of the heading relative to the original data set. + + Parameters: + + event (Event): + An application event. + + cid (int): + A unique heading identifier; typically the index of the + heading relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + self.view.heading(eo.column.cid, anchor=E) + elif cid is not None: + self.view.heading(cid, anchor=E) + + def align_heading_center(self, event=None, cid=None): + """Center align the heading text. This can be triggered by + either an event, or by passing in the `cid`, which is the index + of the heading relative to the original data set. + + Parameters: + + event (Event): + An application event. + + cid (int): + A unique heading identifier; typically the index of the + heading relative to the original dataset. + """ + if event is not None: + eo = self._get_event_objects(event) + self.view.heading(eo.column.cid, anchor=CENTER) + elif cid is not None: + self.view.heading(cid, anchor=CENTER) + + # PRIVATE METHODS + + def _get_event_objects(self, event): + iid = self.view.identify_row(event.y) + col = self.view.identify_column(event.x) + cid = int(self.view.column(col, "id")) + column: TableColumn = self.cidmap.get(cid) + row: TableRow = self.iidmap.get(iid) + data = TableEvent(column, row) + return data + + def _search_table_data(self, _): + """Search the table data for records that meet search criteria. + Currently, this search locates any records that contain the + specified text; it is also case insensitive. + """ + criteria = self._searchcriteria.get() + self._filtered = True + self.tablerows_filtered.clear() + self.unload_table_data() + for row in self.tablerows: + for col in row.values: + if str(criteria).lower() in str(col).lower(): + self.tablerows_filtered.append(row) + break + self._rowindex.set(0) + self.load_table_data() + + # PRIVATE METHODS - SORTING + + def _column_sort_header_reset(self): + """Remove the sort character from the column headers""" + for col in self.tablecolumns: + self.view.heading(col.cid, text=col.headertext) + + def _column_sort_header_update(self, cid): + """Add sort character to the sorted column""" + column: TableColumn = self.cidmap.get(int(cid)) + arrow = UPARROW if column.columnsort == ASCENDING else DOWNARROW + headertext = f"{column.headertext} {arrow}" + self.view.heading(column.cid, text=headertext) + + # PRIVATE METHODS - WIDGET BUILDERS + + def _build_tableview_widget(self, coldata, rowdata, bootstyle): + """Build the data table""" + if self._searchable: + self._build_search_frame() + + self.view = ttk.Treeview( + master=self, + columns=[x for x in range(len(coldata))], + height=self._height, + selectmode=EXTENDED, + show=HEADINGS, + bootstyle=f"{bootstyle}-table", + ) + self.view.pack(fill=BOTH, expand=YES, side=TOP) + self.hbar = ttk.Scrollbar( + master=self, command=self.view.xview, orient=HORIZONTAL + ) + self.hbar.pack(fill=X) + self.view.configure(xscrollcommand=self.hbar.set) + + if self._paginated: + self._build_pagination_frame() + + self.build_table_data(coldata, rowdata) + + self._rightclickmenu_cell = TableCellRightClickMenu(self) + self._rightclickmenu_head = TableHeaderRightClickMenu(self) + self._set_widget_binding() + + def _build_search_frame(self): + """Build the search frame containing the search widgets. This + frame is only created if `searchable=True` when creating the + widget. + """ + frame = ttk.Frame(self, padding=5) + frame.pack(fill=X, side=TOP) + ttk.Label(frame, text=MessageCatalog.translate("Search")).pack(side=LEFT, padx=5) + searchterm = ttk.Entry(frame, textvariable=self._searchcriteria) + searchterm.pack(fill=X, side=LEFT, expand=YES) + searchterm.bind("", self._search_table_data) + searchterm.bind("", self._search_table_data) + if not self._paginated: + ttk.Button( + frame, + text=MessageCatalog.translate("⎌"), + command=self.reset_table, + style="symbol.Link.TButton", + ).pack(side=LEFT) + + def _build_pagination_frame(self): + """Build the frame containing the pagination widgets. This + frame is only built if `pagination=True` when creating the + widget. + """ + pageframe = ttk.Frame(self) + pageframe.pack(fill=X, anchor=N) + + ttk.Button( + pageframe, + text=MessageCatalog.translate("⎌"), + command=self.reset_table, + style="symbol.Link.TButton", + ).pack(side=RIGHT) + + ttk.Separator(pageframe, orient=VERTICAL).pack(side=RIGHT, padx=10) + + ttk.Button( + master=pageframe, + text="»", + command=self.goto_last_page, + style="symbol.Link.TButton", + ).pack(side=RIGHT, fill=Y) + ttk.Button( + master=pageframe, + text="›", + command=self.goto_next_page, + style="symbol.Link.TButton", + ).pack(side=RIGHT, fill=Y) + + ttk.Button( + master=pageframe, + text="‹", + command=self.goto_prev_page, + style="symbol.Link.TButton", + ).pack(side=RIGHT, fill=Y) + ttk.Button( + master=pageframe, + text="«", + command=self.goto_first_page, + style="symbol.Link.TButton", + ).pack(side=RIGHT, fill=Y) + + ttk.Separator(pageframe, orient=VERTICAL).pack(side=RIGHT, padx=10) + + lbl = ttk.Label(pageframe, textvariable=self._pagelimit) + lbl.pack(side=RIGHT, padx=(0, 5)) + ttk.Label(pageframe, text=MessageCatalog.translate("of")).pack(side=RIGHT, padx=(5, 0)) + + index = ttk.Entry(pageframe, textvariable=self._pageindex, width=4) + index.pack(side=RIGHT) + index.bind("", self.goto_page, "+") + index.bind("", self.goto_page, "+") + + ttk.Label(pageframe, text=MessageCatalog.translate("Page")).pack(side=RIGHT, padx=5) + + def _build_table_rows(self, rowdata): + """Build, load, and configure the DataTableRow objects + + Parameters: + + rowdata (List): + An iterable of row data + """ + for row in rowdata: + self.insert_row(END, row) + + def _build_table_columns(self, coldata): + """Build, load, and configure the DataTableColumn objects + + Parameters: + + coldata (List[str|Dict[str, Any]]): + An iterable of column names or a dictionary of column + configuration settings. + """ + for cid, col in enumerate(coldata): + if isinstance(col, str): + self.tablecolumns.append( + TableColumn( + tableview=self, + cid=cid, + text=col, + ) + ) + else: + if "text" not in col: + col["text"] = f"Column {cid}" + self.tablecolumns.append( + TableColumn(tableview=self, cid=cid, **col) + ) + + # PRIVATE METHODS - WIDGET BINDING + + def _set_widget_binding(self): + """Setup the widget binding""" + self.view.bind("", self._header_double_leftclick) + self.view.bind("", self._header_leftclick) + if self.tk.call("tk", "windowingsystem") == "aqua": + sequence = "" + else: + sequence = "" + self.view.bind(sequence, self._table_rightclick) + + # add trace to track pagesize changes + self._pagesize.trace_add("write", self._trace_pagesize) + + # def _select_pagesize(self, event): + # cbo: ttk.Combobox = self.nametowidget(event.widget) + # cbo.select_clear() + # self.goto_first_page() + + def _trace_pagesize(self, *_): + """Callback for changes to page size""" + self.goto_first_page() + + def _header_double_leftclick(self, event): + """Callback for double-click events on the tableview header""" + region = self.view.identify_region(event.x, event.y) + if region == "separator": + self.autofit_columns() + + def _header_leftclick(self, event): + """Callback for left-click events""" + region = self.view.identify_region(event.x, event.y) + if region == "heading": + self.sort_column_data(event) + + def _table_rightclick(self, event): + """Callback for right-click events""" + region = self.view.identify_region(event.x, event.y) + if region == "heading": + self._rightclickmenu_head.tk_popup(event) + elif region != "separator": + self._rightclickmenu_cell.tk_popup(event) + + +class TableCellRightClickMenu(tk.Menu): + """A right-click menu object for the tableview cells - INTERNAL""" + + def __init__(self, master: Tableview): + """ + Parameters: + + master (Tableview): + The parent object + """ + super().__init__(master, tearoff=False) + self.master: Tableview = master + self.view: ttk.Treeview = master.view + self.cid = None + self.iid = None + + config = { + "sortascending": { + "label": f'''⬆ {MessageCatalog.translate("Sort Ascending")}''', + "command": self.sort_column_ascending, + }, + "sortdescending": { + "label": f'''⬇ {MessageCatalog.translate("Sort Descending")}''', + "command": self.sort_column_descending, + }, + "clearfilter": { + "label": f'''{MessageCatalog.translate("⎌")} {MessageCatalog.translate("Clear filters")}''', + "command": self.master.reset_row_filters, + }, + "filterbyvalue": { + "label": f'''{MessageCatalog.translate("Filter by cell's value")}''', + "command": self.filter_to_cell_value, + }, + "hiderows": { + "label": f'''{MessageCatalog.translate("Hide select rows")}''', + "command": self.hide_selected_rows, + }, + "showrows": { + "label": f'''{MessageCatalog.translate("Show only select rows")}''', + "command": self.filter_to_selected_rows, + }, + "exportall": { + "label": f'''{MessageCatalog.translate("Export all records")}''', + "command": self.export_all_records, + }, + "exportpage": { + "label": f'''{MessageCatalog.translate("Export current page")}''', + "command": self.export_current_page, + }, + "exportselection": { + "label": f'''{MessageCatalog.translate("Export current selection")}''', + "command": self.export_current_selection, + }, + "exportfiltered": { + "label": f'''{MessageCatalog.translate("Export records in filter")}''', + "command": self.export_records_in_filter, + }, + "moveup": { + "label": f'''↑ {MessageCatalog.translate("Move up")}''', + "command": self.move_row_up + }, + "movedown": { + "label": f'''↓ {MessageCatalog.translate("Move down")}''', + "command": self.move_row_down, + }, + "movetotop": { + "label": f'''⤒ {MessageCatalog.translate("Move to top")}''', + "command": self.move_row_to_top, + }, + "movetobottom": { + "label": f'''⤓ {MessageCatalog.translate("Move to bottom")}''', + "command": self.move_row_to_bottom, + }, + "alignleft": { + "label": f'''◧ {MessageCatalog.translate("Align left")}''', + "command": self.align_column_left, + }, + "aligncenter": { + "label": f'''◫ {MessageCatalog.translate("Align center")}''', + "command": self.align_column_center, + }, + "alignright": { + "label": f'''◨ {MessageCatalog.translate("Align right")}''', + "command": self.align_column_right, + }, + "deleterows": { + "label": f'''🞨 {MessageCatalog.translate("Delete selected rows")}''', + "command": self.delete_selected_rows, + }, + } + sort_menu = tk.Menu(self, tearoff=False) + sort_menu.add_command(cnf=config["sortascending"]) + sort_menu.add_command(cnf=config["sortdescending"]) + self.add_cascade(menu=sort_menu, label=f'''⇅ {MessageCatalog.translate("Sort")}''') + + filter_menu = tk.Menu(self, tearoff=False) + filter_menu.add_command(cnf=config["clearfilter"]) + filter_menu.add_separator() + filter_menu.add_command(cnf=config["filterbyvalue"]) + filter_menu.add_command(cnf=config["hiderows"]) + filter_menu.add_command(cnf=config["showrows"]) + self.add_cascade(menu=filter_menu, label=f'''⧨ {MessageCatalog.translate("Filter")}''') + + export_menu = tk.Menu(self, tearoff=False) + export_menu.add_command(cnf=config["exportall"]) + export_menu.add_command(cnf=config["exportpage"]) + export_menu.add_command(cnf=config["exportselection"]) + export_menu.add_command(cnf=config["exportfiltered"]) + self.add_cascade(menu=export_menu, label=f'''↔ {MessageCatalog.translate("Export")}''') + + move_menu = tk.Menu(self, tearoff=False) + move_menu.add_command(cnf=config["moveup"]) + move_menu.add_command(cnf=config["movedown"]) + move_menu.add_command(cnf=config["movetotop"]) + move_menu.add_command(cnf=config["movetobottom"]) + self.add_cascade(menu=move_menu, label=f'''⇵ {MessageCatalog.translate("Move")}''') + + align_menu = tk.Menu(self, tearoff=False) + align_menu.add_command(cnf=config["alignleft"]) + align_menu.add_command(cnf=config["aligncenter"]) + align_menu.add_command(cnf=config["alignright"]) + self.add_cascade(menu=align_menu, label=f'''↦ {MessageCatalog.translate("Align")}''') + self.add_command(cnf=config["deleterows"]) + + def tk_popup(self, event): + """Display the menu below the selected cell. + + Parameters: + + event (Event): + The click event that triggers menu. + """ + # capture the column and item that invoked the menu + self.event = event + iid = self.view.identify_row(event.y) + col = self.view.identify_column(event.x) + + # show the menu below the invoking cell + rootx = self.view.winfo_rootx() + rooty = self.view.winfo_rooty() + try: + bbox = self.view.bbox(iid, col) + except: + return + try: + super().tk_popup(rootx + bbox[0], rooty + bbox[1] + bbox[3]) + except IndexError: + pass + + def sort_column_ascending(self): + """Sort the column in ascending order.""" + self.master.sort_column_data(self.event, sort=ASCENDING) + + def sort_column_descending(self): + """Sort the column in descending order.""" + self.master.sort_column_data(self.event, sort=DESCENDING) + + def filter_to_cell_value(self): + """Hide all records except for records where the current + column exactly matches the current cell value.""" + self.master.filter_column_to_value(self.event) + + def filter_to_selected_rows(self): + """Hide all records except for the selected rows.""" + self.master.filter_to_selected_rows() + + def export_all_records(self): + """Export all records to a csv file""" + self.master.export_all_records() + + def export_current_page(self): + """Export records on current page""" + self.master.export_current_page() + + def export_current_selection(self): + """Export rows currently selected""" + self.master.export_current_selection() + + def export_records_in_filter(self): + """Export rows currently filtered""" + self.master.export_records_in_filter() + + def hide_selected_rows(self): + """Hide the selected rows""" + self.master.hide_selected_rows() + + def move_row_to_top(self): + """Move the row to the top of the data set""" + self.master.move_selected_rows_to_top() + + def move_row_to_bottom(self): + """Move the row to the bottom of the dataset""" + self.master.move_selected_rows_to_bottom() + + def move_row_up(self): + """Move the selected above the previous sibling""" + self.master.move_selected_row_up() + + def move_row_down(self): + """Move the selected row below the next sibling""" + self.master.move_row_down() + + def align_column_left(self): + "Left align the column text" + self.master.align_column_left(self.event) + + def align_column_right(self): + """Right align the column text""" + self.master.align_column_right(self.event) + + def align_column_center(self): + """Center align the column text""" + self.master.align_column_center(self.event) + + def delete_selected_rows(self): + """Delete the selected rows""" + iids = self.view.selection() + if len(iids) > 0: + # setting to prev should be in master? + prev_item = self.view.prev(iids[0]) + self.master.delete_rows(iids=iids) + self.view.focus(prev_item) + self.view.selection_set(prev_item) + + +class TableHeaderRightClickMenu(tk.Menu): + """A right-click menu object for the tableview header - INTERNAL""" + + def __init__(self, master: Tableview): + """ + Parameters: + + master (Tableview): + The parent object + """ + super().__init__(master, tearoff=False) + self.master: Tableview = self.master + self.view: ttk.Treeview = master.view + self.event = None + self.columnvars = [] + self._show_menu = None + + config = { + "movetoright": { + "label": f'''→ {MessageCatalog.translate("Move to right")}''', + "command": self.move_column_right, + }, + "movetoleft": { + "label": f'''← {MessageCatalog.translate("Move to left")}''', + "command": self.move_column_left, + }, + "movetofirst": { + "label": f'''⇤ {MessageCatalog.translate("Move to first")}''', + "command": self.move_column_to_first, + }, + "movetolast": { + "label": f'''⇥ {MessageCatalog.translate("Move to last")}''', + "command": self.move_column_to_last, + }, + "alignleft": { + "label": f'''◧ {MessageCatalog.translate("Align left")}''', + "command": self.align_heading_left, + }, + "alignright": { + "label": f'''◨ {MessageCatalog.translate("Align right")}''', + "command": self.align_heading_right, + }, + "aligncenter": { + "label": f'''◫ {MessageCatalog.translate("Align center")}''', + "command": self.align_heading_center, + }, + "resettable": { + "label": f'''{MessageCatalog.translate("⎌")} {MessageCatalog.translate("Reset table")}''', + "command": self.master.reset_table, + }, + "deletecolumn": { + "label": f'''🞨 {MessageCatalog.translate("Delete column")}''', + "command": self.delete_column, + }, + "hidecolumn": { + "label": f'''◑ {MessageCatalog.translate("Hide column")}''', + "command": self.hide_column, + }, + } + + self.add_command(cnf=config["resettable"]) + + # HIDE & SHOW + self._build_show_menu() + self.add_cascade(menu=self._show_menu, label=f'''± {MessageCatalog.translate("Columns")}''') + self.add_separator() + + # MOVE MENU + move_menu = tk.Menu(self, tearoff=False) + move_menu.add_command(cnf=config["movetoleft"]) + move_menu.add_command(cnf=config["movetoright"]) + move_menu.add_command(cnf=config["movetofirst"]) + move_menu.add_command(cnf=config["movetolast"]) + self.add_cascade(menu=move_menu, label=f'''⇄ {MessageCatalog.translate("Move")}''') + + align_menu = tk.Menu(self, tearoff=False) + align_menu.add_command(cnf=config["alignleft"]) + align_menu.add_command(cnf=config["aligncenter"]) + align_menu.add_command(cnf=config["alignright"]) + self.add_cascade(menu=align_menu, label=f'''↦ {MessageCatalog.translate("Align")}''') + self.add_command(cnf=config["hidecolumn"]) + self.add_command(cnf=config["deletecolumn"]) + + def tk_popup(self, event): + # capture the column and item that invoked the menu + self.event = event + self._build_show_menu() + + # show the menu below the invoking cell + rootx = self.view.winfo_rootx() + rooty = self.view.winfo_rooty() + super().tk_popup(rootx + event.x, rooty + event.y + 10) + + def _build_show_menu(self): + """Build the show menu based on currently available columns""" + if self._show_menu is not None: + self._show_menu.delete(0, END) + else: + self._show_menu = tk.Menu(self, tearoff=False) + + self._show_menu.add_command( + label=MessageCatalog.translate("Show All"), command=self.show_all_columns + ) + self._show_menu.add_separator() + + displaycolumns = [x.cid for x in self.master.tablecolumns_visible] + for column in self.master.tablecolumns: + varname = f"column_{column.cid}" + # self.columnvars.append(tk.Variable(name=varname, value=True)) + self._show_menu.add_checkbutton( + label=column._headertext, + command=lambda w=column: self.toggle_columns(w.cid), + variable=varname, + onvalue=True, + offvalue=False, + ) + if column.cid in displaycolumns: + self.setvar(varname, True) + else: + self.setvar(varname, False) + + def toggle_columns(self, cid): + """Toggles the visibility of the selected column""" + variable = f"column_{cid}" + toggled = self.getvar(variable) + if toggled: + self.master.unhide_selected_column(cid=int(cid)) + else: + self.master.hide_selected_column(cid=int(cid)) + + def show_all_columns(self): + """Show all columns""" + for var in self.columnvars: + var.set(value=True) + self.master.reset_column_filters() + + def move_column_left(self): + """Move column one position to the left""" + self.master.move_column_left(self.event) + + def move_column_right(self): + """Move column on position to the right""" + self.master.move_column_right(self.event) + + def move_column_to_first(self): + """Move column to leftmost position""" + self.master.move_column_to_first(self.event) + + def move_column_to_last(self): + """Move column to rightmost position""" + self.master.move_column_to_last(self.event) + + def align_heading_left(self): + """Left align the column header""" + self.master.align_heading_left(self.event) + + def align_heading_right(self): + """Right align the column header""" + self.master.align_heading_right(self.event) + + def align_heading_center(self): + """Center align the column header""" + self.master.align_heading_center(self.event) + + def delete_column(self): + """Delete the selected column""" + eo = self.master._get_event_objects(self.event) + eo.column.delete() + + def hide_column(self): + """Hide the selected column""" + eo = self.master._get_event_objects(self.event) + eo.column.hide() diff --git a/pylibraries/ttkbootstrap/themes/__init__.py b/pylibraries/ttkbootstrap/themes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pylibraries/ttkbootstrap/themes/__pycache__/__init__.cpython-39.pyc b/pylibraries/ttkbootstrap/themes/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bde6aa3e22ead604a348fa93e0a3f0c2766e295 GIT binary patch literal 201 zcmYk0F$w}P5Jj_KA;KO+#GS%Q5V5cmY&<~7x`P|sWMVR_?2){Ztw*pE1Sge#_-~ju z&6u*BD9P?KYw~<%{HKs*BTXYgqYi3X?C&aO{lnWsOAdj_xdFC##f}0-U`z~`p0SQ} zOCqX(K@QG(hR)Uy25!kOswE`LI1lD{HYZHLe6EQ&aWnzAMJG;c>n&?th2b$?IcoY2 K*ZGf^RDA*SDmSM9 literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/themes/__pycache__/standard.cpython-39.pyc b/pylibraries/ttkbootstrap/themes/__pycache__/standard.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65cc84ae7d218525901763e5c7f79088eb1c8d98 GIT binary patch literal 3576 zcmeHJS)1HO6>U#u@dTV01BS4J*@h%;sY)fOAcXWL0wD%svxz_`)hW$%rn}WqtL?N| zVwQw`--Fr15|$8l_$&R&AK;nqBOmuxPr}#_yz-K|UrR?*(O(83X{;To)C1Dn{wQEcN{JcEwUr1LRchwCpN$MMT+ z#C#S`;MsT%o{N)s9-dE}8_2`V8|e6gL(CWAMR+k@f|ufDH0R6d?kmW%jZ<`fC0=!i z`D(lduf+~thZ_%=H<5?uy!k5TX|xd_gn&c@MI1#QW~!^0F%qQEcs;HD2HZlNH6?fomxD#jacH+DP@1)~hbUt=PMrUyj z@1oq_jrZWacpq_c^6={URdc={cjE*2Anw73U`WFPmOOw%N!PoFRN5o|5cgsqBT{J< z=iPqJ@fCAUP*FV7gBsn3+H+YeerAT|sW~T@<2)`hcVG_gKIzt8%s&mCfZo@((0d=X#5m+=)^^Q*-A8vXeEb$sIx^PBh< zzK!qTyLcGi!)1IQKfn(?^GEnGe)1Hvg`eUP{0u+GFYrq|ieKT^^!p7lAMyBb*Zmfc z;dgi(zsDc&NBoI6fA(GX82&=|zg_p&%O~;{{&u#t^Y`V(Xt=xAT--V(k~DCVKr%>! zG$bI)A`VjmTC1oi2$W@_yr(RKKx*=2X{NIdlM~sIJ-NtwYs{O^jYvEO9x#BJ=yE^nd6E$jd^Av z3K6jyQpU07CMlJRh3_bpn1B;hFo$16upA?Bx)EmGwl?{?!J7ghu^iFyo*gPp2G9Qt-}1smOxB8q)ei*6M9deRnF zA*-$~vJr?~6j*-B z8JYSQhBRfEvL}QgH3^huL5VO&Mul2s3VIPUCkcdanNSC*D`^Po;t*L@GqUo>ZmI@0%#r(svwf7j5|#QYI15CQ3AWe33aYSx87OcWK!O z7;R`Z0`!du9j|V55fy%ks1W}BB!rUw<>A8E`Hr)twEjittaGK$Ldw+dB;ictxReo9 zP$fGj@(o+f##O#qj!ZLJh8MkC|ZBR1=r4FI+QRZcOK+ znaell6QYCIRXyFyx9+Xx^~8+$GgR-77R&9~eCp~wS6BJQtQwg*-?R(*=IrJh=gqXD z56^5ko{rr8`L=bnosXQEoY^^&Uw`&o=S;tISO1=Kw-4?doPD%aUq{=h zZmRKk(eAtRZdB1C?Z(ZT_I>ll6?JuCMvl`{H>De#NP9Fa+S5g|S54$;-JVgkvfL} literal 0 HcmV?d00001 diff --git a/pylibraries/ttkbootstrap/themes/standard.py b/pylibraries/ttkbootstrap/themes/standard.py new file mode 100644 index 0000000..0beb835 --- /dev/null +++ b/pylibraries/ttkbootstrap/themes/standard.py @@ -0,0 +1,380 @@ +STANDARD_THEMES = { + "cosmo": { + "type": "light", + "colors": { + "primary": "#2780e3", + "secondary": "#7E8081", + "success": "#3fb618", + "info": "#9954bb", + "warning": "#ff7518", + "danger": "#ff0039", + "light": "#F8F9FA", + "dark": "#373A3C", + "bg": "#ffffff", + "fg": "#373a3c", + "selectbg": "#7e8081", + "selectfg": "#ffffff", + "border": "#ced4da", + "inputfg": "#373a3c", + "inputbg": "#fdfdfe", + "active": "#efefef", + }, + }, + "flatly": { + "type": "light", + "colors": { + "primary": "#2c3e50", + "secondary": "#95a5a6", + "success": "#18bc9c", + "info": "#3498db", + "warning": "#f39c12", + "danger": "#e74c3c", + "light": "#ECF0F1", + "dark": "#7B8A8B", + "bg": "#ffffff", + "fg": "#212529", + "selectbg": "#95a5a6", + "selectfg": "#ffffff", + "border": "#ced4da", + "inputfg": "#212529", + "inputbg": "#ffffff", + "active": "#e2e2e2", + }, + }, + "litera": { + "type": "light", + "colors": { + "primary": "#4582ec", + "secondary": "#adb5bd", + "success": "#02b875", + "info": "#17a2b8", + "warning": "#f0ad4e", + "danger": "#d9534f", + "light": "#F8F9FA", + "dark": "#343A40", + "bg": "#ffffff", + "fg": "#343a40", + "selectbg": "#adb5bd", + "selectfg": "#ffffff", + "border": "#bfbfbf", + "inputfg": "#343a40", + "inputbg": "#fff", + "active": "#e5e5e5", + }, + }, + "minty": { + "type": "light", + "colors": { + "primary": "#78c2ad", + "secondary": "#f3969a", + "success": "#56cc9d", + "info": "#6cc3d5", + "warning": "#ffce67", + "danger": "#ff7851", + "light": "#F8F9FA", + "dark": "#343A40", + "bg": "#ffffff", + "fg": "#5a5a5a", + "selectbg": "#f3969a", + "selectfg": "#ffffff", + "border": "#ced4da", + "inputfg": "#696969", + "inputbg": "#fff", + "active": "#e5e5e5", + }, + }, + "lumen": { + "type": "light", + "colors": { + "primary": "#158cba", + "secondary": "#919191", + "success": "#28b62c", + "info": "#75caeb", + "warning": "#ff851b", + "danger": "#ff4136", + "light": "#F6F6F6", + "dark": "#555555", + "bg": "#ffffff", + "fg": "#555555", + "selectbg": "#919191", + "selectfg": "#ffffff", + "border": "#ced4da", + "inputfg": "#555555", + "inputbg": "#fff", + "active": "#e5e5e5", + }, + }, + "sandstone": { + "type": "light", + "colors": { + "primary": "#325D88", + "secondary": "#8e8c84", + "success": "#93c54b", + "info": "#29abe0", + "warning": "#f47c3c", + "danger": "#d9534f", + "light": "#F8F5F0", + "dark": "#3E3F3A", + "bg": "#ffffff", + "fg": "#3e3f3a", + "selectbg": "#8e8c84", + "selectfg": "#ffffff", + "border": "#ced4da", + "inputfg": "#6E6D69", + "inputbg": "#fff", + "active": "#e5e5e5", + }, + }, + "yeti": { + "type": "light", + "colors": { + "primary": "#008cba", + "secondary": "#707070", + "success": "#43ac6a", + "info": "#5bc0de", + "warning": "#e99002", + "danger": "#f04124", + "light": "#EEEEEE", + "dark": "#222222", + "bg": "#ffffff", + "fg": "#222222", + "selectbg": "#707070", + "selectfg": "#ffffff", + "border": "#cccccc", + "inputfg": "#222222", + "inputbg": "#fff", + "active": "#e5e5e5", + }, + }, + "pulse": { + "type": "light", + "colors": { + "primary": "#593196", + "secondary": "#69676E", + "success": "#13b955", + "info": "#009cdc", + "warning": "#efa31d", + "danger": "#fc3939", + "light": "#F9F8FC", + "dark": "#17141F", + "bg": "#ffffff", + "fg": "#444444", + "selectbg": "#69676e", + "selectfg": "#ffffff", + "border": "#cbc8d0", + "inputfg": "#444444", + "inputbg": "#fdfdfe", + "active": "#e5e5e5", + }, + }, + "united": { + "type": "light", + "colors": { + "primary": "#e95420", + "secondary": "#aea79f", + "success": "#38b44a", + "info": "#17a2b8", + "warning": "#efb73e", + "danger": "#df382c", + "light": "#E9ECEF", + "dark": "#772953", + "bg": "#ffffff", + "fg": "#333333", + "selectbg": "#aea79f", + "selectfg": "#ffffff", + "border": "#ced4da", + "inputfg": "#333333", + "inputbg": "#fff", + "active": "#e5e5e5", + }, + }, + "morph": { + "type": "light", + "colors": { + "primary": "#378DFC", + "secondary": "#aaaaaa", + "success": "#43cc29", + "info": "#5B62F4", + "warning": "#FFC107", + "danger": "#E52527", + "light": "#F0F5FA", + "dark": "#212529", + "bg": "#D9E3F1", + "fg": "#7B8AB8", + "selectbg": "#aaaaaa", + "selectfg": "#FBFDFF", + "border": "#B9C7DA", + "inputfg": "#7F8EBA", + "inputbg": "#F0F5FA", + "active": "#C3CCD8", + }, + }, + "journal": { + "type": "light", + "colors": { + "primary": "#eb6864", + "secondary": "#aaaaaa", + "success": "#22b24c", + "info": "#336699", + "warning": "#f5e625", + "danger": "#f57a00", + "light": "#F8F9FA", + "dark": "#222222", + "bg": "#ffffff", + "fg": "#222222", + "selectbg": "#aaaaaa", + "selectfg": "#ffffff", + "border": "#ced4da", + "inputfg": "#565656", + "inputbg": "#fff", + "active": "#e5e5e5", + }, + }, + "darkly": { + "type": "dark", + "colors": { + "primary": "#375a7f", + "secondary": "#444444", + "success": "#00bc8c", + "info": "#3498db", + "warning": "#f39c12", + "danger": "#e74c3c", + "light": "#ADB5BD", + "dark": "#303030", + "bg": "#222222", + "fg": "#ffffff", + "selectbg": "#555555", + "selectfg": "#ffffff", + "border": "#222222", + "inputfg": "#ffffff", + "inputbg": "#2f2f2f", + "active": "#1F1F1F", + }, + }, + "superhero": { + "type": "dark", + "colors": { + "primary": "#4c9be8", + "secondary": "#4e5d6c", + "success": "#5cb85c", + "info": "#5bc0de", + "warning": "#f0ad4e", + "danger": "#d9534f", + "light": "#ABB6C2", + "dark": "#20374C", + "bg": "#2b3e50", + "fg": "#ffffff", + "selectbg": "#526170", + "selectfg": "#ffffff", + "border": "#222222", + "inputfg": "#ebebeb", + "inputbg": "#32465a", + "active": "#2B4155", + }, + }, + "solar": { + "type": "dark", + "colors": { + "primary": "#bc951a", + "secondary": "#94a2a4", + "success": "#44aca4", + "info": "#3f98d7", + "warning": "#d05e2f", + "danger": "#d95092", + "light": "#A9BDBD", + "dark": "#073642", + "bg": "#002B36", + "fg": "#ffffff", + "selectbg": "#0b5162", + "selectfg": "#ffffff", + "border": "#00252e", + "inputfg": "#A9BDBD", + "inputbg": "#073642", + "active": "#002730", + }, + }, + "cyborg": { + "type": "dark", + "colors": { + "primary": "#2a9fd6", + "secondary": "#555555", + "success": "#77b300", + "info": "#9933cc", + "warning": "#ff8800", + "danger": "#cc0000", + "light": "#ADAFAE", + "dark": "#222222", + "bg": "#060606", + "fg": "#ffffff", + "selectbg": "#454545", + "selectfg": "#ffffff", + "border": "#060606", + "inputfg": "#ffffff", + "inputbg": "#191919", + "active": "#282828", + }, + }, + "vapor": { + "type": "dark", + "colors": { + "primary": "#6e40c0", + "secondary": "#ea38b8", + "success": "#3af180", + "info": "#1da2f2", + "warning": "#ffbd05", + "danger": "#e34b54", + "light": "#44d7e8", + "dark": "#170229", + "bg": "#190831", + "fg": "#32fbe2", + "selectbg": "#461a8a", + "selectfg": "#ffffff", + "border": "#060606", + "inputfg": "#bfb6cd", + "inputbg": "#30115e", + "active": "#17082E", + }, + }, + "simplex": { + "type": "light", + "colors": { + "primary": "#d8220e", + "secondary": "#858e96", + "success": "#469307", + "info": "#0099ce", + "warning": "#d88220", + "danger": "#9a479e", + "light": "#f2f2f2", + "dark": "#3b3d3f", + "bg": "#fcfcfc", + "fg": "#3b3d3f", + "selectbg": "#a9afb6", + "selectfg": "#ffffff", + "border": "#858e96", + "inputfg": "#3b3d3f", + "inputbg": "#fcfcfc", + "active": "#e2e2e2", + }, + }, + "cerculean": { + "type": "light", + "colors": { + "primary": "#4bb1ea", + "secondary": "#a9b4be", + "success": "#84b251", + "info": "#225384", + "warning": "#e16e25", + "danger": "#cf3c40", + "light": "#eceef1", + "dark": "#33383e", + "bg": "#ffffff", + "fg": "#2ea4e7", + "selectbg": "#adb5bd", + "selectfg": "#ffffff", + "border": "#a9b4be", + "inputfg": "#495057", + "inputbg": "#ffffff", + "active": "#e5e5e5", + }, + }, +} diff --git a/pylibraries/ttkbootstrap/themes/user.py b/pylibraries/ttkbootstrap/themes/user.py new file mode 100644 index 0000000..76a5299 --- /dev/null +++ b/pylibraries/ttkbootstrap/themes/user.py @@ -0,0 +1 @@ +USER_THEMES={} \ No newline at end of file diff --git a/pylibraries/ttkbootstrap/toast.py b/pylibraries/ttkbootstrap/toast.py new file mode 100644 index 0000000..d44caf8 --- /dev/null +++ b/pylibraries/ttkbootstrap/toast.py @@ -0,0 +1,243 @@ +from tkinter import font +import ttkbootstrap as ttk +from ttkbootstrap.constants import * +from ttkbootstrap import utility + +# https://www.fontspace.com/freeserif-font-f13277 + +DEFAULT_ICON_WIN32 = "\ue154" +DEFAULT_ICON = "\u25f0" + + +class ToastNotification: + """A semi-transparent popup window for temporary alerts or messages. + You may choose to display the toast for a specified period of time, + otherwise you must click the toast to close it. + + ![toast notification](../assets/toast/toast.png) + + Examples: + + ```python + import ttkbootstrap as ttk + from ttkbootstrap.toast import ToastNotification + + app = ttk.Window() + + toast = ToastNotification( + title="ttkbootstrap toast message", + message="This is a toast message", + duration=3000, + ) + toast.show_toast() + + app.mainloop() + ``` + """ + + def __init__( + self, + title, + message, + duration=None, + bootstyle=LIGHT, + alert=False, + icon=None, + iconfont=None, + position=None, + **kwargs, + ): + """ + Parameters: + + title (str): + The toast title. + + message (str): + The toast message. + + duration (int): + The number of milliseconds to show the toast. If None + (default), then you must click the toast to close it. + + bootstyle (str): + Style keywords used to updated the label style. One of + the accepted color keywords. + + alert (bool): + Indicates whether to ring the display bell when the + toast is shown. + + icon (str): + A unicode character to display on the top-left hand + corner of the toast. The default symbol is OS specific. + Pass an empty string to remove the symbol. + + iconfont (Union[str, Font]): + The font used to render the icon. By default, this is + OS specific. You may need to change the font to enable + better character or emoji support for the icon you + want to use. Windows (Segoe UI Symbol), + Linux (FreeSerif), MacOS (Apple Symbol) + + position (Tuple[int, int, str]): + A tuple that controls the position of the toast. Default + is OS specific. The tuple cooresponds to + (horizontal, vertical, anchor), where the horizontal and + vertical elements represent the position of the toplevel + releative to the anchor, which is "ne" or top-left by + default. Acceptable anchors include: n, e, s, w, nw, ne, + sw, se. For example: (100, 100, 'ne'). + + **kwargs (Dict): + Other keyword arguments passed to the `Toplevel` window. + """ + self.message = message + self.title = title + self.duration = duration + self.bootstyle = bootstyle + self.icon = icon + self.iconfont = iconfont + self.iconfont = None + self.titlefont = None + self.toplevel = None + self.kwargs = kwargs + self.alert = alert + self.position = position + + if "overrideredirect" not in self.kwargs: + self.kwargs["overrideredirect"] = True + if "alpha" not in self.kwargs: + self.kwargs["alpha"] = 0.95 + + if position is not None: + if len(position) != 3: + self.position = None + + def show_toast(self, *_): + """Create and show the toast window.""" + + # build toast + self.toplevel = ttk.Toplevel(**self.kwargs) + self._setup(self.toplevel) + + self.container = ttk.Frame(self.toplevel, bootstyle=self.bootstyle) + self.container.pack(fill=BOTH, expand=YES) + ttk.Label( + self.container, + text=self.icon, + font=self.iconfont, + bootstyle=f"{self.bootstyle}-inverse", + anchor=NW, + ).grid(row=0, column=0, rowspan=2, sticky=NSEW, padx=(5, 0)) + ttk.Label( + self.container, + text=self.title, + font=self.titlefont, + bootstyle=f"{self.bootstyle}-inverse", + anchor=NW, + ).grid(row=0, column=1, sticky=NSEW, padx=10, pady=(5, 0)) + ttk.Label( + self.container, + text=self.message, + wraplength=utility.scale_size(self.toplevel, 300), + bootstyle=f"{self.bootstyle}-inverse", + anchor=NW, + ).grid(row=1, column=1, sticky=NSEW, padx=10, pady=(0, 5)) + + self.toplevel.bind("", self.hide_toast) + + # alert toast + if self.alert: + self.toplevel.bell() + + # specified duration to close + if self.duration: + self.toplevel.after(self.duration, self.hide_toast) + + def hide_toast(self, *_): + """Destroy and close the toast window.""" + try: + alpha = float(self.toplevel.attributes("-alpha")) + if alpha <= 0.1: + self.toplevel.destroy() + else: + self.toplevel.attributes("-alpha", alpha - 0.1) + self.toplevel.after(25, self.hide_toast) + except: + if self.toplevel: + self.toplevel.destroy() + + def _setup(self, window: ttk.Toplevel): + winsys = window.tk.call("tk", "windowingsystem") + + self.toplevel.configure(relief=RAISED) + + # minsize + if "minsize" not in self.kwargs: + w, h = utility.scale_size(self.toplevel, [300, 75]) + self.toplevel.minsize(w, h) + + # heading font + _font = font.nametofont("TkDefaultFont") + self.titlefont = font.Font( + family=_font["family"], + size=_font["size"] + 1, + weight="bold", + ) + # symbol font + self.iconfont = font.Font(size=30, weight="bold") + if winsys == "win32": + self.iconfont["family"] = "Segoe UI Symbol" + self.icon = DEFAULT_ICON_WIN32 if self.icon is None else self.icon + if self.position is None: + x, y = utility.scale_size(self.toplevel, [5, 50]) + self.position = (x, y, SE) + elif winsys == "x11": + self.iconfont["family"] = "FreeSerif" + self.icon = DEFAULT_ICON if self.icon is None else self.icon + if self.position is None: + x, y = utility.scale_size(self.toplevel, [0, 0]) + self.position = (x, y, SE) + else: + self.iconfont["family"] = "Apple Symbols" + self.toplevel.update_idletasks() + self.icon = DEFAULT_ICON if self.icon is None else self.icon + if self.position is None: + x, y = utility.scale_size(self.toplevel, [50, 50]) + self.position = (x, y, NE) + + self.set_geometry() + + def set_geometry(self): + self.toplevel.update_idletasks() # actualize geometry + anchor = self.position[-1] + x_anchor = "-" if "w" not in anchor else "+" + y_anchor = "-" if "n" not in anchor else "+" + screen_w = self.toplevel.winfo_screenwidth() // 2 + screen_h = self.toplevel.winfo_screenheight() // 2 + top_w = self.toplevel.winfo_width() // 2 + top_h = self.toplevel.winfo_height() // 2 + + if all(["e" not in anchor, "w" not in anchor]): + xpos = screen_w - top_w + else: + xpos = self.position[0] + if all(["n" not in anchor, "s" not in anchor]): + ypos = screen_h - top_h + else: + ypos = self.position[1] + + self.toplevel.geometry(f"{x_anchor}{xpos}{y_anchor}{ypos}") + + +if __name__ == "__main__": + + app = ttk.Window() + + ToastNotification( + "ttkbootstrap toast message", + "This is a toast message; you can place a symbol on the top-left that is supported by the selected font. You can either make it appear for a specified period of time, or click to close.", + ).show_toast() + + app.mainloop() diff --git a/pylibraries/ttkbootstrap/tooltip.py b/pylibraries/ttkbootstrap/tooltip.py new file mode 100644 index 0000000..25f5e66 --- /dev/null +++ b/pylibraries/ttkbootstrap/tooltip.py @@ -0,0 +1,175 @@ +import ttkbootstrap as ttk +from ttkbootstrap.constants import * +from ttkbootstrap import utility + + +class ToolTip: + """A semi-transparent tooltip popup window that shows text when the + mouse is hovering over the widget and closes when the mouse is no + longer hovering over the widget. Clicking a mouse button will also + close the tooltip. + + ![](../assets/tooltip/tooltip.gif) + + Examples: + + ```python + import ttkbootstrap as ttk + from ttkbootstrap.constants import * + from ttkbootstrap.tooltip import ToolTip + + app = ttk.Window() + b1 = ttk.Button(app, text="default tooltip") + b1.pack() + b2 = ttk.Button(app, text="styled tooltip") + b2.pack() + + # default tooltip + ToolTip(b1, text="This is the default style") + + # styled tooltip + ToolTip(b2, text="This is dangerous", bootstyle=(DANGER, INVERSE)) + + app.mainloop() + ``` + """ + + def __init__( + self, + widget, + text="widget info", + bootstyle=None, + wraplength=None, + delay=250, # milliseconds + **kwargs, + ): + """ + Parameters: + + widget (Widget): + The tooltip window will position over this widget when + hovering. + + text (str): + The text to display in the tooltip window. + + bootstyle (str): + The style to apply to the tooltip label. You can use + any of the standard ttkbootstrap label styles. + + wraplength (int): + The width of the tooltip window in screenunits before the + text is wrapped to the next line. By default, this will be + a scaled factor of 300. + + **kwargs (Dict): + Other keyword arguments passed to the `Toplevel` window. + """ + self.widget = widget + self.text = text + self.bootstyle = bootstyle + self.wraplength = wraplength or utility.scale_size(self.widget, 300) + self.toplevel = None + self.delay = delay + self.id = None + + # set keyword arguments + kwargs["overrideredirect"] = True + kwargs["master"] = self.widget + if "alpha" not in kwargs: + kwargs["alpha"] = 0.95 + self.toplevel_kwargs = kwargs + + # create default tooltip style + ttk.Style().configure( + style="tooltip.TLabel", + background="#fffddd", + foreground="#333", + bordercolor="#888", + borderwidth=1, + darkcolor="#fffddd", + lightcolor="#fffddd", + relief=RAISED, + ) + + # event binding + self.widget.bind("", self.enter) + self.widget.bind("", self.leave) + self.widget.bind("", self.move_tip) + self.widget.bind("", self.leave) + + def enter(self, event=None): + self.schedule() + + def leave(self, event=None): + self.unschedule() + self.hide_tip() + + def schedule(self): + self.unschedule() + self.id = self.widget.after(self.delay, self.show_tip) + + def unschedule(self): + id = self.id + self.id = None + if id: + self.widget.after_cancel(id) + + def show_tip(self, *_): + """Create a show the tooltip window""" + if self.toplevel: + return + x = self.widget.winfo_pointerx() + 25 + y = self.widget.winfo_pointery() + 10 + + self.toplevel = ttk.Toplevel(position=(x, y), **self.toplevel_kwargs) + lbl = ttk.Label( + master=self.toplevel, + text=self.text, + justify=LEFT, + wraplength=self.wraplength, + padding=10, + ) + lbl.pack(fill=BOTH, expand=YES) + if self.bootstyle: + lbl.configure(bootstyle=self.bootstyle) + else: + lbl.configure(style="tooltip.TLabel") + + def move_tip(self, *_): + """Move the tooltip window to the current mouse position within the + widget. + """ + if self.toplevel: + x = self.widget.winfo_pointerx() + 25 + y = self.widget.winfo_pointery() + 10 + self.toplevel.geometry(f"+{x}+{y}") + + def hide_tip(self, *_): + """Destroy the tooltip window.""" + if self.toplevel: + self.toplevel.destroy() + self.toplevel = None + + +if __name__ == "__main__": + + app = ttk.Window() + + b1 = ttk.Button(app, text="default tooltip") + b1.pack(side=LEFT, padx=20, pady=20, fill=X, expand=YES) + + l1 = ttk.Label(app, text="styled tooltip") + l1.pack(side=LEFT, padx=20, pady=20, fill=X, expand=YES) + + ToolTip( + b1, + text="This is the default tooltip style", + ) + ToolTip( + l1, + text="Do not touch this label unless you are sure you want to do something dangerous.", + bootstyle="danger-inverse", + ) + + app.mainloop() diff --git a/pylibraries/ttkbootstrap/utility.py b/pylibraries/ttkbootstrap/utility.py new file mode 100644 index 0000000..3d3f6ed --- /dev/null +++ b/pylibraries/ttkbootstrap/utility.py @@ -0,0 +1,100 @@ +def enable_high_dpi_awareness(root=None, scaling=None): + """Enable high dpi awareness. + + **Windows OS** + Call the method BEFORE creating the `Tk` object. No parameters + required. + + **Linux OS** + Must provided the `root` and `scaling` parameters. Call the method + AFTER creating the `Tk` object. A number between 1.6 and 2.0 is + usually suffient to scale for high-dpi screen. + + !!! warning + If the `root` argument is provided, then `scaling` must also + be provided. Otherwise, there is no effect. + + Parameters: + + root (tk.Tk): + The root widget + + scaling (float): + Sets and queries the current scaling factor used by Tk to + convert between physical units (for example, points, + inches, or millimeters) and pixels. The number argument is + a floating point number that specifies the number of pixels + per point on window's display. If the window argument is + omitted, it defaults to the main window. If the number + argument is omitted, the current value of the scaling + factor is returned. + + A “point” is a unit of measurement equal to 1/72 inch. A + scaling factor of 1.0 corresponds to 1 pixel per point, + which is equivalent to a standard 72 dpi monitor. A scaling + factor of 1.25 would mean 1.25 pixels per point, which is + the setting for a 90 dpi monitor; setting the scaling factor + to 1.25 on a 72 dpi monitor would cause everything in the + application to be displayed 1.25 times as large as normal. + The initial value for the scaling factor is set when the + application starts, based on properties of the installed + monitor, but it can be changed at any time. Measurements + made after the scaling factor is changed will use the new + scaling factor, but it is undefined whether existing + widgets will resize themselves dynamically to accommodate + the new scaling factor. + """ + try: + from ctypes import windll + windll.user32.SetProcessDPIAware() + except: + pass + + try: + if root and scaling: + root.tk.call('tk', 'scaling', scaling) + except: + pass + +def get_image_name(image): + """Extract and return the tcl/tk image name from a PhotoImage + object. + + Parameters: + + image (ImageTk.PhotoImage): + A photoimage object. + + Returns: + + str: + The tcl/tk name of the photoimage object. + """ + return image._PhotoImage__photo.name + +def scale_size(widget, size): + """Scale the size based on the scaling factor of tkinter. + This is used most frequently to adjust the assets for + image-based widget layouts and font sizes. + + Parameters: + + widget (Widget): + The widget object. + + size (Union[int, List, Tuple]): + A single integer or an iterable of integers + + Returns: + + Union[int, List]: + An integer or list of integers representing the new size. + """ + BASELINE = 1.33398982438864281 + scaling = widget.tk.call('tk', 'scaling') + factor = scaling / BASELINE + + if isinstance(size, int): + return int(size * factor) + elif isinstance(size, tuple) or isinstance(size, list): + return [int(x * factor) for x in size] \ No newline at end of file diff --git a/pylibraries/ttkbootstrap/validation.py b/pylibraries/ttkbootstrap/validation.py new file mode 100644 index 0000000..603703b --- /dev/null +++ b/pylibraries/ttkbootstrap/validation.py @@ -0,0 +1,331 @@ +""" + This module contains classes and functions that are used to add + validation to Entry, Spinbox, and Combobox widgets. Several helper + methods are included which start with the "add" prefix. + + ## Using predefined methods + + When validation is applied to a widget and the input is determined + to be invalid, a 'danger' colored border is applied to the widget. + This border disappears when the widget is determined to have valid + contents. + + Below are a few examples using predefined validation. Browse the + full list in the documentation below: + ```python + app = ttk.Window() + + entry = ttk.Entry() + entry.pack(padx=10, pady=10) + + # check if contents is text + add_text_validation(entry) + + # prevent any entry except text + add_text_validation(entry, when='key') + + # check for a specific list of options + add_option_validation(entry, ['red', 'blue', 'green']) + + # validate against a specific regex expression + add_regex_validation(entry, r'\d{4}-\d{2}-\d{2}') + ``` + + ## Adding a custom validation + + First, create a custom validation function. This must accept a + `ValidationEvent` object and should return a boolean. You should + also use the @validator decorator to convert this method to a + validation method. Check the `ValidationEvent` attributes to + learn about what is returned in this event. + + ```python + from ttkbootstrap import validator, add_validation + + @validator + def validate_long_text(event): + if len(event.postchangetext) > 20: + return True + else: + return False + ``` + + Apply your custom validation to the widget + ```python + add_validation(entry, validate_long_text) + ``` +""" +import ttkbootstrap as ttk +import re + + +class ValidationEvent: + """Contains the attributes of a validation event returned by the + `validatecommand` on a tkinter widget. + + Attributes: + + actioncode (str): + 0 for an attempted deletion, 1 for an attempted insertion, + or -1 if the callback was for focusin, focusout, or a + change to the textvariable. + + insertdeletetext (str): + When the user attempts to insert or delete text, this + attribute will be the index of the beginning of the + insertion or deletion. If the callback was due to focusin, + focusout, or a change to the textvariable, the attribute + will be -1. + + postchangetext (str): + The value that the text will have if the change is allowed. + + prechangetext (str): + The text in the entry before the change. + + insertdeletetext (str): + The text inserted or deleted if the call was due to an + insertion or deletion. + + validationtype (str): + Specifies the widget's validation option which specifies + _when_ the validation will occur. + + widget (Widget): + The widget object that is being validated. + """ + + def __init__(self, d, i, P, s, S, v, V, W): + self.actioncode = d + self.insertdeletetext = i + self.postchangetext = P + self.prechangetext = s + self.insertdeletetext = S + self.validationtype = v + self.validationreason = V + + style = ttk.Style.get_instance() + self.widget = style.master.nametowidget( + W + ) # replace with another method + + +def validator(func): + """Decorates a standard function so that it receives the validation + events returned by the validate command on the tkinter widgets. + + Parameters: + + func (Callable): + The validation function to be decorated. + """ + + def inner(*args, **kw): + event = ValidationEvent(*args) + return func(event, **kw) + + return inner + + +def add_validation(widget, func, when="focusout", **kwargs): + """Adds validation to the widget of type `Entry`, `Combobox`, or + `Spinbox`. The func should accept a parameter of type + `ValidationEvent` and should return a boolean value. + + Parameters: + + widget (Widget): + The widget on which validation will be applied. + + func (Callable): + The function that will be called when a validation event + occurs. + + when (str): + Indicates when the validation event should occur. Possible + values include: + + * focus - whenever the widget gets or loses focus + * focusin - whenever the widget gets focus + * focusout - whenever the widget loses focus + * key - whenever a key is pressed + * all - validate in all of the above situations + + kwargs (Dict): + Optional arguments passed to the callback. + """ + f = widget.register(lambda *e: func(*e, **kwargs)) + subs = (r"%d", r"%i", r"%P", r"%s", r"%S", r"%v", r"%V", r"%W") + widget.configure(validate=when, validatecommand=(f, *subs)) + + +@validator +def _validate_text(event: ValidationEvent): + """Contents is text.""" + if len(event.postchangetext) == 0: + return True + return str(event.postchangetext).isalpha() + + +@validator +def _validate_number(event: ValidationEvent): + """Contents is a number.""" + if len(event.postchangetext) == 0: + return True + return str(event.postchangetext).isnumeric() + + +@validator +def _validate_options(event: ValidationEvent, options): + """Contents is in a list of options""" + return event.postchangetext in options + + +@validator +def _validate_range(event: ValidationEvent, startrange, endrange): + """Contents is a number between the startrange and endrange + inclusive + """ + if len(event.postchangetext) == 0: + return True + try: + num = float(event.postchangetext) + result = num >= startrange and num <= endrange + return result + except: + return False + + +@validator +def _validate_regex(event: ValidationEvent, pattern): + """Contents matches a regex expression""" + match = re.match(pattern, event.postchangetext) + return match is not None + + +# helper methods + + +def add_text_validation(widget, when="focusout"): + """Check if widget contents is alpha. Sets the state to 'Invalid' + if not text. + + Parameters: + + widget (Widget): + The widget on which to add validation. + + when (str): + Specifies when to apply validation. See the `add_validation` + method docstring for a full list of options. + """ + add_validation(widget, _validate_text, when=when) + + +def add_numeric_validation(widget, when="focusout"): + """Check if widget contents is numeric. Sets the state to 'Invalid' + if not a number. + + Parameters: + + widget (Widget): + The widget on which to add validation. + + when (str): + Specifies when to apply validation. See the `add_validation` + method docstring for a full list of options. + """ + add_validation(widget, _validate_number, when=when) + + +def add_phonenumber_validation(widget, when="focusout"): + """Check if the widget contents matches a phone number pattern. + + Parameters: + + widget (Widget): + The widget on which to add validation. + + when (str): + Specifies when to apply validation. See the `add_validation` + method docstring for a full list of options. + """ + pattern = r"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$" + add_validation(widget, _validate_regex, pattern=pattern, when=when) + + +def add_regex_validation(widget, pattern, when="focusout"): + """Check if widget contents matches regular expresssion. Sets the + state to 'Invalid' if no match is found. + + Parameters: + + widget (Widget): + The widget on which to add validation. + + when (str): + Specifies when to apply validation. See the `add_validation` + method docstring for a full list of options. + """ + add_validation(widget, _validate_regex, pattern=pattern, when=when) + + +def add_range_validation(widget, startrange, endrange, when="focusout"): + """Check if widget contents is within a range of numbers, inclusive. + Sets the state to 'Invalid' if the number is outside of the range. + + Parameters: + + widget (Widget): + The widget on which to add validation. + + when (str): + Specifies when to apply validation. See the `add_validation` + method docstring for a full list of options. + """ + add_validation( + widget, + _validate_range, + startrange=startrange, + endrange=endrange, + when=when, + ) + + +def add_option_validation(widget, options, when="focusout"): + """Check if the widget contents is in a list of options. + + + Parameters: + + widget (Widget): + The widget on which to add validation. + + when (str): + Specifies when to apply validation. See the `add_validation` + method docstring for a full list of options. + """ + add_validation(widget, _validate_options, options=options, when=when) + + +if __name__ == "__main__": + + app = ttk.Window() + + @validator + def myvalidation(event: ValidationEvent) -> bool: + print(event.postchangetext) + return True + + entry = ttk.Entry() + entry.pack(padx=10, pady=10) + entry2 = ttk.Entry() + entry2.pack(padx=10, pady=10) + # add_validation(entry, validate_range, startrange=5, endrange=10) + # add_validation(entry, validate_regex, pattern="israel") + add_text_validation(entry, when="key") # prevents from using any numbers + add_text_validation(entry2, when="key") + # add_option_validation(entry, ['red', 'blue', 'green'], 'focusout') + # add_regex_validation(entry, r'\d{4}-\d{2}-\d{2}') + ttk.Button(text="Other").pack(padx=10, pady=10) + app.mainloop() diff --git a/pylibraries/ttkbootstrap/widgets.py b/pylibraries/ttkbootstrap/widgets.py new file mode 100644 index 0000000..afbc7cb --- /dev/null +++ b/pylibraries/ttkbootstrap/widgets.py @@ -0,0 +1,1162 @@ +import tkinter as tk +from tkinter import ttk +from tkinter import font +from tkinter.ttk import Button, Checkbutton, Combobox +from tkinter.ttk import Entry, Frame, Label +from tkinter.ttk import Labelframe, LabelFrame, Menubutton +from tkinter.ttk import Notebook, OptionMenu, PanedWindow +from tkinter.ttk import Panedwindow, Progressbar, Radiobutton +from tkinter.ttk import Scale, Scrollbar, Separator +from tkinter.ttk import Sizegrip, Spinbox, Treeview +from ttkbootstrap.constants import * + +# date entry imports +from ttkbootstrap.dialogs import Querybox +from datetime import datetime + +# floodgauge imports +import math + +# meter imports +from PIL import Image, ImageTk, ImageDraw +from ttkbootstrap.style import Colors +from ttkbootstrap import utility +from ttkbootstrap.style import Bootstyle + +M = 3 # meter image scale, higher number increases resolution + +TTK_WIDGETS = ( + ttk.Button, + ttk.Checkbutton, + ttk.Combobox, + ttk.Entry, + ttk.Frame, + ttk.Labelframe, + ttk.Label, + ttk.Menubutton, + ttk.Notebook, + ttk.Panedwindow, + ttk.Progressbar, + ttk.Radiobutton, + ttk.Scale, + ttk.Scrollbar, + ttk.Separator, + ttk.Sizegrip, + ttk.Spinbox, + ttk.Treeview, + ttk.OptionMenu, +) + +TK_WIDGETS = ( + tk.Tk, + tk.Toplevel, + tk.Button, + tk.Label, + tk.Text, + tk.Frame, + tk.Checkbutton, + tk.Radiobutton, + tk.Entry, + tk.Scale, + tk.Listbox, + tk.Menu, + tk.Menubutton, + tk.LabelFrame, + tk.Canvas, + tk.OptionMenu, + tk.Spinbox, +) + + +class DateEntry(ttk.Frame): + """A date entry widget combines the `Combobox` and a `Button` + with a callback attached to the `get_date` function. + + When pressed, a date chooser popup is displayed. The returned + value is inserted into the combobox. + + The date chooser popup will use the date in the combobox as the + date of focus if it is in the format specified by the + `dateformat` parameter. By default, this format is "%Y-%m-%d". + + The bootstyle api may be used to change the style of the widget. + The available colors include -> primary, secondary, success, + info, warning, danger, light, dark. + + The starting weekday on the date chooser popup can be changed + with the `firstweekday` parameter. By default this value is + `6`, which represents "Sunday". + + The `Entry` and `Button` widgets are accessible from the + `DateEntry.Entry` and `DateEntry.Button` properties. + + ![](../../assets/widgets/date-entry.png) + """ + + def __init__( + self, + master=None, + dateformat=r"%x", + firstweekday=6, + startdate=None, + bootstyle="", + **kwargs, + ): + """ + Parameters: + + master (Widget, optional): + The parent widget. + + dateformat (str, optional): + The format string used to render the text in the entry + widget. For more information on acceptable formats, see https://strftime.org/ + + firstweekday (int, optional): + Specifies the first day of the week. 0=Monday, 1=Tuesday, + etc... + + startdate (datetime, optional): + The date that is in focus when the widget is displayed. Default is + current date. + + bootstyle (str, optional): + A style keyword used to set the focus color of the entry + and the background color of the date button. Available + options include -> primary, secondary, success, info, + warning, danger, dark, light. + + **kwargs (Dict[str, Any], optional): + Other keyword arguments passed to the frame containing the + entry and date button. + """ + self._dateformat = dateformat + self._firstweekday = firstweekday + + self._startdate = startdate or datetime.today() + self._bootstyle = bootstyle + super().__init__(master, **kwargs) + + # add visual components + entry_kwargs = {"bootstyle": self._bootstyle} + if "width" in kwargs: + entry_kwargs["width"] = kwargs.pop("width") + + self.entry = ttk.Entry(self, **entry_kwargs) + self.entry.pack(side=tk.LEFT, fill=tk.X, expand=tk.YES) + + self.button = ttk.Button( + master=self, + command=self._on_date_ask, + bootstyle=f"{self._bootstyle}-date", + ) + self.button.pack(side=tk.LEFT) + + # starting value + self.entry.insert(tk.END, self._startdate.strftime(self._dateformat)) + + def __getitem__(self, key: str): + return self.configure(cnf=key) + + def __setitem__(self, key: str, value): + self.configure(cnf=None, **{key: value}) + + def _configure_set(self, **kwargs): + """Override configure method to allow for setting custom + DateEntry parameters""" + + if "state" in kwargs: + state = kwargs.pop("state") + if state in ["readonly", "invalid"]: + self.entry.configure(state=state) + elif state in ("disabled", "normal"): + self.entry.configure(state=state) + self.button.configure(state=state) + else: + kwargs[state] = state + if "dateformat" in kwargs: + self._dateformat = kwargs.pop("dateformat") + if "firstweekday" in kwargs: + self._firstweekday = kwargs.pop("firstweekday") + if "startdate" in kwargs: + self._startdate = kwargs.pop("startdate") + if "bootstyle" in kwargs: + self._bootstyle = kwargs.pop("bootstyle") + self.entry.configure(bootstyle=self._bootstyle) + self.button.configure(bootstyle=[self._bootstyle, "date"]) + if "width" in kwargs: + width = kwargs.pop("width") + self.entry.configure(width=width) + + super(ttk.Frame, self).configure(**kwargs) + + def _configure_get(self, cnf): + """Override the configure get method""" + if cnf == "state": + entrystate = self.entry.cget("state") + buttonstate = self.button.cget("state") + return {"Entry": entrystate, "Button": buttonstate} + if cnf == "dateformat": + return self._dateformat + if cnf == "firstweekday": + return self._firstweekday + if cnf == "startdate": + return self._startdate + if cnf == "bootstyle": + return self._bootstyle + else: + return super(ttk.Frame, self).configure(cnf=cnf) + + def configure(self, cnf=None, **kwargs): + """Configure the options for this widget. + + Parameters: + + cnf (Dict[str, Any], optional): + A dictionary of configuration options. + + **kwargs: + Optional keyword arguments. + """ + if cnf is not None: + return self._configure_get(cnf) + else: + return self._configure_set(**kwargs) + + def _on_date_ask(self): + """Callback for pushing the date button""" + _val = self.entry.get() or datetime.today().strftime(self._dateformat) + try: + self._startdate = datetime.strptime(_val, self._dateformat) + except Exception as e: + print("Date entry text does not match", self._dateformat) + self._startdate = datetime.today() + self.entry.delete(first=0, last=tk.END) + self.entry.insert( + tk.END, self._startdate.strftime(self._dateformat) + ) + + old_date = datetime.strptime(_val, self._dateformat) + + # get the new date and insert into the entry + new_date = Querybox.get_date( + parent=self.entry, + startdate=old_date, + firstweekday=self._firstweekday, + bootstyle=self._bootstyle, + ) + self.entry.delete(first=0, last=tk.END) + self.entry.insert(tk.END, new_date.strftime(self._dateformat)) + self.entry.focus_force() + + +class Floodgauge(Progressbar): + """A widget that shows the status of a long-running operation + with an optional text indicator. + + Similar to the `ttk.Progressbar`, this widget can operate in + two modes. *determinate* mode shows the amount completed + relative to the total amount of work to be done, and + *indeterminate* mode provides an animated display to let the + user know that something is happening. + + Variable are generated automatically for this widget and can be + linked to other widgets by referencing them via the + `textvariable` and `variable` attributes. + + ![](../../assets/widgets/floodgauge.gif) + + Examples: + + ```python + import ttkbootstrap as ttk + from ttkbootstrap.constants import * + + app = ttk.Window(size=(500, 500)) + + gauge = ttk.Floodgauge( + bootstyle=INFO, + font=(None, 24, 'bold'), + mask='Memory Used {}%', + ) + gauge.pack(fill=BOTH, expand=YES, padx=10, pady=10) + + # autoincrement the gauge + gauge.start() + + # stop the autoincrement + gauge.stop() + + # manually update the gauge value + gauge.configure(value=25) + + # increment the value by 10 steps + gauge.step(10) + + app.mainloop() + ``` + """ + + def __init__( + self, + master=None, + cursor=None, + font=None, + length=None, + maximum=100, + mode=DETERMINATE, + orient=HORIZONTAL, + bootstyle=PRIMARY, + takefocus=False, + text=None, + value=0, + mask=None, + **kwargs, + ): + """ + Parameters: + + master (Widget, optional): + Parent widget. Defaults to None. + + cursor (str, optional): + The cursor that will appear when the mouse is over the + progress bar. Defaults to None. + + font (Union[Font, str], optional): + The font to use for the progress bar label. + + length (int, optional): + Specifies the length of the long axis of the progress bar + (width if orient = horizontal, height if if vertical); + + maximum (float, optional): + A floating point number specifying the maximum `value`. + Defaults to 100. + + mode ('determinate', 'indeterminate'): + Use `indeterminate` if you cannot accurately measure the + relative progress of the underlying process. In this mode, + a rectangle bounces back and forth between the ends of the + widget once you use the `Floodgauge.start()` method. + Otherwise, use `determinate` if the relative progress can be + calculated in advance. + + orient ('horizontal', 'vertical'): + Specifies the orientation of the widget. + + bootstyle (str, optional): + The style used to render the widget. Options include + primary, secondary, success, info, warning, danger, light, + dark. + + takefocus (bool, optional): + This widget is not included in focus traversal by default. + To add the widget to focus traversal, use + `takefocus=True`. + + text (str, optional): + A string of text to be displayed in the Floodgauge label. + This is assigned to the attribute `Floodgauge.textvariable` + + value (float, optional): + The current value of the progressbar. In `determinate` + mode, this represents the amount of work completed. In + `indeterminate` mode, it is interpreted modulo `maximum`; + that is, the progress bar completes one "cycle" when the + `value` increases by `maximum`. + + mask (str, optional): + A string format that can be used to update the Floodgauge + label every time the value is updated. For example, the + string "{}% Storage Used" with a widget value of 45 would + show "45% Storage Used" on the Floodgauge label. If a + mask is set, then the `text` option is ignored. + + **kwargs: + Other configuration options from the option database. + """ + # progress bar value variables + if 'variable' in kwargs: + self._variable = kwargs.pop('variable') + else: + self._variable = tk.IntVar(value=value) + if 'textvariable' in kwargs: + self._textvariable = kwargs.pop('textvariable') + else: + self._textvariable = tk.StringVar(value=text) + self._bootstyle = bootstyle + self._font = font or "helvetica 10" + self._mask = mask + self._traceid = None + + super().__init__( + master=master, + class_="Floodgauge", + cursor=cursor, + length=length, + maximum=maximum, + mode=mode, + orient=orient, + bootstyle=bootstyle, + takefocus=takefocus, + variable=self._variable, + **kwargs, + ) + self._set_widget_text(self._textvariable.get()) + self.bind("<>", self._on_theme_change) + self.bind("<>", self._on_theme_change) + + if self._mask is not None: + self._set_mask() + + def _set_widget_text(self, *_): + ttkstyle = self.cget("style") + if self._mask is None: + text = self._textvariable.get() + else: + value = self._variable.get() + text = self._mask.format(value) + self.tk.call("ttk::style", "configure", ttkstyle, "-text", text) + self.tk.call("ttk::style", "configure", ttkstyle, "-font", self._font) + + def _set_mask(self): + if self._traceid is None: + self._traceid = self._variable.trace_add( + "write", self._set_widget_text + ) + + def _unset_mask(self): + if self._traceid is not None: + self._variable.trace_remove("write", self._traceid) + self._traceid = None + + def _on_theme_change(self, *_): + text = self._textvariable.get() + self._set_widget_text(text) + + def _configure_get(self, cnf): + if cnf == "value": + return self._variable.get() + if cnf == "text": + return self._textvariable.get() + if cnf == "bootstyle": + return self._bootstyle + if cnf == "mask": + return self._mask + if cnf == "font": + return self._font + else: + return super(Progressbar, self).configure(cnf=cnf) + + def _configure_set(self, **kwargs): + if "value" in kwargs: + self._variable.set(kwargs.pop("value")) + if "text" in kwargs: + self._textvariable.set(kwargs.pop("text")) + if "bootstyle" in kwargs: + self._bootstyle = kwargs.get("bootstyle") + if "mask" in kwargs: + self._mask = kwargs.pop("mask") + if "font" in kwargs: + self._font = kwargs.pop("font") + if "variable" in kwargs: + self._variable = kwargs.get("variable") + Progressbar.configure(self, cnf=None, **kwargs) + if "textvariable" in kwargs: + self.textvariable = kwargs.pop("textvariable") + else: + Progressbar.configure(self, cnf=None, **kwargs) + + def __getitem__(self, key: str): + return self._configure_get(cnf=key) + + def __setitem__(self, key: str, value): + self._configure_set(**{key: value}) + + def configure(self, cnf=None, **kwargs): + """Configure the options for this widget. + + Parameters: + + cnf (Dict[str, Any], optional): + A dictionary of configuration options. + + **kwargs: + Optional keyword arguments. + """ + if cnf is not None: + return self._configure_get(cnf) + else: + self._configure_set(**kwargs) + + @property + def textvariable(self): + """Returns the textvariable object""" + return self._textvariable + + @textvariable.setter + def textvariable(self, value): + """Set the new textvariable property""" + self._textvariable = value + self._set_widget_text(self._textvariable.get()) + + @property + def variable(self): + """Returns the variable object""" + return self._variable + + @variable.setter + def variable(self, value): + """Set the new variable object""" + self._variable = value + if self.cget('variable') != value: + self.configure(variable=self._variable) + + +class Meter(ttk.Frame): + """A radial meter that can be used to show progress of long + running operations or the amount of work completed; can also be + used as a dial when set to `interactive=True`. + + This widget is very flexible. There are two primary meter types + which can be set with the `metertype` parameter: 'full' and + 'semi', which shows the arc of the meter in a full or + semi-circle. You can also customize the arc of the circle with + the `arcrange` and `arcoffset` parameters. + + The meter indicator can be displayed as a solid color or with + stripes using the `stripethickness` parameter. By default, the + `stripethickness` is 0, which results in a solid meter + indicator. A higher `stripethickness` results in larger wedges + around the arc of the meter. + + Various text and label options exist. The center text and + meter indicator is formatted with the `meterstyle` parameter. + You can set text on the left and right of this center label + using the `textleft` and `textright` parameters. This is most + commonly used for '$', '%', or other such symbols. + + If you need access to the variables that update the meter, you + you can access these via the `amountusedvar`, `amounttotalvar`, + and the `labelvar`. The value of these properties can also be + retrieved via the `configure` method. + + ![](../../assets/widgets/meter.gif) + + Examples: + + ```python + import ttkbootstrap as ttk + from ttkbootstrap.constants import * + + app = ttk.Window() + + meter = ttk.Meter( + metersize=180, + padding=5, + amountused=25, + metertype="semi", + subtext="miles per hour", + interactive=True, + ) + meter.pack() + + # update the amount used directly + meter.configure(amountused = 50) + + # update the amount used with another widget + entry = ttk.Entry(textvariable=meter.amountusedvar) + entry.pack(fill=X) + + # increment the amount by 10 steps + meter.step(10) + + # decrement the amount by 15 steps + meter.step(-15) + + # update the subtext + meter.configure(subtext="loading...") + + app.mainloop() + ``` + """ + + def __init__( + self, + master=None, + bootstyle=DEFAULT, + arcrange=None, + arcoffset=None, + amounttotal=100, + amountused=0, + wedgesize=0, + metersize=200, + metertype=FULL, + meterthickness=10, + showtext=True, + interactive=False, + stripethickness=0, + textleft=None, + textright=None, + textfont="-size 20 -weight bold", + subtext=None, + subtextstyle=DEFAULT, + subtextfont="-size 10", + stepsize=1, + **kwargs, + ): + """ + Parameters: + + master (Widget): + The parent widget. + + arcrange (int): + The range of the arc if degrees from start to end. + + arcoffset (int): + The amount to offset the arc's starting position in degrees. + 0 is at 3 o'clock. + + amounttotal (int): + The maximum value of the meter. + + amountused (int): + The current value of the meter; displayed in a center label + if the `showtext` property is set to True. + + wedgesize (int): + Sets the length of the indicator wedge around the arc. If + greater than 0, this wedge is set as an indicator centered + on the current meter value. + + metersize (int): + The meter is square. This represents the size of one side + if the square as measured in screen units. + + bootstyle (str): + Sets the indicator and center text color. One of primary, + secondary, success, info, warning, danger, light, dark. + + metertype ('full', 'semi'): + Displays the meter as a full circle or semi-circle. + + meterthickness (int): + The thickness of the indicator. + + showtext (bool): + Indicates whether to show the left, center, and right text + labels on the meter. + + interactive (bool): + Indicates that the user may adjust the meter value with + mouse interaction. + + stripethickness (int): + The indicator can be displayed as a solid band or as + striped wedges around the arc. If the value is greater than + 0, the indicator changes from a solid to striped, where the + value is the thickness of the stripes (or wedges). + + textleft (str): + A short string inserted to the left of the center text. + + textright (str): + A short string inserted to the right of the center text. + + textfont (Union[str, Font]): + The font used to render the center text. + + subtext (str): + Supplemental text that appears below the center text. + + subtextstyle (str): + The bootstyle color of the subtext. One of primary, + secondary, success, info, warning, danger, light, dark. + The default color is Theme specific and is a lighter + shade based on whether it is a 'light' or 'dark' theme. + + subtextfont (Union[str, Font]): + The font used to render the subtext. + + stepsize (int): + Sets the amount by which to change the meter indicator + when incremented by mouse interaction. + + **kwargs: + Other keyword arguments that are passed directly to the + `Frame` widget that contains the meter components. + """ + super().__init__(master=master, **kwargs) + + # widget variables + self.amountusedvar = tk.IntVar(value=amountused) + self.amountusedvar.trace_add("write", self._draw_meter) + self.amounttotalvar = tk.IntVar(value=amounttotal) + self.labelvar = tk.StringVar(value=subtext) + + # misc settings + self._set_arc_offset_range(metertype, arcoffset, arcrange) + self._towardsmaximum = True + self._metersize = utility.scale_size(self, metersize) + self._meterthickness = utility.scale_size(self, meterthickness) + self._stripethickness = stripethickness + self._showtext = showtext + self._wedgesize = wedgesize + self._stepsize = stepsize + self._textleft = textleft + self._textright = textright + self._textfont = textfont + self._subtext = subtext + self._subtextfont = subtextfont + self._subtextstyle = subtextstyle + self._bootstyle = bootstyle + self._interactive = interactive + self._bindids = {} + + self._setup_widget() + + def _setup_widget(self): + self.meterframe = ttk.Frame( + master=self, width=self._metersize, height=self._metersize + ) + self.indicator = ttk.Label(self.meterframe) + self.textframe = ttk.Frame(self.meterframe) + self.textleft = ttk.Label( + master=self.textframe, + text=self._textleft, + font=self._subtextfont, + bootstyle=(self._subtextstyle, "metersubtxt"), + anchor=tk.S, + padding=(0, 5), + ) + self.textcenter = ttk.Label( + master=self.textframe, + textvariable=self.amountusedvar, + bootstyle=(self._bootstyle, "meter"), + font=self._textfont, + ) + self.textright = ttk.Label( + master=self.textframe, + text=self._textright, + font=self._subtextfont, + bootstyle=(self._subtextstyle, "metersubtxt"), + anchor=tk.S, + padding=(0, 5), + ) + self.subtext = ttk.Label( + master=self.meterframe, + text=self._subtext, + bootstyle=(self._subtextstyle, "metersubtxt"), + font=self._subtextfont, + ) + + self.bind("<>", self._on_theme_change) + self.bind("<>", self._on_theme_change) + self._set_interactive_bind() + self._draw_base_image() + self._draw_meter() + + # set widget geometery + self.indicator.place(x=0, y=0) + self.meterframe.pack() + self._set_show_text() + + def _set_widget_colors(self): + bootstyle = (self._bootstyle, "meter", "label") + ttkstyle = Bootstyle.ttkstyle_name(string="-".join(bootstyle)) + textcolor = self._lookup_style_option(ttkstyle, "foreground") + background = self._lookup_style_option(ttkstyle, "background") + troughcolor = self._lookup_style_option(ttkstyle, "space") + self._meterforeground = textcolor + self._meterbackground = Colors.update_hsv(background, vd=-0.1) + self._metertrough = troughcolor + + def _set_meter_text(self): + """Setup and pack the widget labels in the appropriate order""" + self._set_show_text() + self._set_subtext() + + def _set_subtext(self): + if self._subtextfont: + if self._showtext: + self.subtext.place(relx=0.5, rely=0.6, anchor=tk.CENTER) + else: + self.subtext.place(relx=0.5, rely=0.5, anchor=tk.CENTER) + + def _set_show_text(self): + self.textframe.pack_forget() + self.textcenter.pack_forget() + self.textleft.pack_forget() + self.textright.pack_forget() + self.subtext.pack_forget() + + if self._showtext: + if self._subtext: + self.textframe.place(relx=0.5, rely=0.45, anchor=tk.CENTER) + else: + self.textframe.place(relx=0.5, rely=0.5, anchor=tk.CENTER) + + self._set_text_left() + self._set_text_center() + self._set_text_right() + self._set_subtext() + + def _set_text_left(self): + if self._showtext and self._textleft: + self.textleft.pack(side=tk.LEFT, fill=tk.Y) + + def _set_text_center(self): + if self._showtext: + self.textcenter.pack(side=tk.LEFT, fill=tk.Y) + + def _set_text_right(self): + self.textright.configure(text=self._textright) + if self._showtext and self._textright: + self.textright.pack(side=tk.RIGHT, fill=tk.Y) + + def _set_interactive_bind(self): + seq1 = "" + seq2 = "" + + if self._interactive: + self._bindids[seq1] = self.indicator.bind( + seq1, self._on_dial_interact + ) + self._bindids[seq2] = self.indicator.bind( + seq2, self._on_dial_interact + ) + return + + if seq1 in self._bindids: + self.indicator.unbind(seq1, self._bindids.get(seq1)) + self.indicator.unbind(seq2, self._bindids.get(seq2)) + self._bindids.clear() + + def _set_arc_offset_range(self, metertype, arcoffset, arcrange): + if metertype == SEMI: + self._arcoffset = 135 if arcoffset is None else arcoffset + self._arcrange = 270 if arcrange is None else arcrange + else: + self._arcoffset = -90 if arcoffset is None else arcoffset + self._arcrange = 360 if arcrange is None else arcrange + self._metertype = metertype + + def _draw_meter(self, *_): + """Draw a meter""" + img = self._base_image.copy() + draw = ImageDraw.Draw(img) + if self._stripethickness > 0: + self._draw_striped_meter(draw) + else: + self._draw_solid_meter(draw) + + self._meterimage = ImageTk.PhotoImage( + img.resize((self._metersize, self._metersize), Image.CUBIC) + ) + self.indicator.configure(image=self._meterimage) + + def _draw_base_image(self): + """Draw base image to be used for subsequent updates""" + self._set_widget_colors() + self._base_image = Image.new( + mode="RGBA", size=(self._metersize * M, self._metersize * M) + ) + draw = ImageDraw.Draw(self._base_image) + + x1 = y1 = self._metersize * M - 20 + width = self._meterthickness * M + # striped meter + if self._stripethickness > 0: + _from = self._arcoffset + _to = self._arcrange + self._arcoffset + _step = 2 if self._stripethickness == 1 else self._stripethickness + for x in range(_from, _to, _step): + draw.arc( + xy=(0, 0, x1, y1), + start=x, + end=x + self._stripethickness - 1, + fill=self._metertrough, + width=width, + ) + # solid meter + else: + draw.arc( + xy=(0, 0, x1, y1), + start=self._arcoffset, + end=self._arcrange + self._arcoffset, + fill=self._metertrough, + width=width, + ) + + def _draw_solid_meter(self, draw: ImageDraw.Draw): + """Draw a solid meter""" + x1 = y1 = self._metersize * M - 20 + width = self._meterthickness * M + + if self._wedgesize > 0: + meter_value = self._meter_value() + draw.arc( + xy=(0, 0, x1, y1), + start=meter_value - self._wedgesize, + end=meter_value + self._wedgesize, + fill=self._meterforeground, + width=width, + ) + else: + draw.arc( + xy=(0, 0, x1, y1), + start=self._arcoffset, + end=self._meter_value(), + fill=self._meterforeground, + width=width, + ) + + def _draw_striped_meter(self, draw: ImageDraw.Draw): + """Draw a striped meter""" + meter_value = self._meter_value() + x1 = y1 = self._metersize * M - 20 + width = self._meterthickness * M + + if self._wedgesize > 0: + draw.arc( + xy=(0, 0, x1, y1), + start=meter_value - self._wedgesize, + end=meter_value + self._wedgesize, + fill=self._meterforeground, + width=width, + ) + else: + _from = self._arcoffset + _to = meter_value - 1 + _step = self._stripethickness + for x in range(_from, _to, _step): + draw.arc( + xy=(0, 0, x1, y1), + start=x, + end=x + self._stripethickness - 1, + fill=self._meterforeground, + width=width, + ) + + def _meter_value(self) -> int: + """Calculate the value to be used to draw the arc length of the + progress meter.""" + value = int( + (self["amountused"] / self["amounttotal"]) * self._arcrange + + self._arcoffset + ) + return value + + def _on_theme_change(self, *_): + self._draw_base_image() + self._draw_meter() + + def _on_dial_interact(self, e: tk.Event): + """Callback for mouse drag motion on meter indicator""" + dx = e.x - self._metersize // 2 + dy = e.y - self._metersize // 2 + rads = math.atan2(dy, dx) + degs = math.degrees(rads) + + if degs > self._arcoffset: + factor = degs - self._arcoffset + else: + factor = 360 + degs - self._arcoffset + + # clamp the value between 0 and `amounttotal` + amounttotal = self.amounttotalvar.get() + lastused = self.amountusedvar.get() + amountused = (amounttotal / self._arcrange * factor) + + # calculate amount used given stepsize + if amountused > self._stepsize//2: + amountused = amountused // self._stepsize * self._stepsize + self._stepsize + else: + amountused = 0 + # if the number is the name, then do not redraw + if lastused == amountused: + return + # set the amount used variable + if amountused < 0: + self.amountusedvar.set(0) + elif amountused > amounttotal: + self.amountusedvar.set(amounttotal) + else: + self.amountusedvar.set(amountused) + + def _lookup_style_option(self, style: str, option: str): + """Wrapper around the tcl style lookup command""" + value = self.tk.call( + "ttk::style", "lookup", style, "-%s" % option, None, None + ) + return value + + def _configure_get(self, cnf): + """Override the configuration get method""" + if cnf == "arcrange": + return self._arcrange + elif cnf == "arcoffset": + return self._arcoffset + elif cnf == "amounttotal": + return self.amounttotalvar.get() + elif cnf == "amountused": + return self.amountusedvar.get() + elif cnf == "interactive": + return self._interactive + elif cnf == "subtextfont": + return self._subtextfont + elif cnf == "subtextstyle": + return self._subtextstyle + elif cnf == "subtext": + return self._subtext + elif cnf == "metersize": + return self._metersize + elif cnf == "bootstyle": + return self._bootstyle + elif cnf == "metertype": + return self._metertype + elif cnf == "meterthickness": + return self._meterthickness + elif cnf == "showtext": + return self._showtext + elif cnf == "stripethickness": + return self._stripethickness + elif cnf == "textleft": + return self._textleft + elif cnf == "textright": + return self._textright + elif cnf == "textfont": + return self._textfont + elif cnf == "wedgesize": + return self._wedgesize + elif cnf == "stepsize": + return self._stepsize + else: + return super(ttk.Frame, self).configure(cnf) + + def _configure_set(self, **kwargs): + """Override the configuration set method""" + meter_text_changed = False + + if "arcrange" in kwargs: + self._arcrange = kwargs.pop("arcrange") + if "arcoffset" in kwargs: + self._arcoffset = kwargs.pop("arcoffset") + if "amounttotal" in kwargs: + amounttotal = kwargs.pop("amounttotal") + self.amounttotalvar.set(amounttotal) + if "amountused" in kwargs: + amountused = kwargs.pop("amountused") + self.amountusedvar.set(amountused) + if "interactive" in kwargs: + self._interactive = kwargs.pop("interactive") + self._set_interactive_bind() + if "subtextfont" in kwargs: + self._subtextfont = kwargs.pop("subtextfont") + self.subtext.configure(font=self._subtextfont) + self.textleft.configure(font=self._subtextfont) + self.textright.configure(font=self._subtextfont) + if "subtextstyle" in kwargs: + self._subtextstyle = kwargs.pop("subtextstyle") + self.subtext.configure(bootstyle=[self._subtextstyle, "meter"]) + if "metersize" in kwargs: + self._metersize = utility.scale_size(kwargs.pop("metersize")) + self.meterframe.configure( + height=self._metersize, width=self._metersize + ) + if "bootstyle" in kwargs: + self._bootstyle = kwargs.pop("bootstyle") + self.textcenter.configure(bootstyle=[self._bootstyle, "meter"]) + if "metertype" in kwargs: + self._metertype = kwargs.pop("metertype") + if "meterthickness" in kwargs: + self._meterthickness = self.scale_size( + kwargs.pop("meterthickness") + ) + if "stripethickness" in kwargs: + self._stripethickness = kwargs.pop("stripethickness") + if "subtext" in kwargs: + self._subtext = kwargs.pop("subtext") + self.subtext.configure(text=self._subtext) + meter_text_changed = True + if "textleft" in kwargs: + self._textleft = kwargs.pop("textleft") + self.textleft.configure(text=self._textleft) + meter_text_changed = True + if "textright" in kwargs: + self._textright = kwargs.pop("textright") + meter_text_changed = True + if "showtext" in kwargs: + self._showtext = kwargs.pop("showtext") + meter_text_changed = True + if "textfont" in kwargs: + self._textfont = kwargs.pop("textfont") + self.textcenter.configure(font=self._textfont) + if "wedgesize" in kwargs: + self._wedgesize = kwargs.pop("wedgesize") + if "stepsize" in kwargs: + self._stepsize = kwargs.pop("stepsize") + if meter_text_changed: + self._set_meter_text() + + try: + if self._metertype: + self._set_arc_offset_range( + metertype=self._metertype, + arcoffset=self._arcoffset, + arcrange=self._arcrange, + ) + except AttributeError: + return + + self._draw_base_image() + self._draw_meter() + + # pass remaining configurations to `ttk.Frame.configure` + super(ttk.Frame, self).configure(**kwargs) + + def __getitem__(self, key: str): + return self._configure_get(key) + + def __setitem__(self, key: str, value) -> None: + self._configure_set(**{key: value}) + + def configure(self, cnf=None, **kwargs): + """Configure the options for this widget. + + Parameters: + cnf (Dict[str, Any], optional): + A dictionary of configuration options. + + **kwargs: Optional keyword arguments. + """ + if cnf is not None: + return self._configure_get(cnf) + else: + self._configure_set(**kwargs) + + def step(self, delta=1): + """Increase the indicator value by `delta` + + The indicator will reverse direction and count down once it + reaches the maximum value. + + Parameters: + + delta (int): + The amount to change the indicator. + """ + amountused = self.amountusedvar.get() + amounttotal = self.amounttotalvar.get() + if amountused >= amounttotal: + self._towardsmaximum = True + self.amountusedvar.set(amountused - delta) + elif amountused <= 0: + self._towardsmaximum = False + self.amountusedvar.set(amountused + delta) + elif self._towardsmaximum: + self.amountusedvar.set(amountused - delta) + else: + self.amountusedvar.set(amountused + delta) diff --git a/pylibraries/ttkbootstrap/window.py b/pylibraries/ttkbootstrap/window.py new file mode 100644 index 0000000..d6b8c2d --- /dev/null +++ b/pylibraries/ttkbootstrap/window.py @@ -0,0 +1,504 @@ +""" + This module contains a class of the same name that wraps the + tkinter.Tk and ttkbootstrap.style.Style classes to provide a more + consolidated api for initial application startup. +""" +import tkinter +from ttkbootstrap.constants import * +from ttkbootstrap.publisher import Publisher +from ttkbootstrap.style import Style +from ttkbootstrap.icons import Icon +from ttkbootstrap import utility + + +def get_default_root(what=None): + """Returns the default root if it has been created, otherwise + returns a new instance.""" + if not tkinter._support_default_root: + raise RuntimeError("No master specified and tkinter is " + "configured to not support default root") + if not tkinter._default_root: + if what: + raise RuntimeError(f"Too early to {what}: no default root window") + root = tkinter.Tk() + assert tkinter._default_root is root + return tkinter._default_root + + +def apply_class_bindings(window: tkinter.Widget): + """Add class level event bindings in application""" + for className in ["TEntry", "TSpinbox", "TCombobox", "Text"]: + window.bind_class( + className=className, + sequence="", + func=on_disabled_readonly_state, + add="+") + + for sequence in ["", ""]: + window.bind_class( + className=className, + sequence=sequence, + func=on_select_all) + + window.unbind_class("TButton", "") + + def button_default_binding(event): + """The default keybind on a button when the return or enter key + is pressed and the button has focus or is the default button.""" + try: + widget = window.nametowidget(event.widget) + widget.invoke() + except KeyError: + window.tk.call(event.widget, 'invoke') + + window.bind_class("TButton", "", button_default_binding, + add="+") + window.bind_class("TButton", "", button_default_binding, add="+") + + +def apply_all_bindings(window: tkinter.Widget): + """Add bindings to all widgets in the application""" + window.bind_all('', on_map_child, '+') + window.bind_all('', lambda e: Publisher.unsubscribe(e.widget)) + + +def on_disabled_readonly_state(event): + """Change the cursor of entry type widgets to 'arrow' if in a + disabled or readonly state.""" + try: + widget = event.widget + state = str(widget.cget('state')) + cursor = str(widget.cget('cursor')) + if state in (DISABLED, READONLY): + if cursor == 'arrow': + return + else: + widget['cursor'] = 'arrow' + else: + if cursor in ('ibeam', ''): + return + else: + widget['cursor'] = None + except: + pass + + +def on_map_child(event): + """Callback for event which generates a <> virtual + event on the parent""" + widget: tkinter.Widget = event.widget + try: + if widget.master is None: # root widget + return + else: + widget.master.event_generate('<>') + except: + # not a tkinter widget that I'm handling (ex. Combobox.popdown) + return + + +def on_select_all(event): + """Callback to select all text in the input widget when an event is + executed.""" + widget = event.widget + if widget.__class__.__name__ == "Text": + widget.tag_add(SEL, "1.0", END) + widget.mark_set(INSERT, END) + widget.see(END) + else: + widget.select_range(0, END) + widget.icursor(END) + return 'break' + + +class Window(tkinter.Tk): + """A class that wraps the tkinter.Tk class in order to provide a + more convenient api with additional bells and whistles. For more + information on how to use the inherited `Tk` methods, see the + [tcl/tk documentation](https://tcl.tk/man/tcl8.6/TkCmd/wm.htm) + and the [Python documentation](https://docs.python.org/3/library/tkinter.html#tkinter.Tk). + + ![](../../assets/window/window-toplevel.png) + + Examples: + + ```python + app = Window(title="My Application", themename="superhero") + app.mainloop() + ``` + """ + + def __init__( + self, + title="ttkbootstrap", + themename="litera", + iconphoto='', + size=None, + position=None, + minsize=None, + maxsize=None, + resizable=None, + hdpi=True, + scaling=None, + transient=None, + overrideredirect=False, + alpha=1.0, + ): + """ + Parameters: + + title (str): + The title that appears on the application titlebar. + + themename (str): + The name of the ttkbootstrap theme to apply to the + application. + + iconphoto (str): + A path to the image used for the titlebar icon. + Internally this is passed to the `Tk.iconphoto` method + and the image will be the default icon for all windows. + A ttkbootstrap image is used by default. To disable + this default behavior, set the value to `None` and use + the `Tk.iconphoto` or `Tk.iconbitmap` methods directly. + + size (Tuple[int, int]): + The width and height of the application window. + Internally, this argument is passed to the + `Window.geometry` method. + + position (Tuple[int, int]): + The horizontal and vertical position of the window on + the screen relative to the top-left coordinate. + Internally this is passed to the `Window.geometry` + method. + + minsize (Tuple[int, int]): + Specifies the minimum permissible dimensions for the + window. Internally, this argument is passed to the + `Window.minsize` method. + + maxsize (Tuple[int, int]): + Specifies the maximum permissible dimensions for the + window. Internally, this argument is passed to the + `Window.maxsize` method. + + resizable (Tuple[bool, bool]): + Specifies whether the user may interactively resize the + toplevel window. Must pass in two arguments that specify + this flag for _horizontal_ and _vertical_ dimensions. + This can be adjusted after the window is created by using + the `Window.resizable` method. + + hdpi (bool): + Enable high-dpi support for Windows OS. This option is + enabled by default. + + scaling (float): + Sets the current scaling factor used by Tk to convert + between physical units (for example, points, inches, or + millimeters) and pixels. The number argument is a + floating point number that specifies the number of pixels + per point on window's display. + + transient (Union[Tk, Widget]): + Instructs the window manager that this widget is + transient with regard to the widget master. Internally + this is passed to the `Window.transient` method. + + overrideredirect (bool): + Instructs the window manager to ignore this widget if + True. Internally, this argument is passed to the + `Window.overrideredirect(1)` method. + + alpha (float): + On Windows, specifies the alpha transparency level of the + toplevel. Where not supported, alpha remains at 1.0. Internally, + this is processed as `Toplevel.attributes('-alpha', alpha)`. + """ + if hdpi: + utility.enable_high_dpi_awareness() + + super().__init__() + self.winsys = self.tk.call('tk', 'windowingsystem') + + if scaling is not None: + utility.enable_high_dpi_awareness(self, scaling) + + if iconphoto is not None: + if iconphoto == '': + # the default ttkbootstrap icon + self._icon = tkinter.PhotoImage(master=self, data=Icon.icon) + self.iconphoto(True, self._icon) + else: + try: + # the user provided an image path + self._icon = tkinter.PhotoImage(file=iconphoto, master=self) + self.iconphoto(True, self._icon) + except tkinter.TclError: + # The fallback icon if the user icon fails. + print('iconphoto path is bad; using default image.') + self._icon = tkinter.PhotoImage(data=Icon.icon, master=self) + self.iconphoto(True, self._icon) + + self.title(title) + + if size is not None: + width, height = size + self.geometry(f"{width}x{height}") + + if position is not None: + xpos, ypos = position + self.geometry(f"+{xpos}+{ypos}") + + if minsize is not None: + width, height = minsize + self.minsize(width, height) + + if maxsize is not None: + width, height = maxsize + self.maxsize(width, height) + + if resizable is not None: + width, height = resizable + self.resizable(width, height) + + if transient is not None: + self.transient(transient) + + if overrideredirect: + self.overrideredirect(1) + + if alpha is not None: + if self.winsys == 'x11': + self.wait_visibility(self) + self.attributes("-alpha", alpha) + + apply_class_bindings(self) + apply_all_bindings(self) + self._style = Style(themename) + + + @property + def style(self): + """Return a reference to the `ttkbootstrap.style.Style` object.""" + return self._style + + def place_window_center(self): + """Position the toplevel in the center of the screen. Does not + account for titlebar height.""" + self.update_idletasks() + w_height = self.winfo_height() + w_width = self.winfo_width() + s_height = self.winfo_screenheight() + s_width = self.winfo_screenwidth() + xpos = (s_width - w_width) // 2 + ypos = (s_height - w_height) // 2 + self.geometry(f'+{xpos}+{ypos}') + + position_center = place_window_center # alias + + +class Toplevel(tkinter.Toplevel): + """A class that wraps the tkinter.Toplevel class in order to + provide a more convenient api with additional bells and whistles. + For more information on how to use the inherited `Toplevel` + methods, see the [tcl/tk documentation](https://tcl.tk/man/tcl8.6/TkCmd/toplevel.htm) + and the [Python documentation](https://docs.python.org/3/library/tkinter.html#tkinter.Toplevel). + + ![](../../assets/window/window-toplevel.png) + + Examples: + + ```python + app = Toplevel(title="My Toplevel") + app.mainloop() + ``` + """ + + def __init__( + self, + title="ttkbootstrap", + iconphoto='', + size=None, + position=None, + minsize=None, + maxsize=None, + resizable=None, + transient=None, + overrideredirect=False, + windowtype=None, + topmost=False, + toolwindow=False, + alpha=1.0, + **kwargs, + ): + """ + Parameters: + + title (str): + The title that appears on the application titlebar. + + iconphoto (str): + A path to the image used for the titlebar icon. + Internally this is passed to the `Tk.iconphoto` method. + By default the application icon is used. + + size (Tuple[int, int]): + The width and height of the application window. + Internally, this argument is passed to the + `Toplevel.geometry` method. + + position (Tuple[int, int]): + The horizontal and vertical position of the window on + the screen relative to the top-left coordinate. + Internally this is passed to the `Toplevel.geometry` + method. + + minsize (Tuple[int, int]): + Specifies the minimum permissible dimensions for the + window. Internally, this argument is passed to the + `Toplevel.minsize` method. + + maxsize (Tuple[int, int]): + Specifies the maximum permissible dimensions for the + window. Internally, this argument is passed to the + `Toplevel.maxsize` method. + + resizable (Tuple[bool, bool]): + Specifies whether the user may interactively resize the + toplevel window. Must pass in two arguments that specify + this flag for _horizontal_ and _vertical_ dimensions. + This can be adjusted after the window is created by using + the `Toplevel.resizable` method. + + transient (Union[Tk, Widget]): + Instructs the window manager that this widget is + transient with regard to the widget master. Internally + this is passed to the `Toplevel.transient` method. + + overrideredirect (bool): + Instructs the window manager to ignore this widget if + True. Internally, this argument is processed as + `Toplevel.overrideredirect(1)`. + + windowtype (str): + On X11, requests that the window should be interpreted by + the window manager as being of the specified type. Internally, + this is passed to the `Toplevel.attributes('-type', windowtype)`. + + See the [-type option](https://tcl.tk/man/tcl8.6/TkCmd/wm.htm#M64) + for a list of available options. + + topmost (bool): + Specifies whether this is a topmost window (displays above all + other windows). Internally, this processed by the window as + `Toplevel.attributes('-topmost', 1)`. + + toolwindow (bool): + On Windows, specifies a toolwindow style. Internally, this is + processed as `Toplevel.attributes('-toolwindow', 1)`. + + alpha (float): + On Windows, specifies the alpha transparency level of the + toplevel. Where not supported, alpha remains at 1.0. Internally, + this is processed as `Toplevel.attributes('-alpha', alpha)`. + + **kwargs (Dict): + Other optional keyword arguments. + """ + if 'iconify' in kwargs: + iconify = kwargs.pop('iconify') + else: + iconify = None + + super().__init__(**kwargs) + self.winsys = self.tk.call('tk', 'windowingsystem') + + if iconify: + self.iconify() + + if iconphoto != '': + try: + # the user provided an image path + self._icon = tkinter.PhotoImage(file=iconphoto, master=self) + self.iconphoto(True, self._icon) + except tkinter.TclError: + # The fallback icon if the user icon fails. + print('iconphoto path is bad; using default image.') + pass + + self.title(title) + + if size is not None: + width, height = size + self.geometry(f'{width}x{height}') + + if position is not None: + xpos, ypos = position + self.geometry(f"+{xpos}+{ypos}") + + if minsize is not None: + width, height = minsize + self.minsize(width, height) + + if maxsize is not None: + width, height = maxsize + self.maxsize(width, height) + + if resizable is not None: + width, height = resizable + self.resizable(width, height) + + if transient is not None: + self.transient(transient) + + if overrideredirect: + self.overrideredirect(1) + + if windowtype is not None: + if self.winsys == 'x11': + self.attributes("-type", windowtype) + + if topmost: + self.attributes("-topmost", 1) + + if toolwindow: + if self.winsys == 'win32': + self.attributes("-toolwindow", 1) + + if alpha is not None: + if self.winsys == 'x11': + self.wait_visibility(self) + self.attributes("-alpha", alpha) + + @property + def style(self): + """Return a reference to the `ttkbootstrap.style.Style` object.""" + return Style() + + def place_window_center(self): + """Position the toplevel in the center of the screen. Does not + account for titlebar height.""" + self.update_idletasks() + w_height = self.winfo_height() + w_width = self.winfo_width() + s_height = self.winfo_screenheight() + s_width = self.winfo_screenwidth() + xpos = (s_width - w_width) // 2 + ypos = (s_height - w_height) // 2 + self.geometry(f'+{xpos}+{ypos}') + + position_center = place_window_center # alias + +if __name__ == "__main__": + + root = Window(themename="superhero", alpha=0.5, size=(1000, 1000)) + #root.withdraw() + root.place_window_center() + #root.deiconify() + + top = Toplevel(title="My Toplevel", alpha=0.4, size=(1000, 1000)) + top.place_window_center() + + root.mainloop() diff --git a/pylibraries/ttkcreator/__init__.py b/pylibraries/ttkcreator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pylibraries/ttkcreator/__main__.py b/pylibraries/ttkcreator/__main__.py new file mode 100644 index 0000000..ea6c9ce --- /dev/null +++ b/pylibraries/ttkcreator/__main__.py @@ -0,0 +1,472 @@ +import shutil +import json +from uuid import uuid4 +from pathlib import Path +import ttkbootstrap as ttk +from tkinter import Frame +from tkinter.colorchooser import askcolor +from tkinter.filedialog import askopenfilename, asksaveasfilename +from ttkbootstrap.themes import standard, user +from ttkbootstrap.style import ThemeDefinition +from ttkbootstrap.constants import * +from ttkbootstrap.dialogs import Messagebox + + +class ThemeCreator(ttk.Window): + def __init__(self): + super().__init__("TTK Creator") + self.configure_frame = ttk.Frame(self, padding=(10, 10, 5, 10)) + self.configure_frame.pack(side=LEFT, fill=BOTH, expand=YES) + self.demo_frame = ttk.Frame(self, padding=(5, 10, 10, 10)) + self.demo_frame.pack(side=LEFT, fill=BOTH, expand=YES) + self.setup_theme_creator() + self.demo_widgets = DemoWidgets(self, self.style) + self.demo_widgets.pack(fill=BOTH, expand=YES) + + def setup_theme_creator(self): + # application menu + self.menu = ttk.Menu() + self.menu.add_command(label="Save", command=self.save_theme) + self.menu.add_command(label="Reset", command=self.change_base_theme) + self.menu.add_command(label="Import", command=self.import_user_themes) + self.menu.add_command(label="Export", command=self.export_user_themes) + self.configure(menu=self.menu) + + # theme configuration settings + ## user theme name + f1 = ttk.Frame(self.configure_frame, padding=(5, 2)) + ttk.Label(f1, text="name", width=12).pack(side=LEFT) + self.theme_name = ttk.Entry(f1) + self.theme_name.insert(END, "new theme") + self.theme_name.pack(side=LEFT, fill=X, expand=YES) + f1.pack(fill=X, expand=YES) + + ## base theme + f2 = ttk.Frame(self.configure_frame, padding=(5, 2)) + ttk.Label(f2, text="base theme", width=12).pack(side=LEFT) + self.base_theme = ttk.Combobox(f2, values=self.style.theme_names()) + self.base_theme.insert(END, "litera") + self.base_theme.pack(side=LEFT, fill=X, expand=YES) + f2.pack(fill=X, expand=YES, pady=(0, 15)) + self.base_theme.bind("<>", self.change_base_theme) + + ## color options + self.color_rows = [] + for color in self.style.colors.label_iter(): + row = ColorRow(self.configure_frame, color, self.style) + self.color_rows.append(row) + row.pack(fill=BOTH, expand=YES) + row.bind("<>", self.create_temp_theme) + + def create_temp_theme(self, *_): + """Creates a temp theme using the current configure settings and + changes the theme in tkinter to that new theme. + """ + themename = "temp_" + str(uuid4()).replace("-", "")[:10] + colors = {} + for row in self.color_rows: + colors[row.label["text"]] = row.color_value + definition = ThemeDefinition(themename, colors, self.style.theme.type) + self.style.register_theme(definition) + self.style.theme_use(themename) + self.update_color_patches() + + def change_base_theme(self, *_): + """Sets the initial colors used in the color configuration""" + themename = self.base_theme.get() + self.style.theme_use(themename) + self.update_color_patches() + + def update_color_patches(self): + """Updates the color patches next to the color code entry.""" + for row in self.color_rows: + row.color_value = self.style.colors.get(row.label["text"]) + row.update_patch_color() + + def export_user_themes(self): + """Export user themes saved in the user.py file""" + inpath = Path(user.__file__) + outpath = asksaveasfilename( + initialdir="/", + initialfile="user.py", + filetypes=[("python", "*.py")], + ) + if outpath: + shutil.copyfile(inpath, outpath) + Messagebox.ok( + parent=self, + title="Export", + message="User themes have been exported.", + ) + + def import_user_themes(self): + """Import user themes into the user.py file. Any existing data + in the user.py file will be overwritten.""" + outpath = Path(user.__file__) + inpath = askopenfilename( + initialdir="/", + initialfile="user.py", + filetypes=[("python", "*.py")], + ) + confirm = Messagebox.okcancel( + title="Import", + message="This import will overwrite the existing user themes. Ok to import?", + ) + if confirm == "OK" and inpath: + shutil.copyfile(inpath, outpath) + Messagebox.ok( + parent=self, + title="Export", + message="User themes have been imported.", + ) + + def save_theme(self): + """Save the current settings as a new theme. Warn using if + saving will overwrite existing theme.""" + name = self.theme_name.get().lower().replace(" ", "") + + if name in user.USER_THEMES: + result = Messagebox.okcancel( + title="Save Theme", + alert=True, + message=f"Overwrite existing theme {name}?", + ) + if result == "Cancel": + return + + colors = {} + for row in self.color_rows: + colors[row.label["text"]] = row.color_value + + theme = {name: {"type": self.style.theme.type, "colors": colors}} + user.USER_THEMES.update(theme) + standard.STANDARD_THEMES[name] = theme[name] + + # save user themes to file + formatted = json.dumps(user.USER_THEMES, indent=4) + out = 'USER_THEMES = ' + formatted + filepath = user.__file__ + with open(filepath, 'w', encoding='utf-8') as f: + f.write(out) + + definition = ThemeDefinition(name, colors, self.style.theme.type) + self.style.register_theme(definition) + self.style.theme_use(name) + new_themes = [] + for themename in self.style.theme_names(): + if not themename.startswith("temp"): + new_themes.append(themename) + self.base_theme.configure(values=new_themes) + Messagebox.ok(f"The theme {name} has been created", "Save theme") + + +class ColorRow(ttk.Frame): + def __init__(self, master, color, style): + super().__init__(master, padding=(5, 2)) + self.colorname = color + self.style = style + + self.label = ttk.Label(self, text=color, width=12) + self.label.pack(side=LEFT) + self.patch = Frame( + master=self, background=self.style.colors.get(color), width=15 + ) + self.patch.pack(side=LEFT, fill=BOTH, padx=2) + self.entry = ttk.Entry(self, width=12) + self.entry.pack(side=LEFT, fill=X, expand=YES) + self.entry.bind("", self.enter_color) + self.color_picker = ttk.Button( + master=self, + text="...", + bootstyle=SECONDARY, + command=self.pick_color, + ) + self.color_picker.pack(side=LEFT, padx=2) + + # set initial color value and patch color + self.color_value = self.style.colors.get(color) + self.update_patch_color() + + def pick_color(self): + """Callback for when a color is selected from the color chooser""" + color = askcolor(color=self.color_value) + if color[1]: + self.color_value = color[1] + self.update_patch_color() + self.event_generate("<>") + + def enter_color(self, *_): + """Callback for when a color is typed into the entry""" + try: + self.color_value = self.entry.get().lower() + self.update_patch_color() + except: + self.color_value = self.style.colors.get(self.label["text"]) + self.update_patch_color() + self.event_generate("<>") + + def update_patch_color(self): + """Update the color patch frame with the color value stored in + the entry widget.""" + self.entry.delete(0, END) + self.entry.insert(END, self.color_value) + self.patch.configure(background=self.color_value) + + +class DemoWidgets(ttk.Frame): + """Builds a frame containing an example of most ttkbootstrap widgets + with various styles and states applied. + """ + + ZEN = """Beautiful is better than ugly. + Explicit is better than implicit. + Simple is better than complex. + Complex is better than complicated. + Flat is better than nested. + Sparse is better than dense. + Readability counts. + Special cases aren't special enough to break the rules. + Although practicality beats purity. + Errors should never pass silently. + Unless explicitly silenced. + In the face of ambiguity, refuse the temptation to guess. + There should be one-- and preferably only one --obvious way to do it. + Although that way may not be obvious at first unless you're Dutch. + Now is better than never. + Although never is often better than *right* now. + If the implementation is hard to explain, it's a bad idea. + If the implementation is easy to explain, it may be a good idea. + Namespaces are one honking great idea -- let's do more of those!""" + + def __init__(self, master, style): + super().__init__(master) + + self.style: ttk.Style = style + self.create_left_frame() + self.create_right_frame() + + def create_right_frame(self): + container = ttk.Frame(self) + container.pack(side=RIGHT, fill=BOTH, expand=YES, padx=5) + + # demonstrates various button styles + btn_group = ttk.Labelframe( + master=container, text="Buttons", padding=(10, 5) + ) + btn_group.pack(fill=X) + + menu = ttk.Menu(self) + for i, t in enumerate(self.style.theme_names()): + menu.add_radiobutton(label=t, value=i) + + default = ttk.Button(master=btn_group, text="solid button") + default.pack(fill=X, pady=5) + default.focus_set() + + mb = ttk.Menubutton( + master=btn_group, + text="solid menubutton", + bootstyle=SECONDARY, + menu=menu, + ) + mb.pack(fill=X, pady=5) + + cb = ttk.Checkbutton( + master=btn_group, + text="solid toolbutton", + bootstyle=(SUCCESS, TOOLBUTTON), + ) + cb.invoke() + cb.pack(fill=X, pady=5) + + ob = ttk.Button( + master=btn_group, text="outline button", bootstyle=(INFO, OUTLINE) + ) + ob.pack(fill=X, pady=5) + + mb = ttk.Menubutton( + master=btn_group, + text="outline menubutton", + bootstyle=(WARNING, OUTLINE), + menu=menu, + ) + mb.pack(fill=X, pady=5) + + cb = ttk.Checkbutton( + master=btn_group, + text="outline toolbutton", + bootstyle="success-outline-toolbutton", + ) + cb.pack(fill=X, pady=5) + + lb = ttk.Button(master=btn_group, text="link button", bootstyle=LINK) + lb.pack(fill=X, pady=5) + + cb1 = ttk.Checkbutton( + master=btn_group, + text="rounded toggle", + bootstyle=(SUCCESS, ROUND, TOGGLE), + ) + cb1.invoke() + cb1.pack(fill=X, pady=5) + + cb2 = ttk.Checkbutton( + master=btn_group, text="squared toggle", bootstyle=(SQUARE, TOGGLE) + ) + cb2.pack(fill=X, pady=5) + cb2.invoke() + + input_group = ttk.Labelframe( + master=container, text="Other input widgets", padding=10 + ) + input_group.pack(fill=BOTH, pady=(10, 5), expand=YES) + entry = ttk.Entry(input_group) + entry.pack(fill=X) + entry.insert(END, "entry widget") + + password = ttk.Entry(master=input_group, show="•") + password.pack(fill=X, pady=5) + password.insert(END, "password") + + spinbox = ttk.Spinbox(master=input_group, from_=0, to=100) + spinbox.pack(fill=X) + spinbox.set(45) + + cbo = ttk.Combobox( + master=input_group, + text=self.style.theme.name, + values=self.style.theme_names(), + ) + cbo.pack(fill=X, pady=5) + cbo.current(self.style.theme_names().index(self.style.theme.name)) + + de = ttk.DateEntry(input_group) + de.pack(fill=X) + + def create_left_frame(self): + """Create all the left frame widgets""" + container = ttk.Frame(self) + container.pack(side=LEFT, fill=BOTH, expand=YES, padx=5) + + # demonstrates all color options inside a label + color_group = ttk.Labelframe( + master=container, text="Theme color options", padding=10 + ) + color_group.pack(fill=X, side=TOP) + for color in self.style.colors: + cb = ttk.Button(color_group, text=color, bootstyle=color) + cb.pack(side=LEFT, expand=YES, padx=5, fill=X) + + # demonstrates all radiobutton widgets active and disabled + cr_group = ttk.Labelframe( + master=container, text="Checkbuttons & radiobuttons", padding=10 + ) + cr_group.pack(fill=X, pady=10, side=TOP) + cr1 = ttk.Checkbutton(cr_group, text="selected") + cr1.pack(side=LEFT, expand=YES, padx=5) + cr1.invoke() + cr2 = ttk.Checkbutton(cr_group, text="deselected") + cr2.pack(side=LEFT, expand=YES, padx=5) + cr3 = ttk.Checkbutton(cr_group, text="disabled", state=DISABLED) + cr3.pack(side=LEFT, expand=YES, padx=5) + cr4 = ttk.Radiobutton(cr_group, text="selected", value=1) + cr4.pack(side=LEFT, expand=YES, padx=5) + cr4.invoke() + cr5 = ttk.Radiobutton(cr_group, text="deselected", value=2) + cr5.pack(side=LEFT, expand=YES, padx=5) + cr6 = ttk.Radiobutton( + cr_group, text="disabled", value=3, state=DISABLED + ) + cr6.pack(side=LEFT, expand=YES, padx=5) + + # demonstrates the treeview and notebook widgets + ttframe = ttk.Frame(container) + ttframe.pack(pady=5, fill=X, side=TOP) + table_data = [ + ("South Island, New Zealand", 1), + ("Paris", 2), + ("Bora Bora", 3), + ("Maui", 4), + ("Tahiti", 5), + ] + tv = ttk.Treeview( + master=ttframe, columns=[0, 1], show="headings", height=5 + ) + for row in table_data: + tv.insert("", END, values=row) + tv.selection_set("I001") + tv.heading(0, text="City") + tv.heading(1, text="Rank") + tv.column(0, width=300) + tv.column(1, width=70, anchor=CENTER) + tv.pack(side=LEFT, anchor=NE, fill=X) + + nb = ttk.Notebook(ttframe) + nb.pack(side=LEFT, padx=(10, 0), expand=YES, fill=BOTH) + nb_text = ( + "This is a notebook tab.\nYou can put any widget you want here." + ) + nb.add(ttk.Label(nb, text=nb_text), text="Tab 1", sticky=NW) + nb.add( + child=ttk.Label(nb, text="A notebook tab."), + text="Tab 2", + sticky=NW, + ) + nb.add(ttk.Frame(nb), text="Tab 3") + nb.add(ttk.Frame(nb), text="Tab 4") + nb.add(ttk.Frame(nb), text="Tab 5") + + # text widget + txt = ttk.Text(master=container, height=5, width=50, wrap="none") + txt.insert(END, DemoWidgets.ZEN) + txt.pack(side=LEFT, anchor=NW, pady=5, fill=BOTH, expand=YES) + + # demonstrates scale, progressbar, and meter, and scrollbar widgets + lframe_inner = ttk.Frame(container) + lframe_inner.pack(fill=BOTH, expand=YES, padx=10) + scale = ttk.Scale( + master=lframe_inner, orient=HORIZONTAL, value=75, from_=100, to=0 + ) + scale.pack(fill=X, pady=5, expand=YES) + + ttk.Progressbar( + master=lframe_inner, + orient=HORIZONTAL, + value=50, + ).pack(fill=X, pady=5, expand=YES) + + ttk.Progressbar( + master=lframe_inner, + orient=HORIZONTAL, + value=75, + bootstyle="success-striped", + ).pack(fill=X, pady=5, expand=YES) + + m = ttk.Meter( + master=lframe_inner, + metersize=150, + amountused=45, + subtext="meter widget", + bootstyle="info", + interactive=True, + ) + m.pack(pady=10) + + sb = ttk.Scrollbar( + master=lframe_inner, + orient=HORIZONTAL, + ) + sb.set(0.1, 0.9) + sb.pack(fill=X, pady=5, expand=YES) + + sb = ttk.Scrollbar( + master=lframe_inner, orient=HORIZONTAL, bootstyle="danger-round" + ) + sb.set(0.1, 0.9) + sb.pack(fill=X, pady=5, expand=YES) + + +if __name__ == "__main__": + + creator = ThemeCreator() + creator.mainloop() diff --git a/pylibraries/ttkcreator/__pycache__/__init__.cpython-39.pyc b/pylibraries/ttkcreator/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5167d935f6be707be3f5eb8918d353ab3c851ac GIT binary patch literal 192 zcmYj~F$%&!5Jfj&Awmu!Vy3VXL@ewC8xOE-lEEy=X4c(_k|TK~TaRET2+mge;lE-2 zFTTn1nGzj7i#pwB!oM+3~JIihp<`G~^JNoEu?}SM11R0LI9085qkz zwt|9MwaI+cf_Y Ft1s&tGy4Dl literal 0 HcmV?d00001 diff --git a/pylibraries/ttkcreator/__pycache__/__main__.cpython-39.pyc b/pylibraries/ttkcreator/__pycache__/__main__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2ed7103fdba2f247e97db848d810fc88e2c46e3 GIT binary patch literal 13471 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