VIStk 0.3.12.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- VIStk/Form.zip +0 -0
- VIStk/Objects/_Root.py +9 -0
- VIStk/Objects/_SubRoot.py +18 -0
- VIStk/Objects/_Window.py +24 -0
- VIStk/Objects/_WindowGeometry.py +133 -0
- VIStk/Objects/__init__.py +6 -0
- VIStk/Structures/VINFO.py +139 -0
- VIStk/Structures/__init__.py +6 -0
- VIStk/Structures/project.py +92 -0
- VIStk/Structures/release.py +165 -0
- VIStk/Structures/screen.py +153 -0
- VIStk/Templates/Widgets/button.txt +0 -0
- VIStk/Templates/Widgets/combobox.txt +0 -0
- VIStk/Templates/Widgets/entry.txt +0 -0
- VIStk/Templates/Widgets/label.txt +0 -0
- VIStk/Templates/collect.txt +5 -0
- VIStk/Templates/f_element.txt +25 -0
- VIStk/Templates/screen.txt +43 -0
- VIStk/Templates/spec.txt +33 -0
- VIStk/Templates/version.txt +30 -0
- VIStk/VIS.py +54 -0
- VIStk/Widgets/_MenuItem.py +36 -0
- VIStk/Widgets/_MenuWindow.py +22 -0
- VIStk/Widgets/_QuestionWindow.py +89 -0
- VIStk/Widgets/_VISMenu.py +51 -0
- VIStk/Widgets/_WarningWindow.py +8 -0
- VIStk/Widgets/__init__.py +7 -0
- VIStk/__init__.py +0 -0
- VIStk/fUtil.py +90 -0
- vistk-0.3.12.14.dist-info/METADATA +50 -0
- vistk-0.3.12.14.dist-info/RECORD +35 -0
- vistk-0.3.12.14.dist-info/WHEEL +5 -0
- vistk-0.3.12.14.dist-info/entry_points.txt +2 -0
- vistk-0.3.12.14.dist-info/licenses/LICENSE +24 -0
- vistk-0.3.12.14.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import shutil
|
|
4
|
+
import re
|
|
5
|
+
import glob
|
|
6
|
+
from VIStk.Structures.VINFO import *
|
|
7
|
+
import tkinter as TK
|
|
8
|
+
|
|
9
|
+
class Screen(VINFO):
|
|
10
|
+
"""A VIS screen object
|
|
11
|
+
"""
|
|
12
|
+
def __init__(self,name:str,script:str,release:bool=False,icon:str=None,exists:bool=True,desc:str=None):
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.name=name
|
|
15
|
+
self.script=script
|
|
16
|
+
self.release=release
|
|
17
|
+
self.icon=icon
|
|
18
|
+
self.path = self.p_screens+"/"+self.name
|
|
19
|
+
self.m_path = self.p_modules+"/"+self.name
|
|
20
|
+
|
|
21
|
+
if not exists:
|
|
22
|
+
with open(self.p_sinfo,"r") as f:
|
|
23
|
+
info = json.load(f)
|
|
24
|
+
|
|
25
|
+
info[self.title]["Screens"][self.name] = {"script":script,"release":release}
|
|
26
|
+
if not icon == None:
|
|
27
|
+
info[self.title]["Screens"][self.name]["icon"] = icon
|
|
28
|
+
|
|
29
|
+
if not desc == None:
|
|
30
|
+
info[self.title]["Screens"][self.name]["desc"] = desc
|
|
31
|
+
else:
|
|
32
|
+
info[self.title]["Screens"][self.name]["desc"] = "A VIS Created Executable"
|
|
33
|
+
|
|
34
|
+
info[self.title]["Screens"][self.name]["version"] = "1.0.0"#always making first major version of screen
|
|
35
|
+
|
|
36
|
+
info[self.title]["Screens"][self.name]["current"] = None#always making first major version of screen
|
|
37
|
+
|
|
38
|
+
with open(self.p_sinfo,"w") as f:
|
|
39
|
+
json.dump(info,f,indent=4)
|
|
40
|
+
|
|
41
|
+
shutil.copyfile(self.p_templates+"/screen.txt",self.p_project+"/"+script)
|
|
42
|
+
os.mkdir(self.p_screens+"/"+self.name)
|
|
43
|
+
os.mkdir(self.p_modules+"/"+self.name)
|
|
44
|
+
|
|
45
|
+
with open(self.p_sinfo,"r") as f:
|
|
46
|
+
info = json.load(f)
|
|
47
|
+
self.desc = info[self.title]["Screens"][self.name]["desc"]
|
|
48
|
+
self.s_version = info[self.title]["Screens"][self.name]["version"]
|
|
49
|
+
self.current = info[self.title]["Screens"][self.name]["current"]
|
|
50
|
+
|
|
51
|
+
def addElement(self,element:str) -> int:
|
|
52
|
+
if validName(element):
|
|
53
|
+
if not os.path.exists(self.path+"/f_"+element+".py"):
|
|
54
|
+
shutil.copyfile(self.p_templates+"/f_element.txt",self.path+"/f_"+element+".py")
|
|
55
|
+
print(f"Created element f_{element}.py in {self.path}")
|
|
56
|
+
self.patch(element)
|
|
57
|
+
if not os.path.exists(self.m_path+"/m_"+element+".py"):
|
|
58
|
+
with open(self.m_path+"/m_"+element+".py", "w"): pass
|
|
59
|
+
print(f"Created module m_{element}.py in {self.m_path}")
|
|
60
|
+
return 1
|
|
61
|
+
else:
|
|
62
|
+
return 0
|
|
63
|
+
|
|
64
|
+
def addMenu(self,menu:str) -> int:
|
|
65
|
+
pass #will be command line menu creation tool
|
|
66
|
+
|
|
67
|
+
def patch(self,element:str) -> int:
|
|
68
|
+
"""Patches up the template after its copied
|
|
69
|
+
"""
|
|
70
|
+
if os.path.exists(self.path+"/f_"+element+".py"):
|
|
71
|
+
with open(self.path+"/f_"+element+".py","r") as f:
|
|
72
|
+
text = f.read()
|
|
73
|
+
text = text.replace("<frame>","f_"+element)
|
|
74
|
+
with open(self.path+"/f_"+element+".py","w") as f:
|
|
75
|
+
f.write(text)
|
|
76
|
+
print(f"patched f_{element}.py")
|
|
77
|
+
return 1
|
|
78
|
+
else:
|
|
79
|
+
print(f"Could not patch, element does not exist.")
|
|
80
|
+
return 0
|
|
81
|
+
|
|
82
|
+
def stitch(self) -> int:
|
|
83
|
+
"""Connects screen elements to a screen
|
|
84
|
+
"""
|
|
85
|
+
with open(self.p_project+"/"+self.script,"r") as f: text = f.read()
|
|
86
|
+
stitched = []
|
|
87
|
+
#Elements
|
|
88
|
+
pattern = r"#Screen Elements.*#Screen Grid"
|
|
89
|
+
|
|
90
|
+
elements = glob.glob(self.path+'/f_*')#get all elements
|
|
91
|
+
for i in range(0,len(elements),1):#iterate into module format
|
|
92
|
+
elements[i] = elements[i].replace("\\","/")
|
|
93
|
+
elements[i] = elements[i].replace(self.path+"/","Screens."+self.name+".")[:-3]
|
|
94
|
+
stitched.append(elements[i])
|
|
95
|
+
#combine and change text
|
|
96
|
+
elements = "from " + " import *\nfrom ".join(elements) + " import *\n"
|
|
97
|
+
text = re.sub(pattern, "#Screen Elements\n" + elements + "\n#Screen Grid", text, flags=re.DOTALL)
|
|
98
|
+
|
|
99
|
+
#Modules
|
|
100
|
+
pattern = r"#Screen Modules.*#Handle Arguments"
|
|
101
|
+
|
|
102
|
+
modules = glob.glob(self.m_path+'/m_*')#get all modules
|
|
103
|
+
for i in range(0,len(modules),1):#iterate into module format
|
|
104
|
+
modules[i] = modules[i].replace("\\","/")
|
|
105
|
+
modules[i] = modules[i].replace(self.m_path+"/","modules."+self.name+".")[:-3]
|
|
106
|
+
stitched.append(modules[i])
|
|
107
|
+
#combine and change text
|
|
108
|
+
modules = "from " + " import *\nfrom ".join(modules) + " import *\n"
|
|
109
|
+
text = re.sub(pattern, "#Screen Modules\n" + modules + "\n#Handle Arguments", text, flags=re.DOTALL)
|
|
110
|
+
|
|
111
|
+
#write out
|
|
112
|
+
with open(self.p_project+"/"+self.script,"w") as f:
|
|
113
|
+
f.write(text)
|
|
114
|
+
print("Stitched: ")
|
|
115
|
+
for i in stitched:
|
|
116
|
+
print(f"\t{i} to {self.name}")
|
|
117
|
+
|
|
118
|
+
def syncVersion(self) -> int:
|
|
119
|
+
"""Syncs the version stored in sinfo with the version in memory
|
|
120
|
+
"""
|
|
121
|
+
with open(self.p_sinfo,"r") as f:
|
|
122
|
+
info = json.load(f)
|
|
123
|
+
info[self.title]["Screens"][self.name]["current"] = self.current
|
|
124
|
+
with open(self.p_sinfo,"w") as f:
|
|
125
|
+
json.dump(info,f)
|
|
126
|
+
return 1
|
|
127
|
+
|
|
128
|
+
def crntVersion(self) -> int:
|
|
129
|
+
"""Checks if the version needs to be synced and returns 1 if its synced
|
|
130
|
+
"""
|
|
131
|
+
if not self.s_version == self.current:
|
|
132
|
+
self.current = self.version
|
|
133
|
+
self.syncVersion()
|
|
134
|
+
return 1
|
|
135
|
+
else:
|
|
136
|
+
return 0
|
|
137
|
+
|
|
138
|
+
def unLoad(self,root:TK.Tk) -> int:
|
|
139
|
+
"""Unloads all elements on the screen"""
|
|
140
|
+
for element in root.winfo_children():
|
|
141
|
+
try:
|
|
142
|
+
element.destroy()
|
|
143
|
+
except: pass
|
|
144
|
+
#might fail to delete widgets that get deleted by earlier deletions
|
|
145
|
+
|
|
146
|
+
def load(self) -> int:
|
|
147
|
+
"""Loads a new screen onto the root"""
|
|
148
|
+
#should just need to run the python file now
|
|
149
|
+
#cant do that through subprocess.call when compiling
|
|
150
|
+
#can make python dlls? something like this for each screen
|
|
151
|
+
#then itll be easy to load/unload elements
|
|
152
|
+
#can even have these dlls take a parameter for the root
|
|
153
|
+
#then we can do splitscreen and windowed things if we want
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#Default Imports
|
|
2
|
+
from tkinter import *
|
|
3
|
+
from tkinter import ttk
|
|
4
|
+
from Screens.root import *
|
|
5
|
+
#File Specific Imports
|
|
6
|
+
|
|
7
|
+
#Create Frame
|
|
8
|
+
<frame> = ttk.Frame(root)
|
|
9
|
+
<frame>.grid(column=0,row=0,columnspan=1,rowspan=1,sticky=(N, S, E, W))
|
|
10
|
+
|
|
11
|
+
#Configure Frame Grid
|
|
12
|
+
<frame>.columnconfigure(1,weight=1)
|
|
13
|
+
<frame>.rowconfigure(1,weight=1)
|
|
14
|
+
|
|
15
|
+
#Declare Stringvars
|
|
16
|
+
|
|
17
|
+
#Button Commands and Functions
|
|
18
|
+
|
|
19
|
+
#Navigation
|
|
20
|
+
|
|
21
|
+
#########################
|
|
22
|
+
#####Visual Elements#####
|
|
23
|
+
#########################
|
|
24
|
+
|
|
25
|
+
#Element Descriptions
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#Default Imports
|
|
2
|
+
from tkinter import *
|
|
3
|
+
from tkinter import ttk
|
|
4
|
+
from Screens.root import *
|
|
5
|
+
import sys
|
|
6
|
+
#File Specific Imports
|
|
7
|
+
|
|
8
|
+
#Configure Screen
|
|
9
|
+
w = 1080
|
|
10
|
+
h = 720
|
|
11
|
+
ws = root.winfo_screenwidth()
|
|
12
|
+
hs = root.winfo_screenheight()
|
|
13
|
+
x = (ws/2) - (w/2)
|
|
14
|
+
y = (hs/2) - (h/2)
|
|
15
|
+
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
|
16
|
+
root.title("Placeholder Title")
|
|
17
|
+
root.minsize(1080,720)
|
|
18
|
+
#root.iconbitmap("<project>/Images/Icons/<some_icon>")
|
|
19
|
+
|
|
20
|
+
#Screen Elements
|
|
21
|
+
|
|
22
|
+
#Screen Grid
|
|
23
|
+
root.grid_columnconfigure(0,weight=1)
|
|
24
|
+
root.grid_rowconfigure(0,weight=1)
|
|
25
|
+
|
|
26
|
+
#Screen Modules
|
|
27
|
+
|
|
28
|
+
#Handle Arguments
|
|
29
|
+
|
|
30
|
+
#Define Loop Modules
|
|
31
|
+
def loop():
|
|
32
|
+
#screen modules run here
|
|
33
|
+
1+1
|
|
34
|
+
|
|
35
|
+
#Update Loop
|
|
36
|
+
while True:
|
|
37
|
+
try:
|
|
38
|
+
if root.winfo_exists():
|
|
39
|
+
try:loop()
|
|
40
|
+
except:pass
|
|
41
|
+
root.update()
|
|
42
|
+
except:
|
|
43
|
+
break
|
VIStk/Templates/spec.txt
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
$name$_a = Analysis(
|
|
2
|
+
['../$file$'],
|
|
3
|
+
pathex=[],
|
|
4
|
+
binaries=[],
|
|
5
|
+
datas=[],
|
|
6
|
+
hiddenimports=[],
|
|
7
|
+
hookspath=[],
|
|
8
|
+
hooksconfig={},
|
|
9
|
+
runtime_hooks=[],
|
|
10
|
+
excludes=[],
|
|
11
|
+
noarchive=False,
|
|
12
|
+
optimize=0,)
|
|
13
|
+
$name$_pyz = PYZ($name$_a.pure)
|
|
14
|
+
$name$_exe = EXE(
|
|
15
|
+
$name$_pyz,
|
|
16
|
+
$name$_a.scripts,
|
|
17
|
+
$name$_a.binaries,
|
|
18
|
+
$name$_a.datas,
|
|
19
|
+
[],
|
|
20
|
+
exclude_binaries=True,
|
|
21
|
+
name='$name$',
|
|
22
|
+
debug=False,
|
|
23
|
+
bootloader_ignore_signals=False,
|
|
24
|
+
strip=False,
|
|
25
|
+
upx=True,
|
|
26
|
+
console=False,
|
|
27
|
+
disable_windowed_traceback=False,
|
|
28
|
+
argv_emulation=False,
|
|
29
|
+
target_arch=None,
|
|
30
|
+
codesign_identity=None,
|
|
31
|
+
entitlements_file=None,
|
|
32
|
+
icon=['../Icons/$icon$.ico'],
|
|
33
|
+
version='$meta$')
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# UTF-8
|
|
2
|
+
|
|
3
|
+
VSVersionInfo(
|
|
4
|
+
ffi=FixedFileInfo(
|
|
5
|
+
filevers=($sM$,$sm$,$sp$,0),
|
|
6
|
+
prodvers=($M$,$m$,$p$,0),
|
|
7
|
+
mask=0x3f,
|
|
8
|
+
flags=0x0,
|
|
9
|
+
OS=0x40004,
|
|
10
|
+
fileType=0x1,
|
|
11
|
+
subtype=0x0,
|
|
12
|
+
date=(0, 0)
|
|
13
|
+
),
|
|
14
|
+
kids=[
|
|
15
|
+
StringFileInfo(
|
|
16
|
+
[
|
|
17
|
+
StringTable(
|
|
18
|
+
u'040904B0',
|
|
19
|
+
[StringStruct(u'CompanyName', u'$company$'),
|
|
20
|
+
StringStruct(u'FileDescription', u'$desc$'),
|
|
21
|
+
StringStruct(u'FileVersion', u'$sM$.$sm$.$sp$'),
|
|
22
|
+
StringStruct(u'InternalName', u'$title$-$name$'),
|
|
23
|
+
StringStruct(u'LegalCopyright', u'Copyright © $year$ $company$'),
|
|
24
|
+
StringStruct(u'OriginalFilename', u'$name$.exe'),
|
|
25
|
+
StringStruct(u'ProductName', u'$name$'),
|
|
26
|
+
StringStruct(u'ProductVersion', u'$M$.$m$.$p$')])
|
|
27
|
+
]),
|
|
28
|
+
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
|
|
29
|
+
]
|
|
30
|
+
)
|
VIStk/VIS.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
import zipfile
|
|
4
|
+
from importlib import metadata
|
|
5
|
+
from VIStk.Structures import *
|
|
6
|
+
|
|
7
|
+
inp = sys.argv
|
|
8
|
+
print(f"VIS Version {metadata.version("VIStk")}")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
#Copied from source https://stackoverflow.com/a/75246706
|
|
12
|
+
def unzip_without_overwrite(src_path, dst_dir):
|
|
13
|
+
with zipfile.ZipFile(src_path, "r") as zf:
|
|
14
|
+
for member in zf.infolist():
|
|
15
|
+
file_path = os.path.join(dst_dir, member.filename)
|
|
16
|
+
if not os.path.exists(file_path):
|
|
17
|
+
zf.extract(member, dst_dir)
|
|
18
|
+
def __main__():
|
|
19
|
+
match inp[1]:
|
|
20
|
+
case "new"|"New"|"N"|"n":#Create a new VIS project
|
|
21
|
+
project = VINFO()
|
|
22
|
+
|
|
23
|
+
case "add" | "Add" | "a" | "A":
|
|
24
|
+
project = Project()
|
|
25
|
+
match inp[2]:
|
|
26
|
+
case "screen" | "Screen" | "s" | "S":
|
|
27
|
+
if not inp[3] == None:
|
|
28
|
+
screen = project.verScreen(inp[3])
|
|
29
|
+
if len(inp) >= 5:
|
|
30
|
+
match inp[4]:
|
|
31
|
+
case "menu" | "Menu" | "m" | "M":
|
|
32
|
+
screen.addMenu(inp[5])
|
|
33
|
+
case "elements" | "Elements" | "e" | "E":
|
|
34
|
+
for i in inp[5].split("-"):
|
|
35
|
+
screen.addElement(i)
|
|
36
|
+
screen.stitch()
|
|
37
|
+
else:
|
|
38
|
+
project.newScreen(inp[3])
|
|
39
|
+
|
|
40
|
+
case "stitch" | "Stitch" | "s" | "S":
|
|
41
|
+
project = Project()
|
|
42
|
+
screen = project.getScreen(inp[2])
|
|
43
|
+
if not screen == None:
|
|
44
|
+
screen.stitch()
|
|
45
|
+
else:
|
|
46
|
+
print("Screen does not exist")
|
|
47
|
+
|
|
48
|
+
case "release" | "Release" | "r" | "R":
|
|
49
|
+
if len(inp) == 4:
|
|
50
|
+
#newRelease(inp[2],inp[3])
|
|
51
|
+
pass
|
|
52
|
+
else:
|
|
53
|
+
#newRelease(inp[2])
|
|
54
|
+
pass
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from tkinter import *
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
import subprocess
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
class MenuItem():
|
|
7
|
+
"""Each item in the menu is created from the corresponding .json file. Each path should be given relative to xyz/WOM/
|
|
8
|
+
"""
|
|
9
|
+
def __init__(self,parent:Frame|Toplevel|LabelFrame|Tk,path,nav,*args,**kwargs):
|
|
10
|
+
"""Create an item in a row on the menu
|
|
11
|
+
Args:
|
|
12
|
+
root (Tk): Master root for destruction on redirect
|
|
13
|
+
_root (Toplevel): Toplevel object to create menu items in
|
|
14
|
+
path (str): Name of .exe or absolute path to python script
|
|
15
|
+
nav (str): Navigation character to click button
|
|
16
|
+
"""
|
|
17
|
+
self.button = Button(master=parent, *args, **kwargs)
|
|
18
|
+
self.parent = parent
|
|
19
|
+
self.root = parent.winfo_toplevel()
|
|
20
|
+
self.path = path
|
|
21
|
+
self.nav = nav
|
|
22
|
+
self.button.config(command = self.itemPath)
|
|
23
|
+
enter = lambda event: event.widget.configure(background="dodger blue")
|
|
24
|
+
leave = lambda event: event.widget.configure(background="gray94")
|
|
25
|
+
self.button.bind("<Enter>", enter)
|
|
26
|
+
self.button.bind("<Leave>", leave)
|
|
27
|
+
#self.button.pack()
|
|
28
|
+
|
|
29
|
+
def itemPath(self):
|
|
30
|
+
"""Opens the given path or exe for the button
|
|
31
|
+
"""
|
|
32
|
+
if ".exe" in self.path:
|
|
33
|
+
os.startfile(self.path)
|
|
34
|
+
else:
|
|
35
|
+
subprocess.call("pythonw.exe "+self.path)
|
|
36
|
+
self.root.destroy()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from VIStk.Objects import *
|
|
2
|
+
from VIStk.Widgets._VISMenu import VISMenu
|
|
3
|
+
from tkinter import *
|
|
4
|
+
|
|
5
|
+
class MenuWindow(SubRoot):
|
|
6
|
+
def __init__(self,parent:Tk|Toplevel,path:str,*args,**kwargs):
|
|
7
|
+
super().__init__(*args,**kwargs)
|
|
8
|
+
|
|
9
|
+
#Ensure visibility
|
|
10
|
+
self.focus_force()
|
|
11
|
+
|
|
12
|
+
#Load Menu
|
|
13
|
+
self.menu = VISMenu(self, path)
|
|
14
|
+
|
|
15
|
+
#SubWindow Geometry
|
|
16
|
+
self.update()
|
|
17
|
+
self.WindowGeometry.getGeometry(True)
|
|
18
|
+
self.WindowGeometry.setGeometry(width=self.winfo_width(),
|
|
19
|
+
height=self.winfo_height(),
|
|
20
|
+
align="center",
|
|
21
|
+
size_style="window_relative",
|
|
22
|
+
window_ref=parent)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
from tkinter import *
|
|
3
|
+
from tkinter import ttk
|
|
4
|
+
from VIStk.Objects import SubRoot
|
|
5
|
+
|
|
6
|
+
class QuestionWindow(SubRoot):
|
|
7
|
+
"""An empty popout window"""
|
|
8
|
+
def __init__(self, question:str|list[str], answer:str, parent:Toplevel|Tk, ycommand=None, droplist:list=None, *args,**kwargs):
|
|
9
|
+
"""Will create a question window
|
|
10
|
+
y = yes
|
|
11
|
+
n = no
|
|
12
|
+
r = return
|
|
13
|
+
u = continue
|
|
14
|
+
b = back
|
|
15
|
+
x = close
|
|
16
|
+
c = confirm
|
|
17
|
+
d = dropdown
|
|
18
|
+
"""
|
|
19
|
+
super().__init__(*args,**kwargs)
|
|
20
|
+
cs = len(list(answer))
|
|
21
|
+
|
|
22
|
+
for i in range(0,cs,1):
|
|
23
|
+
self.columnconfigure(i,weight=1)
|
|
24
|
+
|
|
25
|
+
#Resolve Question
|
|
26
|
+
if isinstance(question, str):
|
|
27
|
+
self.rowconfigure(0, weight=1)
|
|
28
|
+
rs = 1
|
|
29
|
+
Label(self, text=question, anchor="w").grid(row=0,column=0,columnspan=cs,sticky=(N,S,E,W))
|
|
30
|
+
else:
|
|
31
|
+
rs = len(question)
|
|
32
|
+
for i in range(0,rs,1):
|
|
33
|
+
self.rowconfigure(i, weight=1)
|
|
34
|
+
Label(self, text=question[i], anchor="w").grid(row=i,column=0,columnspan=cs,sticky=(N,S,E,W))
|
|
35
|
+
self.rowconfigure(rs, weight=1)
|
|
36
|
+
|
|
37
|
+
#Resolve Answer
|
|
38
|
+
self.elements = list(answer)
|
|
39
|
+
|
|
40
|
+
self.screen_elements = []
|
|
41
|
+
for i in range(0,len(self.elements),1):
|
|
42
|
+
match self.elements[i]:
|
|
43
|
+
case "y":
|
|
44
|
+
self.screen_elements.append(Button(self, text="Yes", command = lambda: self.ycom(ycommand)))
|
|
45
|
+
self.screen_elements[i].grid(row=rs,column=i,sticky=(N,S,E,W))
|
|
46
|
+
case "n":
|
|
47
|
+
self.screen_elements.append(Button(self, text="No", command = self.xcom))
|
|
48
|
+
self.screen_elements[i].grid(row=rs,column=i,sticky=(N,S,E,W))
|
|
49
|
+
case "r":
|
|
50
|
+
self.screen_elements.append(Button(self, text="Return", command = self.xcom))
|
|
51
|
+
self.screen_elements[i].grid(row=rs,column=i,sticky=(N,S,E,W))
|
|
52
|
+
case "u":
|
|
53
|
+
self.screen_elements.append(Button(self, text="Continue", command = lambda: self.ycom(ycommand)))
|
|
54
|
+
self.screen_elements[i].grid(row=rs,column=i,sticky=(N,S,E,W))
|
|
55
|
+
case "b":
|
|
56
|
+
self.screen_elements.append(Button(self, text="Back", command = self.xcom))
|
|
57
|
+
self.screen_elements[i].grid(row=rs,column=i,sticky=(N,S,E,W))
|
|
58
|
+
case "x":
|
|
59
|
+
self.screen_elements.append(Button(self, text="Close", command = self.xcom))
|
|
60
|
+
self.screen_elements[i].grid(row=rs,column=i,sticky=(N,S,E,W))
|
|
61
|
+
case "c":
|
|
62
|
+
self.screen_elements.append(Button(self, text="Confirm", command = lambda: self.ycom(ycommand)))
|
|
63
|
+
self.screen_elements[i].grid(row=rs,column=i,sticky=(N,S,E,W))
|
|
64
|
+
case "d":
|
|
65
|
+
self.screen_elements.append(ttk.Combobox(self, values=droplist))
|
|
66
|
+
self.screen_elements[i].grid(row=rs,column=i,sticky=(N,S,E,W))
|
|
67
|
+
case _:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
#Ensure visibility
|
|
72
|
+
self.focus_force()
|
|
73
|
+
|
|
74
|
+
#SubWindow Geometry
|
|
75
|
+
self.update()
|
|
76
|
+
self.WindowGeometry.getGeometry(True)
|
|
77
|
+
self.WindowGeometry.setGeometry(width=self.winfo_width(),
|
|
78
|
+
height=self.winfo_height(),
|
|
79
|
+
align="center",
|
|
80
|
+
size_style="window_relative",
|
|
81
|
+
window_ref=parent)
|
|
82
|
+
|
|
83
|
+
def ycom(self,command):
|
|
84
|
+
self.destroy()
|
|
85
|
+
if not command is None:
|
|
86
|
+
command()
|
|
87
|
+
|
|
88
|
+
def xcom(self):
|
|
89
|
+
self.destroy()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from tkinter import *
|
|
3
|
+
from tkinter import ttk
|
|
4
|
+
from VIStk.Widgets import MenuItem
|
|
5
|
+
|
|
6
|
+
class VISMenu():
|
|
7
|
+
"""The menu class drawings a column of buttons with subprocess calls to paths defined in a corresponding .json file.
|
|
8
|
+
|
|
9
|
+
Has two roots because can destory both main window and subwindow on redirect.
|
|
10
|
+
"""
|
|
11
|
+
def __init__(self, parent:Frame|LabelFrame|Toplevel|Tk, path:str):
|
|
12
|
+
"""
|
|
13
|
+
Args:
|
|
14
|
+
root (Tk): Master root for destruction on redirect
|
|
15
|
+
_root (Toplevel): Toplevel object to create menu on
|
|
16
|
+
path (str): Path to .json file describing menu
|
|
17
|
+
destroyOnRedirect (bool): If True the root window will be destroyed on redicet
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
self.parent = parent
|
|
21
|
+
self.root = self.parent.winfo_toplevel()
|
|
22
|
+
self.path = path
|
|
23
|
+
self.ob_dict = []
|
|
24
|
+
self.n_dict = {}
|
|
25
|
+
|
|
26
|
+
#Open json file for menu structure
|
|
27
|
+
with open(path) as file:
|
|
28
|
+
self.dict:dict = json.load(file)
|
|
29
|
+
self.parent.grid_columnconfigure(0,weight=1)
|
|
30
|
+
for i in range(0, len(self.dict.keys()), 1):
|
|
31
|
+
self.parent.grid_rowconfigure(i,weight=1)
|
|
32
|
+
|
|
33
|
+
x = 0
|
|
34
|
+
for item in self.dict:
|
|
35
|
+
ob = MenuItem(self.parent,
|
|
36
|
+
path= self.dict[item]["path"],
|
|
37
|
+
nav = self.dict[item]["nav"],
|
|
38
|
+
text = self.dict[item]["text"],
|
|
39
|
+
relief="flat"
|
|
40
|
+
)
|
|
41
|
+
ob.button.grid(row=x, column=0, sticky=(N,S,E,W))
|
|
42
|
+
self.ob_dict.append(ob)
|
|
43
|
+
self.n_dict[ob.nav]=ob
|
|
44
|
+
x += 1
|
|
45
|
+
|
|
46
|
+
self.root.bind("<KeyPress>",self.menuNav)
|
|
47
|
+
|
|
48
|
+
def menuNav(self,happ:Event):
|
|
49
|
+
k=happ.char
|
|
50
|
+
if self.n_dict.get(k) != None:
|
|
51
|
+
self.n_dict[k].itemPath()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from VIStk.Widgets._QuestionWindow import QuestionWindow
|
|
2
|
+
from tkinter import *
|
|
3
|
+
|
|
4
|
+
class WarningWindow(QuestionWindow):
|
|
5
|
+
def __init__(self, warning:str|list[str], parent:Toplevel|Tk, *args, **kwargs):
|
|
6
|
+
super().__init__(question=warning, parent=parent, answer="u", ycommand=None, *args, **kwargs)
|
|
7
|
+
self.screen_elements[0]["anchor"] = "center"
|
|
8
|
+
self.modalize()
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from VIStk.Widgets._MenuItem import MenuItem
|
|
2
|
+
from VIStk.Widgets._VISMenu import Menu
|
|
3
|
+
from VIStk.Widgets._QuestionWindow import QuestionWindow
|
|
4
|
+
from VIStk.Widgets._MenuWindow import MenuWindow
|
|
5
|
+
from VIStk.Widgets._WarningWindow import WarningWindow
|
|
6
|
+
|
|
7
|
+
__all__ = ["Menu","MenuItem","MenuWindow","QuestionWindow","WarningWindow"]
|
VIStk/__init__.py
ADDED
|
File without changes
|
VIStk/fUtil.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from tkinter import *
|
|
2
|
+
from tkinter.font import Font as tkfont
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
global cfp
|
|
6
|
+
if str.upper(sys.platform)=="WIN32":
|
|
7
|
+
cfp = 'win'
|
|
8
|
+
else:
|
|
9
|
+
cfp = 'rasp'
|
|
10
|
+
|
|
11
|
+
class fUtil():
|
|
12
|
+
def __init__(self):
|
|
13
|
+
if cfp == 'win':
|
|
14
|
+
self.defont = "Arial"
|
|
15
|
+
else: #Liberation Sans is the Linux Available version of Arial
|
|
16
|
+
self.defont = "LiberationSans"
|
|
17
|
+
|
|
18
|
+
def mkfont(size:int,bold:bool=False,font:str="default"):
|
|
19
|
+
"""Creates a font string with wom fonts"""
|
|
20
|
+
|
|
21
|
+
if font == "default":
|
|
22
|
+
return f"{fUtil().defont} {size}{' bold' if bold is True else ''}"
|
|
23
|
+
|
|
24
|
+
def autosize(e:Event=None, relations:list[Widget]=None, offset:int=None,shrink:int=0):
|
|
25
|
+
"""Automatically sizes text given a widget with text."""
|
|
26
|
+
if not e is None: #Always need an event
|
|
27
|
+
#Get info from widget
|
|
28
|
+
widget:Widget = e.widget
|
|
29
|
+
w = widget.winfo_width() - 1 - shrink
|
|
30
|
+
h = widget.winfo_height() - 1
|
|
31
|
+
if w < 1 or h < 1:
|
|
32
|
+
return None
|
|
33
|
+
_root=widget.winfo_toplevel()
|
|
34
|
+
ffont = tkfont(_root, widget["font"])
|
|
35
|
+
fw = ffont.measure(widget["text"])
|
|
36
|
+
fh = ffont.metrics('linespace')
|
|
37
|
+
|
|
38
|
+
#Check widget with tightest relation
|
|
39
|
+
if not relations is None:
|
|
40
|
+
tempwi = widget
|
|
41
|
+
for wi in relations:
|
|
42
|
+
if isinstance(wi["text"],str):
|
|
43
|
+
if wi.winfo_width() > 1:
|
|
44
|
+
test = (wi.winfo_width() - 1 - ffont.measure(wi["text"]) < w-fw)
|
|
45
|
+
else:
|
|
46
|
+
test = False
|
|
47
|
+
else: test = False
|
|
48
|
+
if test:
|
|
49
|
+
widget = wi
|
|
50
|
+
else:
|
|
51
|
+
if wi.winfo_height() - 1 < h:#text has same height so only widget height must be smaller
|
|
52
|
+
if wi.winfo_height() > 1:
|
|
53
|
+
widget = wi
|
|
54
|
+
w = widget.winfo_width() - 1 - shrink
|
|
55
|
+
h = widget.winfo_height() - 1
|
|
56
|
+
fw = ffont.measure(widget["text"])
|
|
57
|
+
relations.append(tempwi)
|
|
58
|
+
|
|
59
|
+
#Final Control Sizes
|
|
60
|
+
_family = ffont.actual(option="family").replace(" ", "")
|
|
61
|
+
_size = ffont.actual(option="size")
|
|
62
|
+
_weight = ffont.actual(option="weight")
|
|
63
|
+
|
|
64
|
+
while True: #Make text larger than frame
|
|
65
|
+
if fw < w and fh < h:
|
|
66
|
+
_size = _size + 1
|
|
67
|
+
else:
|
|
68
|
+
break
|
|
69
|
+
ffont = tkfont(widget.winfo_toplevel(), f"{_family} {_size} {_weight}")
|
|
70
|
+
fh = ffont.metrics('linespace')
|
|
71
|
+
fw = ffont.measure(widget["text"])
|
|
72
|
+
|
|
73
|
+
while True: #Make text fit in frame
|
|
74
|
+
if fw <= w and fh <= h:
|
|
75
|
+
break
|
|
76
|
+
else:
|
|
77
|
+
if _size == 0:break
|
|
78
|
+
_size = _size - 1
|
|
79
|
+
ffont = tkfont(_root, f"{_family} {_size} {_weight}")
|
|
80
|
+
fh = ffont.metrics('linespace')
|
|
81
|
+
fw = ffont.measure(widget["text"])
|
|
82
|
+
|
|
83
|
+
if not offset is None: #Apply offset
|
|
84
|
+
_size = _size - offset
|
|
85
|
+
|
|
86
|
+
if not relations is None: #Correct relations
|
|
87
|
+
for wi in relations:
|
|
88
|
+
wi.configure(font = f"{_family} {_size} {_weight}")
|
|
89
|
+
else:
|
|
90
|
+
widget.configure(font = f"{_family} {_size} {_weight}")
|