diff options
Diffstat (limited to 'graphics/tktexcad/picels.py')
-rw-r--r-- | graphics/tktexcad/picels.py | 1299 |
1 files changed, 1299 insertions, 0 deletions
diff --git a/graphics/tktexcad/picels.py b/graphics/tktexcad/picels.py new file mode 100644 index 0000000000..f8846cc1e0 --- /dev/null +++ b/graphics/tktexcad/picels.py @@ -0,0 +1,1299 @@ +# 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] + +angler = angle(POSSIBLEDXDY) # Für allg. Benutzung +vecangler = angle(VECDXDY) # Für Pfeile + +class picelEx(Exception): + pass + +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.tkbold = IntVar() + Checkbutton(frame, text = PICELLABELBOLD[LANG], variable = self.tkbold, command = self.changecb).grid() + self.tkbold.set(self.bold) + + 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 + + if dx < 0: + self.dx = -self.dx + if dy < 0: + self.dy = -self.dy + if self.dx == 0: + self.len = abs(dy) + else: + self.len = abs(dx) + def vecchangecb(self): + if self.dx == 0: + self.changecb() + return + angle = math.atan(abs(self.dy / float(self.dx))) + ndx, ndy = vecangler.getdxdy(angle)[1] + if self.dx < 0: + ndx = -ndx + if self.dy < 0: + ndy = -ndx + self.dx, self.dy = ndx, ndy + self.changecb() + def changecb(self): + self.changed = 1 + self.tkcallredraw() + def change(self, frame, tkcallredraw): + self.tkcallredraw = tkcallredraw + Label(frame, text = "\line; \vector").grid(columnspan = 2) + + self.tkbold = IntVar() + Checkbutton(frame, text = PICELLABELBOLD[LANG], variable = self.tkbold, command = self.changecb).grid(row = 1, columnspan = 2) + self.tkbold.set(self.bold) + + self.tkvec = IntVar() + Radiobutton(frame, text = LINELABELLINE[LANG], variable = self.tkvec, value = 0, command = self.changecb).grid(row = 2, column = 0) + Radiobutton(frame, text = LINELABELVEC[LANG], variable = self.tkvec, value = 1, command = self.vecchangecb).grid(row = 2, column = 1) + self.tkvec.set(self.vec) + + def copy(self): + return line(self.dx, self.dy, self.len, self.vec, self.bold) + def doscale(self, scale): + self.len *= scale + def draw(self, x, y, maxy, canvas, scale = 1): + len = self.len * scale + if self.changed: + self.vec = self.tkvec.get() + self.bold = self.tkbold.get() + self.changed = 0 + self.uids = [] + # Koordinaten des zweiten Punktes bestimmen + if (abs(self.dx), abs(self.dy)) == (0, 1): # Vertikale Linie + x2 = x + ylen = self.dy/(abs(self.dy)) * len # Vorzeichenbehaftet! + y2 = y + ylen + else: # Alle anderen Liniensorten + # aus x-Projektion der Länge und Neigung den y-Anteil ausrechnen + m = self.dy/float(self.dx) + xlen = self.dx/abs(self.dx) * len # LaTeX-Länge + Vorzeichen -> DeltaX + ylen = m * xlen + x2 = x + xlen + y2 = y + ylen + arrowshape = (ARROWSHAPE[0] * scale * fakt, ARROWSHAPE[1] * scale * fakt, ARROWSHAPE[2] * scale * fakt) + if self.vec: # Pfeil? + ar = LAST + else: + ar = NONE + if self.bold: + wi = THICKWIDTH * fakt + else: + wi = THINWIDTH * fakt + + self.uids = [canvas.create_line(fakt * x, fakt * (maxy - y), fakt * x2, fakt * (maxy - y2), width = wi, arrow = ar, arrowshape = arrowshape)] + return self.uids + +class ovalEx(picelEx): + pass + +class oval(picel): + clickdepth = 2 + BUTTEXTS = (OVALALL[LANG], + OVALT[LANG], + OVALL[LANG], + OVALR[LANG], + OVALB[LANG], + OVALTR[LANG], + OVALTL[LANG], + OVALBL[LANG], + OVALBR[LANG]) + def dumptex(self): + if self.bold: + sf = '\\thicklines' + else: + sf = '\\thinlines' + + selstr = ('', + '[t]', + '[l]', + '[r]', + '[b]', + '[tr]', + '[tl]', + '[bl]', + '[br]')[self.at] + + sf += '\\oval' + `(self.dx, self.dy)` + selstr + + return sf + def atlrbTrTlBlBr2selector(self, atlrbTrTlBlBr): + if atlrbTrTlBlBr == 0: # A + return 1, 1, 1, 1 + elif atlrbTrTlBlBr == 1: # T + return 1, 1, 0, 0 + elif atlrbTrTlBlBr == 2: # L + return 0, 1, 1, 0 + elif atlrbTrTlBlBr == 3: # R + return 1, 0, 0, 1 + elif atlrbTrTlBlBr == 4: # B + return 0, 0, 1, 1 + elif atlrbTrTlBlBr == 5: # TR + return 1, 0, 0, 0 + elif atlrbTrTlBlBr == 6: # TL + return 0, 1, 0, 0 + elif atlrbTrTlBlBr == 7: # BL + return 0, 0, 1, 0 + elif atlrbTrTlBlBr == 8: # BR + return 0, 0, 0, 1 + def __init__(self, dx, dy, atlrbTrTlBlBr = 0, bold = 0): + # atlrb - alles = 0, top = 1, left = 2 ... + if atlrbTrTlBlBr not in range(9): + raise ovalEx + picel.__init__(self) + self.dx = dx + self.dy = dy + self.selector = self.atlrbTrTlBlBr2selector(atlrbTrTlBlBr) + self.at = atlrbTrTlBlBr + self.bold = bold + self.changed = 0 + def dxdy(self, x, y): + dxh = self.dx / 2.0 + dyh = self.dy / 2.0 + if self.at in [4, 8, 5]: #[R, BR, TR]: + x1 = x + else: + x1 = x - dxh + if self.at in [1, 6, 5]: #[T, TL, TR]: + y1 = y + else: + y1 = y - dyh + + if self.at in [3, 6, 7]: #[L, TL, BL]: + x2 = x + else: + x2 = x + dxh + + if self.at in [2, 7, 8]: #[B, BL, BR]: + y2 = y + else: + y2 = y + dyh + return x1, y1, x2, y2 + def savestate(self): + self.state = self.dx, self.dy, self.selector, self.at, self.bold + def cancel(self): + self.dx, self.dy, self.selector, self.at, self.bold = self.state + def minfo(self, ox, oy, x, y): + self.dx = abs(ox - x) * 2 + self.dy = abs(oy - y) * 2 + def changecb(self): + self.changed = 1 + self.tkcallredraw() + def change(self, frame, tkcallredraw): + self.tkcallredraw = tkcallredraw + Label(frame, text = "\oval").pack() + + self.tkbold = IntVar() + Checkbutton(frame, text = PICELLABELBOLD[LANG], variable = self.tkbold, command = self.changecb).pack() + self.tkbold.set(self.bold) + + 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 + + # rechts oben + if self.selector[0]: + self.uids.append( + canvas.create_line( + fakt * mx, fakt * (maxy - y2), fakt * (x2 - d/2), fakt * (maxy - y2), width = wi)) + self.uids.append( + canvas.create_line( + fakt * x2, fakt * (maxy - my), fakt * x2, fakt * (maxy - (y2 - d/2)), width = wi)) + bbox = fakt * (x2 - d), fakt * (maxy - (y2 - d)), fakt * x2, fakt * (maxy - y2) + self.uids.append( + canvas.create_arc(bbox, start = 0, extent = 90, style = ARC, width = wi)) + # Links oben + if self.selector[1]: + self.uids.append( + canvas.create_line( + fakt * (x + d/2), fakt * (maxy - y2), fakt * mx, fakt * (maxy - y2), width = wi)) + self.uids.append( + canvas.create_line( + fakt * x, fakt * (maxy - my), fakt * x, fakt * (maxy - (y2 - d/2)), width = wi)) + bbox = fakt * x, fakt * (maxy - (y2 - d)), fakt * (x + d), fakt * (maxy - y2) + self.uids.append( + canvas.create_arc(bbox, start = 90, extent = 90, style = ARC, width = wi)) + # Links unten + if self.selector[2]: + self.uids.append( + canvas.create_line( + fakt * x, fakt * (maxy - (y + d/2)), fakt * x, fakt * (maxy - my), width = wi)) + self.uids.append( + canvas.create_line( + fakt * (x + d/2), fakt * (maxy - y), fakt * mx, fakt * (maxy - y), width = wi)) + bbox = fakt * x, fakt * (maxy - y), fakt * (x + d), fakt * (maxy - (y + d)) + self.uids.append( + canvas.create_arc(bbox, start = 180, extent = 90, style = ARC, width = wi)) + # Rechts unten + if self.selector[3]: + self.uids.append( + canvas.create_line( + fakt * mx, fakt * (maxy - y), fakt * (x2 - d/2), fakt * (maxy - y), width = wi)) + self.uids.append( + canvas.create_line( + fakt * x2, fakt * (maxy - (y + d/2)), fakt * x2, fakt * (maxy - my), width = wi)) + bbox = fakt * (x2 - d), fakt * (maxy - y), fakt * x2, fakt * (maxy - (y + d)) + self.uids.append( + canvas.create_arc(bbox, start = 270, extent = 90, style = ARC, width = wi)) + return self.uids + +class bezierEx(picelEx): + pass + +class bezier(picel): + clickdepth = 3 + def dumptex(self): + if self.bold: + sf = '\\thicklines' + else: + sf = '\\thinlines' + sf += '\\qbezier' + `(self.xa, self.ya)` + `(self.xb, self.yb)` + `(self.xc, self.yc)` + return sf + def __init__(self, xa, ya, xb, yb, xc, yc, bold = 0): + picel.__init__(self) + self.xa = xa + self.ya = ya + self.xb = xb + self.yb = yb + self.xc = xc + self.yc = yc + self.bold = bold + self.changed = 0 + def dxdy(self, x, y): + x1 = min(self.xa, self.xb, self.xc) + x2 = max(self.xa, self.xb, self.xc) + y1 = min(self.ya, self.yb, self.yc) + y2 = max(self.ya, self.yb, self.yc) + return x1 + x, y1 + y, x2 + x, y2 + y + def copy(self): + return bezier(self.xa, self.ya, self.xb, self.yb, self.xc, self.yc, self.bold) + def savestate(self): + self.state = self.xa, self.ya, self.xb, self.yb, self.xc, self.yc + def cancel(self): + self.xa, self.ya, self.xb, self.yb, self.xc, self.yc = self.state + def minfo(self, ox, oy, x, y): + dx = x - ox + dy = y - oy + self.xc = dx + self.yc = dy + def minfo2(self, ox, oy, x, y): + dx = x - ox + dy = y - oy + self.xb = dx + self.yb = dy + def changecb(self): + self.changed = 1 + self.tkcallredraw() + def change(self, frame, tkcallredraw): + self.tkcallredraw = tkcallredraw + Label(frame, text = "\qbezier").grid() + + self.tkbold = IntVar() + Checkbutton(frame, text = PICELLABELBOLD[LANG], variable = self.tkbold, command = self.changecb).grid() + self.tkbold.set(self.bold) + def doscale(self, scale): + self.xa *= scale + self.ya *= scale + self.xb *= scale + self.yb *= scale + self.xc *= scale + self.yc *= scale + def draw(self, x, y, maxy, canvas, scale = 1): + xa = self.xa * scale + ya = self.ya * scale + xb = self.xb * scale + yb = self.yb * scale + xc = self.xc * scale + yc = self.yc * scale + if self.changed: + self.bold = self.tkbold.get() + self.changed = 0 + self.uids = [] + if self.bold: + wi = THICKWIDTH * fakt + else: + wi = THINWIDTH * fakt + uid = canvas.create_line(fakt * (x + xa), + fakt * (maxy - (y + ya)), + fakt * (x + xb), + fakt * (maxy - (y + yb)), + fakt * (x + xc), + fakt * (maxy - (y + yc)), + smooth = 1, + width = wi + ) + self.uids = [uid] + return self.uids + +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() + + self.tktext = StringVar() + Entry(frame, textvariable = self.tktext).grid() + self.tktext.set(self.text) + self.tktext.trace("w", self.changecb) + def doscale(self, scale): + pass + def draw(self, x, y, maxy, canvas, scale = 1): + self.uids = [] + uid = canvas.create_text(fakt * x, fakt * (maxy - y), anchor = S + W, text = '*TEXT*') + + self.uids = [uid] + return self.uids + +class textboxEx(picelEx): + pass + +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() + + # Fett + self.tkbold = IntVar() + Checkbutton(frame, text = PICELLABELBOLD[LANG], variable = self.tkbold, command = self.changecb).pack() + self.tkbold.set(self.bold) + + # Text + self.tktext = StringVar() + textframe = Frame(frame, borderwidth = 1, relief = RAISED) + textframe.pack(fill = BOTH, expand = 1) + Label(textframe, text = PICELLABELTEXT[LANG]).pack(side = LEFT) + Entry(textframe, textvariable = self.tktext).pack(side = RIGHT, fill = X, expand = 1) + self.tktext.set(self.text) + self.tktext.trace("w", self.changecb2) + + # Rahmenart + + self.tkstyle = IntVar() + borderframe = Frame(frame, borderwidth = 1, relief = RAISED) + borderframe.pack(fill = BOTH, expand = 1) + Label(borderframe, text = TEXTBOXLABELBORDER[LANG]).grid(row = 0, column = 0, columnspan = 3) + Radiobutton(borderframe, text = TEXTBOXLABELNOBORDER[LANG], variable = self.tkstyle, value = -1, command = self.changecb).grid(row = 1, column = 0) + Radiobutton(borderframe, text = TEXTBOXLABELFULLBORDER[LANG], variable = self.tkstyle, value = 0, command = self.changecb).grid(row = 1, column = 1) + Radiobutton(borderframe, text = TEXTBOXLABELDASHEDBORDER[LANG], variable = self.tkstyle, value = 1, command = self.changecb).grid(row = 1, column = 2) + if self.dash < 0: + self.tkstyle.set(-1) + elif self.dash == 0: + self.tkstyle.set(0) + else: + self.tkstyle.set(1) + + self.tkdashlen = DoubleVar() + borderframe2 = Frame(borderframe) + borderframe2.grid(sticky = N+E+S+W, columnspan = 3) + Label(borderframe2, text = TEXTBOXLABELDASHLEN[LANG]).pack(side = LEFT) + EDashLen = Entry(borderframe2, textvariable = self.tkdashlen) + EDashLen.pack(side = RIGHT, fill = X, expand = 1) + EDashLen.bind('<Return>', self.changecbworkaround) + if self.tkstyle.get() == 1: # Inhalt des Strichlängenfeldes bestimmen + self.tkdashlen.set(self.dash) + else: + self.tkdashlen.set(DEFAULTDASHLEN) + + # Position + self.tkpos = IntVar() + posframe = Frame(frame, borderwidth = 1, relief = RAISED) + posframe.pack(fill = BOTH, expand = 1) + Label(posframe, text = TEXTBOXLABELPOS[LANG]).grid(row = 0, columnspan = 3) + Radiobutton(posframe, variable = self.tkpos, value = 00, command = self.changecb).grid(row = 3, column = 0) + Radiobutton(posframe, variable = self.tkpos, value = 01, command = self.changecb).grid(row = 3, column = 1) + Radiobutton(posframe, variable = self.tkpos, value = 02, command = self.changecb).grid(row = 3, column = 2) + + Radiobutton(posframe, variable = self.tkpos, value = 10, command = self.changecb).grid(row = 2, column = 0) + Radiobutton(posframe, variable = self.tkpos, value = 11, command = self.changecb).grid(row = 2, column = 1) + Radiobutton(posframe, variable = self.tkpos, value = 12, command = self.changecb).grid(row = 2, column = 2) + + Radiobutton(posframe, variable = self.tkpos, value = 20, command = self.changecb).grid(row = 1, column = 0) + Radiobutton(posframe, variable = self.tkpos, value = 21, command = self.changecb).grid(row = 1, column = 1) + Radiobutton(posframe, variable = self.tkpos, value = 22, command = self.changecb).grid(row = 1, column = 2) + self.tkpos.set(self.bmt * 10 + self.lmr) + # bmt, lmr -> init + # Beide zusammen werden als value des Knopfes gesichert, indem der vertikale Anteil mit 10 + # multipliziert eingeht. + def dashline(self, x1, x2, y, dash, h, wi, canvas): # Hilfsfunktion + uids = [] + if x2 < x1: # ggf. Parameter vertauschen + dummy = x2 + x2 = x1 + x1 = dummy + + anz = (x2 - x1) / float(dash) # Anzahl der kompletten Striche + rest = (anz - int(anz)) * dash # Länge des Reststückes + z = -1 + for z in range(anz): + if z % 2 == 0: # Nur die Hälfte der Striche anzeigen + if h: + newuid = canvas.create_line(x1 + z * dash, y, x1 + (z + 1) * dash, y, width = wi) + else: + newuid = canvas.create_line(y, x1 + z * dash, y, x1 + (z + 1) * dash, width = wi) + uids.append(newuid) + z += 1 + if z % 2 == 0: + if h: + newuid = canvas.create_line(x1 + z * dash, y, x2, y, width = wi) + else: + newuid = canvas.create_line(y, x1 + z * dash, y, x2, width = wi) + uids.append(newuid) + return uids + def drawdash(self, x, y, maxy, canvas, scale = 1): + "Hilfsfunktion für .draw: alle \dahslines" + dx = self.dx * scale + dy = self.dy * scale + dash = self.dash * scale + self.uids = [] + if self.bold: + wi = THICKWIDTH * fakt + else: + wi = THINWIDTH * fakt + # Horizontale Linien (h = 1) + self.uids += \ + self.dashline(fakt * x, fakt * (x + dx), fakt * (maxy - y), fakt * dash, 1, wi, canvas) + self.uids += \ + self.dashline(fakt * x, fakt * (x + dx), fakt * (maxy - (y + dy)), fakt * dash, 1, wi, canvas) + # Vertikale Linien (h = 0) + self.uids += \ + self.dashline(fakt * (maxy - y), fakt * (maxy - (y + dy)), fakt * x, fakt * dash, 0, wi, canvas) + self.uids += \ + self.dashline(fakt * (maxy - y), fakt * (maxy - (y + dy)), fakt * (x + dx), fakt * dash, 0, wi, canvas) + def drawfull(self, x, y, maxy, canvas, scale = 1): + "Hilfsfunktion für .draw: alle \makebox sowie \framebox" + dx = self.dx * scale + dy = self.dy * scale + self.uids = [] + if self.bold: + wi = THICKWIDTH * fakt + else: + wi = THINWIDTH * fakt + # Horizontale Linien + if self.dash < 0: + col = MAKEBOXCOL + else: + col = "black" + self.uids.append( + canvas.create_line(fakt * x, fakt * (maxy - y), fakt * (x + dx), fakt * (maxy - y), width = wi, fill = col)) + self.uids.append( + canvas.create_line(fakt * x, fakt * (maxy - (y + dy)), fakt * (x + dx), fakt * (maxy - (y + dy)), width = wi, fill = col)) + # Vertikale Linien + self.uids.append( + canvas.create_line(fakt * x, fakt * (maxy - y), fakt * x, fakt * (maxy - (y + dy)), width = wi, fill = col)) + self.uids.append( + canvas.create_line(fakt * (x + dx), fakt * (maxy - y), fakt * (x + dx), fakt * (maxy - (y + dy)), width = wi, fill = col)) + def doscale(self, scale): + self.dx *= scale + self.dy *= scale + if self.dash > 0: + self.dash *= scale + def draw(self, x, y, maxy, canvas, scale = 1): + if self.changed: + self.bold = self.tkbold.get() + val = self.tkpos.get() + self.bmt = val / 10 + self.lmr = val % 10 + # Siehe Kommentar in change + dashstyle = self.tkstyle.get() + if dashstyle == -1: # Ohne Rahmen + self.dash = -1 + elif dashstyle == 0: # Durchgezogen + self.dash = 0 + else: + try: + if self.tkdashlen.get() <= 0: + raise ValueError + self.dash = self.tkdashlen.get() # gestrichelt + except: + self.tkdashlen.set(self.dash) + self.changed = 0 + # Rand zeichnen + if self.dash <= 0: + self.drawfull(x, y, maxy, canvas, scale) + else: + self.drawdash(x, y, maxy, canvas, scale) + dx = self.dx * scale + dy = self.dy * scale + # Text zeichnen + an = '' + xpos = x + dx/2 + ypos = y + dy/2 + if self.text != '': + if self.bmt == 0: + an += S + if dy < 0: + ypos = y + dy + else: + ypos = y + elif self.bmt == 2: + an += N + if dy < 0: + ypos = y + else: + ypos = y + dy + if self.lmr == 0: + an += W + if dx < 0: + xpos = x + dx + else: + xpos = x + elif self.lmr == 2: + an += E + if dx < 0: + xpos = x + else: + xpos = x + dx + if an == '': + an = CENTER + self.uids.append( + canvas.create_text(fakt * xpos, fakt * (maxy - ypos), text = "*TEXT*", anchor = an)) + return self.uids + +class shortstackEx(picelEx): + pass + +class shortstack(picel): + clickdepth = 1 + def dumptex(self): + sf = '\\shortstack' + sf += ['[l]{', '{', '[r]{'][self.lrc] + sf += self.text + '}' + return sf + def __init__(self, lrc, text): + if lrc not in range(3): + raise shortstackEx + picel.__init__(self) + self.text = text + self.lrc = lrc + self.changed = 0 + def dxdy(self, x, y): + return x, y, x, y # Keine Ahnung betreffend die Größe von self.text + def copy(self): + return shortstack(self.lrc, self.text) + def changecb2(self, a1, a2, a3): + self.text = self.tktext.get() + def changecb(self): + self.changed = 1 + self.tkcallredraw() + def change(self, frame, tkcallredraw): + self.tkcallredraw = tkcallredraw + Label(frame, text = "\\shortstack").grid(columnspan = 2) + self.tktext = StringVar() + Label(frame, text = PICELLABELTEXT[LANG]).grid(row = 1, column = 0) + Entry(frame, textvariable = self.tktext).grid(row = 1, column = 1) + self.tktext.set(self.text) + self.tktext.trace("w", self.changecb2) + + self.tklrc = IntVar() + Radiobutton(frame, text = SHORTSTACKLABELLEFT[LANG], variable = self.tklrc, value = 0, command = self.changecb).grid(sticky = W) + Radiobutton(frame, text = SHORTSTACKLABELCENTERED[LANG], variable = self.tklrc, value = 1, command = self.changecb).grid(sticky = W) + Radiobutton(frame, text = SHORTSTACKLABELRIGHT[LANG], variable = self.tklrc, value = 2, command = self.changecb).grid(sticky = W) + self.tklrc.set(self.lrc) + def doscale(self, scale): + pass + def draw(self, x, y, maxy, canvas, scale = 1): + if self.changed: + self.lrc = self.tklrc.get() + self.changed = 0 + self.uids = [] + if self.lrc == 0: + disptext = SHORTSTACKSHOWLEFT[LANG] + elif self.lrc == 1: + disptext = SHORTSTACKSHOWCENTERED[LANG] + elif self.lrc == 2: + disptext = SHORTSTACKSHOWRIGHT[LANG] + self.uids.append( + canvas.create_text(fakt * x, fakt * (maxy - y), text = disptext, anchor = S + W)) + return self.uids + +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 + + # Fenster erstellen + self.tl = Toplevel(root) + self.tl.title = 'Abmessungen' + + self.tkx = DoubleVar() + self.tky = DoubleVar() + self.tkname = StringVar() + self.tkx.set(sizex) + self.tky.set(sizey) + self.tkname.set(name) + Label(self.tl, text = SIZECHANGEX[LANG]).grid(row = 0, column = 0) + Label(self.tl, text = SIZECHANGEY[LANG]).grid(row = 1, column = 0) + Label(self.tl, text = SIZECHANGENAME[LANG]).grid(row = 2, column = 0) + Entry(self.tl, textvariable = self.tkx).grid(row = 0, column = 1, sticky = E + W) + Entry(self.tl, textvariable = self.tky).grid(row = 1, column = 1, sticky = E + W) + Entry(self.tl, textvariable = self.tkname).grid(row = 2, column = 1, sticky = E + W) + Button(self.tl, command = self.apply, text = SIZECHANGEOK[LANG], state = ACTIVE).grid(row = 3, column = 0, columnspan = 2) + self.tl.bind("<Return>", self.apply) + def apply(self, event = None): + xgot, ygot, namegot = self.tkx.get(), self.tky.get(), self.tkname.get() + if xgot < 0: + xgot = self.sizex + if ygot < 0: + ygot = self.sizey + if namegot in self.names: + namegot = self.name + self.changedcb(xgot, ygot, namegot) + self.tl.destroy() + +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)] |