tkjoystick.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # -*- coding: utf-8 -*-
00003 # -*- Python -*-
00004 
00005 #
00006 # @brief tk joystick
00007 # @date $Date$
00008 # @author Norkai Ando <n-ando@aist.go.jp>
00009 #
00010 # Copyright (C) 2007
00011 #     Noriaki Ando
00012 #     Task-intelligence Research Group,
00013 #     Intelligent Systems Research Institute,
00014 #     National Institute of
00015 #         Advanced Industrial Science and Technology (AIST), Japan
00016 #     All rights reserved.
00017 #
00018 # $Id$
00019 #
00020 
00021 # $Log$
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         # Joystick ID
00182         self.key = key
00183         self.id = self.canvas.create_oval(x-r, y-r, x+r, y+r, **key)
00184         # ID of the line from center to joystick handle
00185         self.line = None
00186         # (x,y) text ID
00187         self.xy_text = None
00188         # (r,th) text ID
00189         self.pol_text = None
00190 
00191         # Offset from display coordinate system to joystick coordinate
00192         # system
00193         self.offsetx = x
00194         self.offsety = y
00195 
00196         # Position of the joystick in the display joystick coordinate
00197         # system
00198         self.x = 0
00199         self.y = 0
00200 
00201         # Position of the jpystick in the jpystick coordinate system
00202         self.pos_x = 0
00203         self.pos_y = 0
00204 
00205         self.coffx = 0
00206         self.coffy = 0
00207 
00208         # Binding to joystick
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         # Calculate offset on the clocked position
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 #        self.draw_text()
00234 
00235         # Callback
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         # Moving length of drag
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         # Moving circle
00247         self.canvas.move(self.id, dx, dy)
00248         self.canvas.tag_raise(self.id)
00249         # Calculate joystick position
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         # Callback
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         # Moving length to return back
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         # Callback
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         # scale variable
00317         self.scale_var = DoubleVar()
00318         self.scale_var.set(1.0)
00319 
00320         # checkbutton variables
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         # x-y entry variables
00333         self.xentry = StringVar()
00334         self.xentry.set("0.0")
00335         self.yentry = StringVar()
00336         self.yentry.set("0.0")
00337 
00338         # polarpos entry variables
00339         self.rentry = StringVar()
00340         self.rentry.set("0.0")
00341         self.thentry = StringVar()
00342         self.thentry.set("0.0")
00343 
00344         # text position
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         # vector line
00351         self.vline_color = "#aaaaaa"
00352         self.vline_width = 2
00353         self.default_pitch = 50
00354 
00355         # callback function
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         # coaxial pattern
00368         self.can_circle = CanvasCircle(self.canvas, 
00369                                    self.x0, self.y0,
00370                                    self.width, self.height, pitch=50)
00371 
00372         # axis
00373         self.can_axis = CanvasAxis(self.canvas, self.width, self.height)
00374 
00375         # x-y text
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         # polar position text
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         # joystick circle
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         # right side widgets
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()


openrtm_aist_python
Author(s): Shinji Kurihara
autogenerated on Thu Aug 27 2015 14:17:28