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