- v1.3 : Realized that I didn’t need to track mouse position and button-presses in the Tablet class: Pygame does all this for you. Solves a lot of problems. Tablet object now only returns back pressure info, which is all we really want from it anyway.
In Pygame, tablets (or at least my Wacom Bamboo) acts just like a mouse. But it appears that Pygame, and the underlying SDL don’t detect tablet pressure. I’ve read that starting with SDL 1.3/2.0 it will detect for tablet pressure, but until then (currently v1.2)… what options are there? It doesn’t seem that Python itself has any built-in modules for this either (that I can find…).
After asking the user groups, I tracked down an interface into wintab api through the “Python Computer Graphics Kit” (cgkit). The API was developed by LCS/Telegraphics and maintained by Wacom: cgkit has a wintab wrapper (docs) that appears to hook into Wacom standards.
But even with their docs, it took me a bit of troubleshooting to turn it into something usable, but I have: ‘tablet.py’ which contains a ‘Tablet’ class, and a ‘pressureTest.py’ Pygame app showing it off:
- cgkit download. Required to run tablet.py
- tablet_1_3.py – Module containing the Tablet class.
- pressureTest_0_6.py – Simple Pygame example using the Tablet class.
- Built with Python 2.6.2, Pygame 1.9.1, cgkit 2.0.0 alpha9
tablet.py is a module containing a Tablet class, that will give you tablet querying functionality in your Pygame programs. In a nutshell, you create a Tablet object, then inside the main loop you query your Tablet.getPressure() method to query the tablet ‘pressure’ as an int, mapped from 0-1023. You do this as a compliment to the normal Pygame mouse queries for position and button-press.
Here’s the few lines of code you’d use to put it in a Pygame app (illustrated in pressureTest.py):
from tablet import Tablet tablet = Tablet() # Main loop: while looping: pressure = tablet.getPressure()
This is the general implementation behind capturing the tablet data (crack open tablet_1_3.py to see what’s going on). I should point out this was all by trial and error. There could be better\more efficient\more eloquent ways of doing this. But, this works
- The wintab module needs to know the ‘window handle’ \ ‘window id’ for the current Pygame app. The Tablet object finds the window id based on the current Pygame window:
- self.hwnd = pygame.display.get_wm_info()["window"]
- Create a new wintab Context object that will capture our tablet data:
- self.context = wintab.Context()
- Define what type of data our context will return via Packet objects. These are based on wintab constants:
- self.context.pktdata = ( PK_NORMAL_PRESSURE )
- It should be noted that the default values for pkdata (if you don’t define your own) are: xpos, ypos, buttons.
- Open out context:
- self.context.open(self.hwnd, True)
- Then later we can query our Tablet object once per Pygame loop for Packet object data. A LOT of packet data can be returned. So we query the “extent” of the packets that were returned:
- packetExtents = self.context.queuePacketsEx()
- If the pen is away from the tablet, it will return None, so in that case, we don’t eval anything.
- Presuming we found packet data, we the last packet list from the sub-list of packets that were returned:
- packets = self.context.packetsGet(packetExtents)
- And, we isolate the last packet in that sublist: (did I mention there were a lot of packets being returned?)
- packet = packets[-1]
- Based on that Packet object, we can now query it for the all important pressure data, via its ‘packet.normalpressure’ attribute.
The key takeaway is setting the Context.pktdata attribute: This is the step that controls what is returend via the packets. Depending on your tablet, a lot more info can be returned.