# 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("", 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 = "").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('', 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("", 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)]