summaryrefslogtreecommitdiff
path: root/graphics/tktexcad
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/tktexcad
Initial commit
Diffstat (limited to 'graphics/tktexcad')
-rw-r--r--graphics/tktexcad/config.py76
-rw-r--r--graphics/tktexcad/doc/tkTeXCAD.sgml107
-rw-r--r--graphics/tktexcad/doc/tkTeXCAD.txt198
-rw-r--r--graphics/tktexcad/doc/tkTeXCADen.sgml103
-rw-r--r--graphics/tktexcad/doc/tkTeXCADen.txt198
-rw-r--r--graphics/tktexcad/globalpars.py35
-rw-r--r--graphics/tktexcad/internat.py123
-rw-r--r--graphics/tktexcad/picels.py1299
-rw-r--r--graphics/tktexcad/readme.de46
-rw-r--r--graphics/tktexcad/readme.en42
-rw-r--r--graphics/tktexcad/tktc.py948
11 files changed, 3175 insertions, 0 deletions
diff --git a/graphics/tktexcad/config.py b/graphics/tktexcad/config.py
new file mode 100644
index 0000000000..a6f894ed52
--- /dev/null
+++ b/graphics/tktexcad/config.py
@@ -0,0 +1,76 @@
+### CONFIGURATION FOR TkTeXCad
+
+### Sprache
+### Language
+### langage
+# 0 -> Deutsch, 1 -> English, 2 -> fran,cais
+LANG = 0
+
+### Vergroesserung (pixel pro pt)
+### Magnification (pixel per pt)
+### Agrandissement (pixel par pt)
+# anfangs gewählte ~
+# initial ~
+# ~ initiale
+FAKT = 2
+# anwählbare Vergrößerungen
+# selectable magnification
+# agrandissements sélectionnables
+FAKTS = [1, 2, 3, 5, 8, 11, 14, 17]
+
+### Neue Datei
+### New File
+### Fichier nouveau
+# Größe (x, y [pt]), Name der Hauptbildklasse
+# Size (x, y [pt]), name of the main picture class
+# Largeur (x, y [pt]), nom du classement principal
+NEWPIC = (200, 200, "main")
+
+### Ausgangsgröße des Fensters (x, y [Pixel])
+### Initial window size (x, y [pixel])
+### Taille de la fenêtre initiale (x, y [pixel])
+CANVASWIDTH = 500
+CANVASHEIGHT = 700
+
+### Abstand der Gitternetzlinien in pt
+### space between grid lines (pt)
+### distance entre droites de la quadrillage (pt)
+GRID = 5
+# Netz eingangs zeigen (1), sonst 0
+# Show grid on startup (1), otherwise 0
+# Afficher quadrillage à démarrage (1), sinon (0)
+FGRID = 1
+
+# Für \dashbox: Standardlänge von Linien (pt)
+# \dashbox: initial length of lines (pt)
+# \dashbox: longeur initiale des droites (pt)
+DEFAULTDASHLEN = 1
+
+### Farben
+### Colors
+### Couleurs
+# Hintergrund
+# Background
+#
+CANVASBG = "white"
+# Rahmenfarbe
+# Frame
+# fond
+PICFRAMECOL = "red" # Rahmen
+# markierte Elemente
+# marked Elements
+# elements sélectionnés
+SELECTCOL = "blue" # Markierung markierter Elemente
+# Netz
+# Grid
+# Quadrillage
+GRIDCOL = "yellow" # Farbe des Netzes
+# "Rahmen" eine \makebox
+# "Frame" of a \makebox
+# "cadre" d'un \makebox
+MAKEBOXCOL = "gray"
+
+# exportierte Dateien
+# exported files
+# fichiers exportés
+SUFFIX = ".tex"
diff --git a/graphics/tktexcad/doc/tkTeXCAD.sgml b/graphics/tktexcad/doc/tkTeXCAD.sgml
new file mode 100644
index 0000000000..8c9e89f158
--- /dev/null
+++ b/graphics/tktexcad/doc/tkTeXCAD.sgml
@@ -0,0 +1,107 @@
+<!doctype linuxdoc system>
+
+<article>
+
+<!-- Title information -->
+
+<title>TkTeXCAD -- Kurzbeschreibung
+<author>Hilmar Straube, <tt/hilmstr@web.de/
+<date>13. August 2001
+<abstract>
+Kurzbeschreibung zu TkTeXCAD, einem Python/Tkinter Programm zum Erstellen von picture-Umgebungen für LaTeX.
+</abstract>
+
+<!-- Table of contents -->
+<toc>
+
+<!-- Begin the document -->
+
+<sect>Einführung
+<p>TkTeXCAD soll die Erstellung von picture-Umgebungen für LaTeX vereinfachen. Es kennt deren Einschränkungen und
+versucht, die Ausgabe von TeX möglichst getreu schon vor dem Export darzustellen. Es kann Dateien im eigenen Format
+(realisiert mit dem Python-Modul pickle) laden und speichern, LaTeX-Code aber nur exportieren. Es ist vor allem auf die
+Verarbeitung von wiederverwendbaren Teilbildern hin ausgelegt.
+
+<sect>(Nicht-)Fähigkeiten
+
+<p>Das Programm rechnet grundsätzlich in pt und benutzt nur die picture Umgebung ohne Erweiterungspakte (außer graphicx
+für das einfügen von EPS-Dateien).
+
+Das Programm erzeugt also nur folgendes: \circle (leerer Kreis; abgestuft bis 40 pt), \circle* (ausgefüllt, nur bis 15
+pt), Linien und Pfeile (eingeschränkte Neigungswinkel; verdickte waagerechte Linien als gefüllte Rechtecke gebraucht),
+verschiedene Textboxen, die quadratischen \qbezier-Kurven, \includegraphics (benötigt die Angabe von
+\usepackage{graphicx} im Vorspann) und natürlich Befehle zum Einfügen ggf. skalierter Teilbilder.
+
+Weiterhin lassen sich mehrere gleiche Objekte im jeweils gleichen Abstand mittels \multiput erzeugen.
+
+<sect1>Teilbilder
+
+<p>Das Programm unterscheidet zwischen Teilbildklassen und Teilbildinstanzen. Eine Teilbildklasse besteht aus einigen
+Objekten mit bestimmten Eigenschaften. Eine Instanz der Klasse ist dann wieder ein Objekt einer oder mehrerer anderer
+Teilbildklassen und erscheint dort als benutzerdefiniertes Objekt. Ändert sich die innere Teilbildklasse, ändert sich
+die Darstellung in allen Instanzen.
+
+Das Objekt "Instanz einer Teilbildklasse" lässt sich auch skalieren (\unitlength). Dabei können sich die Rundungen von
+Ovalen und die Durchmesser von Kreisen unproportional ändern. Auch dies dürfte korrekt dargestellt werden.
+
+Ein Bild besteht damit für TkTeXCAD aus einigen Bildklassen, deren erste das Hauptbild darstellt und Instanzen aller
+anderen ggf. indirekt enthält. Beim Exportieren erzeugt jede Bildklasse eine picture-Umgebung, auf die vom Hauptbild
+zugegriffen wird. Deren Name ist auch der Dateiname. Sie müssen also dafür Sorge tragen, dass dieser Name auf ihrem
+Rechnersystem legal ist.
+
+<sect1>EPS-Einbindung
+
+<p>Wie schon erwähnt, können Sie EPS-Bilder einfügen. Das Programm kann auch den Inhalt dieser Bilder in der Vorschau
+anzeigen, braucht dazu aber die Hilfe der Python Imaging Library (PIL). Diese ihrerseits greift auf Ghostscript zurück,
+kann das aber nur auf UNIX-Derivaten mit installiertem Ghostscript. Falls Sie Python mit der PIL auf einer anderen
+Plattform benutzen, hängen Sie an die Zeile "import Image" einige Buchstaben an, so dass ein "ImportError" hervorgerufen
+wird, der dem Programm das Nichtvorhandensein der EPS-Unterstützung anzeigt.
+
+Das äußert sich dadurch, dass jegliche eingefügten EPS-Dateien als durchgestrichenes Rechteck erscheinen und für das
+Programm dessen Breite/Höhe-Verhältnis unbekannt ist. Es nimmt daher (meist fälschlicherweise 1 zu 1 an).
+
+Weder in den für den Eigengebrauch gespeicherten .pic-Dateien, noch in den exportierten Dateien werden die EPS-Dateien
+gespeichert. Sie sollten also sicher stellen, dass Sie alle verwendeten Grafikdateien mitkopiert werden. (In den
+exportierten .tex-Dateien stehen absolute Pfade.)
+
+<sect>Hinweise zur Bedienung
+
+<sect1>Das Menü Anzeige
+<p>Jede Eingabe eines Koordinatenpunktes wird in LaTeX-Koordinaten umgerechnet und außerdem noch auf den nächsten
+Netzpunkt gesetzt. Das Netz kann angezeigt oder versteckt sein, ist aber immer aktiv. Ist seine Wirkung unerwünscht,
+stellen sie einfach den Netzabstand klein genug.
+
+Zur Umrechnung in LaTeX-Koordinaten wird die Angabe im oberen Teil dieses Menüs benutzt. Sie gibt an, wieviele
+Bildschirmpunkte einer Einheit (pt) entsprechen.
+
+<sect1>Das Menü Teilbildklassen
+<p>Eine neue Teilbildklasse wird aus den gerade markierten (Elemente/... markieren) Objekten erstellt. Diese Objekte
+werden entfernt und an ihre Stelle wird dann eine (erste) Instanz der neu erstellten Klasse an Stelle der alten Objekte
+eingefügt.
+
+Darunter werden geladenen Teilbildklassen angezeigt; die Klassen, die gerade nicht eingefügt werden können, sind
+deaktiviert. Mit dem untersten Menüpunkt können alle bisher erzeugten Teilbildklassen zur Bearbeitung aufgerufen werden.
+
+Das passt vielleicht nicht ganz so gut hier her, ist aber wohl doch ein wenig erklärungsbedürftig: Mit \multiput
+wiederholten Bildelementen kann man von einem Multielement zu mehreren Einzelelement zerlegen (Multi auflösen). Dann sind
+die Bildelement nicht mehr verbunden und können für sich geändert werden. Bildinstanz auflösen zerlegt eine Bildinstanz in
+ihre Einzelteile, das in einem \multiput dann gleich mehrfach. Dadurch ist dieser Bildteil von der Klasse getrennt;
+es hat aber keinen Einfluss auf andere Instanzen der Klasse.
+
+<sect1>Das Menü Datei
+<p>Laden, Speichern und Importieren beziehen sich auf die von TkTeXCAD erzeugten .pic-Dateien. Importieren funktioniert
+nur, wenn die Namen der Klassen in der zu importierenden Datei keine Überscheidungen mit den gerade geladenen Klassen
+haben. Von den so importierten Klassen können dann Instanzen erzeugt werden.
+
+Der Export erstellt zu jeder Klasse eine .tex-Datei gleichen Namens. Diese beziehen sich aufeinander und ihre Hauptdatei
+kann mit \import{<em/name/} in ihre LaTeX-Datei eingefügt werden.
+
+</article>
+
+
+
+
+
+
+
+
diff --git a/graphics/tktexcad/doc/tkTeXCAD.txt b/graphics/tktexcad/doc/tkTeXCAD.txt
new file mode 100644
index 0000000000..e7eaa9dad5
--- /dev/null
+++ b/graphics/tktexcad/doc/tkTeXCAD.txt
@@ -0,0 +1,198 @@
+ TkTeXCAD -- Kurzbeschreibung
+ Hilmar Straube, hilmstr@web.de
+ 13. August 2001
+
+ Kurzbeschreibung zu TkTeXCAD, einem Python/Tkinter Programm zum
+ Erstellen von picture-Umgebungen für LaTeX.
+ ______________________________________________________________________
+
+ Inhaltsverzeichnis
+
+
+ 1. Einführung
+
+ 2. (Nicht-)Fähigkeiten
+
+ 2.1 Teilbilder
+ 2.2 EPS-Einbindung
+
+ 3. Hinweise zur Bedienung
+
+ 3.1 Das Menü Anzeige
+ 3.2 Das Menü Teilbildklassen
+ 3.3 Das Menü Datei
+
+
+ ______________________________________________________________________
+
+ 11.. EEiinnffüühhrruunngg
+
+ TkTeXCAD soll die Erstellung von picture-Umgebungen für LaTeX
+ vereinfachen. Es kennt deren Einschränkungen und versucht, die Ausgabe
+ von TeX möglichst getreu schon vor dem Export darzustellen. Es kann
+ Dateien im eigenen Format (realisiert mit dem Python-Modul pickle)
+ laden und speichern, LaTeX-Code aber nur exportieren. Es ist vor allem
+ auf die Verarbeitung von wiederverwendbaren Teilbildern hin ausgelegt.
+
+
+ 22.. ((NNiicchhtt--))FFäähhiiggkkeeiitteenn
+
+ Das Programm rechnet grundsätzlich in pt und benutzt nur die picture
+ Umgebung ohne Erweiterungspakte (außer graphicx für das einfügen von
+ EPS-Dateien).
+
+ Das Programm erzeugt also nur folgendes: \circle (leerer Kreis;
+ abgestuft bis 40 pt), \circle* (ausgefüllt, nur bis 15 pt), Linien und
+ Pfeile (eingeschränkte Neigungswinkel; verdickte waagerechte Linien
+ als gefüllte Rechtecke gebraucht), verschiedene Textboxen, die
+ quadratischen \qbezier-Kurven, \includegraphics (benötigt die Angabe
+ von \usepackage{graphicx} im Vorspann) und natürlich Befehle zum
+ Einfügen ggf. skalierter Teilbilder.
+
+ Weiterhin lassen sich mehrere gleiche Objekte im jeweils gleichen
+ Abstand mittels \multiput erzeugen.
+
+
+ 22..11.. TTeeiillbbiillddeerr
+
+ Das Programm unterscheidet zwischen Teilbildklassen und
+ Teilbildinstanzen. Eine Teilbildklasse besteht aus einigen Objekten
+ mit bestimmten Eigenschaften. Eine Instanz der Klasse ist dann wieder
+ ein Objekt einer oder mehrerer anderer Teilbildklassen und erscheint
+ dort als benutzerdefiniertes Objekt. Ändert sich die innere
+ Teilbildklasse, ändert sich die Darstellung in allen Instanzen.
+
+ Das Objekt "Instanz einer Teilbildklasse" lässt sich auch skalieren
+ (\unitlength). Dabei können sich die Rundungen von Ovalen und die
+ Durchmesser von Kreisen unproportional ändern. Auch dies dürfte
+ korrekt dargestellt werden.
+
+ Ein Bild besteht damit für TkTeXCAD aus einigen Bildklassen, deren
+ erste das Hauptbild darstellt und Instanzen aller anderen ggf.
+ indirekt enthält. Beim Exportieren erzeugt jede Bildklasse eine
+ picture-Umgebung, auf die vom Hauptbild zugegriffen wird. Deren Name
+ ist auch der Dateiname. Sie müssen also dafür Sorge tragen, dass
+ dieser Name auf ihrem Rechnersystem legal ist.
+
+
+ 22..22.. EEPPSS--EEiinnbbiinndduunngg
+
+ Wie schon erwähnt, können Sie EPS-Bilder einfügen. Das Programm kann
+ auch den Inhalt dieser Bilder in der Vorschau anzeigen, braucht dazu
+ aber die Hilfe der Python Imaging Library (PIL). Diese ihrerseits
+ greift auf Ghostscript zurück, kann das aber nur auf UNIX-Derivaten
+ mit installiertem Ghostscript. Falls Sie Python mit der PIL auf einer
+ anderen Plattform benutzen, hängen Sie an die Zeile "import Image"
+ einige Buchstaben an, so dass ein "ImportError" hervorgerufen wird,
+ der dem Programm das Nichtvorhandensein der EPS-Unterstützung anzeigt.
+
+ Das äußert sich dadurch, dass jegliche eingefügten EPS-Dateien als
+ durchgestrichenes Rechteck erscheinen und für das Programm dessen
+ Breite/Höhe-Verhältnis unbekannt ist. Es nimmt daher (meist
+ fälschlicherweise 1 zu 1 an).
+
+ Weder in den für den Eigengebrauch gespeicherten .pic-Dateien, noch in
+ den exportierten Dateien werden die EPS-Dateien gespeichert. Sie
+ sollten also sicher stellen, dass Sie alle verwendeten Grafikdateien
+ mitkopiert werden. (In den exportierten .tex-Dateien stehen absolute
+ Pfade.)
+
+
+ 33.. HHiinnwweeiissee zzuurr BBeeddiieennuunngg
+
+ 33..11.. DDaass MMeennüü AAnnzzeeiiggee
+
+ Jede Eingabe eines Koordinatenpunktes wird in LaTeX-Koordinaten
+ umgerechnet und außerdem noch auf den nächsten Netzpunkt gesetzt. Das
+ Netz kann angezeigt oder versteckt sein, ist aber immer aktiv. Ist
+ seine Wirkung unerwünscht, stellen sie einfach den Netzabstand klein
+ genug.
+
+ Zur Umrechnung in LaTeX-Koordinaten wird die Angabe im oberen Teil
+ dieses Menüs benutzt. Sie gibt an, wieviele Bildschirmpunkte einer
+ Einheit (pt) entsprechen.
+
+
+ 33..22.. DDaass MMeennüü TTeeiillbbiillddkkllaasssseenn
+
+ Eine neue Teilbildklasse wird aus den gerade markierten (Elemente/...
+ markieren) Objekten erstellt. Diese Objekte werden entfernt und an
+ ihre Stelle wird dann eine (erste) Instanz der neu erstellten Klasse
+ an Stelle der alten Objekte eingefügt.
+
+ Darunter werden geladenen Teilbildklassen angezeigt; die Klassen, die
+ gerade nicht eingefügt werden können, sind deaktiviert. Mit dem
+ untersten Menüpunkt können alle bisher erzeugten Teilbildklassen zur
+ Bearbeitung aufgerufen werden.
+
+ Das passt vielleicht nicht ganz so gut hier her, ist aber wohl doch
+ ein wenig erklärungsbedürftig: Mit \multiput wiederholten
+ Bildelementen kann man von einem Multielement zu mehreren
+ Einzelelement zerlegen (Multi auflösen). Dann sind die Bildelement
+ nicht mehr verbunden und können für sich geändert werden. Bildinstanz
+ auflösen zerlegt eine Bildinstanz in ihre Einzelteile, das in einem
+ \multiput dann gleich mehrfach. Dadurch ist dieser Bildteil von der
+ Klasse getrennt; es hat aber keinen Einfluss auf andere Instanzen der
+ Klasse.
+
+
+ 33..33.. DDaass MMeennüü DDaatteeii
+
+ Laden, Speichern und Importieren beziehen sich auf die von TkTeXCAD
+ erzeugten .pic-Dateien. Importieren funktioniert nur, wenn die Namen
+ der Klassen in der zu importierenden Datei keine Überscheidungen mit
+ den gerade geladenen Klassen haben. Von den so importierten Klassen
+ können dann Instanzen erzeugt werden.
+
+ Der Export erstellt zu jeder Klasse eine .tex-Datei gleichen Namens.
+ Diese beziehen sich aufeinander und ihre Hauptdatei kann mit
+ \import{_n_a_m_e} in ihre LaTeX-Datei eingefügt werden.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/graphics/tktexcad/doc/tkTeXCADen.sgml b/graphics/tktexcad/doc/tkTeXCADen.sgml
new file mode 100644
index 0000000000..ab11f2fac4
--- /dev/null
+++ b/graphics/tktexcad/doc/tkTeXCADen.sgml
@@ -0,0 +1,103 @@
+<!doctype linuxdoc system>
+
+<article>
+
+<!-- Title information -->
+
+<title>TkTeXCAD -- Short description
+<author>Hilmar Straube, <tt/hilmstr@web.de/
+<date>Aug 16, 2001
+<abstract>
+Short description for TkTeXCAD, a Python/Tkinter-Script to interactively create LaTeX picture environments.
+</abstract>
+
+<!-- Table of contents -->
+<toc>
+
+<!-- Begin the document -->
+
+<sect>Introduction
+
+<p>TkTeXCAD means to make creation of standard LaTeX picture environments easy. It knows about LaTeX' limitations and
+attempts to preview the dvi output. It is able to save and load pictures in its own format (realized using the module
+pickle provided with Python), but only exports LaTeX source. Special focus is laid on processing reusable picture
+parts.
+
+<sect>(Not-)Features
+
+<p>The program uses the unit pt and the picture environment without additional extension packages (except graphicx for
+.eps inclusion).
+
+So, the program only generates these: \circle (empty circle; stepped up to 40 pt), \circle* (filled, only up to 15 pt),
+lines and vectors (limited elevations; a very bold version is used to implement filled rectangles), various text boxes,
+the \qbezier splines, \includegraphics (requieres \usepackage{graphicx} in the preamble) and of course commands to
+insert saved picture parts.
+
+Moreover, it is possible to show the same object several times by using \multiput.
+
+<sect1>Saved picture parts
+
+<p>The program makes a difference between classes of pictures and instances of these picture classes. A picture class
+contains some objects with their individual properties. An instance of a picture class can then be an object inside
+other picture classes where it appears as a special, user-defined objects. Whenever the inner picture class changes, all
+instances of this class will be changed as well.
+
+The object "instance of a picture class" can be scaled (\unitlength). This may cause the diameters of the arcs of \oval
+and the circles to change un-proportional, but the program will (hopefully) preview that.
+
+So a picture for TkTeXCAD consists of several picture classes. There is a main picture, that will be inserted directly
+into the LaTeX source. All other classes will be referenced to (possibly indirectly) from the main picture. When
+exporting, the program creates a file for every exported picture class <em/with exactly the same name; so you have to
+take care that this name will be legal on your system./
+
+<sect1>Inclusion of EPS pictures
+
+<p>As noted above, the program is able to include EPS files. It will preview their contents, but will need the help of
+the Python Imaging Library (PIL). This library then uses Ghostscript to render the pictures, but only on UNIX derivates
+with Ghostscript installed. If you have already installed the PIL on another platform, append some letters to the line
+"import Image" in tktc.py. This will raise an "ImportError" exception and thus tell the programm that there is no EPS
+support on your system.
+
+With no EPS support, every EPS picture will appear as a striked out rectangle whose aspect ratio is unknown. The
+programm will assume 1/1 which is probably wrong.
+
+The EPSs will not be saved, neiter in the .pic files of tkTeXCad nor in the exported files. So, you should copy them
+with your whole LaTeX document. (The exported files contain absolute paths to the files.)
+
+<sect>Notes on the user interface
+
+<sect1>The display menu
+
+<p>Every click into the canvas will be transformed into LaTeX coordinates. Additionally it will be placed onto the
+nearest point in the grid. This grid can be hidden, but this does not change that behavior. If you don't want it,
+just make the grid spacing small enough.
+
+To change the coordinate system, the data in the upper field of the menu is used. It denotes the number of points on
+the screen that will be one TeX pt.
+
+<sect1>The menu picture parts
+
+<p>To create a new picture class, select the objects you want to transfer into (elements/... mark) and select "create
+new picture class". These objects will be deleted in the picture class you are just editing and pasted into the newly
+created class. Then, the program places the first instance of this newly created picture class exactly to where it took
+the objects from.
+
+The next menu items are the picture classes that you have already created. Some of them may be grayed to make it
+impossible to insert them into classes whose instances they contain (to avoid circular references).
+
+The last menu item gives access to editing all classes.
+
+This does not really fit in here, but probably needs some explanation: Objects that are \multiputted can be changed into
+several stand-alone objects by selecting "create count simple objects". The same thing can be done with an instance of a
+picture class. This detaches it from its picture class; changes to the picture class will not affect the newly created
+objects any more.
+
+<sect>The file menu
+<p>Load, save and import refer to the files created by TkTeXCAD and pickle. Import will only work, if the imported file
+does not contain any picture classes whose names are ambiguous with those already in memory. The imported classes can
+then be selected in the menu picture classes.
+
+Exporting creates for every picture class a .tex file with the same name. These refer to one another and can be placed
+into your LaTeX source using \import{<em/name/>
+
+</article>
diff --git a/graphics/tktexcad/doc/tkTeXCADen.txt b/graphics/tktexcad/doc/tkTeXCADen.txt
new file mode 100644
index 0000000000..2a1c6933d7
--- /dev/null
+++ b/graphics/tktexcad/doc/tkTeXCADen.txt
@@ -0,0 +1,198 @@
+ TkTeXCAD -- Short description
+ Hilmar Straube, hilmstr@web.de
+ Aug 16, 2001
+
+ Short description for TkTeXCAD, a Python/Tkinter-Script to interac-
+ tively create LaTeX picture environments.
+ ______________________________________________________________________
+
+ Table of Contents
+
+
+ 1. Introduction
+
+ 2. (Not-)Features
+
+ 2.1 Saved picture parts
+ 2.2 Inclusion of EPS pictures
+
+ 3. Notes on the user interface
+
+ 3.1 The display menu
+ 3.2 The menu picture parts
+
+ 4. The file menu
+
+
+
+ ______________________________________________________________________
+
+ 11.. IInnttrroodduuccttiioonn
+
+ TkTeXCAD means to make creation of standard LaTeX picture environments
+ easy. It knows about LaTeX' limitations and attempts to preview the
+ dvi output. It is able to save and load pictures in its own format
+ (realized using the module pickle provided with Python), but only
+ exports LaTeX source. Special focus is laid on processing reusable
+ picture parts.
+
+
+ 22.. ((NNoott--))FFeeaattuurreess
+
+ The program uses the unit pt and the picture environment without
+ additional extension packages (except graphicx for .eps inclusion).
+
+ So, the program only generates these: \circle (empty circle; stepped
+ up to 40 pt), \circle* (filled, only up to 15 pt), lines and vectors
+ (limited elevations; a very bold version is used to implement filled
+ rectangles), various text boxes, the \qbezier splines,
+ \includegraphics (requieres \usepackage{graphicx} in the preamble) and
+ of course commands to insert saved picture parts.
+
+ Moreover, it is possible to show the same object several times by
+ using \multiput.
+
+
+ 22..11.. SSaavveedd ppiiccttuurree ppaarrttss
+
+ The program makes a difference between classes of pictures and
+ instances of these picture classes. A picture class contains some
+ objects with their individual properties. An instance of a picture
+ class can then be an object inside other picture classes where it
+ appears as a special, user-defined objects. Whenever the inner picture
+ class changes, all instances of this class will be changed as well.
+
+ The object "instance of a picture class" can be scaled (\unitlength).
+ This may cause the diameters of the arcs of \oval and the circles to
+ change un-proportional, but the program will (hopefully) preview that.
+
+ So a picture for TkTeXCAD consists of several picture classes. There
+ is a main picture, that will be inserted directly into the LaTeX
+ source. All other classes will be referenced to (possibly indirectly)
+ from the main picture. When exporting, the program creates a file for
+ every exported picture class _w_i_t_h _e_x_a_c_t_l_y _t_h_e _s_a_m_e _n_a_m_e_; _s_o _y_o_u _h_a_v_e
+ _t_o _t_a_k_e _c_a_r_e _t_h_a_t _t_h_i_s _n_a_m_e _w_i_l_l _b_e _l_e_g_a_l _o_n _y_o_u_r _s_y_s_t_e_m_.
+
+
+ 22..22.. IInncclluussiioonn ooff EEPPSS ppiiccttuurreess
+
+ As noted above, the program is able to include EPS files. It will
+ preview their contents, but will need the help of the Python Imaging
+ Library (PIL). This library then uses Ghostscript to render the
+ pictures, but only on UNIX derivates with Ghostscript installed. If
+ you have already installed the PIL on another platform, append some
+ letters to the line "import Image" in tktc.py. This will raise an
+ "ImportError" exception and thus tell the programm that there is no
+ EPS support on your system.
+
+ With no EPS support, every EPS picture will appear as a striked out
+ rectangle whose aspect ratio is unknown. The programm will assume 1/1
+ which is probably wrong.
+
+ The EPSs will not be saved, neiter in the .pic files of tkTeXCad nor
+ in the exported files. So, you should copy them with your whole LaTeX
+ document. (The exported files contain absolute paths to the files.)
+
+
+ 33.. NNootteess oonn tthhee uusseerr iinntteerrffaaccee
+
+ 33..11.. TThhee ddiissppllaayy mmeennuu
+
+ Every click into the canvas will be transformed into LaTeX
+ coordinates. Additionally it will be placed onto the nearest point in
+ the grid. This grid can be hidden, but this does not change that
+ behavior. If you don't want it, just make the grid spacing small
+ enough.
+
+ To change the coordinate system, the data in the upper field of the
+ menu is used. It denotes the number of points on the screen that will
+ be one TeX pt.
+
+
+ 33..22.. TThhee mmeennuu ppiiccttuurree ppaarrttss
+
+ To create a new picture class, select the objects you want to transfer
+ into (elements/... mark) and select "create new picture class". These
+ objects will be deleted in the picture class you are just editing and
+ pasted into the newly created class. Then, the program places the
+ first instance of this newly created picture class exactly to where it
+ took the objects from.
+
+ The next menu items are the picture classes that you have already
+ created. Some of them may be grayed to make it impossible to insert
+ them into classes whose instances they contain (to avoid circular
+ references).
+
+ The last menu item gives access to editing all classes.
+
+ This does not really fit in here, but probably needs some explanation:
+ Objects that are \multiputted can be changed into several stand-alone
+ objects by selecting "create count simple objects". The same thing
+ can be done with an instance of a picture class. This detaches it
+ from its picture class; changes to the picture class will not affect
+ the newly created objects any more.
+
+
+ 44.. TThhee ffiillee mmeennuu
+
+ Load, save and import refer to the files created by TkTeXCAD and
+ pickle. Import will only work, if the imported file does not contain
+ any picture classes whose names are ambiguous with those already in
+ memory. The imported classes can then be selected in the menu picture
+ classes.
+
+ Exporting creates for every picture class a .tex file with the same
+ name. These refer to one another and can be placed into your LaTeX
+ source using \import{_n_a_m_e>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/graphics/tktexcad/globalpars.py b/graphics/tktexcad/globalpars.py
new file mode 100644
index 0000000000..f3c36daaf2
--- /dev/null
+++ b/graphics/tktexcad/globalpars.py
@@ -0,0 +1,35 @@
+# 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 config import FAKT
+
+CLIPBOARDNAME = None # gesichert als Name des Clipboard
+
+VERSION = "0.9 BETA"
+PROGNAME = "TkTeXCad " + VERSION
+
+THINWIDTH = 0.3 # Diese Angaben sind in pt
+THICKWIDTH = 0.8
+ARROWSHAPE = 8.0/FAKT, 10.0/FAKT, 3.0/FAKT
+
+POSSIBLEDIAMETERS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 20, 24, 28, 32, 36, 40]
+
+POSSIBLEDIAMETERSAS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
+
+POSSIBLEDXDY = [(0, 1), (1, 0),
+ (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6),
+ (2, 1), (2, 3), (2, 5),
+ (3, 1), (3, 2), (3, 4), (3, 5),
+ (4, 1), (4, 3), (4, 5),
+ (5, 1), (5, 2), (5, 3), (5, 4), (5, 6),
+ (6, 1), (6, 5)] # hier ohne negative Möglichkeiten
+
+VECDXDY = [(0, 1), (1, 0),
+ (1, 1), (1, 2), (1, 3), (1, 4),
+ (2, 1), (2, 3),
+ (3, 1), (3, 2), (3, 4),
+ (4, 1), (4, 3)] # Nur für Pfeile
+
+
diff --git a/graphics/tktexcad/internat.py b/graphics/tktexcad/internat.py
new file mode 100644
index 0000000000..52cb9298a7
--- /dev/null
+++ b/graphics/tktexcad/internat.py
@@ -0,0 +1,123 @@
+# TkTeXCad
+# (C) Copyright 2001 Hilmar Straube; dieses Programm untersteht der GPL.
+# Keinerlei Garantie auf was auch immer, was mit diesem Programm zu tun hat.
+
+NOPIL = ['PIL nicht gefunden. Siehe EPS in der Dokumentation.', 'PIL not found. See EPS in documentation', 'PIL pas trouvé, voyez documentation.']
+
+CHANGEWINTITLE = ['Anpassen', 'Change', 'changer']
+STATUSNOSELECTION = ['Nichts ausgewählt!', 'No selection', "Rien n'est sélectionné!"]
+MENUFILELOAD = ['Datei laden Ctrl-l', 'Load file Ctrl-l', 'Ouvrir fichier Ctrl-l']
+MENUFILEINSERT = ['Datei einfügen Ctrl-i', 'Insert file Ctrl-i', 'Insérer fichier Ctrl-i']
+MENUFILESAVE = ['Datei speichern Ctrl-s', 'Save file Ctrl-s', 'Sauvegarder fichier Ctrl-s']
+MENUTEXEXPORT = ['Picture-Umgebung exportieren Ctrl-x', 'Export picture environment Ctrl-x', 'Exporter environnement picture Ctrl-x']
+MENUQUIT = ['Programm beenden Ctrl-q', 'Quit Ctrl-q', 'Quitter Ctrl-q']
+MENUCIRCLE = ['k Kreis', 'k Circle', 'k cercle']
+MENUCIRCLEAS = ['K gef. Kreis', 'K filled Circle', 'K cercle remplit']
+MENULINE = ['l Linie', 'l Line', 'l droite']
+MENUVECTOR = ['L Pfeil', 'L Vector', 'L flèche']
+MENUOVAL = ['o Oval', 'o Oval', 'o oval']
+MENUBEZIER = ['q Bezier', 'q Bezier', 'q bezier']
+MENUBB = ['B gefülltes Rechteck', 'B Filled box', 'B panneau remplit']
+MENUEPS = ['e EPS einfügen', 'e insert EPS', 'e insérer EPS']
+MENUSIMPLETEXT = ['t Einfacher Text', 't Simple text', 't texte simple']
+MENUSHORTSTACK = ['a Shortstack', 'a Short stack', 'a short stack']
+MENUTEXTBOX = ['b Textrechteck', 'b Textbox', 'b panneau de texte']
+MENUSCALELABEL = [' Pixel pro pt', ' pixel per pt', ' pixel par pt']
+MENUSHOWGRID = ['Netz anzeigen Ctrl-g', 'Show grid Ctrl-g', 'Afficher quadrillage Ctrl-g']
+MENUCHANGEGRID = ['Netzdichte ändern ...', 'Change grid ...', 'Changer quadrillage ...']
+MENUSELECT = ['... auswählen <Leertaste>', '... select <space>', '... sélectionner <Espace>']
+MENUDESELECT = ['... abwählen <Esc>', '... deselect <Esc>', '... annuller sélection <Esc>']
+MENUDEL = ['... entfernen <Del>', '... remove <Del>', '... effacer éléments sélectionnés <Del>']
+MENUCLASSES = ['Teilbildklassen', 'Picture classes', 'Classement']
+MENUFILE = ['Datei', 'File', 'Fichier']
+MENUNEW = ['Neu', 'New', 'Nouveau']
+MENUDISPLAY = ['Anzeige', 'Display', 'Écran']
+MENUELEM = ['Elemente', 'Elements', 'Éléments']
+FTLABEL = ['Gesicherte Dateien', 'Saved files', 'Fichiers sauvegardées']
+LOADTITLE = ['Dateiname (laden)', 'Filename (load)', 'Fichier (ouvrir)']
+LOADERROR = ['Fehler beim Laden: ', 'Load error: ', "Faute d'ouverture: "]
+SAVETITLE = ['Dateiname (sichern)', 'Filename (save)', 'Fichier (sauvegarder)']
+SAVEERROR = ['Fehler beim Speichern: ', 'Save error: ', 'Faute de sauvegarde']
+INSERTTITLE = ['Dateiname (einfügen)', 'Filename (insert)', 'Fichier (insérer)']
+STATUSNAMECOL = ['Kann Datei nicht einfügen: sich überschneidende Teilbildnamen', "Can't insert file: Name conflict", "Insertion impossible: recoupement des noms"]
+LABELNEWGRID = ['Neuer Gitternetzabstand (pt)', 'New space between grid lines (pt)', 'Nouvelle distance entre droites (pt)']
+MENUNEWPICCLASS = ['Neu erstellen', 'New picture class', 'Nouveau classement']
+MENUADJUSTDXDY = ['Größe anpassen', 'Adjust size', 'Adaption de la largeur']
+MENUNEWINSTANCE = ['Instanz einfügen', 'Insert instance', 'Nouvelle instance']
+MENUDELCLASS = ['Löschen', 'Delete', 'Effacer']
+MENUSIZECHANGE = ['Aktuelle Klasseneigenschaften ändern', 'Change current class properties', 'Changer propriétés du classement active']
+MENUCHANGECLASS = ['Klasse ändern', 'Change class', 'Changer classement active']
+STATUSNOCLASSSEL = ['Keine Teilbildklasse ausgewählt.', 'No picture class selected.', 'Aucun classement selectionné']
+STATUSBLOCKED = ['Teilbildklasse blockiert.', 'Picture class blocked.', 'Classement bloqué']
+STATUSDELCLASSREJECT = ['Es sind noch Instanzen dieser Klasse in Benutzung.', 'This picture class is still in use.', 'Il y a encore des instances de ce classe.']
+STATUSCANCEL = ['Abgebrochen!', 'Cancelled!', 'Annullé!']
+STATUSDONE = ['Fertig.', 'Done.', 'Fini.']
+STATUSPOINT2 = ['Bitte zweiten Punkt angeben!', 'Please specify second point', 'Spécifiez 2ième point s.v.p.']
+STATUSPOINT3 = ['Kontrollpunkt ...', 'Control point ...', 'Point de contrôle ...']
+STATUSPOINT1 = ['Bitte Punkt angeben!', 'Please specify root point.', 'Spécifiez point s.v.p.']
+INITIALSIMPLETEXT = ['', '', '']
+INITIALSHORTSTACK = ['Zeile1\\\\Zeile2', 'Line1\\\\Line2', 'Ligne1\\\\Ligne2']
+INITIALTEXTBOX = ['', '', '']
+STATUSXELSSELECTED = [' Element(e) gewählt', ' Element(s) selected', ' élement(s) sélectionné(s)']
+STATUSDESELECT = ['Auswahl zurückgenommen.', 'Selection cancelled', 'Sélection annullée']
+STATUSELSDELETED = ['Ausgewählte Bildelemente gelöscht.', 'Selection deleted', 'Elements effacés']
+LABELNEWCLASSNAME = ['Name', 'Name', 'Nom']
+STATUSINVALIDNAME = ['Ungültiger Name', 'Invalid Name', 'Nom pas validé']
+STATUSNEWCLASS = ['Teilbild erzeugt.', 'New picture class created', 'Nouvelle classement créé']
+MULTIDX = ['dx', 'dx', 'dx']
+MULTIDY = ['dy', 'dy', 'dy']
+MULTIN = ['Anzahl', 'Count', 'Nombre']
+MULTISETROOT = ['Ursprung verändern', 'Change root point', 'Changer racine']
+MULTISETP2 = ['Zweiter Punkt', 'Second point', '2ième point']
+MULTISETP3 = ['Kontrollpunkt', 'Control point', '3ième point']
+MULTIUNMULTI = ['Multi auflösen', 'Create "count" single objects', "Créér 'nombre' d'objects simples"]
+MULTIUNPIC = ['Bildinstanz auflösen', 'Class instance -> simple objects', "Instance -> objects simples"]
+GENMARK = [' generierte picture-Umgebung\n', ' generated picture environment\n', ' créa environnement picture.\n']
+PICTURELABELCLASS = ['Teilbildklasse: ', 'Picture class: ', 'Classement: ']
+PICTURELABELSCALE = ['Skalierung (%)', 'Scale (%)', 'Agrandissement (%)']
+PICELLABELBOLD = ['Fett', 'Bold', 'Gras']
+CIRCLELABELFILLED = ['Ausgefüllt', 'Filled', 'Remplit']
+LINELABELLINE = ['Linie', 'Line', 'Droite']
+LINELABELVEC = ['Pfeil', 'Vector', 'Flèche']
+OVALALL = ['Vollständig', 'all', 'complet']
+OVALT = ['Obere Hälfte', 'top', 'moitié supérieure']
+OVALL = ['Linke Hälfte', 'left', 'moitié inférieure']
+OVALR = ['Rechte Hälfte', 'right', 'moitié gauche']
+OVALB = ['Untere Hälfte', 'bottom', 'moitié droite']
+OVALTR = ['Viertel oben rechts', 'top right', 'moitié supérieure droite']
+OVALTL = ['Viertel oben links', 'top left', 'moitié supérieure gauche']
+OVALBL = ['Viertel unten links', 'bottom left', 'moitié inférieure gauche']
+OVALBR = ['Viertel unten rechts', 'bottom right', 'moitié inférieure droite']
+LABELSIMPLETEXT = ['<einfacher Text>', '<simple text>', '<texte simple>']
+PICELSHOWTEXT = ['*TEXT*', '*TEXT*', '*TEXT*']
+PICELLABELTEXT = ['Text:', 'Text: ', 'Text:']
+TEXTBOXLABELBORDER = ['Rahmen', 'Frame', 'Cadre']
+TEXTBOXLABELNOBORDER = ['ohne Rahmen', 'No frame', 'Pas de cadre']
+TEXTBOXLABELFULLBORDER = ['durchgezogener Rahmen', 'full frame', 'Cadre continué']
+TEXTBOXLABELDASHEDBORDER = ['gestrichelter Rahmen', 'dashed frame', 'Cadre interrompu']
+TEXTBOXLABELDASHLEN = ['Strichlänge (pt)', 'Dash length', 'Longeur des droites']
+TEXTBOXLABELPOS = ['Position: ', 'Position: ', 'Position: ']
+SHORTSTACKLABELLEFT = ['Linksbündig', 'Left', 'Aligné à gauche']
+SHORTSTACKLABELCENTERED = ['Zentriert', 'Centered', 'centré']
+SHORTSTACKLABELRIGHT = ['Rechtsbündig', 'Right', 'Aligné à droite']
+SHORTSTACKSHOWLEFT = ['*SHORTSTACK*\n*linksbündig*', '*SHORTSTACK*\n*left*', '*SHORTSTACK*\n*aligné a gauche*']
+SHORTSTACKSHOWCENTERED = ['*SHORTSTACK*\n*zentriert*', '*SHORTSTACK*\n*centered*', '*SHORTSTACK*\n*centré*']
+SHORTSTACKSHOWRIGHT = ['*SHORTSTACK*\n*rechtsbündig*', '*SHORTSTACK*\n*right*', '*SHORTSTACK*\n*aligné a droite*']
+SIZECHANGETITLE = ['Klasseneigenschaften', 'Class properties', 'Propriétés du classement']
+SIZECHANGEX = ['X-Größe (pt)', 'X-size (pt)', 'Largeur X']
+SIZECHANGEY = ['Y-Größe (pt)', 'Y-size (pt)', 'Largeur Y']
+SIZECHANGENAME = ['Name', 'Name', 'Nom']
+SIZECHANGEOK = ['Ok', 'Ok', 'Ok']
+
+EPSLABEL = ['EPS Dateien', 'EPS files', 'Fichiers EPS']
+EPSCANNOTOPEN = ['Kann Datei nicht öffnen.', 'Cannot open file', 'Ouverture de fichier impossible']
+EPSNOEPS = ['Datei ist keine EPS-Datei.', 'No EPS-file', 'Pas de fichier EPS']
+EPSASPECT = ['Original Seiten/Längen-Verhältnis', 'Original aspect ratio', 'Relation longeur/largeur originale']
+EPSLOADTITLE = ['Dateiname (EPS laden)', 'Filename (load EPS)', 'Fichier (ouvrir EPS)']
+EPSINSERTNOTE = ['Siehe Hinweise in der Dokumentation!', 'See notes in the documentation.', 'Voyez documentation.']
+
+MENUCLIP = ['Zwischenablage', 'Clipboard', 'Mémoire temporaire']
+STATUSCLIPEMPTY = ['Zwischenablage leer.', 'The clipboard is empty.', 'Vide.']
+CLIPCOPY = ['Kopieren Ctrl-c', 'Copy Ctrl-c', 'Copier Ctrl-c']
+CLIPCUT = ['Ausschneiden Ctrl-x', 'Cut Ctrl-x', 'Couper Ctrl-x']
+CLIPPASTE = ['Einfügen Ctrl-v', 'Paste Ctrl-v', 'Insérer Ctrl-v']
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)]
diff --git a/graphics/tktexcad/readme.de b/graphics/tktexcad/readme.de
new file mode 100644
index 0000000000..6a00e6b6d0
--- /dev/null
+++ b/graphics/tktexcad/readme.de
@@ -0,0 +1,46 @@
+TkTeXCAD
+
+VERWENDUNGSZWECK: Erstellen von picture-Umgebungen für LaTeX.
+
+VERSION: Bugfix zum ersten Beta; Vektoren funktionieren jetzt richtig
+(vorher waren unmögliche Richtungen definiert);
+Kopieren/Ausschneiden/Einfügen; graphisches EPS einfügen.
+Kurzbeschreibung auch auf Englisch
+
+INSTALLATION: Sie benötigen den Interpreter Python ab Version 2.0 mit
+der Erweiterung Tkinter für die Oberfläche. Python gibt es auf
+www.python.org, bei der Mac- und Windowsinstallation ist meines
+Wissens Tk gleich integriert, für unixartige Plattformen gibt es Tk
+bei www.scriptics.com.
+ Bei den meisten Linux-Distributionen dürfte es zum Lieferumfang
+gehören, die Windows-Installation von Python ist problemlos.
+ Am einfachsten kopieren Sie die drei .py-Scripte in ein Verzeichnis
+ihrer Wahl und starten es mit "python tktc.py" bzw. mit einem
+Doppelklick unter Windows. (Der Interpreter ist standardmäßig mit der
+Erweiterung .py verknüpft.)
+
+KONFIGURATION: ändern Sie die kommentierten Zeilen in der Datei
+config.py und löschen Sie danach die byte-kompilierte Form config.pyc.
+
+Kurze Hinweise zur BEDIENUNG finden sich in den beiden .sgml-Dateien;
+diese Formate sind verschiedentlich umwandelbar; hier der Einfachheit
+halber als Text noch mitgeliefert.
+
+COPYRIGHT auf TkTeXCAD: Das Programm darf unter den Bedingung der GPL
+benutzt, verändert ... werden, diese ist in der Datei COPYING
+mitgeliefert. (c) 2001 Hilmar Straube; ich übernehme keinerlei
+Ansprüche für jegliche Verluste, die aus der Benutztung dieses
+Programmes entstehen könnten.
+
+Haben Sie Fehler gefunden, Fragen, Vorschläge oder Erweiterungen zum
+Programm, können Sie ich unter hilmstr@web.de erreichen.
+
+
+
+
+
+
+
+
+
+
diff --git a/graphics/tktexcad/readme.en b/graphics/tktexcad/readme.en
new file mode 100644
index 0000000000..7f7a113919
--- /dev/null
+++ b/graphics/tktexcad/readme.en
@@ -0,0 +1,42 @@
+TkTeXCAD
+
+PURPOSE: Creation of LaTeX picture environments.
+
+VERSION: Bugfix to first beta. Vectors work properly now (some
+impossible elevations were not excluded); Copy/Cut/Paste; graphical
+EPS inclusion; short description now in english, too.
+
+INSTALLATION: You'll need the Python interpreter >= 2.0 with the
+extension Tkinter. Python is available at www.python.org, as far as I
+know, the extension is built into the Windows and Mac
+installation. For Unix like platforms, Tk is available at
+www.scriptics.com. Most Linux distributions already include Python/Tk,
+the Windows installation is relatively straight forward.
+ When having a working interpreter, copy the tree .py scripts to a
+directory you like and start it with "python tktc" or with a double
+click unter Windows. (The interpreter is connected to .py files after
+installation.)
+
+CUSTOMIZATION: Change the commented rows in config.py and delete the
+byte-compiled form config.pyc. As you prefer english, you'll probably
+want to change the language of the user interface. So set LANG = 1.
+
+Some notes on USING the program can be found in the two .sgml
+files. This format can be converted into various other formats, for
+convinience, a simple text version is included.
+
+COPYRIGHT: The program may be used, copied, changed ... under the
+conditions of the GPL, given in the COPYING. (c) 2001 Hilmar Straube;
+NO WARRENTY OF ANY KIND.
+
+If you have bug reports, questions, suggestions, extensions, ... let me know: hilmstr@web.de.
+
+
+
+
+
+
+
+
+
+
diff --git a/graphics/tktexcad/tktc.py b/graphics/tktexcad/tktc.py
new file mode 100644
index 0000000000..b20e64086b
--- /dev/null
+++ b/graphics/tktexcad/tktc.py
@@ -0,0 +1,948 @@
+# 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 tkSimpleDialog
+import tkFileDialog
+import tkMessageBox
+
+import traceback # PrettyPrint TB
+import math
+import sys # Traceback, exit
+
+import pickle # Datei speichern/laden
+
+try:
+ import Image # EPS-Unterstützung
+ import ImageTk
+ FPIL = 1
+except ImportError:
+ FPIL = 0
+
+from internat import *
+from config import *
+from globalpars import *
+from picels import *
+
+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 mainwindow: # Benutzeroberfläche
+ def __init__(self, picc):
+ self.root = Tk() # Hauptfenster
+
+ self.selected = [] # Nix angewählt
+ self.parts = pcs() # Noch keine Teilbilder
+ self.tkclass = IntVar() # Für die Auswahl der blockerten
+ self.tkclass.set(-1)
+ self.tkchangeclass = IntVar() # ... und unblockierten Teilbilder in picmenu
+ self.tkchangeclass.set(0)
+
+ self.root.title(PROGNAME)
+ helperwin = Toplevel(self.root)
+ helperwin.transient(self.root)
+ helperwin.protocol('WM_DELETE_WINDOW', self.donothing)
+ self.helperwin = helperwin
+ helperwin.title(CHANGEWINTITLE[LANG])
+ self.changeframe = Frame(helperwin) # Frame für weitere Operationen
+ self.changeframe.pack(fill = BOTH, expand = 1) # Ist hier nur, damit es zerstört werden kann. Das eigentlich wichtige wird in .editwin getan.
+ Label(self.changeframe, text = STATUSNOSELECTION[LANG]).pack()
+
+ # Widgets basteln
+
+ self.yscrollbar = Scrollbar(self.root, orient = VERTICAL)
+ self.xscrollbar = Scrollbar(self.root, orient = HORIZONTAL)
+
+ self.canvas = Canvas(self.root, xscrollcommand = self.xscrollbar.set, yscrollcommand = self.yscrollbar.set, borderwidth = 1, relief = RIDGE, width = CANVASWIDTH, height = CANVASHEIGHT, background = CANVASBG)
+
+ self.yscrollbar['command'] = self.canvas.yview
+ self.xscrollbar['command'] = self.canvas.xview
+
+ self.statusframe = Frame(self.root, borderwidth = 1, relief = SUNKEN)
+ self.statuslabel = Label(self.statusframe)
+ self.statusframe.grid(row = 2, column = 0, columnspan = 2, sticky = E + W)
+ self.statuslabel.pack(side = LEFT)
+
+ # Anzeigen
+
+ self.canvas.grid(row = 0, column = 0, sticky = N + E + S + W)
+ self.yscrollbar.grid(row = 0, column = 1, sticky = N + S)
+ self.xscrollbar.grid(row = 1, column = 0, sticky = W + E)
+ self.root.rowconfigure(0, weight = 1)
+ self.root.columnconfigure(0, weight = 1)
+
+ filemenu = Menu()
+ filemenu.add_command(label = MENUFILELOAD[LANG], command = self.filemenu_load)
+ filemenu.add_command(label = MENUFILEINSERT[LANG], command = self.filemenu_import)
+ filemenu.add_command(label = MENUFILESAVE[LANG], command = self.filemenu_save)
+ filemenu.add_command(label = MENUTEXEXPORT[LANG], command = self.filemenu_export)
+ filemenu.add_separator()
+ filemenu.add_command(label = MENUQUIT[LANG], command = self.filemenu_quit)
+
+ self.root.bind("<Control-l>", self.filemenu_load)
+ self.root.bind("<Control-s>", self.filemenu_save)
+ self.root.bind("<Control-i>", self.filemenu_import)
+ self.root.bind("<Control-x>", self.filemenu_export)
+ self.root.bind("<Control-q>", self.filemenu_quit)
+
+ # Menu bauen
+ newmenu = Menu()
+ newmenu.add_command(label = MENUCIRCLE[LANG], command = self.newcircle)
+ newmenu.add_command(label = MENUCIRCLEAS[LANG], command = self.newcircleas)
+ newmenu.add_command(label = MENULINE[LANG], command = self.newline)
+ newmenu.add_command(label = MENUVECTOR[LANG], command = self.newvec)
+ newmenu.add_command(label = MENUOVAL[LANG], command = self.newoval)
+ newmenu.add_command(label = MENUBEZIER[LANG], command = self.newbezier)
+ newmenu.add_command(label = MENUBB[LANG], command = self.newbb)
+ newmenu.add_command(label = MENUEPS[LANG], command = self.neweps)
+ newmenu.add_separator()
+ newmenu.add_command(label = MENUSIMPLETEXT[LANG], command = self.newsimpletext)
+ newmenu.add_command(label = MENUSHORTSTACK[LANG], command = self.newshortstack)
+ newmenu.add_command(label = MENUTEXTBOX[LANG], command = self.newtextbox)
+
+ self.root.bind("k", self.newcircle)
+ self.root.bind("K", self.newcircleas)
+ self.root.bind("l", self.newline)
+ self.root.bind("L", self.newvec)
+ self.root.bind("o", self.newoval)
+ self.root.bind("q", self.newbezier)
+ self.root.bind("B", self.newbb)
+ self.root.bind("e", self.neweps)
+
+ self.root.bind("t", self.newsimpletext)
+ self.root.bind("a", self.newshortstack)
+ self.root.bind("b", self.newtextbox)
+
+ faktmenu = Menu()
+ self.tkfakt = IntVar()
+ self.tkfakt.set(FAKT)
+ for fakt in FAKTS:
+ faktmenu.add_radiobutton(label = `fakt` + MENUSCALELABEL[LANG], variable = self.tkfakt, value = fakt, command = self.faktmenu_change)
+ faktmenu.add_separator()
+ self.tkfGrid = IntVar()
+ self.tkfGrid.set(1)
+ faktmenu.add_checkbutton(label = MENUSHOWGRID[LANG], variable = self.tkfGrid, command = self.faktmenu_gridf)
+ faktmenu.add_command(label = MENUCHANGEGRID[LANG], command = self.faktmenu_grid)
+ self.root.bind("<Control-g>", self.faktmenu_gridfkey)
+ self.root.bind("<Control-Shift-G>", self.faktmenu_grid)
+
+ selmenu = Menu()
+ selmenu.add_command(label = MENUSELECT[LANG], command = self.getuids)
+ selmenu.add_command(label = MENUDESELECT[LANG], command = self.clearselect)
+ selmenu.add_command(label = MENUDEL[LANG], command = self.delselected)
+ self.root.bind("<space>", self.getuids)
+ self.root.bind("<Escape>", self.clearselect)
+ self.root.bind("<Delete>", self.delselected)
+
+ clipmenu = Menu()
+ clipmenu.add_command(label = CLIPCOPY[LANG], command = self.clip_copy)
+ clipmenu.add_command(label = CLIPCUT[LANG], command = self.clip_cut)
+ clipmenu.add_command(label = CLIPPASTE[LANG], command = self.clip_paste)
+ self.root.bind("<Control-c>", self.clip_copy)
+ self.root.bind("<Control-x>", self.clip_cut)
+ self.root.bind("<Control-v>", self.clip_paste)
+
+ # Passt nirgendwo sonst
+ self.root.bind('<Control-a>', self.picmenu_adjustdxdy)
+
+ self.picmenu = Menu(postcommand = self.updatepicmenu, tearoff = 0) # Abreißen gibt bei diesem Menü heftig Ärger
+ self.viewpicture(picc)
+
+ # Funktionsweise des Callbackwaldes:
+ # die new...-Funktionen erstellen das gefragte Objekt und stellen Fest, ob
+ # ein, zwei oder drei Punkte gefordert sind (mousinputdepth).
+ # Daraufhin rufen sie eine Funktion auf, die einen (ebenfalls leeren) putter darum erstellt.
+ # Der erste Klick füllt in clickcb diese Putterstruktur.
+ # Sind mehr Punkte nötig, werden die Mausbewegungen in motion abgefangen und das picel über
+ # seines putters minfo-Routine über die Position der Maus informiert.
+ # Nach dieser Information wird die Anzeige aufgefrischt.
+ # Bei Tiefe 3 wird dann noch clickcb2 ausgeführt, der motion so ändert, dass minfo2 aufgerufen wird.
+ # Sind alle Informationen aufgerufen, folgt done und ein self.update().
+
+ menu = Menu(self.root)
+ self.root["menu"] = menu
+ menu.add_cascade(label = MENUCLASSES[LANG], menu = self.picmenu)
+ menu.add_cascade(label = MENUFILE[LANG], menu = filemenu)
+ menu.add_cascade(label = MENUNEW[LANG], menu = newmenu)
+ menu.add_cascade(label = MENUDISPLAY[LANG], menu = faktmenu)
+ menu.add_cascade(label = MENUELEM[LANG], menu = selmenu)
+ menu.add_cascade(label = MENUCLIP[LANG], menu = clipmenu)
+
+ # Ist gerade kein Menü vorhanden, soll ein linker Mausklick das Bearbeitungsfenster öffnen
+ self.canvas.bind("<Button-1>", self.editwin)
+ self.frameuid = None
+ self.griduids = []
+
+ def filemenu_load(self, event = None):
+ self.changeframe.destroy()
+ ft = [(FTLABEL[LANG], "*.pic")]
+ filename = tkFileDialog.askopenfilename(title = LOADTITLE[LANG], filetypes = ft)
+ if filename == '':
+ return
+ try:
+ f = open(filename)
+ self.parts = pickle.load(f)
+
+ for picclass in self.parts.pictures: # Für alle Putter, die gerade geladen werden diese Information bereit stellen.
+ for putter in picclass.putters:
+ putter.notifyunpickle(self)
+
+ f.close()
+ self.viewpicture(self.parts.pictures[0])
+ self.changeframe.destroy()
+ self.update()
+ except:
+ tkMessageBox.showerror(title = PROGNAME, message = LOADERROR[LANG] + `sys.exc_info()[0]`)
+ traceback.print_tb(sys.exc_info()[2])
+ def filemenu_save(self, event = None):
+ ft = [(FTLABEL[LANG], "*.pic")]
+ filename = tkFileDialog.asksaveasfilename(title = SAVETITLE[LANG], filetypes = ft, defaultextension = '.pic')
+ if filename == '':
+ return
+ try:
+ f = open(filename, "w")
+ for pc in self.parts.pictures: # Unpickelbare tk-Variablen vernichten
+ pc.untk()
+ pickle.dump(self.parts, f)
+ for picclass in self.parts.pictures: # Für alle Putter, die gerade geladen werden: diese Information bereit stellen.
+ for putter in picclass.putters:
+ putter.notifypickle(self)
+
+ f.close()
+ except:
+ tkMessageBox.showerror(title = PROGNAME, message = SAVEERROR[LANG] + `sys.exc_info()[0]`)
+ traceback.print_tb(sys.exc_info()[2])
+ def filemenu_import(self, event = None): # fast wie load
+ self.changeframe.destroy()
+ ft = [(FTLABEL[LANG], "*.pic")]
+ filename = tkFileDialog.askopenfilename(title = INSERTTITLE[LANG], filetypes = ft)
+ if filename == '':
+ return
+ try:
+ f = open(filename)
+ newparts = pickle.load(f)
+ f.close()
+ for newname in newparts.names:
+ if newname in self.parts.names:
+ self.status(STATUSNAMECOL[LANG])
+ return
+ # Keine Überschneidungen
+ for picclass in newparts.pictures: # Für alle Putter, die gerade geladen werden: diese Information bereit stellen.
+ for putter in picclass.putters:
+ putter.notifypickle(self)
+ self.parts.pictures += newparts.pictures
+ self.parts.names += newparts.names
+ except:
+ tkMessageBox.showerror(title = PROGNAME, message = LOADERROR[LANG] + `sys.exc_info()[0]`)
+ traceback.print_tb(sys.exc_info()[2])
+ def filemenu_export(self, event = None):
+ z = 0
+ for z in range(len(self.parts.names)):
+ f = open(self.parts.names[z] + SUFFIX, 'w')
+ f.write(self.parts.pictures[z].dumptex())
+ def filemenu_quit(self, event = None):
+ self.root.destroy()
+ def faktmenu_change(self, event = None):
+ global fakt
+ fakt = self.tkfakt.get()
+ notifyfaktchange(fakt)
+ self.update()
+ def faktmenu_gridf(self, event = None):
+ global FGRID
+ FGRID = self.tkfGrid.get()
+ self.update()
+ def faktmenu_gridfkey(self, event = None):
+ self.tkfGrid.set(not self.tkfGrid.get())
+ self.faktmenu_gridf()
+ def faktmenu_grid(self, event = None):
+ global GRID
+ g = tkSimpleDialog.askfloat(PROGNAME, LABELNEWGRID[LANG], minvalue = 0.0000001)
+ if g != None:
+ GRID = g
+ self.update()
+ def picmenu_addmenus(self, menu, block): # Hängt an menu Radiobuttons an, möglicherweise enthaltende blockiert, oder auch nicht.
+ if block:
+ blocks = self.parts.pictures[0].getallblocks(self.pic.pc) + [self.pic.pc]
+ for z in range(len(self.parts.pictures)):
+ sf = self.parts.names[z]
+ if block:
+ if self.parts.pictures[z] in blocks:
+ menu.add_radiobutton(label = sf, state = DISABLED, variable = self.tkclass, value = z)
+ else:
+ menu.add_radiobutton(label = sf, state = NORMAL, variable = self.tkclass, value = z)
+ else:
+ menu.add_radiobutton(label = sf, variable = self.tkchangeclass, value = z, command = self.picmenu_changepc)
+ def updatepicmenu(self):
+ self.picmenu.delete(0, 99999) # Das ist unschön!
+
+ self.picmenu.add_command(label = MENUNEWPICCLASS[LANG], command = self.createpic)
+ self.picmenu.add_separator()
+
+ self.picmenu_addmenus(self.picmenu, 1)
+ changemenu = Menu()
+ self.picmenu_addmenus(changemenu, 0)
+
+ self.picmenu.add_separator()
+
+ self.picmenu.add_command(label = MENUNEWINSTANCE[LANG], command = self.picmenu_newpicture)
+ self.picmenu.add_command(label = MENUDELCLASS[LANG], command = self.picmenu_delpc)
+ self.picmenu.add_command(label = MENUSIZECHANGE[LANG], command = self.picmenu_changesize)
+ self.picmenu.add_command(label = MENUADJUSTDXDY[LANG], command = self.picmenu_adjustdxdy)
+
+ self.picmenu.add_cascade(label = MENUCHANGECLASS[LANG], menu = changemenu)
+ def picmenu_getclass(self, nr = 0): # Liest tkclass aus und gibt Fehlermeldung und None bzw. Klasse
+ blocks = rootpc.getallblocks(self.pic.pc) + [self.pic.pc]
+ classnr = self.tkclass.get()
+ if classnr == -1:
+ self.status(STATUSNOCLASSEL[LANG])
+ return None
+ elif self.parts.pictures[classnr] in blocks:
+ self.status(STATUSBLOCKED[LANG])
+ return None
+ else:
+ if nr: # Nutzer der Klasse wählt, ob er Nummer oder Instanz braucht
+ return classnr
+ else:
+ return self.parts.pictures[classnr]
+ def picmenu_newpicture(self, event = None):
+ picclass = self.picmenu_getclass()
+ if picclass == None:
+ return
+ self.pendingpicel = picture(picclass, 1.0, self.pic.pc)
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def picmenu_delpc(self, event = None):
+ nr = self.picmenu_getclass(nr = 1)
+ if nr == None:
+ return
+ picclass = self.parts.pictures[nr]
+ if picclass == None:
+ return
+
+ blocks = self.parts.pictures[0].getallblocks(self.parts.pictures[nr]) # Klassen bestimmen, in deren Instanzen die Klasse enthalten ist.
+ if blocks != [] or nr == 0: # Löschbar ist er nur, wenn nirgendwo Instanzen lungern und es nicht das Hauptbild ist.
+ self.status(STATUSDELCLASSREJECT[LANG])
+ return
+ del self.parts[nr]
+ def picmenu_changepc(self, event = None):
+ pc = self.parts.pictures[self.tkchangeclass.get()]
+ self.viewpicture(pc)
+ self.changeframe.destroy()
+ self.update()
+ def picmenu_changesize(self, event = None): # Fenster zur Größenänderung
+ sc = sizechanger(self.root, self.pic.pc.xsize, self.pic.pc.ysize, self.pic.pc.name, self.parts.names, self.picmenu_sccb)
+ def picmenu_sccb(self, xs, ys, name):
+ self.pic.pc.xsize = xs
+ self.pic.pc.ysize = ys
+
+ self.parts.changename(self.pic.pc.name, name) # in parts und pc ändern
+
+ self.canvas.delete(self.frameuid)
+ self.frameuid = self.canvas.create_rectangle(0, fakt * self.pic.pc.ysize, fakt * self.pic.pc.xsize, 0, fill = None, outline = PICFRAMECOL)
+ self.update()
+ def picmenu_adjustdxdy(self, event = None):
+ self.pic.pc.adjustdxdy()
+ self.update()
+ # ENDE picmenu
+ def clip_copy(self, event = None):
+ #def createpicwork(self, newpicclass, action):
+ # action: 0: Instanz einfügen; action 1: Einzelelemente lassen; action 2: Einzelemente nur verschwinden lassen
+ self.clipboard = pictureclass(0, 0, CLIPBOARDNAME)
+ self.createpicwork(self.clipboard, 1)
+ self.update()
+ def clip_cut(self, event = None):
+ self.clipboard = pictureclass(0, 0, CLIPBOARDNAME)
+ self.createpicwork(self.clipboard, 2)
+ self.update()
+ def clip_paste(self, event = None):
+ self.pendingpicel = picture(self.clipboard, 1.0, self.pic.pc)
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel() # done kümmert sich um die Auflösung in Einzelelemente
+ # ENDE clip
+ def viewpicture(self, picc):
+ self.tkclass.set(-1) # Danach ist die angeklickte Klasse mgl. blockiert
+ try:
+ self.pic.undraw(self.canvas)
+ except AttributeError:
+ pass # Falls pic noch nicht existiert
+ self.pic = picture(picc, 1.0, None)
+ def donothing(self): # Um das Nebenfenster zu erhalten
+ pass
+ def drawgrid(self):
+ self.griduids = []
+ xanz = int(self.pic.pc.xsize / GRID)
+ yanz = int(self.pic.pc.ysize / GRID)
+ if xanz != 0:
+ xstep = self.pic.pc.xsize / float(xanz)
+ if yanz != 0:
+ ystep = self.pic.pc.ysize / float(yanz)
+ for z in range(xanz):
+ self.griduids.append(self.canvas.create_line(fakt * (z * xstep),
+ fakt * (self.pic.pc.ysize - 0),
+ fakt * (z * xstep),
+ 0, # fakt * (ysize - ysize)
+ fill = GRIDCOL))
+ for z in range(yanz):
+ self.griduids.append(self.canvas.create_line(0,
+ fakt * (self.pic.pc.ysize - z * ystep),
+ fakt * self.pic.pc.xsize,
+ fakt * (self.pic.pc.ysize - z * ystep),
+ fill = GRIDCOL))
+ for uid in self.griduids:
+ self.canvas.lower(uid)
+ def undrawgrid(self):
+ for uid in self.griduids:
+ self.canvas.delete(uid)
+ def update(self):
+ # Rahmen und Netz und Bild entfernen (falls vorhanden)
+ if self.frameuid != None:
+ self.canvas.delete(self.frameuid)
+ self.undrawgrid()
+ self.pic.undraw(self.canvas)
+
+ if FGRID:
+ self.drawgrid()
+ self.frameuid = self.canvas.create_rectangle(0, fakt * self.pic.pc.ysize, fakt * self.pic.pc.xsize, 0, fill = None, outline = PICFRAMECOL) # Rahmen
+
+ self.pic.draw(0, 0, self.pic.pc.ysize, self.canvas) # Bild
+ self.canvas['scrollregion'] = self.canvas.bbox(ALL) # Scrollleisten einstellen
+ self.selected = [] # mgl. Auswahl angezeigt löschen, Anzeige ist schon weg (Bilder auflösen könnte Ärger geben beim weiter halten)
+ def status(self, text):
+ self.statuslabel['text'] = text
+ def editwin(self, event): # Dem Mausklick nächste Uid suchen, putter holen und aufrufen
+ x = event.widget.canvasx(event.x)
+ y = event.widget.canvasy(event.y)
+ if FGRID: # Das Netz stört bei find-closest
+ self.undrawgrid()
+ uid = event.widget.find_closest(x, y)[0]
+ if FGRID:
+ self.drawgrid()
+ putter = self.pic.getputter(uid)
+ # Putter == 0: Rahmen wurde angeklickt:
+ if putter == 0:
+ return
+ self.editwin2(putter)
+ def editwin2(self, putter): # wird von putter und done (Objekt erzeugt) aufgerufen
+ self.changeframe.destroy()
+ self.changeframe = Frame(self.helperwin)
+ self.changeframe.pack(fill = BOTH, expand = 1)
+ putter.change(self.changeframe, self)
+ def bindings(self, done, cancel):
+ self.canvas.bind("<Motion>", self.motion)
+ self.canvas.bind("<Button-1>", done)
+ self.canvas.bind("<Button-3>", cancel)
+ self.movecb_uids = []
+ def unbind(self):
+ self.canvas.bind("<Button-1>", self.editwin) # Zurückbinden
+ self.canvas.unbind("<Motion>")
+ self.canvas.unbind("<Button-3>")
+ def motion(self, event):
+ self.pendingputter.undraw(self.canvas)
+ x = round(self.canvas.canvasx(event.x) / float(fakt * GRID)) * GRID
+ y = self.pic.pc.ysize - round(self.canvas.canvasy(event.y) / float(fakt * GRID)) * GRID
+ if self.motionmode == 0:
+ self.pendingputter.x = x
+ self.pendingputter.y = y
+ elif self.motionmode == 1:
+ self.pendingputter.minfo(x, y) # Information übertragen
+ elif self.motionmode == 2:
+ self.pendingputter.minfo2(x, y) # Falls drei Mausklicks nötig sein sollten ...
+ self.pendingputter.draw(0, 0, self.pic.pc.ysize, self.canvas) # neu zeichnen
+ def cancel(self, event):
+ self.unbind()
+ self.status(STATUSCANCEL[LANG])
+ self.pendingputter.undraw(self.canvas)
+ del self.pendingpicel
+ del self.pendingputter
+ def done(self, event):
+ if self.mouseinputdepth == 1:
+ self.pendingputter.x = round(self.canvas.canvasx(event.x) / float(fakt * GRID)) * GRID
+ self.pendingputter.y = self.pic.pc.ysize - round(self.canvas.canvasy(event.y) / float(fakt * GRID)) * GRID
+ self.unbind()
+ self.status(STATUSDONE[LANG])
+
+ self.pic.putters.append(self.pendingputter) # Erstelltes Objekt einfügen sowie hiesige Referenz entfernen
+
+ if not(self.pendingputter.pe.__class__ == picture and self.pendingputter.pe.pc.name == CLIPBOARDNAME):
+ self.editwin2(self.pendingputter) # Auflöser!
+
+ self.pendingputter.notifydone(self) # Benachrichtigung schicken
+
+ del self.pendingpicel
+ del self.pendingputter
+
+ self.update() # UIDs auffrischen
+ def clickcb(self, event):
+ # Putter mit richtigen Anfangskoordinaten versorgen
+ self.pendingputter.x = round(self.canvas.canvasx(event.x) / float(fakt * GRID)) * GRID
+ self.pendingputter.y = self.pic.pc.ysize - round(self.canvas.canvasy(event.y) / float(fakt * GRID)) * GRID
+ self.status(STATUSPOINT2[LANG])
+ if self.mouseinputdepth == 2:
+ self.bindings(self.done, self.cancel) # done, cancel und motion aktivieren
+ elif self.mouseinputdepth == 3:
+ self.bindings(self.clickcb2, self.cancel)
+ elif self.mouseinputdepth == 200:
+ self.bindings(self.getuids2, self.cancel)
+ self.motionmode = 1
+ def clickcb2(self, event):
+ self.status(STATUSPOINT3[LANG])
+ if self.mouseinputdepth == 3:
+ self.bindings(self.done, self.cancel)
+ self.motionmode = 2
+ def newpicel(self, event = None):
+ self.pendingputter = multiputter(0, 0, 0, 0, 1, self.pendingpicel)
+ self.status(STATUSPOINT1[LANG])
+ self.motionmode = 0
+ if self.mouseinputdepth > 1:
+ self.bindings(self.clickcb, self.cancel)
+ else:
+ self.bindings(self.done, self.cancel)
+ def newline(self, event = None):
+ self.pendingpicel = line(1, 1, 1, 0)
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def newvec(self, event = None):
+ self.pendingpicel = line(1, 1, 1, 1)
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def newcircleas(self, event = None):
+ self.pendingpicel = circle(1, 1)
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def newcircle(self, event = None):
+ self.pendingpicel = circle(1, 0)
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def newoval(self, event = None):
+ self.pendingpicel = oval(3, 2)
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def newbezier(self, event = None):
+ self.pendingpicel = bezier(0, 0, 1, 1, 2, 1)
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def newbb(self, event = None):
+ self.pendingpicel = bb(2, 2)
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def neweps(self, event = None):
+ self.pendingpicel = eps()
+ ft = [(FTLABEL[LANG], "*.eps")]
+ filename = tkFileDialog.askopenfilename(title = EPSLOADTITLE[LANG], filetypes = ft)
+ if FPIL:
+ if not self.pendingpicel.loadfile(filename, self):
+ del self.pendingpicel
+ return
+ else:
+ self.pendingpicel.filename = filename
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def newsimpletext(self, event = None):
+ self.pendingpicel = simpletext(INITIALSIMPLETEXT[LANG])
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def newshortstack(self, event = None):
+ self.pendingpicel = shortstack(1, INITIALSHORTSTACK[LANG])
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def newtextbox(self, event = None):
+ self.pendingpicel = textbox(0, 0, 1, 1, DEFAULTDASHLEN, '')
+ self.mouseinputdepth = self.pendingpicel.clickdepth
+ self.newpicel()
+ def getuids(self, event = None):
+ self.pendingpicel = textbox(0, 0, 1, 1, 0, INITIALTEXTBOX[LANG])
+ self.mouseinputdepth = 200
+ self.newpicel()
+ # Unschön (200): Teilt den Nachfolgenden Prozeduren mit, dass hier eigentlich nur die Anzeigeroutinen der Textbox benutzt werden, aber nur deren Koordinaten interessieren. Damit werden dann in einer veränderten Version von done die richtigen Koordinaten gesichert.
+ def getuids2(self, event): # Genau hierhin. self.pendingpicel dürfte nun die gewünschten Daten enthalten
+ self.update() # Eine einfache undraw-Routine scheitert daran, dass fill unterschiedliche Werte anfangs hat.
+ # Koordinaten ausrechnen
+ self.pendingputter.undraw(self.canvas) # Die uids löschen, die zum gezogenen Rahmen gehören, später wird er auch gelöscht.
+ x = fakt * self.pendingputter.x
+ y = fakt * (self.pic.pc.ysize - self.pendingputter.y)
+ x2 = fakt * self.pendingpicel.dx + x
+ y2 = fakt * (self.pic.pc.ysize - (self.pendingputter.y + self.pendingpicel.dy))
+ uids = self.canvas.find_overlapping(x, y, x2, y2)
+ uids = list(uids) # find liefert tupel
+
+ putters = []
+ for uid in uids:
+ for putter in self.pic.putters:
+ if uid in putter.uids and putter not in putters:
+ putters.append(putter)
+ self.selected = putters
+
+ self.status(`len(putters)` + STATUSXELSSELECTED[LANG])
+
+ # Hilfsrechteck nun endgültig löschen
+
+ del self.pendingpicel
+ del self.pendingputter
+
+ self.unbind()
+ self.select()
+ def select(self): # Auswahl anzeigen
+ for putter in self.selected:
+ putter.mark(self.canvas)
+ def clearselect(self, event = None): # Auswahl löschen
+ self.selected = []
+ self.status(STATUSDESELECT[LANG])
+ self.update()
+ def delselected(self, event = None, update = 1): # AusGEWÄHLTE löschen
+ for putter in self.selected:
+ putter.undraw(self.canvas)
+ index = self.pic.putters.index(putter)
+ del self.pic.putters[index]
+ if update:
+ self.status(STATUSELSDELETED[LANG])
+ self.update()
+ def createpic(self):
+ if self.selected != []:
+ # Namen abfragen und überprüfen
+ name = tkSimpleDialog.askstring(PROGNAME, LABELNEWCLASSNAME[LANG])
+ if name == None or not(self.parts.newname(name)) or name == "":
+ self.status(STATUSINVALIDNAME[LANG])
+ return
+ pc = pictureclass(0, 0, name)
+ self.createpicwork(pc, 0)
+ self.update()
+ self.parts.append(pc)
+ self.status(STATUSNEWCLASS[LANG])
+ else:
+ self.status(STATUSNOSELECTION[LANG])
+ def createpicwork(self, newpicclass, action):
+ # action: 0: Instanz einfügen; action 1: Einzelelemente lassen; action 2: Einzelemente nur verschwinden lassen
+ if self.selected != []:
+ x1, y1, x2, y2 = plboundingbox(self.selected)
+ dx = x2 - x1
+ dy = y2 - y1
+
+ newpicclass.xsize, newpicclass.ysize = dx, dy
+
+ # Alle putter bewegen
+ for putter in self.selected:
+ putter.x -= x1
+ putter.y -= y1
+ # Neue Bildklasse damit füllen
+ if action != 1:
+ newpicclass.putters = self.selected
+ else: # Für Kopieren brauchen wir eine hier und eine Unabhängige in der Zwischenablage
+ for el in self.selected:
+ elcopy = el.copy()
+ el.x += x1
+ el.y += y1
+ newpicclass.putters.append(elcopy)
+ self.pic.undraw(self.canvas) # alle Canvasitems entfernen (Referenz könnte verschwinden)
+ if action != 1:
+ self.delselected(update = 0) # ... jetzt
+ if action == 0:
+ self.pic.putters.append( # Anstelle dessen kommte nur ein putter für das neu erstellte Objekt der neuen Klasse
+ multiputter(x1, y1, 0, 0, 1, picture(newpicclass, 1.0, self.pic.pc))
+ )
+ self.changeframe.destroy()
+
+class multiputter: # Steht für einen multiput-Befehl
+ def dumptex(self):
+ if self.pe.__class__ in [textbox, bb]: # Dieser macht das selbst, da LaTeX nicht mit negativen Höhen und Breiten rechnet.
+ return self.pe.dumptex(self.x, self.y, self.dx, self.dy, self.n)
+ else:
+ if self.n != 1:
+ sf = '\\multiput' + `(self.x, self.y)` + `(self.dx, self.dy)` + '{' + `self.n` + '}{'
+ else:
+ sf = '\\put' + `(self.x, self.y)` + '{'
+
+ sf += self.pe.dumptex() + '}'
+ return sf
+ def __init__(self, x, y, dx, dy, n, pe):
+ "dx, dy - jew. Verschiebung beim n-ten Anzeigen"
+ self.x = x
+ self.y = y
+ self.pe = pe
+ self.dx = dx
+ self.dy = dy
+ self.n = n
+ self.multiput = 1
+ self.uids = []
+ def mark(self, canvas):
+ for uid in self.uids:
+ type = canvas.type(uid)
+ if type in ['arc', 'oval']:
+ canvas.itemconfigure(uid, outline = SELECTCOL)
+ elif type == 'image':
+ pass
+ else:
+ canvas.itemconfigure(uid, fill = SELECTCOL)
+ def draw(self, x, y, maxy, canvas, scale = 1): # Element mehrfach zeichnen
+ # Ist firstcall gesetzt, werden x- und y-Position nicht skaliert, aber scale übergeben.
+ # Für den ersten Aufruf aus einem picture
+ self.uids = []
+ for z in range(self.n):
+ self.uids += self.pe.draw(
+ scale * (self.x + z * self.dx + x),
+ scale * (self.y + z * self.dy + y),
+ scale * maxy, canvas, scale)
+ return self.uids
+ def undraw(self, canvas):
+ self.pe.undrawhook()
+ for uid in self.uids:
+ canvas.delete(uid)
+ self.uids = []
+ def minfo(self, x, y): # Leitet nur weiter
+ self.pe.minfo(self.x, self.y, x, y)
+ def minfo2(self, x, y):
+ self.pe.minfo2(self.x, self.y, x, y)
+ def dxdy(self): # Liefert Abmessungen des multiput in LaTeX-Koordinaten
+ x1, y1, x2, y2 = self.pe.dxdy(self.x, self.y)
+ if self.dx > 0:
+ x2 += (self.n - 1) * self.dx
+ else:
+ x1 += (self.n - 1) * self.dx
+ if self.dy > 0:
+ y2 += (self.n - 1) * self.dy
+ else:
+ y1 += (self.n - 1) * self.dy
+ return x1, y1, x2, y2
+ # moveput, click2 und click3 nutzen vom mainwindow die Routinen in motionmode 0 bis 2
+ def click2(self):
+ self.pe.savestate()
+ p = self.tkparent
+ p.motionmode = 1
+ p.pendingputter = self
+ p.bindings(self.done, self.c23cancel)
+ def click3(self):
+ self.pe.savestate()
+ p = self.tkparent
+ p.motionmode = 2
+ p.pendingputter = self
+ p.bindings(self.done, self.c23cancel)
+ def c23cancel(self, event):
+ self.pe.cancel()
+ self.cancel()
+ def moveput(self):
+ p = self.tkparent
+ p.motionmode = 0
+ self.cancelx = self.x
+ self.cancely = self.y
+ p.bindings(self.done, self.mpcancel)
+ p.pendingputter = self
+ def mpcancel(self, event):
+ self.x = self.cancelx
+ self.y = self.cancely
+ self.cancel()
+ def cancel(self):
+ self.tkparent.status(STATUSCANCEL[LANG])
+ del self.tkparent.pendingputter
+ self.tkparent.unbind()
+ self.tkparent.update()
+ def done(self, event):
+ self.tkparent.status(STATUSDONE[LANG])
+ del self.tkparent.pendingputter
+ self.tkparent.unbind()
+ def capply(self, event): # Eingegebene Felder lesen und fehlerprüfen
+ try:
+ self.dx = self.tkdx.get()
+ except ValueError:
+ self.tkdx.set(self.dx)
+ try:
+ self.dy = self.tkdy.get()
+ except ValueError:
+ self.tkdy.set(self.dy)
+ try:
+ if self.tkn.get() < 1:
+ raise ValueError
+ self.n = self.tkn.get()
+ if self.n == 1:
+ self.tkBMultiAuf['state'] = DISABLED
+ else:
+ self.tkBMultiAuf['state'] = NORMAL
+ except ValueError:
+ self.tkn.set(self.n)
+ self.tkcallredraw()
+ def change(self, frame, parent):
+ # Multifelder anzeigen
+ self.tkframe = frame # Speichern für die Auflösebefehle
+ self.tkparent = parent
+ self.tkcallredraw = parent.update
+ Label(frame, text = '\[multi]put').grid(row = 0, column = 0, columnspan = 2)
+ Label(frame, text = MULTIDX[LANG]).grid(row = 1, column = 0)
+ Label(frame, text = MULTIDY[LANG]).grid(row = 2, column = 0)
+ Label(frame, text = MULTIN[LANG]).grid(row = 3, column = 0)
+
+ self.tkdx = DoubleVar()
+ self.tkdy = DoubleVar()
+ self.tkn = IntVar()
+ self.tkdx.set(self.dx)
+ self.tkdy.set(self.dy)
+ self.tkn.set(self.n)
+ e1 = Entry(frame, textvariable = self.tkdx)
+ e1.grid(sticky = W + E, row = 1, column = 1)
+ e2 = Entry(frame, textvariable = self.tkdy)
+ e2.grid(sticky = W + E, row = 2, column = 1)
+ e3 = Entry(frame, textvariable = self.tkn)
+ e3.grid(sticky = W + E, row = 3, column = 1)
+ e1.bind("<Return>", self.capply)
+ e2.bind("<Return>", self.capply)
+ e3.bind("<Return>", self.capply)
+
+ # Punktfelder nach picel anzeigen
+ Button(frame, text = MULTISETROOT[LANG], command = self.moveput).grid(row = 4, columnspan = 2, sticky = E + W)
+ if self.pe.clickdepth > 1:
+ Button(frame, text = MULTISETP2[LANG], command = self.click2).grid(row = 5, columnspan = 2, sticky = E + W)
+ if self.pe.clickdepth > 2:
+ Button(frame, text = MULTISETP3[LANG], command = self.click3).grid(row = 6, columnspan = 2, sticky = E + W)
+
+ self.tkBMultiAuf = Button(frame, text = MULTIUNMULTI[LANG], state = [NORMAL, DISABLED][self.n == 1], command = self.cbMultiAuf)
+ self.tkBMultiAuf.grid(row = 7, column = 0, sticky = E + W)
+ if self.pe.__class__ == picture:
+ Button(frame, text = MULTIUNPIC[LANG], command = self.cbPicAuf).grid(row = 7, column = 1, sticky = E + W)
+ # picel auffordern, sein Konfigurationsfenster anzuzeigen
+ peframe = Frame(frame, width = 100, height = 100, borderwidth = 2, relief = RIDGE)
+ peframe.grid(sticky = N + S + W + E, columnspan = 2)
+
+ self.pe.change(peframe, self.tkcallredraw)
+ def copy(self):
+ return multiputter(self.x, self.y, self.dx, self.dy, self.n, self.pe.copy())
+ def cbMultiAuf(self): # Multiputter in viele putter auflösen
+ # Referenz zum enthaltenden Bild holen
+ putters = self.tkparent.pic.pc.putters # pic ist eigentlich nicht nötig
+ # Neue Putter für n - 1 erstellen und sich selbst zurückstufen
+ for z in range(1, self.n):
+ pecopy = self.pe.copy()
+ newputter = multiputter(self.x + z * self.dx, self.y + z * self.dy, 0, 0, 1, pecopy)
+ putters.append(newputter)
+ self.dx = 0
+ self.dy = 0
+ self.n = 1
+ # Änderungen anzeigen
+ self.tkparent.update()
+ self.tkframe.destroy()
+ def cbPicAuf(self):
+ self.undraw(self.tkparent.canvas)
+ # Referenz zum enthaltenden Bild holen
+ putters = self.tkparent.pic.pc.putters # pic ist eigentlich nicht nötig
+
+ # Alle Bildelemente aus Klasse duplizieren und direkt in die enthaltende Klasse einfügen
+ for z in range(self.n):
+ for putter in self.pe.pc.putters:
+ pecopy = putter.pe.copy()
+ pecopy.doscale(self.pe.scale)
+ newputter = multiputter(self.pe.scale * (putter.x + self.dx * z) + self.x,
+ self.pe.scale * (putter.y + self.dy * z) + self.y,
+ self.pe.scale * putter.dx,
+ self.pe.scale * putter.dy,
+ putter.n, pecopy)
+ putters.append(newputter)
+ del putters[putters.index(self)] # Die Instanz entfernt den letzten Bezug auf sich selbst.
+ # Änderungen anzeigen
+ self.tkparent.update()
+ if self.__dict__.has_key("tkframe"):
+ self.tkframe.destroy()
+ def notifydone(self, parent):
+ self.tkparent = parent
+ if self.pe.__class__ == picture and self.pe.pc.name == CLIPBOARDNAME:
+ self.cbPicAuf()
+ def notifyunpickle(self, parent): # Dies wird für jedes frisch entpickelte Element aufgerufen.
+ if self.pe.__class__ == eps:
+ self.pe.loadfile(self.pe.filename, parent)
+ def notifypickle(self, parent): # Dies wird für jedes frisch gepickelte Element aufgerufen.
+ if self.pe.__class__ == eps:
+ self.pe.loadfile(self.pe.filename, parent)
+ parent.update()
+
+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.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()
+
+rootpc = pictureclass(NEWPIC[0], NEWPIC[1], NEWPIC[2])
+
+win = mainwindow(rootpc)
+win.parts.append(rootpc)
+
+win.update()
+
+win.status(PROGNAME)
+
+win.root.mainloop()
+
+
+
+
+
+