Last active
January 11, 2024 17:51
-
-
Save Kaki-In/76b2c32016c4f9a56093218969a0485b to your computer and use it in GitHub Desktop.
Tkinter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from math import * | |
from kandinsky import * | |
from ion import * | |
from time import * | |
class SystemExit(Exception):pass | |
MOUSE_IMG='\x00\x00\x00\x00\x00\x00\uc1a6\uc1a6\uc1a6\x00\x00\uc1a6\uc1a6\uc1a6\x00\x00\uc1a6\uc1a6\uc1a6\x00\x00\x00\x00\x00\x00' | |
CHARS_DEFAULT=[ | |
"x",None,None,"\r", | |
"exp(","log(","log10(","1j",",","**", | |
"sin(","cos(","tan(","pi","sqrt(","**2", | |
"7","8","9","(",")", | |
"4","5","6","*","/", | |
"1","2","3","+","-", | |
"0",".","e",None,"\n"] | |
CHARS_SHIFT=[ | |
None,None,None,"\r", | |
"[","]","{","}","_","→", | |
"asin(","acos(","atan(","=","<",">", | |
"μ","8","Ω","()",")", | |
"4","5","6","*","/", | |
"1","2","3","+","-", | |
"0",".","e",None,"\n"] | |
CHARS_MINA=[ | |
":",";",'"',"%", | |
"a","b","c","d","e","f", | |
"g","h","i","j","k","l", | |
"m","n","o","p","q", | |
"r","s","t","u","v", | |
"w","x","y","z"," ", | |
"?","!","\\","@","\n"] | |
CHARS_MAJA=[ | |
":",";","'","%", | |
"A",'B','C',"D",'E','F', | |
'G','H','I','J','K','L', | |
'M','N','O','P','Q', | |
'R','S','T','U','V', | |
'W','X','Y','Z',' ', | |
"?","!","\\","@","\n"] | |
def displayedLayout(): | |
return _layout | |
class Pixmap(): | |
def __init__(self,w,h,data=""): | |
self.img=data or "\x00"*w*h | |
if len(self.img)!=w*h:raise ValueError("invalid size") | |
self.dim=(w,h) | |
def getPixel(self,x,y):return self.color(self.img[self.pos(x,y)]) | |
def pos(self,x,y):return x+y*self.dim[0] | |
def setPixel(self,x,y,col):self.img=self.img[:self.pos(x,y)]+self.char(col)+self.img[self.pos(x,y)+1:] | |
def fill_rect(self,x,y,w,h,col): | |
for xi in range(w): | |
for yi in range(h): | |
self.setPixel(x+xi,y+yi,col) | |
def color(self,char): | |
num=ord(char) | |
return (((num//(32*64))%32)*8,((num//32)%64)*4,(num%32)*8) | |
def char(self,color): | |
num=(color[0]//8)*32*64+(color[1]//4)*32+color[2]//8 | |
return chr(num) | |
class MousePixmap(Pixmap): | |
def __init__(self,dim=5,data=MOUSE_IMG): | |
super().__init__(dim,dim,data) | |
class Mouse(): | |
def __init__(self,x,y): | |
self.pos=None | |
self.shown=False | |
self.image=MousePixmap() | |
self.under=Pixmap(0,0) | |
self.show(x,y) | |
def capture(self,x,y): | |
pad=self.image.dim[0]//2 | |
p=Pixmap(*self.image.dim) | |
for xi in range(self.image.dim[0]): | |
for yi in range(self.image.dim[1]): | |
p.setPixel(xi,yi,get_pixel(x+xi-pad,y+yi-pad)) | |
return p | |
def draw(self,pixmap,x,y): | |
pad=pixmap.dim[0]//2 | |
for xi in range(pixmap.dim[0]): | |
for yi in range(pixmap.dim[0]):set_pixel(x-pad+xi,y-pad+yi,pixmap.getPixel(xi,yi)) | |
def show(self,x,y): | |
if self.shown:self.hide() | |
self.under=self.capture(x,y) | |
self.draw(self.image,x,y) | |
self.pos=(x,y) | |
self.shown=True | |
def hide(self): | |
self.draw(self.under,*self.pos) | |
self.pos=None | |
self.shown=False | |
class Keyboard(): | |
_keys={} | |
mode=0 | |
change_evt=[] | |
pressed_evt=[] | |
released_evt=[] | |
def keyPressed(self,key): | |
if key in self._keys: | |
k=self._keys[key] | |
if keydown(key): | |
if k[0]: | |
if monotonic()-k[1]>0.1: | |
k[1]=monotonic() | |
self.emit(self.pressed_evt,key) | |
return True | |
elif monotonic()-k[1]>0.5: | |
k[0],k[1]=[True,monotonic()] | |
self.emit(self.pressed_evt,key) | |
return True | |
elif keydown(key): | |
self._keys[key]=[False,monotonic()] | |
self.emit(self.pressed_evt,key) | |
return True | |
return False | |
def loadMode(self,key): | |
mode=self.mode | |
if key is KEY_SHIFT: | |
if mode%2:mode-=1 | |
else:mode+=1 | |
elif key is KEY_ALPHA: | |
if mode<4:mode+=2 | |
else:mode=0 | |
elif 0<mode<4:mode=0 | |
if mode!=self.mode:self.emit(self.change_evt,mode) | |
self.mode=mode | |
def getKey(self): | |
k=[self.nameToKey(i) for i in get_keys()] | |
for i in self._keys.copy(): | |
if not i in k: | |
del self._keys[i] | |
self.emit(self.released_evt,i) | |
for i in k: | |
if self.keyPressed(i): | |
m=self.mode | |
self.loadMode(i) | |
return i,m | |
return None,None | |
def charOfKey(self,key,mode): | |
m=mode | |
if m>3:m-=2 | |
key-=max([(key-29)//6,0])+14 | |
if key<0:return | |
c=[CHARS_DEFAULT,CHARS_SHIFT,CHARS_MINA,CHARS_MAJA][m][key] | |
if c=="%" and mode>3:c="\r" | |
return c | |
def nameToKey(self,name): | |
if name=="tan":return KEY_TANGENT | |
elif name=="sin":return KEY_SINE | |
elif name=="cos":return KEY_COSINE | |
elif name=="(":return KEY_RIGHTPARENTHESIS | |
elif name==")":return KEY_LEFTPARENTHESIS | |
elif name=="*":return KEY_MULTIPLICATION | |
elif name=="/":return KEY_DIVISION | |
elif name=="+":return KEY_PLUS | |
elif name=="-":return KEY_MINUS | |
elif name==".":return KEY_DOT | |
else: | |
if name.isdigit():i=("ZERO","ONE","TWO","THREE","FOUR","FIVE","SIX","SEVEN","EIGHT","NINE")[int(name)] | |
else:i=name.upper() | |
return eval("KEY_"+i) | |
def emit(self,fs,data): | |
for f in fs:f(data) | |
class View(): | |
def __init__(self): | |
self._attrs={"background":(255,255,255),"geometry":(10,10,50,50),"_parent":None} | |
self._modified=False | |
def draw(self): | |
gm=self._attrs["geometry"] | |
fill_rect(gm[0],gm[1],gm[2],gm[3],self._attrs["background"]) | |
def _draw(self,force): | |
if self._modified or force:self.draw() | |
self._modified=False | |
def get(self,info):return self._attrs[info] | |
def set(self,**vals): | |
for v in vals: | |
if (not v in self._attrs) or self._attrs[v]!=vals[v]: | |
self._modified=True | |
break | |
self._attrs.update(vals) | |
def setFocus(self,view="self"): | |
parent=self.get("_parent") | |
if parent:parent.setFocus(self if view=="self" else view) | |
def _onPress(self,x,y):self.get("_parent")._onPress(x,y) | |
def _onRelease(self,x,y):self.get("_parent")._onRelease(x,y) | |
def _onClick(self,x,y):self.get("_parent")._onClick(x,y) | |
def _onKey(self,key,mode):return False | |
def _viewAt(self,x,y): | |
g=self.get("geometry") | |
if g[0]<x<g[0]+g[2] and g[1]<y<g[1]+g[3]:return self | |
class Tk(): | |
def __init__(self): | |
self.mouse=Mouse(160,111) | |
self.keyboard=Keyboard() | |
self.keyboard.change_evt.append(lambda mode:self.show()) | |
self.view=None | |
self._waits=[] | |
self._focus=None | |
def after(self,time,func): | |
a=(monotonic()+time,func) | |
self._waits.append(a) | |
return a | |
def cancel(self,a):self._waits.remove(a) | |
def show(self,force=False): | |
pos=self.mouse.pos | |
self.mouse.hide() | |
self.view._draw(force) | |
self.mouse.show(*pos) | |
self.drawMode(self.keyboard.mode) | |
def drawMode(self,mode): | |
t=[" "*5,"alpha","alock"][mode//2] | |
if mode%2: | |
t=t.upper() | |
if t==" "*5:t="shift" | |
draw_string(t,285,209,(255,255,255),(195,55,50),True) | |
def main(self): | |
try:self.process() | |
except (SystemExit,KeyboardInterrupt):pass | |
def process(self): | |
self.view.set(_parent=self) | |
fill_rect(0,0,320,222,(0,0,0)) | |
self.show(force=True) | |
key=None | |
pressed=False | |
selview=None | |
while not key is KEY_HOME: | |
sleep(0.01) | |
pos=self.mouse.pos | |
self.mainDelays() | |
key,mode=self.keyboard.getKey() | |
if self._focus and key is not None: | |
if self.callEvent(self._focus,"key",(key,mode)): | |
continue | |
if key is KEY_LEFT:self.mouse.show(max([pos[0]-5,0]),pos[1]) | |
elif key is KEY_RIGHT:self.mouse.show(min([pos[0]+5,320]),pos[1]) | |
elif key is KEY_UP:self.mouse.show(pos[0],max([pos[1]-5,0])) | |
elif key is KEY_DOWN:self.mouse.show(pos[0],min([pos[1]+5,222])) | |
elif key is KEY_OK and not pressed: | |
view=self.view._viewAt(*pos) | |
if view:view.set(_selected=True) | |
self.callEvent(view,"press",pos) | |
pressed=True | |
self.setFocus(view) | |
if pressed and not keydown(KEY_OK): | |
view=self.view._viewAt(*pos) | |
pressed=False | |
self.callEvent(self._focus,"release",pos) | |
if view is self._focus: | |
self.callEvent(view,"click",pos) | |
if key:self.onKey(key,mode) | |
def mainDelays(self): | |
for i in self._waits.copy(): | |
if i[0]<monotonic(): | |
self._waits.remove(i) | |
i[1]() | |
self.show() | |
def callEvent(self,view,event,data): | |
if view is None: | |
return self.setFocus(None) | |
attr="_on"+event[0].upper()+event[1:] | |
r=getattr(view,attr)(*data) | |
self.show() | |
return r | |
def onKey(self,key,mode):pass | |
def setFocus(self,view): | |
if self._focus:self._focus.set(_selected=False) | |
self._focus=view | |
if view:view.set(_selected=True) | |
def exit(self):raise SystemExit() | |
class WordHighlight(): | |
def __init__(self): | |
self._vals=[] | |
def color(self,text,color): | |
self._vals.append((text,color)) | |
def __iter__(self): | |
return iter(self._vals) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from tkinter import * | |
class Label(View): | |
def __init__(self,text=""): | |
super().__init__() | |
self.set(text=text,size="big",color=(0,0,0),gravity="",wrap="words",decay=(0,0),colorate=()) | |
def textPos(self,lines,t): | |
gm=self._attrs["geometry"] | |
if self._attrs["size"]=="big":s,h=10,18 | |
else:s,h=7,13 | |
w=len((lines[t:t+1] or [("",)])[0][0])*s | |
gr=self._attrs["gravity"] | |
if "e" in gr:x=gm[0]+gm[2]-w-1 | |
elif "w" in gr:x=gm[0] | |
else:x=gm[0]+(gm[2]-w)//2 | |
if "s" in gr:y=gm[1]+gm[3]-h*(len(lines) or 1)-1 | |
elif "n" in gr:y=gm[1] | |
else:y=gm[1]+(gm[3]-h*(len(lines) or 1))//2 | |
return x,y,w,h,s | |
def draw(self): | |
gm=self._attrs["geometry"] | |
back=self.get("background") | |
lines=self.getDisplayedTexts() | |
decay=self.get("decay") | |
fill_rect(gm[0],gm[1],gm[2],gm[3],back) | |
for t in range(len(lines)): | |
x,y,w,h,s=self.textPos(lines,t) | |
x+=decay[0] | |
y+=decay[1] | |
if y+t*h<=gm[1]+gm[3] and y+(t+1)*h>=gm[1]: | |
c=0 | |
while c<len(lines[t][0]): | |
i1,i2=self.wordAt(lines[t][0],c) | |
word=lines[t][0][i1:i2] | |
if x+i1*s<=gm[0]+gm[2] and x+i2*s>=gm[0]: | |
col=self._attrs["color"] | |
if self.get("colorate"): | |
if word: | |
for i in self.get("colorate"): | |
if i[0]==word: | |
col=i[1] | |
break | |
draw_string(word,x+c*s,y+t*h,col,back,self._attrs["size"]=="small") | |
c+=len(word) or 1 | |
def wordAt(self,line,c): | |
chrs=" ,\\\"'.;:=<>?!*+-/→[]{}(),%" | |
if line[c] in chrs:return c,c+1 | |
i1=0 | |
i2=len(line) | |
for i in chrs: | |
try:i1=max([c+line[:c+1].rindex(i)-len(line[:c])+1,i1]) | |
except:pass | |
try:i2=min([c+line[c:].index(i),i2]) | |
except:pass | |
return i1,i2 | |
def getDisplayedTexts(self): | |
wrap=self.get("wrap") | |
text=self.get("text") | |
width=self.get("geometry")[2] | |
s=7 if self.get("size")=="small" else 10 | |
space=width//s | |
if not text:return () | |
if wrap=="lines":return tuple([(i,True) for i in text.split("\n")]) | |
elif wrap=="space": | |
l=[] | |
for t in text.split("\n"): | |
i=-1 | |
for i in range((len(t)-1)//space):l.append((t[i*space:(i+1)*space],False)) | |
l.append((t[(i+1)*space:(i+2)*space],True)) | |
return tuple(l) | |
else: | |
l=[["",None]] | |
w=False | |
for line in text.split("\n"): | |
for word in line.split(" "): | |
if len(l[-1][0])+w+len(word)>space: | |
if w:l[-1][1]=True | |
else:l.pop(-1) | |
while len(word)>space: | |
l.append([word[:space],False]) | |
word=word[space:] | |
l.append(["",None]) | |
w=False | |
if w:l[-1][0]+=" " | |
l[-1][0]+=word | |
w=True | |
l[-1][1]=True | |
l.append(["",None]) | |
w=False | |
return tuple(l[:-1]) | |
class Layout(View): | |
def __init__(self): | |
super().__init__() | |
self.set(orientation=None,margin=0) | |
self._children=[] | |
def reloadChildrenGeometry(self): | |
geometry=self.get("geometry") | |
orientation=self.get("orientation") | |
if orientation=="horizontal":pos,base,space,size=geometry | |
elif orientation=="vertical":base,pos,size,space=geometry | |
else:return | |
share=0 | |
addMarg=False | |
for i in self._children: | |
if addMarg:space-=self.get("margin") | |
if i[1]=="absolute":space-=i[2] | |
else:share+=i[2] | |
addMarg=True | |
addMarg=False | |
for i in self._children: | |
if addMarg:pos+=self.get("margin") | |
if i[1]=="absolute":taken=i[2] | |
else:taken=i[2]/share*space | |
if self.get("orientation")=="horizontal":i[0].set(geometry=(int(pos),base,int(taken),size)) | |
else:i[0].set(geometry=(base,int(pos),size,int(taken))) | |
pos+=taken | |
addMarg=True | |
def draw(self): | |
self.reloadChildrenGeometry() | |
g=self.get("geometry") | |
fill_rect(*(g+(self.get("background"),))) | |
def _draw(self,force): | |
modified = self._modified | |
super()._draw(force) | |
for i in self._children:i[0]._draw(force or modified) | |
def _viewAt(self,x,y): | |
self.reloadChildrenGeometry() | |
for i in reversed(self._children): | |
ca=i[0]._viewAt(x,y) | |
if ca:return ca | |
return self | |
def addChild(self,child,size,stype): | |
self._children.append((child,stype,size)) | |
child.set(_parent=self) | |
def removeChild(self,child): | |
for i in self._children: | |
if i[0] is child: | |
self._children.remove(i) | |
child.set(_parent=None) | |
return | |
class PushableLayout(Layout): | |
def __init__(self): | |
super().__init__() | |
self.set(onclick=lambda x,y:None,depth=2) | |
def draw(self,x,y): | |
x,y,w,h=self.get("geometry") | |
d=self.get("depth") | |
b=self.get("background") | |
if self.get("_pushed"): | |
self.set(geometry=(x+d,y+d,w-d,h-d)) | |
super().draw() | |
fill_rect(x,y,w,d,b) | |
fill_rect(x,y,d,h,b) | |
else: | |
self.set(geometry=(x,y,w-d,h-d)) | |
super().draw() | |
fill_rect(x,y+h-d,w,d,b) | |
fill_rect(x+w-d,y,d,h,b) | |
self.set(geometry=(x,y,w,h)) | |
def _onPress(self,x,y):self.set(_pushed=True) | |
def _onRelease(self,x,y):self.set(_pushed=False) | |
def _onClick(self,x,y):self.get(onclick)(x,y) | |
class Entry(Label): | |
def __init__(self,text): | |
super().__init__(text) | |
self.set(onChange=lambda value:None,border=(195,55,50),cpos=len(text),type="multiline",barcol=(195,55,50),_selected=False) | |
def draw(self): | |
gm=self.get("geometry") | |
self.set(geometry=(gm[0]+2,gm[1]+2,gm[2]-4,gm[3]-4)) | |
text=self.get("text") | |
if self.get("cpos")>len(text):self.set(cpos=len(text)) | |
cpos=self.getCursorPosition(self.get("cpos")) | |
decay=list(self.get("decay")) | |
l=self.getDisplayedTexts() | |
x,y,w,h,s=self.textPos(l,cpos[1]) | |
if self.get("size")=="big":cpos=(cpos[0]*10,cpos[1]*18) | |
else:cpos=(cpos[0]*7,cpos[1]*13) | |
if x+cpos[0]+decay[0]<gm[0]+1:decay[0]=gm[0]+1-x-cpos[0] | |
if y+cpos[1]+decay[1]<gm[1]+1:decay[1]=gm[1]+1-y-cpos[1] | |
if x+cpos[0]+decay[0]>gm[0]+gm[2]-2:decay[0]=gm[0]+gm[2]-2-x-cpos[0] | |
if y+cpos[1]+decay[1]+h>gm[1]+gm[3]-2:decay[1]=gm[1]+gm[3]-2-y-cpos[1]-h | |
self.set(decay=tuple(decay)) | |
super().draw() | |
self.set(geometry=gm) | |
b=self.get("border") | |
if self.get("_selected"):b=[i//2 for i in b] | |
fill_rect(gm[0],gm[1],gm[2],2,b) | |
fill_rect(gm[0],gm[1],2,gm[3],b) | |
fill_rect(gm[0],gm[1]+gm[3]-2,gm[2],2,b) | |
fill_rect(gm[0]+gm[2]-2,gm[1],2,gm[3],b) | |
if not self.get("_selected"):return | |
fill_rect(x+cpos[0]+decay[0]+1,5+y+cpos[1]+decay[1],1,s,self.get("barcol")) | |
def _onKey(self,key,mode): | |
cpos=self.get("cpos") | |
p=self.getCursorPosition(cpos) | |
text=self.get("text") | |
if key is KEY_OK:self.setFocus(None) | |
elif key is KEY_RIGHT and cpos<len(text): | |
cpos+=1 | |
if mode==1: | |
while cpos<len(text) and text[cpos]!="\n":cpos+=1 | |
elif key is KEY_LEFT and cpos>0: | |
cpos-=1 | |
if mode==1: | |
while cpos>0 and text[cpos-1]!="\n":cpos-=1 | |
elif key is KEY_UP: | |
if mode==1:cpos=0 | |
else: | |
lastcpos=cpos | |
np=self.getCursorPosition(lastcpos) | |
while cpos>0 and (np[1]==p[1] or np[0]>p[0]): | |
cpos-=1 | |
np=self.getCursorPosition(cpos) | |
elif key is KEY_DOWN: | |
if mode==1:cpos=len(text) | |
else: | |
lastcpos=cpos | |
np=self.getCursorPosition(lastcpos) | |
while cpos<len(text) and (np[1]==p[1] or (np[0]<p[0] and text[cpos]!="\n")): | |
cpos+=1 | |
np=self.getCursorPosition(cpos) | |
elif key is not None: | |
c=Keyboard().charOfKey(key,mode) | |
if c=="\r": | |
if cpos>0: | |
text=text[:cpos-1]+text[cpos:] | |
cpos-=1 | |
if mode==1: | |
while cpos>0 and text[cpos-1]!="\n": | |
text=text[:cpos-1]+text[cpos:] | |
cpos-=1 | |
elif c and (c!="\n" or self.get("type")=="multiline") and (c in "0123456789." or self.get("type")!="number"): | |
text=text[:cpos]+c+text[cpos:] | |
cpos+=len(c) | |
self.set(cpos=cpos,text=text) | |
return True | |
def getCursorPosition(self,cpos): | |
lines=self.getDisplayedTexts() | |
if not lines:return (0,0) | |
y=0 | |
for line in lines: | |
l=len(line[0]) | |
if cpos>l: | |
cpos-=l+line[1] | |
y+=1 | |
else:break | |
return cpos,y | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment