''' Touch Tracer Line Drawing Demonstration ======================================= This demonstrates tracking each touch registered to a device. You should see a basic background image. When you press and hold the mouse, you should see cross-hairs with the coordinates written next to them. As you drag, it leaves a trail. Additional information, like pressure, will be shown if they are in your device's touch.profile. .. note:: A function `calculate_points` handling the points which will be drawn has by default implemented a delay of 5 steps. To get more precise visual results lower the value of the optional keyword argument `steps`. This program specifies an icon, the file icon.png, in its App subclass. It also uses the particle.png file as the source for drawing the trails which are white on transparent. The file touchtracer.kv describes the application. The file android.txt is used to package the application for use with the Kivy Launcher Android application. For Android devices, you can copy/paste this directory into /sdcard/kivy/touchtracer on your Android device. ''' __version__ = '1.0' import kivy kivy.require('1.0.6') from kivy.app import App from kivy.uix.floatlayout import FloatLayout from kivy.uix.label import Label from kivy.graphics import Color, Rectangle, Point, GraphicException from kivy.metrics import dp from random import random from math import sqrt def calculate_points(x1, y1, x2, y2, steps=5): dx = x2 - x1 dy = y2 - y1 dist = sqrt(dx * dx + dy * dy) if dist < steps: return o = [] m = dist / steps for i in range(1, int(m)): mi = i / m lastx = x1 + dx * mi lasty = y1 + dy * mi o.extend([lastx, lasty]) return o class Touchtracer(FloatLayout): def normalize_pressure(self, pressure): print(pressure) # this might mean we are on a device whose pressure value is # incorrectly reported by SDL2, like recent iOS devices. if pressure == 0.0: return 1 return dp(pressure * 10) def on_touch_down(self, touch): win = self.get_parent_window() ud = touch.ud ud['group'] = g = str(touch.uid) pointsize = 5 print(touch.profile) if 'pressure' in touch.profile: ud['pressure'] = touch.pressure pointsize = self.normalize_pressure(touch.pressure) ud['color'] = random() with self.canvas: Color(ud['color'], 1, 1, mode='hsv', group=g) ud['lines'] = [ Rectangle(pos=(touch.x, 0), size=(1, win.height), group=g), Rectangle(pos=(0, touch.y), size=(win.width, 1), group=g), Point(points=(touch.x, touch.y), source='particle.png', pointsize=pointsize, group=g)] ud['label'] = Label(size_hint=(None, None)) self.update_touch_label(ud['label'], touch) self.add_widget(ud['label']) touch.grab(self) return True def on_touch_move(self, touch): if touch.grab_current is not self: return ud = touch.ud ud['lines'][0].pos = touch.x, 0 ud['lines'][1].pos = 0, touch.y index = -1 while True: try: points = ud['lines'][index].points oldx, oldy = points[-2], points[-1] break except IndexError: index -= 1 points = calculate_points(oldx, oldy, touch.x, touch.y) # if pressure changed create a new point instruction if 'pressure' in ud: old_pressure = ud['pressure'] if ( not old_pressure or not .99 < (touch.pressure / old_pressure) < 1.01 ): g = ud['group'] pointsize = self.normalize_pressure(touch.pressure) with self.canvas: Color(ud['color'], 1, 1, mode='hsv', group=g) ud['lines'].append( Point(points=(), source='particle.png', pointsize=pointsize, group=g)) if points: try: lp = ud['lines'][-1].add_point for idx in range(0, len(points), 2): lp(points[idx], points[idx + 1]) except GraphicException: pass ud['label'].pos = touch.pos import time t = int(time.time()) if t not in ud: ud[t] = 1 else: ud[t] += 1 self.update_touch_label(ud['label'], touch) def on_touch_up(self, touch): if touch.grab_current is not self: return touch.ungrab(self) ud = touch.ud self.canvas.remove_group(ud['group']) self.remove_widget(ud['label']) def update_touch_label(self, label, touch): label.text = 'ID: %s\nPos: (%d, %d)\nClass: %s' % ( touch.id, touch.x, touch.y, touch.__class__.__name__) label.texture_update() label.pos = touch.pos label.size = label.texture_size[0] + 20, label.texture_size[1] + 20 class TouchtracerApp(App): title = 'Touchtracer' icon = 'icon.png' def build(self): return Touchtracer() def on_pause(self): return True if __name__ == '__main__': TouchtracerApp().run()