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
VIStk/Form.zip
ADDED
|
Binary file
|
VIStk/Objects/_Root.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from tkinter import *
|
|
2
|
+
from VIStk.Objects._WindowGeometry import WindowGeometry
|
|
3
|
+
from VIStk.Objects._Window import Window
|
|
4
|
+
|
|
5
|
+
class Root(Tk, Window):
|
|
6
|
+
"""A wrapper for the Tk class with VIS attributes"""
|
|
7
|
+
def __init__(self,*args,**kwargs):
|
|
8
|
+
super().__init__(*args,**kwargs)
|
|
9
|
+
self.WindowGeometry = WindowGeometry(self)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from tkinter import *
|
|
2
|
+
from VIStk.Objects._WindowGeometry import WindowGeometry
|
|
3
|
+
from VIStk.Objects._Window import Window
|
|
4
|
+
|
|
5
|
+
class SubRoot(Toplevel, Window):
|
|
6
|
+
"""A wrapper for the Toplevel class with VIS attributes"""
|
|
7
|
+
def __init__(self,*args,**kwargs):
|
|
8
|
+
super().__init__(*args,**kwargs)
|
|
9
|
+
self.WindowGeometry = WindowGeometry(self)
|
|
10
|
+
|
|
11
|
+
def modalize(self):
|
|
12
|
+
"""Makes the SubWindow modal"""
|
|
13
|
+
self.focus_force()
|
|
14
|
+
|
|
15
|
+
self.transient(self.master)
|
|
16
|
+
self.grab_set()
|
|
17
|
+
|
|
18
|
+
self.master.wait_window(self)
|
VIStk/Objects/_Window.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from tkinter import *
|
|
2
|
+
|
|
3
|
+
class Window:
|
|
4
|
+
"""A VIS Window object"""
|
|
5
|
+
def __init__(self):
|
|
6
|
+
"""Initializes the VIS Window"""
|
|
7
|
+
|
|
8
|
+
def fullscreen(self,absolute:bool=False):
|
|
9
|
+
if absolute is False:
|
|
10
|
+
try: #On Linux
|
|
11
|
+
self.wm_attributes("-zoomed", True)
|
|
12
|
+
except TclError: #On Windows
|
|
13
|
+
self.state('zoomed')
|
|
14
|
+
else:
|
|
15
|
+
self.attributes("-fullscreen", True)
|
|
16
|
+
|
|
17
|
+
def unfullscreen(self,absolute:bool=False):
|
|
18
|
+
if absolute is False:
|
|
19
|
+
try: #On Linux
|
|
20
|
+
self.wm_attributes("-zoomed", False)
|
|
21
|
+
except TclError: #On Windows
|
|
22
|
+
self.state('normal')
|
|
23
|
+
else:
|
|
24
|
+
self.attributes("-fullscreen", False)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from tkinter import *
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
query = Tk()
|
|
5
|
+
try: #On Linux
|
|
6
|
+
query.wm_attributes("-zoomed", True)
|
|
7
|
+
except TclError: #On Windows
|
|
8
|
+
query.state('zoomed')
|
|
9
|
+
query.update()
|
|
10
|
+
global hs, ws
|
|
11
|
+
ws = query.winfo_width()-2#Unclear about this offset
|
|
12
|
+
hs = query.winfo_height()-9#Might be operating system specific
|
|
13
|
+
query.destroy()
|
|
14
|
+
print(f"Screen has usable size of {ws}x{hs}")
|
|
15
|
+
|
|
16
|
+
class WindowGeometry():
|
|
17
|
+
"""Handles geometry relations and sizing/resizing for windows"""
|
|
18
|
+
def __init__(self,window:Tk|Toplevel):
|
|
19
|
+
"""Creates a window geometry object and automatically extracts the size and location
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
window (Tk|Toplevel): The window to access geometry of
|
|
23
|
+
"""
|
|
24
|
+
self.window:Tk|Toplevel = window
|
|
25
|
+
self.getGeometry()
|
|
26
|
+
window.WindowGeometry = self
|
|
27
|
+
|
|
28
|
+
def getGeometry(self, respect_size:bool=False):
|
|
29
|
+
"""Sets the internal geometry of object to match the window"""
|
|
30
|
+
geo_str = self.window.geometry()
|
|
31
|
+
geo_list = geo_str.split("x")
|
|
32
|
+
ng_list = [int(geo_list[0])]
|
|
33
|
+
for i in geo_list[1].split("+"):
|
|
34
|
+
ng_list.append(int(i))
|
|
35
|
+
|
|
36
|
+
if respect_size:
|
|
37
|
+
self.geometry = [self.window.winfo_width(), self.window.winfo_height(), ng_list[2], ng_list[3]]
|
|
38
|
+
else:
|
|
39
|
+
self.geometry = ng_list
|
|
40
|
+
|
|
41
|
+
def stripGeometry(self,objects:tuple[Literal["w","h","x","y"]]|Literal["all"]):
|
|
42
|
+
"""Returns integer values of the requested items"""
|
|
43
|
+
geo_str = self.window.geometry()
|
|
44
|
+
geo_list = geo_str.split("x")
|
|
45
|
+
ng_list = [int(geo_list[0])]
|
|
46
|
+
for i in geo_list[1].split("+"):
|
|
47
|
+
ng_list.append(int(i))
|
|
48
|
+
|
|
49
|
+
geo_list = []
|
|
50
|
+
if objects == "all":
|
|
51
|
+
geo_list = ng_list
|
|
52
|
+
else:
|
|
53
|
+
if "w" in objects: geo_list.append(ng_list[0])
|
|
54
|
+
if "h" in objects: geo_list.append(ng_list[1])
|
|
55
|
+
if "x" in objects: geo_list.append(ng_list[2])
|
|
56
|
+
if "y" in objects: geo_list.append(ng_list[3])
|
|
57
|
+
|
|
58
|
+
return geo_list
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def setGeometry(self,width:int=None,height:int=None,x:int=None,y:int=None,align:Literal["center","n","ne","e","se","s","sw","w","nw"]=None,size_style:Literal["pixels","screen_relative","window_relative"]=None,window_ref:Tk|Toplevel=None):
|
|
62
|
+
"""Sets the geometry of the window"""
|
|
63
|
+
global hs, ws
|
|
64
|
+
ox, oy = 0, 0
|
|
65
|
+
|
|
66
|
+
if width is None: width = self.geometry[0]
|
|
67
|
+
if height is None: height = self.geometry[1]
|
|
68
|
+
|
|
69
|
+
#Check if aligning or using coordinates
|
|
70
|
+
if align is None:
|
|
71
|
+
if x is None: x = self.geometry[2]
|
|
72
|
+
if y is None: y = self.geometry[3]
|
|
73
|
+
else:
|
|
74
|
+
x = None
|
|
75
|
+
y = None
|
|
76
|
+
|
|
77
|
+
#No adjustment needs to be made if pixels are given
|
|
78
|
+
|
|
79
|
+
if size_style == "window_relative":
|
|
80
|
+
if not window_ref is None:
|
|
81
|
+
_ws = window_ref.winfo_width()
|
|
82
|
+
_hs = window_ref.winfo_height()
|
|
83
|
+
|
|
84
|
+
if not _ws is None: ws = _ws
|
|
85
|
+
if not _hs is None: hs = _hs
|
|
86
|
+
|
|
87
|
+
geo_ref = WindowGeometry(window_ref).stripGeometry(("x","y"))
|
|
88
|
+
|
|
89
|
+
ox = geo_ref[0]
|
|
90
|
+
oy = geo_ref[1]
|
|
91
|
+
|
|
92
|
+
#Will always hand over relative sizing to screen relative
|
|
93
|
+
size_style = "screen_relative"
|
|
94
|
+
|
|
95
|
+
if size_style == "screen_relative":
|
|
96
|
+
if not width == self.geometry[0]:
|
|
97
|
+
width = ws*width/100
|
|
98
|
+
|
|
99
|
+
if not height == self.geometry[1]:
|
|
100
|
+
height = hs*height/100
|
|
101
|
+
|
|
102
|
+
if not align is None:
|
|
103
|
+
match align:
|
|
104
|
+
case "center":
|
|
105
|
+
x = ws/2 - width/2 + ox
|
|
106
|
+
y = hs/2 - height/2 + oy
|
|
107
|
+
case "n":
|
|
108
|
+
x = ws/2 - width/2 + ox
|
|
109
|
+
y = 0 + oy
|
|
110
|
+
case "ne":
|
|
111
|
+
x = ws - width + ox
|
|
112
|
+
y = 0 + oy
|
|
113
|
+
case "e":
|
|
114
|
+
x = ws - width + ox
|
|
115
|
+
y = hs/2 - height/2 + oy
|
|
116
|
+
case "se":
|
|
117
|
+
x = ws - width + ox
|
|
118
|
+
y = hs - height + oy
|
|
119
|
+
case "s":
|
|
120
|
+
x = ws/2 - width/2 + ox
|
|
121
|
+
y = hs - height + oy
|
|
122
|
+
case "sw":
|
|
123
|
+
x = 0 + ox
|
|
124
|
+
y = hs - height + oy
|
|
125
|
+
case "w":
|
|
126
|
+
x = 0 + ox
|
|
127
|
+
y = hs/2 - height/2 + oy
|
|
128
|
+
case "nw":
|
|
129
|
+
x = 0 + ox
|
|
130
|
+
y = 0 + oy
|
|
131
|
+
|
|
132
|
+
self.geometry = [int(width), int(height), int(x-7), int(y)]
|
|
133
|
+
self.window.geometry('%dx%d+%d+%d' % tuple(self.geometry))
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import zipfile
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
7
|
+
#Copied from source
|
|
8
|
+
#https://stackoverflow.com/a/75246706
|
|
9
|
+
def unzip_without_overwrite(src_path, dst_dir):
|
|
10
|
+
with zipfile.ZipFile(src_path, "r") as zf:
|
|
11
|
+
for member in zf.infolist():
|
|
12
|
+
file_path = os.path.join(dst_dir, member.filename)
|
|
13
|
+
if not os.path.exists(file_path):
|
|
14
|
+
zf.extract(member, dst_dir)
|
|
15
|
+
|
|
16
|
+
def getPath():
|
|
17
|
+
"""Searches for .VIS folder and returns from path.cfg
|
|
18
|
+
"""
|
|
19
|
+
sto = 0
|
|
20
|
+
while True:
|
|
21
|
+
try:
|
|
22
|
+
step=""
|
|
23
|
+
for i in range(0,sto,1): #iterate on sto to step backwards and search for project info
|
|
24
|
+
step = "../" + step
|
|
25
|
+
if os.path.exists(step+".VIS/"):
|
|
26
|
+
return open(step+".VIS/path.cfg","r").read().replace("\\","/") #return stored path
|
|
27
|
+
else:
|
|
28
|
+
if os.path.exists(step):
|
|
29
|
+
sto += 1
|
|
30
|
+
else:
|
|
31
|
+
return None #return none if cant escape more
|
|
32
|
+
except:
|
|
33
|
+
return None #if failed return none
|
|
34
|
+
|
|
35
|
+
def validName(name:str):
|
|
36
|
+
"""Checks if provided path is a valid filename
|
|
37
|
+
"""
|
|
38
|
+
if " " in name:
|
|
39
|
+
print("Cannot have spaces in file name.")
|
|
40
|
+
return False
|
|
41
|
+
if "/" in name or "\\" in name:
|
|
42
|
+
print("Cannot have filepath deliminator in file name.")
|
|
43
|
+
return False
|
|
44
|
+
if "<" in name or ">" in name or ":" in name or '"' in name or "|" in name or "?" in name or "*" in name:
|
|
45
|
+
print('Invlaid ASCII characters for windows file creation, please remove all <>:"|?* from file name.')
|
|
46
|
+
return False
|
|
47
|
+
if name.split(".")[0] in ["CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"]:
|
|
48
|
+
print(f"Filename {name} reserved by OS.")
|
|
49
|
+
return False
|
|
50
|
+
if "" == name:
|
|
51
|
+
print("Must provide a name for file.")
|
|
52
|
+
return False
|
|
53
|
+
else:
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class VINFO():
|
|
58
|
+
"""Overarching control structure within the /.VIS/ folder
|
|
59
|
+
"""
|
|
60
|
+
def __init__(self):
|
|
61
|
+
if getPath() == None:
|
|
62
|
+
wd = os.getcwd()
|
|
63
|
+
os.mkdir(wd+"\\.VIS")
|
|
64
|
+
open(wd+"/.VIS/path.cfg","w").write(wd) if os.path.exists(wd+"/.VIS/path.cfg") else open(wd+"/.VIS/path.cfg", 'a').write(wd)
|
|
65
|
+
print(f"Stored project path in path.cfg as {wd} in {wd}/.VIS/path.cfg")
|
|
66
|
+
|
|
67
|
+
unzip_without_overwrite("Form.zip",wd)
|
|
68
|
+
print(f"Copied structure to {wd}")
|
|
69
|
+
|
|
70
|
+
shutil.copytree("./Templates",wd+"/.VIS/Templates",dirs_exist_ok=True)
|
|
71
|
+
print(f"Loaded default templates into {wd}/.VIS/Templates/")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
#DO NOT MESS WITH THE TEMPLATE HEADERS
|
|
75
|
+
|
|
76
|
+
title = input("Enter a name for the VIS project: ")
|
|
77
|
+
self.title = title
|
|
78
|
+
info = {}
|
|
79
|
+
info[self.title] = {}
|
|
80
|
+
info[self.title]["Screens"]={}
|
|
81
|
+
info[self.title]["defaults"]={}
|
|
82
|
+
info[self.title]["defaults"]["icon"]="VIS"#default icon
|
|
83
|
+
self.d_icon = "VIS"
|
|
84
|
+
self[self.title]["metadata"]={}
|
|
85
|
+
comp = input("What company is this for(or none)? ")
|
|
86
|
+
if not comp in ["none","None"]:
|
|
87
|
+
info[self.title]["metadata"]["company"] = comp
|
|
88
|
+
self.company = comp
|
|
89
|
+
else:
|
|
90
|
+
info[self.title]["metadata"]["company"] = None
|
|
91
|
+
self.company = None
|
|
92
|
+
|
|
93
|
+
version = input("What is the initial version for the project (0.0.1 default): ")
|
|
94
|
+
vers = version.split(".")
|
|
95
|
+
if len(vers)==3:
|
|
96
|
+
if vers[0].isnumeric() and vers[1].isnumeric() and vers[2].isnumeric():
|
|
97
|
+
self.version = version
|
|
98
|
+
else:
|
|
99
|
+
self.version = "0.0.1"
|
|
100
|
+
else:
|
|
101
|
+
self.version = "0.0.1"
|
|
102
|
+
info[self.title]["metadata"]["version"] = self.version
|
|
103
|
+
|
|
104
|
+
with open(wd+"/.VIS/project.json","w") as f:
|
|
105
|
+
f.write("{}")
|
|
106
|
+
json.dump(info,f,indent=4)
|
|
107
|
+
print(f"Setup project.json for project {self.title} in {wd}/.VIS/")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
#Need to get current python location where VIS is installed
|
|
111
|
+
self.p_vis = subprocess.check_output('python -c "import os, sys; print(os.path.dirname(sys.executable))"').decode().strip("\r\n")+"\\Lib\\site-packages\\VIS\\"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
self.p_project = getPath()
|
|
115
|
+
self.p_vinfo = self.p_project + "/.VIS"
|
|
116
|
+
self.p_sinfo = self.p_vinfo + "/project.json"
|
|
117
|
+
with open(self.p_sinfo,"r") as f:
|
|
118
|
+
info = json.load(f)
|
|
119
|
+
self.title = list(info.keys())[0]
|
|
120
|
+
self.version = info[self.title]["metadata"]["version"]
|
|
121
|
+
self.company = info[self.title]["metadata"]["company"]
|
|
122
|
+
|
|
123
|
+
self.screenlist = []
|
|
124
|
+
self.p_screens = self.p_project +"/Screens"
|
|
125
|
+
self.p_modules = self.p_project +"/modules"
|
|
126
|
+
self.p_templates = self.p_vinfo + "/Templates"
|
|
127
|
+
self.p_icons = self.p_project + "/Icons"
|
|
128
|
+
self.p_images = self.p_project + "/Images"
|
|
129
|
+
|
|
130
|
+
def setVersion(self,version:str):
|
|
131
|
+
"""Sets a new project version
|
|
132
|
+
"""
|
|
133
|
+
with open(self.p_sinfo,"r") as f:
|
|
134
|
+
info = json.load(f)
|
|
135
|
+
|
|
136
|
+
info[self.title]["metadata"]["version"] = version
|
|
137
|
+
|
|
138
|
+
with open(self.p_sinfo,"w") as f:
|
|
139
|
+
json.dump(info,f,indent=4)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from VIStk.Structures.VINFO import *
|
|
3
|
+
from VIStk.Structures.screen import *
|
|
4
|
+
|
|
5
|
+
class Project(VINFO):
|
|
6
|
+
"""VIS Project Object
|
|
7
|
+
"""
|
|
8
|
+
def __init__(self):
|
|
9
|
+
"""Initializes or load a VIS project
|
|
10
|
+
"""
|
|
11
|
+
super().__init__()
|
|
12
|
+
with open(self.p_sinfo,"r") as f:
|
|
13
|
+
info = json.load(f)
|
|
14
|
+
self.name = list(info.keys())[0]
|
|
15
|
+
|
|
16
|
+
for screen in list(info[self.name]["Screens"].keys()):
|
|
17
|
+
scr = Screen(screen,
|
|
18
|
+
info[self.name]["Screens"][screen]["script"],
|
|
19
|
+
info[self.name]["Screens"][screen]["release"],
|
|
20
|
+
info[self.name]["Screens"][screen].get("icon"),
|
|
21
|
+
exists=True)
|
|
22
|
+
self.screenlist.append(scr)
|
|
23
|
+
self.d_icon = info[self.name]["defaults"]["icon"]
|
|
24
|
+
|
|
25
|
+
def newScreen(self,screen:str) -> int:
|
|
26
|
+
"""Creates a new screen with some prompting
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
0 Failed
|
|
30
|
+
1 Success
|
|
31
|
+
"""
|
|
32
|
+
#Check for valid filename
|
|
33
|
+
if not validName(screen):
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
with open(self.p_sinfo,"r") as f:
|
|
37
|
+
info = json.load(f) #Load info
|
|
38
|
+
|
|
39
|
+
name = self.title
|
|
40
|
+
if info[name]["Screens"].get(screen) == None: #If Screen does not exist in VINFO
|
|
41
|
+
while True: #ensures a valid name is used for script
|
|
42
|
+
match input(f"Should python script use name {screen}.py? "):
|
|
43
|
+
case "Yes" | "yes" | "Y" | "y":
|
|
44
|
+
script = screen + ".py"
|
|
45
|
+
break
|
|
46
|
+
case _:
|
|
47
|
+
script = input("Enter the name for the script file: ").strip(".py")+".py"
|
|
48
|
+
if validName(script):
|
|
49
|
+
break
|
|
50
|
+
|
|
51
|
+
match input("Should this screen have its own .exe?: "):
|
|
52
|
+
case "Yes" | "yes" | "Y" | "y":
|
|
53
|
+
release = True
|
|
54
|
+
case _:
|
|
55
|
+
release = False
|
|
56
|
+
ictf =input("What is the icon for this screen (or none)?: ")
|
|
57
|
+
icon = ictf.strip(".ico") if ".ICO" in ictf.upper() else None
|
|
58
|
+
desc = input("Write a description for this screen: ")
|
|
59
|
+
self.screenlist.append(Screen(screen,script,release,icon,False,desc))
|
|
60
|
+
|
|
61
|
+
return 1
|
|
62
|
+
else:
|
|
63
|
+
print(f"Information for {screen} already in project.")
|
|
64
|
+
return 1
|
|
65
|
+
|
|
66
|
+
def hasScreen(self,screen:str) -> bool:
|
|
67
|
+
"""Checks if the project has the correct screen
|
|
68
|
+
"""
|
|
69
|
+
for i in self.screenlist:
|
|
70
|
+
if i.name == screen:
|
|
71
|
+
return True
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
def getScreen(self,screen:str) -> Screen:
|
|
75
|
+
"""Returns a screen object by its name
|
|
76
|
+
"""
|
|
77
|
+
for i in self.screenlist:
|
|
78
|
+
if i.name == screen:
|
|
79
|
+
return i
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
def verScreen(self,screen:str) -> Screen:
|
|
83
|
+
"""Verifies a screen exists and returns it
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
screen (Screen): Verified screen
|
|
87
|
+
"""
|
|
88
|
+
if not self.hasScreen(screen):
|
|
89
|
+
self.newScreen(screen)
|
|
90
|
+
scr = self.getScreen(screen)
|
|
91
|
+
return scr
|
|
92
|
+
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from VIStk.Structures.project import *
|
|
2
|
+
from VIStk.Structures.VINFO import *
|
|
3
|
+
from VIStk.Structures.screen import *
|
|
4
|
+
import subprocess
|
|
5
|
+
import shutil
|
|
6
|
+
from os.path import exists
|
|
7
|
+
import time
|
|
8
|
+
import datetime
|
|
9
|
+
|
|
10
|
+
info = {}
|
|
11
|
+
|
|
12
|
+
class Release():
|
|
13
|
+
"""A VIS Release object"""
|
|
14
|
+
def __init__(self, project :Project, version:str="",type:str="",note:str=""):
|
|
15
|
+
"""Creates a Release object to release or examine a releaes of a project"""
|
|
16
|
+
self.project = project
|
|
17
|
+
self.version = version
|
|
18
|
+
self.type = type
|
|
19
|
+
self.note = note
|
|
20
|
+
|
|
21
|
+
def build(self):
|
|
22
|
+
"""Build project spec file for release
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
print(f"Creating project.spec for {self.project.name}")
|
|
26
|
+
|
|
27
|
+
with open(self.project.p_vinfo+"/Templates/spec.txt","r") as f:
|
|
28
|
+
spec = f.read()
|
|
29
|
+
with open(self.project.p_vinfo+"/Templates/collect.txt","r") as f:
|
|
30
|
+
collect = f.read()
|
|
31
|
+
|
|
32
|
+
spec_list = []
|
|
33
|
+
name_list = []
|
|
34
|
+
os.mkdir(self.project.p_vinfo+"/Build")
|
|
35
|
+
for i in self.project.screenlist:
|
|
36
|
+
if i.release:
|
|
37
|
+
name_list.append(i.name)
|
|
38
|
+
if not i.icon == None:
|
|
39
|
+
icon = i.icon
|
|
40
|
+
else:
|
|
41
|
+
icon = self.project.d_icon
|
|
42
|
+
spec_list.append(spec.replace("$name$",i.name))
|
|
43
|
+
spec_list[len(spec_list)-1] = spec_list[len(spec_list)-1].replace("$icon$",icon)
|
|
44
|
+
spec_list[len(spec_list)-1] = spec_list[len(spec_list)-1].replace("$file$",i.script)
|
|
45
|
+
|
|
46
|
+
#build metadata
|
|
47
|
+
with open(self.project.p_templates+"/version.txt","r") as f:
|
|
48
|
+
meta = f.read()
|
|
49
|
+
|
|
50
|
+
#Update Overall Project Version
|
|
51
|
+
vers = self.project.version.split(".")
|
|
52
|
+
major = vers[0]
|
|
53
|
+
minor = vers[1]
|
|
54
|
+
patch = vers[2]
|
|
55
|
+
meta = meta.replace("$M$",major)
|
|
56
|
+
meta = meta.replace("$m$",minor)
|
|
57
|
+
meta = meta.replace("$p$",patch)
|
|
58
|
+
|
|
59
|
+
#Update Screen Version
|
|
60
|
+
vers = i.s_version.split(".")
|
|
61
|
+
major = vers[0]
|
|
62
|
+
minor = vers[1]
|
|
63
|
+
patch = vers[2]
|
|
64
|
+
meta = meta.replace("$sM$",major)
|
|
65
|
+
meta = meta.replace("$sm$",minor)
|
|
66
|
+
meta = meta.replace("$sp$",patch)
|
|
67
|
+
|
|
68
|
+
if self.project.company != None:
|
|
69
|
+
meta = meta.replace("$company$",self.project.company)
|
|
70
|
+
meta = meta.replace("$year$",str(datetime.datetime.now().year))
|
|
71
|
+
else:
|
|
72
|
+
meta = meta.replace(" VALUE \"CompanyName\", VER_COMPANYNAME_STR\n","")
|
|
73
|
+
meta = meta.replace(" VALUE \"LegalCopyright\", VER_LEGALCOPYRIGHT_STR\n","")
|
|
74
|
+
meta = meta.replace("#define VER_LEGAL_COPYRIGHT_STR \"Copyright © $year$ $company$\\0\"\n\n","")
|
|
75
|
+
meta = meta.replace("$name$",i.name)
|
|
76
|
+
meta = meta.replace("$desc$",i.desc)
|
|
77
|
+
|
|
78
|
+
with open(self.project.p_vinfo+f"/Build/{i.name}.txt","w") as f:
|
|
79
|
+
f.write(meta)
|
|
80
|
+
spec_list[len(spec_list)-1] = spec_list[len(spec_list)-1].replace("$meta$",self.project.p_vinfo+f"/Build/{i.name}.txt")
|
|
81
|
+
spec_list.append("\n\n")
|
|
82
|
+
|
|
83
|
+
insert = ""
|
|
84
|
+
for i in name_list:
|
|
85
|
+
insert=insert+"\n\t"+i+"_exe,\n\t"+i+"_a.binaries,\n\t"+i+"_a.zipfiles,\n\t"+i+"_a.datas,"
|
|
86
|
+
collect = collect.replace("$insert$",insert)
|
|
87
|
+
collect = collect.replace("$version$",self.project.name+"-"+self.version) if not self.version == "" else collect.replace("$version$",self.project.name)
|
|
88
|
+
|
|
89
|
+
header = "# -*- mode: python ; coding: utf-8 -*-\n\n\n"
|
|
90
|
+
|
|
91
|
+
with open(self.project.p_vinfo+"/project.spec","w") as f:
|
|
92
|
+
f.write(header)
|
|
93
|
+
with open(self.project.p_vinfo+"/project.spec","a") as f:
|
|
94
|
+
f.writelines(spec_list)
|
|
95
|
+
f.write(collect)
|
|
96
|
+
|
|
97
|
+
print(f"Finished creating project.spec for {self.project.title} {self.version if not self.version =="" else "current"}")#advanced version will improve this
|
|
98
|
+
|
|
99
|
+
def clean(self):
|
|
100
|
+
"""Cleans up build environment to save space
|
|
101
|
+
"""
|
|
102
|
+
print("Cleaning up build environment")
|
|
103
|
+
shutil.rmtree(self.project.p_vinfo+"/Build")
|
|
104
|
+
print("Appending Screen Data To Environment")
|
|
105
|
+
if self.version == " ":
|
|
106
|
+
if exists(f"{self.project.p_project}/dist/{self.project.title}/Icons/"): shutil.rmtree(f"{self.project.p_project}/dist/{self.project.title}/Icons/")
|
|
107
|
+
if exists(f"{self.project.p_project}/dist/{self.project.title}/Images/"): shutil.rmtree(f"{self.project.p_project}/dist/{self.project.title}/Images/")
|
|
108
|
+
shutil.copytree(self.project.p_project+"/Icons/",f"{self.project.p_project}/dist/{self.project.title}/Icons/",dirs_exist_ok=True)
|
|
109
|
+
shutil.copytree(self.project.p_project+"/Images/",f"{self.project.p_project}/dist/{self.project.title}/Images/",dirs_exist_ok=True)
|
|
110
|
+
else:
|
|
111
|
+
if exists(f"{self.project.p_project}/dist/{self.project.title}/Icons/"): shutil.rmtree(f"{self.project.p_project}/dist/{self.project.name}/Icons/")
|
|
112
|
+
if exists(f"{self.project.p_project}/dist/{self.project.title}/Images/"): shutil.rmtree(f"{self.project.p_project}/dist/{self.project.name}/Images/")
|
|
113
|
+
shutil.copytree(self.project.p_project+"/Icons/",f"{self.project.p_project}/dist/{self.project.title}-{self.version.strip(" ")}/Icons/",dirs_exist_ok=True)
|
|
114
|
+
shutil.copytree(self.project.p_project+"/Images/",f"{self.project.p_project}/dist/{self.project.title}-{self.version.strip(" ")}/Images/",dirs_exist_ok=True)
|
|
115
|
+
print(f"\n\nReleased new{self.version}build of {self.project.title}!")
|
|
116
|
+
|
|
117
|
+
def newVersion(self):
|
|
118
|
+
"""Updates the project version, PERMANENT, cannot be undone
|
|
119
|
+
"""
|
|
120
|
+
old = str(self.project.version)
|
|
121
|
+
vers = self.project.version.split(".")
|
|
122
|
+
if self.version == "Major":
|
|
123
|
+
vers[0] = str(int(vers[0])+1)
|
|
124
|
+
vers[1] = str(0)
|
|
125
|
+
vers[2] = str(0)
|
|
126
|
+
if self.version == "Minor":
|
|
127
|
+
vers[1] = str(int(vers[1])+1)
|
|
128
|
+
vers[2] = str(0)
|
|
129
|
+
if self.version == "Patch":
|
|
130
|
+
vers[2] = str(int(vers[2])+1)
|
|
131
|
+
|
|
132
|
+
self.project.setVersion(f"{vers[0]}.{vers[1]}.{vers[2]}")
|
|
133
|
+
self.project = VINFO()
|
|
134
|
+
print(f"Updated Version {old}=>{self.project.version}")
|
|
135
|
+
|
|
136
|
+
def makeRelease(self):
|
|
137
|
+
"""Releases a version of your project
|
|
138
|
+
"""
|
|
139
|
+
match self.version:
|
|
140
|
+
case "a":
|
|
141
|
+
self.build("alpha")
|
|
142
|
+
subprocess.call(f"pyinstaller {self.project.p_vinfo}/project.spec --noconfirm --distpath {self.project.p_project}/dist/ --log-level FATAL")
|
|
143
|
+
self.clean(" alpha ")
|
|
144
|
+
case "b":
|
|
145
|
+
self.build("beta")
|
|
146
|
+
subprocess.call(f"pyinstaller {self.project.p_vinfo}/project.spec --noconfirm --distpath {self.project.p_project}/dist/ --log-level FATAL")
|
|
147
|
+
self.clean(" beta ")
|
|
148
|
+
case "c":
|
|
149
|
+
self.newVersion(type)
|
|
150
|
+
self.build()
|
|
151
|
+
subprocess.call(f"pyinstaller {self.project.p_vinfo}/project.spec --noconfirm --distpath {self.project.p_project}/dist/ --log-level FATAL")
|
|
152
|
+
self.clean()
|
|
153
|
+
case "sync":
|
|
154
|
+
self.build("alpha")
|
|
155
|
+
subprocess.call(f"pyinstaller {self.project.p_vinfo}/project.spec --noconfirm --distpath {self.project.p_project}/dist/ --log-level FATAL")
|
|
156
|
+
self.clean(" alpha ")
|
|
157
|
+
self.build("beta")
|
|
158
|
+
subprocess.call(f"pyinstaller {self.project.p_vinfo}/project.spec --noconfirm --distpath {self.project.p_project}/dist/ --log-level FATAL")
|
|
159
|
+
self.clean(" beta ")
|
|
160
|
+
self.build()
|
|
161
|
+
subprocess.call(f"pyinstaller {self.project.p_vinfo}/project.spec --noconfirm --distpath {self.project.p_project}/dist/ --log-level FATAL")
|
|
162
|
+
self.clean()
|
|
163
|
+
print("\t- alpha\n\t- beta\n\t- current")
|
|
164
|
+
case _:
|
|
165
|
+
print(f"Could not release Project Version {self.version}")
|