# TkTeXCad
# (C) Copyright 2001 Hilmar Straube; dieses Programm untersteht der GPL.
# Keinerlei Garantie auf was auch immer, was mit diesem Programm zu tun hat.
from Tkinter import *
import math
from internat import * # Sprache und Konfiguration auch hier benutzen
from config import *
from globalpars import *
fakt = FAKT
CLIPBOARDNAME = None # gesichert als Name des Clipboard
def notifyfaktchange(newfakt):
global fakt
fakt = newfakt
class diameter: # Sucht einen passenden Durchmesser zu gegebenem
def getd(self, d, as):
index = 0
if as:
pd = POSSIBLEDIAMETERSAS
else:
pd = POSSIBLEDIAMETERS
while pd[index] < d:
index += 1
if index >= len(pd) - 1:
return pd[index]
if index == 0: # index - 1 gibt �rger!
return pd[0]
if abs(pd[index] - d) < abs(pd[index - 1] - d):
return pd[index]
else:
return pd[index - 1]
diameterer = diameter()
class angle: # Klasse zum Bestimmen eines besten LaTeX-Paares zu einem gegebenen Winkel
def __init__(self, set):
self.erg = []
for dxdy in set:
if dxdy != (0, 1):
m = dxdy[1]/float(dxdy[0])
alpha = math.atan(m)
self.erg.append([alpha, dxdy])
else:
self.erg.append([math.pi/2, dxdy])
def getdxdy(self, alpha): # Winkel im Bereich [0; pi/2]
dev = -1
index = -1
z = 0
for el in self.erg:
aktudev = abs(el[0] - alpha)
if aktudev < dev or index == -1:
index = z
dev = aktudev
z += 1
return self.erg[index]
class picel:
# Basisklasse f�r alle Bildelemente; jedes muss sich aus Parametern erzeugen lassen,
# Sich selbst zeichnen, f�r sich in uids alle dabei benutzten ids sichern und diese an den Aufrufer zur�ckliefern.
# Grund: picel1 ist in putter1 ist in picture1 ist in putter2 ist in picture2 ist in Oberfl�che
# Oberfl�cher liefert id, und picture kann sagen, dass diese in putter2, in ... -> R�ckschluss
def __init__(self):
self.uids = []
def undrawhook(self):
pass
def draw(self): # WIRD NICHT AUFGERUFEN! Nur zur Hilfestellung.
self.uids = []
return self.uids
class pcs: # Klasse zur Verwaltung aller Teilbildklassen und ihrer Namen
def __init__(self):
self.pictures = []
self.names = []
def append(self, item):
self.pictures.append(item)
self.names.append(item.name)
def __delitem__(self, nr):
del self.pictures[nr]
del self.names[nr]
def newname(self, name):
return not(name in self.names)
def changename(self, oldname, newname):
try:
index = self.names.index(oldname)
self.names[index] = newname
self.pictures[index].name = newname
return 1
except ValueError:
return 0
def plboundingbox(putterlist):
"Bestimmt Extremausma�e der enthaltenen Elemente und setzt dann die eigenen entsprechend"
x1 = x2 = y1 = y2 = None # Umschlie�endes Rechteck feststellen
for el in putterlist:
dx1, dy1, dx2, dy2 = el.dxdy()
if x1 > dx1 or x1 == None:
x1 = dx1
if x2 < dx2 or x2 == None:
x2 = dx2
if y1 > dy1 or y1 == None:
y1 = dy1
if y2 < dy2 or y2 == None:
y2 = dy2
return (x1, y1, x2, y2)
class pictureEx(picelEx):
pass
class pictureclass:
def dumptex(self):
sf = '% ' + PROGNAME + GENMARK[LANG]
sf += '\\begin{picture}' + `(self.xsize, self.ysize)` + '\n'
for el in self.putters:
sf += el.dumptex() + '\n'
sf += '\\end{picture}'
return sf
def __init__(self, xsize, ysize, name):
if xsize < 0 or ysize < 0:
raise pictureEx
self.xsize = xsize
self.ysize = ysize
self.name = name
self.putters = []
def untk(self): # Alle Elemente, die mit tk beginnen, k�nnen nicht gepickelt werden, werden also vorher gel�scht.
# Diese Prozedur muss f�r jede zu pickelnde Klasse aufgerufen werden
for putter in self.putters:
keys = putter.__dict__.keys()
for key in keys:
if key[:2] == 'tk':
del putter.__dict__[key]
keys = putter.pe.__dict__.keys()
for key in keys:
if key[:2] == 'tk':
del putter.pe.__dict__[key]
def getallblocks(self, ofpc, root = None):
# Die Methode liefert alle Klassen, deren Instanzen in dem Baum von self ofpc enthalten.
if root == None: # Baumausgang sichern
root = self
blocked = []
for putter in self.putters: # Alle Putter der Klasse durchlaufen
if putter.pe.__class__ == picture:
# Dieser putter ist ein Bild.
if putter.pe.pc == ofpc: # Ist Putter picture und Instanz der gefragten Klasse
bc = putter.pe.bc
if bc not in blocked: # ... h�nge alle noch nicht blockierten Klassen an
blocked.append(bc)
erg = root.getallblocks(bc) # Geh den Baum nochmal durch: Alle Klassen, die enthaltende enthalten sind ebenfalls zu blockieren.
for el in erg: # Dopplungen vermeiden
if el not in blocked:
blocked.append(el)
else:
# Der Baum spaltet sich auf; das picel im putter ist ein anderes Bild
erg = putter.pe.pc.getallblocks(ofpc, root) # Hereingehen.
for el in erg:
if el not in blocked:
blocked.append(el)
return blocked # R�ckgabe
def adjustdxdy(self): # Minimale Abmessungen bestimmen und benutzen; nicht wenn kein Putter da ist.
if self.putters != []:
x1, y1, x2, y2 = plboundingbox(self.putters)
for z in range(len(self.putters)):
self.putters[z].x -= x1
self.putters[z].y -= y1
self.xsize = x2 - x1
self.ysize = y2 - y1
class picture(picel):
clickdepth = 1
def dumptex(self):
return '\\setlength{\\unitlength}{' + `self.scale`+ '\\unitlength}\\input{' + self.pc.name + '}'
def __init__(self, pc, scale, blockedclasses):
# letzter Parameter: Liste der Klassen, in deren Instanzen das neue Bild enthalten ist
self.pc = pc
self.putters = self.pc.putters
self.bc = blockedclasses
self.scale = scale
def dxdy(self, x, y):
return x, y, x + self.pc.xsize, x + self.pc.ysize
def copy(self):
return picture(self.pc, self.scale, self.bc)
def changecb(self, event = None):
try:
self.scale = self.tkscale.get() / 100.0
self.tkcallredraw()
except ValueError:
self.tkscale.set(self.scale)
def change(self, frame, tkcallredraw):
self.tkcallredraw = tkcallredraw
Label(frame, text = PICTURELABELCLASS[LANG] + self.pc.name).grid(row = 0, columnspan = 2)
Label(frame, text = PICTURELABELSCALE[LANG]).grid(row = 1, column = 0)
self.tkscale = DoubleVar()
self.tkscale.set(self.scale * 100)
e = Entry(frame, textvariable = self.tkscale)
e.grid(row = 1, column = 1, sticky = E + W)
e.bind("<Return>", self.changecb)
def doscale(self, scale):
self.scale *= scale
def draw(self, x, y, maxy, canvas, scale = 1):
self.uids = []
for putter in self.putters:
self.uids += putter.draw(x / self.scale / scale, y / self.scale / scale, maxy / self.scale / scale, canvas, self.scale * scale)
# Im neuen System ist f�r den Aufgerufenen x,y und maxy entsprechend gr��er
return self.uids
def getputter(self, uid):
"Nur f�r oberstes picture; funktioniert nat. auch mit multiputters; 0 f�r Misserfolg"
for putter in self.putters:
if uid in putter.uids:
return putter
return 0
def undraw(self, canvas):
"NUR F�R OBERSTES (angezeigtes) PICTURE, ruft alle putter.undraws auf"
self.uids = []
for putter in self.putters:
putter.undraw(canvas)
class circleEx(picelEx):
pass
class circle(picel):
clickdepth = 2
def dumptex(self):
if self.bold:
sf = '\\thicklines'
else:
sf = '\\thinlines'
sf += '\\circle'
if self.filled:
sf += '*'
sf += '{' + `self.d` + '}'
return sf
def __init__(self, d, filled, bold = 0):
picel.__init__(self)
if filled:
if d not in POSSIBLEDIAMETERSAS:
raise circleEx
else:
if d not in POSSIBLEDIAMETERS:
raise circleEx
self.d = float(d) # LaTeX erwartet Durchmesser!
self.filled = filled
self.bold = bold
self.changed = 0
def dxdy(self, x, y):
d = self.d
return x - d/2, y - d/2, x + d/2, y + d/2
def savestate(self):
self.state = self.d, self.filled, self.bold
def cancel(self):
self.d, self.filled, self.bold = self.state
def minfo(self, ox, oy, x, y): # Rechnet mit zweiten Punkt von Benutzeroberfl�che die neue Neigung aus
dx = x - ox # Relative Position
dy = y - oy
e = 2 * (dx**2 + dy**2)**0.5 # Entfernung bestimmen
self.d = float(diameterer.getd(e, self.filled)) # damit d/2 nicht Durchmesser 1 und 2 ... gleich macht!
def changecb(self):
self.changed = 1
self.tkcallredraw()
def change(self, frame, tkcallredraw):
self.tkcallredraw = tkcallredraw
Label(frame, text = "\circle; \circle*").grid()
self.tkfilled = IntVar()
Checkbutton(frame, text = CIRCLELABELFILLED[LANG], variable = self.tkfilled, command = self.changecb).grid()
self.tkfilled.set(self.filled)
def copy(self):
return circle(self.d, self.filled, self.bold)
def doscale(self, scale):
d = self.d * scale
self.d = diameterer.getd(d, self.filled)
def draw(self, x, y, maxy, canvas, scale = 1):
if self.changed:
self.bold = self.tkbold.get()
self.filled = self.tkfilled.get()
self.d = float(diameterer.getd(self.d, self.filled)) # mgl. Durchmesser von gef�lltem und ungef�lltem Kreis sind verschieden
self.changed = 0
self.uids = []
d = self.d * scale
d = diameterer.getd(d, self.filled)
r = d/2.0
boundbox = (fakt * (x - r),
fakt * (maxy - y - r),
fakt * (x + r),
fakt * (maxy - y + r))
if self.bold:
wi = THICKWIDTH * fakt
else:
wi = THINWIDTH * fakt
self.uids = [canvas.create_oval(boundbox, width = wi)]
if self.filled:
canvas.itemconfigure(self.uids[0], fill = "BLACK")
return self.uids
class lineEx(picelEx):
pass
class line(picel):
clickdepth = 2
def dumptex(self):
if self.bold:
sf = '\\thicklines'
else:
sf = '\\thinlines'
if self.vec:
sf += '\\vector'
else:
sf += '\\line'
sf += `(self.dx, self.dy)` # :-)
sf += '{' + `self.len`+ '}'
return sf
def __init__(self, dx, dy, len, vec, bold = 0):
picel.__init__(self)
if not (abs(dx), abs(dy)) in POSSIBLEDXDY:
raise lineEx
self.dx = dx
self.dy = dy
self.len = len
self.vec = vec
self.bold = bold
self.changed = 0 # f�r die Konfiguration via change
def dxdy(self, x, y):
absdxdy = abs(self.dx), abs(self.dy)
if absdxdy == (0, 1): # L�ngen im ersten Quadranten bestimmen
dx = 0
dy = self.len
else:
dx = self.len
m = absdxdy[1]/float(absdxdy[0])
dy = m * dx
if self.dx < 0:
dx = -dx
if self.dy < 0:
dy = -dy
x2 = x + dx # zweite Punkte bilden
y2 = y + dy
return min(x, x2), min(y, y2), max(x, x2), max(y, y2) # richtig geordnete R�ckgabe
def savestate(self):
self.state = self.dx, self.dy, self.len, self.vec, self.bold
def cancel(self):
self.dx, self.dy, self.len, self.vec, self.bold = self.state
def minfo(self, ox, oy, x, y): # Rechnet mit zweiten Punkt von Benutzeroberfl�che die neue Neigung aus
dx = x - ox # Relative Position
dy = y - oy
if dx == 0:
alpha = math.pi/2
else:
m = dy/float(dx)
alpha = abs(math.atan(m)) # Winkel des zweiten Punktes zum ersten in den Bereich [0; pi/2] pressen.
if self.vec:
self.dx, self.dy = vecangler.getdxdy(alpha)[1] # n�chsten LaTeX-Neigungswinkel errechnen
else:
self.dx, self.dy = angler.getdxdy(alpha)[1] # n�chsten LaTeX-Neigungswinkel errechnen
self.tksel = IntVar()
for z in range(9):
Radiobutton(frame, text = self.BUTTEXTS[z], variable = self.tksel, value = z, command = self.changecb).pack(anchor = W)
self.tksel.set(self.at)
def copy(self):
return oval(self.dx, self.dy, self.at, self.bold)
def doscale(self, scale):
self.dx *= scale
self.dy *= scale
def draw(self, x, y, maxy, canvas, scale = 1):
dx = self.dx * scale
dy = self.dy * scale
if self.changed:
self.bold = self.tkbold.get()
self.at = self.tksel.get()
self.selector = self.atlrbTrTlBlBr2selector(self.at)
self.changed = 0
self.uids = []
# K�rzere Seite bestimmen
if dx < dy:
lmin = dx
else:
lmin = dy
d = 0 # Daraus kann LaTeX machen was es will. Wohl nicht viel.
# Radius bestimmen
for i in range(len(POSSIBLEDIAMETERS) - 1, -1, -1):
if POSSIBLEDIAMETERS[i] <= lmin:
d = POSSIBLEDIAMETERS[i]
break
d = float(d)
# Eckpunkte bestimmen
mx = x
my = y
x -= dx/2
y -= dy/2
x2 = x + dx
y2 = y + dy
# Kreisb�gen zeichnen
if self.bold:
wi = THICKWIDTH * fakt
else:
wi = THINWIDTH * fakt
class simpletext(picel):
# Einfacher Textstring
clickdepth = 1
def dumptex(self):
return self.text
def __init__(self, text):
picel.__init__(self)
self.text = text
def dxdy(self, x, y): # Keine Ahnung, wie gro� der Text wird, ich m�sste ja LaTeX neu schreiben!
return x, y, x, y
def copy(self):
return simpletext(self.text)
def changecb(self, a1, a2, a3):
self.text = self.tktext.get() # Muss eine Zeichenkette sein
def change(self, frame, tkcallredraw):
self.tkcallredraw = tkcallredraw
Label(frame, text = "<einfacher Text>").grid()
class textbox(picel):
# VORSICHT: textbox erlaubt negative dx, dy!
clickdepth = 2
def dumptex(self, x, y, dx, dy, n): # und daher wird dieser hier nur mit allen Informationen von multiputer aufgefordert, dessen Job zu �bernehmen.
sdx = self.dx
sdy = self.dy
if sdx < 0:
sdx = -sdx
x -= sdx
if sdy < 0:
sdy = -sdy
y -= sdy
if n != 1:
sf = '\\multiput' + `(x, y)` + `(dx, dy)` + '{' + `n` + '}{'
else:
sf = '\\put' + `(x, y)` + '{'
if self.bold:
sf += '\\thicklines'
else:
sf += '\\thinlines'
# Auswahl des Kommandos
if self.dash < 0:
sf += '\\makebox'
elif self.dash == 0:
sf += '\\framebox'
elif self.dash > 0:
sf += '\\dashbox{' + `self.dash` + '}'
sf += `(sdx, sdy)`
# Poitionierungsangabe
if self.lmr != 1 or self.bmt != 1:
sf += '['
sf += ['l', '', 'r'][self.lmr]
sf += ['b', '', 't'][self.bmt]
sf += ']'
sf += '{' + self.text
sf += '}}' # von der box und vom putter
return sf
def __init__(self, dx, dy, lmr, bmt, dash, text, bold = 0):
"lmr -> 0: l, 1: m, 2: r ...; dash = 0 -> \framebox, sonst Strichl�nge; negativ -> kein Rahmen"
self.lmr = lmr
self.bmt = bmt
if not (lmr in range(3) and bmt in range(3)):
raise textboxEx
picel.__init__(self)
self.text = text
self.dx = dx
self.dy = dy
self.dash = dash
self.bold = bold
self.changed = 0
def copy(self):
return textbox(self.dx, self.dy, self.lmr, self.bmt, self.dash, self.text, self.bold)
def dxdy(self, x, y):
if self.dx < 0:
x1 = x + self.dx
x2 = x
else:
x1 = x
x2 = x + self.dx
if self.dy < 0:
y1 = y + self.dy
y2 = y
else:
y1 = y
y2 = y + self.dy
return x1, y1, x2, y2
def savestate(self):
self.state = self.lmr, self.bmt, self.text, self.dx, self.dy, self.dash, self.bold
def cancel(self):
self.lmr, self.bmt, self.text, self.dx, self.dy, self.dash, self.bold = self.state
def minfo(self, ox, oy, x, y):
self.dx = x - ox
self.dy = y - oy
def changecb2(self, a1, a2, a3):
self.text = self.tktext.get()
self.changed = 1
self.tkcallredraw()
def changecb(self):
self.changed = 1
self.tkcallredraw()
def changecbworkaround(self, event):
self.changecb()
def change(self, frame, tkcallredraw):
self.tkcallredraw = tkcallredraw
Label(frame, text = "\dashbox; \framebox; \makebox").pack()
class sizechanger: # �ndert dazu noch den Namen
def __init__(self, root, sizex, sizey, name, names, changedcb):
# Alte Werte; names: Liste der benutzten Namen; changedcb(x, y, name)
self.sizex = sizex
self.sizey = sizey
self.name = name
self.names = names
self.changedcb = changedcb
class bb(picel):
"Dicker Strich in Funktion eines ausgef�llten Rechtecks; Skalierungsfehler: Breite wird nie angepasst; richtige Darstellung (hoffentlich)"
clickdepth = 2
def dumptex(self, x, y, dx, dy, n): # Braucht auch die Informationen vom Putter, denn putter kommt links in der Mitte.
py = y + self.dy / 2.0
if self.dx < 0:
px = self.dx + x
else:
px = x
len = abs(self.dx)
thickness = abs(self.dy)
if n != 1:
sf = '\\multiput' + `(px, py)` + `(dx, dy)` + '{' + `n` + '}{'
else:
sf = '\\put' + `(px, py)` + '{'
sf += '\\linethickness{' + `thickness`+ '\unitlength}'
sf += '\\line(1, 0){' + `len` + '} }'
return sf
def __init__(self, dx, dy):
picel.__init__(self)
self.dx = dx
self.dy = dy
def dxdy(self, x, y):
if self.dx < 0:
x1 = x + self.dx
x2 = x
else:
x1 = x
x2 = x + self.dx
if self.dy < 0:
y1 = y + self.dy
y2 = y
else:
y1 = y
y2 = y + self.dy
return x1, y1, x2, y2
def savestate(self):
self.state = self.dx, self.dy
def cancel(self):
self.dx, self.dy = self.state
def minfo(self, ox, oy, x, y):
self.dx = x - ox # Relative Position
self.dy = y - oy
def change(self, frame, tkcallredraw):
Label(frame, text = "\\textwidth\\line").pack()
def copy(self):
return bb(self.dx, self.dy)
def doscale(self, scale):
self.dx *= scale
self.dy *= scale
def draw(self, x, y, maxy, canvas, scale = 1):
boundbox = (fakt * (x),
fakt * (maxy - y),
fakt * (x + self.dx * scale),
fakt * (maxy - (y + self.dy * scale)))
return [canvas.create_rectangle(boundbox, fill = "BLACK")]
class objignorestatus:
def status(sf):
pass
class tkfiledummy: # simuliert die gebrauchte Funktionalit�t
def __init__(self):
self.size = (10, 10)
def resize(self, newsizetup):
self.size = newsizetup
class eps(picel):
clickdepth = 2
def dumptex(self):
sf = "\includegraphics[width = " + `self.dx` + '\unitlength, height = ' + `self.dy` + "\unitlength]{" + self.filename + "}"
return sf
def loadfile(self, filename, statusobj):
print "loadfile called"
self.filename = filename
try:
self.tkfile = Image.open(filename)
except:
statusobj.status(EPSCANNOTOPEN[LANG])
self.tkfile = tkfiledummy()
return 0
if self.tkfile.format != 'EPS':
statusobj.status(EPSNOEPS[LANG])
self.tkfile = tkfiledummy()
return 0
return 1
def __init__(self, dx = 0, dy = 0, keepaspect = 1):
picel.__init__(self)
self.tkfile = tkfiledummy() # NOCH NICHT BENUTZEN!!! Loadfile aufrufen!
self.dx = dx
self.dy = dy
self.keepaspect = keepaspect
self.tkimagelist = [] # Photoimages brauchen zus�tzliche Referenz. Bei putter.undraw() wird imagelist geleert.
self.changed = 0
def dxdy(self, x, y):
return x, y, x + self.dx, y + self.dy
def savestate(self):
self.state = self.tkfile, self.dx, self.dy
def cancel(self):
self.tkfile, self.dx, self.dy = self.state
def minfo(self, ox, oy, x, y):
aspect = self.tkfile.size[0] / float(self.tkfile.size[1])
dx = x - ox # Relative Position
dy = y - oy
if dx < 0:
dx = 0
if dy < 0:
dy = 0
if self.keepaspect: # Verh�ltnis L�nge/H�he beibehalten
dx = aspect * dy
self.dx, self.dy = dx, dy
def changecb(self):
self.changed = 1
self.tkcallredraw()
def change(self, frame, tkcallredraw):
self.tkcallredraw = tkcallredraw
Label(frame, text = "\epsfig").pack()
Label(frame, text = EPSINSERTNOTE[LANG]).pack()
self.tkkeepaspect = IntVar()
self.tkkeepaspect.set(self.keepaspect)
Checkbutton(frame, text = EPSASPECT[LANG], variable = self.tkkeepaspect, command = self.changecb).pack()
def copy(self):
neweps = eps(self.dx, self.dy, self.keepaspect)
neweps.loadfile(self.filename, objignorestatus())
return neweps
def doscale(self, scale):
self.dx *= scale
self.dy *= scale
def undrawhook(self):
self.tkimagelist = []
def draw(self, x, y, maxy, canvas, scale = 1):
if self.changed:
self.keepaspect = self.tkkeepaspect.get()
if self.keepaspect: # verzerrtes Bild richten, wenn Benutzer ausgeschalteten aspect wieder eingeschaltet hat.
self.minfo(0, 0, self.dx, self.dy)
width = self.dx * fakt * scale
height = self.dy * fakt * scale
if self.tkfile.__class__ != tkfiledummy:
im = ImageTk.PhotoImage(image = self.tkfile.resize((width, height)) )
self.tkimagelist.append(im)
uid = canvas.create_image(fakt * x + width / 2, fakt * maxy - (fakt * y + height / 2), image = im)
return [uid]
else:
boundbox = (fakt * (x),
fakt * (maxy - y),
fakt * (x + self.dx * scale),
fakt * (maxy - (y + self.dy * scale)))
return [canvas.create_rectangle(boundbox),
canvas.create_line(boundbox)]