00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 from Tkinter import *
00025 import time
00026 import math
00027 import mutex
00028 import sys
00029
00030 class ToggleItem:
00031 def __init__(self):
00032 self.active = True
00033
00034 def activate(self):
00035 self.active = True
00036 self.draw()
00037
00038 def deactivate(self):
00039 self.active = False
00040 self.delete()
00041
00042 def toggle(self):
00043 if self.active:
00044 self.deactivate()
00045 else:
00046 self.activate()
00047
00048
00049 class CanvasText(ToggleItem):
00050 def __init__(self, canvas, text, x, y):
00051 ToggleItem.__init__(self)
00052 self.canvas = canvas
00053 self.id = self.canvas.create_text(x, y, text=text)
00054 self.text = text
00055 self.x = x
00056 self.y = y
00057 self.draw_text(x, y, text)
00058
00059 def draw(self):
00060 if self.active == False: return
00061 self.delete()
00062 self.id = self.canvas.create_text(self.x, self.y, text=self.text)
00063
00064 def draw_text(self, x, y, text):
00065 self.x = x
00066 self.y = y
00067 self.text = text
00068 self.draw()
00069
00070 def delete(self):
00071 self.canvas.delete(self.id)
00072
00073
00074 class CanvasAxis(ToggleItem):
00075 def __init__(self, canvas, width, height):
00076 ToggleItem.__init__(self)
00077 self.x0 = width/2
00078 self.y0 = height/2
00079 self.width = width
00080 self.height = height
00081 self.canvas = canvas
00082 self.id = [None] * 4
00083 self.draw()
00084
00085 def draw(self):
00086 if self.active == False: return
00087 self.delete()
00088 self.id[0] = self.canvas.create_line(0, self.height/2,
00089 self.width, self.height/2)
00090 self.id[1] = self.canvas.create_text(self.width - 10,
00091 self.height/2 + 10,
00092 text="x")
00093 self.id[2] = self.canvas.create_line(self.width/2, 0,
00094 self.width/2, self.height)
00095 self.id[3] = self.canvas.create_text(self.width/2 + 10,
00096 + 10, text="y")
00097 return
00098
00099 def delete(self):
00100 for i in self.id:
00101 self.canvas.delete(i)
00102
00103 class CanvasCircle(ToggleItem):
00104 def __init__(self, canvas, x, y, width, height, pitch):
00105 ToggleItem.__init__(self)
00106 self.canvas = canvas
00107 self.x = x
00108 self.y = y
00109 self.width = width
00110 self.height = height
00111 self.pitch = pitch
00112 self.id = []
00113 self.draw()
00114
00115 def draw(self):
00116 if self.active == False: return
00117 self.delete()
00118 if self.pitch == 0:
00119 circnum = 0
00120 else:
00121 circnum = max(self.width, self.height) / self.pitch
00122 circrange = range(circnum)
00123 circrange.reverse()
00124 for i in circrange:
00125 x0 = self.x - self.pitch * i
00126 y0 = self.y - self.pitch * i
00127 x1 = self.x + self.pitch * i
00128 y1 = self.y + self.pitch * i
00129 if i % 2 == 0:
00130 color = "#dddddd"
00131 else:
00132 color = "#eeeeee"
00133 id = self.canvas.create_oval(x0, y0, x1, y1, fill=color,
00134 outline="#ffffff")
00135 self.id.append(id)
00136 id = self.id
00137 id.reverse()
00138 for i in id:
00139 self.canvas.tag_lower(i)
00140
00141 def delete(self):
00142 for c in self.id:
00143 self.canvas.delete(c)
00144
00145 def set_pitch(self, pitch):
00146 self.pitch = pitch
00147 self.draw()
00148
00149 class CanvasLine(ToggleItem):
00150 def __init__(self, canvas, x0, y0, x1, y1, color, width):
00151 ToggleItem.__init__(self)
00152 self.canvas = canvas
00153 self.x0 = x0
00154 self.y0 = y0
00155 self.x1 = y1
00156 self.y1 = y1
00157 self.color = color
00158 self.width = width
00159 self.id = self.canvas.create_line(self.x0, self.y0, self.x1, self.y1,
00160 fill=self.color, width=self.width)
00161 self.draw()
00162
00163 def draw(self):
00164 if self.active == False: return
00165 self.delete()
00166 self.id = self.canvas.create_line(self.x0, self.y0, self.x1, self.y1,
00167 fill=self.color, width=self.width)
00168 def draw_line(self, x0, y0, x1, y1):
00169 self.x0 = x0
00170 self.y0 = y0
00171 self.x1 = x1
00172 self.y1 = y1
00173 self.draw()
00174
00175 def delete(self):
00176 self.canvas.delete(self.id)
00177
00178 class Stick:
00179 def __init__(self, canvas, x, y, r, **key):
00180 self.canvas = canvas
00181
00182 self.key = key
00183 self.id = self.canvas.create_oval(x-r, y-r, x+r, y+r, **key)
00184
00185 self.line = None
00186
00187 self.xy_text = None
00188
00189 self.pol_text = None
00190
00191
00192
00193 self.offsetx = x
00194 self.offsety = y
00195
00196
00197
00198 self.x = 0
00199 self.y = 0
00200
00201
00202 self.pos_x = 0
00203 self.pos_y = 0
00204
00205 self.coffx = 0
00206 self.coffy = 0
00207
00208
00209 self.make_binds()
00210
00211 def set_on_drag_start(self, func):
00212 self.on_drag_start = func
00213
00214 def set_on_dragging(self, func):
00215 self.on_dragging = func
00216
00217 def set_on_drag_end(self, func):
00218 self.on_drag_end = func
00219
00220 def make_binds(self):
00221 self.canvas.tag_bind(self.id, '<1>', self.drag_start)
00222 self.canvas.tag_bind(self.id, '<Button1-Motion>', self.dragging)
00223 self.canvas.tag_bind(self.id, '<Button1-ButtonRelease>', self.drag_end)
00224
00225 def drag_start(self, event):
00226
00227 x1 = event.x - self.offsetx
00228 y1 = event.y - self.offsety
00229 self.coffx = x1 - self.x
00230 self.coffy = y1 - self.y
00231
00232 self.calc_pol(self.pos_x, self.pos_y)
00233
00234
00235
00236 if self.on_drag_start != None:
00237 self.on_drag_start((self.pos_x, self.pos_y), (self.r, self.th))
00238
00239 def dragging(self, event):
00240
00241 x1 = event.x - self.offsetx
00242 y1 = event.y - self.offsety
00243 dx = (x1 - self.x) - self.coffx
00244 dy = (y1 - self.y) - self.coffy
00245
00246
00247 self.canvas.move(self.id, dx, dy)
00248 self.canvas.tag_raise(self.id)
00249
00250 self.x = x1 - self.coffx
00251 self.y = y1 - self.coffy
00252 self.pos_x = self.x
00253 self.pos_y = -self.y
00254 self.calc_pol(self.pos_x, self.pos_y)
00255
00256
00257 if self.on_dragging != None:
00258 self.on_dragging((self.pos_x, self.pos_y), (self.r, self.th))
00259
00260 def drag_end(self, event):
00261 x1 = event.x - self.offsetx
00262 y1 = event.y - self.offsety
00263
00264 dx = x1 - self.coffx
00265 dy = y1 - self.coffy
00266 self.canvas.move(self.id, -dx, -dy)
00267 self.x = 0
00268 self.y = 0
00269 self.pos_x = 0
00270 self.pos_y = 0
00271 self.calc_pol(self.pos_x, self.pos_y)
00272
00273 if self.on_drag_end != None:
00274 self.on_drag_end((self.pos_x, self.pos_y), (self.r, self.th))
00275
00276 def calc_pol(self, x, y):
00277 r = math.hypot(x, y)
00278 if r == 0:
00279 th = 720.0
00280 elif y >= 0.0:
00281 th = math.acos((x * 1.0) / (r * 1.0)) * 180.0 / math.pi
00282 elif y < 0.0:
00283 th = math.acos((x * 1.0) / (r * 1.0)) * -180.0 / math.pi
00284 self.r = r
00285 self.th = th
00286 return (r, th)
00287
00288 def get_pos(self):
00289 return self.pos_x, self.pos_y, self.r, self.th
00290
00291
00292 class TkJoystick(Frame):
00293 def __init__(self, r=10, width=300, height=300, master=None):
00294 Frame.__init__(self, master)
00295
00296 self.pos_x = 0.0
00297 self.pos_y = 0.0
00298 self.pol_r = 0.0
00299 self.pol_th = 0.0
00300
00301 self.stick_r = r
00302
00303 self.width = width
00304 self.height = height
00305
00306 self.wd = 160
00307
00308 self.x0 = width/2
00309 self.y0 = height/2
00310
00311 self.xentry = ""
00312 self.yentry = ""
00313 self.rentry = ""
00314 self.thentry = ""
00315
00316
00317 self.scale_var = DoubleVar()
00318 self.scale_var.set(1.0)
00319
00320
00321 self.vline_check = StringVar()
00322 self.vline_check.set("on")
00323 self.axis_check = StringVar()
00324 self.axis_check.set("on")
00325 self.circ_check = StringVar()
00326 self.circ_check.set("on")
00327 self.xy_check = StringVar()
00328 self.xy_check.set("on")
00329 self.pol_check = StringVar()
00330 self.pol_check.set("on")
00331
00332
00333 self.xentry = StringVar()
00334 self.xentry.set("0.0")
00335 self.yentry = StringVar()
00336 self.yentry.set("0.0")
00337
00338
00339 self.rentry = StringVar()
00340 self.rentry.set("0.0")
00341 self.thentry = StringVar()
00342 self.thentry.set("0.0")
00343
00344
00345 self.xytext_x = self.width - 50
00346 self.xytext_y = self.height - 25
00347 self.poltext_x = self.width - 60
00348 self.poltext_y = self.height - 10
00349
00350
00351 self.vline_color = "#aaaaaa"
00352 self.vline_width = 2
00353 self.default_pitch = 50
00354
00355
00356 self.on_update = None
00357
00358 self.init()
00359 self.pack()
00360
00361
00362 def init(self):
00363 self.canvas = Canvas(self, bg="white", \
00364 width = self.width, height = self.height)
00365 self.canvas.pack(side=LEFT)
00366
00367
00368 self.can_circle = CanvasCircle(self.canvas,
00369 self.x0, self.y0,
00370 self.width, self.height, pitch=50)
00371
00372
00373 self.can_axis = CanvasAxis(self.canvas, self.width, self.height)
00374
00375
00376 text = 'x: %4d, y: %4d' % (0, 0)
00377 self.can_xytext = CanvasText(self.canvas, text,
00378 self.xytext_x, self.xytext_y)
00379
00380
00381 text = 'r: %4.2f, th: NaN' % (0.0)
00382 self.can_poltext = CanvasText(self.canvas, text,
00383 self.poltext_x, self.poltext_y)
00384
00385 self.can_vline = CanvasLine(self.canvas, self.x0, self.y0,
00386 self.x0, self.y0,
00387 self.vline_color, self.vline_width)
00388
00389
00390 self.stick = Stick(self.canvas, self.width/2, self.height/2,
00391 self.stick_r,
00392 fill="#999999", width=1)
00393 self.stick.set_on_drag_start(self.on_pos_update)
00394 self.stick.set_on_dragging(self.on_pos_update)
00395 self.stick.set_on_drag_end(self.on_pos_update)
00396
00397
00398
00399 self.frame = Frame(self, height=self.height, width=self.wd)
00400 self.frame.pack(side=LEFT, fill=Y, padx=3, pady=3)
00401 self.create_scale(self.frame).pack(side=TOP, fill=Y)
00402 self.create_checkbutton(self.frame).pack(side=TOP, fill=Y)
00403 self.create_pollabel(self.frame).pack(side=BOTTOM, fill=Y)
00404 self.create_xylabel(self.frame).pack(side=BOTTOM, fill=Y)
00405 return
00406
00407 def create_scale(self, frame):
00408 f = Frame(frame, bd=2, relief=GROOVE)
00409 dummy = Frame(f, width=self.wd)
00410 dummy.pack(side=TOP)
00411 sl = Scale(f, from_=0, to=10, resolution=0.01,
00412 label="Scale Factor", command=self.on_scale,
00413 variable=self.scale_var, orient=HORIZONTAL)
00414 bt = Button(f, text="Reset Scale", command=self.reset_scale)
00415 sl.pack(side=TOP, fill=X)
00416 bt.pack(side=TOP, fill=X)
00417 return f
00418
00419 def on_scale(self, val):
00420 v = float(val)
00421 if v == 0.0:
00422 pitch = 0
00423 else:
00424 pitch = self.default_pitch/v
00425 self.can_circle.set_pitch(pitch)
00426
00427 def create_checkbutton(self, frame):
00428 f = Frame(frame, bd=2, relief=GROOVE)
00429 dummy = Frame(f, width=self.wd)
00430 dummy.pack(side=TOP)
00431 vec = Checkbutton(f, text="Vector Line",
00432 onvalue="on", offvalue="off",
00433 justify=LEFT, anchor=W,
00434 variable=self.vline_check,
00435 command=self.can_vline.toggle)
00436 axis = Checkbutton(f, text="Axis",
00437 onvalue="on", offvalue="off",
00438 justify=LEFT, anchor=W,
00439 variable=self.axis_check,
00440 command=self.can_axis.toggle)
00441 circ = Checkbutton(f, text="Background",
00442 onvalue="on", offvalue="off",
00443 justify=LEFT, anchor=W,
00444 variable=self.circ_check,
00445 command=self.can_circle.toggle)
00446 xy = Checkbutton(f, text="X-Y position",
00447 onvalue="on", offvalue="off",
00448 justify=LEFT, anchor=W,
00449 variable=self.xy_check,
00450 command=self.can_xytext.toggle)
00451 pol = Checkbutton(f, text="Polar postion",
00452 onvalue="on", offvalue="off",
00453 justify=LEFT, anchor=W,
00454 variable=self.pol_check,
00455 command=self.can_poltext.toggle)
00456 for w in [vec, axis, circ, xy, pol]:
00457 w.pack(side=TOP, anchor=W, fill=X)
00458 return f
00459
00460 def create_xylabel(self, frame):
00461 f = Frame(frame)
00462 dummy = Frame(f, width=self.wd)
00463 dummy.pack(side=TOP)
00464 xl = Label(f, text="x: ", width=3)
00465 xe = Entry(f, width=7, textvariable=self.xentry,
00466 justify=RIGHT, relief=GROOVE)
00467 yl = Label(f, text="y: ", width=3)
00468 ye = Entry(f, width=7, textvariable=self.yentry,
00469 justify=RIGHT, relief=GROOVE)
00470
00471 for w in [ye, yl, xe, xl]:
00472 w.pack(side=RIGHT, fill=X)
00473 return f
00474
00475 def create_pollabel(self, frame):
00476 f = Frame(frame, width=self.wd)
00477 dummy = Frame(f, width=self.wd)
00478 dummy.pack(side=TOP)
00479 rl = Label(f, text="r: ", width=3)
00480 re = Entry(f, width=7, textvariable=self.rentry,
00481 justify=RIGHT, relief=GROOVE)
00482 thl = Label(f, text="th:", width=3)
00483 the = Entry(f, width=7, textvariable=self.thentry,
00484 justify=RIGHT, relief=GROOVE)
00485 for w in [the, thl, re, rl]:
00486 w.pack(side=RIGHT, fill=X)
00487 return f
00488
00489 def reset_scale(self):
00490 self.scale_var.set(1.)
00491 pitch = self.default_pitch/1.0
00492 self.can_circle.set_pitch(pitch)
00493
00494 def get_pos(self):
00495 return self.pos_x, self.pos_y, self.pol_r, self.pol_th
00496
00497 def on_pos_update(self, pos, pol):
00498 self.pos_x = pos[0] * self.scale_var.get()
00499 self.pos_y = pos[1] * self.scale_var.get()
00500 self.pol_r = pol[0] * self.scale_var.get()
00501 self.pol_th = pol[1]
00502
00503 xt = '%4d' % (self.pos_x)
00504 yt = '%4d' % (self.pos_y)
00505 rt = '%5.2f' % (self.pol_r)
00506 if pol[1] > 360: tht = 'NaN'
00507 else: tht = '%5.2f' % self.pol_th
00508
00509 self.xentry.set(xt)
00510 self.yentry.set(yt)
00511 self.rentry.set(rt)
00512 self.thentry.set(tht)
00513
00514 text = 'x: %4d, y: %4d' % (pos[0], pos[1])
00515 self.can_xytext.draw_text(self.xytext_x, self.xytext_y, text)
00516 if pol[1] > 360:
00517 text = 'r: %5.2f, th: NaN' % (pol[0])
00518 else:
00519 text = 'r: %5.2f, th: %5.2f' % (pol[0], pol[1])
00520 self.can_poltext.draw_text(self.poltext_x, self.poltext_y, text)
00521
00522 self.can_vline.draw_line(self.x0, self.y0,
00523 self.x0 + pos[0],
00524 self.y0 - pos[1])
00525
00526 if self.on_update != None:
00527 self.on_update((self.pos_x, self.pos_y),
00528 (self.pol_r, self.pol_th))
00529
00530 def set_on_update(self, func):
00531 self.on_update = func
00532
00533 def test ():
00534 m = TkJoystick()
00535
00536 while 1:
00537 m.update()
00538 print m.get_pos()
00539
00540
00541 if __name__ == '__main__': test()