tkjoystick.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: euc-jp -*-
3 
4 #
5 # @brief tk joystick
6 # @date $Date$
7 # @author Norkai Ando <n-ando@aist.go.jp>
8 #
9 # Copyright (C) 2007
10 # Noriaki Ando
11 # Task-intelligence Research Group,
12 # Intelligent Systems Research Institute,
13 # National Institute of
14 # Advanced Industrial Science and Technology (AIST), Japan
15 # All rights reserved.
16 #
17 # $Id$
18 #
19 
20 # $Log$
21 #
22 
23 from Tkinter import *
24 import time
25 import math
26 import mutex
27 import sys
28 
29 class ToggleItem:
30  def __init__(self):
31  self.active = True
32 
33  def activate(self):
34  self.active = True
35  self.draw()
36 
37  def deactivate(self):
38  self.active = False
39  self.delete()
40 
41  def toggle(self):
42  if self.active:
43  self.deactivate()
44  else:
45  self.activate()
46 
47 
49  def __init__(self, canvas, text, x, y):
50  ToggleItem.__init__(self)
51  self.canvas = canvas
52  self.id = self.canvas.create_text(x, y, text=text)
53  self.text = text
54  self.x = x
55  self.y = y
56  self.draw_text(x, y, text)
57 
58  def draw(self):
59  if self.active == False: return
60  self.delete()
61  self.id = self.canvas.create_text(self.x, self.y, text=self.text)
62 
63  def draw_text(self, x, y, text):
64  self.x = x
65  self.y = y
66  self.text = text
67  self.draw()
68 
69  def delete(self):
70  self.canvas.delete(self.id)
71 
72 
74  def __init__(self, canvas, width, height):
75  ToggleItem.__init__(self)
76  self.x0 = width/2
77  self.y0 = height/2
78  self.width = width
79  self.height = height
80  self.canvas = canvas
81  self.id = [None] * 4
82  self.draw()
83 
84  def draw(self):
85  if self.active == False: return
86  self.delete()
87  self.id[0] = self.canvas.create_line(0, self.height/2,
88  self.width, self.height/2)
89  self.id[1] = self.canvas.create_text(self.width - 10,
90  self.height/2 + 10,
91  text="x")
92  self.id[2] = self.canvas.create_line(self.width/2, 0,
93  self.width/2, self.height)
94  self.id[3] = self.canvas.create_text(self.width/2 + 10,
95  + 10, text="y")
96  return
97 
98  def delete(self):
99  for i in self.id:
100  self.canvas.delete(i)
101 
103  def __init__(self, canvas, x, y, width, height, pitch):
104  ToggleItem.__init__(self)
105  self.canvas = canvas
106  self.x = x
107  self.y = y
108  self.width = width
109  self.height = height
110  self.pitch = pitch
111  self.id = []
112  self.draw()
113 
114  def draw(self):
115  if self.active == False: return
116  self.delete()
117  if self.pitch == 0:
118  circnum = 0
119  else:
120  circnum = max(self.width, self.height) / self.pitch
121  circrange = range(int(circnum))
122  circrange.reverse()
123  for i in circrange:
124  x0 = self.x - self.pitch * i
125  y0 = self.y - self.pitch * i
126  x1 = self.x + self.pitch * i
127  y1 = self.y + self.pitch * i
128  if i % 2 == 0:
129  color = "#dddddd"
130  else:
131  color = "#eeeeee"
132  id = self.canvas.create_oval(x0, y0, x1, y1, fill=color,
133  outline="#ffffff")
134  self.id.append(id)
135  id = self.id
136  id.reverse()
137  for i in id:
138  self.canvas.tag_lower(i)
139 
140  def delete(self):
141  for c in self.id:
142  self.canvas.delete(c)
143 
144  def set_pitch(self, pitch):
145  self.pitch = pitch
146  self.draw()
147 
149  def __init__(self, canvas, x0, y0, x1, y1, color, width):
150  ToggleItem.__init__(self)
151  self.canvas = canvas
152  self.x0 = x0
153  self.y0 = y0
154  self.x1 = y1
155  self.y1 = y1
156  self.color = color
157  self.width = width
158  self.id = self.canvas.create_line(self.x0, self.y0, self.x1, self.y1,
159  fill=self.color, width=self.width)
160  self.draw()
161 
162  def draw(self):
163  if self.active == False: return
164  self.delete()
165  self.id = self.canvas.create_line(self.x0, self.y0, self.x1, self.y1,
166  fill=self.color, width=self.width)
167  def draw_line(self, x0, y0, x1, y1):
168  self.x0 = x0
169  self.y0 = y0
170  self.x1 = x1
171  self.y1 = y1
172  self.draw()
173 
174  def delete(self):
175  self.canvas.delete(self.id)
176 
177 class Stick:
178  def __init__(self, canvas, x, y, r, **key):
179  self.canvas = canvas
180  # ジョイスティックID
181  self.key = key
182  self.id = self.canvas.create_oval(x-r, y-r, x+r, y+r, **key)
183  # 中心からジョイスティックへの線ID
184  self.line = None
185  # (x,y) テキスト表示ID
186  self.xy_text = None
187  # (r,th) テキスト表示ID
188  self.pol_text = None
189 
190  # 画面座標系から画面ジョイスティック座標系へのオフセット self.offsetx = x self.offsety = y # 画面ジョイスティック座標系でのジョイスティック位置 self.x = 0 self.y = 0 # ジョイスティック座標系でのジョイスティック位置 self.pos_x = 0 self.pos_y = 0 self.coffx = 0 self.coffy = 0 # ジョイスティックへのバインド self.make_binds() # テキスト描画 def set_on_drag_start(self, func): self.on_drag_start = func def set_on_dragging(self, func): self.on_dragging = func def set_on_drag_end(self, func): self.on_drag_end = func def make_binds(self): self.canvas.tag_bind(self.id, '<1>', self.drag_start) self.canvas.tag_bind(self.id, '<Button1-Motion>', self.dragging) self.canvas.tag_bind(self.id, '<Button1-ButtonRelease>', self.drag_end) def drag_start(self, event): # クリック位置のオフセットを計算 x1 = event.x - self.offsetx y1 = event.y - self.offsety self.coffx = x1 - self.x self.coffy = y1 - self.y self.calc_pol(self.pos_x, self.pos_y) # self.draw_text() # コールバック if self.on_drag_start != None: self.on_drag_start((self.pos_x, self.pos_y), (self.r, self.th)) def dragging(self, event): # ドラッグの移動量 x1 = event.x - self.offsetx y1 = event.y - self.offsety dx = (x1 - self.x) - self.coffx dy = (y1 - self.y) - self.coffy # 円を移動 self.canvas.move(self.id, dx, dy) self.canvas.tag_raise(self.id) # ジョイスティック位置を計算 self.x = x1 - self.coffx self.y = y1 - self.coffy self.pos_x = self.x self.pos_y = -self.y self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_dragging != None: self.on_dragging((self.pos_x, self.pos_y), (self.r, self.th)) def drag_end(self, event): x1 = event.x - self.offsetx y1 = event.y - self.offsety # 戻すための移動量 dx = x1 - self.coffx dy = y1 - self.coffy self.canvas.move(self.id, -dx, -dy) self.x = 0 self.y = 0 self.pos_x = 0 self.pos_y = 0 self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_drag_end != None: self.on_drag_end((self.pos_x, self.pos_y), (self.r, self.th)) def calc_pol(self, x, y): r = math.hypot(x, y) if r == 0: th = 720.0 elif y >= 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * 180.0 / math.pi elif y < 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * -180.0 / math.pi self.r = r self.th = th return (r, th) def get_pos(self): return self.pos_x, self.pos_y, self.r, self.th class TkJoystick(Frame): def __init__(self, r=10, width=300, height=300, master=None): Frame.__init__(self, master) self.pos_x = 0.0 self.pos_y = 0.0 self.pol_r = 0.0 self.pol_th = 0.0 self.stick_r = r self.width = width self.height = height self.wd = 160 self.x0 = width/2 self.y0 = height/2 self.xentry = "" self.yentry = "" self.rentry = "" self.thentry = "" # scale variable self.scale_var = DoubleVar() self.scale_var.set(1.0) # checkbutton variables self.vline_check = StringVar() self.vline_check.set("on") self.axis_check = StringVar() self.axis_check.set("on") self.circ_check = StringVar() self.circ_check.set("on") self.xy_check = StringVar() self.xy_check.set("on") self.pol_check = StringVar() self.pol_check.set("on") # x-y entry variables self.xentry = StringVar() self.xentry.set("0.0") self.yentry = StringVar() self.yentry.set("0.0") # polarpos entry variables self.rentry = StringVar() self.rentry.set("0.0") self.thentry = StringVar() self.thentry.set("0.0") # text position self.xytext_x = self.width - 50 self.xytext_y = self.height - 25 self.poltext_x = self.width - 60 self.poltext_y = self.height - 10 # vector line self.vline_color = "#aaaaaa" self.vline_width = 2 self.default_pitch = 50 # callback function self.on_update = None self.init() self.pack() def init(self): self.canvas = Canvas(self, bg="white", \ width = self.width, height = self.height) self.canvas.pack(side=LEFT) # coaxial pattern self.can_circle = CanvasCircle(self.canvas, self.x0, self.y0, self.width, self.height, pitch=50) # axis self.can_axis = CanvasAxis(self.canvas, self.width, self.height) # x-y text text = 'x: %4d, y: %4d' % (0, 0) self.can_xytext = CanvasText(self.canvas, text, self.xytext_x, self.xytext_y) # polar position text text = 'r: %4.2f, th: NaN' % (0.0) self.can_poltext = CanvasText(self.canvas, text, self.poltext_x, self.poltext_y) self.can_vline = CanvasLine(self.canvas, self.x0, self.y0, self.x0, self.y0, self.vline_color, self.vline_width) # joystick circle self.stick = Stick(self.canvas, self.width/2, self.height/2, self.stick_r, fill="#999999", width=1) self.stick.set_on_drag_start(self.on_pos_update) self.stick.set_on_dragging(self.on_pos_update) self.stick.set_on_drag_end(self.on_pos_update) # right side widgets self.frame = Frame(self, height=self.height, width=self.wd) self.frame.pack(side=LEFT, fill=Y, padx=3, pady=3) self.create_scale(self.frame).pack(side=TOP, fill=Y) self.create_checkbutton(self.frame).pack(side=TOP, fill=Y) self.create_pollabel(self.frame).pack(side=BOTTOM, fill=Y) self.create_xylabel(self.frame).pack(side=BOTTOM, fill=Y) return def create_scale(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) sl = Scale(f, from_=0, to=10, resolution=0.01, label="Scale Factor", command=self.on_scale, variable=self.scale_var, orient=HORIZONTAL) bt = Button(f, text="Reset Scale", command=self.reset_scale) sl.pack(side=TOP, fill=X) bt.pack(side=TOP, fill=X) return f def on_scale(self, val): v = float(val) if v == 0.0: pitch = 0 else: pitch = self.default_pitch/v self.can_circle.set_pitch(pitch) def create_checkbutton(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) vec = Checkbutton(f, text="Vector Line", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.vline_check, command=self.can_vline.toggle) axis = Checkbutton(f, text="Axis", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.axis_check, command=self.can_axis.toggle) circ = Checkbutton(f, text="Background", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.circ_check, command=self.can_circle.toggle) xy = Checkbutton(f, text="X-Y position", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.xy_check, command=self.can_xytext.toggle) pol = Checkbutton(f, text="Polar postion", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.pol_check, command=self.can_poltext.toggle) for w in [vec, axis, circ, xy, pol]: w.pack(side=TOP, anchor=W, fill=X) return f def create_xylabel(self, frame): f = Frame(frame) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) xl = Label(f, text="x: ", width=3) xe = Entry(f, width=7, textvariable=self.xentry, justify=RIGHT, relief=GROOVE) yl = Label(f, text="y: ", width=3) ye = Entry(f, width=7, textvariable=self.yentry, justify=RIGHT, relief=GROOVE) for w in [ye, yl, xe, xl]: w.pack(side=RIGHT, fill=X) return f def create_pollabel(self, frame): f = Frame(frame, width=self.wd) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) rl = Label(f, text="r: ", width=3) re = Entry(f, width=7, textvariable=self.rentry, justify=RIGHT, relief=GROOVE) thl = Label(f, text="th:", width=3) the = Entry(f, width=7, textvariable=self.thentry, justify=RIGHT, relief=GROOVE) for w in [the, thl, re, rl]: w.pack(side=RIGHT, fill=X) return f def reset_scale(self): self.scale_var.set(1.) pitch = self.default_pitch/1.0 self.can_circle.set_pitch(pitch) def get_pos(self): return self.pos_x, self.pos_y, self.pol_r, self.pol_th def on_pos_update(self, pos, pol): self.pos_x = pos[0] * self.scale_var.get() self.pos_y = pos[1] * self.scale_var.get() self.pol_r = pol[0] * self.scale_var.get() self.pol_th = pol[1] xt = '%4d' % (self.pos_x) yt = '%4d' % (self.pos_y) rt = '%5.2f' % (self.pol_r) if pol[1] > 360: tht = 'NaN' else: tht = '%5.2f' % self.pol_th self.xentry.set(xt) self.yentry.set(yt) self.rentry.set(rt) self.thentry.set(tht) text = 'x: %4d, y: %4d' % (pos[0], pos[1]) self.can_xytext.draw_text(self.xytext_x, self.xytext_y, text) if pol[1] > 360: text = 'r: %5.2f, th: NaN' % (pol[0]) else: text = 'r: %5.2f, th: %5.2f' % (pol[0], pol[1]) self.can_poltext.draw_text(self.poltext_x, self.poltext_y, text) self.can_vline.draw_line(self.x0, self.y0, self.x0 + pos[0], self.y0 - pos[1]) if self.on_update != None: self.on_update((self.pos_x, self.pos_y), (self.pol_r, self.pol_th)) def set_on_update(self, func): self.on_update = func def test (): m = TkJoystick() while 1: m.update() print(m.get_pos()) if __name__ == '__main__': test()
191  self.offsetx = x
192  self.offsety = y
193 
194  # 画面ジョイスティック座標系でのジョイスティック位置 self.x = 0 self.y = 0 # ジョイスティック座標系でのジョイスティック位置 self.pos_x = 0 self.pos_y = 0 self.coffx = 0 self.coffy = 0 # ジョイスティックへのバインド self.make_binds() # テキスト描画 def set_on_drag_start(self, func): self.on_drag_start = func def set_on_dragging(self, func): self.on_dragging = func def set_on_drag_end(self, func): self.on_drag_end = func def make_binds(self): self.canvas.tag_bind(self.id, '<1>', self.drag_start) self.canvas.tag_bind(self.id, '<Button1-Motion>', self.dragging) self.canvas.tag_bind(self.id, '<Button1-ButtonRelease>', self.drag_end) def drag_start(self, event): # クリック位置のオフセットを計算 x1 = event.x - self.offsetx y1 = event.y - self.offsety self.coffx = x1 - self.x self.coffy = y1 - self.y self.calc_pol(self.pos_x, self.pos_y) # self.draw_text() # コールバック if self.on_drag_start != None: self.on_drag_start((self.pos_x, self.pos_y), (self.r, self.th)) def dragging(self, event): # ドラッグの移動量 x1 = event.x - self.offsetx y1 = event.y - self.offsety dx = (x1 - self.x) - self.coffx dy = (y1 - self.y) - self.coffy # 円を移動 self.canvas.move(self.id, dx, dy) self.canvas.tag_raise(self.id) # ジョイスティック位置を計算 self.x = x1 - self.coffx self.y = y1 - self.coffy self.pos_x = self.x self.pos_y = -self.y self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_dragging != None: self.on_dragging((self.pos_x, self.pos_y), (self.r, self.th)) def drag_end(self, event): x1 = event.x - self.offsetx y1 = event.y - self.offsety # 戻すための移動量 dx = x1 - self.coffx dy = y1 - self.coffy self.canvas.move(self.id, -dx, -dy) self.x = 0 self.y = 0 self.pos_x = 0 self.pos_y = 0 self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_drag_end != None: self.on_drag_end((self.pos_x, self.pos_y), (self.r, self.th)) def calc_pol(self, x, y): r = math.hypot(x, y) if r == 0: th = 720.0 elif y >= 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * 180.0 / math.pi elif y < 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * -180.0 / math.pi self.r = r self.th = th return (r, th) def get_pos(self): return self.pos_x, self.pos_y, self.r, self.th class TkJoystick(Frame): def __init__(self, r=10, width=300, height=300, master=None): Frame.__init__(self, master) self.pos_x = 0.0 self.pos_y = 0.0 self.pol_r = 0.0 self.pol_th = 0.0 self.stick_r = r self.width = width self.height = height self.wd = 160 self.x0 = width/2 self.y0 = height/2 self.xentry = "" self.yentry = "" self.rentry = "" self.thentry = "" # scale variable self.scale_var = DoubleVar() self.scale_var.set(1.0) # checkbutton variables self.vline_check = StringVar() self.vline_check.set("on") self.axis_check = StringVar() self.axis_check.set("on") self.circ_check = StringVar() self.circ_check.set("on") self.xy_check = StringVar() self.xy_check.set("on") self.pol_check = StringVar() self.pol_check.set("on") # x-y entry variables self.xentry = StringVar() self.xentry.set("0.0") self.yentry = StringVar() self.yentry.set("0.0") # polarpos entry variables self.rentry = StringVar() self.rentry.set("0.0") self.thentry = StringVar() self.thentry.set("0.0") # text position self.xytext_x = self.width - 50 self.xytext_y = self.height - 25 self.poltext_x = self.width - 60 self.poltext_y = self.height - 10 # vector line self.vline_color = "#aaaaaa" self.vline_width = 2 self.default_pitch = 50 # callback function self.on_update = None self.init() self.pack() def init(self): self.canvas = Canvas(self, bg="white", \ width = self.width, height = self.height) self.canvas.pack(side=LEFT) # coaxial pattern self.can_circle = CanvasCircle(self.canvas, self.x0, self.y0, self.width, self.height, pitch=50) # axis self.can_axis = CanvasAxis(self.canvas, self.width, self.height) # x-y text text = 'x: %4d, y: %4d' % (0, 0) self.can_xytext = CanvasText(self.canvas, text, self.xytext_x, self.xytext_y) # polar position text text = 'r: %4.2f, th: NaN' % (0.0) self.can_poltext = CanvasText(self.canvas, text, self.poltext_x, self.poltext_y) self.can_vline = CanvasLine(self.canvas, self.x0, self.y0, self.x0, self.y0, self.vline_color, self.vline_width) # joystick circle self.stick = Stick(self.canvas, self.width/2, self.height/2, self.stick_r, fill="#999999", width=1) self.stick.set_on_drag_start(self.on_pos_update) self.stick.set_on_dragging(self.on_pos_update) self.stick.set_on_drag_end(self.on_pos_update) # right side widgets self.frame = Frame(self, height=self.height, width=self.wd) self.frame.pack(side=LEFT, fill=Y, padx=3, pady=3) self.create_scale(self.frame).pack(side=TOP, fill=Y) self.create_checkbutton(self.frame).pack(side=TOP, fill=Y) self.create_pollabel(self.frame).pack(side=BOTTOM, fill=Y) self.create_xylabel(self.frame).pack(side=BOTTOM, fill=Y) return def create_scale(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) sl = Scale(f, from_=0, to=10, resolution=0.01, label="Scale Factor", command=self.on_scale, variable=self.scale_var, orient=HORIZONTAL) bt = Button(f, text="Reset Scale", command=self.reset_scale) sl.pack(side=TOP, fill=X) bt.pack(side=TOP, fill=X) return f def on_scale(self, val): v = float(val) if v == 0.0: pitch = 0 else: pitch = self.default_pitch/v self.can_circle.set_pitch(pitch) def create_checkbutton(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) vec = Checkbutton(f, text="Vector Line", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.vline_check, command=self.can_vline.toggle) axis = Checkbutton(f, text="Axis", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.axis_check, command=self.can_axis.toggle) circ = Checkbutton(f, text="Background", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.circ_check, command=self.can_circle.toggle) xy = Checkbutton(f, text="X-Y position", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.xy_check, command=self.can_xytext.toggle) pol = Checkbutton(f, text="Polar postion", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.pol_check, command=self.can_poltext.toggle) for w in [vec, axis, circ, xy, pol]: w.pack(side=TOP, anchor=W, fill=X) return f def create_xylabel(self, frame): f = Frame(frame) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) xl = Label(f, text="x: ", width=3) xe = Entry(f, width=7, textvariable=self.xentry, justify=RIGHT, relief=GROOVE) yl = Label(f, text="y: ", width=3) ye = Entry(f, width=7, textvariable=self.yentry, justify=RIGHT, relief=GROOVE) for w in [ye, yl, xe, xl]: w.pack(side=RIGHT, fill=X) return f def create_pollabel(self, frame): f = Frame(frame, width=self.wd) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) rl = Label(f, text="r: ", width=3) re = Entry(f, width=7, textvariable=self.rentry, justify=RIGHT, relief=GROOVE) thl = Label(f, text="th:", width=3) the = Entry(f, width=7, textvariable=self.thentry, justify=RIGHT, relief=GROOVE) for w in [the, thl, re, rl]: w.pack(side=RIGHT, fill=X) return f def reset_scale(self): self.scale_var.set(1.) pitch = self.default_pitch/1.0 self.can_circle.set_pitch(pitch) def get_pos(self): return self.pos_x, self.pos_y, self.pol_r, self.pol_th def on_pos_update(self, pos, pol): self.pos_x = pos[0] * self.scale_var.get() self.pos_y = pos[1] * self.scale_var.get() self.pol_r = pol[0] * self.scale_var.get() self.pol_th = pol[1] xt = '%4d' % (self.pos_x) yt = '%4d' % (self.pos_y) rt = '%5.2f' % (self.pol_r) if pol[1] > 360: tht = 'NaN' else: tht = '%5.2f' % self.pol_th self.xentry.set(xt) self.yentry.set(yt) self.rentry.set(rt) self.thentry.set(tht) text = 'x: %4d, y: %4d' % (pos[0], pos[1]) self.can_xytext.draw_text(self.xytext_x, self.xytext_y, text) if pol[1] > 360: text = 'r: %5.2f, th: NaN' % (pol[0]) else: text = 'r: %5.2f, th: %5.2f' % (pol[0], pol[1]) self.can_poltext.draw_text(self.poltext_x, self.poltext_y, text) self.can_vline.draw_line(self.x0, self.y0, self.x0 + pos[0], self.y0 - pos[1]) if self.on_update != None: self.on_update((self.pos_x, self.pos_y), (self.pol_r, self.pol_th)) def set_on_update(self, func): self.on_update = func def test (): m = TkJoystick() while 1: m.update() print(m.get_pos()) if __name__ == '__main__': test()
195  self.x = 0
196  self.y = 0
197 
198  # ジョイスティック座標系でのジョイスティック位置 self.pos_x = 0 self.pos_y = 0 self.coffx = 0 self.coffy = 0 # ジョイスティックへのバインド self.make_binds() # テキスト描画 def set_on_drag_start(self, func): self.on_drag_start = func def set_on_dragging(self, func): self.on_dragging = func def set_on_drag_end(self, func): self.on_drag_end = func def make_binds(self): self.canvas.tag_bind(self.id, '<1>', self.drag_start) self.canvas.tag_bind(self.id, '<Button1-Motion>', self.dragging) self.canvas.tag_bind(self.id, '<Button1-ButtonRelease>', self.drag_end) def drag_start(self, event): # クリック位置のオフセットを計算 x1 = event.x - self.offsetx y1 = event.y - self.offsety self.coffx = x1 - self.x self.coffy = y1 - self.y self.calc_pol(self.pos_x, self.pos_y) # self.draw_text() # コールバック if self.on_drag_start != None: self.on_drag_start((self.pos_x, self.pos_y), (self.r, self.th)) def dragging(self, event): # ドラッグの移動量 x1 = event.x - self.offsetx y1 = event.y - self.offsety dx = (x1 - self.x) - self.coffx dy = (y1 - self.y) - self.coffy # 円を移動 self.canvas.move(self.id, dx, dy) self.canvas.tag_raise(self.id) # ジョイスティック位置を計算 self.x = x1 - self.coffx self.y = y1 - self.coffy self.pos_x = self.x self.pos_y = -self.y self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_dragging != None: self.on_dragging((self.pos_x, self.pos_y), (self.r, self.th)) def drag_end(self, event): x1 = event.x - self.offsetx y1 = event.y - self.offsety # 戻すための移動量 dx = x1 - self.coffx dy = y1 - self.coffy self.canvas.move(self.id, -dx, -dy) self.x = 0 self.y = 0 self.pos_x = 0 self.pos_y = 0 self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_drag_end != None: self.on_drag_end((self.pos_x, self.pos_y), (self.r, self.th)) def calc_pol(self, x, y): r = math.hypot(x, y) if r == 0: th = 720.0 elif y >= 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * 180.0 / math.pi elif y < 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * -180.0 / math.pi self.r = r self.th = th return (r, th) def get_pos(self): return self.pos_x, self.pos_y, self.r, self.th class TkJoystick(Frame): def __init__(self, r=10, width=300, height=300, master=None): Frame.__init__(self, master) self.pos_x = 0.0 self.pos_y = 0.0 self.pol_r = 0.0 self.pol_th = 0.0 self.stick_r = r self.width = width self.height = height self.wd = 160 self.x0 = width/2 self.y0 = height/2 self.xentry = "" self.yentry = "" self.rentry = "" self.thentry = "" # scale variable self.scale_var = DoubleVar() self.scale_var.set(1.0) # checkbutton variables self.vline_check = StringVar() self.vline_check.set("on") self.axis_check = StringVar() self.axis_check.set("on") self.circ_check = StringVar() self.circ_check.set("on") self.xy_check = StringVar() self.xy_check.set("on") self.pol_check = StringVar() self.pol_check.set("on") # x-y entry variables self.xentry = StringVar() self.xentry.set("0.0") self.yentry = StringVar() self.yentry.set("0.0") # polarpos entry variables self.rentry = StringVar() self.rentry.set("0.0") self.thentry = StringVar() self.thentry.set("0.0") # text position self.xytext_x = self.width - 50 self.xytext_y = self.height - 25 self.poltext_x = self.width - 60 self.poltext_y = self.height - 10 # vector line self.vline_color = "#aaaaaa" self.vline_width = 2 self.default_pitch = 50 # callback function self.on_update = None self.init() self.pack() def init(self): self.canvas = Canvas(self, bg="white", \ width = self.width, height = self.height) self.canvas.pack(side=LEFT) # coaxial pattern self.can_circle = CanvasCircle(self.canvas, self.x0, self.y0, self.width, self.height, pitch=50) # axis self.can_axis = CanvasAxis(self.canvas, self.width, self.height) # x-y text text = 'x: %4d, y: %4d' % (0, 0) self.can_xytext = CanvasText(self.canvas, text, self.xytext_x, self.xytext_y) # polar position text text = 'r: %4.2f, th: NaN' % (0.0) self.can_poltext = CanvasText(self.canvas, text, self.poltext_x, self.poltext_y) self.can_vline = CanvasLine(self.canvas, self.x0, self.y0, self.x0, self.y0, self.vline_color, self.vline_width) # joystick circle self.stick = Stick(self.canvas, self.width/2, self.height/2, self.stick_r, fill="#999999", width=1) self.stick.set_on_drag_start(self.on_pos_update) self.stick.set_on_dragging(self.on_pos_update) self.stick.set_on_drag_end(self.on_pos_update) # right side widgets self.frame = Frame(self, height=self.height, width=self.wd) self.frame.pack(side=LEFT, fill=Y, padx=3, pady=3) self.create_scale(self.frame).pack(side=TOP, fill=Y) self.create_checkbutton(self.frame).pack(side=TOP, fill=Y) self.create_pollabel(self.frame).pack(side=BOTTOM, fill=Y) self.create_xylabel(self.frame).pack(side=BOTTOM, fill=Y) return def create_scale(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) sl = Scale(f, from_=0, to=10, resolution=0.01, label="Scale Factor", command=self.on_scale, variable=self.scale_var, orient=HORIZONTAL) bt = Button(f, text="Reset Scale", command=self.reset_scale) sl.pack(side=TOP, fill=X) bt.pack(side=TOP, fill=X) return f def on_scale(self, val): v = float(val) if v == 0.0: pitch = 0 else: pitch = self.default_pitch/v self.can_circle.set_pitch(pitch) def create_checkbutton(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) vec = Checkbutton(f, text="Vector Line", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.vline_check, command=self.can_vline.toggle) axis = Checkbutton(f, text="Axis", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.axis_check, command=self.can_axis.toggle) circ = Checkbutton(f, text="Background", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.circ_check, command=self.can_circle.toggle) xy = Checkbutton(f, text="X-Y position", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.xy_check, command=self.can_xytext.toggle) pol = Checkbutton(f, text="Polar postion", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.pol_check, command=self.can_poltext.toggle) for w in [vec, axis, circ, xy, pol]: w.pack(side=TOP, anchor=W, fill=X) return f def create_xylabel(self, frame): f = Frame(frame) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) xl = Label(f, text="x: ", width=3) xe = Entry(f, width=7, textvariable=self.xentry, justify=RIGHT, relief=GROOVE) yl = Label(f, text="y: ", width=3) ye = Entry(f, width=7, textvariable=self.yentry, justify=RIGHT, relief=GROOVE) for w in [ye, yl, xe, xl]: w.pack(side=RIGHT, fill=X) return f def create_pollabel(self, frame): f = Frame(frame, width=self.wd) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) rl = Label(f, text="r: ", width=3) re = Entry(f, width=7, textvariable=self.rentry, justify=RIGHT, relief=GROOVE) thl = Label(f, text="th:", width=3) the = Entry(f, width=7, textvariable=self.thentry, justify=RIGHT, relief=GROOVE) for w in [the, thl, re, rl]: w.pack(side=RIGHT, fill=X) return f def reset_scale(self): self.scale_var.set(1.) pitch = self.default_pitch/1.0 self.can_circle.set_pitch(pitch) def get_pos(self): return self.pos_x, self.pos_y, self.pol_r, self.pol_th def on_pos_update(self, pos, pol): self.pos_x = pos[0] * self.scale_var.get() self.pos_y = pos[1] * self.scale_var.get() self.pol_r = pol[0] * self.scale_var.get() self.pol_th = pol[1] xt = '%4d' % (self.pos_x) yt = '%4d' % (self.pos_y) rt = '%5.2f' % (self.pol_r) if pol[1] > 360: tht = 'NaN' else: tht = '%5.2f' % self.pol_th self.xentry.set(xt) self.yentry.set(yt) self.rentry.set(rt) self.thentry.set(tht) text = 'x: %4d, y: %4d' % (pos[0], pos[1]) self.can_xytext.draw_text(self.xytext_x, self.xytext_y, text) if pol[1] > 360: text = 'r: %5.2f, th: NaN' % (pol[0]) else: text = 'r: %5.2f, th: %5.2f' % (pol[0], pol[1]) self.can_poltext.draw_text(self.poltext_x, self.poltext_y, text) self.can_vline.draw_line(self.x0, self.y0, self.x0 + pos[0], self.y0 - pos[1]) if self.on_update != None: self.on_update((self.pos_x, self.pos_y), (self.pol_r, self.pol_th)) def set_on_update(self, func): self.on_update = func def test (): m = TkJoystick() while 1: m.update() print(m.get_pos()) if __name__ == '__main__': test()
199  self.pos_x = 0
200  self.pos_y = 0
201 
202  self.coffx = 0
203  self.coffy = 0
204 
205  # ジョイスティックへのバインド self.make_binds() # テキスト描画 def set_on_drag_start(self, func): self.on_drag_start = func def set_on_dragging(self, func): self.on_dragging = func def set_on_drag_end(self, func): self.on_drag_end = func def make_binds(self): self.canvas.tag_bind(self.id, '<1>', self.drag_start) self.canvas.tag_bind(self.id, '<Button1-Motion>', self.dragging) self.canvas.tag_bind(self.id, '<Button1-ButtonRelease>', self.drag_end) def drag_start(self, event): # クリック位置のオフセットを計算 x1 = event.x - self.offsetx y1 = event.y - self.offsety self.coffx = x1 - self.x self.coffy = y1 - self.y self.calc_pol(self.pos_x, self.pos_y) # self.draw_text() # コールバック if self.on_drag_start != None: self.on_drag_start((self.pos_x, self.pos_y), (self.r, self.th)) def dragging(self, event): # ドラッグの移動量 x1 = event.x - self.offsetx y1 = event.y - self.offsety dx = (x1 - self.x) - self.coffx dy = (y1 - self.y) - self.coffy # 円を移動 self.canvas.move(self.id, dx, dy) self.canvas.tag_raise(self.id) # ジョイスティック位置を計算 self.x = x1 - self.coffx self.y = y1 - self.coffy self.pos_x = self.x self.pos_y = -self.y self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_dragging != None: self.on_dragging((self.pos_x, self.pos_y), (self.r, self.th)) def drag_end(self, event): x1 = event.x - self.offsetx y1 = event.y - self.offsety # 戻すための移動量 dx = x1 - self.coffx dy = y1 - self.coffy self.canvas.move(self.id, -dx, -dy) self.x = 0 self.y = 0 self.pos_x = 0 self.pos_y = 0 self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_drag_end != None: self.on_drag_end((self.pos_x, self.pos_y), (self.r, self.th)) def calc_pol(self, x, y): r = math.hypot(x, y) if r == 0: th = 720.0 elif y >= 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * 180.0 / math.pi elif y < 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * -180.0 / math.pi self.r = r self.th = th return (r, th) def get_pos(self): return self.pos_x, self.pos_y, self.r, self.th class TkJoystick(Frame): def __init__(self, r=10, width=300, height=300, master=None): Frame.__init__(self, master) self.pos_x = 0.0 self.pos_y = 0.0 self.pol_r = 0.0 self.pol_th = 0.0 self.stick_r = r self.width = width self.height = height self.wd = 160 self.x0 = width/2 self.y0 = height/2 self.xentry = "" self.yentry = "" self.rentry = "" self.thentry = "" # scale variable self.scale_var = DoubleVar() self.scale_var.set(1.0) # checkbutton variables self.vline_check = StringVar() self.vline_check.set("on") self.axis_check = StringVar() self.axis_check.set("on") self.circ_check = StringVar() self.circ_check.set("on") self.xy_check = StringVar() self.xy_check.set("on") self.pol_check = StringVar() self.pol_check.set("on") # x-y entry variables self.xentry = StringVar() self.xentry.set("0.0") self.yentry = StringVar() self.yentry.set("0.0") # polarpos entry variables self.rentry = StringVar() self.rentry.set("0.0") self.thentry = StringVar() self.thentry.set("0.0") # text position self.xytext_x = self.width - 50 self.xytext_y = self.height - 25 self.poltext_x = self.width - 60 self.poltext_y = self.height - 10 # vector line self.vline_color = "#aaaaaa" self.vline_width = 2 self.default_pitch = 50 # callback function self.on_update = None self.init() self.pack() def init(self): self.canvas = Canvas(self, bg="white", \ width = self.width, height = self.height) self.canvas.pack(side=LEFT) # coaxial pattern self.can_circle = CanvasCircle(self.canvas, self.x0, self.y0, self.width, self.height, pitch=50) # axis self.can_axis = CanvasAxis(self.canvas, self.width, self.height) # x-y text text = 'x: %4d, y: %4d' % (0, 0) self.can_xytext = CanvasText(self.canvas, text, self.xytext_x, self.xytext_y) # polar position text text = 'r: %4.2f, th: NaN' % (0.0) self.can_poltext = CanvasText(self.canvas, text, self.poltext_x, self.poltext_y) self.can_vline = CanvasLine(self.canvas, self.x0, self.y0, self.x0, self.y0, self.vline_color, self.vline_width) # joystick circle self.stick = Stick(self.canvas, self.width/2, self.height/2, self.stick_r, fill="#999999", width=1) self.stick.set_on_drag_start(self.on_pos_update) self.stick.set_on_dragging(self.on_pos_update) self.stick.set_on_drag_end(self.on_pos_update) # right side widgets self.frame = Frame(self, height=self.height, width=self.wd) self.frame.pack(side=LEFT, fill=Y, padx=3, pady=3) self.create_scale(self.frame).pack(side=TOP, fill=Y) self.create_checkbutton(self.frame).pack(side=TOP, fill=Y) self.create_pollabel(self.frame).pack(side=BOTTOM, fill=Y) self.create_xylabel(self.frame).pack(side=BOTTOM, fill=Y) return def create_scale(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) sl = Scale(f, from_=0, to=10, resolution=0.01, label="Scale Factor", command=self.on_scale, variable=self.scale_var, orient=HORIZONTAL) bt = Button(f, text="Reset Scale", command=self.reset_scale) sl.pack(side=TOP, fill=X) bt.pack(side=TOP, fill=X) return f def on_scale(self, val): v = float(val) if v == 0.0: pitch = 0 else: pitch = self.default_pitch/v self.can_circle.set_pitch(pitch) def create_checkbutton(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) vec = Checkbutton(f, text="Vector Line", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.vline_check, command=self.can_vline.toggle) axis = Checkbutton(f, text="Axis", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.axis_check, command=self.can_axis.toggle) circ = Checkbutton(f, text="Background", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.circ_check, command=self.can_circle.toggle) xy = Checkbutton(f, text="X-Y position", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.xy_check, command=self.can_xytext.toggle) pol = Checkbutton(f, text="Polar postion", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.pol_check, command=self.can_poltext.toggle) for w in [vec, axis, circ, xy, pol]: w.pack(side=TOP, anchor=W, fill=X) return f def create_xylabel(self, frame): f = Frame(frame) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) xl = Label(f, text="x: ", width=3) xe = Entry(f, width=7, textvariable=self.xentry, justify=RIGHT, relief=GROOVE) yl = Label(f, text="y: ", width=3) ye = Entry(f, width=7, textvariable=self.yentry, justify=RIGHT, relief=GROOVE) for w in [ye, yl, xe, xl]: w.pack(side=RIGHT, fill=X) return f def create_pollabel(self, frame): f = Frame(frame, width=self.wd) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) rl = Label(f, text="r: ", width=3) re = Entry(f, width=7, textvariable=self.rentry, justify=RIGHT, relief=GROOVE) thl = Label(f, text="th:", width=3) the = Entry(f, width=7, textvariable=self.thentry, justify=RIGHT, relief=GROOVE) for w in [the, thl, re, rl]: w.pack(side=RIGHT, fill=X) return f def reset_scale(self): self.scale_var.set(1.) pitch = self.default_pitch/1.0 self.can_circle.set_pitch(pitch) def get_pos(self): return self.pos_x, self.pos_y, self.pol_r, self.pol_th def on_pos_update(self, pos, pol): self.pos_x = pos[0] * self.scale_var.get() self.pos_y = pos[1] * self.scale_var.get() self.pol_r = pol[0] * self.scale_var.get() self.pol_th = pol[1] xt = '%4d' % (self.pos_x) yt = '%4d' % (self.pos_y) rt = '%5.2f' % (self.pol_r) if pol[1] > 360: tht = 'NaN' else: tht = '%5.2f' % self.pol_th self.xentry.set(xt) self.yentry.set(yt) self.rentry.set(rt) self.thentry.set(tht) text = 'x: %4d, y: %4d' % (pos[0], pos[1]) self.can_xytext.draw_text(self.xytext_x, self.xytext_y, text) if pol[1] > 360: text = 'r: %5.2f, th: NaN' % (pol[0]) else: text = 'r: %5.2f, th: %5.2f' % (pol[0], pol[1]) self.can_poltext.draw_text(self.poltext_x, self.poltext_y, text) self.can_vline.draw_line(self.x0, self.y0, self.x0 + pos[0], self.y0 - pos[1]) if self.on_update != None: self.on_update((self.pos_x, self.pos_y), (self.pol_r, self.pol_th)) def set_on_update(self, func): self.on_update = func def test (): m = TkJoystick() while 1: m.update() print(m.get_pos()) if __name__ == '__main__': test()
206  self.make_binds()
207  # テキスト描画 def set_on_drag_start(self, func): self.on_drag_start = func def set_on_dragging(self, func): self.on_dragging = func def set_on_drag_end(self, func): self.on_drag_end = func def make_binds(self): self.canvas.tag_bind(self.id, '<1>', self.drag_start) self.canvas.tag_bind(self.id, '<Button1-Motion>', self.dragging) self.canvas.tag_bind(self.id, '<Button1-ButtonRelease>', self.drag_end) def drag_start(self, event): # クリック位置のオフセットを計算 x1 = event.x - self.offsetx y1 = event.y - self.offsety self.coffx = x1 - self.x self.coffy = y1 - self.y self.calc_pol(self.pos_x, self.pos_y) # self.draw_text() # コールバック if self.on_drag_start != None: self.on_drag_start((self.pos_x, self.pos_y), (self.r, self.th)) def dragging(self, event): # ドラッグの移動量 x1 = event.x - self.offsetx y1 = event.y - self.offsety dx = (x1 - self.x) - self.coffx dy = (y1 - self.y) - self.coffy # 円を移動 self.canvas.move(self.id, dx, dy) self.canvas.tag_raise(self.id) # ジョイスティック位置を計算 self.x = x1 - self.coffx self.y = y1 - self.coffy self.pos_x = self.x self.pos_y = -self.y self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_dragging != None: self.on_dragging((self.pos_x, self.pos_y), (self.r, self.th)) def drag_end(self, event): x1 = event.x - self.offsetx y1 = event.y - self.offsety # 戻すための移動量 dx = x1 - self.coffx dy = y1 - self.coffy self.canvas.move(self.id, -dx, -dy) self.x = 0 self.y = 0 self.pos_x = 0 self.pos_y = 0 self.calc_pol(self.pos_x, self.pos_y) # コールバック if self.on_drag_end != None: self.on_drag_end((self.pos_x, self.pos_y), (self.r, self.th)) def calc_pol(self, x, y): r = math.hypot(x, y) if r == 0: th = 720.0 elif y >= 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * 180.0 / math.pi elif y < 0.0: th = math.acos((x * 1.0) / (r * 1.0)) * -180.0 / math.pi self.r = r self.th = th return (r, th) def get_pos(self): return self.pos_x, self.pos_y, self.r, self.th class TkJoystick(Frame): def __init__(self, r=10, width=300, height=300, master=None): Frame.__init__(self, master) self.pos_x = 0.0 self.pos_y = 0.0 self.pol_r = 0.0 self.pol_th = 0.0 self.stick_r = r self.width = width self.height = height self.wd = 160 self.x0 = width/2 self.y0 = height/2 self.xentry = "" self.yentry = "" self.rentry = "" self.thentry = "" # scale variable self.scale_var = DoubleVar() self.scale_var.set(1.0) # checkbutton variables self.vline_check = StringVar() self.vline_check.set("on") self.axis_check = StringVar() self.axis_check.set("on") self.circ_check = StringVar() self.circ_check.set("on") self.xy_check = StringVar() self.xy_check.set("on") self.pol_check = StringVar() self.pol_check.set("on") # x-y entry variables self.xentry = StringVar() self.xentry.set("0.0") self.yentry = StringVar() self.yentry.set("0.0") # polarpos entry variables self.rentry = StringVar() self.rentry.set("0.0") self.thentry = StringVar() self.thentry.set("0.0") # text position self.xytext_x = self.width - 50 self.xytext_y = self.height - 25 self.poltext_x = self.width - 60 self.poltext_y = self.height - 10 # vector line self.vline_color = "#aaaaaa" self.vline_width = 2 self.default_pitch = 50 # callback function self.on_update = None self.init() self.pack() def init(self): self.canvas = Canvas(self, bg="white", \ width = self.width, height = self.height) self.canvas.pack(side=LEFT) # coaxial pattern self.can_circle = CanvasCircle(self.canvas, self.x0, self.y0, self.width, self.height, pitch=50) # axis self.can_axis = CanvasAxis(self.canvas, self.width, self.height) # x-y text text = 'x: %4d, y: %4d' % (0, 0) self.can_xytext = CanvasText(self.canvas, text, self.xytext_x, self.xytext_y) # polar position text text = 'r: %4.2f, th: NaN' % (0.0) self.can_poltext = CanvasText(self.canvas, text, self.poltext_x, self.poltext_y) self.can_vline = CanvasLine(self.canvas, self.x0, self.y0, self.x0, self.y0, self.vline_color, self.vline_width) # joystick circle self.stick = Stick(self.canvas, self.width/2, self.height/2, self.stick_r, fill="#999999", width=1) self.stick.set_on_drag_start(self.on_pos_update) self.stick.set_on_dragging(self.on_pos_update) self.stick.set_on_drag_end(self.on_pos_update) # right side widgets self.frame = Frame(self, height=self.height, width=self.wd) self.frame.pack(side=LEFT, fill=Y, padx=3, pady=3) self.create_scale(self.frame).pack(side=TOP, fill=Y) self.create_checkbutton(self.frame).pack(side=TOP, fill=Y) self.create_pollabel(self.frame).pack(side=BOTTOM, fill=Y) self.create_xylabel(self.frame).pack(side=BOTTOM, fill=Y) return def create_scale(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) sl = Scale(f, from_=0, to=10, resolution=0.01, label="Scale Factor", command=self.on_scale, variable=self.scale_var, orient=HORIZONTAL) bt = Button(f, text="Reset Scale", command=self.reset_scale) sl.pack(side=TOP, fill=X) bt.pack(side=TOP, fill=X) return f def on_scale(self, val): v = float(val) if v == 0.0: pitch = 0 else: pitch = self.default_pitch/v self.can_circle.set_pitch(pitch) def create_checkbutton(self, frame): f = Frame(frame, bd=2, relief=GROOVE) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) vec = Checkbutton(f, text="Vector Line", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.vline_check, command=self.can_vline.toggle) axis = Checkbutton(f, text="Axis", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.axis_check, command=self.can_axis.toggle) circ = Checkbutton(f, text="Background", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.circ_check, command=self.can_circle.toggle) xy = Checkbutton(f, text="X-Y position", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.xy_check, command=self.can_xytext.toggle) pol = Checkbutton(f, text="Polar postion", onvalue="on", offvalue="off", justify=LEFT, anchor=W, variable=self.pol_check, command=self.can_poltext.toggle) for w in [vec, axis, circ, xy, pol]: w.pack(side=TOP, anchor=W, fill=X) return f def create_xylabel(self, frame): f = Frame(frame) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) xl = Label(f, text="x: ", width=3) xe = Entry(f, width=7, textvariable=self.xentry, justify=RIGHT, relief=GROOVE) yl = Label(f, text="y: ", width=3) ye = Entry(f, width=7, textvariable=self.yentry, justify=RIGHT, relief=GROOVE) for w in [ye, yl, xe, xl]: w.pack(side=RIGHT, fill=X) return f def create_pollabel(self, frame): f = Frame(frame, width=self.wd) dummy = Frame(f, width=self.wd) dummy.pack(side=TOP) rl = Label(f, text="r: ", width=3) re = Entry(f, width=7, textvariable=self.rentry, justify=RIGHT, relief=GROOVE) thl = Label(f, text="th:", width=3) the = Entry(f, width=7, textvariable=self.thentry, justify=RIGHT, relief=GROOVE) for w in [the, thl, re, rl]: w.pack(side=RIGHT, fill=X) return f def reset_scale(self): self.scale_var.set(1.) pitch = self.default_pitch/1.0 self.can_circle.set_pitch(pitch) def get_pos(self): return self.pos_x, self.pos_y, self.pol_r, self.pol_th def on_pos_update(self, pos, pol): self.pos_x = pos[0] * self.scale_var.get() self.pos_y = pos[1] * self.scale_var.get() self.pol_r = pol[0] * self.scale_var.get() self.pol_th = pol[1] xt = '%4d' % (self.pos_x) yt = '%4d' % (self.pos_y) rt = '%5.2f' % (self.pol_r) if pol[1] > 360: tht = 'NaN' else: tht = '%5.2f' % self.pol_th self.xentry.set(xt) self.yentry.set(yt) self.rentry.set(rt) self.thentry.set(tht) text = 'x: %4d, y: %4d' % (pos[0], pos[1]) self.can_xytext.draw_text(self.xytext_x, self.xytext_y, text) if pol[1] > 360: text = 'r: %5.2f, th: NaN' % (pol[0]) else: text = 'r: %5.2f, th: %5.2f' % (pol[0], pol[1]) self.can_poltext.draw_text(self.poltext_x, self.poltext_y, text) self.can_vline.draw_line(self.x0, self.y0, self.x0 + pos[0], self.y0 - pos[1]) if self.on_update != None: self.on_update((self.pos_x, self.pos_y), (self.pol_r, self.pol_th)) def set_on_update(self, func): self.on_update = func def test (): m = TkJoystick() while 1: m.update() print(m.get_pos()) if __name__ == '__main__': test()
208 
209  def set_on_drag_start(self, func):
210  self.on_drag_start = func
211 
212  def set_on_dragging(self, func):
213  self.on_dragging = func
214 
215  def set_on_drag_end(self, func):
216  self.on_drag_end = func
217 
218  def make_binds(self):
219  self.canvas.tag_bind(self.id, '<1>', self.drag_start)
220  self.canvas.tag_bind(self.id, '<Button1-Motion>', self.dragging)
221  self.canvas.tag_bind(self.id, '<Button1-ButtonRelease>', self.drag_end)
222 
223  def drag_start(self, event):
224  # クリック位置のオフセットを計算
225  x1 = event.x - self.offsetx
226  y1 = event.y - self.offsety
227  self.coffx = x1 - self.x
228  self.coffy = y1 - self.y
229 
230  self.calc_pol(self.pos_x, self.pos_y)
231 # self.draw_text()
232 
233  # コールバック
234  if self.on_drag_start != None:
235  self.on_drag_start((self.pos_x, self.pos_y), (self.r, self.th))
236 
237  def dragging(self, event):
238  # ドラッグの移動量
239  x1 = event.x - self.offsetx
240  y1 = event.y - self.offsety
241  dx = (x1 - self.x) - self.coffx
242  dy = (y1 - self.y) - self.coffy
243 
244  # 円を移動
245  self.canvas.move(self.id, dx, dy)
246  self.canvas.tag_raise(self.id)
247  # ジョイスティック位置を計算
248  self.x = x1 - self.coffx
249  self.y = y1 - self.coffy
250  self.pos_x = self.x
251  self.pos_y = -self.y
252  self.calc_pol(self.pos_x, self.pos_y)
253 
254  # コールバック
255  if self.on_dragging != None:
256  self.on_dragging((self.pos_x, self.pos_y), (self.r, self.th))
257 
258  def drag_end(self, event):
259  x1 = event.x - self.offsetx
260  y1 = event.y - self.offsety
261  # 戻すための移動量
262  dx = x1 - self.coffx
263  dy = y1 - self.coffy
264  self.canvas.move(self.id, -dx, -dy)
265  self.x = 0
266  self.y = 0
267  self.pos_x = 0
268  self.pos_y = 0
269  self.calc_pol(self.pos_x, self.pos_y)
270  # コールバック
271  if self.on_drag_end != None:
272  self.on_drag_end((self.pos_x, self.pos_y), (self.r, self.th))
273 
274  def calc_pol(self, x, y):
275  r = math.hypot(x, y)
276  if r == 0:
277  th = 720.0
278  elif y >= 0.0:
279  th = math.acos((x * 1.0) / (r * 1.0)) * 180.0 / math.pi
280  elif y < 0.0:
281  th = math.acos((x * 1.0) / (r * 1.0)) * -180.0 / math.pi
282  self.r = r
283  self.th = th
284  return (r, th)
285 
286  def get_pos(self):
287  return self.pos_x, self.pos_y, self.r, self.th
288 
289 
290 class TkJoystick(Frame):
291  def __init__(self, r=10, width=300, height=300, master=None):
292  Frame.__init__(self, master)
293 
294  self.pos_x = 0.0
295  self.pos_y = 0.0
296  self.pol_r = 0.0
297  self.pol_th = 0.0
298 
299  self.stick_r = r
300 
301  self.width = width
302  self.height = height
303 
304  self.wd = 160
305 
306  self.x0 = width/2
307  self.y0 = height/2
308 
309  self.xentry = ""
310  self.yentry = ""
311  self.rentry = ""
312  self.thentry = ""
313 
314  # scale variable
315  self.scale_var = DoubleVar()
316  self.scale_var.set(1.0)
317 
318  # checkbutton variables
319  self.vline_check = StringVar()
320  self.vline_check.set("on")
321  self.axis_check = StringVar()
322  self.axis_check.set("on")
323  self.circ_check = StringVar()
324  self.circ_check.set("on")
325  self.xy_check = StringVar()
326  self.xy_check.set("on")
327  self.pol_check = StringVar()
328  self.pol_check.set("on")
329 
330  # x-y entry variables
331  self.xentry = StringVar()
332  self.xentry.set("0.0")
333  self.yentry = StringVar()
334  self.yentry.set("0.0")
335 
336  # polarpos entry variables
337  self.rentry = StringVar()
338  self.rentry.set("0.0")
339  self.thentry = StringVar()
340  self.thentry.set("0.0")
341 
342  # text position
343  self.xytext_x = self.width - 50
344  self.xytext_y = self.height - 25
345  self.poltext_x = self.width - 60
346  self.poltext_y = self.height - 10
347 
348  # vector line
349  self.vline_color = "#aaaaaa"
350  self.vline_width = 2
351  self.default_pitch = 50
352 
353  # callback function
354  self.on_update = None
355 
356  self.init()
357  self.pack()
358 
359 
360  def init(self):
361  self.canvas = Canvas(self, bg="white", \
362  width = self.width, height = self.height)
363  self.canvas.pack(side=LEFT)
364 
365  # coaxial pattern
367  self.x0, self.y0,
368  self.width, self.height, pitch=50)
369 
370  # axis
371  self.can_axis = CanvasAxis(self.canvas, self.width, self.height)
372 
373  # x-y text
374  text = 'x: %4d, y: %4d' % (0, 0)
375  self.can_xytext = CanvasText(self.canvas, text,
376  self.xytext_x, self.xytext_y)
377 
378  # polar position text
379  text = 'r: %4.2f, th: NaN' % (0.0)
380  self.can_poltext = CanvasText(self.canvas, text,
381  self.poltext_x, self.poltext_y)
382 
383  self.can_vline = CanvasLine(self.canvas, self.x0, self.y0,
384  self.x0, self.y0,
385  self.vline_color, self.vline_width)
386 
387  # joystick circle
388  self.stick = Stick(self.canvas, self.width/2, self.height/2,
389  self.stick_r,
390  fill="#999999", width=1)
391  self.stick.set_on_drag_start(self.on_pos_update)
392  self.stick.set_on_dragging(self.on_pos_update)
393  self.stick.set_on_drag_end(self.on_pos_update)
394 
395 
396  # right side widgets
397  self.frame = Frame(self, height=self.height, width=self.wd)
398  self.frame.pack(side=LEFT, fill=Y, padx=3, pady=3)
399  self.create_scale(self.frame).pack(side=TOP, fill=Y)
400  self.create_checkbutton(self.frame).pack(side=TOP, fill=Y)
401  self.create_pollabel(self.frame).pack(side=BOTTOM, fill=Y)
402  self.create_xylabel(self.frame).pack(side=BOTTOM, fill=Y)
403  return
404 
405  def create_scale(self, frame):
406  f = Frame(frame, bd=2, relief=GROOVE)
407  dummy = Frame(f, width=self.wd)
408  dummy.pack(side=TOP)
409  sl = Scale(f, from_=0, to=10, resolution=0.01,
410  label="Scale Factor", command=self.on_scale,
411  variable=self.scale_var, orient=HORIZONTAL)
412  bt = Button(f, text="Reset Scale", command=self.reset_scale)
413  sl.pack(side=TOP, fill=X)
414  bt.pack(side=TOP, fill=X)
415  return f
416 
417  def on_scale(self, val):
418  v = float(val)
419  if v == 0.0:
420  pitch = 0
421  else:
422  pitch = self.default_pitch/v
423  self.can_circle.set_pitch(pitch)
424 
425  def create_checkbutton(self, frame):
426  f = Frame(frame, bd=2, relief=GROOVE)
427  dummy = Frame(f, width=self.wd)
428  dummy.pack(side=TOP)
429  vec = Checkbutton(f, text="Vector Line",
430  onvalue="on", offvalue="off",
431  justify=LEFT, anchor=W,
432  variable=self.vline_check,
433  command=self.can_vline.toggle)
434  axis = Checkbutton(f, text="Axis",
435  onvalue="on", offvalue="off",
436  justify=LEFT, anchor=W,
437  variable=self.axis_check,
438  command=self.can_axis.toggle)
439  circ = Checkbutton(f, text="Background",
440  onvalue="on", offvalue="off",
441  justify=LEFT, anchor=W,
442  variable=self.circ_check,
443  command=self.can_circle.toggle)
444  xy = Checkbutton(f, text="X-Y position",
445  onvalue="on", offvalue="off",
446  justify=LEFT, anchor=W,
447  variable=self.xy_check,
448  command=self.can_xytext.toggle)
449  pol = Checkbutton(f, text="Polar postion",
450  onvalue="on", offvalue="off",
451  justify=LEFT, anchor=W,
452  variable=self.pol_check,
453  command=self.can_poltext.toggle)
454  for w in [vec, axis, circ, xy, pol]:
455  w.pack(side=TOP, anchor=W, fill=X)
456  return f
457 
458  def create_xylabel(self, frame):
459  f = Frame(frame)
460  dummy = Frame(f, width=self.wd)
461  dummy.pack(side=TOP)
462  xl = Label(f, text="x: ", width=3)
463  xe = Entry(f, width=7, textvariable=self.xentry,
464  justify=RIGHT, relief=GROOVE)
465  yl = Label(f, text="y: ", width=3)
466  ye = Entry(f, width=7, textvariable=self.yentry,
467  justify=RIGHT, relief=GROOVE)
468 
469  for w in [ye, yl, xe, xl]:
470  w.pack(side=RIGHT, fill=X)
471  return f
472 
473  def create_pollabel(self, frame):
474  f = Frame(frame, width=self.wd)
475  dummy = Frame(f, width=self.wd)
476  dummy.pack(side=TOP)
477  rl = Label(f, text="r: ", width=3)
478  re = Entry(f, width=7, textvariable=self.rentry,
479  justify=RIGHT, relief=GROOVE)
480  thl = Label(f, text="th:", width=3)
481  the = Entry(f, width=7, textvariable=self.thentry,
482  justify=RIGHT, relief=GROOVE)
483  for w in [the, thl, re, rl]:
484  w.pack(side=RIGHT, fill=X)
485  return f
486 
487  def reset_scale(self):
488  self.scale_var.set(1.)
489  pitch = self.default_pitch/1.0
490  self.can_circle.set_pitch(pitch)
491 
492  def get_pos(self):
493  return self.pos_x, self.pos_y, self.pol_r, self.pol_th
494 
495  def on_pos_update(self, pos, pol):
496  self.pos_x = pos[0] * self.scale_var.get()
497  self.pos_y = pos[1] * self.scale_var.get()
498  self.pol_r = pol[0] * self.scale_var.get()
499  self.pol_th = pol[1]
500 
501  xt = '%4d' % (self.pos_x)
502  yt = '%4d' % (self.pos_y)
503  rt = '%5.2f' % (self.pol_r)
504  if pol[1] > 360: tht = 'NaN'
505  else: tht = '%5.2f' % self.pol_th
506 
507  self.xentry.set(xt)
508  self.yentry.set(yt)
509  self.rentry.set(rt)
510  self.thentry.set(tht)
511 
512  text = 'x: %4d, y: %4d' % (pos[0], pos[1])
513  self.can_xytext.draw_text(self.xytext_x, self.xytext_y, text)
514  if pol[1] > 360:
515  text = 'r: %5.2f, th: NaN' % (pol[0])
516  else:
517  text = 'r: %5.2f, th: %5.2f' % (pol[0], pol[1])
518  self.can_poltext.draw_text(self.poltext_x, self.poltext_y, text)
519 
520  self.can_vline.draw_line(self.x0, self.y0,
521  self.x0 + pos[0],
522  self.y0 - pos[1])
523 
524  if self.on_update != None:
525  self.on_update((self.pos_x, self.pos_y),
526  (self.pol_r, self.pol_th))
527 
528  def set_on_update(self, func):
529  self.on_update = func
530 
531 def test ():
532  m = TkJoystick()
533 
534  while 1:
535  m.update()
536  print(m.get_pos())
537 
538 
539 if __name__ == '__main__': test()
def on_pos_update(self, pos, pol)
Definition: tkjoystick.py:495
def __init__(self, canvas, text, x, y)
Definition: tkjoystick.py:49
def set_on_drag_end(self, func)
Definition: tkjoystick.py:215
def create_xylabel(self, frame)
Definition: tkjoystick.py:458
def __init__(self, canvas, x, y, width, height, pitch)
Definition: tkjoystick.py:103
Definition: test.py:1
def set_on_drag_start(self, func)
Definition: tkjoystick.py:209
def drag_start(self, event)
Definition: tkjoystick.py:223
def create_checkbutton(self, frame)
Definition: tkjoystick.py:425
def dragging(self, event)
Definition: tkjoystick.py:237
def draw_line(self, x0, y0, x1, y1)
Definition: tkjoystick.py:167
def drag_end(self, event)
Definition: tkjoystick.py:258
def __init__(self, canvas, x0, y0, x1, y1, color, width)
Definition: tkjoystick.py:149
def __init__(self, r=10, width=300, height=300, master=None)
Definition: tkjoystick.py:291
def create_pollabel(self, frame)
Definition: tkjoystick.py:473
typedef int
Definition: png.h:1113
def draw_text(self, x, y, text)
Definition: tkjoystick.py:63
def __init__(self, canvas, width, height)
Definition: tkjoystick.py:74
def __init__(self, canvas, x, y, r, key)
Definition: tkjoystick.py:178
def set_on_dragging(self, func)
Definition: tkjoystick.py:212
static int max(int a, int b)
def calc_pol(self, x, y)
Definition: tkjoystick.py:274


openhrp3
Author(s): AIST, General Robotix Inc., Nakamura Lab of Dept. of Mechano Informatics at University of Tokyo
autogenerated on Thu Sep 8 2022 02:24:05