mirror of
https://github.com/thewesker/lazy-dsi-file-downloader.git
synced 2025-12-20 12:31:11 -05:00
Compare commits
10 Commits
ghactionst
...
macostest4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5937d32b87 | ||
|
|
6367e8f8cc | ||
|
|
958973fe26 | ||
|
|
3aaad7a456 | ||
|
|
498755fd49 | ||
|
|
fc7e2b2e2e | ||
|
|
f2e62ad95e | ||
|
|
46cfe7815a | ||
|
|
9fae5314c1 | ||
|
|
0541727424 |
42
.github/workflows/executables.yml
vendored
42
.github/workflows/executables.yml
vendored
@@ -31,12 +31,12 @@ jobs:
|
||||
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
||||
- name: Make binary
|
||||
run: |
|
||||
pyinstaller --onefile --add-data "certifi;certifi" --add-data "requests;requests" --add-data "urllib3;urllib3" --add-data "lazy.ico;." --add-data "chardet;chardet" --icon "lazy.ico" --console --name "lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}" main.py
|
||||
pyinstaller --onefile --add-data "certifi;certifi" --add-data "requests;requests" --add-data "urllib3;urllib3" --add-data "lazy.ico;." --add-data "chardet;chardet" --icon "lazy.ico" --console --name "lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-Windows" main.py
|
||||
cd dist
|
||||
- name: Publish builds
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}.exe
|
||||
path: dist/lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-Windows.exe
|
||||
name: windows
|
||||
|
||||
python:
|
||||
@@ -51,20 +51,52 @@ jobs:
|
||||
- name: Make zip
|
||||
run: |
|
||||
mkdir temporary
|
||||
cp -r certifi chardet Darwin idna Linux requests urllib3 temporary/
|
||||
cp -r certifi chardet Darwin idna Linux requests urllib3 tkmacosx temporary/
|
||||
cp LICENSE main.py README.md temporary/
|
||||
zip lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-Python3.zip temporary
|
||||
cd temporary
|
||||
chmod +x main.py
|
||||
zip -r ../lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-Python3.zip *
|
||||
- name: Publish artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-Python3.zip
|
||||
name: python
|
||||
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
name: macOS
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.x"
|
||||
architecture: "x64"
|
||||
- name: Install pyinstaller
|
||||
run: |
|
||||
pip3 install pyinstaller
|
||||
- name: Get tag
|
||||
id: vars
|
||||
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
||||
- name: Create executable
|
||||
run: |
|
||||
pyinstaller --onefile --add-data "tkmacosx:tkmacosx" --add-data "Darwin:Darwin" --add-data "certifi:certifi" --add-data "requests:requests" --add-data "urllib3:urllib3" --add-data "lazy.ico:." --add-data "chardet:chardet" --icon "lazy.ico" --console --name "lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-macOS" main.py
|
||||
cd dist
|
||||
chmod +x lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-macOS
|
||||
zip ../lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-macOS.zip lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-macOS
|
||||
- name: Publish artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: lazy-dsi-file-downloader-${{ steps.vars.outputs.tag }}-macOS.zip
|
||||
name: macos
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
name: Publish builds
|
||||
if: ${{ success() }}
|
||||
needs: [windows,python]
|
||||
needs: [windows,python,macos]
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
|
||||
163
main.py
163
main.py
@@ -157,6 +157,8 @@ def start():
|
||||
|
||||
#Variables
|
||||
directory = SDentry
|
||||
if directory.endswith("\\") or directory.endswith("/"):
|
||||
directory = directory[:-1]
|
||||
version = firmwareVersion.get()
|
||||
unlaunchNeeded = unlaunch.get()
|
||||
|
||||
@@ -287,6 +289,8 @@ def start():
|
||||
os.remove(directory+"/hiya.dsi")
|
||||
proc = Popen([_7za,"x","-aoa",downloadLocation, "-o"+directory,"for SDNAND SD card\hiya.dsi"])
|
||||
ret_val = proc
|
||||
shutil.move(directory+"/for SDNAND SD card/hiya.dsi",directory+"/hiya.dsi")
|
||||
shutil.rmtree(directory+"/for SDNAND SD card/")
|
||||
|
||||
|
||||
else:
|
||||
@@ -387,7 +391,7 @@ def extraHomebrew(source):
|
||||
frame.pack(fill=tkinter.BOTH, expand=True)
|
||||
|
||||
|
||||
okButton = tkinter.Button(homebrewWindow, text = "OK", font=(buttonFont), command=lambda:okButtonPress(homebrewWindow,source),bg="#f0f0f0",fg="#000000")
|
||||
okButton = Button(homebrewWindow, text = "OK", font=(buttonFont), command=lambda:okButtonPress(homebrewWindow,source),bg="#f0f0f0",fg="#000000")
|
||||
okButton.pack(side=tkinter.RIGHT, padx=5, pady=5)
|
||||
homebrewWindow.update()
|
||||
canvas.config(scrollregion=canvas.bbox("all"))
|
||||
@@ -433,9 +437,9 @@ def summonWindow0():
|
||||
bullet = tkinter.Label(topFrame, text=x, font=(paragraphFont),fg=foregroundColour,wraplength=450, justify="left")
|
||||
bullet.grid(column=0,row=count+3, sticky="w", padx=5)
|
||||
|
||||
discordButton = tkinter.Button(bottomFrame, text="DS⁽ⁱ⁾ Mode Hacking Discord server", fg=foregroundColour,bg=buttonColour, font=(buttonFont),command=lambda:webbrowser.open("https://discord.gg/yD3spjv",new=1))
|
||||
discordButton = Button(bottomFrame, text="DS⁽ⁱ⁾ Mode Hacking Discord server", fg=foregroundColour,bg=buttonColour, font=(buttonFont),command=lambda:webbrowser.open("https://discord.gg/yD3spjv",new=1))
|
||||
discordButton.pack(side=tkinter.LEFT, padx="5", pady="5")
|
||||
nextButton = tkinter.Button(bottomFrame, text="Next",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),summonWindow1()])
|
||||
nextButton = Button(bottomFrame, text="Next",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),summonWindow1()])
|
||||
nextButton.pack(side=tkinter.RIGHT, padx="5", pady="5")
|
||||
|
||||
window.protocol("WM_DELETE_WINDOW",lambda:closeButtonPress(window))
|
||||
@@ -463,9 +467,9 @@ def summonWindow1():
|
||||
selector["menu"].config(bg=buttonColour,fg=foregroundColour,font=(buttonFont))
|
||||
selector.grid(column=0,row=5,sticky="w")
|
||||
|
||||
backButton = tkinter.Button(bottomFrame,text="Back", font=(buttonFont),fg=foregroundColour,bg=backButtonColour,command=lambda: [topFrame.destroy(),bottomFrame.destroy(),summonWindow0()], width="8")
|
||||
backButton = Button(bottomFrame,text="Back", font=(buttonFont),fg=foregroundColour,bg=backButtonColour,command=lambda: [topFrame.destroy(),bottomFrame.destroy(),summonWindow0()], width="8")
|
||||
backButton.pack(side=tkinter.LEFT)
|
||||
nextButton = tkinter.Button(bottomFrame, text="Next",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),summonWindow2()])
|
||||
nextButton = Button(bottomFrame, text="Next",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),summonWindow2()])
|
||||
nextButton.pack(side=tkinter.RIGHT, padx="5", pady="5")
|
||||
window.protocol("WM_DELETE_WINDOW",lambda:closeButtonPress(window))
|
||||
|
||||
@@ -496,9 +500,9 @@ def summonWindow2():
|
||||
updateHiyaCheck.grid(column=0,row=7,sticky="w")
|
||||
buttonExtraHomebrew = tkinter.Button(topFrame, text = "Additional homebrew...", command =lambda:[extraHomebrew(window)], fg=foregroundColour,font=(buttonFont),bg=buttonColour)
|
||||
buttonExtraHomebrew.grid(column=0,row=8,sticky="w",pady=5)
|
||||
backButton = tkinter.Button(bottomFrame,text="Back", font=(buttonFont),fg=foregroundColour,bg=backButtonColour,command=lambda: [topFrame.destroy(),bottomFrame.destroy(),summonWindow1()], width="8")
|
||||
backButton = Button(bottomFrame,text="Back", font=(buttonFont),fg=foregroundColour,bg=backButtonColour,command=lambda: [topFrame.destroy(),bottomFrame.destroy(),summonWindow1()], width="8")
|
||||
backButton.pack(side=tkinter.LEFT)
|
||||
nextButton = tkinter.Button(bottomFrame, text="Next",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),summonWindow3()])
|
||||
nextButton = Button(bottomFrame, text="Next",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),summonWindow3()])
|
||||
nextButton.pack(side=tkinter.RIGHT, padx="5", pady="5")
|
||||
window.protocol("WM_DELETE_WINDOW",lambda:closeButtonPress(window))
|
||||
|
||||
@@ -517,11 +521,11 @@ def summonWindow3():
|
||||
noticeLabel.grid(column=0,row=2,sticky="w")
|
||||
SDentry = tkinter.Entry(topFrame, fg=foregroundColour,bg=buttonColour,font=(buttonFont),width=25)
|
||||
SDentry.grid(column=0, row=3,sticky="w")
|
||||
chooseDirButton = tkinter.Button(topFrame, text = "Click to select folder", command =lambda:chooseDir(topFrame,SDentry),fg=foregroundColour,bg=buttonColour,font=(buttonFont),width=25)
|
||||
chooseDirButton = Button(topFrame, text = "Click to select folder", command =lambda:chooseDir(topFrame,SDentry),fg=foregroundColour,bg=buttonColour,font=(buttonFont),width=25)
|
||||
chooseDirButton.grid(column=0, row=4,sticky="w",pady=5)
|
||||
backButton = tkinter.Button(bottomFrame,text="Back", font=(buttonFont),fg=foregroundColour,bg=backButtonColour,command=lambda: [topFrame.destroy(),bottomFrame.destroy(),summonWindow2()], width="8")
|
||||
backButton = Button(bottomFrame,text="Back", font=(buttonFont),fg=foregroundColour,bg=backButtonColour,command=lambda: [topFrame.destroy(),bottomFrame.destroy(),summonWindow2()], width="8")
|
||||
backButton.pack(side=tkinter.LEFT)
|
||||
nextButton = tkinter.Button(bottomFrame, text="Start",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[globalify(SDentry.get()),topFrame.destroy(),bottomFrame.destroy(),summonWindow4()])
|
||||
nextButton = Button(bottomFrame, text="Start",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[globalify(SDentry.get()),topFrame.destroy(),bottomFrame.destroy(),summonWindow4()])
|
||||
nextButton.pack(side=tkinter.RIGHT, padx="5", pady="5")
|
||||
window.protocol("WM_DELETE_WINDOW",lambda:closeButtonPress(window))
|
||||
|
||||
@@ -546,10 +550,10 @@ def summonWindow4():
|
||||
outputBox.grid(column=0,row=2,sticky="w")
|
||||
startThread.start()
|
||||
global finalbackButton
|
||||
finalbackButton = tkinter.Button(bottomFrame,state="disabled", text="Back", font=(buttonFont),fg=foregroundColour,bg=backButtonColour,command=lambda: [topFrame.destroy(),bottomFrame.destroy(),summonWindow3()], width="8")
|
||||
finalbackButton = Button(bottomFrame,state="disabled", text="Back", font=(buttonFont),fg=foregroundColour,bg=backButtonColour,command=lambda: [topFrame.destroy(),bottomFrame.destroy(),summonWindow3()], width="8")
|
||||
finalbackButton.pack(side=tkinter.LEFT)
|
||||
global finalnextButton
|
||||
finalnextButton = tkinter.Button(bottomFrame, state="disabled", text="Finish",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),summonWindow5()])
|
||||
finalnextButton = Button(bottomFrame, state="disabled", text="Finish",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),summonWindow5()])
|
||||
finalnextButton.pack(side=tkinter.RIGHT, padx="5", pady="5")
|
||||
window.protocol("WM_DELETE_WINDOW",lambda:donothing)
|
||||
|
||||
@@ -578,84 +582,89 @@ def summonWindow5():
|
||||
w = w + 1
|
||||
label= tkinter.Label(topFrame,text="Press the Close button to Exit",font=(bodyFont),fg=foregroundColour)
|
||||
label.grid(column=0,row=w+1,sticky="w")
|
||||
finish = tkinter.Button(bottomFrame, text="Close",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),closeButtonPress(window)])
|
||||
finish = Button(bottomFrame, text="Close",width="8", fg=foregroundColour,bg=nextButtonColour, font=(buttonFont),command=lambda:[topFrame.destroy(),bottomFrame.destroy(),closeButtonPress(window)])
|
||||
finish.pack(side=tkinter.RIGHT, padx="5", pady="5")
|
||||
window.protocol("WM_DELETE_WINDOW",lambda:closeButtonPress(window))
|
||||
|
||||
|
||||
if(sys.version_info.major < 3):
|
||||
print("This program will ONLY work on Python 3 and above")
|
||||
sys.exit()
|
||||
if __name__ == "__main__":
|
||||
|
||||
root = tkinter.Tk()
|
||||
window = tkinter.Toplevel(root)
|
||||
root.withdraw()
|
||||
if(sys.version_info.major < 3):
|
||||
print("This program will ONLY work on Python 3 and above")
|
||||
sys.exit()
|
||||
|
||||
#Homebrew Database
|
||||
homebrewDB = json.loads(requests.get('https://raw.githubusercontent.com/YourKalamity/just-a-dsi-DB/master/just-a-dsi-DB.json').content)
|
||||
homebrewList = []
|
||||
for x in homebrewDB:
|
||||
homebrewList.append(tkinter.IntVar())
|
||||
root = tkinter.Tk()
|
||||
window = tkinter.Toplevel(root)
|
||||
root.withdraw()
|
||||
|
||||
#Homebrew Database
|
||||
homebrewDB = json.loads(requests.get('https://raw.githubusercontent.com/YourKalamity/just-a-dsi-DB/master/just-a-dsi-DB.json').content)
|
||||
homebrewList = []
|
||||
for x in homebrewDB:
|
||||
homebrewList.append(tkinter.IntVar())
|
||||
|
||||
|
||||
#TKinter Vars
|
||||
downloadmemorypit = tkinter.IntVar(value=1)
|
||||
firmwareVersion = tkinter.StringVar()
|
||||
firmwareVersion.set(dsiVersions[0])
|
||||
downloadtwlmenu = tkinter.IntVar(value=1)
|
||||
downloaddumptool = tkinter.IntVar(value=1)
|
||||
unlaunch = tkinter.IntVar(value=0)
|
||||
godmode9i = tkinter.IntVar(value=0)
|
||||
updateHiyaCFW = tkinter.IntVar(value=0)
|
||||
#TKinter Vars
|
||||
downloadmemorypit = tkinter.IntVar(value=1)
|
||||
firmwareVersion = tkinter.StringVar()
|
||||
firmwareVersion.set(dsiVersions[0])
|
||||
downloadtwlmenu = tkinter.IntVar(value=1)
|
||||
downloaddumptool = tkinter.IntVar(value=1)
|
||||
unlaunch = tkinter.IntVar(value=0)
|
||||
godmode9i = tkinter.IntVar(value=0)
|
||||
updateHiyaCFW = tkinter.IntVar(value=0)
|
||||
|
||||
#Fonts
|
||||
titleFont = tkinter.font.Font(
|
||||
family= "Segoe UI",
|
||||
size= 15,
|
||||
weight= "bold"
|
||||
#Fonts
|
||||
titleFont = tkinter.font.Font(
|
||||
family= "Segoe UI",
|
||||
size= 15,
|
||||
weight= "bold"
|
||||
)
|
||||
subtitleFont = tkinter.font.Font(
|
||||
family= "Segoe UI",
|
||||
size= 11,
|
||||
slant= "italic"
|
||||
)
|
||||
subtitleFont = tkinter.font.Font(
|
||||
family= "Segoe UI",
|
||||
size= 11,
|
||||
slant= "italic"
|
||||
)
|
||||
|
||||
bodyFont = tkinter.font.Font(
|
||||
family= "Segoe UI",
|
||||
underline= False,
|
||||
size = 11
|
||||
|
||||
bodyFont = tkinter.font.Font(
|
||||
family= "Segoe UI",
|
||||
underline= False,
|
||||
size = 11
|
||||
)
|
||||
buttonFont = tkinter.font.Font(
|
||||
family="Segoe UI",
|
||||
underline=False,
|
||||
size = 11,
|
||||
weight = "bold"
|
||||
)
|
||||
buttonFont = tkinter.font.Font(
|
||||
family="Segoe UI",
|
||||
underline=False,
|
||||
size = 11,
|
||||
weight = "bold"
|
||||
)
|
||||
|
||||
bigListFont = tkinter.font.Font(
|
||||
family="Segoe UI",
|
||||
underline=False,
|
||||
size=9
|
||||
)
|
||||
bigListFont = tkinter.font.Font(
|
||||
family="Segoe UI",
|
||||
underline=False,
|
||||
size=9
|
||||
)
|
||||
|
||||
paragraphFont = tkinter.font.Font(
|
||||
family="Segoe UI",
|
||||
size=10
|
||||
)
|
||||
paragraphFont = tkinter.font.Font(
|
||||
family="Segoe UI",
|
||||
size=10
|
||||
)
|
||||
|
||||
if platform.system() == "Darwin": #Why is macOS so difficult...
|
||||
backgroundColour = "#f0f0f0" #How dull and boring
|
||||
foregroundColour = "black"
|
||||
buttonColour = "#f0f0f0"
|
||||
backButtonColour = "#f0f0f0"
|
||||
nextButtonColour = "#f0f0f0"
|
||||
else: #Non Jeve Stobs worshippers
|
||||
backgroundColour = "#252a34"
|
||||
foregroundColour = "white"
|
||||
buttonColour = "#7289DA"
|
||||
backButtonColour = "#567487"
|
||||
nextButtonColour = "#027b76"
|
||||
if platform.system() == "Darwin": #Why is macOS so difficult...
|
||||
from tkmacosx import Button
|
||||
backgroundColour = "#f0f0f0" #How dull and boring
|
||||
foregroundColour = "black"
|
||||
buttonColour = "#f0f0f0"
|
||||
backButtonColour = "#f0f0f0"
|
||||
nextButtonColour = "#f0f0f0"
|
||||
|
||||
|
||||
summonWindow0()
|
||||
root.mainloop()
|
||||
else: #Non Jeve Stobs worshippers
|
||||
from tkinter import Button
|
||||
backgroundColour = "#252a34"
|
||||
foregroundColour = "white"
|
||||
buttonColour = "#7289DA"
|
||||
backButtonColour = "#567487"
|
||||
nextButtonColour = "#027b76"
|
||||
|
||||
summonWindow0()
|
||||
root.mainloop()
|
||||
|
||||
37
tkmacosx/__init__.py
Normal file
37
tkmacosx/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# Copyright 2020 Saad Mairaj
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
This module provides some modified widgets of Tkinter which works better on macos
|
||||
and some more useful functions and classes as well. For example Button of tkmacosx which
|
||||
looks and feels exactly like a native tkinter button can change its background
|
||||
and foreground colors.
|
||||
|
||||
Read more about tkmacosx in detail on
|
||||
https://github.com/Saadmairaj/tkmacosx/tree/master/tkmacosx.
|
||||
"""
|
||||
|
||||
__version__ = '0.1.4'
|
||||
|
||||
from tkmacosx.basewidget import check_appearance, get_shade, delta
|
||||
from tkmacosx.variables import ColorVar, DictVar, SaveVar, demo_colorvar, demo_savevar
|
||||
from tkmacosx.widget import Button, CircleButton, SFrame, Marquee, demo_sframe, demo_button, demo_marquee
|
||||
from tkmacosx.colors import Hex, OrderedHex, all_colors, named_colors, hue_color
|
||||
from tkmacosx.colorscale import Colorscale, demo_colorscale, gradient
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo_sframe()
|
||||
demo_button()
|
||||
demo_colorvar()
|
||||
demo_colorscale()
|
||||
324
tkmacosx/__main__.py
Normal file
324
tkmacosx/__main__.py
Normal file
@@ -0,0 +1,324 @@
|
||||
# Copyright 2020 Saad Mairaj
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
import random
|
||||
if sys.version_info.major == 2:
|
||||
import ttk
|
||||
import Tkinter as tk
|
||||
import tkFont as font
|
||||
from tkColorChooser import askcolor
|
||||
elif sys.version_info.major == 3:
|
||||
from tkinter import ttk
|
||||
import tkinter as tk
|
||||
import tkinter.font as font
|
||||
from tkinter.colorchooser import askcolor
|
||||
|
||||
from tkmacosx.widget import *
|
||||
from tkmacosx.variables import *
|
||||
from tkmacosx.colorscale import Colorscale
|
||||
from tkmacosx.colors import Hex as C_dict, all_colors
|
||||
|
||||
|
||||
color_list = [list(i.values())[0].get('hex') for i in all_colors]
|
||||
|
||||
|
||||
def grid(root, row, column):
|
||||
"Defines rows and columns if grid method is used"
|
||||
if column:
|
||||
for y in range(column):
|
||||
tk.Grid.columnconfigure(root, y, weight=1)
|
||||
if row:
|
||||
for x in range(row):
|
||||
tk.Grid.rowconfigure(root, x, weight=1)
|
||||
return
|
||||
|
||||
|
||||
def sort_random_values(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
value = fn(*args, **kwargs)
|
||||
if isinstance(value, (list, tuple)) and len(value) == 1:
|
||||
return value[0]
|
||||
return value
|
||||
return wrapper
|
||||
|
||||
|
||||
def choices(seq, k):
|
||||
"Random choices."
|
||||
return tuple(random.choice(seq) for i in range(k))
|
||||
|
||||
|
||||
@sort_random_values
|
||||
def get_random_colors(k=1):
|
||||
return choices(color_list, k=k)
|
||||
|
||||
|
||||
@sort_random_values
|
||||
def get_random_font(k=1):
|
||||
def family(): return random.choice(font.families())
|
||||
|
||||
def weight(): return random.choice(('bold', 'normal'))
|
||||
|
||||
def slant(): return random.choice(('italic', 'roman'))
|
||||
|
||||
def size(): return random.randrange(9, 30)
|
||||
|
||||
def underline(): return random.choice((True, False))
|
||||
|
||||
overstrike = underline
|
||||
return tuple(font.Font(family=family(), weight=weight(),
|
||||
slant=slant(), underline=underline(),
|
||||
overstrike=overstrike(), size=30,) for i in range(k))
|
||||
|
||||
|
||||
@sort_random_values
|
||||
def get_random_size(k=1, start=10, end=200):
|
||||
return tuple(choices([i for i in range(start, end)], k=k))
|
||||
|
||||
|
||||
@sort_random_values
|
||||
def get_random_relief(k=1):
|
||||
return choices(('flat', 'groove', 'raised',
|
||||
'ridge', 'solid', 'sunken'), k=k)
|
||||
|
||||
|
||||
class Sample(tk.Tk):
|
||||
def __init__(self):
|
||||
tk.Tk.__init__(self)
|
||||
self.resizable(0, 0)
|
||||
self.geometry('420x700+300+100')
|
||||
self.title('Mac OSX Button Testing')
|
||||
self.wm_attributes('-modified', 1)
|
||||
self.main_color = ColorVar(value='#ffe6f4')
|
||||
self['bg'] = self.main_color
|
||||
grid(self, 25, 5)
|
||||
self.L1 = tk.Label(self, text='Comparison',
|
||||
bg=self.main_color, font=('', 18, 'bold'))
|
||||
self.L1.grid(row=0, column=0, columnspan=5, sticky='nsew')
|
||||
Button(self, text='Hello').grid(row=1, column=1, sticky='s')
|
||||
ttk.Button(self, text='Hello').grid(row=1, column=3, sticky='s')
|
||||
tk.Button(self, text='Hello').grid(row=1, column=2, sticky='s')
|
||||
tk.Label(self, bg=self.main_color, font=('', 10),
|
||||
text='(Mac OSX)').grid(row=2, column=1, sticky='n',)
|
||||
tk.Label(self, bg=self.main_color, font=('', 10),
|
||||
text='(ttk themed)').grid(row=2, column=3, sticky='n')
|
||||
tk.Label(self, bg=self.main_color, font=('', 10),
|
||||
text='(Default)').grid(row=2, column=2, sticky='n')
|
||||
ttk.Separator(self, orient='vertical').grid(
|
||||
row=3, column=0, columnspan=5, sticky='ew')
|
||||
|
||||
# ------------ Seperator -------------
|
||||
|
||||
# ------------ Demonstration ------------
|
||||
|
||||
self.sfr = SFrame(self, bg=self.main_color)
|
||||
self.sfr.grid(rowspan=27, columnspan=5, sticky='nsew')
|
||||
for i in range(5):
|
||||
self.sfr.grid_columnconfigure(i, weight=1)
|
||||
self.L2 = tk.Label(self.sfr, text='Demonstration',
|
||||
bg=self.main_color, font=('', 20, 'bold'))
|
||||
self.L2.grid(row=1, column=0, columnspan=5,
|
||||
sticky='new', pady=(20, 10))
|
||||
|
||||
# ------------ Active Color ------------
|
||||
|
||||
self.L3 = tk.Label(self.sfr, text='1. Change Active color', bg=self.main_color,
|
||||
font=('', 15, 'bold'))
|
||||
self.L3.grid(row=2, column=0, columnspan=5, sticky='nsew', pady=10)
|
||||
self.L4 = tk.Label(self.sfr, text='The active color can be changed to any gradient color.',
|
||||
bg=self.main_color, font=('', 10))
|
||||
self.L4.grid(row=3, column=0, columnspan=5, sticky='new')
|
||||
self.B1 = Button(self.sfr, text='Press Me', pady=20)
|
||||
self.B1.grid(row=4, column=0, columnspan=5, pady=20)
|
||||
self.C1 = tk.StringVar(value='Select')
|
||||
self.L5 = tk.Label(self.sfr, text='From',
|
||||
bg=self.main_color, font=('', 12))
|
||||
self.L5.grid(row=5, column=1, sticky='nwe')
|
||||
self.Om1 = tk.OptionMenu(self.sfr, self.C1, *C_dict.keys(),
|
||||
command=self.change_active_color)
|
||||
self.Om1.config(bg=self.main_color, width=15)
|
||||
self.Om1.grid(row=6, column=1, sticky='s', pady=(0, 10))
|
||||
for i in range(self.Om1['menu'].index('end')+1):
|
||||
self.Om1['menu'].entryconfig(i, foreground=list(C_dict)[i])
|
||||
self.C2 = tk.StringVar(value='Select')
|
||||
self.L6 = tk.Label(self.sfr, text='To',
|
||||
bg=self.main_color, font=('', 12))
|
||||
self.L6.grid(row=5, column=3, sticky='nwe')
|
||||
self.Om2 = tk.OptionMenu(self.sfr, self.C2, *C_dict.keys(),
|
||||
command=self.change_active_color)
|
||||
self.Om2.config(bg=self.main_color, width=15)
|
||||
self.Om2.grid(row=6, column=3, sticky='s', pady=(0, 10))
|
||||
for i in range(self.Om2['menu'].index('end')+1):
|
||||
self.Om2['menu'].entryconfig(i, foreground=list(C_dict)[i])
|
||||
|
||||
# ------------ Background Color ------------
|
||||
|
||||
# ttk.Separator(self.sfr, orient='vertical').grid(row=6, column=2, columnspan=1, sticky='ew')
|
||||
self.L7 = tk.Label(self.sfr, text='2. Change Background color', bg=self.main_color,
|
||||
font=('', 15, 'bold'))
|
||||
self.L7.grid(row=7, column=0, columnspan=5,
|
||||
sticky='nsew', pady=(50, 0))
|
||||
self.L8 = tk.Label(self.sfr, text='Click on the button to choose the color.',
|
||||
bg=self.main_color, font=('', 10))
|
||||
self.L8.grid(row=8, column=0, columnspan=5, sticky='new', pady=10)
|
||||
|
||||
self.B2 = Button(self.sfr, text='Color me',
|
||||
font=('', 30,), pady=10, padx=10)
|
||||
self.B2.grid(row=9, column=0, columnspan=5, sticky='', pady=20)
|
||||
|
||||
self.B3 = Button(self.sfr, text='Change Background Color',
|
||||
bg='#d0c0ea', borderless=1)
|
||||
self.B3['command'] = lambda: self.B2.config(bg=askcolor()[1])
|
||||
self.B3.grid(row=10, column=0, columnspan=5, sticky='w', pady=10, padx=10)
|
||||
self.B4 = Button(self.sfr, text='Change Foreground Color',
|
||||
bg="#d0c0ea", borderless=1)
|
||||
self.B4['command'] = lambda: self.B2.config(fg=askcolor()[1])
|
||||
self.B4.grid(row=10, column=0, columnspan=5, sticky='e', pady=10, padx=10)
|
||||
|
||||
# ------------ Borderless ------------
|
||||
|
||||
self.L9 = tk.Label(self.sfr, text='3. Switch Between Borderless', bg=self.main_color,
|
||||
font=('', 15, 'bold'))
|
||||
self.L9.grid(row=11, column=0, columnspan=5,
|
||||
sticky='sew', pady=(50, 0))
|
||||
self.L10 = tk.Label(self.sfr, text="""
|
||||
In borderless it will blend with its parent widget background color.
|
||||
Give parameter `borderless = True / False` to use it.""", bg=self.main_color, font=('', 10))
|
||||
self.L10.grid(row=12, column=0, columnspan=5, sticky='new')
|
||||
|
||||
self.B5 = Button(self.sfr, text='No Borders', borderless=1, height=40,
|
||||
bg='#212F3D', fg='white', activebackground=("#EAECEE", "#212F3D"))
|
||||
self.B5.grid(row=13, columnspan=5, pady=(20, 5))
|
||||
|
||||
self.B6 = Button(self.sfr, text='No Borders', borderless=1, height=40,
|
||||
bg='#F7DC6F', fg='#21618C', activebackground=('#B3B6B7', '#58D68D'))
|
||||
self.B6.grid(row=14, columnspan=5, pady=(0, 20))
|
||||
self.var1 = tk.BooleanVar(value=True)
|
||||
self.CB1 = tk.Checkbutton(self.sfr, text='Toggle Borderless', variable=self.var1,
|
||||
bg=self.main_color, command=self.change_borderless_state)
|
||||
self.CB1.grid(row=15, columnspan=5, pady=(0, 10))
|
||||
|
||||
# ------------ Bordercolor ------------
|
||||
|
||||
self.L11 = tk.Label(self.sfr, text='4. Change Bordercolor', bg=self.main_color,
|
||||
font=('', 15, 'bold'))
|
||||
self.L11.grid(row=16, column=0, columnspan=5,
|
||||
sticky='sew', pady=(50, 0))
|
||||
self.L12 = tk.Label(self.sfr, text="Change Bordercolor of the button\nNote: if borderless=True, then the bordercolor won't work.",
|
||||
bg=self.main_color, font=('', 10))
|
||||
self.L12.grid(row=17, column=0, columnspan=5, sticky='new')
|
||||
|
||||
self.B7 = Button(self.sfr, text='Button', pady=10,
|
||||
padx=5, font=('Zapfino', 12, 'bold'))
|
||||
self.B7.grid(row=18, columnspan=5, pady=30)
|
||||
|
||||
self.CS1 = Colorscale(self.sfr, value='hex', mousewheel=1,
|
||||
command=lambda e: self.B7.config(bordercolor=e))
|
||||
self.CS1.grid(row=19, columnspan=5, sticky='ew', padx=10)
|
||||
|
||||
self.CS2 = Colorscale(self.sfr, value='hex', mousewheel=1,
|
||||
gradient=('#FCF6F5', '#990011'),
|
||||
command=lambda e: self.B7.config(bg=e))
|
||||
self.CS2.grid(row=20, columnspan=5, sticky='ew', padx=10, pady=5)
|
||||
|
||||
self.CS3 = Colorscale(self.sfr, value='hex', mousewheel=1,
|
||||
gradient=('green', 'yellow'),
|
||||
command=lambda e: self.B7.config(fg=e))
|
||||
self.CS3.grid(row=21, columnspan=5, sticky='ew', padx=10)
|
||||
|
||||
self.CS4 = Colorscale(self.sfr, value='hex', mousewheel=1,
|
||||
gradient=('pink', 'purple'),
|
||||
command=lambda e: self.B7.config(overforeground=e))
|
||||
self.CS4.grid(row=22, columnspan=5, sticky='ew', padx=10, pady=5)
|
||||
|
||||
# ------------ Random button styling ------------
|
||||
self.L11 = tk.Label(self.sfr, text='5. Button Styling', bg=self.main_color,
|
||||
font=('', 15, 'bold'))
|
||||
self.L11.grid(row=23, column=0, columnspan=5,
|
||||
sticky='sew', pady=(50, 0))
|
||||
self.L12 = tk.Label(self.sfr, text="Press the button to ramdomise the style of the button.",
|
||||
bg=self.main_color, font=('', 10))
|
||||
self.L12.grid(row=24, column=0, columnspan=5, sticky='new')
|
||||
|
||||
self.B10 = Button(self.sfr, text='Button', borderless=1)
|
||||
self.B10.grid(row=25, columnspan=5, pady=20)
|
||||
|
||||
self.B11 = Button(self.sfr, text='Change Style', borderless=1, fg='#21618C',
|
||||
activebackground=('#B3B6B7', '#58D68D'), command=self.change_button_style)
|
||||
self.B11.grid(row=26, columnspan=5, ipady=5)
|
||||
|
||||
self.button_clicks = 1
|
||||
self.Text1 = tk.Text(self.sfr, background=self.main_color, highlightthickness=0,
|
||||
relief='sunken', height=20, bd=2, padx=10)
|
||||
self.Text1.grid(row=27, columnspan=5, pady=20, padx=20)
|
||||
self.sfr._avoid_mousewheel((self.Text1, self.CS1, self.CS2, self.CS3, self.CS4))
|
||||
self.change_button_style()
|
||||
self.update_idletasks()
|
||||
|
||||
def change_active_color(self, *ags):
|
||||
c1 = self.C1.get() if not self.C1.get() == 'Select' else None
|
||||
c2 = self.C2.get() if not self.C2.get() == 'Select' else None
|
||||
self.Om1.config(bg=c1)
|
||||
self.Om2.config(bg=c2)
|
||||
self.B1['activebackground'] = (c1, c2)
|
||||
|
||||
def change_borderless_state(self):
|
||||
if self.var1.get():
|
||||
self.B5['borderless'] = 1
|
||||
self.B6['borderless'] = 1
|
||||
else:
|
||||
self.B5['borderless'] = 0
|
||||
self.B6['borderless'] = 0
|
||||
|
||||
def change_button_style(self):
|
||||
cnf = dict(
|
||||
foreground=get_random_colors(),
|
||||
background=get_random_colors(),
|
||||
activebackground=get_random_colors(2),
|
||||
activeforeground=get_random_colors(),
|
||||
overrelief=get_random_relief(),
|
||||
relief=get_random_relief(),
|
||||
highlightthickness=get_random_size(1, 1, 5),
|
||||
borderwidth=get_random_size(1, 1, 5),
|
||||
font=get_random_font(),
|
||||
highlightbackground=get_random_colors(),
|
||||
focuscolor=get_random_colors(),
|
||||
overbackground=get_random_colors(),
|
||||
overforeground=get_random_colors(),
|
||||
)
|
||||
self.B10.config(**cnf)
|
||||
|
||||
self.Text1.insert('end', '\n\nBUTTON STYLE %s\n\n'%self.button_clicks)
|
||||
for k,v in cnf.items():
|
||||
if k == 'font':
|
||||
self.Text1.insert('end', 'font:\n')
|
||||
for i, j in v.config().items():
|
||||
self.Text1.insert('end', '\t%s\t\t= %s\n' %(i,j))
|
||||
else:
|
||||
self.Text1.insert('end', '%s\t\t\t= %s\n' %(k,v))
|
||||
self.Text1.see('end')
|
||||
self.Text1.insert('end', '\n\n'+'-'*40)
|
||||
self.button_clicks += 1
|
||||
self.sfr['canvas'].yview_moveto('1.0')
|
||||
self.update_idletasks()
|
||||
|
||||
|
||||
def main():
|
||||
"Demonstration of tkmacosx."
|
||||
Sample().mainloop()
|
||||
|
||||
|
||||
# Testing Demo
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
1532
tkmacosx/basewidget.py
Normal file
1532
tkmacosx/basewidget.py
Normal file
File diff suppressed because it is too large
Load Diff
1266
tkmacosx/colors.py
Normal file
1266
tkmacosx/colors.py
Normal file
File diff suppressed because it is too large
Load Diff
401
tkmacosx/colorscale.py
Normal file
401
tkmacosx/colorscale.py
Normal file
@@ -0,0 +1,401 @@
|
||||
# Copyright 2020 Saad Mairaj
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
'''
|
||||
Newer style colorchoosers for tkinter module.
|
||||
|
||||
Version: 0.1.4
|
||||
'''
|
||||
|
||||
__version__ = "0.1.4"
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
if sys.version_info.major == 2: # For python 2.x
|
||||
import Tkinter as _tk
|
||||
from tkFont import Font
|
||||
import tkFont as font
|
||||
elif sys.version_info.major == 3: # For python 3.x
|
||||
import tkinter as _tk
|
||||
from tkinter import font
|
||||
from tkinter.font import Font
|
||||
|
||||
import colour
|
||||
import tkmacosx.basewidget as tkb
|
||||
|
||||
|
||||
HEX = 'hex'
|
||||
RGB = 'rgb'
|
||||
|
||||
|
||||
def gradient(iteration):
|
||||
"""This function returns a list of HSL values
|
||||
of all the colors in an order."""
|
||||
|
||||
ops = {'+': lambda c, step: min(1.0, c + step),
|
||||
'-': lambda c, step: max(0.0, c - step)}
|
||||
|
||||
index = 0
|
||||
operation = '+'
|
||||
iteration = max(0, iteration-2)
|
||||
rgb, _list = [1.0, 0.0, 0.0], []
|
||||
combo = ((2, 1, 0), (2, 0, 1), (0, 2, 1), (0, 1, 2), (1, 0, 2), (1, 2, 0))
|
||||
step = float(len(combo)) / float(iteration)
|
||||
_list.append('#%02x%02x%02x' % (round(rgb[0]*255),
|
||||
round(rgb[1]*255), round(rgb[2]*255)))
|
||||
for i in range(iteration):
|
||||
if (rgb[combo[index][1]] == 1.0 and operation == '+') or \
|
||||
(rgb[combo[index][1]] == 0.0 and operation == '-'):
|
||||
operation = '-' if operation == '+' else '+'
|
||||
index += 1
|
||||
rgb[combo[index][1]] = ops[operation](rgb[combo[index][1]], step)
|
||||
_list.append('#%02x%02x%02x' % (round(rgb[0]*255),
|
||||
round(rgb[1]*255), round(rgb[2]*255)))
|
||||
_list.append('#%02x%02x%02x' % (round(1.0*255),
|
||||
round(0.0*255), round(0.0*255)))
|
||||
return _list
|
||||
|
||||
|
||||
class Colorscale(tkb._Canvas):
|
||||
"""
|
||||
## Color Scale.
|
||||
This is ColorScale alternate to tkinter's colorchooser.
|
||||
|
||||
### Args:
|
||||
- `value`: Get either 'RGB' or 'HEX'.
|
||||
- `command`: callback function with an argument.
|
||||
- `orient`: Set the orientation.
|
||||
- `mousewheel`: Set mousewheel to scroll marker.
|
||||
- `variable`: Give tkinter variable (`StringVar`).
|
||||
- `showinfo`: Shows hex or rbg while selecting color.
|
||||
- `gradient`: Take tuple of two colors or default.
|
||||
- `showinfodelay`: Delay before the showinfo disappears (in ms).
|
||||
"""
|
||||
|
||||
_features = ('value', 'command', 'orient', 'mousewheel', 'variable', 'showinfo',
|
||||
'showinfodelay', 'gradient',) # add more features
|
||||
|
||||
def __init__(self, master=None, cnf={}, **kw):
|
||||
kw = {k: v for k, v in _tk._cnfmerge(
|
||||
(cnf, kw)).items() if v is not None}
|
||||
self.cnf = {k: kw.pop(k, None)
|
||||
for k in kw.copy() if k in self._features}
|
||||
|
||||
self.cnf['value'] = self.cnf.get('value', 'hex')
|
||||
self.cnf['orient'] = self.cnf.get('orient', 'vertical')
|
||||
self.cnf['gradient'] = self.cnf.get('gradient', 'default')
|
||||
self.cnf['showinfo'] = self.cnf.get('showinfo', True)
|
||||
self.cnf['showinfodelay'] = self.cnf.get('showinfodelay', 1500)
|
||||
|
||||
kw['width'] = kw.get(
|
||||
"width", 250 if 'ver' in self.cnf['orient'] else 30)
|
||||
kw['height'] = kw.get(
|
||||
"height", 30 if 'ver' in self.cnf['orient'] else 250)
|
||||
kw['highlightthickness'] = kw.get('highlightthickness', 0)
|
||||
tkb._Canvas.__init__(self, master=master, cnf={}, **kw)
|
||||
|
||||
# Protected members of the class
|
||||
self._size = (0, 0)
|
||||
self._marker_color = 'black'
|
||||
self._xy = int((self.winfo_reqwidth() if 'ver' in \
|
||||
self['orient'] else self.winfo_reqheight()) / 3)
|
||||
|
||||
binds = [{'className': 'set_size', 'sequence': '<Configure>', 'func': self._set_size},
|
||||
{'className': 'b1_on_motion', 'sequence': '<B1-Motion>',
|
||||
'func': self._move_marker},
|
||||
{'className': 'b1_press', 'sequence': '<Button-1>', 'func': self._move_marker}]
|
||||
tkb._bind(self, *binds)
|
||||
self._set_mousewheel()
|
||||
|
||||
def _set_size(self, evt=None):
|
||||
"""Internal function."""
|
||||
if evt.width == self._size[0] and evt.height == self._size[1]:
|
||||
return
|
||||
self._size = (evt.width, evt.height)
|
||||
self._create_items('create', safe_create=True)
|
||||
|
||||
def _callback(self, color):
|
||||
"""Internal function."""
|
||||
if self.cnf.get('command'):
|
||||
self.cnf['command'](color)
|
||||
if self.cnf.get('variable'):
|
||||
self.cnf['variable'].set(color)
|
||||
|
||||
def _create_items(self, cmd, safe_create=False, **kw):
|
||||
"""Internal function.\n
|
||||
Checks and creates (text, marker,
|
||||
showinfo, gradient lines) items."""
|
||||
|
||||
def check_tag(tag):
|
||||
"""Internal function.\n
|
||||
If `cmd="check"` and the tag does not exist then
|
||||
the tag is created, but if `cmd="create"` and
|
||||
safe_create=True this will delete the tag if exists
|
||||
and creates a new tag with same settings."""
|
||||
if cmd == 'check':
|
||||
c = False
|
||||
elif cmd == 'create':
|
||||
c = True
|
||||
else:
|
||||
raise ValueError(
|
||||
'%s is not a valid command! Takes -create, -check' % cmd)
|
||||
cond1 = bool(not self.find('withtag', tag) or c)
|
||||
cond2 = bool(tag not in kw.get('avoid', []))
|
||||
if safe_create and cond1 and cond2:
|
||||
self.delete(tag)
|
||||
return cond1 and cond2
|
||||
|
||||
ids = []
|
||||
|
||||
if check_tag('gradient'):
|
||||
w, h = self.winfo_width(), self.winfo_height()
|
||||
iteration = w if 'ver' in self.cnf['orient'] else h
|
||||
# iteration -=
|
||||
if self.cnf.get('gradient', 'default') == 'default':
|
||||
color_list = gradient(iteration)
|
||||
elif isinstance(self.cnf.get('gradient'), (list, tuple)):
|
||||
c1 = colour.Color(self.cnf.get('gradient')[0])
|
||||
c2 = colour.Color(self.cnf.get('gradient')[1])
|
||||
color_list = c1.range_to(c2, iteration)
|
||||
elif isinstance(self.cnf.get('gradient'), str):
|
||||
c = colour.Color(self.cnf.get('gradient'))
|
||||
color_list = c.range_to(c, iteration)
|
||||
|
||||
for count, c in enumerate(color_list):
|
||||
if self.cnf['orient'] == 'vertical':
|
||||
ags = (count, -1, count, h)
|
||||
elif self.cnf['orient'] == 'horizontal':
|
||||
ags = (-1, count, w, count)
|
||||
ids.append(self._create('line', ags, {
|
||||
'fill': c, 'tag': 'gradient'}))
|
||||
|
||||
if check_tag('borderline'):
|
||||
w, h = self.winfo_width(), self.winfo_height()
|
||||
borderline_points = kw.get('borderline_points',
|
||||
(1, 1, self.winfo_width()-2,
|
||||
self.winfo_height()-2, 0))
|
||||
ids.append(self.rounded_rect(borderline_points, width=2,
|
||||
outline='#81b3f4', tag='borderline',
|
||||
style='arc'))
|
||||
|
||||
if check_tag('marker'):
|
||||
marker_points = kw.get('marker_points', (
|
||||
self._xy if 'ver' in self.cnf['orient'] else 2,
|
||||
2 if 'ver' in self.cnf['orient'] else self._xy,
|
||||
5 if 'ver' in self.cnf['orient'] else self.winfo_width() - 4,
|
||||
self.winfo_height() - 4 if 'ver' in self.cnf['orient'] else 5,
|
||||
2))
|
||||
ids.append(self.rounded_rect(marker_points, width=2,
|
||||
outline=self._marker_color,
|
||||
tag="marker", style='arc'))
|
||||
|
||||
if check_tag('markerbg'):
|
||||
markerbg_points = kw.get('markerbg_points')
|
||||
cnf = kw.get('markerbg_cnf')
|
||||
if not markerbg_points:
|
||||
return None
|
||||
ids.append(self._rounded(markerbg_points, **cnf))
|
||||
|
||||
if check_tag('info'):
|
||||
info_points = kw.get('info_points')
|
||||
cnf = kw.get('info_cnf')
|
||||
if not info_points:
|
||||
return None
|
||||
ids.append(self._create('text', info_points, cnf))
|
||||
|
||||
return ids
|
||||
|
||||
def _release(self, evt=None):
|
||||
"""Internal function."""
|
||||
self.delete('info', 'markerbg')
|
||||
|
||||
def _move_marker(self, evt, mw=None):
|
||||
"""Internal function."""
|
||||
if mw:
|
||||
evt.x = mw if 'ver' in self.cnf['orient'] else 10
|
||||
evt.y = 10 if 'ver' in self.cnf['orient'] else mw
|
||||
|
||||
self.after_cancel(getattr(self, '_remove_id', ' '))
|
||||
self._remove_id = self.after(self.cnf['showinfodelay'], self._release)
|
||||
|
||||
cond_x = bool(evt.x > 0 and evt.x < self.winfo_width())
|
||||
cond_y = bool(evt.y > 0 and evt.y < self.winfo_height())
|
||||
cond_state = bool(self['state'] not in 'disabled')
|
||||
|
||||
if not (cond_x and cond_y and cond_state):
|
||||
return
|
||||
|
||||
if not mw:
|
||||
self._xy = evt.x if 'ver' in self.cnf['orient'] else evt.y
|
||||
|
||||
c_id = self.find('overlapping', evt.x, evt.y, evt.x, evt.y)
|
||||
hexcode = self.itemcget(c_id[0], 'fill')
|
||||
rgb = [int(i/65535.0*255.0) for i in self.winfo_rgb(hexcode)]
|
||||
self._marker_color = 'black' if (
|
||||
rgb[0]*0.299 + rgb[1]*0.587 + rgb[2]*0.114) > 110 else 'white'
|
||||
|
||||
self._configure(('itemconfigure', 'borderline'),
|
||||
{'outline': hexcode}, None)
|
||||
|
||||
if self.cnf['value'] == "rgb":
|
||||
spacer, spacbg = 65, 55
|
||||
text = ' | '.join([v+':'+str(f)
|
||||
for f, v in zip(rgb, ('R', 'G', 'B'))])
|
||||
self._callback(rgb)
|
||||
elif self.cnf['value'] == "hex":
|
||||
spacer, spacbg = 35, 25
|
||||
text = hexcode
|
||||
self._callback(hexcode)
|
||||
|
||||
ver_cond = evt.x < self.winfo_width() - (spacbg+spacer)\
|
||||
and self['orient'] == 'vertical'
|
||||
hor_cond = evt.y < self.winfo_height() - (spacbg+spacer)\
|
||||
and self['orient'] == 'horizontal'
|
||||
markerbg_points = info_points = ()
|
||||
|
||||
if bool(ver_cond or hor_cond) and self['showinfo']:
|
||||
markerbg_points = (
|
||||
evt.x+spacer - spacbg if 'ver' in self.cnf['orient'] else self.winfo_width()/2-6,
|
||||
self.winfo_height()/2-6 if 'ver' in self.cnf['orient'] else evt.y+spacer-spacbg,
|
||||
evt.x+spacer + spacbg if 'ver' in self.cnf['orient'] else self.winfo_width()/2+7,
|
||||
self.winfo_height()/2+7 if 'ver' in self.cnf['orient'] else evt.y+spacer+spacbg,
|
||||
6)
|
||||
|
||||
info_points = (
|
||||
(evt.x+spacer if 'ver' in self.cnf['orient'] else self.winfo_width()/2,
|
||||
self.winfo_height()/2 if 'ver' in self.cnf['orient'] else evt.y+spacer))
|
||||
|
||||
elif self['showinfo']:
|
||||
markerbg_points = (
|
||||
evt.x-spacer - spacbg if 'ver' in self.cnf['orient'] else self.winfo_width()/2-6,
|
||||
self.winfo_height()/2-6 if 'ver' in self.cnf['orient'] else evt.y-spacer-spacbg,
|
||||
evt.x-spacer +spacbg if 'ver' in self.cnf['orient'] else self.winfo_width()/2+7,
|
||||
self.winfo_height()/2+7 if 'ver' in self.cnf['orient'] else evt.y-spacer+spacbg,
|
||||
6)
|
||||
|
||||
info_points = (
|
||||
(evt.x-spacer if 'ver' in self.cnf['orient'] else self.winfo_width()/2,
|
||||
self.winfo_height()/2 if 'ver' in self.cnf['orient'] else evt.y-spacer))
|
||||
|
||||
markerbg_cnf = {'fill': self._marker_color, 'tag': 'markerbg'}
|
||||
info_cnf = {'angle': 0 if 'ver' in self.cnf['orient'] else 90,
|
||||
'text': text, 'font': Font(size=10), 'tag': "info", 'fill': hexcode}
|
||||
|
||||
self._create_items('create', safe_create=True, avoid=('gradient', 'borderline'),
|
||||
info_points=info_points, markerbg_points=markerbg_points,
|
||||
info_cnf=info_cnf, markerbg_cnf=markerbg_cnf)
|
||||
|
||||
return True
|
||||
|
||||
def _set_mousewheel(self, evt=None):
|
||||
"""Internal function.\n
|
||||
Sets mousewheel scrolling."""
|
||||
|
||||
def on_mousewheel(evt=None):
|
||||
"Internal function."
|
||||
ver_cond = self._xy < self.winfo_width() \
|
||||
and self['orient'] == 'vertical'
|
||||
hor_cond = self._xy < self.winfo_height() \
|
||||
and self['orient'] == 'horizontal'
|
||||
if tkb.delta(evt) <= -1 and (ver_cond or hor_cond):
|
||||
self._xy += 1
|
||||
if not self._move_marker(evt, mw=self._xy):
|
||||
self._xy -= 1
|
||||
if tkb.delta(evt) >= 1 and self._xy > 1:
|
||||
self._xy -= 1
|
||||
if not self._move_marker(evt, mw=self._xy):
|
||||
self._xy += 1
|
||||
|
||||
if self.cnf.get('mousewheel'):
|
||||
tkb._bind(self, className='mousewheel',
|
||||
sequence='<MouseWheel>', func=on_mousewheel)
|
||||
tkb._bind(self, className='mousewheel_x11',
|
||||
sequence='<Button-4>', func=on_mousewheel)
|
||||
tkb._bind(self, className='mousewheel_x11',
|
||||
sequence='<Button-5>', func=on_mousewheel)
|
||||
else:
|
||||
tkb._bind(self, className='mousewheel',
|
||||
sequence='<MouseWheel>')
|
||||
tkb._bind(self, className='mousewheel_x11',
|
||||
sequence='<Button-4>')
|
||||
tkb._bind(self, className='mousewheel_x11',
|
||||
sequence='<Button-5>')
|
||||
|
||||
def _configure(self, cmd, cnf=None, kw=None):
|
||||
"""Internal function."""
|
||||
kw1 = _tk._cnfmerge((cnf, kw))
|
||||
self.cnf.update(
|
||||
{k: kw.pop(k, None) for k in kw1.copy() if k in self._features})
|
||||
self.cnf = {k: v for k, v in self.cnf.copy().items() if v is not None}
|
||||
_return = tkb._Canvas._configure(self, cmd, None, kw1)
|
||||
if _tk._cnfmerge((cnf, kw)).get('gradient'):
|
||||
self._create_items('create', safe_create=True,
|
||||
avoid=('borderline', 'marker',
|
||||
'markerbg', 'info'))
|
||||
self._set_mousewheel()
|
||||
if _return and isinstance(_return, dict):
|
||||
_return.update(self.cnf)
|
||||
return _return
|
||||
|
||||
def cget(self, key):
|
||||
"""Return the resource value for a KEY given as string."""
|
||||
if key in self.cnf.keys():
|
||||
return self.cnf.get(key)
|
||||
return tkb._Canvas.cget(self, key)
|
||||
__getitem__ = cget
|
||||
|
||||
def keys(self):
|
||||
"""Return a list of all resource names of this widget."""
|
||||
return list(sorted(self.config() + self._features))
|
||||
|
||||
def destroy(self):
|
||||
"""Destroy this widget."""
|
||||
self.after_cancel(getattr(self, '_remove_id', ' '))
|
||||
return tkb._Canvas.destroy(self)
|
||||
|
||||
|
||||
# ------------------------------------ #
|
||||
# Testing and demo #
|
||||
# ------------------------------------ #
|
||||
|
||||
def demo_colorscale():
|
||||
from tkmacosx.variables import ColorVar
|
||||
|
||||
root = _tk.Tk()
|
||||
root.title("Tkinter Color Bar")
|
||||
var = ColorVar()
|
||||
_tk.Label(root, text="I am a Label, Hello! :-)",
|
||||
bg=var).pack(padx=10, pady=10)
|
||||
Colorscale(root, value='hex', variable=var, mousewheel=1,
|
||||
gradient=('pink', 'purple')).pack(padx=10, pady=10)
|
||||
Colorscale(root, value='hex', variable=var, mousewheel=1,
|
||||
gradient=('green', 'yellow')).pack(padx=10, pady=10)
|
||||
Colorscale(root, value='hex', variable=var, mousewheel=1,
|
||||
gradient=('purple', 'cyan')).pack(padx=10, pady=10)
|
||||
Colorscale(root, value='hex', variable=var, mousewheel=1,
|
||||
gradient=('white', '#89ABE3')).pack(padx=10, pady=10)
|
||||
Colorscale(root, value='hex', variable=var, mousewheel=1,
|
||||
gradient=('#5F4B8B', '#E69A8D'), orient='horizontal'
|
||||
).pack(padx=10, pady=10, side='left')
|
||||
Colorscale(root, value='hex', variable=var, mousewheel=1,
|
||||
gradient=('#990011', '#FCF6F5'), orient='horizontal'
|
||||
).pack(padx=60, pady=10, side='left')
|
||||
Colorscale(root, value='hex', variable=var, mousewheel=1,
|
||||
orient='horizontal').pack(padx=10, pady=10, side='left')
|
||||
root.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo_colorscale()
|
||||
366
tkmacosx/variables.py
Normal file
366
tkmacosx/variables.py
Normal file
@@ -0,0 +1,366 @@
|
||||
# Copyright 2020 Saad Mairaj
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import re
|
||||
import sys
|
||||
import ast
|
||||
import pickle as pkl
|
||||
|
||||
if sys.version_info.major == 2:
|
||||
import Tkinter as _tk
|
||||
elif sys.version_info.major == 3:
|
||||
import tkinter as _tk
|
||||
|
||||
|
||||
# Modified Misc._options(...) to make ColorVar work with tkinter
|
||||
# {(self, option, tag): (var, cbname)}
|
||||
_all_traces_colorvar = {}
|
||||
|
||||
|
||||
def _colorvar_patch_cget(fn):
|
||||
"""Internal function."""
|
||||
def _patch(self, key):
|
||||
"""Return the resource value for a KEY given as string."""
|
||||
if (self, key) in _all_traces_colorvar:
|
||||
return _all_traces_colorvar[(self, key)][0]
|
||||
return fn(self, key)
|
||||
return _patch
|
||||
|
||||
|
||||
def _colorvar_patch_destroy(fn):
|
||||
"""Internal function.\n
|
||||
Deletes the traces if any when widget is destroy."""
|
||||
|
||||
def _patch(self):
|
||||
"""Interanl function."""
|
||||
if self._tclCommands is not None:
|
||||
# Deletes the widget from the _all_traces_colorvar
|
||||
# and deletes the traces too.
|
||||
for key, value in dict(_all_traces_colorvar).items():
|
||||
if self == key[0]:
|
||||
var, cbname = value
|
||||
try: var.trace_vdelete('w', cbname)
|
||||
except: pass
|
||||
_all_traces_colorvar.pop(key)
|
||||
return fn(self)
|
||||
return _patch
|
||||
|
||||
|
||||
def _colorvar_patch_configure(fn):
|
||||
"""Internal function.\n
|
||||
Patch for Canvas items to support ColorVar functionality."""
|
||||
|
||||
def _patch(self, cmd, cnf, kw):
|
||||
"""Internal function."""
|
||||
|
||||
cnf = _tk._cnfmerge((cnf, kw))
|
||||
cnf_copy = dict(cnf)
|
||||
# -------------------- Added the below block --------------------
|
||||
# Add the resources to the list to have ColorVar functionality.
|
||||
if isinstance(cmd, tuple) and isinstance(self, _tk.Canvas):
|
||||
tags = self.find('withtag', cmd[1])
|
||||
for tag in tags:
|
||||
for i in ('activefill', 'activeoutline', 'disabledfill',
|
||||
'disabledoutline', 'fill', 'outline', 'background',
|
||||
'activebackground', 'activeforeground',
|
||||
'disabledbackground', 'disabledforeground',
|
||||
'foreground'):
|
||||
if isinstance(cnf_copy.get(i), _tk.Variable):
|
||||
var = cnf_copy[i]
|
||||
cbname = var.trace_variable('w', lambda a, b, c,
|
||||
cls=self, opt=i,
|
||||
tagId=tag, var=var:
|
||||
cls._configure(('itemconfigure',tagId),
|
||||
{opt: var.get()}, None))
|
||||
if (self, (i, tag)) in _all_traces_colorvar:
|
||||
v, cb = _all_traces_colorvar.get((self, (i, tag)))
|
||||
v.trace_vdelete('w', cb)
|
||||
_all_traces_colorvar[(self, (i, tag))] = (var, cbname)
|
||||
else:
|
||||
_all_traces_colorvar[(self, (i, tag))] = (var, cbname)
|
||||
cnf[i] = var.get()
|
||||
return fn(self, cmd, cnf, None)
|
||||
return _patch
|
||||
|
||||
|
||||
def _colorvar_patch_options(fn):
|
||||
"""Internal function.\n
|
||||
Patch for ColorVar to work with tkinter widgets."""
|
||||
|
||||
def _patch(self, cnf, kw=None):
|
||||
"""Internal function."""
|
||||
if kw:
|
||||
cnf = _tk._cnfmerge((cnf, kw))
|
||||
else:
|
||||
cnf = _tk._cnfmerge(cnf)
|
||||
|
||||
for i in ('fg', 'foreground', 'bg', 'background',
|
||||
'activebackground', 'activeforeground', 'disabledforeground',
|
||||
'highlightbackground', 'highlightcolor', 'selectforeground',
|
||||
'readonlybackground', 'selectbackground', 'insertbackground',
|
||||
'disabledbackground'):
|
||||
if isinstance(cnf.get(i), _tk.Variable):
|
||||
var = cnf[i]
|
||||
cbname = var.trace_variable('w', lambda a, b, c, i=i, var=var,
|
||||
cls=self: cls.config({i: var.get()}))
|
||||
if (self, i) in _all_traces_colorvar:
|
||||
v, cb = _all_traces_colorvar.get((self, i))
|
||||
v.trace_vdelete('w', cb)
|
||||
_all_traces_colorvar[(self, i)] = (var, cbname)
|
||||
else:
|
||||
_all_traces_colorvar[(self, i)] = (var, cbname)
|
||||
cnf[i] = var.get()
|
||||
# [issue-1] once a ColorVar is assigned, it cannot be removed
|
||||
# untill the widget is destroyed or give another ColorVar
|
||||
# [issue-1] (trial) the below doesn't work as excepted.
|
||||
# elif (self, i) in _all_traces_colorvar:
|
||||
# if self[i] != _all_traces_colorvar[(self, i)][0].get():
|
||||
# print( self, i, self[i])
|
||||
# v, cb = _all_traces_colorvar.pop((self, i))
|
||||
# v.trace_vdelete('w', cb)
|
||||
return fn(self, cnf, None)
|
||||
return _patch
|
||||
|
||||
|
||||
def _create(self, itemType, args, kw): # Args: (val, val, ..., cnf={})
|
||||
"""Internal function."""
|
||||
args = _tk._flatten(args)
|
||||
cnf = args[-1]
|
||||
if isinstance(cnf, (dict, tuple)):
|
||||
args = args[:-1]
|
||||
else:
|
||||
cnf = {}
|
||||
|
||||
# -------------------- Added the below block --------------------
|
||||
# Add the resources to the list to have ColorVar functionality.
|
||||
ckw = _tk._cnfmerge((cnf, kw))
|
||||
var = None
|
||||
for i in ('activefill', 'activeoutline', 'disabledfill',
|
||||
'disabledoutline', 'fill', 'outline', 'background',
|
||||
'activebackground', 'activeforeground', 'foreground',
|
||||
'disabledbackground', 'disabledforeground'):
|
||||
if isinstance(ckw.get(i), _tk.Variable):
|
||||
var = ckw[i]
|
||||
_all_traces_colorvar[(self, (i, None))] = (var, None)
|
||||
if isinstance(cnf, dict) and i in cnf:
|
||||
cnf[i] = var.get()
|
||||
elif i in kw:
|
||||
kw[i] = var.get()
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
tagId = self.tk.getint(self.tk.call(
|
||||
self._w, 'create', itemType,
|
||||
*(args + self._options(cnf, kw))))
|
||||
|
||||
for key, value in dict(_all_traces_colorvar).items():
|
||||
if isinstance(key[1], (tuple, list)):
|
||||
wid, (opt, tag_id) = key
|
||||
var, cbname = value
|
||||
if tag_id is None and cbname is None:
|
||||
cbname = var.trace_variable('w', lambda a, b, c,
|
||||
cls=self, opt=opt,
|
||||
tagId=tagId, var=var:
|
||||
cls._configure(('itemconfigure',tagId),
|
||||
{opt: var.get()}, None))
|
||||
_all_traces_colorvar[(self, (opt, tagId))] = (var, cbname)
|
||||
_all_traces_colorvar.pop((self, (opt, None)))
|
||||
return tagId
|
||||
|
||||
|
||||
_tk.Misc.destroy = _colorvar_patch_destroy(_tk.Misc.destroy)
|
||||
_tk.Misc._options = _colorvar_patch_options(_tk.Misc._options)
|
||||
_tk.Misc._configure = _colorvar_patch_configure(_tk.Misc._configure)
|
||||
_tk.Canvas._create = _create
|
||||
|
||||
|
||||
class ColorVar(_tk.Variable):
|
||||
"""Value holder for HEX color. Default is white"""
|
||||
|
||||
_default = "white"
|
||||
_rgbstring = re.compile(r'#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$')
|
||||
|
||||
def __init__(self, master=None, value=None, name=None):
|
||||
"""Construct a color variable. (bg, fg, ..)
|
||||
|
||||
MASTER can be given as master widget.
|
||||
VALUE is an optional value (defaults to "")
|
||||
NAME is an optional Tcl name (defaults to PY_VARnum).
|
||||
|
||||
If NAME matches an existing variable and VALUE is omitted
|
||||
then the existing value is retained.
|
||||
"""
|
||||
_tk.Variable.__init__(self, master, value, name)
|
||||
|
||||
def set(self, value=''):
|
||||
"""Set the variable to VALUE."""
|
||||
if value.startswith('#'):
|
||||
if not bool(self._rgbstring.match(value)):
|
||||
raise ValueError('"{}" is not a valid HEX.'.format(value))
|
||||
elif isinstance(value, str):
|
||||
try:
|
||||
r, g, b = self._root.winfo_rgb(value)
|
||||
c = (r/257, g/257, b/257)
|
||||
value = '#%02x%02x%02x' % (int(c[0]), int(c[1]), int(c[2]))
|
||||
except:
|
||||
raise ValueError(
|
||||
'Could not find right HEX for "{}".'.format(value))
|
||||
return self._tk.globalsetvar(self._name, value)
|
||||
initialize = set
|
||||
|
||||
def get(self):
|
||||
"""Return value of variable color."""
|
||||
value = self._tk.globalgetvar(self._name)
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
return str(value)
|
||||
|
||||
|
||||
class DictVar(_tk.Variable):
|
||||
"""
|
||||
#### Value holder for Dictionaries.
|
||||
Get a specific value by getting the key from this \
|
||||
`get(self, key=None, d=None)` method if exists in the dictionary. \n
|
||||
if `key=None` it will return the complete dictionary.
|
||||
"""
|
||||
_default = {}
|
||||
|
||||
def __init__(self, master=None, value=None, name=None):
|
||||
"""Construct a string variable.
|
||||
|
||||
MASTER can be given as master widget.
|
||||
VALUE is an optional value (defaults to {})
|
||||
NAME is an optional Tcl name (defaults to PY_VARnum).
|
||||
|
||||
If NAME matches an existing variable and VALUE is omitted
|
||||
then the existing value is retained.
|
||||
"""
|
||||
_tk.Variable.__init__(self, master, value, name)
|
||||
|
||||
def get(self, key=None, d=None):
|
||||
"""Return value of variable as string."""
|
||||
value = self._tk.globalgetvar(self._name)
|
||||
if not isinstance(value, dict):
|
||||
value = ast.literal_eval(value)
|
||||
if key:
|
||||
return value.get(key, d)
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def SaveVar(var, master=None, value=None, name=None, filename='data.pkl'):
|
||||
"""Save tkinter variable data in a pickle file and load the
|
||||
same value when the program is executed next time.
|
||||
|
||||
#### If the content of the file changes, it might not load correct values \
|
||||
to the assigned variables. To avoid this issue use `name` to \
|
||||
refer to the exact assigned values later.
|
||||
|
||||
### Args:
|
||||
- `var`: Give the `tkinter.Variable` class like (`tk.StringVar`, `tk.IntVar`).
|
||||
- `master`: Parent widget.
|
||||
- `value`: Set value.
|
||||
- `name`: Set a name to group variables or to refer to assigned value when loaded.
|
||||
- `filename`: Set the name of the save file. (To make the file invisible in the \
|
||||
directory start the name of the file with "." like ".cache-savevar")
|
||||
|
||||
### Return:
|
||||
- returns the tk.Variable instance passed to `var` argument.
|
||||
|
||||
### Example:
|
||||
root = tk.Tk()
|
||||
var1 = SaveVar(tk.StringVar,'Enter Username','Var1','.cache-savevar')
|
||||
var2 = SaveVar(tk.StringVar,'Enter Password','Var2','.cache-savevar')
|
||||
tk.Entry(root,textvariable=var1).pack()
|
||||
tk.Entry(root,textvariable=var2).pack()
|
||||
root.mainloop()"""
|
||||
|
||||
def update_val(*args):
|
||||
"""Internal function for updating the value for variable"""
|
||||
try: # try/except , if the file doesn't exists.
|
||||
open1 = open(filename, 'rb')
|
||||
tmpdict = pkl.load(open1) # load saved dictionary data.
|
||||
# Block of code to check for the right value.
|
||||
if tmpdict.get(str(var)):
|
||||
old, default = tmpdict.get(str(var))
|
||||
new = var.get()
|
||||
if new != default:
|
||||
var.set(new)
|
||||
elif new == default and not startup[0]:
|
||||
var.set(default)
|
||||
else:
|
||||
var.set(old)
|
||||
tmpdict.update({str(var): (var.get(), defaultval)})
|
||||
open1.close()
|
||||
except Exception as e:
|
||||
tmpdict = {}
|
||||
tmpdict[str(var)] = (var.get(), defaultval)
|
||||
|
||||
open2 = open(filename, 'wb')
|
||||
pkl.dump(tmpdict, open2)
|
||||
startup[0] = False
|
||||
open2.close()
|
||||
|
||||
startup = [True]
|
||||
if not(filename.endswith('.pickle') or filename.endswith('.pkl')) \
|
||||
and not filename.startswith('.'):
|
||||
filename = filename+'.pkl'
|
||||
var = var(master=master, value=value, name=name)
|
||||
defaultval = var.get() # get a default value of the variable
|
||||
update_val()
|
||||
for mode, cbname in (var.trace_vinfo()):
|
||||
if mode[0] == 'w' and update_val.__name__ in cbname:
|
||||
try:
|
||||
var.trace_vdelete('w', cbname)
|
||||
except:
|
||||
pass
|
||||
res = var.trace_variable('w', update_val)
|
||||
return var
|
||||
|
||||
|
||||
# ------------------ Testing ------------------
|
||||
|
||||
def demo_colorvar():
|
||||
import tkmacosx.colors as colors
|
||||
root = _tk.Tk()
|
||||
root.geometry('100x100')
|
||||
color = ColorVar()
|
||||
color_list = list(colors.OrderedHex)
|
||||
L = _tk.Label(root, textvariable=color, bg=color)
|
||||
L.place(relx=0.5, rely=0.5, anchor='center')
|
||||
|
||||
def change_color(c=0):
|
||||
if root.winfo_exists():
|
||||
if c >= len(color_list):
|
||||
c = 0
|
||||
color.set(color_list[c])
|
||||
root.after(100, change_color, c+1)
|
||||
|
||||
change_color()
|
||||
root.mainloop()
|
||||
|
||||
|
||||
def demo_savevar():
|
||||
root = _tk.Tk()
|
||||
var1 = SaveVar(_tk.StringVar, root, 'Enter Username',
|
||||
'Var1', '.cache-savevar')
|
||||
var2 = SaveVar(_tk.StringVar, root, 'Enter Password',
|
||||
'Var2', '.cache-savevar')
|
||||
_tk.Entry(root, textvariable=var1).pack()
|
||||
_tk.Entry(root, textvariable=var2).pack()
|
||||
root.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo_colorvar()
|
||||
demo_savevar()
|
||||
251
tkmacosx/widget.py
Normal file
251
tkmacosx/widget.py
Normal file
@@ -0,0 +1,251 @@
|
||||
# Copyright 2020 Saad Mairaj
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
if sys.version_info.major == 2:
|
||||
import Tkinter as _TK
|
||||
elif sys.version_info.major == 3:
|
||||
import tkinter as _TK
|
||||
import tkmacosx.basewidget as tkb
|
||||
|
||||
|
||||
class SFrame(tkb.SFrameBase):
|
||||
"""### Scrollable Frame ButtonBase.
|
||||
(Only supports vertical scrolling)
|
||||
|
||||
Sames as tkinter Frame. These are some extra resources.
|
||||
- `scrollbarwidth`: Set the width of scrollbar.
|
||||
- `mousewheel`: Set mousewheel scrolling.
|
||||
- `avoidmousewheel`: Give widgets that also have mousewheel scrolling and is a child of SFrame \
|
||||
this will configure widgets to support their mousewheel scrolling as well. \
|
||||
For eg:- Text widget inside SFrame can have mousewheel scrolling as well as SFrame.
|
||||
|
||||
Scrollbar of SFrame can be configured by calling `scrollbar_configure(**options)`.
|
||||
To access methods of the scrollbar it can be called through the scrollbar instance `self['scrollbar']`.
|
||||
|
||||
### How to use?
|
||||
Use it like a normal frame.
|
||||
|
||||
### Example:
|
||||
|
||||
root = Tk()
|
||||
frame = SFrame(root, bg='pink')
|
||||
frame.pack()
|
||||
|
||||
for i in range(100):
|
||||
Button(frame, text='Button %s'%i).pack()
|
||||
|
||||
root.mainloop()
|
||||
"""
|
||||
|
||||
def __init__(self, master=None, cnf={}, **kw):
|
||||
tkb.SFrameBase.__init__(self, master=master, cnf=cnf, **kw)
|
||||
# Extra functions
|
||||
self.scrollbar_configure = self['scrollbar'].configure
|
||||
|
||||
|
||||
class Button(tkb.ButtonBase):
|
||||
"""Button for macos, supports almost all the features of tkinter button,
|
||||
- Looks very similar to ttk Button.
|
||||
- There are few extra features as compared to default Tkinter Button:
|
||||
- To check the list of all the resources. To get an overview about
|
||||
the allowed keyword arguments call the method `keys`.
|
||||
print(Button().keys())
|
||||
|
||||
### Examples:
|
||||
import tkinter as tk
|
||||
import tkmacosx as tkm
|
||||
import tkinter.ttk as ttk
|
||||
|
||||
root = tk.Tk()
|
||||
root.geometry('200x200')
|
||||
tkm.Button(root, text='Mac OSX', bg='lightblue', fg='yellow').pack()
|
||||
tk.Button(root, text='Mac OSX', bg='lightblue', fg='yellow').pack()
|
||||
ttk.Button(root, text='Mac OSX').pack()
|
||||
root.mainloop()
|
||||
|
||||
### Get a cool gradient effect in activebackground color.
|
||||
import tkinter as tk
|
||||
import tkmacosx as tkm
|
||||
|
||||
root = tk.Tk()
|
||||
root.geometry('200x200')
|
||||
tkm.Button(root, text='Press Me!!', activebackground=('pink','blue') ).pack()
|
||||
tkm.Button(root, text='Press Me!!', activebackground=('yellow','green') ).pack()
|
||||
tkm.Button(root, text='Press Me!!', activebackground=('red','blue') ).pack()
|
||||
root.mainloop()"""
|
||||
|
||||
def __init__(self, master=None, cnf={}, **kw):
|
||||
tkb.ButtonBase.__init__(self, 'normal', master, cnf, **kw)
|
||||
|
||||
def invoke(self):
|
||||
"""Invoke the command associated with the button.
|
||||
|
||||
The return value is the return value from the command,
|
||||
or an empty string if there is no command associated with
|
||||
the button. This command is ignored if the button's state
|
||||
is disabled.
|
||||
"""
|
||||
if self['state'] not in ('disable', 'disabled'):
|
||||
return self.cnf['command']() if self.cnf.get('command') else None
|
||||
|
||||
|
||||
class CircleButton(tkb.ButtonBase):
|
||||
"""
|
||||
#### Beta-Disclaimer: Please note that this is a BETA version of this widget. \
|
||||
Issues at https://github.com/Saadmairaj/tkmacosx/issues/new/choose \
|
||||
or email me at saadmairaj@yahoo.in.
|
||||
|
||||
Circle shaped Button supports almost all options of a tkinter button
|
||||
and have some few more useful options such as 'activebackground', overbackground',
|
||||
'overforeground', 'activeimage', 'activeforeground', 'borderless' and much more.
|
||||
|
||||
To check all the configurable options for CircleButton run `print(CircleButton().keys())`.
|
||||
|
||||
Example:
|
||||
```
|
||||
import tkinter as tk
|
||||
import tkmacosx as tkm
|
||||
|
||||
root = tk.Tk()
|
||||
cb = tkm.CircleButton(root, text='Hello')
|
||||
cb.pack()
|
||||
root.mainloop()
|
||||
```
|
||||
"""
|
||||
def __init__(self, master=None, cnf={}, **kw):
|
||||
tkb.ButtonBase.__init__(self, 'circle', master, cnf, **kw)
|
||||
|
||||
def invoke(self):
|
||||
"""Invoke the command associated with the button.
|
||||
|
||||
The return value is the return value from the command,
|
||||
or an empty string if there is no command associated with
|
||||
the button. This command is ignored if the button's state
|
||||
is disabled.
|
||||
"""
|
||||
if self['state'] not in ('disable', 'disabled'):
|
||||
return self.cnf['command']() if self.cnf.get('command') else None
|
||||
|
||||
|
||||
class Marquee(tkb.MarqueeBase):
|
||||
"""Use `Marquee` for creating scrolling text which moves from
|
||||
right to left only if the text does not fit completely.
|
||||
|
||||
### Args:
|
||||
- `text`: Give a string to display.
|
||||
- `font`: Font of the text.
|
||||
- `fg`: Set foreground color of the text.
|
||||
- `fps`: Set fps(frames per seconds).
|
||||
- `left_margin`: Set left margin to make text move further to right before reset.
|
||||
- `initial_delay`: Delay before text start to move.
|
||||
- `end_delay`: Delay before text reset.
|
||||
- `smoothness`: Set the smoothness of the animation.
|
||||
|
||||
### Example:
|
||||
root=tk.Tk()
|
||||
marquee = Marquee(root,
|
||||
text='This text will move from right to left if does not fit the window.')
|
||||
marquee.pack()
|
||||
root.mainloop()
|
||||
|
||||
### To move the text when cursor is over the text then follow the below example.
|
||||
|
||||
text = "Please hover over the text to move it. \
|
||||
This text will move only if the cursor hovers over the text widget".
|
||||
root = tk.Tk()
|
||||
m = tkm.Marquee(root, bg='lightgreen', text=text)
|
||||
m.pack()
|
||||
m.stop(True)
|
||||
m.bind('<Enter>', lambda _: m.play())
|
||||
m.bind('<Leave>', lambda _: m.stop())
|
||||
root.mainloop()
|
||||
"""
|
||||
|
||||
def reset(self):
|
||||
"""Reset the text postion."""
|
||||
self._reset(True)
|
||||
self._stop_state = False
|
||||
|
||||
def stop(self, reset=False):
|
||||
"""Stop the text movement."""
|
||||
if reset:
|
||||
self.reset()
|
||||
self._stop_state = True
|
||||
self.after_cancel(self.after_id)
|
||||
self.after_id = ' '
|
||||
|
||||
def play(self, reset=False):
|
||||
"""Play/continue the text movement."""
|
||||
if not self._stop_state and not reset:
|
||||
return
|
||||
self._stop_state = False
|
||||
if reset:
|
||||
self.reset()
|
||||
self._animate()
|
||||
|
||||
|
||||
# ------------------ Testing ------------------
|
||||
|
||||
|
||||
def demo_button():
|
||||
if sys.version_info.major == 2:
|
||||
import ttk
|
||||
elif sys.version_info.major == 3:
|
||||
from tkinter import ttk
|
||||
root = _TK.Tk()
|
||||
root.title('Mac OSX Button Demo')
|
||||
page_color = '#FFFFC6'
|
||||
root.geometry('300x400')
|
||||
root['bg'] = page_color
|
||||
_TK.Label(root, text='Can you tell the difference?', font=(
|
||||
'', 16, 'bold'), bg=page_color).pack(pady=(20, 5))
|
||||
Button(root, text='Press Me', disabledbackground='red').pack(pady=(0, 5))
|
||||
ttk.Button(root, text="Press Me").pack()
|
||||
_TK.Label(root, text='Change Background Color', font=(
|
||||
'', 16, 'bold'), bg=page_color).pack(pady=(20, 5))
|
||||
Button(root, text='Press Me', bg='pink',
|
||||
activebackground=('pink', 'blue')).pack()
|
||||
_TK.Label(root, text='Blend In', font=('', 16, 'bold'),
|
||||
bg=page_color).pack(pady=(20, 5))
|
||||
Button(root, text='Press Me', bg='yellow', activebackground=(
|
||||
'orange', 'lime'), borderless=1).pack()
|
||||
_TK.Label(root, text='Change bordercolor', font=(
|
||||
'', 16, 'bold'), bg=page_color).pack(pady=(20, 5))
|
||||
Button(root, text='Press Me', bg='red', fg='white',
|
||||
activebackground=('red', 'blue'), bordercolor='blue').pack()
|
||||
root.mainloop()
|
||||
|
||||
|
||||
def demo_sframe():
|
||||
root = _TK.Tk()
|
||||
frame = SFrame(root, bg='pink')
|
||||
frame.pack()
|
||||
for i in range(50):
|
||||
Button(frame, text='Button %s' % i, borderless=1).pack()
|
||||
root.mainloop()
|
||||
|
||||
|
||||
def demo_marquee():
|
||||
root = _TK.Tk()
|
||||
marquee = Marquee(
|
||||
root, text='This text will move from right to left if does not fit the window.')
|
||||
marquee.pack()
|
||||
root.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo_sframe()
|
||||
demo_button()
|
||||
demo_marquee()
|
||||
Reference in New Issue
Block a user