# File MagicTriangleV06.py => wurzel's webpage # File MagicTriangleV05.py copyright info # File MagicTriangleV04.py FINAL VERSION !!! # from File MagicTriangleV02.py # from File MagicTriangleV01.py # from File testPopV02.py # from File prelimsV02.py 2003 Feb 23 # Copyright(C)2003, 2004 Roy A. Keir and Marilyn L. Keir # v06 incorporates stuff from algGrid03.py to make it a single # file for wurz's webpage. Should make distribution simpler. # v05 merely adds copyright information and permission so that # MagicTriangle can appear in wurzel parsons-keir's web page for # fun and educational Python programs. We have chosen to allow # non-commercial uses for free. Commercial users must negotiate # with us for a license. # ================================================================= from Tkinter import * ##from algGrid03 import * from math import sqrt, atan, sin, cos, tan, pi # ================================================================= # ================================================================= # INCORPORATING AN AUXILIARY FILE TO MAKE DISTRIBUTION SIMPLER # File algGrid03.py 2003 Mar 06 # Copyright (C)2003, Roy A. Keir and Marilyn L. Keir # from: File algGridV02.py 2003 Mar 06 # from: File algGridV01.py 2003 Feb 18 # V03 exchanges row & col parameters and makes more reasonable # guesses about Var() handling, I hope. # Stuff to simplify building a grid and stuffing it. V02 adds # the gridBuild() procedure. # Summary of function def's # def gridConstLabel(mstr, conClm, conRow, conText # , fgColor = "Black"): # def gridIntVarLabel(mstr, ivlClm, ivlRow, ivlIntVar # , fgColor = "Black"): # def gridButton(mstr, btnClm, btnRow, btnText, btnCallBack, # rowSpn = 1, colSpn = 1, btnClr = "Black"): # def gridSpacer(mstr, spcrClm, spcrRow, spcrWidth) # def gridEntry(mstr, entryClm, entryRow, entryVar): # def gridBuild(mstr , colChars , rowHts , makeVisible = 0): # ================================================================= ##from Tkinter import * # Special grid packages. def gridConstLabel(mstr, conClm, conRow, conText , fgColor = "Black"): c = Label(mstr, text = conText, padx = 2, pady = 2, fg = fgColor, bd = 1, relief = RAISED) c.grid(column = conClm, row = conRow, sticky = N, pady = 3) return c def gridIntVarLabel(mstr, ivlClm, ivlRow, ivlIntVar , fgColor = "Black"): iV = Label(mstr, textvariable = ivlIntVar, padx = 2, pady = 2, fg = fgColor, bd = 1, relief = RAISED) iV.grid(column = ivlClm,row = ivlRow, sticky = N) return iV def gridButton(mstr, btnClm, btnRow, btnText, btnCallBack, colSpn = 1, rowSpn = 1, btnClr = "Black"): b = Button(mstr, text = btnText, command = btnCallBack, takefocus = "YES", fg = btnClr) b.grid(column = btnClm, row = btnRow, columnspan = colSpn, rowspan = rowSpn, sticky = NE) return b def gridSpacer(mstr, spcrClm, spcrRow, spcrWidth): s = Label(mstr, text = " "*spcrWidth) s.grid(column = spcrClm, row = spcrRow, sticky = NW) return s def gridEntry(mstr, entryClm, entryRow, entryVar): e = Entry(mstr, width = 7, textvariable = entryVar, justify = RIGHT) e.grid(column = entryClm, row = entryRow, sticky = N) return e # ================================================================= # To use procedure gridBuild(), prepare two Python lists: # (List 1): for each row in the grid, put in the maximum # number of lines of text you will wish to enter in that # row, as in "RowHeights" below. # # # 7 visible rows (one with two lines, one with 4) #RowHeights = [ 1, 1, 1, 1, 2, 4, 1] # (List 2): for each column in the grid, put in the maximum # number of characters you will wish to enter in that # column, as in "ClmWidths" below. gridBuilder uses # a constant-width font ("Courier", 8) for spacing. # # # 5 visible columns #ClmWidths = [ 8 , 12 , 12 , 12 , 12 ] #ClmTitles = [" ","IntVar","StringVar","DoubleVar","BooleanVar"] # Then call gridBuild as follows: #g= gridBuild(frame, ClmWidths, RowHeights, makeVisible = 0) # Calling with makeVisible = 1 will expose the grid structure. # ================================================================= #g= gridBuild(frame, ClmWidths, RowHeights, makeVisible) def gridBuild(mstr , colChars , rowHts , makeVisible = 0): if makeVisible: bdDbg , rlfDbg = 1 , RIDGE # debug else: bdDbg , rlfDbg = 1 , FLAT # normal gF = ("Courier", 8) # constant-spacing font rowHts = [1] + rowHts + [1] colChars = [1] + colChars + [1] cuR = len(rowHts) cuC = len(colChars) # bottom and top rows cSpTHdls = [] cSpBHdls = [] for n in range(len(colChars)): # bottom row if makeVisible: rSp = "*" else: rSp = " " cHB = Label(mstr, text = rSp * colChars[n] , borderwidth = bdDbg , relief = rlfDbg, font = gF) cSpBHdls.append(cHB) cHB.grid(row = cuR - 1, column = n, padx = 2) # top row r = n % 10 rSp0 = "%d" % r rSpN = "." cHT = Label(mstr , text = rSp0 + rSpN * (colChars[n] - 1) , borderwidth = bdDbg, relief = rlfDbg , font = gF) cSpTHdls.append(cHT) cHT.grid(row = 0, column = n, padx = 2) # right- and left-most columns rSpLHdls = [] rSpRHdls = [] for n in range(1, len(rowHts) - 1): r = n % 10 # right-most column if makeVisible: rSp = "*" cSp = "\n*" else: rSp = " " cSp = "\n " rHR = Label(mstr, text = rSp + cSp * (rowHts[n] - 1) , borderwidth = bdDbg , relief = rlfDbg, font = gF) rSpRHdls.append(rHR) rHR.grid(row = n, column = cuC - 1, pady = 2) # left-most column rSp = "%d" % r cSp = "\n." rHL = Label(mstr, text = rSp + cSp * (rowHts[n] - 1) , borderwidth = bdDbg , relief = rlfDbg, font = gF) rSpLHdls.append(rHL) rHL.grid(row = n, column = 0, pady = 2) # ================================================================= # ================================================================= # ================================================================= # CONSTANTS AND INITIAL VALUES # shared halftan30 = 0.5*tan(pi/6.0) sDgts = "+-123456789" dgts = "0123456789" copyrightNotice =\ "COPYRIGHT (C) 2003, 2004 Roy A. Keir and Marilyn L. Keir" ##aboutCopyPermTitle = " About using this copyrighted program... " ##aboutCopyPermText =\ ##""" ##Copyright (c) 2003, 2004 Roy A. Keir and Marilyn L. Keir ## ##This program is free for educational and private, noncommercial ##use. You may modify and/or redistribute it so long as you do not ##charge to do so, and so long as this copyright message and disclaimer ##are retained in their original form. If you wish to make ##commercial use of this program, please contact the authors to make ##licensing arrangements: ## ## rmkeir @ earthlink . net ## ##If you make improvements to this software, we'd love to see them ##and have an opportunity to incorporate them into the main distribution. ## ##IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, ##SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF ##THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH ##DAMAGE. ## ##THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT ##LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A ##PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, ##AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, ##SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ##""" ## # ----------------------------------------------------------------- # stuff from prelim headerPrelim = " SO, WHAT'S THE PROBLEM HERE ? " headerRub = " LET'S LOOK AT THE REAL TARGET... " ###headerText = " WELCOME TO THE MAGIC TRIANGLE ! " howMvVxTitle = " How to change the shape of TARGET : " howMvVxText =\ """ To make the nearest vertex jump to a new location outside of its gray finder circle, click the mouse cursor at the new location. If you hold the mouse button down while moving the mouse, you can drag the vertex anywhere. For precise placement, you may drag any vertex from its current location by placing the cursor inside its gray circle and dragging. The offset between the vertex and the cursor location will be preserved, so that the cursor will not block the view as you dock the vertex at its new location. To demonstrate, try to place any vertex exactly on top of any other, with and without using the offset method. You can also move any vertex precisely to a new location by typing new values for its coordinates, then clicking on the Update button.""" # ---------------------------------------------------------------------------------------- buildTgtTitle = " How to construct the TARGET triangle : " buildTgtText =\ """ Start with an arbitrary GIVEN triangle, shown in blue, (we arbitrarily used an equilateral triangle for its symmetry, but any shape will do). Next, construct an isosceles triangle with vertex angle of 120 degrees (2 pi / 3) outside of and along one side of the GIVEN triangle. Do the same to the other sides. Connect the 120-deg. vertices to form the TARGET triangle, shown in red. Since GIVEN is equilateral, it is easy to see that TARGET must also be equilateral, but what would happen if you could replace GIVEN with a scalene triangle ???""" q1TgtTitle = " The main question : " q1TgtText =\ """ Suppose that you could change the shape of the blue GIVEN triangle by dragging a vertex to any place on the screen (for example making GIVEN obtuse by moving C left until the angle at A is greater than 90 degrees) : What do you suppose would happen to the shape of TARGET ? Do you think you could prove it ? How would you begin ? Give these questions some thought, then click 'I'M READY'. You'll find more suggestions and questions under the 'MORE QUESTIONS :' button...""" # ---------------------------------------------------------------------------------------- # moreQTgt will also appear from a callback on the main screen. moreQTgtTitle = " Some additional questions to ponder... " moreQTgtText =\ """ If you were to drag vertex C to a point on the line between A and B, what would happen to the area of GIVEN ? What would happen to the size, shape, and location of TARGET ? Proof ? Similarly, what would happen if you dragged C to a point along the extension of the line between A and B but beyond B ? Proof ? If you drag C ACROSS the line between A and B : What will happen to TARGET ? If you drag C farther until GIVEN is equilateral again : What will happen to TARGET ? If you move C in a small circle around this location : What will happen to TARGET ? Could you prove your answers ? How would you start ? Mathematically, given the coordinates of A, B, and C : (1) give an algorithm for the location of TARGET (2) give an algorithm for the area of TARGET""" # ----------------------------------------------------------------- # STUFF FOR 'About...' aboutThisProbTitle = " About this Triangle Problem... " aboutThisProbText =\ """ All we know about this problem is that it appeared on the 1956 Stanford Mathematics Contest for high school students. The task was to present a mathematical proof of the relationship between shape of the TARGET triangle and the shape of the GIVEN triangle. The problem was mentioned in a planning meeting for the University of Utah Mathematics Department's "Math Circle" for interested high school students (and a few qualified junior high students). This is a very exciting and educational gathering of math-oriented students with members of the faculty. The faculty member who brought up the problem had solved it in 1956, one of only three who succeeded. If anyone reading this knows more about the problem, its discoverer, what the discoverer was really looking for at the time of the discovery, who gave the first proof, or where the problem appears in any publication, we would be most grateful for the information. Send any information to: keir @ math . utah . edu""" aboutCopyPermTitle = " About using this copyrighted program... " aboutCopyPermText =\ """ Copyright (c) 2003, 2004 Roy A. Keir and Marilyn L. Keir This program is free for educational and private, noncommercial use. You may modify and/or redistribute it so long as you do not charge to do so, and so long as this copyright message and disclaimer are retained in their original form. If you wish to make commercial use of this program, please contact the authors to make licensing arrangements: rmkeir @ earthlink . net If you make improvements to this software, we'd love to see them and have an opportunity to incorporate them into the main distribution. IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. """ aboutThisPrgmTitle = " About this Program..." aboutThisPrgmText =\ """ This program was written because Marilyn told Roy about the problem just when Roy was looking for a relatively simple application to program as a way to learn a new (to him) programming language called 'Python' (and it also let him verify his guess about the shape of the TARGET triangle without having to prove it). The program was written in Python 2.2.2, using only the standard Windows Python download (available free at www.python.org). The code itself doesn't use any Windows-specific stuff, so it should run just fine on any recent version of Python on Linux, Unix, or Mac, according to happy Python users. The program has been run in Python 2.3.2 (Windows XP) and on a linux machine. My programming background is scanty but includes one or more programs in each of 10 languages. I wrote a couple of small applications in C, one with color graphics and the other with an interface to a popular database system. I gave up programming when it seemed necessary to learn either C++ or Java and was only enticed by the promise of a quick path to acceptable GUI's. While Python's documentation is copious and accessible, I have had a hard time finding some of the information about Tkinter, the graphics engine. Even so, it has been only a short time from inspiration to completion, and the experience has been great fun. The two files that make up the program total only 41KB (much of it text like this!), even with the primitive coding methods I used. I am a Python fan.""" aboutMareWareTitle = " About MareWare..." aboutMareWareText =\ """ MareWare is a family partnership with two owners, two directors, two presidents, two supervisors, two workers, and two gofers, comprising a total of two people. Most of the inspiration comes from Marilyn, most of the perspiration from Roy. 'The Magic Triangle' is a toy and demonstration, not a product. If you want to include it in a product, contact us. MareWare's previous product was 'AlgeBrush', written before Utah public schools had color monitors, mouses, or Windows. A student using AlgeBrush makes a line-drawing, in color, using only algebraic functions and relations with domain and/or range constraints. It was DOS-based and keyboard-based, of necessity, and there was no convenient way to print students' drawings. 'AlgeBrush for Windows' is on the drawing boards and was the bait for Roy to learn Python and Tkinter and write this program. Marilyn formerly taught in Utah public schools, most recently at Skyline High School, where she taught classes ranging from entry-level Algebra to BC Calculus. She has won awards for her teaching at the local, state, and national levels (Granite Excel), PAESMT, Woodrow Wilson Fellowship, Tandy National Scholar among them) and is currently in the Math department of the University of Utah teaching math and math teaching methods to prospective math teachers. Her full c.v. can be found on the U.'s website under Math/Keir. Roy is a retired computer designer (head of logic design of the Bendix G-20, in case any of you reading this are really, really old) who taught in the Computer Science Department at the University of Utah 1967-1976. The best prize he ever won was Marilyn.""" # ================================================================= # CANVAS MISC. -- PRELIM pCnvsWd = 160 pCnvsHt = 120 x0, x1, x2 = pCnvsWd/2 - 40, pCnvsWd/2 + 40, pCnvsWd/2 y0, y1, y2 = 85, 85, 26 # ================================================================= # CANVAS MISC. -- MAIN rCnvsWd = 600 rCnvsHt = rCnvsWd * 3/4 rCnvsTp = rCnvsHt - 1 ovalRad = 20 ovalWd = 1 ovOutline = "Gray" #ovLineWd = 1 # ================================================================= # INITIAL VALUES AND POINT LOCATIONS -- MAIN xA0, yA0 = 200, rCnvsTp - 100 xB0, yB0 = 400, rCnvsTp - 100 xC0, yC0 = 300, rCnvsTp - 273 xA,yA,xB,yB,xC,yC = xA0,yA0,xB0,yB0,xC0,yC0 closestVrtx = "A" # DEBUG value cursOffY, cursOffX = 0, 0 # for dragging from within a vertex's oval areaABC = 50000 # DEBUG values: these should never be displayed areaTgt = 75300 # DEBUG lenABBC = 101 # DEBUG values lenBCCA = 103 # DEBUG lenCAAB = 105 # DEBUG area120AB = 10000 # DEBUG values area120BC = 20000 # DEBUG area120CA = 30000 # DEBUG area120 = 60000 # DEBUG value # END OF MAIN CONSTANTS AND INITIAL VALUES # ================================================================= # GENERAL def's def dist(x0 , y0 , x1 , y1 ): return (sqrt((x1-x0)*(x1-x0) + (y0-y1)*(y0-y1))) def area(u0, v0, u1, v1, u2, v2): return -((v2 - v0)*(u1 - u0) - (v1 - v0)*(u2 - u0))/2 # for the canvas coords of the 120 deg vertices def perp120(x0, y0, x1, y1): dx, dy = x1-x0, y1-y0 x0p, y0p = (x0+x1)/2, (y0+y1)/2 x1p, y1p = x0p - dy*halftan30, y0p + dx*halftan30 return (x1p,y1p) # ----------------------------------------------------------------- # tests if a string parses as an integer def sIsInt(s): if not len(s): return "None" else: if s[0] not in sDgts: return "None" else: for c in s[1:]: if c not in dgts: return "None" else: return s # END OF GENERAL def's # ================================================================= # SHARED def's # ----------------------------------------------------------------- # STUFF FOR ABOUT MENUs (Toplevels ?) def callAboutTriangleShr(): # print "callAboutTriangleShr() was called." abtTriTopLvl = Toplevel(None, width = 50, bd = 3, relief = GROOVE) abtTriTopLbl = Label(abtTriTopLvl, text = aboutThisProbTitle) abtTriTopLbl.grid(row = 0, sticky=N, padx = 7,pady = 5) abtTriTopTxt = Label(abtTriTopLvl, text = aboutThisProbText, anchor = W, justify = LEFT) abtTriTopTxt.grid(row = 1, sticky = N, padx = 7, pady = 5) def callAboutCopyPermShr(): # print "callAboutCopyPermShr() was called." abtCpyTopLvl = Toplevel(None, width = 50, bd = 3, relief = GROOVE) abtCpyTopLbl = Label(abtCpyTopLvl, text = aboutCopyPermTitle) abtCpyTopLbl.grid(row = 0, sticky = N, padx = 7, pady = 5) abtCpyTopTxt = Label(abtCpyTopLvl, text = aboutCopyPermText, anchor = W, justify = LEFT) abtCpyTopTxt.grid(row = 1, sticky = N, padx = 7, pady = 5) def callAboutThisPrgmShr(): # print "callAboutThisPrgmShr() was called." abtPrgTopLvl = Toplevel(None, width = 50, bd = 3, relief = GROOVE) abtPrgTopLbl = Label(abtPrgTopLvl, text = aboutThisPrgmTitle) abtPrgTopLbl.grid(row = 0, sticky=N, padx= 7,pady = 5) abtPrgTopTxt = Label(abtPrgTopLvl, text = aboutThisPrgmText, anchor = W, justify = LEFT) abtPrgTopTxt.grid(row = 1, sticky = N, padx = 7, pady = 5) def callAboutMareWareShr(): # print "callAboutMareWareShr() was called." abtMWrTopLvl = Toplevel(None, width = 50, bd = 3, relief = GROOVE) abtMWrTopLbl = Label(abtMWrTopLvl, text = aboutMareWareTitle) abtMWrTopLbl.grid(row = 0, sticky=N, padx= 7,pady = 5) abtMWrTopTxt = Label(abtMWrTopLvl, text = aboutMareWareText, anchor = W, justify = LEFT) abtMWrTopTxt.grid(row = 1, sticky = N, padx = 7, pady = 5) # ----------------------------------------------------------------- # MAKING SHARED MENU ' About... ' def makeMenuAbt(): global aboutMenu, mnuBar mnuBar = Menu(root) aboutMenu = Menu(mnuBar, tearoff = 0) aboutMenu.add_command(label = aboutThisProbTitle, command = callAboutTriangleShr) aboutMenu.add_command(label = aboutCopyPermTitle, command = callAboutCopyPermShr) aboutMenu.add_command(label = aboutThisPrgmTitle, command = callAboutThisPrgmShr) aboutMenu.add_command(label = aboutMareWareTitle, command = callAboutMareWareShr) mnuBar.add_cascade( label = "About...", menu = aboutMenu) root.config(menu = mnuBar) # ----------------------------------------------------------------- # MAKING MENU ' Reminders... ' def makeMenuRem(): global questMenu, mnuBar questMenu = Menu(mnuBar, tearoff = 0) questMenu.add_command(label = buildTgtTitle, command = buildTgtShr) questMenu.add_command(label = howMvVxTitle, command = howMvVxRub) questMenu.add_separator() questMenu.add_command(label = q1TgtTitle, command = q1TgtShr) questMenu.add_command(label = moreQTgtTitle, command = moreQRub) mnuBar.add_cascade( label = "Reminders : ", menu = questMenu) # ================================================================= # PRELIM-SPECIFIC def's # ----------------------------------------------------------------- # called from menu 'Reminders' def buildTgtShr(): howTgtTopLvl = Toplevel(None, width = 50, bd = 3, relief = GROOVE) howTgtTopLbl = Label(howTgtTopLvl, text = buildTgtTitle) howTgtTopLbl.grid(row = 0, sticky=N, padx= 7,pady = 5) howTgtTopTxt = Label(howTgtTopLvl, text = buildTgtText, anchor = W, justify = LEFT) howTgtTopTxt.grid(row = 1, sticky = N, padx = 7, pady = 5) def howMvVxRub(): mvVxTopLvl = Toplevel(None, width = 50, bd = 3, relief = GROOVE) mvVxTopLbl = Label(mvVxTopLvl, text = howMvVxTitle) mvVxTopLbl.grid(row = 0, sticky=N, padx= 7,pady = 5) mvVxTopTxt = Label(mvVxTopLvl, text = howMvVxText, anchor = W, justify = LEFT) mvVxTopTxt.grid(row = 1, sticky = N, padx = 7, pady = 5) # ----------------------------------------------------------------- def q1TgtShr(): q1TgtTopLvl = Toplevel(None, width = 50, bd = 3, relief = GROOVE) q1TgtTopLbl = Label(q1TgtTopLvl, text = q1TgtTitle) q1TgtTopLbl.grid(row = 0, sticky = N, padx = 7,pady = 5) q1TgtTopTxt = Label(q1TgtTopLvl, text = q1TgtText, anchor = W, justify = LEFT) q1TgtTopTxt.grid(row = 1, sticky = N, padx = 7, pady = 5) def moreQRub(): moreQTopLvl = Toplevel(None, width = 50, bd = 3, relief = GROOVE) moreQLbl = Label(moreQTopLvl, text = moreQTgtTitle) moreQLbl.grid(row = 0, sticky = N, padx = 7,pady = 5) moreQTxt = Label(moreQTopLvl, text = moreQTgtText, anchor = W, justify = LEFT) moreQTxt.grid(row = 1, sticky = N, padx = 7, pady = 5) # ================================================================= # BEGIN TRIANGLE-SPECIFIC def's # This is tied to the "Restore" button... def restoreGvnRub(): global xA,yA,xB,yB,xC,yC,xA0,yA0,xB0,yB0,xC0,yC0 xA,yA,xB,yB,xC,yC = xA0,yA0,xB0,yB0,xC0,yC0 updateAllRub() # Acquire, test, and install typed-in triangle coords def updateVxsRub(): global xA ,yA ,xB ,yB ,xC ,yC global statusAX,statusAY,statusBX,statusBY,statusCX,statusCY global valAX ,valAY ,valBX ,valBY ,valCX ,valCY strg = sIsInt(statusAX.get()) if not (strg == "None"): xA = int(strg) valAX.set(xA) strg = sIsInt(statusBX.get()) if not (strg == "None"): xB = int(strg) valBX.set(xB) strg = sIsInt(statusCX.get()) if not (strg == "None"): xC = int(strg) valCX.set(xC) strg = sIsInt(statusAY.get()) if not (strg == "None"): yA = rCnvsTp - int(strg) valAY.set(rCnvsTp - yA) strg = sIsInt(statusBY.get()) if not (strg == "None"): yB = rCnvsTp - int(strg) valBY.set(rCnvsTp - yB) strg = sIsInt(statusCY.get()) if not (strg == "None"): yC = rCnvsTp - int(strg) valCY.set(rCnvsTp - yC) updateAllRub() # END OF TRIANGLE-SPECIFIC def's # ================================================================= # BEGIN BUILDING prelim's SCREEN # put copyright notice & label at the top of pFrmPrelim def showCpyrtPrelim(): cHdr = Label(pFrmPrelim, text = copyrightNotice, bd = 3, relief = FLAT, font = ("MS Sans Serif", 4)) cHdr.grid(row = 0, column = 0, columnspan = 2, padx = 3, pady = 3) pHdr = Label(pFrmPrelim, text = headerPrelim, bd = 3, relief = RIDGE, font = ("MS Sans Serif", 12)) pHdr.grid(row = 1, column = 0, columnspan = 2, padx = 3, pady = 3) # ----------------------------------------------------------------- def createScrnPrelim(frm, startrow): global x0 , y0 , x1 , y1 , x2 , y2 global x01, y01, x12, y12, x20, y20 cnvs = [] nwlns =\ ["","\n\n\n\n\n\n\n",\ "","\n\n\n\n\n\n\n",\ "","\n\n\n\n\n\n\n"] for i in range(6): rTxt = nwlns[i] r = Label(frm, text = rTxt, bd = 3, relief = FLAT) r.grid( row = startrow + i, column = 0, sticky = N) r = Label(frm, text = rTxt, bd = 3, relief = FLAT) r.grid( row = startrow + i, column = 3, sticky = N) tLb0Ttl = Label(frm, text = buildTgtTitle, anchor = N, justify = LEFT, bd = 3, relief = RIDGE) tLb0Ttl.grid(row = startrow + 0, column = 1, rowspan = 1, sticky = N, ipadx = 3, ipady = 1, padx = 7, pady = 1) tLb0 = Label(frm, text = buildTgtText, anchor = N, justify = LEFT, bd = 3, relief = RIDGE) tLb0.grid( row = startrow + 1, column = 1, rowspan = 3, sticky = N, ipadx = 3, ipady = 1, padx = 7, pady = 1) tLb1Ttl = Label(frm, text = q1TgtTitle, anchor = N, justify = LEFT, bd = 3, relief = RIDGE) tLb1Ttl.grid(row = startrow + 4, column = 1, rowspan = 1, sticky = N, ipadx = 3, ipady = 1, padx = 7, pady = 1) tLb1 = Label(frm, text = q1TgtText, anchor = NW, justify = LEFT, bd = 3, relief = RIDGE) tLb1.grid( row = startrow + 5, column = 1, rowspan = 3, sticky = N, ipadx = 3, ipady = 1, padx = 7, pady = 1) currRow = startrow for i in range(3): c = Canvas(frm, height = pCnvsHt, width = pCnvsWd, bg = "Yellow", bd = 3, relief = SUNKEN) cnvs.append(c) cnvs[i].grid(row = currRow, rowspan = 2, column = 2, sticky = N, ipadx = 1, ipady = 1, padx = 1, pady = 1) cnvs[i].create_polygon(x0, y0, x1, y1, x2, y2, width = 1, outline = "Blue", fill = "Light blue") cnvs[i].create_text(x0 - 5, y0 + 5, fill = "Blue", text = "A", anchor = NE) cnvs[i].create_text(x1 + 5, y1 + 5, fill = "Blue", text = "B", anchor = NW) cnvs[i].create_text(x2 , y2 - 5, fill = "Blue", text = "C", anchor = S) currRow = currRow + 2 # create the exterior 30-120-30 vertices (x01, y01) = perp120(x0, y0, x1, y1) (x12, y12) = perp120(x1, y1, x2, y2) (x20, y20) = perp120(x2, y2, x0, y0) # for top cnvs, draw one 30-120-30 cnvs[0].create_line(x1, y1, x12, y12, x2, y2, width = 1, fill = "tan") cnvs[0].create_text(x12 + 5, y12 - 10, fill = "Red", text = "BC", anchor = SW) # for the other canvases, draw & label all 3 for i in [1,2]: ex01 = cnvs[i].create_line(x0, y0, x01, y01, x1, y1, width = 1, fill = "tan") ex12 = cnvs[i].create_line(x1, y1, x12, y12, x2, y2, width = 1, fill = "tan") ex20 = cnvs[i].create_line(x2, y2, x20, y20, x0, y0, width = 1, fill = "tan") # and label them lblAB = cnvs[i].create_text(x01 , y01 + 5, fill = "Red", text = "AB", anchor = N) lblBC = cnvs[i].create_text(x12 + 5, y12 - 7, fill = "Red", text = "BC", anchor = SW) lblCA = cnvs[i].create_text(x20 - 5, y20 - 7, fill = "Red", text = "CA", anchor = SE) # connect the 120 deg vertices targetPoly = cnvs[2].create_polygon(x01, y01, x12, y12, x20, y20, outline = "Red", fill = "", tags = "updt") return(currRow) # ----------------------------------------------------------------- def lowerBtnsPrelim(): for i in range(10): s = gridSpacer(bFrmPrelim, 0, i, 12) moreQBtn = Button(bFrmPrelim, text="MORE QUESTIONS:", command=moreQRub) moreQBtn.grid(row = 0, column = 3, padx = 3, pady = 3) ready1tn = Button(bFrmPrelim, text="I'M READY", command=callImReadyPrelim) ready1tn.grid(row = 0, column = 9, padx = 3, pady = 3, sticky = E) # END BUILDING prelim's SCREEN # ================================================================= # BEGIN BUILDING MAIN's SCREEN # put copyright notice & label at the top of pFrmPrelim def showCpyrtRub(frm): cHdr = Label(frm, text = copyrightNotice, bd = 3, relief = FLAT, font = ("MS Sans Serif", 4)) cHdr.grid(row = 0, column = 0, columnspan = 2, padx = 3, pady = 3) pHdr = Label(frm, text = headerRub, bd = 3, relief = RIDGE, font = ("MS Sans Serif", 12)) pHdr.grid(row = 1, column = 0, columnspan = 2, padx = 3, pady = 3) # ----------------------------------------------------------------- # Constant MAIN CANVAS STUFF goes here # cCnvsRub has labelled tick marks in y and x, & lines across def makeTicksRub(): global cCnvsRub iT = rCnvsWd/20 for yT in range(49, rCnvsTp, 100): tick = cCnvsRub.create_line( 0 , yT, iT, yT) line = cCnvsRub.create_line( iT , yT, rCnvsWd, yT, fill = "Light gray") lblY = cCnvsRub.create_text( 5 , yT, fill = "Blue", text = rCnvsTp - yT , anchor = SW) for xT in range( 0, rCnvsWd, 100): tick = cCnvsRub.create_line( xT , 0, xT, iT) line = cCnvsRub.create_line( xT , iT, xT, rCnvsTp, fill = "Light gray") lblX = cCnvsRub.create_text( xT + 5, 5, fill = "Blue", text = xT , anchor = NW) def makeSttsBarRub(): global valAX ,valAY ,valBX ,valBY ,valCX ,valCY global xA , yA , xB , yB , xC , yC global lenVarABBC, lenVarBCCA, lenVarCAAB global areaVarGvn, areaVarTgt, area120Ext global sttsTxtOfX, sttsTxtOfY global statusAX , statusAY , statusBX, statusBY global statusCX , statusCY global sFrmRub # TOP ROW of statusbar to hold labels rowCnvs = 0 for i in range(10): gridSpacer(sFrmRub, 1, rowCnvs, 20) xVals = gridConstLabel(sFrmRub, 1, rowCnvs, " X VALUES:") yVals = gridConstLabel(sFrmRub, 2, rowCnvs, " Y VALUES:") sideLbl = gridConstLabel(sFrmRub, 4, rowCnvs, "Target SIDE:") lenLbl = gridConstLabel(sFrmRub, 5, rowCnvs, " LENGTH:") trinLbl = gridConstLabel(sFrmRub, 7, rowCnvs, " TRIANGLES:") areaLbl = gridConstLabel(sFrmRub, 8, rowCnvs, " AREA:") # SECOND ROW of statusbar to show Vertex A rowCnvs = rowCnvs + 1 vrtxALbl = gridConstLabel(sFrmRub, 0, rowCnvs, " Vertex A ") valAX = IntVar() statusAX = gridEntry( sFrmRub, 1, rowCnvs, valAX) valAX.set(xA) valAY = IntVar() statusAY = gridEntry( sFrmRub, 2, rowCnvs, valAY) valAY.set(rCnvsTp - yA) buttonUpdt = gridButton( sFrmRub, 3, rowCnvs, "Update", updateVxsRub, rowSpn = 3) ABBCLbl = gridConstLabel(sFrmRub, 4, rowCnvs, " AB ---> BC ") lenVarABBC = IntVar() sttsLenABBC = gridIntVarLabel(sFrmRub, 5, rowCnvs, lenVarABBC) lenVarABBC.set(" %3.2f " % lenABBC) trinLbl = gridConstLabel(sFrmRub, 7, rowCnvs, "A--->B--->C ", fgColor = "Blue") areaVarGvn = IntVar() statusABC = gridIntVarLabel( sFrmRub, 8, rowCnvs, areaVarGvn, fgColor = "Blue") areaVarGvn.set(" %6d " % areaABC) buttonRstr = gridButton( sFrmRub, 9, rowCnvs, "Restore :", restoreGvnRub, rowSpn = 2, btnClr = "Blue") # THIRD ROW of statusbar to show Vertex B rowCnvs = rowCnvs + 1 vrtxBLbl = gridConstLabel(sFrmRub, 0, rowCnvs, " Vertex B ") valBX = IntVar() statusBX = gridEntry( sFrmRub, 1, rowCnvs, valBX) valBX.set(xB) valBY = IntVar() statusBY = gridEntry( sFrmRub, 2, rowCnvs, valBY) valBY.set(rCnvsTp - yB) BCCALbl = gridConstLabel(sFrmRub, 4, rowCnvs, " BC ---> CA ") lenVarBCCA = IntVar() sttsLenBC = gridIntVarLabel( sFrmRub, 5, rowCnvs, lenVarBCCA) lenVarBCCA.set(" %3.2f " % lenBCCA) trinLbl = gridConstLabel(sFrmRub, 7, rowCnvs, "AB->BC->CA ", fgColor = "Red") areaVarTgt = IntVar() statusTgt = gridIntVarLabel( sFrmRub, 8, rowCnvs, areaVarTgt, fgColor = "Red") areaVarTgt.set(" %6d " % areaTgt) # FOURTH ROW of statusbar to show Vertex C rowCnvs = rowCnvs + 1 vrtxCLbl = gridConstLabel(sFrmRub, 0, rowCnvs, " Vertex C ") valCX = IntVar() statusCX = gridEntry( sFrmRub, 1, rowCnvs, valCX) valCX.set(xC) valCY = IntVar() statusCY = gridEntry( sFrmRub, 2, rowCnvs, valCY) valCY.set(rCnvsTp - yC) CAABLbl = gridConstLabel(sFrmRub, 4, rowCnvs, " CA ---> AB ") lenVarCAAB = IntVar() sttsLenCAAB = gridIntVarLabel( sFrmRub, 5, rowCnvs, lenVarCAAB) lenVarCAAB.set(" %3.2f " % lenCAAB) extnLbl = gridConstLabel(sFrmRub, 7, rowCnvs, "external 120s", fgColor = "brown") area120Ext = IntVar() statusTgt = gridIntVarLabel( sFrmRub, 8, rowCnvs, area120Ext, fgColor = "brown") area120Ext.set(" %6d " % (area120AB + area120BC + area120CA)) # FIFTH ROW of statusbar to show Cursor offset rowCnvs = rowCnvs + 1 fxPtLbl = gridConstLabel(sFrmRub, 0, rowCnvs, " Cursor Offset ") sttsTxtOfX = IntVar() statusOfX = gridIntVarLabel(sFrmRub, 1, rowCnvs, sttsTxtOfX) sttsTxtOfX.set(" %3d " % cursOffX) sttsTxtOfY = IntVar() statusOfY = gridIntVarLabel(sFrmRub, 2, rowCnvs, sttsTxtOfY) sttsTxtOfY.set(" %3d " % -cursOffY) buttonOkFx = gridButton( sFrmRub, 9, rowCnvs, "How To:", howMvVxRub) #SIXTH ROW, anyone ? rowCnvs = rowCnvs + 1 # END OF CONSTANT STUFF ON MAIN's CANVAS & FRAME # END BUILDING MAIN's SCREEN # ================================================================= # THIS IS WHERE updateAllRub() HOLDS ALL THE VARIABLE STUFF def updateAllRub(): global xA,yA,xB,yB,xC,yC,xAB,yAB,xBC,yBC,xCA,yCA global area120AB , area120BC , area120CA global areaVarGvn, areaVarTgt, areaVar120 global valAX, valAY, valBX, valBY, valCX, valCY # print " entering updateAllRub():" cCnvsRub.delete("updt") # Draw the Given triangle & its labels triangleGvn = cCnvsRub.create_polygon(xA, yA, xB, yB, xC, yC, width = 1, outline = "Blue", fill = "light blue", tags = "updt") ovA = cCnvsRub.create_oval(xA - ovalRad, yA - ovalRad, xA + ovalRad, yA + ovalRad, tags = "updt", outline = ovOutline) ovB = cCnvsRub.create_oval(xB - ovalRad, yB - ovalRad, xB + ovalRad, yB + ovalRad, tags = "updt", outline = ovOutline) ovC = cCnvsRub.create_oval(xC - ovalRad, yC - ovalRad, xC + ovalRad, yC + ovalRad, tags = "updt", outline = ovOutline) lblA1 = cCnvsRub.create_text(xA - 15, yA - 15, fill = "Blue", text = "A", anchor = SW, tags = "updt") lblA2 = cCnvsRub.create_text(xA + 15, yA + 15, fill = "Blue", text = "A", anchor = NE, tags = "updt") lblB1 = cCnvsRub.create_text(xB - 15, yB + 15, fill = "Blue", text = "B", anchor = NE, tags = "updt") lblB2 = cCnvsRub.create_text(xB + 15, yB - 15, fill = "Blue", text = "B", anchor = SW, tags = "updt") lblC1 = cCnvsRub.create_text(xC - 23, yC , fill = "Blue", text = "C", anchor = E, tags = "updt") lblC2 = cCnvsRub.create_text(xC + 23, yC , fill = "Blue", text = "C", anchor = W , tags = "updt") # calculate its area areaABC = area(xA, yA, xB, yB, xC, yC) # create the exterior 30-120-30 triangles (xAB, yAB) = perp120(xA, yA, xB, yB) (xBC, yBC) = perp120(xB, yB, xC, yC) (xCA, yCA) = perp120(xC, yC, xA, yA) # calculate their areas area120AB = area(xA, yA, xAB, yAB, xB, yB) area120BC = area(xB, yB, xBC, yBC, xC, yC) area120CA = area(xC, yC, xCA, yCA, xA, yA) # draw them exAB = cCnvsRub.create_line(xA, yA, xAB, yAB, xB, yB, width = 1, fill = "tan", tags = "updt") exBC = cCnvsRub.create_line(xB, yB, xBC, yBC, xC, yC, width = 1, fill = "tan", tags = "updt") exCA = cCnvsRub.create_line(xC, yC, xCA, yCA, xA, yA, width = 1, fill = "tan", tags = "updt") # and label them lblAB = cCnvsRub.create_text(xAB + 5, yAB + 10, fill = "Red", text = "AB", anchor = N, tags = "updt") lblBC = cCnvsRub.create_text(xBC + 5, yBC - 10, fill = "Red", text = "BC", anchor = SW, tags = "updt") lblCA = cCnvsRub.create_text(xCA - 5, yCA - 10, fill = "Red", text = "CA", anchor = SE, tags = "updt") # connect the 120 deg vertices targetPoly = cCnvsRub.create_polygon(xAB, yAB, xBC, yBC, xCA, yCA, outline = "Red", fill = "", tags = "updt") # calculate its area areaTgt = area(xAB, yAB, xBC, yBC, xCA, yCA) # update the status bar info valAX.set(xA) valAY.set(rCnvsTp - yA) valBX.set(xB) valBY.set(rCnvsTp - yB) valCX.set(xC) valCY.set(rCnvsTp - yC) lenABBC = dist(xAB, yAB, xBC, yBC) lenVarABBC.set(" %3.2f " % lenABBC) lenBCCA = dist(xBC, yBC, xCA, yCA) lenVarBCCA.set(" %3.2f " % lenBCCA) lenCAAB = dist(xCA, yCA, xAB, yAB) lenVarCAAB.set(" %3.2f " % lenCAAB) areaVarGvn.set(" %6d " % areaABC) areaVarTgt.set(" %6d " % areaTgt) area120Ext.set(" %6d " % (area120AB + area120BC + area120CA)) sttsTxtOfX.set(" %3d " % cursOffX) sttsTxtOfY.set(" %3d " % -cursOffY) # print " leaving updateAllRub():" def clickB1Rub(event): global xA, yA, xB, yB, xC, yC, cursOffX, cursOffY, closestVrtx dAclick = dist(xA, yA, event.x, event.y) dBclick = dist(xB, yB, event.x, event.y) dCclick = dist(xC, yC, event.x, event.y) closestVrtx = "X" if dAclick < dBclick: if dAclick < dCclick: closestVrtx = "A" if dAclick > ovalRad: xA, yA, cursOffX, cursOffY = event.x, event.y, 0, 0 else: cursOffX, cursOffY = event.x - xA, event.y - yA else: closestVrtx = "C" if dCclick > ovalRad: xC, yC, cursOffX, cursOffY = event.x, event.y, 0, 0 else: cursOffX, cursOffY = event.x - xC, event.y - yC elif dBclick < dCclick: closestVrtx = "B" if dBclick > ovalRad: xB, yB, cursOffX, cursOffY = event.x, event.y, 0, 0 else: cursOffX, cursOffY = event.x - xB, event.y - yB else: closestVrtx = "C" if dCclick > ovalRad: xC, yC, cursOffX, cursOffY = event.x, event.y, 0, 0 else: cursOffX, cursOffY = event.x - xC, event.y - yC updateAllRub() # ----------------------------------------------------------------- def dragB1Rub(event): global movingB1, xA, yA, xB, yB, xC, yC, closestVrtx movingB1 = 1 if closestVrtx == "A": xA, yA = event.x - cursOffX, event.y - cursOffY elif closestVrtx == "B": xB, yB = event.x - cursOffX, event.y - cursOffY elif closestVrtx == "C": xC, yC = event.x - cursOffX, event.y - cursOffY else: print "callDragB1(%d, %d) got lost" % (event.x, event.y) updateAllRub() # ----------------------------------------------------------------- def releaseB1Rub(event): global movingB1, cursOffX, cursOffY cursOffX, cursOffY = 0, 0 movingB1 = 0 updateAllRub() # ================================================================= # THE MAIN BANGER, SWITCHES FROM prelim TO triangle def callImReadyPrelim(): global cCnvsRub, sFrmRub makeMenuRem() pFrmPrelim.destroy() bFrmPrelim.destroy() tFrmRub = Frame(rFrmShr) tFrmRub.pack() showCpyrtRub(tFrmRub) cFrmRub = Frame(rFrmShr) cFrmRub.pack() # cCnvsRub is where main's action takes place... cCnvsRub = Canvas(cFrmRub, height = rCnvsHt, width = rCnvsWd, bg = "Yellow", bd = 0, cursor="crosshair") cCnvsRub.pack() cCnvsRub.config(highlightthickness = 0) makeTicksRub() # ----------------------------------------------------------------- # main's event-binding cCnvsRub.bind("" , clickB1Rub) cCnvsRub.bind("" , dragB1Rub) cCnvsRub.bind("", releaseB1Rub) # ----------------------------------------------------------------- sFrmRub = Frame(rFrmShr) sFrmRub.pack(side = LEFT) xA,yA,xB,yB,xC,yC = xA0,yA0,xB0,yB0,xC0,yC0 makeSttsBarRub() updateAllRub() # restoreGvnRub()'s last line is UpdateAllRub() # ================================================================= # LAUNCH Tkinter AND BEGIN THE PROGRAM PROPER root = Tk() # create the 'About...' menu makeMenuAbt() # rFrmShr is shared by prelim and triangle rFrmShr = Frame(root) rFrmShr.pack() # ----------------------------------------------------------------- # Frame pFrmPrelim holds the prelim's label, text, & canvases pFrmPrelim = Frame(rFrmShr) pFrmPrelim.pack() # Puts copyright notice and label at top of Prelim showCpyrtPrelim() # draws the 3 triangles and tells about the problem createScrnPrelim(pFrmPrelim, startrow = 2) # Frame bFrmPrelim holds buttons below the Labels and Canvases bFrmPrelim = Frame(rFrmShr) bFrmPrelim.pack() lowerBtnsPrelim() # ================================================================= root.mainloop() # =================================================================