Skip to content

Instantly share code, notes, and snippets.

@Kaki-In
Last active January 11, 2024 17:51
Show Gist options
  • Save Kaki-In/76b2c32016c4f9a56093218969a0485b to your computer and use it in GitHub Desktop.
Save Kaki-In/76b2c32016c4f9a56093218969a0485b to your computer and use it in GitHub Desktop.
Tkinter
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)
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