PageRenderTime 43ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/harmonograph/tk_harmonograph.py

https://bitbucket.org/petecarr/my-pi-projects
Python | 640 lines | 573 code | 24 blank | 43 comment | 27 complexity | 86b127e52ade877c189412d6757539eb MD5 | raw file
  1. from tkinter import *
  2. from math import pi, sin, cos, exp
  3. #----------------------------------------------------------------------------
  4. # Things to try:
  5. # Display equations being shown - possibly in a small window -
  6. # controlled by a button
  7. # Gray out scales not applicable to a display like Lissajous
  8. # Add Spiral Envelopes
  9. # Change scales for Potted displays which use a different formula
  10. # Save button for printing (make background white!!)
  11. # Load ??
  12. # More object oriented. Separate modules.
  13. # Source control.
  14. # Handle window resizing.
  15. # Try PyQt (python 2.7!), OpenGL
  16. # Generalize plotting
  17. #----------------------------------------------------------------------------
  18. CANVASWIDTH=1000
  19. CANVASHEIGHT=800
  20. BGCOLOR="black"
  21. LINECOLOR="red"
  22. linecolor=LINECOLOR
  23. colors = ("red", "orange", "yellow", "green", "cyan", "blue", "magenta")
  24. Ax1,Fx1,Px1,Ax2,Fx2,Px2 = 0.0,0.0,0.0,0.0,0.0,0.0
  25. Ay1,Fy1,Py1,Ay2,Fy2,Py2 = 0.0,0.0,0.0,0.0,0.0,0.0
  26. Dkx, Dky = 0.0, 0.0
  27. cx = CANVASWIDTH/2
  28. cy = CANVASHEIGHT/2
  29. NO_DK_LIMIT = 2.0*pi
  30. drawing_limit = NO_DK_LIMIT
  31. #----------------------------------------------------------------------------
  32. # Different plots
  33. spirograph = False
  34. lissajous = True
  35. damped_pends = False
  36. #----------------------------------------------------------------------------
  37. print_equation = False
  38. #----------------------------------------------------------------------------
  39. # Animation control
  40. ani = False
  41. #----------------------------------------------------------------------------
  42. # multicolor control
  43. multcol = False
  44. #----------------------------------------------------------------------------
  45. # Potted Displays
  46. potted_draw=False
  47. potted_index=0
  48. global cindx, CINDX_MAX, A1, A2, B1, B2, C1, C2, D1, D2, E1, E2, F1, F2
  49. CINDX_MAX = 13
  50. A1=0; B1=1; C1=2; D1=3; E1=4; F1=5; A2=6; B2=7; C2=8; D2=9; E2=10; F2=11
  51. cfs = [
  52. # A1 B1 C1 D1 E1 F1 A2 B2 C2 D2 E2 Fx2
  53. # no decay
  54. [0.0, 1.5, 1.5, 3.5, 1.5, 2.8, 0.0, 1.55, 1.55, 2.55, 1.55,-2.864],
  55. #with decay
  56. [1.0, 1.5, 1.5, -1.5, 1.5, 2.8, 0.1, 1.55, 1.55, 1.55, 1.55,-2.864],
  57. [0.3, 1.5, 1.5, -1.5, 1.5, 2.8, 0.1, 1.55, 1.55, 1.55, 1.55, 2.864],
  58. #spirographs
  59. [1.0, 3.0, 0.0, 0.0, 3.0, 0.2, 1.0, 0.0, 0.3, -0.3, 0.0, 5.0],
  60. [1.0, 2.0, 2.0, 2.0, -2.0, 0.5, 3.0, -1.0, 1.0, -1.0, -1.0, 5.0],
  61. #2 lobes
  62. [0.25, 4.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 4.0, 0.0, 2.03],
  63. [0.25, 4.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 4.0, 0.0, 2.0],
  64. # oddities
  65. [1.0, -1.0,-1.0, 1.0, 1.0, 2.0, 2.0, -2.0, 2.0, 2.0, 2.0, 1.0],
  66. # should be 4 lobes, but isn't
  67. [1.0, 2.0, 2.0, -2.0, -2.0, 1.0, 0.3, -0.65, 0.65, 0.65, 0.65, 3.01],
  68. # 3 lobes
  69. [1.0, 1.0, 1.0, -1.0, 1.0, 2.0, 2.0, -2.0, 2.0, 2.0, 2.0, 1.0],
  70. # 5 lobes
  71. [0.4, 1.4, 1.4, -1.4, 1.4, 2.0, 1.0, -1.5, 1.5, 1.5, 1.5, 3.0],
  72. # 7 lobes
  73. [1.0, 1.0, 1.0, -1.0, 1.0, 2.0, 2.0, -2.0, 2.0, 2.0, 2.0, 1.5],
  74. [0.1, 1.0, 1.0, -1.0, 1.0, 2.0, 2.0, -2.0, 2.0, 2.0, 2.0, 1.5],
  75. # cardoid
  76. [1.0, -1.0, 1.0, 1.0, 1.0, 2.0, 2.0, -2.0, 2.0, 2.0, 2.0, 1.0]
  77. ]
  78. #x=cx+(b1*sin(f1*t)+c1*cos(f1*t))*100.0*exp(-a1*0.01*t)+ #note was 100.0
  79. # (b2*sin(f2*t)+c2*cos(f2*t))*100.0*exp(-a2*0.01*t)
  80. #y=cy+(d1*sin(f1*t)+e1*cos(f1*t))*100.0*exp(-a1*0.01*t)+
  81. # (d2*sin(f2*t)+e2*cos(f2*t))*100.0*exp(-a2*0.01*t)
  82. def _harmonograph_(t):
  83. global print_equation
  84. coeffs = cfs[potted_index]
  85. a1 = coeffs[A1]; b1 = coeffs[B1]; c1 = coeffs[C1]
  86. d1 = coeffs[D1]; e1 = coeffs[E1]; f1 = coeffs[F1]
  87. a2 = coeffs[A2]; b2 = coeffs[B2]; c2 = coeffs[C2]
  88. d2 = coeffs[D2]; e2 = coeffs[E2]; f2 = coeffs[F2]
  89. DSCALE=80.0
  90. if a1 != 0.0: r1 = DSCALE*exp(-a1*0.01*t)
  91. else: r1 = DSCALE
  92. if a2 != 0.0: r2 = DSCALE*exp(-a2*0.01*t)
  93. else: r2 = DSCALE
  94. s1 = sin(f1*t)
  95. s2 = sin(f2*t)
  96. t1 = cos(f1*t)
  97. t2 = cos(f2*t)
  98. x = cx + r1*(b1*s1 + c1*t1) + r2*(b2*s2 + c2*t2)
  99. y = cy + r1*(d1*s1 + e1*t1) + r2*(d2*s2 + e2*t2)
  100. if print_equation:
  101. try:
  102. if a1 != 0.0: r1_="80.0*exp(-"+repr(a1)+"*0.01*t)*"
  103. else : r1_ = "80.0*"
  104. if a2 != 0.0: r2_="80.0*exp(-"+repr(a2)+"*0.01*t)*"
  105. else: r2_ = "80.0*"
  106. s1_="*sin("+repr(f1)+"*t)"
  107. s2_="*sin("+repr(f2)+"*t)"
  108. t1_="*cos("+repr(f1)+"*t)"
  109. t2_="*cos("+repr(f2)+"*t)"
  110. print("x = "+repr(cx)+ "+"+
  111. r1_+"("+repr(b1)+s1_+"+"+repr(c1)+t1_+")+"+
  112. r2_+"("+repr(b2)+s2_+"+"+repr(c2)+t2_+")")
  113. print("y = "+repr(cy)+ "+"+
  114. r1_+"("+repr(d1)+s1_+"+"+repr(e1)+t1_+")+"+
  115. r2_+"("+repr(d2)+s2_+"+"+repr(e2)+t2_+")")
  116. except TypeError:
  117. print("Error in show equation")
  118. print_equation = False
  119. return (x,y)
  120. #-------------------------------------------------------------------------
  121. # R is outer radius, r is inner. Rho is pen radius relative to inner wheel.
  122. # Added decay factors for extra spectacle
  123. R=0.0
  124. r = 0.0
  125. Rrr = 0.0
  126. Rpr = 0.0
  127. Rho = 0.0
  128. def _spirograph_(t):
  129. global spirograph, Ax1, Ax2, Ay1, Dkx, Dky, cx, cy
  130. global R, R, Rrr, Rpr, Rho
  131. global print_equation
  132. if t == 0.0:
  133. R=Ax1; r = Ax2
  134. Rrr = (R-r)/r
  135. Rpr = R-r
  136. Rho = Ay1
  137. if print_equation:
  138. try:
  139. print("x = "+repr(cx)+" + ("+repr(Rpr)+"* cos(t) - "+
  140. repr(Rho)+" * cos("+repr(Rrr)+"*t))*exp(-"+
  141. repr(Dkx)+"*t)")
  142. print("y = "+repr(cy)+" + ("+repr(Rpr)+"* sin(t) - "+
  143. repr(Rho)+" * sin("+repr(Rrr)+"*t))*exp(-"+
  144. repr(Dky)+"*t)")
  145. except TypeError:
  146. print(cx,Rpr,Rho,Rrr,Dkx)
  147. print_equation = False
  148. x = cx + (Rpr*cos(t) -Rho*cos(Rrr*t))*exp(-Dkx*t)
  149. y = cy + (Rpr*sin(t) -Rho*sin(Rrr*t))*exp(-Dky*t)
  150. return (x,y)
  151. #-------------------------------------------------------------------------
  152. def harmonograph_draw(canvas):
  153. global Ax1, Fx1, Px1, Ax2, Fx2, Px2, Ay1, Fy1, Py1, Ay2, Fy2, Py2, cx, cy
  154. global linecolor, spirograph, potted_draw, multcol
  155. global print_equation
  156. x=0.0; y = 0.0
  157. canvas.delete(ALL)
  158. if Dkx > 0.0 or Dky > 0.0:
  159. drawing_limit = Dlimit # a slider
  160. elif potted_draw:
  161. drawing_limit = 100.0
  162. else:
  163. drawing_limit = NO_DK_LIMIT
  164. t = 0.0
  165. while t < drawing_limit:
  166. try:
  167. if spirograph:
  168. x,y = _spirograph_(t)
  169. elif potted_draw:
  170. x,y = _harmonograph_(t)
  171. else:
  172. if print_equation:
  173. try:
  174. if Dkx == 0.0:
  175. Dkx_ = ""
  176. else:
  177. Dkx_ = "*exp(-"+repr(Dkx)+"*t)"
  178. if Px1 == 0.0:
  179. Px1_ = ""
  180. else:
  181. Px1_ = "+"+repr(Px1)
  182. if Px2 == 0.0:
  183. Px2_ = ""
  184. else:
  185. Px2_ = "+"+repr(Px2)
  186. if Py1 == 0.0:
  187. Py1_ = ""
  188. else:
  189. Py1_ = "+"+repr(Py1)
  190. if Py2 == 0.0:
  191. Py2_ = ""
  192. else:
  193. Py2_ = "+"+repr(Py2)
  194. if Ax1 == 0.0:
  195. Ax1_ = ""
  196. else:
  197. Ax1_ = repr(Ax1)+"*sin("+repr(Fx1)+"*t"+Px1_+")"
  198. if Ax2 == 0.0:
  199. Ax2_ = ""
  200. else:
  201. Ax2_ = "+"+repr(Ax2)+"*sin("+repr(Fx2)+"*t+"+repr(Px2)
  202. print("x="+repr(cx)+"+("+Ax1_+Ax2_+")"+Dkx_)
  203. if Dky == 0.0:
  204. Dky_ = ""
  205. else:
  206. Dky_ = "*exp(-"+repr(Dky)+"*t)"
  207. if Ay1 == 0.0:
  208. Ay1_ = ""
  209. else:
  210. Ay1_ = repr(Ay1)+"*sin("+repr(Fy1)+"*t+"+repr(Py1)+")"
  211. if Ay2 == 0.0:
  212. Ay2_ = ""
  213. else:
  214. Ay2 = "+"+repr(Ay2)+"*sin("+repr(Fy2)+"*t+"+repr(Py2)
  215. print("y="+repr(cy)+"+("+Ay1_+Ay2_+")"+Dky_)
  216. except TypeError:
  217. print(cx, Ax1, Fx1, Px1)
  218. print_equation = False
  219. x = cx + int(Ax1*sin(Fx1*t+Px1) +
  220. Ax2*sin(Fx2*t+Px2))*exp(-Dkx*t)
  221. y = cy + int(Ay1*sin(Fy1*t+Py1) +
  222. Ay2*sin(Fy2*t+Py2))*exp(-Dky*t)
  223. except TypeError:
  224. pass
  225. if multcol:
  226. if (t -float(int(t))) < 0.00000000001:
  227. lineindex = colors.index(linecolor)
  228. lineindex = lineindex+1
  229. if lineindex >= len(colors): lineindex = 0
  230. linecolor = colors[lineindex]
  231. if t > 0.0:
  232. canvas.create_line(lastx,lasty,x,y, width=1, fill=linecolor)
  233. lastx=x; lasty=y
  234. t = t + 0.01
  235. #print(lastx, lasty, x,y)
  236. class Scales():
  237. def __init__(self, root, orient, label, from_, to, resolution = 1,
  238. variable=0.0):
  239. self.value = 0
  240. self.root = root
  241. self.orient = orient
  242. self.label = label
  243. self.from_ = from_
  244. self.to = to
  245. self.resolution = resolution
  246. self.variable= variable
  247. self.scale=Scale(self.root, orient = self.orient, label = self.label,
  248. from_=self.from_, to = self.to,
  249. resolution = self.resolution,
  250. command = self.update_value)
  251. self.scale.pack()
  252. self.scale.set(variable)
  253. def update_value(self, scaleValue):
  254. self.variable = scaleValue
  255. self.scale.set(scaleValue)
  256. refresh_now()
  257. def get(self):
  258. #print("Scales val="+str(self.val))
  259. return self.variable
  260. def set(self, scaleValue):
  261. self.variable = scaleValue
  262. self.scale.set(scaleValue)
  263. def refresh_now():
  264. global Ax1, Fx1, Px1, Ax2, Fx2, Px2, Ay1, Fy1, Py1, Ay2, Fy2, Py2
  265. global Dkx, Dky, Dlimit
  266. Ax1 = float(Ax1scale.get())
  267. Fx1 = float(Fx1scale.get())
  268. Px1 = float(Px1scale.get())*pi/180.0
  269. Ax2 = float(Ax2scale.get())
  270. Fx2 = float(Fx2scale.get())
  271. Px2 = float(Px2scale.get())*pi/180.0
  272. Dkx = float(Dkxscale.get())
  273. Ay1 = float(Ay1scale.get())
  274. Fy1 = float(Fy1scale.get())
  275. Py1 = float(Py1scale.get())*pi/180.0
  276. Ay2 = float(Ay2scale.get())
  277. Fy2 = float(Fy2scale.get())
  278. Py2 = float(Py2scale.get())*pi/180.0
  279. Dky = float(Dkyscale.get())
  280. Dlimit = float(Dlimitscale.get())
  281. harmonograph_draw(canvas)
  282. def quitNow():
  283. global tk
  284. tk.destroy()
  285. #sys.exit(None)
  286. #--------------------------------------------------------------------------
  287. tk = Tk()
  288. lf = Frame(tk, borderwidth=2, relief=RAISED)
  289. lf.pack(fill = BOTH, side =LEFT)
  290. lf.master.title("Harmonograph")
  291. cf = Frame(tk, borderwidth=2, relief=RAISED)
  292. cf.pack(side =LEFT)
  293. rf = Frame(tk, borderwidth=2, relief=RAISED)
  294. rf.pack(fill = BOTH, side =RIGHT)
  295. bottomleftframe = Frame(lf)
  296. bottomleftframe.pack(fill = BOTH, side = BOTTOM )
  297. bottomrightframe = Frame(rf)
  298. bottomrightframe.pack(fill = BOTH, side = BOTTOM )
  299. leftLabel= Label(lf, text="X-coefficients", borderwidth=2, relief=RAISED)
  300. leftLabel.pack(side=TOP)
  301. Ax1scale = Scales(lf, orient=HORIZONTAL, label = "Ax1",
  302. from_=0, to=CANVASWIDTH/2, variable=Ax1)
  303. Fx1scale = Scales(lf, orient=HORIZONTAL, label = "Fx1",
  304. from_=0, to=15, variable=Fx1)
  305. Px1scale = Scales(lf, orient=HORIZONTAL, label = "Px1",
  306. from_=0, to=360, resolution = 1, variable=Px1)
  307. Ax2scale = Scales(lf, orient=HORIZONTAL, label = "Ax2",
  308. from_=0, to=CANVASWIDTH/2, variable=Ax2)
  309. Fx2scale = Scales(lf, orient=HORIZONTAL, label = "Fx2",
  310. from_=0, to=15, variable=Fx2)
  311. Px2scale = Scales(lf, orient=HORIZONTAL, label = "Px2",
  312. from_=0, to=360, resolution = 10, variable=Px2)
  313. Dkxscale = Scales(lf, orient=HORIZONTAL, label = "Decay rate",
  314. from_=0.0, to=0.5, resolution = 0.01, variable=Dkx)
  315. Dlimitscale = Scales(lf, orient=HORIZONTAL, label = "Plot Length (secs)",
  316. from_=0.01, to=100.0, resolution = 0.01,
  317. variable=drawing_limit)
  318. Dlimitscale.set(drawing_limit)
  319. #--------------------------------------------------------------------------
  320. #centerLabel= Label(cf, text="Harmonograph", borderwidth=2, relief=RAISED)
  321. #centerLabel.pack(side=TOP, fill=BOTH)
  322. global canvas
  323. canvas = Canvas(cf, width = CANVASWIDTH, height = CANVASHEIGHT, bg=BGCOLOR)
  324. canvas.pack()
  325. def key(event):
  326. if (event.keysym == 'Escape'):
  327. print("Pressed Escape, quitting")
  328. quitNow()
  329. #elif (event.keycode == <Print>): #doesn't work
  330. #print("Print")
  331. else:
  332. print ("pressed", repr(event.keysym))
  333. print ("pressed", repr(event.keycode))
  334. def callback(event):
  335. canvas.focus_set()
  336. #print ("clicked at", event.x, event.y)
  337. canvas.bind("<Key>", key)
  338. canvas.bind("<Button-1>", callback)
  339. #--------------------------------------------------------------------------
  340. rightLabel= Label(rf, text="Y-coefficients", borderwidth=2, relief=RAISED)
  341. rightLabel.pack(side=TOP)
  342. Ay1scale = Scales(rf, orient=HORIZONTAL, label = "Ay1",
  343. from_=0, to=CANVASHEIGHT/2, variable=Ay1)
  344. Fy1scale = Scales(rf, orient=HORIZONTAL, label = "Fy1",
  345. from_=0, to=15, variable=Fy1)
  346. Py1scale = Scales(rf, orient=HORIZONTAL, label = "Py1",
  347. from_=0, to=360, resolution = 10, variable=Py1)
  348. Ay2scale = Scales(rf, orient=HORIZONTAL, label = "Ay2",
  349. from_=0, to=CANVASHEIGHT/2, variable=Ay2)
  350. Fy2scale = Scales(rf, orient=HORIZONTAL, label = "Fy2",
  351. from_=0, to=15, variable=Fy2)
  352. Py2scale = Scales(rf, orient=HORIZONTAL, label = "Py2",
  353. from_=0, to=360,resolution = 10, variable=Py2)
  354. Dkyscale = Scales(rf, orient=HORIZONTAL, label = "Decay rate",
  355. from_=0.0, to=0.5, resolution = 0.01, variable=Dky)
  356. #--------------------------------------------------------------------------
  357. buttonframe = Frame(bottomrightframe, borderwidth=2)
  358. buttonframe.pack(fill = BOTH, side = TOP )
  359. # Options.
  360. #--------------------------------------------------------------------------
  361. # Show the current equations being plotted
  362. def show_equation():
  363. global print_equation
  364. print_equation =True
  365. refresh_now()
  366. equation = Button(buttonframe, text = "Show equation",
  367. command=show_equation)
  368. equation.pack(anchor=W)
  369. #--------------------------------------------------------------------------
  370. # Animate display by incrementing Px1 to rotate the display'
  371. def animate_display():
  372. global ani
  373. if ani: ani=False
  374. else: ani = True
  375. # move Py1 at intervals to rotate the display
  376. if ani:
  377. try:
  378. while ani:
  379. Px1 = int(Px1scale.get())
  380. Px1 = Px1+5
  381. if Px1 >=360: Px1 = 0
  382. Px1scale.set(Px1)
  383. refresh_now()
  384. tk.update_idletasks()
  385. tk.update()
  386. except TclError:
  387. pass # avoid errors when window is closed
  388. animate = Checkbutton(buttonframe, text = "Animate",
  389. command=animate_display)
  390. animate.config(indicatoron=1)
  391. animate.pack(anchor=W)
  392. #--------------------------------------------------------------------------
  393. # load up potted values that make nice pictures
  394. def potted_display():
  395. global potted_draw
  396. # saved values for cool display
  397. if potted_draw:
  398. potted_draw=False
  399. else:
  400. potted_draw = True
  401. Dlimitscale.set(float(100.0))
  402. refresh_now()
  403. def next_display():
  404. global potted_draw, potted_index
  405. if potted_draw:
  406. potted_index= potted_index+1
  407. if potted_index > CINDX_MAX:
  408. potted_index= 0
  409. Dlimitscale.set(float(100.0))
  410. refresh_now()
  411. def last_display():
  412. global potted_draw, potted_index
  413. if potted_draw:
  414. potted_index= potted_index-1
  415. if potted_index < 0:
  416. potted_index= CINDX_MAX
  417. Dlimitscale.set(float(100.0))
  418. refresh_now()
  419. pbuttonframe = Frame(bottomrightframe, borderwidth=2)
  420. pbuttonframe.pack(fill = BOTH, side = TOP )
  421. potted = Checkbutton(pbuttonframe, text = "Potted displays",
  422. command=potted_display)
  423. potted.config(indicatoron=1)
  424. potted.pack(anchor=W)
  425. potted_last = Button(pbuttonframe, text = "Last plot",
  426. command=last_display)
  427. potted_last.pack(side=LEFT)
  428. potted_next = Button(pbuttonframe, text = "Next plot",
  429. command=next_display)
  430. potted_next.pack(side=RIGHT)
  431. #--------------------------------------------------------------------------
  432. # Multicolor - switch colors every time t goes through a multiple of 2*pi
  433. def multicolor_display():
  434. global multcol
  435. # saved values for cool displays
  436. if multcol: multcol= False
  437. else: multcol = True
  438. refresh_now()
  439. multicolor = Checkbutton(bottomrightframe, text = "Multicolor",
  440. command=multicolor_display)
  441. multicolor.config(indicatoron=1)
  442. multicolor.pack(anchor=W)
  443. #--------------------------------------------------------------------------
  444. # Select between 3 modes of display: Lissajous, Spirograph or harmonograph
  445. def clear_scales():
  446. CLR = float(0.0)
  447. Ax1scale.set(CLR)
  448. Ax2scale.set(CLR)
  449. Ay1scale.set(CLR)
  450. Ay2scale.set(CLR)
  451. Fx1scale.set(CLR)
  452. Fx2scale.set(CLR)
  453. Fy1scale.set(CLR)
  454. Fy2scale.set(CLR)
  455. Px1scale.set(CLR)
  456. Px2scale.set(CLR)
  457. Py1scale.set(CLR)
  458. Py2scale.set(CLR)
  459. Dkxscale.set(CLR)
  460. Dkyscale.set(CLR)
  461. Dlimitscale.set(CLR)
  462. def set_mode():
  463. global Ax1, Fx1, Px1, Ax2, Fx2, Px2, Ay1, Fy1, Py1, Ay2, Fy2, Py2
  464. global Dkx, Dky, Dlimit, lissajous, spirograph, damped_pends
  465. global which_plot
  466. mode = str(which_plot.get())
  467. if mode =="LI":
  468. lissajous = True
  469. spirograph = False
  470. damped_pends = False
  471. elif mode == "SP":
  472. spirograph = True
  473. damped_pends = False
  474. lissajous = False
  475. elif mode == "DP":
  476. damped_pends = True
  477. spirograph = False
  478. lissajous = False
  479. clear_scales()
  480. if lissajous:
  481. Ax1scale.set(float(CANVASWIDTH/4.0))
  482. Fx1scale.set(float(8.0))
  483. Px1scale.set(float(0.0))
  484. Ay1scale.set(float(CANVASHEIGHT/4.0))
  485. Fy1scale.set(float(7.0))
  486. Py1scale.set(float(90.0))
  487. Dlimitscale.set(float(2.0*pi/8.0)) # should be moved into harmonograph
  488. elif spirograph:
  489. R = CANVASHEIGHT/4.0 # Outer circle radius
  490. r = CANVASHEIGHT/40.0 # Inner circle radius
  491. Rho = CANVASHEIGHT/4.0 # Pen radius
  492. Ax1scale.set(float(R))
  493. Ay1scale.set(float(Rho))
  494. Ax2scale.set(float(r))
  495. Dlimitscale.set(float(2.0*pi))
  496. elif damped_pends:
  497. Ax1scale.set(float(CANVASWIDTH/4.0))
  498. Fx1scale.set(float(4.0))
  499. Px1scale.set(float(0.0))
  500. Ay1scale.set(float(CANVASHEIGHT/4.0))
  501. Fy1scale.set(float(8.0))
  502. Py1scale.set(float(90.0))
  503. Dkxscale.set(float(0.1))
  504. Dkyscale.set(float(0.0))
  505. Dlimitscale.set(float(2.0*pi))
  506. refresh_now()
  507. MODES = [
  508. ("Damped Pendulums", "DP"),
  509. ("Lissajous", "LI"),
  510. ("Spirograph", "SP")
  511. ]
  512. which_plot = StringVar()
  513. which_plot.set("LI") # initialize
  514. for text, mode in MODES:
  515. b = Radiobutton(bottomrightframe, text=text,
  516. variable=which_plot, value=mode, command=set_mode)
  517. b.config(indicatoron=1)
  518. b.pack(anchor=W)
  519. set_mode()
  520. #--------------------------------------------------------------------------
  521. # This radiobox stuff doesn't like to appear before Tk is defined
  522. def select_color():
  523. global which_color, linecolor
  524. #print(which_color.get())
  525. linecolor=which_color.get()
  526. refresh_now()
  527. which_color = StringVar()
  528. rb = []
  529. def rbox(colors):
  530. for color in (colors):
  531. r = Radiobutton(bottomleftframe, width=8,
  532. text=color, variable=which_color,
  533. borderwidth=4,
  534. bg=color, fg="black",
  535. activebackground=color, activeforeground="white",
  536. selectcolor=color,
  537. highlightbackground=color,
  538. value=color,
  539. command=select_color)
  540. r.config(indicatoron=0)
  541. r.pack(anchor=CENTER, fill=X)
  542. rb.append(r)
  543. rbox(colors)
  544. which_color.set("red")
  545. #-----------------------------------------------------------------------------
  546. refrbutton = Button(bottomleftframe, text="Refresh", fg="blue",
  547. command = refresh_now)
  548. refrbutton.pack(side = LEFT)
  549. quitbutton = Button(bottomleftframe, text="Quit", fg="red",
  550. command = quitNow)
  551. quitbutton.pack(side = RIGHT)
  552. tk.mainloop()