--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>MINWii</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.python.pydev.PyDevBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.python.pydev.pythonNature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>\r
+<?eclipse-pydev version="1.0"?>\r
+\r
+<pydev_project>\r
+<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">\r
+<path>/MINWiiV7/src</path>\r
+</pydev_pathproperty>\r
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property>\r
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>\r
+</pydev_project>\r
--- /dev/null
+#Mon Dec 07 03:31:40 CET 2009\r
+eclipse.preferences.version=1\r
+encoding//src/mxmMidi/DataTypeConverters.py=ISO-8859-1\r
+encoding//src/mxmMidi/EventDispatcher.py=ISO-8859-1\r
+encoding//src/mxmMidi/MidiFileParser.py=ISO-8859-1\r
+encoding//src/mxmMidi/MidiInFile.py=ISO-8859-1\r
+encoding//src/mxmMidi/MidiInStream.py=ISO-8859-1\r
+encoding//src/mxmMidi/MidiOutFile.py=ISO-8859-1\r
+encoding//src/mxmMidi/MidiOutStream.py=ISO-8859-1\r
+encoding//src/mxmMidi/MidiToText.py=ISO-8859-1\r
+encoding//src/mxmMidi/RawInstreamFile.py=ISO-8859-1\r
+encoding//src/mxmMidi/RawOutstreamFile.py=ISO-8859-1\r
+encoding//src/mxmMidi/__init__.py=ISO-8859-1\r
+encoding//src/mxmMidi/constants.py=ISO-8859-1\r
--- /dev/null
+Wiimote1.led1 = true\r
+Wiimote2.led1 = true\r
+Wiimote2.led4 = true\r
+Wiimote3.led2 = true\r
+Wiimote4.led2 = true\r
+Wiimote4.led4 = true\r
+Wiimote5.led3 = true\r
+Wiimote6.led3 = true\r
+Wiimote6.led4 = true\r
+//Mouse Control Script using IR\r
+//by vkapadia with much assistance from inio\r
+//vkapadia@vkapadia.com\r
+//\r
+//Calibration:\r
+//To calibrate, run this program and put the Wiimote on a flat surface face-up.\r
+//Then read the values in the debug line (next to the run button).\r
+//Change these values until the debug line reads approx. all zeros.\r
+var.xtrim = -7\r
+var.ytrim = -17\r
+var.ztrim = -7\r
+//\r
+//Options:\r
+var.deadzone = 5 //distance in pixels that you have to move the wiimote in\r
+ //order for it to register movement. Creates a "dead zone" around the pointer\r
+ //to make it easier to click. Higher = smoother but less accurate.\r
+//fake cursor init\r
+cursor2.visible = true\r
+//more options to be added later\r
+\r
+//Controls:\r
+//Point Wiimote = Move Mouse\r
+//D-Pad = Arrow Keys\r
+//B-Button = Left Click\r
+//Home = Middle Click\r
+//A-Button = Right Click\r
+//Plus and Minus = Control Volume\r
+//One = Unmapped\r
+//Two = Unmapped\r
+//\r
+//If the pointer hits the edge of the screen, the Wiimote will rumble a bit.\r
+//\r
+//The LEDs attempt to emulate KITT's grill from Knight Rider\r
+\r
+//***Do not edit anything below this line unless you know what you are doing.***\r
+var.accx = wiimote.RawForceX + var.xtrim\r
+var.accy = wiimote.RawForceY + var.ytrim\r
+var.accz = wiimote.RawForceZ + var.ztrim\r
+\r
+if wiimote.dot1vis and wiimote.dot2vis then\r
+\r
+ if var.accy > -7 then\r
+ var.orientation = 0\r
+ elseif var.accy > -45 then\r
+ if var.accx < 0 then\r
+ var.orientation = 3\r
+ else\r
+ var.orientation = 1\r
+ endif\r
+ else\r
+ var.orientation = 2\r
+ endif\r
+\r
+ if var.leftpoint = 0 then\r
+ if var.orientation = 0 then\r
+ if wiimote.dot1x < wiimote.dot2x then\r
+ var.leftpoint = 1\r
+ else\r
+ var.leftpoint = 2\r
+ endif\r
+ endif\r
+ if var.orientation = 1 then\r
+ if wiimote.dot1y > wiimote.dot2y then\r
+ var.leftpoint = 1\r
+ else\r
+ var.leftpoint = 2\r
+ endif\r
+ endif\r
+ if var.orientation = 2 then\r
+ if wiimote.dot1x > wiimote.dot2x then\r
+ var.leftpoint = 1\r
+ else\r
+ var.leftpoint = 2\r
+ endif\r
+ endif\r
+ if var.orientation = 3 then\r
+ if wiimote.dot1y < wiimote.dot2y then\r
+ var.leftpoint = 1\r
+ else\r
+ var.leftpoint = 2\r
+ endif\r
+ endif\r
+ endif\r
+\r
+ if var.leftpoint = 1 then\r
+ var.fix1x = wiimote.dot1x\r
+ var.fix1y = wiimote.dot1y\r
+ var.fix2x = wiimote.dot2x\r
+ var.fix2y = wiimote.dot2y\r
+ else\r
+ var.fix1x = wiimote.dot2x\r
+ var.fix1y = wiimote.dot2y\r
+ var.fix2x = wiimote.dot1x\r
+ var.fix2y = wiimote.dot1y\r
+ endif\r
+\r
+ var.dx = var.fix2x - var.fix1x\r
+ var.dy = var.fix2y - var.fix1y\r
+ var.cx = (var.fix1x+var.fix2x)/1024.0 - 1\r
+ var.cy = (var.fix1y+var.fix2y)/1024.0 - .75\r
+\r
+ var.d = sqrt(var.dx*var.dx+var.dy*var.dy)\r
+\r
+ var.dx = var.dx / var.d\r
+ var.dy = var.dy / var.d\r
+\r
+ var.ox = -var.dy*var.cy-var.dx*var.cx;\r
+ var.oy = -var.dx*var.cy+var.dy*var.cx;\r
+\r
+ var.ax = (var.ox * screen.desktopwidth) + (screen.desktopwidth / 2)\r
+ var.ay = (-var.oy * screen.desktopwidth) + (screen.desktopheight / 2)\r
+\r
+ var.dx = var.ax - cursor2.posx\r
+ var.dy = var.ay - cursor2.posy\r
+\r
+ var.d = sqrt((var.dx*var.dx)+(var.dy*var.dy))\r
+\r
+ var.a = 180 / (200 + var.d * var.d * var.d * .001)\r
+\r
+ if var.d <= var.deadzone then var.a = 1\r
+\r
+ //debug = var.d + " " + var.a\r
+\r
+ var.finalx = cursor2.posx * var.a + var.ax * (1 - var.a)\r
+ var.finaly = cursor2.posy * var.a + var.ay * (1 - var.a)\r
+\r
+\r
+ cursor2.posx = smooth(var.finalx,3,5)\r
+ cursor2.posy = smooth(var.finaly,3,5)\r
+\r
+else\r
+\r
+ var.leftpoint = 0\r
+\r
+endif\r
+\r
+var.xpos = var.finalx\r
+var.ypos = var.finaly\r
+ppjoy1.analog0 = ensuremaprange(var.xpos,0,screen.desktopwidth,-1,1)\r
+ppjoy1.analog1 = ensuremaprange(var.ypos,0,screen.desktopheight,-1,1)\r
+\r
+if wiimote1.B == true\r
+ ppjoy1.digital0 = true\r
+else\r
+ ppjoy1.digital0 = false\r
+endif\r
+\r
+debug = var.xpos+ " " + var.ypos\r
--- /dev/null
+Wiimote1.led1 = true\r
+Wiimote2.led1 = true\r
+Wiimote2.led4 = true\r
+Wiimote3.led2 = true\r
+Wiimote4.led2 = true\r
+Wiimote4.led4 = true\r
+Wiimote5.led3 = true\r
+Wiimote6.led3 = true\r
+Wiimote6.led4 = true\r
+//Mouse Control Script using IR\r
+//by vkapadia with much assistance from inio\r
+//vkapadia@vkapadia.com\r
+//\r
+//Calibration:\r
+//To calibrate, run this program and put the Wiimote on a flat surface face-up.\r
+//Then read the values in the debug line (next to the run button).\r
+//Change these values until the debug line reads approx. all zeros.\r
+var.xtrim1 = -1\r
+var.ytrim1 = -25\r
+var.ztrim1 = 2\r
+\r
+var.xtrim2 = -1\r
+var.ytrim2 = -25\r
+var.ztrim2 = 2\r
+\r
+var.coeff = 0.1\r
+\r
+//\r
+//Options:\r
+var.deadzone = 5 //distance in pixels that you have to move the wiimote in\r
+ //order for it to register movement. Creates a "dead zone" around the pointer\r
+ //to make it easier to click. Higher = smoother but less accurate.\r
+//fake cursor init\r
+\r
+//cursor2.visible = true\r
+\r
+//more options to be added later\r
+\r
+//Controls:\r
+//Point Wiimote = Move Mouse\r
+//D-Pad = Arrow Keys\r
+//B-Button = Left Click\r
+//Home = Middle Click\r
+//A-Button = Right Click\r
+//Plus and Minus = Control Volume\r
+//One = Unmapped\r
+//Two = Unmapped\r
+//\r
+//If the pointer hits the edge of the screen, the Wiimote will rumble a bit.\r
+//\r
+//The LEDs attempt to emulate KITT's grill from Knight Rider\r
+\r
+//***Do not edit anything below this line unless you know what you are doing.***\r
+var.accx1 = wiimote1.RawForceX1 + var.xtrim1\r
+var.accy1 = wiimote1.RawForceY1 + var.ytrim1\r
+var.accz1 = wiimote1.RawForceZ1 + var.ztrim1\r
+\r
+if wiimote1.dot1vis and wiimote1.dot2vis then\r
+\r
+ if var.accy1 > -7 then\r
+ var.orientation1 = 0\r
+ elseif var.accy1 > -45 then\r
+ if var.accx1 < 0 then\r
+ var.orientation1 = 3\r
+ else\r
+ var.orientation1 = 1\r
+ endif\r
+ else\r
+ var.orientation1 = 2\r
+ endif\r
+\r
+ if var.leftpoint1 = 0 then\r
+ if var.orientation1 = 0 then\r
+ if wiimote1.dot1x < wiimote1.dot2x then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ if var.orientation1 = 1 then\r
+ if wiimote1.dot1y > wiimote1.dot2y then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ if var.orientation1 = 2 then\r
+ if wiimote1.dot1x > wiimote1.dot2x then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ if var.orientation = 3 then\r
+ if wiimote1.dot1y < wiimote1.dot2y then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ endif\r
+\r
+ if var.leftpoint1 = 1 then\r
+ var.fix1x1 = wiimote1.dot1x\r
+ var.fix1y1 = wiimote1.dot1y\r
+ var.fix2x1 = wiimote1.dot2x\r
+ var.fix2y1 = wiimote1.dot2y\r
+ else\r
+ var.fix1x1 = wiimote1.dot2x\r
+ var.fix1y1 = wiimote1.dot2y\r
+ var.fix2x1 = wiimote1.dot1x\r
+ var.fix2y1 = wiimote1.dot1y\r
+ endif\r
+\r
+ var.dx1 = var.fix2x1 - var.fix1x1\r
+ var.dy1 = var.fix2y1 - var.fix1y1\r
+ var.cx1 = (var.fix1x1+var.fix2x1)/1024.0 - 1\r
+ var.cy1 = (var.fix1y1+var.fix2y1)/1024.0 - .75\r
+\r
+ var.d1 = sqrt(var.dx1*var.dx1+var.dy1*var.dy1)\r
+\r
+ var.dx1 = var.dx1 / var.d1\r
+ var.dy1 = var.dy1 / var.d1\r
+\r
+ var.ox1 = -var.dy1*var.cy1-var.dx1*var.cx1;\r
+ var.oy1 = -var.dx1*var.cy1+var.dy1*var.cx1;\r
+\r
+ var.ax1 = (var.ox1 * var.coeff*screen.desktopwidth) + (screen.desktopwidth* var.coeff / 2)\r
+ var.ay1 = (-var.oy1* var.coeff * screen.desktopwidth) + (screen.desktopheight* var.coeff / 2)\r
+\r
+ var.dx1 = var.ax1 - cursor2.posx\r
+ var.dy1 = var.ay1 - cursor2.posy\r
+\r
+ var.d1 = sqrt((var.dx1*var.dx1)+(var.dy1*var.dy1))\r
+\r
+ var.a1 = 180 / (200 + var.d1 * var.d1 * var.d1 * .001)\r
+\r
+ if var.d1 <= var.deadzone then var.a1 = 1\r
+\r
+ //debug = var.d + " " + var.a\r
+\r
+ var.finalx1 = cursor2.posx * var.a1 + var.ax1 * (1 - var.a1)\r
+ var.finaly1 = cursor2.posy * var.a1 + var.ay1 * (1 - var.a1)\r
+\r
+\r
+ cursor2.posx = smooth(var.finalx1,3,5)\r
+ cursor2.posy = smooth(var.finaly1,3,5)\r
+\r
+else\r
+\r
+ var.leftpoint1 = 0\r
+\r
+endif\r
+\r
+var.xpos1 = var.finalx1\r
+var.ypos1 = var.finaly1\r
+ppjoy1.analog0 = ensuremaprange(var.xpos1,0,screen.desktopwidth* var.coeff,-1,1)\r
+ppjoy1.analog1 = ensuremaprange(var.ypos1,0,screen.desktopheight* var.coeff,-1,1)\r
+\r
+if wiimote1.B or wiimote1.A or wiimote1.Up or wiimote1.down or wiimote1.Left or wiimote1.Right == true\r
+ ppjoy1.digital0 = true\r
+else\r
+ ppjoy1.digital0 = false\r
+endif\r
+\r
+debug = var.accx1+ " " + var.accy1+ " " + var.accz1\r
--- /dev/null
+Wiimote1.led1 = true\r
+Wiimote2.led1 = true\r
+Wiimote2.led4 = true\r
+Wiimote3.led2 = true\r
+Wiimote4.led2 = true\r
+Wiimote4.led4 = true\r
+Wiimote5.led3 = true\r
+Wiimote6.led3 = true\r
+Wiimote6.led4 = true\r
+//Mouse Control Script using IR\r
+//by vkapadia with much assistance from inio\r
+//vkapadia@vkapadia.com\r
+//\r
+//Calibration:\r
+//To calibrate, run this program and put the Wiimote on a flat surface face-up.\r
+//Then read the values in the debug line (next to the run button).\r
+//Change these values until the debug line reads approx. all zeros.\r
+var.xtrim1 = -1\r
+var.ytrim1 = -25\r
+var.ztrim1 = 2\r
+\r
+var.xtrim2 = 2\r
+var.ytrim2 = -30\r
+var.ztrim2 = 2\r
+\r
+var.coeff = 0.1\r
+\r
+//\r
+//Options:\r
+var.deadzone = 5 //distance in pixels that you have to move the wiimote in\r
+ //order for it to register movement. Creates a "dead zone" around the pointer\r
+ //to make it easier to click. Higher = smoother but less accurate.\r
+//fake cursor init\r
+\r
+//cursor2.visible = true\r
+\r
+//more options to be added later\r
+\r
+//Controls:\r
+//Point Wiimote = Move Mouse\r
+//D-Pad = Arrow Keys\r
+//B-Button = Left Click\r
+//Home = Middle Click\r
+//A-Button = Right Click\r
+//Plus and Minus = Control Volume\r
+//One = Unmapped\r
+//Two = Unmapped\r
+//\r
+//If the pointer hits the edge of the screen, the Wiimote will rumble a bit.\r
+//\r
+//The LEDs attempt to emulate KITT's grill from Knight Rider\r
+\r
+//WIIMOTE 1\r
+//***Do not edit anything below this line unless you know what you are doing.***\r
+var.accx1 = wiimote1.RawForceX + var.xtrim1\r
+var.accy1 = wiimote1.RawForceY + var.ytrim1\r
+var.accz1 = wiimote1.RawForceZ + var.ztrim1\r
+\r
+if wiimote1.dot1vis and wiimote1.dot2vis then\r
+\r
+ if var.accy1 > -7 then\r
+ var.orientation1 = 0\r
+ elseif var.accy1 > -45 then\r
+ if var.accx1 < 0 then\r
+ var.orientation1 = 3\r
+ else\r
+ var.orientation1 = 1\r
+ endif\r
+ else\r
+ var.orientation1 = 2\r
+ endif\r
+\r
+ if var.leftpoint1 = 0 then\r
+ if var.orientation1 = 0 then\r
+ if wiimote1.dot1x < wiimote1.dot2x then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ if var.orientation1 = 1 then\r
+ if wiimote1.dot1y > wiimote1.dot2y then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ if var.orientation1 = 2 then\r
+ if wiimote1.dot1x > wiimote1.dot2x then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ if var.orientation = 3 then\r
+ if wiimote1.dot1y < wiimote1.dot2y then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ endif\r
+\r
+ if var.leftpoint1 = 1 then\r
+ var.fix1x1 = wiimote1.dot1x\r
+ var.fix1y1 = wiimote1.dot1y\r
+ var.fix2x1 = wiimote1.dot2x\r
+ var.fix2y1 = wiimote1.dot2y\r
+ else\r
+ var.fix1x1 = wiimote1.dot2x\r
+ var.fix1y1 = wiimote1.dot2y\r
+ var.fix2x1 = wiimote1.dot1x\r
+ var.fix2y1 = wiimote1.dot1y\r
+ endif\r
+\r
+ var.dx1 = var.fix2x1 - var.fix1x1\r
+ var.dy1 = var.fix2y1 - var.fix1y1\r
+ var.cx1 = (var.fix1x1+var.fix2x1)/1024.0 - 1\r
+ var.cy1 = (var.fix1y1+var.fix2y1)/1024.0 - .75\r
+\r
+ var.d1 = sqrt(var.dx1*var.dx1+var.dy1*var.dy1)\r
+\r
+ var.dx1 = var.dx1 / var.d1\r
+ var.dy1 = var.dy1 / var.d1\r
+\r
+ var.ox1 = -var.dy1*var.cy1-var.dx1*var.cx1;\r
+ var.oy1 = -var.dx1*var.cy1+var.dy1*var.cx1;\r
+\r
+ var.ax1 = (var.ox1 * var.coeff*screen.desktopwidth) + (screen.desktopwidth* var.coeff / 2)\r
+ var.ay1 = (-var.oy1* var.coeff * screen.desktopwidth) + (screen.desktopheight* var.coeff / 2)\r
+\r
+ var.dx1 = var.ax1 - cursor2.posx\r
+ var.dy1 = var.ay1 - cursor2.posy\r
+\r
+ var.d1 = sqrt((var.dx1*var.dx1)+(var.dy1*var.dy1))\r
+\r
+ var.a1 = 180 / (200 + var.d1 * var.d1 * var.d1 * .001)\r
+\r
+ if var.d1 <= var.deadzone then var.a1 = 1\r
+\r
+ //debug = var.d + " " + var.a\r
+\r
+ var.finalx1 = cursor2.posx * var.a1 + var.ax1 * (1 - var.a1)\r
+ var.finaly1 = cursor2.posy * var.a1 + var.ay1 * (1 - var.a1)\r
+\r
+\r
+ cursor2.posx = smooth(var.finalx1,3,5)\r
+ cursor2.posy = smooth(var.finaly1,3,5)\r
+\r
+else\r
+\r
+ var.leftpoint1 = 0\r
+\r
+endif\r
+\r
+var.xpos1 = var.finalx1\r
+var.ypos1 = var.finaly1\r
+ppjoy1.analog0 = ensuremaprange(var.xpos1,0,screen.desktopwidth* var.coeff,-1,1)\r
+ppjoy1.analog1 = ensuremaprange(var.ypos1,0,screen.desktopheight* var.coeff,-1,1)\r
+\r
+if wiimote1.B or wiimote1.A or wiimote1.Up or wiimote1.down or wiimote1.Left or wiimote1.Right == true\r
+ ppjoy1.digital0 = true\r
+else\r
+ ppjoy1.digital0 = false\r
+endif\r
+\r
+//WIIMOTE 2\r
+//***Do not edit anything below this line unless you know what you are doing.***\r
+var.accx2 = wiimote2.RawForceX + var.xtrim2\r
+var.accy2 = wiimote2.RawForceY + var.ytrim2\r
+var.accz2 = wiimote2.RawForceZ + var.ztrim2\r
+\r
+if wiimote2.dot1vis and wiimote2.dot2vis then\r
+\r
+ if var.accy2 > -7 then\r
+ var.orientation2 = 0\r
+ elseif var.accy2 > -45 then\r
+ if var.accx2 < 0 then\r
+ var.orientation2 = 3\r
+ else\r
+ var.orientation2 = 1\r
+ endif\r
+ else\r
+ var.orientation2 = 2\r
+ endif\r
+\r
+ if var.leftpoint2 = 0 then\r
+ if var.orientation2 = 0 then\r
+ if wiimote2.dot1x < wiimote2.dot2x then\r
+ var.leftpoint2 = 1\r
+ else\r
+ var.leftpoint2 = 2\r
+ endif\r
+ endif\r
+ if var.orientation2 = 1 then\r
+ if wiimote2.dot1y > wiimote2.dot2y then\r
+ var.leftpoint2 = 1\r
+ else\r
+ var.leftpoint2 = 2\r
+ endif\r
+ endif\r
+ if var.orientation2 = 2 then\r
+ if wiimote2.dot1x > wiimote2.dot2x then\r
+ var.leftpoint2 = 1\r
+ else\r
+ var.leftpoint2 = 2\r
+ endif\r
+ endif\r
+ if var.orientation = 3 then\r
+ if wiimote2.dot1y < wiimote2.dot2y then\r
+ var.leftpoint2 = 1\r
+ else\r
+ var.leftpoint2 = 2\r
+ endif\r
+ endif\r
+ endif\r
+\r
+ if var.leftpoint2 = 1 then\r
+ var.fix1x2 = wiimote2.dot1x\r
+ var.fix1y2 = wiimote2.dot1y\r
+ var.fix2x2 = wiimote2.dot2x\r
+ var.fix2y2 = wiimote2.dot2y\r
+ else\r
+ var.fix1x2 = wiimote2.dot2x\r
+ var.fix1y2 = wiimote2.dot2y\r
+ var.fix2x2 = wiimote2.dot1x\r
+ var.fix2y2 = wiimote2.dot1y\r
+ endif\r
+\r
+ var.dx2 = var.fix2x2 - var.fix1x2\r
+ var.dy2 = var.fix2y2 - var.fix1y2\r
+ var.cx2 = (var.fix1x2+var.fix2x2)/1024.0 - 1\r
+ var.cy2 = (var.fix1y2+var.fix2y2)/1024.0 - .75\r
+\r
+ var.d2 = sqrt(var.dx2*var.dx2+var.dy2*var.dy2)\r
+\r
+ var.dx2 = var.dx2 / var.d2\r
+ var.dy2 = var.dy2 / var.d2\r
+\r
+ var.ox2 = -var.dy2*var.cy2-var.dx2*var.cx2;\r
+ var.oy2 = -var.dx2*var.cy2+var.dy2*var.cx2;\r
+\r
+ var.ax2 = (var.ox2 * var.coeff*screen.desktopwidth) + (screen.desktopwidth* var.coeff / 2)\r
+ var.ay2 = (-var.oy2* var.coeff * screen.desktopwidth) + (screen.desktopheight* var.coeff / 2)\r
+\r
+ var.dx2 = var.ax2 - cursor3.posx\r
+ var.dy2 = var.ay2 - cursor3.posy\r
+\r
+ var.d2 = sqrt((var.dx2*var.dx2)+(var.dy2*var.dy2))\r
+\r
+ var.a2 = 180 / (200 + var.d2 * var.d2 * var.d2 * .001)\r
+\r
+ if var.d2 <= var.deadzone then var.a2 = 1\r
+\r
+ //debug = var.d + " " + var.a\r
+\r
+ var.finalx2 = cursor3.posx * var.a2 + var.ax2 * (1 - var.a2)\r
+ var.finaly2 = cursor3.posy * var.a2 + var.ay2 * (1 - var.a2)\r
+\r
+\r
+ cursor3.posx = smooth(var.finalx2,3,5)\r
+ cursor3.posy = smooth(var.finaly2,3,5)\r
+\r
+else\r
+\r
+ var.leftpoint2 = 0\r
+\r
+endif\r
+\r
+var.xpos2 = var.finalx2\r
+var.ypos2 = var.finaly2\r
+ppjoy2.analog0 = ensuremaprange(var.xpos2,0,screen.desktopwidth* var.coeff,-1,1)\r
+ppjoy2.analog1 = ensuremaprange(var.ypos2,0,screen.desktopheight* var.coeff,-1,1)\r
+\r
+if wiimote2.B or wiimote2.A or wiimote2.Up or wiimote2.down or wiimote2.Left or wiimote2.Right == true\r
+ ppjoy2.digital0 = true\r
+else\r
+ ppjoy2.digital0 = false\r
+endif\r
+\r
+debug = var.accx2+ " " + var.accy2+ " " + var.accz2\r
--- /dev/null
+Wiimote1.led1 = true\r
+Wiimote2.led1 = true\r
+Wiimote2.led4 = true\r
+Wiimote3.led2 = true\r
+Wiimote4.led2 = true\r
+Wiimote4.led4 = true\r
+Wiimote5.led3 = true\r
+Wiimote6.led3 = true\r
+Wiimote6.led4 = true\r
+//Mouse Control Script using IR\r
+//by vkapadia with much assistance from inio\r
+//vkapadia@vkapadia.com\r
+//\r
+//Calibration:\r
+//To calibrate, run this program and put the Wiimote on a flat surface face-up.\r
+//Then read the values in the debug line (next to the run button).\r
+//Change these values until the debug line reads approx. all zeros.\r
+var.xtrim1 = -1\r
+var.ytrim1 = -25\r
+var.ztrim1 = 2\r
+\r
+var.xtrim2 = 3\r
+var.ytrim2 = -30\r
+var.ztrim2 = 2\r
+\r
+var.xtrim3 = 2\r
+var.ytrim3 = -27\r
+var.ztrim3 = 2\r
+\r
+var.xtrim4 = 2\r
+var.ytrim4 = -30\r
+var.ztrim4 = 2\r
+\r
+var.coeff = 0.1\r
+\r
+//\r
+//Options:\r
+var.deadzone = 5 //distance in pixels that you have to move the wiimote in\r
+ //order for it to register movement. Creates a "dead zone" around the pointer\r
+ //to make it easier to click. Higher = smoother but less accurate.\r
+//fake cursor init\r
+\r
+//cursor2.visible = true\r
+\r
+//more options to be added later\r
+\r
+//Controls:\r
+//Point Wiimote = Move Mouse\r
+//D-Pad = Arrow Keys\r
+//B-Button = Left Click\r
+//Home = Middle Click\r
+//A-Button = Right Click\r
+//Plus and Minus = Control Volume\r
+//One = Unmapped\r
+//Two = Unmapped\r
+//\r
+//If the pointer hits the edge of the screen, the Wiimote will rumble a bit.\r
+//\r
+//The LEDs attempt to emulate KITT's grill from Knight Rider\r
+\r
+//WIIMOTE 1\r
+//***Do not edit anything below this line unless you know what you are doing.***\r
+var.accx1 = wiimote1.RawForceX + var.xtrim1\r
+var.accy1 = wiimote1.RawForceY + var.ytrim1\r
+var.accz1 = wiimote1.RawForceZ + var.ztrim1\r
+\r
+if wiimote1.dot1vis and wiimote1.dot2vis then\r
+\r
+ if var.accy1 > -7 then\r
+ var.orientation1 = 0\r
+ elseif var.accy1 > -45 then\r
+ if var.accx1 < 0 then\r
+ var.orientation1 = 3\r
+ else\r
+ var.orientation1 = 1\r
+ endif\r
+ else\r
+ var.orientation1 = 2\r
+ endif\r
+\r
+ if var.leftpoint1 = 0 then\r
+ if var.orientation1 = 0 then\r
+ if wiimote1.dot1x < wiimote1.dot2x then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ if var.orientation1 = 1 then\r
+ if wiimote1.dot1y > wiimote1.dot2y then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ if var.orientation1 = 2 then\r
+ if wiimote1.dot1x > wiimote1.dot2x then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ if var.orientation1 = 3 then\r
+ if wiimote1.dot1y < wiimote1.dot2y then\r
+ var.leftpoint1 = 1\r
+ else\r
+ var.leftpoint1 = 2\r
+ endif\r
+ endif\r
+ endif\r
+\r
+ if var.leftpoint1 = 1 then\r
+ var.fix1x1 = wiimote1.dot1x\r
+ var.fix1y1 = wiimote1.dot1y\r
+ var.fix2x1 = wiimote1.dot2x\r
+ var.fix2y1 = wiimote1.dot2y\r
+ else\r
+ var.fix1x1 = wiimote1.dot2x\r
+ var.fix1y1 = wiimote1.dot2y\r
+ var.fix2x1 = wiimote1.dot1x\r
+ var.fix2y1 = wiimote1.dot1y\r
+ endif\r
+\r
+ var.dx1 = var.fix2x1 - var.fix1x1\r
+ var.dy1 = var.fix2y1 - var.fix1y1\r
+ var.cx1 = (var.fix1x1+var.fix2x1)/1024.0 - 1\r
+ var.cy1 = (var.fix1y1+var.fix2y1)/1024.0 - .75\r
+\r
+ var.d1 = sqrt(var.dx1*var.dx1+var.dy1*var.dy1)\r
+\r
+ var.dx1 = var.dx1 / var.d1\r
+ var.dy1 = var.dy1 / var.d1\r
+\r
+ var.ox1 = -var.dy1*var.cy1-var.dx1*var.cx1;\r
+ var.oy1 = -var.dx1*var.cy1+var.dy1*var.cx1;\r
+\r
+ var.ax1 = (var.ox1 * var.coeff*screen.desktopwidth) + (screen.desktopwidth* var.coeff / 2)\r
+ var.ay1 = (-var.oy1* var.coeff * screen.desktopwidth) + (screen.desktopheight* var.coeff / 2)\r
+\r
+ var.dx1 = var.ax1 - cursor2.posx\r
+ var.dy1 = var.ay1 - cursor2.posy\r
+\r
+ var.d1 = sqrt((var.dx1*var.dx1)+(var.dy1*var.dy1))\r
+\r
+ var.a1 = 180 / (200 + var.d1 * var.d1 * var.d1 * .001)\r
+\r
+ if var.d1 <= var.deadzone then var.a1 = 1\r
+\r
+ //debug = var.d + " " + var.a\r
+\r
+ var.finalx1 = cursor2.posx * var.a1 + var.ax1 * (1 - var.a1)\r
+ var.finaly1 = cursor2.posy * var.a1 + var.ay1 * (1 - var.a1)\r
+\r
+\r
+ cursor2.posx = smooth(var.finalx1,3,5)\r
+ cursor2.posy = smooth(var.finaly1,3,5)\r
+\r
+else\r
+\r
+ var.leftpoint1 = 0\r
+\r
+endif\r
+\r
+var.xpos1 = var.finalx1\r
+var.ypos1 = var.finaly1\r
+ppjoy1.analog0 = ensuremaprange(var.xpos1,0,screen.desktopwidth* var.coeff,-1,1)\r
+ppjoy1.analog1 = ensuremaprange(var.ypos1,0,screen.desktopheight* var.coeff,-1,1)\r
+\r
+if wiimote1.B or wiimote1.A or wiimote1.Up or wiimote1.down or wiimote1.Left or wiimote1.Right == true\r
+ ppjoy1.digital0 = true\r
+else\r
+ ppjoy1.digital0 = false\r
+endif\r
+\r
+//WIIMOTE 2\r
+//***Do not edit anything below this line unless you know what you are doing.***\r
+var.accx2 = wiimote2.RawForceX + var.xtrim2\r
+var.accy2 = wiimote2.RawForceY + var.ytrim2\r
+var.accz2 = wiimote2.RawForceZ + var.ztrim2\r
+\r
+if wiimote2.dot1vis and wiimote2.dot2vis then\r
+\r
+ if var.accy2 > -7 then\r
+ var.orientation2 = 0\r
+ elseif var.accy2 > -45 then\r
+ if var.accx2 < 0 then\r
+ var.orientation2 = 3\r
+ else\r
+ var.orientation2 = 1\r
+ endif\r
+ else\r
+ var.orientation2 = 2\r
+ endif\r
+\r
+ if var.leftpoint2 = 0 then\r
+ if var.orientation2 = 0 then\r
+ if wiimote2.dot1x < wiimote2.dot2x then\r
+ var.leftpoint2 = 1\r
+ else\r
+ var.leftpoint2 = 2\r
+ endif\r
+ endif\r
+ if var.orientation2 = 1 then\r
+ if wiimote2.dot1y > wiimote2.dot2y then\r
+ var.leftpoint2 = 1\r
+ else\r
+ var.leftpoint2 = 2\r
+ endif\r
+ endif\r
+ if var.orientation2 = 2 then\r
+ if wiimote2.dot1x > wiimote2.dot2x then\r
+ var.leftpoint2 = 1\r
+ else\r
+ var.leftpoint2 = 2\r
+ endif\r
+ endif\r
+ if var.orientation2 = 3 then\r
+ if wiimote2.dot1y < wiimote2.dot2y then\r
+ var.leftpoint2 = 1\r
+ else\r
+ var.leftpoint2 = 2\r
+ endif\r
+ endif\r
+ endif\r
+\r
+ if var.leftpoint2 = 1 then\r
+ var.fix1x2 = wiimote2.dot1x\r
+ var.fix1y2 = wiimote2.dot1y\r
+ var.fix2x2 = wiimote2.dot2x\r
+ var.fix2y2 = wiimote2.dot2y\r
+ else\r
+ var.fix1x2 = wiimote2.dot2x\r
+ var.fix1y2 = wiimote2.dot2y\r
+ var.fix2x2 = wiimote2.dot1x\r
+ var.fix2y2 = wiimote2.dot1y\r
+ endif\r
+\r
+ var.dx2 = var.fix2x2 - var.fix1x2\r
+ var.dy2 = var.fix2y2 - var.fix1y2\r
+ var.cx2 = (var.fix1x2+var.fix2x2)/1024.0 - 1\r
+ var.cy2 = (var.fix1y2+var.fix2y2)/1024.0 - .75\r
+\r
+ var.d2 = sqrt(var.dx2*var.dx2+var.dy2*var.dy2)\r
+\r
+ var.dx2 = var.dx2 / var.d2\r
+ var.dy2 = var.dy2 / var.d2\r
+\r
+ var.ox2 = -var.dy2*var.cy2-var.dx2*var.cx2;\r
+ var.oy2 = -var.dx2*var.cy2+var.dy2*var.cx2;\r
+\r
+ var.ax2 = (var.ox2 * var.coeff*screen.desktopwidth) + (screen.desktopwidth* var.coeff / 2)\r
+ var.ay2 = (-var.oy2* var.coeff * screen.desktopwidth) + (screen.desktopheight* var.coeff / 2)\r
+\r
+ var.dx2 = var.ax2 - cursor3.posx\r
+ var.dy2 = var.ay2 - cursor3.posy\r
+\r
+ var.d2 = sqrt((var.dx2*var.dx2)+(var.dy2*var.dy2))\r
+\r
+ var.a2 = 180 / (200 + var.d2 * var.d2 * var.d2 * .001)\r
+\r
+ if var.d2 <= var.deadzone then var.a2 = 1\r
+\r
+ //debug = var.d + " " + var.a\r
+\r
+ var.finalx2 = cursor3.posx * var.a2 + var.ax2 * (1 - var.a2)\r
+ var.finaly2 = cursor3.posy * var.a2 + var.ay2 * (1 - var.a2)\r
+\r
+\r
+ cursor3.posx = smooth(var.finalx2,3,5)\r
+ cursor3.posy = smooth(var.finaly2,3,5)\r
+\r
+else\r
+\r
+ var.leftpoint2 = 0\r
+\r
+endif\r
+\r
+var.xpos2 = var.finalx2\r
+var.ypos2 = var.finaly2\r
+ppjoy2.analog0 = ensuremaprange(var.xpos2,0,screen.desktopwidth* var.coeff,-1,1)\r
+ppjoy2.analog1 = ensuremaprange(var.ypos2,0,screen.desktopheight* var.coeff,-1,1)\r
+\r
+if wiimote2.B or wiimote2.A or wiimote2.Up or wiimote2.down or wiimote2.Left or wiimote2.Right == true\r
+ ppjoy2.digital0 = true\r
+else\r
+ ppjoy2.digital0 = false\r
+endif\r
+\r
+debug = var.accx2+ " " + var.accy2+ " " + var.accz2\r
+\r
+//WIIMOTE 3\r
+//***Do not edit anything below this line unless you know what you are doing.***\r
+var.accx3 = wiimote3.RawForceX + var.xtrim3\r
+var.accy3 = wiimote3.RawForceY + var.ytrim3\r
+var.accz3 = wiimote3.RawForceZ + var.ztrim3\r
+\r
+if wiimote3.dot1vis and wiimote3.dot2vis then\r
+\r
+ if var.accy3 > -7 then\r
+ var.orientation3 = 0\r
+ elseif var.accy3 > -45 then\r
+ if var.accx3 < 0 then\r
+ var.orientation3 = 3\r
+ else\r
+ var.orientation3 = 1\r
+ endif\r
+ else\r
+ var.orientation3 = 2\r
+ endif\r
+\r
+ if var.leftpoint3 = 0 then\r
+ if var.orientation3 = 0 then\r
+ if wiimote3.dot1x < wiimote3.dot2x then\r
+ var.leftpoint3 = 1\r
+ else\r
+ var.leftpoint3 = 2\r
+ endif\r
+ endif\r
+ if var.orientation3 = 1 then\r
+ if wiimote3.dot1y > wiimote3.dot2y then\r
+ var.leftpoint3 = 1\r
+ else\r
+ var.leftpoint3 = 2\r
+ endif\r
+ endif\r
+ if var.orientation3 = 2 then\r
+ if wiimote3.dot1x > wiimote3.dot2x then\r
+ var.leftpoint3 = 1\r
+ else\r
+ var.leftpoint3 = 2\r
+ endif\r
+ endif\r
+ if var.orientation3 = 3 then\r
+ if wiimote3.dot1y < wiimote3.dot2y then\r
+ var.leftpoint3 = 1\r
+ else\r
+ var.leftpoint3 = 2\r
+ endif\r
+ endif\r
+ endif\r
+\r
+ if var.leftpoint3 = 1 then\r
+ var.fix1x3 = wiimote3.dot1x\r
+ var.fix1y3 = wiimote3.dot1y\r
+ var.fix2x3 = wiimote3.dot2x\r
+ var.fix2y3 = wiimote3.dot2y\r
+ else\r
+ var.fix1x3 = wiimote3.dot2x\r
+ var.fix1y3 = wiimote3.dot2y\r
+ var.fix2x3 = wiimote3.dot1x\r
+ var.fix2y3 = wiimote3.dot1y\r
+ endif\r
+\r
+ var.dx3 = var.fix2x3 - var.fix1x3\r
+ var.dy3 = var.fix2y3 - var.fix1y3\r
+ var.cx3 = (var.fix1x3+var.fix2x3)/1024.0 - 1\r
+ var.cy3 = (var.fix1y3+var.fix2y3)/1024.0 - .75\r
+\r
+ var.d3 = sqrt(var.dx3*var.dx3+var.dy3*var.dy3)\r
+\r
+ var.dx3 = var.dx3 / var.d3\r
+ var.dy3 = var.dy3 / var.d3\r
+\r
+ var.ox3 = -var.dy3*var.cy3-var.dx3*var.cx3;\r
+ var.oy3 = -var.dx3*var.cy3+var.dy3*var.cx3;\r
+\r
+ var.ax3 = (var.ox3 * var.coeff*screen.desktopwidth) + (screen.desktopwidth* var.coeff / 2)\r
+ var.ay3 = (-var.oy3* var.coeff * screen.desktopwidth) + (screen.desktopheight* var.coeff / 2)\r
+\r
+ var.dx3 = var.ax3 - cursor4.posx\r
+ var.dy3 = var.ay3 - cursor4.posy\r
+\r
+ var.d3 = sqrt((var.dx3*var.dx3)+(var.dy3*var.dy3))\r
+\r
+ var.a3 = 180 / (200 + var.d3 * var.d3 * var.d3 * .001)\r
+\r
+ if var.d3 <= var.deadzone then var.a3 = 1\r
+\r
+ //debug = var.d + " " + var.a\r
+\r
+ var.finalx3 = cursor4.posx * var.a3 + var.ax3 * (1 - var.a3)\r
+ var.finaly3 = cursor4.posy * var.a3 + var.ay3 * (1 - var.a3)\r
+\r
+\r
+ cursor4.posx = smooth(var.finalx3,3,5)\r
+ cursor4.posy = smooth(var.finaly3,3,5)\r
+\r
+else\r
+\r
+ var.leftpoint3 = 0\r
+\r
+endif\r
+\r
+var.xpos3 = var.finalx3\r
+var.ypos3 = var.finaly3\r
+ppjoy3.analog0 = ensuremaprange(var.xpos3,0,screen.desktopwidth* var.coeff,-1,1)\r
+ppjoy3.analog1 = ensuremaprange(var.ypos3,0,screen.desktopheight* var.coeff,-1,1)\r
+\r
+if wiimote3.B or wiimote3.A or wiimote3.Up or wiimote3.down or wiimote3.Left or wiimote3.Right == true\r
+ ppjoy3.digital0 = true\r
+else\r
+ ppjoy3.digital0 = false\r
+endif\r
+\r
+debug = var.accx1+ " " + var.accy1+ " " + var.accz1\r
+\r
+//WIIMOTE 3\r
+//***Do not edit anything below this line unless you know what you are doing.***\r
+var.accx4 = wiimote4.RawForceX + var.xtrim4\r
+var.accy4 = wiimote4.RawForceY + var.ytrim4\r
+var.accz4 = wiimote4.RawForceZ + var.ztrim4\r
+\r
+if wiimote4.dot1vis and wiimote4.dot2vis then\r
+\r
+ if var.accy4 > -7 then\r
+ var.orientation4 = 0\r
+ elseif var.accy4 > -45 then\r
+ if var.accx4 < 0 then\r
+ var.orientation4 = 3\r
+ else\r
+ var.orientation4 = 1\r
+ endif\r
+ else\r
+ var.orientation4 = 2\r
+ endif\r
+\r
+ if var.leftpoint4 = 0 then\r
+ if var.orientation4 = 0 then\r
+ if wiimote4.dot1x < wiimote4.dot2x then\r
+ var.leftpoint4 = 1\r
+ else\r
+ var.leftpoint4 = 2\r
+ endif\r
+ endif\r
+ if var.orientation4 = 1 then\r
+ if wiimote4.dot1y > wiimote4.dot2y then\r
+ var.leftpoint4 = 1\r
+ else\r
+ var.leftpoint4 = 2\r
+ endif\r
+ endif\r
+ if var.orientation4 = 2 then\r
+ if wiimote4.dot1x > wiimote4.dot2x then\r
+ var.leftpoint4 = 1\r
+ else\r
+ var.leftpoint4 = 2\r
+ endif\r
+ endif\r
+ if var.orientation4 = 3 then\r
+ if wiimote4.dot1y < wiimote4.dot2y then\r
+ var.leftpoint4 = 1\r
+ else\r
+ var.leftpoint4 = 2\r
+ endif\r
+ endif\r
+ endif\r
+\r
+ if var.leftpoint4 = 1 then\r
+ var.fix1x4 = wiimote4.dot1x\r
+ var.fix1y4 = wiimote4.dot1y\r
+ var.fix2x4 = wiimote4.dot2x\r
+ var.fix2y4 = wiimote4.dot2y\r
+ else\r
+ var.fix1x4 = wiimote4.dot2x\r
+ var.fix1y4 = wiimote4.dot2y\r
+ var.fix2x4 = wiimote4.dot1x\r
+ var.fix2y4 = wiimote4.dot1y\r
+ endif\r
+\r
+ var.dx4 = var.fix2x4 - var.fix1x4\r
+ var.dy4 = var.fix2y4 - var.fix1y4\r
+ var.cx4 = (var.fix1x4+var.fix2x4)/1024.0 - 1\r
+ var.cy4 = (var.fix1y4+var.fix2y4)/1024.0 - .75\r
+\r
+ var.d4 = sqrt(var.dx4*var.dx4+var.dy4*var.dy4)\r
+\r
+ var.dx4 = var.dx4 / var.d4\r
+ var.dy4 = var.dy4 / var.d4\r
+\r
+ var.ox4 = -var.dy4*var.cy4-var.dx4*var.cx4;\r
+ var.oy4 = -var.dx4*var.cy4+var.dy4*var.cx4;\r
+\r
+ var.ax4 = (var.ox4 * var.coeff*screen.desktopwidth) + (screen.desktopwidth* var.coeff / 2)\r
+ var.ay4 = (-var.oy4* var.coeff * screen.desktopwidth) + (screen.desktopheight* var.coeff / 2)\r
+\r
+ var.dx4 = var.ax4 - cursor5.posx\r
+ var.dy4 = var.ay4 - cursor5.posy\r
+\r
+ var.d4 = sqrt((var.dx4*var.dx4)+(var.dy4*var.dy4))\r
+\r
+ var.a4 = 180 / (200 + var.d4 * var.d4 * var.d4 * .001)\r
+\r
+ if var.d4 <= var.deadzone then var.a4 = 1\r
+\r
+ //debug = var.d + " " + var.a\r
+\r
+ var.finalx4 = cursor5.posx * var.a4 + var.ax4 * (1 - var.a4)\r
+ var.finaly4 = cursor5.posy * var.a4 + var.ay4 * (1 - var.a4)\r
+\r
+\r
+ cursor5.posx = smooth(var.finalx4,3,5)\r
+ cursor5.posy = smooth(var.finaly4,3,5)\r
+\r
+else\r
+\r
+ var.leftpoint4 = 0\r
+\r
+endif\r
+\r
+var.xpos4 = var.finalx4\r
+var.ypos4 = var.finaly4\r
+ppjoy4.analog0 = ensuremaprange(var.xpos4,0,screen.desktopwidth* var.coeff,-1,1)\r
+ppjoy4.analog1 = ensuremaprange(var.ypos4,0,screen.desktopheight* var.coeff,-1,1)\r
+\r
+if wiimote4.B or wiimote4.A or wiimote4.Up or wiimote4.down or wiimote4.Left or wiimote4.Right == true\r
+ ppjoy4.digital0 = true\r
+else\r
+ ppjoy4.digital0 = false\r
+endif\r
+\r
--- /dev/null
+'''\r
+Created on 15 juil. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+from gui.constants import *\r
+\r
+class Wiimote:\r
+ '''\r
+ Object representing a Wiimote\r
+ \r
+ number:\r
+ The number of the Wiimote\r
+ port:\r
+ The pypm object representing the MIDI port on which the Wiimote emits\r
+ instrument:\r
+ The instrument associated with the Wiimote\r
+ cursor:\r
+ The cursor associated with the Wiimote\r
+ '''\r
+\r
+ def __init__(self, number, portNumber, port, instrument, cursor):\r
+ '''\r
+ Constructor\r
+ \r
+ number:\r
+ The number of the Wiimote\r
+ portNumber:\r
+ The number of the port (as numbered by pypm) on which the wiimote emits\r
+ port:\r
+ The pypm object representing the MIDI port on which the Wiimote emits\r
+ instrument:\r
+ The instrument associated with the Wiimote\r
+ cursor:\r
+ The cursor associated with the Wiimote \r
+ '''\r
+ \r
+ self.number = number\r
+ self.portNumber = portNumber\r
+ self.port = port\r
+ self.instrument = instrument\r
+ self.cursor = cursor\r
+ self.numberPlayed = 0\r
+ \r
+ def getNoteOnHexCode(self):\r
+ return (0x90 + self.instrument.channel - 1)\r
+ \r
+ def getAftertouchHexCode(self):\r
+ return (0xA0 + self.instrument.channel - 1)\r
+ \r
+ def getCCHexCode(self):\r
+ return (0xB0 + self.instrument.channel - 1)\r
+ \r
+ def playNote(self, note, velocity):\r
+ noteNumber = self.instrument.getNote(note)\r
+ \r
+ if noteNumber != None :\r
+ noteOnHexCode = self.getNoteOnHexCode()\r
+ CCHexCode = self.getCCHexCode()\r
+ else :\r
+ noteNumber = defaultInstrumentNote\r
+ noteOnHexCode = defaultNoteOnHexCode\r
+ CCHexCode = defaultCCHexCode\r
+ \r
+ self.port.write_short(noteOnHexCode, noteNumber , 127)\r
+ self.port.write_short(CCHexCode, 07, velocity)\r
+\r
+ def playNoteByNoteNumber(self, midiNoteNumber, velocity):\r
+ noteNumber = self.instrument.getNoteByNoteNumber(midiNoteNumber)\r
+ \r
+ if noteNumber != None :\r
+ noteOnHexCode = self.getNoteOnHexCode()\r
+ CCHexCode = self.getCCHexCode()\r
+ else :\r
+ noteNumber = defaultInstrumentNote\r
+ noteOnHexCode = defaultNoteOnHexCode\r
+ CCHexCode = defaultCCHexCode\r
+ \r
+ self.port.write_short(noteOnHexCode, noteNumber , 127)\r
+ self.port.write_short(CCHexCode, 07, velocity)\r
+ \r
+ self.numberPlayed += 1\r
+ \r
+ def stopNote(self, note):\r
+ noteNumber = self.instrument.getNote(note)\r
+ if noteNumber != None :\r
+ noteOnHexCode = self.getNoteOnHexCode()\r
+ else :\r
+ noteNumber = defaultInstrumentNote\r
+ noteOnHexCode = defaultNoteOnHexCode\r
+ \r
+ self.port.write_short(noteOnHexCode, noteNumber, 0)\r
+ \r
+ def stopNoteByNoteNumber(self, midiNoteNumber):\r
+ noteNumber = self.instrument.getNoteByNoteNumber(midiNoteNumber)\r
+ if noteNumber != None :\r
+ noteOnHexCode = self.getNoteOnHexCode()\r
+ else :\r
+ noteNumber = defaultInstrumentNote\r
+ noteOnHexCode = defaultNoteOnHexCode\r
+ \r
+ self.port.write_short(noteOnHexCode, noteNumber, 0)\r
+ \r
+ def allNotesOff(self):\r
+ CCHexCode = self.getCCHexCode()\r
+ self.port.write_short(CCHexCode,123,0)
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 29 mai 2009\r
+\r
+@author: samsam\r
+'''\r
+\r
+import os, sys, math\r
+\r
+import pygame\r
+from pygame.sprite import Sprite\r
+\r
+class WarpingCursor(Sprite):\r
+ '''\r
+ The class for animating the warping cursor\r
+ \r
+ screen:\r
+ the screen on which the WarpingCursor is painted\r
+ images:\r
+ The images constituting the animation of the cursor\r
+ durations:\r
+ The duration of each image in the animation\r
+ centerPosition:\r
+ The Position of the center of the cursor\r
+ _imagePointer:\r
+ A pointer to the current image\r
+ _animationOffset:\r
+ The time elapsed since when the current image should have been displayed\r
+ '''\r
+ screen = None\r
+ images = None\r
+ durations = None\r
+ centerPosition = None\r
+ _imagePointer = None\r
+ _animationOffset = None\r
+ \r
+\r
+ def __init__(self, scr, images, durations, initCenterPosition,flashImage):\r
+ '''\r
+ Constructor\r
+ \r
+ scr:\r
+ the screen on which the WarpingCursor is painted\r
+ images:\r
+ The images constituting the animation of the cursor\r
+ durations:\r
+ The duration of each image in the animation\r
+ initCenterPosition:\r
+ The Position of the center of the cursor at the beginning \r
+ '''\r
+ self.screen = scr\r
+ self.images = images\r
+ self.flashImagePath = flashImage\r
+ self.durations = durations\r
+ self.centerPosition = initCenterPosition\r
+ self.flashLength = 100\r
+ self.flashing = False\r
+ self.image = pygame.image.load(self.images[0]).convert_alpha()\r
+ self._imagePointer = 0\r
+ self._animationOffset = 0\r
+ self._flashTimer = 0 \r
+ \r
+ def update(self, elapsedTime, centerPosition):\r
+ '''\r
+ Update the cursor's look and position\r
+ \r
+ elapsedTime:\r
+ The time passed since the previous update\r
+ centerPosition:\r
+ the new position of the creep\r
+ '''\r
+ self._updateImage(elapsedTime)\r
+ self.centerPosition = centerPosition\r
+ if self.flashing :\r
+ self._flashTimer += elapsedTime\r
+ if self._flashTimer > self.flashLength:\r
+ self.flashing = False\r
+ \r
+ def _updateImage(self, elapsedTime):\r
+ '''\r
+ Update the cursor's image\r
+ \r
+ elapsedTime:\r
+ The time passed since the previous update\r
+ '''\r
+ self._animationOffset += elapsedTime\r
+ \r
+ if self._animationOffset > self.durations[self._imagePointer]:\r
+ #New animation offset is computed first, before updating the pointer\r
+ self._animationOffset -= self.durations[self._imagePointer]\r
+ #point to the next image (restarts from the beginning when it reaches the end)\r
+ self._imagePointer = (self._imagePointer + 1) % len(self.images)\r
+ \r
+ if self.flashing:\r
+ self.image = pygame.image.load(self.flashImagePath).convert_alpha() \r
+ else : \r
+ self.image = pygame.image.load(self.images[self._imagePointer]).convert_alpha()\r
+ \r
+ def flash(self,flashLength = None):\r
+ self._flashTimer = 0\r
+ self.flashing = True\r
+ if flashLength:\r
+ self.flashlength = flashLength\r
+ \r
+ def blit(self,surface):\r
+ '''\r
+ Draw the circle on surface\r
+ '''\r
+ \r
+ newPos = (self.centerPosition[0] - self.image.get_width() / 2, self.centerPosition[1] - self.image.get_height() / 2)\r
+ surface.blit(self.image, newPos)\r
+\r
+def createImageListFromPath(path, imageCount):\r
+ '''\r
+ Create a list of images for a cursor (the concatenation of the original and reversed lists of images).\r
+ Images must be stored as path/imageNumber.png\r
+ \r
+ path:\r
+ The folder where the images for that cursor are stored\r
+ imageCount:\r
+ The number of images in the folder\r
+ '''\r
+ \r
+ tempImages = [''.join([path, '/', str(i), '.png']) for i in range(imageCount)]\r
+ #tempImagesReversed = tempImages[:]\r
+ #tempImagesReversed.reverse()\r
+ #return(tempImages+tempImagesReversed)\r
+ return(tempImages) \r
+\r
+#testing\r
+if __name__ == "__main__" :\r
+ window = pygame.display.set_mode((1680, 1050), pygame.FULLSCREEN)\r
+ screen = pygame.display.get_surface()\r
+ clock = pygame.time.Clock()\r
+ \r
+ images = createImageListFromPath('cursorImages/black',11)\r
+ durations = [50 for i in range(22)]\r
+ position = (400, 300)\r
+ cursor = WarpingCursor(screen, images, durations, position)\r
+ \r
+ while True:\r
+ # Limit frame speed to 50 FPS\r
+ #\r
+ timePassed = clock.tick(50)\r
+ \r
+ for event in pygame.event.get():\r
+ if event.type == pygame.QUIT:\r
+ sys.exit()\r
+ if event.type == pygame.MOUSEMOTION:\r
+ position = event.pos\r
+ \r
+ cursor.update(timePassed, position)\r
+ cursor.blit(screen)\r
+ pygame.display.flip()\r
+ \r
+ \r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg2397"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="cursor.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/sbenven/Desktop/cursorGifs/cursor00.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs2399">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 24 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="48 : 24 : 1"
+ inkscape:persp3d-origin="24 : 16 : 1"
+ id="perspective2405" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7"
+ inkscape:cx="-1.2857143"
+ inkscape:cy="24.45"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1125"
+ inkscape:window-x="1680"
+ inkscape:window-y="25"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <sodipodi:guide
+ orientation="0,1"
+ position="-4.4285714,24"
+ id="guide3229" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="24,-18.571429"
+ id="guide3231" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2402">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path2413"
+ sodipodi:cx="37.57143"
+ sodipodi:cy="36.57143"
+ sodipodi:rx="21.571428"
+ sodipodi:ry="21.571428"
+ d="M 59.142859,36.57143 A 21.571428,21.571428 0 1 1 16.000002,36.57143 A 21.571428,21.571428 0 1 1 59.142859,36.57143 z"
+ transform="matrix(1.0873786,0,0,1.0873787,-16.782941,-15.838419)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;stroke:#000000;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3227"
+ sodipodi:cx="10.928572"
+ sodipodi:cy="13.928572"
+ sodipodi:rx="13.785714"
+ sodipodi:ry="13.785714"
+ d="M 24.714286,13.928572 A 13.785714,13.785714 0 1 1 -2.8571424,13.928572 A 13.785714,13.785714 0 1 1 24.714286,13.928572 z"
+ transform="matrix(1.5759606,0,0,1.567424,6.8484288,2.1217689)"
+ inkscape:export-filename="/home/sbenven/Desktop/cursorGifs/cursor100.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg2397"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="cursorBlue.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/sbenven/Desktop/cursorGifs/cursor00.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs2399">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 24 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="48 : 24 : 1"
+ inkscape:persp3d-origin="24 : 16 : 1"
+ id="perspective2405" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7"
+ inkscape:cx="-31.071429"
+ inkscape:cy="24.45"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1125"
+ inkscape:window-x="1680"
+ inkscape:window-y="25"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <sodipodi:guide
+ orientation="0,1"
+ position="-4.4285714,24"
+ id="guide3229" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="24,-18.571429"
+ id="guide3231" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2402">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#0000ff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path2413"
+ sodipodi:cx="37.57143"
+ sodipodi:cy="36.57143"
+ sodipodi:rx="21.571428"
+ sodipodi:ry="21.571428"
+ d="M 59.142859,36.57143 A 21.571428,21.571428 0 1 1 16.000002,36.57143 A 21.571428,21.571428 0 1 1 59.142859,36.57143 z"
+ transform="matrix(1.0873786,0,0,1.0873787,-16.782941,-15.838419)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;stroke:#0000ff;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3227"
+ sodipodi:cx="10.928572"
+ sodipodi:cy="13.928572"
+ sodipodi:rx="13.785714"
+ sodipodi:ry="13.785714"
+ d="M 24.714286,13.928572 A 13.785714,13.785714 0 1 1 -2.8571424,13.928572 A 13.785714,13.785714 0 1 1 24.714286,13.928572 z"
+ transform="matrix(1.5759606,0,0,1.567424,6.8484293,2.1217687)"
+ inkscape:export-filename="/home/sbenven/Desktop/cursorGifs/cursor100.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg2397"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="cursorGreen.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/sbenven/Desktop/cursorGifs/cursor00.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs2399">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 24 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="48 : 24 : 1"
+ inkscape:persp3d-origin="24 : 16 : 1"
+ id="perspective2405" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7"
+ inkscape:cx="-31.071429"
+ inkscape:cy="24.45"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1125"
+ inkscape:window-x="1680"
+ inkscape:window-y="25"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <sodipodi:guide
+ orientation="0,1"
+ position="-4.4285714,24"
+ id="guide3229" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="24,-18.571429"
+ id="guide3231" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2402">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#00ff00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path2413"
+ sodipodi:cx="37.57143"
+ sodipodi:cy="36.57143"
+ sodipodi:rx="21.571428"
+ sodipodi:ry="21.571428"
+ d="M 59.142859,36.57143 A 21.571428,21.571428 0 1 1 16.000002,36.57143 A 21.571428,21.571428 0 1 1 59.142859,36.57143 z"
+ transform="matrix(1.0873786,0,0,1.0873787,-16.782941,-15.838419)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;stroke:#00ff00;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3227"
+ sodipodi:cx="10.928572"
+ sodipodi:cy="13.928572"
+ sodipodi:rx="13.785714"
+ sodipodi:ry="13.785714"
+ d="M 24.714286,13.928572 A 13.785714,13.785714 0 1 1 -2.8571424,13.928572 A 13.785714,13.785714 0 1 1 24.714286,13.928572 z"
+ transform="matrix(1.5759606,0,0,1.567424,6.8484285,2.1680224)"
+ inkscape:export-filename="/home/sbenven/Desktop/cursorGifs/cursor100.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg2397"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="cursorRed.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/sbenven/Desktop/cursorGifs/cursor00.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs2399">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 24 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="48 : 24 : 1"
+ inkscape:persp3d-origin="24 : 16 : 1"
+ id="perspective2405" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7"
+ inkscape:cx="-31.071429"
+ inkscape:cy="22.307143"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1125"
+ inkscape:window-x="1680"
+ inkscape:window-y="25"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <sodipodi:guide
+ orientation="0,1"
+ position="-4.4285714,24"
+ id="guide3229" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="24,-18.571429"
+ id="guide3231" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2402">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path2413"
+ sodipodi:cx="37.57143"
+ sodipodi:cy="36.57143"
+ sodipodi:rx="21.571428"
+ sodipodi:ry="21.571428"
+ d="M 59.142859,36.57143 A 21.571428,21.571428 0 1 1 16.000002,36.57143 A 21.571428,21.571428 0 1 1 59.142859,36.57143 z"
+ transform="matrix(1.0873786,0,0,1.0873787,-16.782941,-15.838419)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;stroke:#ff0000;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3227"
+ sodipodi:cx="10.928572"
+ sodipodi:cy="13.928572"
+ sodipodi:rx="13.785714"
+ sodipodi:ry="13.785714"
+ d="M 24.714286,13.928572 A 13.785714,13.785714 0 1 1 -2.8571424,13.928572 A 13.785714,13.785714 0 1 1 24.714286,13.928572 z"
+ transform="matrix(1.5759606,0,0,1.567424,6.9912861,2.264626)"
+ inkscape:export-filename="/home/sbenven/Desktop/cursorGifs/cursor100.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ </g>
+</svg>
--- /dev/null
+desktop background desktop.png
+
+input font Vera.ttf 16
+input background input.normal.png
+input color #000000
+input:focus background input.focus.png
+input padding_left 6
+input padding_right 6
+input padding_top 3
+input padding_bottom 3
+
+link font Vera.ttf 24
+link color #0000FF
+link:hover color #FF0000
+link:down color #00FF00
+
+label font Vera.ttf 16
+label color #000000
+
+document font Vera.ttf 16
+document color #000000
+div font Vera.ttf 16
+div color #000000
+
+td font Vera.ttf 16
+td color #000000
+th font Vera.ttf 16
+th color #000000
+
+h1 font Vera.ttf 24
+h1 color #000000
+h2 font Vera.ttf 20
+h2 color #000000
+h3 font Vera.ttf 16
+h3 color #000000
+h4 font Vera.ttf 14
+h4 color #000000
+h5 font Vera.ttf 12
+h5 color #000000
+h6 font Vera.ttf 10
+h6 color #000000
+
+ul font Vera.ttf 16
+ul color #000000
+ol font Vera.ttf 16
+ol color #000000
+li font Vera.ttf 16
+li color #000000
+li padding_left 32
+
+pre font mono 16
+pre color #000000
+code font mono 16
+code color #000000
+
+checkbox off checkbox.off.normal.tga
+checkbox on checkbox.on.normal.tga
+checkbox:hover off checkbox.off.hover.tga
+checkbox:hover on checkbox.on.hover.tga
+checkbox:down off checkbox.off.hover.tga
+checkbox:down on checkbox.on.hover.tga
+
+switch off checkbox.off.normal.tga
+switch on checkbox.on.normal.tga
+switch:hover off checkbox.off.hover.tga
+switch:hover on checkbox.on.hover.tga
+switch:down off checkbox.off.hover.tga
+switch:down on checkbox.on.hover.tga
+
+radio off radio.off.normal.tga
+radio on radio.on.normal.tga
+radio:hover off radio.off.hover.tga
+radio:hover on radio.on.hover.tga
+radio:down off radio.off.hover.tga
+radio:down on radio.on.hover.tga
+
+button background button.normal.tga
+button:hover background button.hover.tga
+button:down background button.down.tga
+button padding_left 8
+button padding_right 8
+button padding_top 1
+button padding_bottom 1
+button.label font Vera.ttf 16
+button.label color #000000
+
+slider background slider.tga
+slider bar slider.bar.normal.tga
+slider:hover bar slider.bar.hover.tga
+slider width 16
+slider height 16
+
+hslider background hslider.tga
+hslider bar hslider.bar.normal.tga
+hslider:hover bar hslider.bar.hover.tga
+hslider:down bar hslider.bar.hover.tga
+hslider width 16
+hslider height 16
+
+vslider background vslider.tga
+vslider bar vslider.bar.normal.tga
+vslider:hover bar vslider.bar.hover.tga
+vslider:down bar vslider.bar.hover.tga
+vslider width 16
+vslider height 16
+
+xhscrollbar height 16
+xhscrollbar background scroller.slide.h.tga
+xhscrollbar bar scroller.slide.bar.normal.tga
+xhscrollbar:hover bar scroller.slide.bar.hover.tga
+
+xvscrollbar width 16
+xvscrollbar background scroller.slide.v.tga
+xvscrollbar bar scroller.slide.bar.normal.tga
+xvscrollbar:hover bar scroller.slide.bar.hover.tga
+
+hscrollbar.slider background hslider.tga
+hscrollbar.slider bar hslider.bar.normal.tga
+hscrollbar.slider:hover bar hslider.bar.hover.tga
+hscrollbar.slider:down bar hslider.bar.hover.tga
+hscrollbar.slider width 16
+hscrollbar.slider height 16
+hscrollbar minus hslider.left.tga
+hscrollbar plus hslider.right.tga
+
+vscrollbar.slider background vslider.tga
+vscrollbar.slider bar vslider.bar.normal.tga
+vscrollbar.slider:hover bar vslider.bar.hover.tga
+vscrollbar.slider:down bar vslider.bar.hover.tga
+vscrollbar.slider width 16
+vscrollbar.slider height 16
+vscrollbar minus vslider.up.tga
+vscrollbar plus vslider.down.tga
+
+
+select.selected background select.selected.normal.tga
+select.selected:hover background select.selected.hover.tga
+select.selected:down background select.selected.down.tga
+select.selected padding_left 4
+select.selected padding_right 4
+select.selected padding_top 1
+select.selected padding_bottom 1
+select.arrow background select.arrow.normal.tga
+select.arrow:hover background select.arrow.hover.tga
+select.arrow:down background select.arrow.down.tga
+select.arrow padding_left 1
+select.arrow padding_right 1
+
+select.options background select.options.png
+select.option background select.option.normal.png
+select.option:hover background select.option.hover.png
+select.option:down background select.option.hover.png
+select.option padding_left 4
+select.option padding_right 4
+select.option padding_top 1
+select.option padding_bottom 1
+#select.option border_top 1
+#select.option border_right 1
+#select.option border_bottom 1
+#select.option border_left 1
+
+select.option.label font Vera.ttf 16
+select.option.label color #000000
+select.options padding_left 1
+select.options padding_right 1
+select.options padding_top 1
+select.options padding_bottom 1
+select arrow select.arrow.png
+
+
+dialog background dialog.bar.png
+xdialog.bar background dialog.bar.png
+dialog.bar padding_left 8
+dialog.bar padding_right 8
+dialog.bar padding_top 2
+dialog.bar padding_bottom 1
+dialog.bar.close image dialog.close.normal.tga
+dialog.bar.close:hover image dialog.close.hover.tga
+dialog.bar.close:down image dialog.close.down.tga
+dialog.main background dialog.png
+dialog.main padding_left 8
+dialog.main padding_right 8
+dialog.main padding_top 4
+dialog.main padding_bottom 4
+
+keysym font Vera.ttf 16
+keysym background input.normal.png
+keysym color #000000
+keysym:focus background input.focus.png
+keysym padding_left 6
+keysym padding_right 6
+keysym padding_top 3
+keysym padding_bottom 3
+
+tool background tool.normal.tga
+tool:hover background tool.hover.tga
+tool:down background tool.down.tga
+tool padding_left 4
+tool padding_right 4
+tool padding_top 1
+tool padding_bottom 1
+tool.label font Vera.ttf 16
+tool.label color #000000
+
+menu background menu.normal.tga
+menu:hover background menu.hover.tga
+menu:down background menu.down.tga
+menu padding_left 6
+menu padding_right 6
+menu padding_top 3
+menu padding_bottom 3
+menu.label font Vera.ttf 16
+menu.label color #000000
+
+menu-open background menu.down.tga
+menu-open:hover background menu.down.tga
+menu-open:down background menu.down.tga
+menu-open padding_left 6
+menu-open padding_right 6
+menu-open padding_top 3
+menu-open padding_bottom 3
+
+menu.options background select.options.png
+menu.option background select.option.normal.png
+menu.option:hover background select.option.hover.png
+menu.option:down background select.option.hover.png
+menu.option padding_left 6
+menu.option padding_right 6
+menu.option padding_top 1
+menu.option padding_bottom 1
+menu.option.label font Vera.ttf 16
+menu.option.label color #000000
+menu.options padding_left 1
+menu.options padding_right 1
+menu.options padding_top 1
+menu.options padding_bottom 1
+menu arrow select.arrow.tga
+
+
+scrollarea.content background #ffffff
+scrollarea.content padding_left 1
+scrollarea.content padding_right 1
+scrollarea.content padding_top 1
+scrollarea.content padding_bottom 1
+
+
+list.item background list.item.normal.png
+list.item:hover background list.item.down.png
+list.item:down background list.item.down.png
+list.item padding_left 4
+list.item padding_right 4
+list.item padding_top 2
+list.item padding_bottom 2
+list.item margin_bottom 1
+list.item align -1
+list.item.label font Vera.ttf 14
+list.item.label color #000000
+
+list background list.png
+list padding_left 1
+list padding_right 1
+list padding_top 1
+list padding_bottom 1
+list.content background #eeeeee
+list.content padding_left 1
+list.content padding_right 1
+list.content padding_top 1
+list.content padding_bottom 1
+
+filedialog.folder image filebrowser.folder.png
+filedialog.label font Vera.ttf 14
+filedialog.label color #000000
+filedialog.title.label font Vera.ttf 16
+filedialog.title.label color #000000
+filedialog.input font Vera.ttf 14
+filedialog.input background input.normal.png
+filedialog.input color #000000
+filedialog.input:focus background input.focus.png
+filedialog.input padding_left 6
+filedialog.input padding_right 6
+filedialog.input padding_top 3
+filedialog.input padding_bottom 3
+
+dialog.title.label font Vera.ttf 16
+dialog.title.label color #000000
+
+
+progressbar background progressbar.tga
+progressbar bar progressbar.bar.tga
+progressbar width 16
+progressbar height 16
--- /dev/null
+import pygame
+from pygame.locals import *
+pygame.display.init()
+pygame.display.set_mode((80,80),32)
+
+def prep(name):
+ fname = name+".png"
+ img = pygame.image.load(fname)
+ w,h = img.get_width()/2,img.get_height()/2
+
+ out = pygame.Surface((w*3,h*3),SWSURFACE|SRCALPHA,32)
+ out.fill((0,0,0,0))
+ out.blit(img.subsurface(0,0,w,h),(0,0))
+ out.blit(img.subsurface(w,0,w,h),(w*2,0))
+ out.blit(img.subsurface(0,h,w,h),(0,h*2))
+ out.blit(img.subsurface(w,h,w,h),(w*2,h*2))
+ for i in range(0,w):
+ img = out.subsurface((w-1,0,1,h*3)).convert_alpha()
+ out.blit(img,(w+i,0))
+ for i in range(0,h):
+ img = out.subsurface((0,h-1,w*3,1)).convert_alpha()
+ out.blit(img,(0,h+i))
+
+ return out,w,h
+
+todo = [
+ ('button.normal','dot.normal',None,3,3,'789456123'),
+ ('button.hover','dot.hover',None,3,3,'789456123'),
+ ('button.down','dot.down',None,3,3,'789456123'),
+
+ ('checkbox.off.normal','box.normal',None,2,2,'7913'),
+ ('checkbox.on.normal','box.down','check',2,2,'7913'),
+ ('checkbox.off.hover','box.hover',None,2,2,'7913'),
+ ('checkbox.on.hover','box.hover','check',2,2,'7913'),
+
+ ('radio.off.normal','dot.normal',None,2,2,'7913'),
+ ('radio.on.normal','dot.down','radio',2,2,'7913'),
+ ('radio.off.hover','dot.hover',None,2,2,'7913'),
+ ('radio.on.hover','dot.hover','radio',2,2,'7913'),
+
+ ('tool.normal','box.normal',None,3,3,'789456123'),
+ ('tool.hover','box.hover',None,3,3,'789456123'),
+ ('tool.down','box.down',None,3,3,'789456123'),
+
+ ('hslider','idot.normal',None,3,3,'789456123'),
+ ('hslider.bar.normal','dot.normal',None,3,3,'789456123'),
+ ('hslider.bar.hover','dot.hover',None,3,3,'789456123'),
+ ('hslider.left','sbox.normal','left',2,2,'7913'),
+ ('hslider.right','sbox.normal','right',2,2,'7913'),
+
+
+ ('vslider','idot.normal',None,3,3,'789456123'),
+ ('vslider.bar.normal','vdot.normal',None,3,3,'789456123'),
+ ('vslider.bar.hover','vdot.hover',None,3,3,'789456123'),
+ ('vslider.up','vsbox.normal','up',2,2,'7913'),
+ ('vslider.down','vsbox.normal','down',2,2,'7913'),
+
+ ('dialog.close.normal','rdot.hover',None,2,2,'7913'),
+ ('dialog.close.hover','rdot.hover','x',2,2,'7913'),
+ ('dialog.close.down','rdot.down','x',2,2,'7913'),
+
+ ('menu.normal','desktop',None,1,1,'7'),
+ ('menu.hover','box.normal',None,3,3,'789456123'),
+ ('menu.down','box.down',None,3,3,'789456123'),
+
+ ('select.selected.normal','box.normal',None,3,3,'788455122'),
+ ('select.selected.hover','box.hover',None,3,3,'788455122'),
+ ('select.selected.down','box.down',None,3,3,'788455122'),
+
+ ('select.arrow.normal','box.hover',None,3,3,'889556223'),
+ ('select.arrow.hover','box.hover',None,3,3,'889556223'),
+ ('select.arrow.down','box.down',None,3,3,'889556223'),
+
+ ('progressbar','sbox.normal',None,3,3,'789456123'),
+ ('progressbar.bar','box.hover',None,3,3,'789456123'),
+ ]
+
+for fname,img,over,ww,hh,s in todo:
+ print fname
+ img,w,h = prep(img)
+ out = pygame.Surface((ww*w,hh*h),SWSURFACE|SRCALPHA,32)
+ out.fill((0,0,0,0))
+ n = 0
+ for y in range(0,hh):
+ for x in range(0,ww):
+ c = int(s[n])
+ xx,yy = (c-1)%3,2-(c-1)/3
+ out.blit(img.subsurface((xx*w,yy*h,w,h)),(x*w,y*h))
+ n += 1
+ if over != None:
+ over = pygame.image.load(over+".png")
+ out.blit(over,(0,0))
+ pygame.image.save(out,fname+".tga")
+
+
+
+
+
--- /dev/null
+dot and box.xcf:
+
+color -170
+
+.down
+.hover +64 brightness
+.normal, grayscale +127 brightness, +48 contrast
+
--- /dev/null
+desktop background desktop.png
+
+input font Vera.ttf 16
+input background input.normal.png
+input color #000000
+input:focus background input.focus.png
+input padding_left 6
+input padding_right 6
+input padding_top 3
+input padding_bottom 3
+
+label font Vera.ttf 16
+label color #000000
+
+document font Vera.ttf 16
+document color #000000
+div font Vera.ttf 16
+div color #000000
+
+td font Vera.ttf 16
+td color #000000
+th font Vera.ttf 16
+th color #000000
+
+h1 font Vera.ttf 24
+h1 color #000000
+h2 font Vera.ttf 20
+h2 color #000000
+h3 font Vera.ttf 16
+h3 color #000000
+h4 font Vera.ttf 14
+h4 color #000000
+h5 font Vera.ttf 12
+h5 color #000000
+h6 font Vera.ttf 10
+h6 color #000000
+
+ul font Vera.ttf 16
+ul color #000000
+ol font Vera.ttf 16
+ol color #000000
+li font Vera.ttf 16
+li color #000000
+li padding_left 32
+
+pre font mono 16
+pre color #000000
+code font mono 16
+code color #000000
+
+checkbox off checkbox.off.normal.png
+checkbox on checkbox.on.normal.png
+checkbox:down off checkbox.off.down.png
+checkbox:down on checkbox.on.down.png
+
+switch off checkbox.off.normal.png
+switch on checkbox.on.normal.png
+switch:down off checkbox.off.down.png
+switch:down on checkbox.on.down.png
+
+radio off radio.off.normal.png
+radio on radio.on.normal.png
+radio:down off radio.off.down.png
+radio:down on radio.on.down.png
+
+button background button.normal.png
+button:down background button.down.png
+button padding_left 8
+button padding_right 8
+button padding_top 1
+button padding_bottom 1
+button.label font Vera.ttf 16
+button.label color #000000
+
+slider background slider.png
+slider bar slider.bar.normal.png
+slider:down bar slider.bar.down.png
+slider width 16
+slider height 16
+
+hslider background slider.png
+hslider bar slider.bar.normal.png
+hslider:down bar slider.bar.down.png
+hslider width 16
+hslider height 16
+
+vslider background slider.png
+vslider bar slider.bar.normal.png
+vslider:down bar slider.bar.down.png
+vslider width 16
+vslider height 16
+
+select.selected background select.selected.normal.png
+#select.selected:down background select.selected.down.png
+select.selected padding_left 4
+select.selected padding_right 4
+select.selected padding_top 1
+select.selected padding_bottom 1
+select.arrow background select.arrow.normal.png
+select.arrow:down background select.arrow.down.png
+select.arrow padding_left 1
+select.arrow padding_right 1
+
+select.options background select.options.png
+select.option background select.option.normal.png
+#select.option:hover background select.option.hover.png
+select.option padding_left 4
+select.option padding_right 4
+select.option padding_top 1
+select.option padding_bottom 1
+#select.option border_top 1
+#select.option border_right 1
+#select.option border_bottom 1
+#select.option border_left 1
+
+select.option.label font Vera.ttf 16
+select.option.label color #000000
+select.options padding_left 1
+select.options padding_right 1
+select.options padding_top 1
+select.options padding_bottom 1
+select arrow select.arrow.png
+
+
+dialog.bar background dialog.bar.png
+dialog.bar padding_left 8
+dialog.bar padding_right 8
+dialog.bar padding_top 2
+dialog.bar padding_bottom 1
+dialog.bar.close image dialog.close.normal.png
+dialog.bar.close:down image dialog.close.down.png
+dialog.main background dialog.png
+dialog.main padding_left 8
+dialog.main padding_right 8
+dialog.main padding_top 4
+dialog.main padding_bottom 4
+
+keysym font helvetica 16
+keysym background input.normal.png
+keysym color #000000
+keysym:focus background input.focus.png
+keysym padding_left 6
+keysym padding_right 6
+keysym padding_top 3
+keysym padding_bottom 3
+
+tool background tool.normal.png
+tool:down background tool.down.png
+tool padding_left 4
+tool padding_right 4
+tool padding_top 1
+tool padding_bottom 1
+tool.label font Vera.ttf 16
+tool.label color #000000
+
+menu background menu.normal.png
+menu:hover background menu.hover.png
+menu:down background menu.down.png
+menu padding_left 6
+menu padding_right 6
+menu padding_top 3
+menu padding_bottom 3
+menu.label font Vera.ttf 16
+menu.label color #000000
+
+menu-open background menu.down.png
+menu-open:down background menu.down.png
+menu-open padding_left 6
+menu-open padding_right 6
+menu-open padding_top 3
+menu-open padding_bottom 3
+
+menu.options background select.options.png
+menu.option background menu.option.normal.png
+menu.option:hover background menu.option.hover.png
+#menu.option.label color #FF0000
+menu.option padding_left 6
+menu.option padding_right 6
+menu.option padding_top 1
+menu.option padding_bottom 1
+menu.option.label font Vera.ttf 16
+menu.option.label color #000000
+menu.options padding_left 1
+menu.options padding_right 1
+menu.options padding_top 1
+menu.options padding_bottom 1
+menu arrow select.arrow.png
+
+
+scrollarea.content background #ffffff
+scrollarea.content padding_left 1
+scrollarea.content padding_right 1
+scrollarea.content padding_top 1
+scrollarea.content padding_bottom 1
+hscrollbar height 15
+##hscrollbar background scroller.slide.h.png
+hscrollbar background slider.png
+##hscrollbar bar scroller.slide.bar.normal.png
+hscrollbar bar slider.bar.normal.png
+##hscrollbar:down bar scroller.slide.bar.down.png
+vscrollbar width 15
+##vscrollbar background scroller.slide.v.png
+vscrollbar background slider.png
+##vscrollbar bar scroller.slide.bar.normal.png
+vscrollbar bar slider.bar.normal.png
+##vscrollbar:down bar scroller.slide.bar.down.png
+
+list.item background list.item.normal.png
+#list.item:down background list.item.down.png
+list.item padding_left 4
+list.item padding_right 4
+list.item padding_top 2
+list.item padding_bottom 2
+list.item margin_bottom 1
+list.item align -1
+list.item.label font Vera.ttf 14
+list.item.label color #000000
+
+list background list.png
+list padding_left 1
+list padding_right 1
+list padding_top 1
+list padding_bottom 1
+list.content background #eeeeee
+list.content padding_left 1
+list.content padding_right 1
+list.content padding_top 1
+list.content padding_bottom 1
+
+filedialog.folder image filebrowser.folder.png
+filedialog.label font Vera.ttf 14
+filedialog.label color #000000
+filedialog.title.label font Vera.ttf 16
+filedialog.title.label color #000000
+filedialog.input font Vera.ttf 14
+filedialog.input background input.normal.png
+filedialog.input color #000000
+filedialog.input:focus background input.focus.png
+filedialog.input padding_left 6
+filedialog.input padding_right 6
+filedialog.input padding_top 3
+filedialog.input padding_bottom 3
+
+
--- /dev/null
+tool.draw image icons48.draw.tga
+tool.pixel image icons48.pixel.tga
+tool.line image icons48.line.tga
+tool.fill image icons48.fill.tga
+
+tool.select image icons48.select.tga
+tool.eraser image icons48.eraser.tga
+
+tool.tile image icons48.tile.tga
+tool.code image icons48.code.tga
+tool.bkgr image icons48.bkgr.tga
--- /dev/null
+# odict.py
+# An Ordered Dictionary object
+# Copyright (C) 2005 Nicola Larosa, Michael Foord
+# E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk
+
+# This software is licensed under the terms of the BSD license.
+# http://www.voidspace.org.uk/python/license.shtml
+# Basically you're free to copy, modify, distribute and relicense it,
+# So long as you keep a copy of the license with it.
+
+# Documentation at http://www.voidspace.org.uk/python/odict.html
+# For information about bugfixes, updates and support, please join the
+# Pythonutils mailing list:
+# http://groups.google.com/group/pythonutils/
+# Comments, suggestions and bug reports welcome.
+
+"""A dict that keeps keys in insertion order"""
+from __future__ import generators
+
+__author__ = ('Nicola Larosa <nico-NoSp@m-tekNico.net>,'
+ 'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>')
+
+__docformat__ = "restructuredtext en"
+
+__revision__ = '$Id: odict.py 129 2005-09-12 18:15:28Z teknico $'
+
+__version__ = '0.2.2'
+
+__all__ = ['OrderedDict', 'SequenceOrderedDict']
+
+import sys
+INTP_VER = sys.version_info[:2]
+if INTP_VER < (2, 2):
+ raise RuntimeError("Python v.2.2 or later required")
+
+import types, warnings
+
+class OrderedDict(dict):
+ """
+ A class of dictionary that keeps the insertion order of keys.
+
+ All appropriate methods return keys, items, or values in an ordered way.
+
+ All normal dictionary methods are available. Update and comparison is
+ restricted to other OrderedDict objects.
+
+ Various sequence methods are available, including the ability to explicitly
+ mutate the key ordering.
+
+ __contains__ tests:
+
+ >>> d = OrderedDict(((1, 3),))
+ >>> 1 in d
+ 1
+ >>> 4 in d
+ 0
+
+ __getitem__ tests:
+
+ >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2]
+ 1
+ >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4]
+ Traceback (most recent call last):
+ KeyError: 4
+
+ __len__ tests:
+
+ >>> len(OrderedDict())
+ 0
+ >>> len(OrderedDict(((1, 3), (3, 2), (2, 1))))
+ 3
+
+ get tests:
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.get(1)
+ 3
+ >>> d.get(4) is None
+ 1
+ >>> d.get(4, 5)
+ 5
+ >>> d
+ OrderedDict([(1, 3), (3, 2), (2, 1)])
+
+ has_key tests:
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.has_key(1)
+ 1
+ >>> d.has_key(4)
+ 0
+ """
+
+ def __init__(self, init_val=(), strict=False):
+ """
+ Create a new ordered dictionary. Cannot init from a normal dict,
+ nor from kwargs, since items order is undefined in those cases.
+
+ If the ``strict`` keyword argument is ``True`` (``False`` is the
+ default) then when doing slice assignment - the ``OrderedDict`` you are
+ assigning from *must not* contain any keys in the remaining dict.
+
+ >>> OrderedDict()
+ OrderedDict([])
+ >>> OrderedDict({1: 1})
+ Traceback (most recent call last):
+ TypeError: undefined order, cannot get items from dict
+ >>> OrderedDict({1: 1}.items())
+ OrderedDict([(1, 1)])
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d
+ OrderedDict([(1, 3), (3, 2), (2, 1)])
+ >>> OrderedDict(d)
+ OrderedDict([(1, 3), (3, 2), (2, 1)])
+ """
+ self.strict = strict
+ dict.__init__(self)
+ if isinstance(init_val, OrderedDict):
+ self._sequence = init_val.keys()
+ dict.update(self, init_val)
+ elif isinstance(init_val, dict):
+ # we lose compatibility with other ordered dict types this way
+ raise TypeError('undefined order, cannot get items from dict')
+ else:
+ self._sequence = []
+ self.update(init_val)
+
+### Special methods ###
+
+ def __delitem__(self, key):
+ """
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> del d[3]
+ >>> d
+ OrderedDict([(1, 3), (2, 1)])
+ >>> del d[3]
+ Traceback (most recent call last):
+ KeyError: 3
+ >>> d[3] = 2
+ >>> d
+ OrderedDict([(1, 3), (2, 1), (3, 2)])
+ >>> del d[0:1]
+ >>> d
+ OrderedDict([(2, 1), (3, 2)])
+ """
+ if isinstance(key, types.SliceType):
+ # FIXME: efficiency?
+ keys = self._sequence[key]
+ for entry in keys:
+ dict.__delitem__(self, entry)
+ del self._sequence[key]
+ else:
+ # do the dict.__delitem__ *first* as it raises
+ # the more appropriate error
+ dict.__delitem__(self, key)
+ self._sequence.remove(key)
+
+ def __eq__(self, other):
+ """
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d == OrderedDict(d)
+ True
+ >>> d == OrderedDict(((1, 3), (2, 1), (3, 2)))
+ False
+ >>> d == OrderedDict(((1, 0), (3, 2), (2, 1)))
+ False
+ >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
+ False
+ >>> d == dict(d)
+ False
+ >>> d == False
+ False
+ """
+ if isinstance(other, OrderedDict):
+ # FIXME: efficiency?
+ # Generate both item lists for each compare
+ return (self.items() == other.items())
+ else:
+ return False
+
+ def __lt__(self, other):
+ """
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
+ >>> c < d
+ True
+ >>> d < c
+ False
+ >>> d < dict(c)
+ Traceback (most recent call last):
+ TypeError: Can only compare with other OrderedDicts
+ """
+ if not isinstance(other, OrderedDict):
+ raise TypeError('Can only compare with other OrderedDicts')
+ # FIXME: efficiency?
+ # Generate both item lists for each compare
+ return (self.items() < other.items())
+
+ def __le__(self, other):
+ """
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
+ >>> e = OrderedDict(d)
+ >>> c <= d
+ True
+ >>> d <= c
+ False
+ >>> d <= dict(c)
+ Traceback (most recent call last):
+ TypeError: Can only compare with other OrderedDicts
+ >>> d <= e
+ True
+ """
+ if not isinstance(other, OrderedDict):
+ raise TypeError('Can only compare with other OrderedDicts')
+ # FIXME: efficiency?
+ # Generate both item lists for each compare
+ return (self.items() <= other.items())
+
+ def __ne__(self, other):
+ """
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d != OrderedDict(d)
+ False
+ >>> d != OrderedDict(((1, 3), (2, 1), (3, 2)))
+ True
+ >>> d != OrderedDict(((1, 0), (3, 2), (2, 1)))
+ True
+ >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
+ False
+ >>> d != dict(d)
+ True
+ >>> d != False
+ True
+ """
+ if isinstance(other, OrderedDict):
+ # FIXME: efficiency?
+ # Generate both item lists for each compare
+ return not (self.items() == other.items())
+ else:
+ return True
+
+ def __gt__(self, other):
+ """
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
+ >>> d > c
+ True
+ >>> c > d
+ False
+ >>> d > dict(c)
+ Traceback (most recent call last):
+ TypeError: Can only compare with other OrderedDicts
+ """
+ if not isinstance(other, OrderedDict):
+ raise TypeError('Can only compare with other OrderedDicts')
+ # FIXME: efficiency?
+ # Generate both item lists for each compare
+ return (self.items() > other.items())
+
+ def __ge__(self, other):
+ """
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
+ >>> e = OrderedDict(d)
+ >>> c >= d
+ False
+ >>> d >= c
+ True
+ >>> d >= dict(c)
+ Traceback (most recent call last):
+ TypeError: Can only compare with other OrderedDicts
+ >>> e >= d
+ True
+ """
+ if not isinstance(other, OrderedDict):
+ raise TypeError('Can only compare with other OrderedDicts')
+ # FIXME: efficiency?
+ # Generate both item lists for each compare
+ return (self.items() >= other.items())
+
+ def __repr__(self):
+ """
+ Used for __repr__ and __str__
+
+ >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
+ >>> r1
+ "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])"
+ >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
+ >>> r2
+ "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])"
+ >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
+ True
+ >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
+ True
+ """
+ return '%s([%s])' % (self.__class__.__name__, ', '.join(
+ ['(%r, %r)' % (key, self[key]) for key in self._sequence]))
+
+ def __setitem__(self, key, val):
+ """
+ Allows slice assignment, so long as the slice is an OrderedDict
+ >>> d = OrderedDict()
+ >>> d['a'] = 'b'
+ >>> d['b'] = 'a'
+ >>> d[3] = 12
+ >>> d
+ OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)])
+ >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4)))
+ >>> d
+ OrderedDict([(1, 2), (2, 3), (3, 4)])
+ >>> d[::2] = OrderedDict(((7, 8), (9, 10)))
+ >>> d
+ OrderedDict([(7, 8), (2, 3), (9, 10)])
+ >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)))
+ >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
+ >>> d
+ OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
+ >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True)
+ >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
+ >>> d
+ OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
+
+ >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True)
+ >>> a[3] = 4
+ >>> a
+ OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> a
+ OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)])
+ Traceback (most recent call last):
+ ValueError: slice assignment must be from unique keys
+ >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)))
+ >>> a[3] = 4
+ >>> a
+ OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> a
+ OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> a
+ OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> a
+ OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)])
+
+ >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> d[:1] = 3
+ Traceback (most recent call last):
+ TypeError: slice assignment requires an OrderedDict
+
+ >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+ >>> d[:1] = OrderedDict([(9, 8)])
+ >>> d
+ OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)])
+ """
+ if isinstance(key, types.SliceType):
+ if not isinstance(val, OrderedDict):
+ # FIXME: allow a list of tuples?
+ raise TypeError('slice assignment requires an OrderedDict')
+ keys = self._sequence[key]
+ # NOTE: Could use ``range(*key.indices(len(self._sequence)))``
+ indexes = range(len(self._sequence))[key]
+ if key.step is None:
+ # NOTE: new slice may not be the same size as the one being
+ # overwritten !
+ # NOTE: What is the algorithm for an impossible slice?
+ # e.g. d[5:3]
+ pos = key.start or 0
+ del self[key]
+ newkeys = val.keys()
+ for k in newkeys:
+ if k in self:
+ if self.strict:
+ raise ValueError('slice assignment must be from '
+ 'unique keys')
+ else:
+ # NOTE: This removes duplicate keys *first*
+ # so start position might have changed?
+ del self[k]
+ self._sequence = (self._sequence[:pos] + newkeys +
+ self._sequence[pos:])
+ dict.update(self, val)
+ else:
+ # extended slice - length of new slice must be the same
+ # as the one being replaced
+ if len(keys) != len(val):
+ raise ValueError('attempt to assign sequence of size %s '
+ 'to extended slice of size %s' % (len(val), len(keys)))
+ # FIXME: efficiency?
+ del self[key]
+ item_list = zip(indexes, val.items())
+ # smallest indexes first - higher indexes not guaranteed to
+ # exist
+ item_list.sort()
+ for pos, (newkey, newval) in item_list:
+ if self.strict and newkey in self:
+ raise ValueError('slice assignment must be from unique'
+ ' keys')
+ self.insert(pos, newkey, newval)
+ else:
+ if key not in self:
+ self._sequence.append(key)
+ dict.__setitem__(self, key, val)
+
+ def __getitem__(self, key):
+ """
+ Allows slicing. Returns an OrderedDict if you slice.
+ >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)])
+ >>> b[::-1]
+ OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)])
+ >>> b[2:5]
+ OrderedDict([(5, 2), (4, 3), (3, 4)])
+ >>> type(b[2:4])
+ <class '__main__.OrderedDict'>
+ """
+ if isinstance(key, types.SliceType):
+ # FIXME: does this raise the error we want?
+ keys = self._sequence[key]
+ # FIXME: efficiency?
+ return OrderedDict([(entry, self[entry]) for entry in keys])
+ else:
+ return dict.__getitem__(self, key)
+
+ __str__ = __repr__
+
+ def __setattr__(self, name, value):
+ """
+ Implemented so that accesses to ``sequence`` raise a warning and are
+ diverted to the new ``setkeys`` method.
+ """
+ if name == 'sequence':
+ warnings.warn('Use of the sequence attribute is deprecated.'
+ ' Use the keys method instead.', DeprecationWarning)
+ # NOTE: doesn't return anything
+ self.setkeys(value)
+ else:
+ # FIXME: do we want to allow arbitrary setting of attributes?
+ # Or do we want to manage it?
+ object.__setattr__(self, name, value)
+
+ def __getattr__(self, name):
+ """
+ Implemented so that access to ``sequence`` raises a warning.
+
+ >>> d = OrderedDict()
+ >>> d.sequence
+ []
+ """
+ if name == 'sequence':
+ warnings.warn('Use of the sequence attribute is deprecated.'
+ ' Use the keys method instead.', DeprecationWarning)
+ # NOTE: Still (currently) returns a direct reference. Need to
+ # because code that uses sequence will expect to be able to
+ # mutate it in place.
+ return self._sequence
+ else:
+ # raise the appropriate error
+ raise AttributeError("OrderedDict has no '%s' attribute" % name)
+
+ def __deepcopy__(self, memo):
+ """
+ To allow deepcopy to work with OrderedDict.
+
+ >>> from copy import deepcopy
+ >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)])
+ >>> a['test'] = {}
+ >>> b = deepcopy(a)
+ >>> b == a
+ True
+ >>> b is a
+ False
+ >>> a['test'] is b['test']
+ False
+ """
+ from copy import deepcopy
+ return self.__class__(deepcopy(self.items(), memo), self.strict)
+
+
+### Read-only methods ###
+
+ def copy(self):
+ """
+ >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy()
+ OrderedDict([(1, 3), (3, 2), (2, 1)])
+ """
+ return OrderedDict(self)
+
+ def items(self):
+ """
+ ``items`` returns a list of tuples representing all the
+ ``(key, value)`` pairs in the dictionary.
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.items()
+ [(1, 3), (3, 2), (2, 1)]
+ >>> d.clear()
+ >>> d.items()
+ []
+ """
+ return zip(self._sequence, self.values())
+
+ def keys(self):
+ """
+ Return a list of keys in the ``OrderedDict``.
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.keys()
+ [1, 3, 2]
+ """
+ return self._sequence[:]
+
+ def values(self, values=None):
+ """
+ Return a list of all the values in the OrderedDict.
+
+ Optionally you can pass in a list of values, which will replace the
+ current list. The value list must be the same len as the OrderedDict.
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.values()
+ [3, 2, 1]
+ """
+ return [self[key] for key in self._sequence]
+
+ def iteritems(self):
+ """
+ >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems()
+ >>> ii.next()
+ (1, 3)
+ >>> ii.next()
+ (3, 2)
+ >>> ii.next()
+ (2, 1)
+ >>> ii.next()
+ Traceback (most recent call last):
+ StopIteration
+ """
+ def make_iter(self=self):
+ keys = self.iterkeys()
+ while True:
+ key = keys.next()
+ yield (key, self[key])
+ return make_iter()
+
+ def iterkeys(self):
+ """
+ >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys()
+ >>> ii.next()
+ 1
+ >>> ii.next()
+ 3
+ >>> ii.next()
+ 2
+ >>> ii.next()
+ Traceback (most recent call last):
+ StopIteration
+ """
+ return iter(self._sequence)
+
+ __iter__ = iterkeys
+
+ def itervalues(self):
+ """
+ >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues()
+ >>> iv.next()
+ 3
+ >>> iv.next()
+ 2
+ >>> iv.next()
+ 1
+ >>> iv.next()
+ Traceback (most recent call last):
+ StopIteration
+ """
+ def make_iter(self=self):
+ keys = self.iterkeys()
+ while True:
+ yield self[keys.next()]
+ return make_iter()
+
+### Read-write methods ###
+
+ def clear(self):
+ """
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.clear()
+ >>> d
+ OrderedDict([])
+ """
+ dict.clear(self)
+ self._sequence = []
+
+ def pop(self, key, *args):
+ """
+ No dict.pop in Python 2.2, gotta reimplement it
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.pop(3)
+ 2
+ >>> d
+ OrderedDict([(1, 3), (2, 1)])
+ >>> d.pop(4)
+ Traceback (most recent call last):
+ KeyError: 4
+ >>> d.pop(4, 0)
+ 0
+ >>> d.pop(4, 0, 1)
+ Traceback (most recent call last):
+ TypeError: pop expected at most 2 arguments, got 3
+ """
+ if len(args) > 1:
+ raise TypeError, ('pop expected at most 2 arguments, got %s' %
+ (len(args) + 1))
+ if key in self:
+ val = self[key]
+ del self[key]
+ else:
+ try:
+ val = args[0]
+ except IndexError:
+ raise KeyError(key)
+ return val
+
+ def popitem(self, i=-1):
+ """
+ Delete and return an item specified by index, not a random one as in
+ dict. The index is -1 by default (the last item).
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.popitem()
+ (2, 1)
+ >>> d
+ OrderedDict([(1, 3), (3, 2)])
+ >>> d.popitem(0)
+ (1, 3)
+ >>> OrderedDict().popitem()
+ Traceback (most recent call last):
+ KeyError: 'popitem(): dictionary is empty'
+ >>> d.popitem(2)
+ Traceback (most recent call last):
+ IndexError: popitem(): index 2 not valid
+ """
+ if not self._sequence:
+ raise KeyError('popitem(): dictionary is empty')
+ try:
+ key = self._sequence[i]
+ except IndexError:
+ raise IndexError('popitem(): index %s not valid' % i)
+ return (key, self.pop(key))
+
+ def setdefault(self, key, defval = None):
+ """
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.setdefault(1)
+ 3
+ >>> d.setdefault(4) is None
+ True
+ >>> d
+ OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)])
+ >>> d.setdefault(5, 0)
+ 0
+ >>> d
+ OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)])
+ """
+ if key in self:
+ return self[key]
+ else:
+ self[key] = defval
+ return defval
+
+ def update(self, from_od):
+ """
+ Update from another OrderedDict or sequence of (key, value) pairs
+
+ >>> d = OrderedDict(((1, 0), (0, 1)))
+ >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1))))
+ >>> d
+ OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)])
+ >>> d.update({4: 4})
+ Traceback (most recent call last):
+ TypeError: undefined order, cannot get items from dict
+ >>> d.update((4, 4))
+ Traceback (most recent call last):
+ TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence
+ """
+ if isinstance(from_od, OrderedDict):
+ for key, val in from_od.items():
+ self[key] = val
+ elif isinstance(from_od, dict):
+ # we lose compatibility with other ordered dict types this way
+ raise TypeError('undefined order, cannot get items from dict')
+ else:
+ # FIXME: efficiency?
+ # sequence of 2-item sequences, or error
+ for item in from_od:
+ try:
+ key, val = item
+ except TypeError:
+ raise TypeError('cannot convert dictionary update'
+ ' sequence element "%s" to a 2-item sequence' % item)
+ self[key] = val
+
+ def rename(self, old_key, new_key):
+ """
+ Rename the key for a given value, without modifying sequence order.
+
+ For the case where new_key already exists this raise an exception,
+ since if new_key exists, it is ambiguous as to what happens to the
+ associated values, and the position of new_key in the sequence.
+
+ >>> od = OrderedDict()
+ >>> od['a'] = 1
+ >>> od['b'] = 2
+ >>> od.items()
+ [('a', 1), ('b', 2)]
+ >>> od.rename('b', 'c')
+ >>> od.items()
+ [('a', 1), ('c', 2)]
+ >>> od.rename('c', 'a')
+ Traceback (most recent call last):
+ ValueError: New key already exists: 'a'
+ >>> od.rename('d', 'b')
+ Traceback (most recent call last):
+ KeyError: 'd'
+ """
+ if new_key == old_key:
+ # no-op
+ return
+ if new_key in self:
+ raise ValueError("New key already exists: %r" % new_key)
+ # rename sequence entry
+ value = self[old_key]
+ old_idx = self._sequence.index(old_key)
+ self._sequence[old_idx] = new_key
+ # rename internal dict entry
+ dict.__delitem__(self, old_key)
+ dict.__setitem__(self, new_key, value)
+
+ def setitems(self, items):
+ """
+ This method allows you to set the items in the dict.
+
+ It takes a list of tuples - of the same sort returned by the ``items``
+ method.
+
+ >>> d = OrderedDict()
+ >>> d.setitems(((3, 1), (2, 3), (1, 2)))
+ >>> d
+ OrderedDict([(3, 1), (2, 3), (1, 2)])
+ """
+ self.clear()
+ # FIXME: this allows you to pass in an OrderedDict as well :-)
+ self.update(items)
+
+ def setkeys(self, keys):
+ """
+ ``setkeys`` all ows you to pass in a new list of keys which will
+ replace the current set. This must contain the same set of keys, but
+ need not be in the same order.
+
+ If you pass in new keys that don't match, a ``KeyError`` will be
+ raised.
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.keys()
+ [1, 3, 2]
+ >>> d.setkeys((1, 2, 3))
+ >>> d
+ OrderedDict([(1, 3), (2, 1), (3, 2)])
+ >>> d.setkeys(['a', 'b', 'c'])
+ Traceback (most recent call last):
+ KeyError: 'Keylist is not the same as current keylist.'
+ """
+ # FIXME: Efficiency? (use set for Python 2.4 :-)
+ # NOTE: list(keys) rather than keys[:] because keys[:] returns
+ # a tuple, if keys is a tuple.
+ kcopy = list(keys)
+ kcopy.sort()
+ self._sequence.sort()
+ if kcopy != self._sequence:
+ raise KeyError('Keylist is not the same as current keylist.')
+ # NOTE: This makes the _sequence attribute a new object, instead
+ # of changing it in place.
+ # FIXME: efficiency?
+ self._sequence = list(keys)
+
+ def setvalues(self, values):
+ """
+ You can pass in a list of values, which will replace the
+ current list. The value list must be the same len as the OrderedDict.
+
+ (Or a ``ValueError`` is raised.)
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.setvalues((1, 2, 3))
+ >>> d
+ OrderedDict([(1, 1), (3, 2), (2, 3)])
+ >>> d.setvalues([6])
+ Traceback (most recent call last):
+ ValueError: Value list is not the same length as the OrderedDict.
+ """
+ if len(values) != len(self):
+ # FIXME: correct error to raise?
+ raise ValueError('Value list is not the same length as the '
+ 'OrderedDict.')
+ self.update(zip(self, values))
+
+### Sequence Methods ###
+
+ def index(self, key):
+ """
+ Return the position of the specified key in the OrderedDict.
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.index(3)
+ 1
+ >>> d.index(4)
+ Traceback (most recent call last):
+ ValueError: list.index(x): x not in list
+ """
+ return self._sequence.index(key)
+
+ def insert(self, index, key, value):
+ """
+ Takes ``index``, ``key``, and ``value`` as arguments.
+
+ Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in
+ the OrderedDict.
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.insert(0, 4, 0)
+ >>> d
+ OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)])
+ >>> d.insert(0, 2, 1)
+ >>> d
+ OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)])
+ >>> d.insert(8, 8, 1)
+ >>> d
+ OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)])
+ """
+ if key in self:
+ # FIXME: efficiency?
+ del self[key]
+ self._sequence.insert(index, key)
+ dict.__setitem__(self, key, value)
+
+ def reverse(self):
+ """
+ Reverse the order of the OrderedDict.
+
+ >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+ >>> d.reverse()
+ >>> d
+ OrderedDict([(2, 1), (3, 2), (1, 3)])
+ """
+ self._sequence.reverse()
+
+ def sort(self, *args, **kwargs):
+ """
+ Sort the key order in the OrderedDict.
+
+ This method takes the same arguments as the ``list.sort`` method on
+ your version of Python.
+
+ >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4)))
+ >>> d.sort()
+ >>> d
+ OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)])
+ """
+ self._sequence.sort(*args, **kwargs)
+
+class Keys(object):
+ # FIXME: should this object be a subclass of list?
+ """
+ Custom object for accessing the keys of an OrderedDict.
+
+ Can be called like the normal ``OrderedDict.keys`` method, but also
+ supports indexing and sequence methods.
+ """
+
+ def __init__(self, main):
+ self._main = main
+
+ def __call__(self):
+ """Pretend to be the keys method."""
+ return self._main._keys()
+
+ def __getitem__(self, index):
+ """Fetch the key at position i."""
+ # NOTE: this automatically supports slicing :-)
+ return self._main._sequence[index]
+
+ def __setitem__(self, index, name):
+ """
+ You cannot assign to keys, but you can do slice assignment to re-order
+ them.
+
+ You can only do slice assignment if the new set of keys is a reordering
+ of the original set.
+ """
+ if isinstance(index, types.SliceType):
+ # FIXME: efficiency?
+ # check length is the same
+ indexes = range(len(self._main._sequence))[index]
+ if len(indexes) != len(name):
+ raise ValueError('attempt to assign sequence of size %s '
+ 'to slice of size %s' % (len(name), len(indexes)))
+ # check they are the same keys
+ # FIXME: Use set
+ old_keys = self._main._sequence[index]
+ new_keys = list(name)
+ old_keys.sort()
+ new_keys.sort()
+ if old_keys != new_keys:
+ raise KeyError('Keylist is not the same as current keylist.')
+ orig_vals = [self._main[k] for k in name]
+ del self._main[index]
+ vals = zip(indexes, name, orig_vals)
+ vals.sort()
+ for i, k, v in vals:
+ if self._main.strict and k in self._main:
+ raise ValueError('slice assignment must be from '
+ 'unique keys')
+ self._main.insert(i, k, v)
+ else:
+ raise ValueError('Cannot assign to keys')
+
+ ### following methods pinched from UserList and adapted ###
+ def __repr__(self): return repr(self._main._sequence)
+
+ # FIXME: do we need to check if we are comparing with another ``Keys``
+ # object? (like the __cast method of UserList)
+ def __lt__(self, other): return self._main._sequence < other
+ def __le__(self, other): return self._main._sequence <= other
+ def __eq__(self, other): return self._main._sequence == other
+ def __ne__(self, other): return self._main._sequence != other
+ def __gt__(self, other): return self._main._sequence > other
+ def __ge__(self, other): return self._main._sequence >= other
+ # FIXME: do we need __cmp__ as well as rich comparisons?
+ def __cmp__(self, other): return cmp(self._main._sequence, other)
+
+ def __contains__(self, item): return item in self._main._sequence
+ def __len__(self): return len(self._main._sequence)
+ def __iter__(self): return self._main.iterkeys()
+ def count(self, item): return self._main._sequence.count(item)
+ def index(self, item, *args): return self._main._sequence.index(item, *args)
+ def reverse(self): self._main._sequence.reverse()
+ def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds)
+ def __mul__(self, n): return self._main._sequence*n
+ __rmul__ = __mul__
+ def __add__(self, other): return self._main._sequence + other
+ def __radd__(self, other): return other + self._main._sequence
+
+ ## following methods not implemented for keys ##
+ def __delitem__(self, i): raise TypeError('Can\'t delete items from keys')
+ def __iadd__(self, other): raise TypeError('Can\'t add in place to keys')
+ def __imul__(self, n): raise TypeError('Can\'t multiply keys in place')
+ def append(self, item): raise TypeError('Can\'t append items to keys')
+ def insert(self, i, item): raise TypeError('Can\'t insert items into keys')
+ def pop(self, i=-1): raise TypeError('Can\'t pop items from keys')
+ def remove(self, item): raise TypeError('Can\'t remove items from keys')
+ def extend(self, other): raise TypeError('Can\'t extend keys')
+
+class Items(object):
+ """
+ Custom object for accessing the items of an OrderedDict.
+
+ Can be called like the normal ``OrderedDict.items`` method, but also
+ supports indexing and sequence methods.
+ """
+
+ def __init__(self, main):
+ self._main = main
+
+ def __call__(self):
+ """Pretend to be the items method."""
+ return self._main._items()
+
+ def __getitem__(self, index):
+ """Fetch the item at position i."""
+ if isinstance(index, types.SliceType):
+ # fetching a slice returns an OrderedDict
+ return self._main[index].items()
+ key = self._main._sequence[index]
+ return (key, self._main[key])
+
+ def __setitem__(self, index, item):
+ """Set item at position i to item."""
+ if isinstance(index, types.SliceType):
+ # NOTE: item must be an iterable (list of tuples)
+ self._main[index] = OrderedDict(item)
+ else:
+ # FIXME: Does this raise a sensible error?
+ orig = self._main.keys[index]
+ key, value = item
+ if self._main.strict and key in self and (key != orig):
+ raise ValueError('slice assignment must be from '
+ 'unique keys')
+ # delete the current one
+ del self._main[self._main._sequence[index]]
+ self._main.insert(index, key, value)
+
+ def __delitem__(self, i):
+ """Delete the item at position i."""
+ key = self._main._sequence[i]
+ if isinstance(i, types.SliceType):
+ for k in key:
+ # FIXME: efficiency?
+ del self._main[k]
+ else:
+ del self._main[key]
+
+ ### following methods pinched from UserList and adapted ###
+ def __repr__(self): return repr(self._main.items())
+
+ # FIXME: do we need to check if we are comparing with another ``Items``
+ # object? (like the __cast method of UserList)
+ def __lt__(self, other): return self._main.items() < other
+ def __le__(self, other): return self._main.items() <= other
+ def __eq__(self, other): return self._main.items() == other
+ def __ne__(self, other): return self._main.items() != other
+ def __gt__(self, other): return self._main.items() > other
+ def __ge__(self, other): return self._main.items() >= other
+ def __cmp__(self, other): return cmp(self._main.items(), other)
+
+ def __contains__(self, item): return item in self._main.items()
+ def __len__(self): return len(self._main._sequence) # easier :-)
+ def __iter__(self): return self._main.iteritems()
+ def count(self, item): return self._main.items().count(item)
+ def index(self, item, *args): return self._main.items().index(item, *args)
+ def reverse(self): self._main.reverse()
+ def sort(self, *args, **kwds): self._main.sort(*args, **kwds)
+ def __mul__(self, n): return self._main.items()*n
+ __rmul__ = __mul__
+ def __add__(self, other): return self._main.items() + other
+ def __radd__(self, other): return other + self._main.items()
+
+ def append(self, item):
+ """Add an item to the end."""
+ # FIXME: this is only append if the key isn't already present
+ key, value = item
+ self._main[key] = value
+
+ def insert(self, i, item):
+ key, value = item
+ self._main.insert(i, key, value)
+
+ def pop(self, i=-1):
+ key = self._main._sequence[i]
+ return (key, self._main.pop(key))
+
+ def remove(self, item):
+ key, value = item
+ try:
+ assert value == self._main[key]
+ except (KeyError, AssertionError):
+ raise ValueError('ValueError: list.remove(x): x not in list')
+ else:
+ del self._main[key]
+
+ def extend(self, other):
+ # FIXME: is only a true extend if none of the keys already present
+ for item in other:
+ key, value = item
+ self._main[key] = value
+
+ def __iadd__(self, other):
+ self.extend(other)
+
+ ## following methods not implemented for items ##
+
+ def __imul__(self, n): raise TypeError('Can\'t multiply items in place')
+
+class Values(object):
+ """
+ Custom object for accessing the values of an OrderedDict.
+
+ Can be called like the normal ``OrderedDict.values`` method, but also
+ supports indexing and sequence methods.
+ """
+
+ def __init__(self, main):
+ self._main = main
+
+ def __call__(self):
+ """Pretend to be the values method."""
+ return self._main._values()
+
+ def __getitem__(self, index):
+ """Fetch the value at position i."""
+ if isinstance(index, types.SliceType):
+ return [self._main[key] for key in self._main._sequence[index]]
+ else:
+ return self._main[self._main._sequence[index]]
+
+ def __setitem__(self, index, value):
+ """
+ Set the value at position i to value.
+
+ You can only do slice assignment to values if you supply a sequence of
+ equal length to the slice you are replacing.
+ """
+ if isinstance(index, types.SliceType):
+ keys = self._main._sequence[index]
+ if len(keys) != len(value):
+ raise ValueError('attempt to assign sequence of size %s '
+ 'to slice of size %s' % (len(name), len(keys)))
+ # FIXME: efficiency? Would be better to calculate the indexes
+ # directly from the slice object
+ # NOTE: the new keys can collide with existing keys (or even
+ # contain duplicates) - these will overwrite
+ for key, val in zip(keys, value):
+ self._main[key] = val
+ else:
+ self._main[self._main._sequence[index]] = value
+
+ ### following methods pinched from UserList and adapted ###
+ def __repr__(self): return repr(self._main.values())
+
+ # FIXME: do we need to check if we are comparing with another ``Values``
+ # object? (like the __cast method of UserList)
+ def __lt__(self, other): return self._main.values() < other
+ def __le__(self, other): return self._main.values() <= other
+ def __eq__(self, other): return self._main.values() == other
+ def __ne__(self, other): return self._main.values() != other
+ def __gt__(self, other): return self._main.values() > other
+ def __ge__(self, other): return self._main.values() >= other
+ def __cmp__(self, other): return cmp(self._main.values(), other)
+
+ def __contains__(self, item): return item in self._main.values()
+ def __len__(self): return len(self._main._sequence) # easier :-)
+ def __iter__(self): return self._main.itervalues()
+ def count(self, item): return self._main.values().count(item)
+ def index(self, item, *args): return self._main.values().index(item, *args)
+
+ def reverse(self):
+ """Reverse the values"""
+ vals = self._main.values()
+ vals.reverse()
+ # FIXME: efficiency
+ self[:] = vals
+
+ def sort(self, *args, **kwds):
+ """Sort the values."""
+ vals = self._main.values()
+ vals.sort(*args, **kwds)
+ self[:] = vals
+
+ def __mul__(self, n): return self._main.values()*n
+ __rmul__ = __mul__
+ def __add__(self, other): return self._main.values() + other
+ def __radd__(self, other): return other + self._main.values()
+
+ ## following methods not implemented for values ##
+ def __delitem__(self, i): raise TypeError('Can\'t delete items from values')
+ def __iadd__(self, other): raise TypeError('Can\'t add in place to values')
+ def __imul__(self, n): raise TypeError('Can\'t multiply values in place')
+ def append(self, item): raise TypeError('Can\'t append items to values')
+ def insert(self, i, item): raise TypeError('Can\'t insert items into values')
+ def pop(self, i=-1): raise TypeError('Can\'t pop items from values')
+ def remove(self, item): raise TypeError('Can\'t remove items from values')
+ def extend(self, other): raise TypeError('Can\'t extend values')
+
+class SequenceOrderedDict(OrderedDict):
+ """
+ Experimental version of OrderedDict that has a custom object for ``keys``,
+ ``values``, and ``items``.
+
+ These are callable sequence objects that work as methods, or can be
+ manipulated directly as sequences.
+
+ Test for ``keys``, ``items`` and ``values``.
+
+ >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
+ >>> d
+ SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+ >>> d.keys
+ [1, 2, 3]
+ >>> d.keys()
+ [1, 2, 3]
+ >>> d.setkeys((3, 2, 1))
+ >>> d
+ SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
+ >>> d.setkeys((1, 2, 3))
+ >>> d.keys[0]
+ 1
+ >>> d.keys[:]
+ [1, 2, 3]
+ >>> d.keys[-1]
+ 3
+ >>> d.keys[-2]
+ 2
+ >>> d.keys[0:2] = [2, 1]
+ >>> d
+ SequenceOrderedDict([(2, 3), (1, 2), (3, 4)])
+ >>> d.keys.reverse()
+ >>> d.keys
+ [3, 1, 2]
+ >>> d.keys = [1, 2, 3]
+ >>> d
+ SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+ >>> d.keys = [3, 1, 2]
+ >>> d
+ SequenceOrderedDict([(3, 4), (1, 2), (2, 3)])
+ >>> a = SequenceOrderedDict()
+ >>> b = SequenceOrderedDict()
+ >>> a.keys == b.keys
+ 1
+ >>> a['a'] = 3
+ >>> a.keys == b.keys
+ 0
+ >>> b['a'] = 3
+ >>> a.keys == b.keys
+ 1
+ >>> b['b'] = 3
+ >>> a.keys == b.keys
+ 0
+ >>> a.keys > b.keys
+ 0
+ >>> a.keys < b.keys
+ 1
+ >>> 'a' in a.keys
+ 1
+ >>> len(b.keys)
+ 2
+ >>> 'c' in d.keys
+ 0
+ >>> 1 in d.keys
+ 1
+ >>> [v for v in d.keys]
+ [3, 1, 2]
+ >>> d.keys.sort()
+ >>> d.keys
+ [1, 2, 3]
+ >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True)
+ >>> d.keys[::-1] = [1, 2, 3]
+ >>> d
+ SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
+ >>> d.keys[:2]
+ [3, 2]
+ >>> d.keys[:2] = [1, 3]
+ Traceback (most recent call last):
+ KeyError: 'Keylist is not the same as current keylist.'
+
+ >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
+ >>> d
+ SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+ >>> d.values
+ [2, 3, 4]
+ >>> d.values()
+ [2, 3, 4]
+ >>> d.setvalues((4, 3, 2))
+ >>> d
+ SequenceOrderedDict([(1, 4), (2, 3), (3, 2)])
+ >>> d.values[::-1]
+ [2, 3, 4]
+ >>> d.values[0]
+ 4
+ >>> d.values[-2]
+ 3
+ >>> del d.values[0]
+ Traceback (most recent call last):
+ TypeError: Can't delete items from values
+ >>> d.values[::2] = [2, 4]
+ >>> d
+ SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+ >>> 7 in d.values
+ 0
+ >>> len(d.values)
+ 3
+ >>> [val for val in d.values]
+ [2, 3, 4]
+ >>> d.values[-1] = 2
+ >>> d.values.count(2)
+ 2
+ >>> d.values.index(2)
+ 0
+ >>> d.values[-1] = 7
+ >>> d.values
+ [2, 3, 7]
+ >>> d.values.reverse()
+ >>> d.values
+ [7, 3, 2]
+ >>> d.values.sort()
+ >>> d.values
+ [2, 3, 7]
+ >>> d.values.append('anything')
+ Traceback (most recent call last):
+ TypeError: Can't append items to values
+ >>> d.values = (1, 2, 3)
+ >>> d
+ SequenceOrderedDict([(1, 1), (2, 2), (3, 3)])
+
+ >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
+ >>> d
+ SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+ >>> d.items()
+ [(1, 2), (2, 3), (3, 4)]
+ >>> d.setitems([(3, 4), (2 ,3), (1, 2)])
+ >>> d
+ SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
+ >>> d.items[0]
+ (3, 4)
+ >>> d.items[:-1]
+ [(3, 4), (2, 3)]
+ >>> d.items[1] = (6, 3)
+ >>> d.items
+ [(3, 4), (6, 3), (1, 2)]
+ >>> d.items[1:2] = [(9, 9)]
+ >>> d
+ SequenceOrderedDict([(3, 4), (9, 9), (1, 2)])
+ >>> del d.items[1:2]
+ >>> d
+ SequenceOrderedDict([(3, 4), (1, 2)])
+ >>> (3, 4) in d.items
+ 1
+ >>> (4, 3) in d.items
+ 0
+ >>> len(d.items)
+ 2
+ >>> [v for v in d.items]
+ [(3, 4), (1, 2)]
+ >>> d.items.count((3, 4))
+ 1
+ >>> d.items.index((1, 2))
+ 1
+ >>> d.items.index((2, 1))
+ Traceback (most recent call last):
+ ValueError: list.index(x): x not in list
+ >>> d.items.reverse()
+ >>> d.items
+ [(1, 2), (3, 4)]
+ >>> d.items.reverse()
+ >>> d.items.sort()
+ >>> d.items
+ [(1, 2), (3, 4)]
+ >>> d.items.append((5, 6))
+ >>> d.items
+ [(1, 2), (3, 4), (5, 6)]
+ >>> d.items.insert(0, (0, 0))
+ >>> d.items
+ [(0, 0), (1, 2), (3, 4), (5, 6)]
+ >>> d.items.insert(-1, (7, 8))
+ >>> d.items
+ [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)]
+ >>> d.items.pop()
+ (5, 6)
+ >>> d.items
+ [(0, 0), (1, 2), (3, 4), (7, 8)]
+ >>> d.items.remove((1, 2))
+ >>> d.items
+ [(0, 0), (3, 4), (7, 8)]
+ >>> d.items.extend([(1, 2), (5, 6)])
+ >>> d.items
+ [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)]
+ """
+
+ def __init__(self, init_val=(), strict=True):
+ OrderedDict.__init__(self, init_val, strict=strict)
+ self._keys = self.keys
+ self._values = self.values
+ self._items = self.items
+ self.keys = Keys(self)
+ self.values = Values(self)
+ self.items = Items(self)
+ self._att_dict = {
+ 'keys': self.setkeys,
+ 'items': self.setitems,
+ 'values': self.setvalues,
+ }
+
+ def __setattr__(self, name, value):
+ """Protect keys, items, and values."""
+ if not '_att_dict' in self.__dict__:
+ object.__setattr__(self, name, value)
+ else:
+ try:
+ fun = self._att_dict[name]
+ except KeyError:
+ OrderedDict.__setattr__(self, name, value)
+ else:
+ fun(value)
+
+if __name__ == '__main__':
+ if INTP_VER < (2, 3):
+ raise RuntimeError("Tests require Python v.2.3 or later")
+ # turn off warnings for tests
+ warnings.filterwarnings('ignore')
+ # run the code tests in doctest format
+ import doctest
+ m = sys.modules.get('__main__')
+ globs = m.__dict__.copy()
+ globs.update({
+ 'INTP_VER': INTP_VER,
+ })
+ doctest.testmod(m, globs=globs)
+
--- /dev/null
+#Copyright 2006 DR0ID <dr0id@bluewin.ch> http://mypage.bluewin.ch/DR0ID
+#
+#
+#
+"""
+Allow to draw some gradients relatively easy.
+"""
+
+__author__ = "$Author: DR0ID $"
+__version__= "$Revision: 109 $"
+__date__ = "$Date: 2007-08-09 20:33:32 +0200 (Do, 09 Aug 2007) $"
+
+import pygame
+import math
+
+BLEND_MODES_AVAILABLE = False
+vernum = pygame.vernum
+if vernum[0]>=1 and vernum[1]>=8:
+ BLEND_MODES_AVAILABLE = True
+
+
+class ColorInterpolator(object):
+ '''
+ ColorInterpolator(distance, color1, color2, rfunc, gfunc, bfunc, afunc)
+
+ interpolates a color over the distance using different functions for r,g,b,a
+ separately (a= alpha).
+ '''
+ def __init__(self, distance, color1, color2, rfunc, gfunc, bfunc, afunc):
+ object.__init__(self)
+
+ self.rInterpolator = FunctionInterpolator(color1[0], color2[0], distance, rfunc)
+ self.gInterpolator = FunctionInterpolator(color1[1], color2[1], distance, gfunc)
+ self.bInterpolator = FunctionInterpolator(color1[2], color2[2], distance, bfunc)
+ if len(color1)==4 and len(color2)==4:
+ self.aInterpolator = FunctionInterpolator(color1[3], color2[3], distance, afunc)
+ else:
+ self.aInterpolator = FunctionInterpolator(255, 255, distance, afunc)
+
+ def eval(self, x):
+ '''
+ eval(x) -> color
+
+ returns the color at the position 0<=x<=d (actually not bound to this interval).
+ '''
+## print "colorInterp x", x, self.rInterpolator.eval(x), self.gInterpolator.eval(x), self.bInterpolator.eval(x)
+ return [self.rInterpolator.eval(x),
+ self.gInterpolator.eval(x),
+ self.bInterpolator.eval(x),
+ self.aInterpolator.eval(x)]
+
+
+
+class FunctionInterpolator(object):
+ '''
+ FunctionINterpolator(startvalue, endvalue, trange, func)
+
+ interpolates a function y=f(x) in the range trange with
+ startvalue = f(0)
+ endvalue = f(trange)
+ using the function func
+ '''
+ def __init__(self, startvalue, endvalue, trange, func):
+ object.__init__(self)
+ # function
+ self.func = func
+ # y-scaling
+ self.a = endvalue-startvalue
+ if self.a == 0:
+ self.a = 1.
+ # x-scaling
+ if trange!=0:
+ self.b = 1./abs(trange)
+ else:
+ self.b = 1.
+ # x-displacement
+ self.c = 0
+ # y-displacement
+ self.d = min(max(startvalue,0),255)
+
+ def eval(self, x):
+ '''
+ eval(x)->float
+
+ return value at position x
+ '''
+ # make sure that the returned value is in [0,255]
+## return int(round(min(max(self.a*self.func(self.b*(x+self.c))+self.d, 0), 255)))
+ return int(min(max(self.a*self.func(self.b*(x+self.c))+self.d, 0), 255))
+
+
+
+##def gradient(surface,
+## startpoint,
+## endpoint,
+## startcolor,
+## endcolor,
+## Rfunc = (lambda x:x),
+## Gfunc = (lambda x:x),
+## Bfunc = (lambda x:x),
+## Afunc = (lambda x:1),
+## type = "line",
+## mode = None ):
+## '''
+## surface : surface to draw on
+## startpoint: (x,y) point on surface
+## endpoint : (x,y) point on surface
+## startcolor: (r,g,b,a) color at startpoint
+## endcolor : (r,g,b,a) color at endpoint
+## Rfunc : function y = f(x) with startcolor =f(0) and endcolor = f(1) where 0 is at startpoint and 1 at endpoint
+## Gfunc : --- " ---
+## Bfunc : --- " ---
+## Afunc : --- " ---
+## these functions are evaluated in the range 0 <= x <= 1 and 0<= y=f(x) <= 1
+## type : "line", "circle" or "rect"
+## mode : "+", "-", "*", None (how the pixels are drawen)
+##
+## returns : surface with the color characteristics w,h = (d, 256) and d = length of endpoint-startpoint
+##
+## '''
+## dx = endpoint[0]-startpoint[0]
+## dy = endpoint[1]-startpoint[1]
+## d = int(round(math.hypot(dx, dy)))
+## angle = math.degrees( math.atan2(dy, dx) )
+##
+## color = ColorInterpolator(d, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
+##
+## if type=="line":
+## h = int(2.*math.hypot(*surface.get_size()))
+### bigSurf = pygame.Surface((d, h)).convert_alpha()
+## bigSurf = pygame.Surface((d, h), pygame.SRCALPHA)#.convert_alpha()
+### bigSurf = pygame.Surface((d, 1), pygame.SRCALPHA)#.convert_alpha()
+## bigSurf.lock()
+## bigSurf.fill((0,0,0,0))
+## bigSurf.set_colorkey((0,0,0,0))
+## for x in range(d):
+## pygame.draw.line(bigSurf, color.eval(x), (x,0), (x,h), 1)
+### for x in range(d):
+### bigSurf.set_at((x, 0), color.eval(x))
+### bigSurf = pygame.transform.scale(bigSurf, (d, h))
+##
+## bigSurf = pygame.transform.rotate(bigSurf, -angle) #rotozoom(bigSurf, -angle, 1)
+## bigSurf.set_colorkey((0,0,0, 0))
+## rect = bigSurf.get_rect()
+## srect = pygame.Rect(rect)
+## dx = d/2. * math.cos(math.radians(angle))
+## dy = d/2. * math.sin(math.radians(angle))
+## rect.center = startpoint
+## rect.move_ip(dx, dy)
+## bigSurf.unlock()
+##
+## elif type=="circle":
+## bigSurf = pygame.Surface((2*d, 2*d)).convert_alpha()
+## bigSurf.fill((0,0,0,0))
+## bigSurf.lock()
+## for x in range(d, 0, -1):
+## pygame.draw.circle(bigSurf, color.eval(x), (d,d), x)
+## bigSurf.unlock()
+## rect = bigSurf.get_rect()
+## srect = pygame.Rect(rect)
+## rect.center = (startpoint[0], startpoint[1])
+##
+## elif type=="rect":
+## bigSurf = pygame.Surface((2*d, 2*d)).convert_alpha()
+## bigSurf.fill((0,0,0,0))
+## c = bigSurf.get_rect().center
+## bigSurf.lock()
+## for x in range(d,-1,-1):
+## r = pygame.Rect(0,0,2*x,2*x)
+## r.center = c
+## pygame.draw.rect(bigSurf, color.eval(x), r)
+## bigSurf.unlock()
+## bigSurf = pygame.transform.rotozoom(bigSurf, -angle, 1)
+## bigSurf.set_colorkey((0,0,0, 0))
+##
+## rect = bigSurf.get_rect()
+## srect = pygame.Rect(rect)
+## rect.center = startpoint
+## else:
+## raise NameError("type must be one of \"line\",\"circle\" or \"rect\"")
+##
+## if mode is None:
+## surface.blit(bigSurf, rect, srect)
+## else:
+## if mode=="+":
+## cf = pygame.color.add
+## elif mode=="*":
+## cf = pygame.color.multiply
+## elif mode=="-":
+## cf = pygame.color.subtract
+## else:
+## raise NameError("type must be one of \"+\", \"*\", \"-\" or None")
+## irect = surface.get_clip().clip(rect)
+## surface.lock()
+## for x in range(irect.left, irect.left+irect.width):
+## for y in range(irect.top, irect.top+irect.height):
+## surface.set_at((x,y), cf(surface.get_at((x,y)), bigSurf.get_at((x-rect.left, y-rect.top)) ) )
+## surface.unlock()
+##
+## del bigSurf
+## char = pygame.Surface((d+1, 257))
+### char.fill((0,0,0))
+### ox = 0
+### oldcol = color.eval(0)
+### for x in range(d):
+### col = color.eval(x)
+### pygame.draw.line(char, (255,0,0), (x, 256-col[0]), (ox, 256-oldcol[0]))
+### pygame.draw.line(char, (0,255,0), (x, 256-col[1]), (ox, 256-oldcol[1]))
+### pygame.draw.line(char, (0,0,255), (x, 256-col[2]), (ox, 256-oldcol[2]))
+### pygame.draw.line(char, (255,255,255), (x, 256-col[3]), (ox, 256-oldcol[3]))
+### ox = x
+### oldcol = col
+###
+## return char
+
+
+
+
+def vertical(size, startcolor, endcolor):
+ """
+ Draws a vertical linear gradient filling the entire surface. Returns a
+ surface filled with the gradient (numeric is only 2-3 times faster).
+ """
+ height = size[1]
+ bigSurf = pygame.Surface((1,height)).convert_alpha()
+ dd = 1.0/height
+ sr, sg, sb, sa = startcolor
+ er, eg, eb, ea = endcolor
+ rm = (er-sr)*dd
+ gm = (eg-sg)*dd
+ bm = (eb-sb)*dd
+ am = (ea-sa)*dd
+ for y in range(height):
+ bigSurf.set_at((0,y),
+ (int(sr + rm*y),
+ int(sg + gm*y),
+ int(sb + bm*y),
+ int(sa + am*y))
+ )
+ return pygame.transform.scale(bigSurf, size)
+
+
+def horizontal(size, startcolor, endcolor):
+ """
+ Draws a horizontal linear gradient filling the entire surface. Returns a
+ surface filled with the gradient (numeric is only 2-3 times faster).
+ """
+ width = size[0]
+ bigSurf = pygame.Surface((width, 1)).convert_alpha()
+ dd = 1.0/width
+ sr, sg, sb, sa = startcolor
+ er, eg, eb, ea = endcolor
+ rm = (er-sr)*dd
+ gm = (eg-sg)*dd
+ bm = (eb-sb)*dd
+ am = (ea-sa)*dd
+ for y in range(width):
+ bigSurf.set_at((y,0),
+ (int(sr + rm*y),
+ int(sg + gm*y),
+ int(sb + bm*y),
+ int(sa + am*y))
+ )
+ return pygame.transform.scale(bigSurf, size)
+
+
+def radial(radius, startcolor, endcolor):
+ """
+ Draws a linear raidal gradient on a square sized surface and returns
+ that surface.
+ """
+ bigSurf = pygame.Surface((2*radius, 2*radius)).convert_alpha()
+ bigSurf.fill((0,0,0,0))
+ dd = -1.0/radius
+ sr, sg, sb, sa = endcolor
+ er, eg, eb, ea = startcolor
+ rm = (er-sr)*dd
+ gm = (eg-sg)*dd
+ bm = (eb-sb)*dd
+ am = (ea-sa)*dd
+
+ draw_circle = pygame.draw.circle
+ for rad in range(radius, 0, -1):
+ draw_circle(bigSurf, (er + int(rm*rad),
+ eg + int(gm*rad),
+ eb + int(bm*rad),
+ ea + int(am*rad)), (radius, radius), rad)
+ return bigSurf
+
+def squared(width, startcolor, endcolor):
+ """
+ Draws a linear sqared gradient on a square sized surface and returns
+ that surface.
+ """
+ bigSurf = pygame.Surface((width, width)).convert_alpha()
+ bigSurf.fill((0,0,0,0))
+ dd = -1.0/(width/2)
+ sr, sg, sb, sa = endcolor
+ er, eg, eb, ea = startcolor
+ rm = (er-sr)*dd
+ gm = (eg-sg)*dd
+ bm = (eb-sb)*dd
+ am = (ea-sa)*dd
+
+ draw_rect = pygame.draw.rect
+ for currentw in range((width/2), 0, -1):
+ pos = (width/2)-currentw
+ draw_rect(bigSurf, (er + int(rm*currentw),
+ eg + int(gm*currentw),
+ eb + int(bm*currentw),
+ ea + int(am*currentw)), pygame.Rect(pos, pos, 2*currentw, 2*currentw ))
+ return bigSurf
+
+
+def vertical_func(size, startcolor, endcolor, Rfunc = (lambda x:x), Gfunc = (lambda x:x), Bfunc = (lambda x:x), Afunc = (lambda x:1)):
+ """
+ Draws a vertical linear gradient filling the entire surface. Returns a
+ surface filled with the gradient (numeric is only 2x faster).
+ Rfunc, Gfunc, Bfunc and Afunc are function like y = f(x). They define
+ how the color changes.
+ """
+ height = size[1]
+ bigSurf = pygame.Surface((1,height)).convert_alpha()
+ color = ColorInterpolator(height, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
+ for y in range(0, height):
+ bigSurf.set_at((0,y), color.eval(y+0.1))
+ return pygame.transform.scale(bigSurf, size)
+
+
+def horizontal_func(size, startcolor, endcolor, Rfunc = (lambda x:x), Gfunc = (lambda x:x), Bfunc = (lambda x:x), Afunc = (lambda x:1)):
+ """
+ Draws a horizontal linear gradient filling the entire surface. Returns a
+ surface filled with the gradient (numeric is only 2x faster).
+ Rfunc, Gfunc, Bfunc and Afunc are function like y = f(x). They define
+ how the color changes.
+ """
+ width = size[0]
+ bigSurf = pygame.Surface((width, 1)).convert_alpha()
+ color = ColorInterpolator(width, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
+ for y in range(0, width):
+ bigSurf.set_at((y, 0), color.eval(y+0.1))
+ return pygame.transform.scale(bigSurf, size)
+
+def radial_func(radius, startcolor, endcolor, Rfunc = (lambda x:x), Gfunc = (lambda x:x), Bfunc = (lambda x:x), Afunc = (lambda x:1), colorkey=(0,0,0,0)):
+ """
+ Draws a linear raidal gradient on a square sized surface and returns
+ that surface.
+ """
+ bigSurf = pygame.Surface((2*radius, 2*radius)).convert_alpha()
+ if len(colorkey)==3:
+ colorkey += (0,)
+ bigSurf.fill(colorkey)
+ color = ColorInterpolator(radius, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
+ draw_circle = pygame.draw.circle
+ for rad in range(radius, 0, -1):
+ draw_circle(bigSurf, color.eval(rad), (radius, radius), rad)
+ return bigSurf
+
+def radial_func_offset(radius, startcolor, endcolor, Rfunc = (lambda x:x), Gfunc = (lambda x:x), Bfunc = (lambda x:x), Afunc = (lambda x:1), colorkey=(0,0,0,0), offset=(0,0)):
+ """
+ Draws a linear raidal gradient on a square sized surface and returns
+ that surface.
+ offset is the amount the center of the gradient is displaced of the center of the image.
+ Unfotunately this function ignores alpha.
+ """
+ bigSurf = pygame.Surface((2*radius, 2*radius))#.convert_alpha()
+
+ mask = pygame.Surface((2*radius, 2*radius), pygame.SRCALPHA)#.convert_alpha()
+ mask.fill(colorkey)
+ mask.set_colorkey((255,0,255))
+ pygame.draw.circle(mask, (255,0,255), (radius, radius), radius)
+
+ if len(colorkey)==3:
+ colorkey += (0,)
+ bigSurf.fill(colorkey)
+
+ color = ColorInterpolator(radius, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
+ draw_circle = pygame.draw.circle
+ radi = radius + int(math.hypot(offset[0], offset[1])+1)
+ for rad in range(radi, 0, -1):
+ draw_circle(bigSurf, color.eval(rad), (radius+offset[0], radius+offset[1]), rad)
+
+ bigSurf.blit(mask, (0,0))
+ bigSurf.set_colorkey(colorkey)
+ return bigSurf
+
+
+def squared_func(width, startcolor, endcolor, Rfunc = (lambda x:x), Gfunc = (lambda x:x), Bfunc = (lambda x:x), Afunc = (lambda x:1), offset=(0,0)):
+ """
+ Draws a linear sqared gradient on a square sized surface and returns
+ that surface.
+ """
+ bigSurf = pygame.Surface((width, width)).convert_alpha()
+ bigSurf.fill((0,0,0,0))
+ color = ColorInterpolator(width/2, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
+ draw_rect = pygame.draw.rect
+ widthh = width+2*int(max(abs(offset[0]),abs(offset[1])))
+ for currentw in range((widthh/2), 0, -1):
+## pos = (width/2)-currentw
+ rect = pygame.Rect(0, 0, 2*currentw, 2*currentw )
+ rect.center = (width/2+offset[0], width/2+offset[1])
+ draw_rect(bigSurf, color.eval(currentw), rect)
+ return bigSurf
+
+def draw_gradient(surface, startpoint, endpoint, startcolor, endcolor, Rfunc = (lambda x:x), Gfunc = (lambda x:x), Bfunc = (lambda x:x), Afunc = (lambda x:1), mode=0):
+ """
+ Instead of returning an Surface, this function draw it directy onto the
+ given Surface and returns the rect.
+ """
+ dx = endpoint[0]-startpoint[0]
+ dy = endpoint[1]-startpoint[1]
+ d = int(round(math.hypot(dx, dy)))
+ angle = math.degrees( math.atan2(dy, dx) )
+
+ h = int(2.*math.hypot(*surface.get_size()))
+
+ bigSurf = horizontal_func((d,h), startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
+
+## bigSurf = pygame.transform.rotate(bigSurf, -angle) #rotozoom(bigSurf, -angle, 1)
+ bigSurf = pygame.transform.rotozoom(bigSurf, -angle, 1)
+## bigSurf.set_colorkey((0,0,0, 0))
+ rect = bigSurf.get_rect()
+ srect = pygame.Rect(rect)
+ dx = d/2. * math.cos(math.radians(angle))
+ dy = d/2. * math.sin(math.radians(angle))
+ rect.center = startpoint
+ rect.move_ip(dx, dy)
+ if BLEND_MODES_AVAILABLE:
+ return surface.blit(bigSurf, rect, None, mode)
+ else:
+ return surface.blit(bigSurf, rect)
+
+
+def draw_circle(surface, startpoint, endpoint, startcolor, endcolor, Rfunc = (lambda x:x), Gfunc = (lambda x:x), Bfunc = (lambda x:x), Afunc = (lambda x:1), mode=0):
+ """
+ Instead of returning an Surface, this function draw it directy onto the
+ given Surface and returns the rect.
+ """
+ dx = endpoint[0]-startpoint[0]
+ dy = endpoint[1]-startpoint[1]
+ radius = int(round(math.hypot(dx, dy)))
+ pos = (startpoint[0]-radius, startpoint[1]-radius)
+ if BLEND_MODES_AVAILABLE:
+ return surface.blit(radial_func(radius, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc), pos, None, mode)
+ else:
+ return surface.blit(radial_func(radius, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc), pos)
+
+def draw_squared(surface, startpoint, endpoint, startcolor, endcolor, Rfunc = (lambda x:x), Gfunc = (lambda x:x), Bfunc = (lambda x:x), Afunc = (lambda x:1), mode=0):
+ """
+ Instead of returning an Surface, this function draw it directy onto the
+ given Surface and returns the rect.
+ """
+ dx = endpoint[0]-startpoint[0]
+ dy = endpoint[1]-startpoint[1]
+ angle = math.degrees( math.atan2(dy, dx) )
+ width = 2*int(round(math.hypot(dx, dy)))
+
+ bigSurf = squared_func(width, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
+
+ bigSurf = pygame.transform.rotozoom(bigSurf, -angle, 1)
+## bigSurf.set_colorkey((0,0,0, 0))
+ rect = bigSurf.get_rect()
+ rect.center = startpoint
+ if BLEND_MODES_AVAILABLE:
+ return surface.blit(bigSurf, rect, None, mode)
+ else:
+ return surface.blit(bigSurf, rect)
+
+
+def chart(startpoint, endpoint, startcolor, endcolor, Rfunc = (lambda x:x), Gfunc = (lambda x:x), Bfunc = (lambda x:x), Afunc = (lambda x:1), scale=None):
+ """
+ This returns a Surface where the change of the colors over the distance
+ (the width of the image) is showen as a line.
+ scale: a float, 1 is not scaling
+ """
+ dx = endpoint[0]-startpoint[0]
+ dy = endpoint[1]-startpoint[1]
+ distance = int(round(math.hypot(dx, dy)))
+ color = ColorInterpolator(distance, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
+ bigSurf = pygame.Surface((distance, 256))
+ bigSurf.fill((0,)*3)
+ oldcol = color.eval(0)
+ for x in range(distance):
+ r, g, b, a = color.eval(x)
+ pygame.draw.line(bigSurf, (255, 0, 0, 128), (x-1, oldcol[0]), (x, r))
+ pygame.draw.line(bigSurf, (0, 255, 0, 128), (x-1, oldcol[1]), (x, g))
+ pygame.draw.line(bigSurf, (0, 0, 255, 128), (x-1, oldcol[2]), (x, b))
+ pygame.draw.line(bigSurf, (255, 255, 255, 128), (x-1, oldcol[3]), (x, a))
+ oldcol = (r,g,b,a)
+ if scale:
+## return pygame.transform.scale(bigSurf, size)
+ return pygame.transform.rotozoom(bigSurf, 0, scale)
+ return pygame.transform.flip(bigSurf, 0, 1)
+#------------------------------------------------------------------------------
+
+
+
+
+def genericFxyGradient(surf, clip, color1, color2, func, intx, yint, zint=None):
+ """
+ genericFxyGradient(size, color1, color2,func, intx, yint, zint=None)
+
+ some sort of highfield drawer :-)
+
+ surf : surface to draw
+ clip : rect on surf to draw in
+ color1 : start color
+ color2 : end color
+ func : function z = func(x,y)
+ xint : interval in x direction where the function is evaluated
+ yint : interval in y direction where the function is evaluated
+ zint : if not none same as yint or xint, if None then the max and min value
+ of func is taken as z-interval
+
+ color = a*func(b*(x+c), d*(y+e))+f
+ """
+ # make shure that x1<x2 and y1<y2 and z1<z2
+ w,h = clip.size
+ x1 = min(intx)
+ x2 = max(intx)
+ y1 = min(yint)
+ y2 = max(yint)
+ if zint: # if user give us z intervall, then use it
+ z1 = min(zint)
+ z2 = max(zint)
+ else: # look for extrema of function (not best algorithme)
+ z1 = func(x1,y1)
+ z2 = z1
+ for i in range(w):
+ for j in range(h):
+ r = func(i,j)
+ z1 = min(z1, r)
+ z2 = max(z2, r)
+
+ x1 = float(x1)
+ x2 = float(x2)
+ y1 = float(y1)
+ y2 = float(y2)
+ z1 = float(z1)
+ z2 = float(z2)
+ if len(color1)==3:
+ color1 = list(color1)
+ color1.append(255)
+ if len(color2)==3:
+ color2 = list(color2)
+ color2.append(255)
+
+ # calculate streching and displacement variables
+ a = ((color2[0]-color1[0])/(z2-z1), \
+ (color2[1]-color1[1])/(z2-z1), \
+ (color2[2]-color1[2])/(z2-z1), \
+ (color2[3]-color1[3])/(z2-z1) ) # streching in z direction
+ b = (x2-x1)/float(w) # streching in x direction
+ d = (y2-y1)/float(h) # streching in y direction
+ f = ( color1[0]-a[0]*z1, \
+ color1[1]-a[1]*z1, \
+ color1[2]-a[2]*z1, \
+ color1[3]-a[3]*z1 )# z displacement
+ c = x1/b
+ e = y1/d
+
+ surff = pygame.surface.Surface((w,h)).convert_alpha()
+ # generate values
+ for i in range(h):
+ for j in range(w):
+ val = func(b*(j+c), d*(i+e))
+ #clip color
+ color = ( max(min(a[0]*val+f[0],255),0), \
+ max(min(a[1]*val+f[1],255),0), \
+ max(min(a[2]*val+f[2],255),0), \
+ max(min(a[3]*val+f[3],255),0) )
+ surff.set_at( (j,i), color )
+ surf.blit(surff, clip)
+
+
+
--- /dev/null
+'''\r
+Created on 21 janv. 2010\r
+\r
+@author: samsam\r
+'''\r
+import pygame\r
+\r
+from gui.FamiliarizerPGUConfiguration import FamiliarizerPGUConfiguration\r
+\r
+class DefaultFamiliarizer(FamiliarizerPGUConfiguration):\r
+ '''\r
+ classdocs\r
+ '''\r
+\r
+\r
+ def __init__(self,window):\r
+ '''\r
+ Constructor\r
+ '''\r
+ FamiliarizerPGUConfiguration.__init__(self, window,defaultParams = True)\r
+\r
+if __name__ == "__main__" :\r
+ pygame.init()\r
+ modeResolution = (1024,768)\r
+ window = pygame.display.set_mode(modeResolution,pygame.FULLSCREEN)\r
+ familiarizer = DefaultFamiliarizer(window)\r
+ pygame.quit()
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 21 janv. 2010\r
+\r
+@author: samsam\r
+'''\r
+\r
+import pygame\r
+import pygame.midi\r
+import sys\r
+import time\r
+import pickle\r
+\r
+from numpy import array\r
+from numpy.linalg import norm\r
+\r
+from math import floor\r
+\r
+from gui.constants import *\r
+from PlayingScreen import PlayingScreen\r
+from instruments.Instrument import Instrument\r
+from cursor.WarpingCursor import *\r
+from controllers.Wiimote import Wiimote\r
+from logging.EventLog import EventLog\r
+from logging.PickleableEvent import PickleableEvent \r
+from InstrumentChoice import InstrumentChoice\r
+\r
+class DummyInstrumentChoice():\r
+ '''\r
+ classdocs\r
+ '''\r
+\r
+\r
+ def __init__(self,wiimotes, window, screen, clock, joys, portOffset, activeWiimotes, eventLog=None, replay = False, logFilePath = None, scaleFactor = 1):\r
+ '''\r
+ Constructor\r
+ '''\r
+ self.wiimotes =wiimotes\r
+ self.activeWiimotes =activeWiimotes\r
+ self.window = window\r
+ self.screen = screen\r
+ self.scaleFactor = scaleFactor\r
+ self.width = int(floor(screen.get_width()*self.scaleFactor))\r
+ self.height = int(floor(screen.get_height()*self.scaleFactor))\r
+ self.blitOrigin = ((self.screen.get_width()-self.width)/2,(self.screen.get_height()-self.height)/2)\r
+ self.clock = clock \r
+ self.savedScreen = pygame.Surface(self.screen.get_size())\r
+ self.savedScreen.fill((255, 255, 255))\r
+ self.playerScreen = pygame.Surface(self.savedScreen.get_size())\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ self.cursorPositions = []\r
+ for i in range(len(self.wiimotes)):\r
+ self.wiimotes[i].cursor.screen = self.playerScreen\r
+ self.cursorPositions.append(self.wiimotes[i].cursor.centerPosition)\r
+ self.joys = joys\r
+ self.portOffset = portOffset\r
+ self.eventLog = eventLog\r
+ self.replay = replay
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 15 juil. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+\r
+import pygame\r
+import pygame.midi\r
+import sys\r
+import time\r
+import pickle\r
+import random\r
+\r
+from numpy import array\r
+from numpy.linalg import norm\r
+\r
+from math import floor\r
+\r
+from gui.constants import *\r
+from PlayingScreen import PlayingScreen\r
+from instruments.Instrument import Instrument\r
+from cursor.WarpingCursor import *\r
+from controllers.Wiimote import Wiimote\r
+from logging.EventLog import EventLog\r
+from logging.PickleableEvent import PickleableEvent \r
+\r
+joyNames = ["PPJoy Virtual joystick 1", "PPJoy Virtual joystick 2", "PPJoy Virtual joystick 3"]\r
+portNames = ["Out To MIDI Yoke: 1"]\r
+majorScale = [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]\r
+minorScale = [55, 56, 58, 60, 62, 63, 65, 67, 68, 70, 72]\r
+myxolydianScale = [55, 57, 58, 60, 62, 64, 65, 67, 69, 70, 72]\r
+dorianScale = [55, 57, 58, 60, 62, 63, 65, 67, 69, 70, 72]\r
+instrumentImagePathList = ["piano", "guitare", "accordeon", "violon", "flute", "tuba", "orgue", "violoncelle", "celesta"]\r
+octaves = [0, -1, 0, 1, 1, -2, 0, -1, 0]\r
+\r
+class Familiarizer:\r
+ '''\r
+ The screen for choosing instruments\r
+ \r
+ instruments: \r
+ The available instruments \r
+ wiimotes: \r
+ The wiimotes used in this session\r
+ window:\r
+ The main display window\r
+ screen:\r
+ The main display surface\r
+ clock:\r
+ The clock used to animate the screen\r
+ savedScreen:\r
+ The background that is painted every time\r
+ playerScreen:\r
+ The buffer for painting everything before bliting\r
+ width:\r
+ The width of the window in pixels\r
+ height:\r
+ The height of the window in pixels\r
+ done:\r
+ Goes to True when all instruments have been selected\r
+ cursorPositions:\r
+ The positions of the cursors on the screen, in pixels\r
+ imageRects:\r
+ The rectangles where the images of the instruments are located\r
+ focus:\r
+ The numbers of the instruments currently in focus\r
+ '''\r
+ \r
+ def __init__(self, instruments, wiimotes, window, screen, clock, joys, portOffset, eventLog=None, replay = False, logFilePath = None, scaleFactor = 1, level = 1):\r
+ '''\r
+ Constructor\r
+ \r
+ instruments: \r
+ The instruments for this session \r
+ wiimotes: \r
+ The wiimotes used in this session\r
+ '''\r
+ \r
+ \r
+ self.scaleFactor = scaleFactor\r
+ \r
+ self.instruments = instruments\r
+ self.wiimotes = wiimotes\r
+ self.window = window\r
+ self.screen = screen\r
+ self.clock = clock\r
+ self.width = int(floor(screen.get_width()*self.scaleFactor))\r
+ self.height = int(floor(screen.get_height()*self.scaleFactor))\r
+ self.blitOrigin = ((self.screen.get_width()-self.width)/2,(self.screen.get_height()-self.height)/2) \r
+ self.joys = joys\r
+ self.portOffset = portOffset\r
+ \r
+ self.currentWiimote = 0\r
+ self.done = False \r
+ \r
+ self.cursorPositions = []\r
+ self.imageRects = []\r
+ self.displayedInstruments = []\r
+ self.savedImageRects = []\r
+ self.focus = []\r
+ self.level = level\r
+ \r
+ if eventLog == None:\r
+ self.eventLog = EventLog()\r
+ self.replay = False\r
+ else:\r
+ self.eventLog = eventLog\r
+ self.replay = replay\r
+ \r
+ self.savedScreen = pygame.Surface(self.screen.get_size()) \r
+ \r
+ self.displayedInstruments.append(0)\r
+ if level == 1 :\r
+ self.displayedInstruments.append(2)\r
+ \r
+ self.savedScreen.fill((255, 255, 255))\r
+ for i in range(len(self.displayedInstruments)):\r
+ self.imageRects.append(self.drawInstrument(self.displayedInstruments[i]))\r
+ \r
+ self.savedImageRects = self.imageRects[:]\r
+ \r
+ #Draw the initial cursor on the buffer\r
+ self.playerScreen = pygame.Surface(self.savedScreen.get_size())\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ #Create the list of instrument focus (one focus per wiimote)\r
+ self.focus.append(None)\r
+ #Set the screen for the cursors (it can't be set before)\r
+ self.wiimotes[i].cursor.screen = self.playerScreen\r
+ self.cursorPositions.append(self.wiimotes[i].cursor.centerPosition)\r
+ \r
+ self.wiimotes[self.currentWiimote].cursor.blit(self.playerScreen)\r
+ \r
+ #The main loop\r
+ while self.done == False :\r
+ \r
+ #Clear the cursors from the screen\r
+ self.drawBackground()\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ \r
+ # Limit frame speed to 50 FPS\r
+ #\r
+ timePassed = self.clock.tick(50)\r
+ \r
+ if self.replay:\r
+ self.eventLog.update(timePassed)\r
+ pickledEventsToPost = self.eventLog.getPickledEvents() \r
+ for pickledEvent in pickledEventsToPost:\r
+ pygame.event.post(pickledEvent.event)\r
+ \r
+ events = pygame.event.get()\r
+ \r
+ if not self.replay:\r
+ pickledEvents = [PickleableEvent(event.type,event.dict) for event in events if self.eventFilter(event)]\r
+ if pickledEvents != [] :\r
+ self.eventLog.appendEventGroup(pickledEvents)\r
+ \r
+ for event in events:\r
+ self.input(event)\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])\r
+ \r
+ self.wiimotes[self.currentWiimote].cursor.blit(self.playerScreen) \r
+ \r
+ self.focus[self.currentWiimote] = None\r
+ \r
+ for i in range(len(self.imageRects)) :\r
+ if self.imageRects[i].collidepoint(self.cursorPositions[self.currentWiimote]):\r
+ self.focus[self.currentWiimote] = i\r
+ \r
+ self.screen.blit(self.playerScreen, (0, 0))\r
+ \r
+ pygame.display.flip()\r
+ \r
+ def input(self, event):\r
+ if event.type == pygame.QUIT:\r
+ pygame.midi.quit()\r
+ sys.exit()\r
+ if event.type == pygame.JOYAXISMOTION:\r
+ self.updateCursorPositionFromJoy(event)\r
+ if event.type == pygame.JOYBUTTONDOWN :\r
+ self.joyClicked(event)\r
+ if event.type == pygame.MOUSEBUTTONDOWN:\r
+ self.mouseClicked(event)\r
+ if event.type == pygame.MOUSEMOTION:\r
+ self.updateCursorPositionFromMouse(event)\r
+ if event.type == pygame.KEYDOWN:\r
+ if event.key == pygame.K_q:\r
+ self.done = True \r
+ \r
+ def updateCursorPositionFromJoy(self, joyEvent):\r
+ joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()\r
+ correctedJoyId = joyNames.index(joyName) \r
+ if correctedJoyId < len(self.cursorPositions):\r
+ if joyEvent.axis == 0 :\r
+ self.cursorPositions[correctedJoyId] = (int((joyEvent.value + 1) / 2 * self.screen.get_width()), self.cursorPositions[correctedJoyId][1])\r
+ if joyEvent.axis == 1 :\r
+ self.cursorPositions[correctedJoyId] = (self.cursorPositions[correctedJoyId][0], int((joyEvent.value + 1) / 2 * self.screen.get_height())) \r
+ \r
+ def assignInstrumentToWiimote(self, joyEvent):\r
+ joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()\r
+ correctedJoyId = joyNames.index(joyName)\r
+ if self.zoomed[correctedJoyId] == self.focus[correctedJoyId]:\r
+ self.wiimotes[correctedJoyId].instrument = self.instruments[self.focus[correctedJoyId]]\r
+ self.zoomed[correctedJoyId] = None\r
+ self.imageRects = self.savedImageRects[:]\r
+ if self.currentWiimote<len(self.wiimotes)-1:\r
+ self.currentWiimote = self.currentWiimote+1\r
+ else:\r
+ self.zoomed[correctedJoyId] = self.focus[correctedJoyId]\r
+ if self.hasFinished():\r
+ self.done = True\r
+ \r
+ def updateCursorPositionFromMouse(self, mouseEvent):\r
+ self.cursorPositions[0] = mouseEvent.pos\r
+ \r
+ def assignInstrumentToMouse(self, mouseEvent):\r
+ self.wiimotes[0].instrument = self.instruments[self.focus[0]]\r
+ if self.hasFinished():\r
+ self.done = True\r
+ \r
+ def hasFinished(self):\r
+ finished = True\r
+ for wiimote in self.wiimotes:\r
+ if wiimote.instrument == None:\r
+ finished = False\r
+ return(finished)\r
+ \r
+ def eventFilter(self, event):\r
+ c = event.type\r
+ if c == 17:\r
+ return False\r
+ elif c == pygame.MOUSEMOTION or pygame.MOUSEBUTTONDOWN or pygame.MOUSEBUTTONUP or pygame.JOYAXISMOTION or pygame.JOYBUTTONDOWN or pygame.JOYBUTTONUP or pygame.KEYDOWN:\r
+ return True\r
+ else:\r
+ return False\r
+ \r
+ def drawInstrument(self,instrumentNumber,drawPos = None):\r
+ if not drawPos :\r
+ drawPos = array([random.randint(0,self.width/2),random.randint(0,self.height/2)])\r
+ curImage = pygame.image.load(self.instruments[instrumentNumber].image).convert_alpha()\r
+ scaledImage = pygame.transform.smoothscale(curImage, (self.width / 2, self.height / 2))\r
+ imageRect = self.savedScreen.blit(scaledImage, drawPos + self.blitOrigin)\r
+ pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), imageRect, 5)\r
+ return imageRect \r
+ \r
+ def drawBackground(self):\r
+ self.savedScreen.fill((255, 255, 255))\r
+ for i in range(len(self.displayedInstruments)):\r
+ self.drawInstrument(self.displayedInstruments[i], self.imageRects[i].topleft)\r
+ if i in self.focus:\r
+ pygame.draw.rect(self.savedScreen, pygame.Color(0, 255, 0, 255), self.imageRects[i], 10)\r
+ \r
+ def mouseClicked(self,mouseEvent):\r
+ correctedJoyId = 0\r
+ self.wiimotes[correctedJoyId].cursor.flash(400)\r
+ if self.focus[correctedJoyId] != None :\r
+ self.imageRects.pop(self.focus[correctedJoyId])\r
+ instrumentNumber = self.displayedInstruments.pop(self.focus[correctedJoyId])\r
+ self.drawBackground()\r
+ self.imageRects.append(self.drawInstrument(instrumentNumber))\r
+ self.displayedInstruments.append(instrumentNumber)\r
+ self.wiimotes[correctedJoyId].instrument = self.instruments[instrumentNumber]\r
+ octave = self.wiimotes[correctedJoyId].instrument.octave\r
+ noteOnHexCode = self.wiimotes[correctedJoyId].getNoteOnHexCode()\r
+ baseTime = pygame.midi.time()\r
+ self.wiimotes[correctedJoyId].port.write([[[noteOnHexCode,60+12*octave,127],baseTime],[[noteOnHexCode,60+12*octave,0],baseTime+500],[[noteOnHexCode,65+12*octave,100],baseTime+510],[[noteOnHexCode,65+12*octave,0],baseTime+1000]])\r
+ self.wiimotes[correctedJoyId].instrument = None\r
+ \r
+ def joyClicked(self,joyEvent):\r
+ joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()\r
+ correctedJoyId = joyNames.index(joyName)\r
+ self.wiimotes[correctedJoyId].cursor.flash(400)\r
+ if self.focus[correctedJoyId] != None :\r
+ self.imageRects.pop(self.focus[correctedJoyId])\r
+ instrumentNumber = self.displayedInstruments.pop(self.focus[correctedJoyId])\r
+ self.drawBackground()\r
+ self.imageRects.append(self.drawInstrument(instrumentNumber))\r
+ self.displayedInstruments.append(instrumentNumber)\r
+ self.wiimotes[correctedJoyId].instrument = self.instruments[instrumentNumber]\r
+ octave = self.wiimotes[correctedJoyId].instrument.octave\r
+ noteOnHexCode = self.wiimotes[correctedJoyId].getNoteOnHexCode()\r
+ baseTime = pygame.midi.time()\r
+ self.wiimotes[correctedJoyId].port.write([[[noteOnHexCode,60+12*octave,127],baseTime],[[noteOnHexCode,60+12*octave,0],baseTime+500],[[noteOnHexCode,65+12*octave,100],baseTime+510],[[noteOnHexCode,65+12*octave,0],baseTime+1000]])\r
+ self.wiimotes[correctedJoyId].instrument = None\r
+ \r
+\r
+def zoomRect(rect, ratio):\r
+ zoomedRect = rect.inflate(int(floor((ratio - 1) * rect.width)), int(floor((ratio - 1) * rect.height)))\r
+ return(zoomedRect) \r
+\r
+if __name__ == "__main__":\r
+ pygame.init()\r
+ #pygame.event.set_blocked([pygame.MOUSEBUTTONDOWN,pygame.MOUSEBUTTONUP,pygame.MOUSEMOTION])\r
+ \r
+ pygame.midi.init()\r
+ instruments = [Instrument(majorScale, i + 1, "".join(["../instruments/instrumentImages/", instrumentImagePathList[i], ".jpg"]), octaves[i]) for i in range(9)]\r
+ \r
+ joys = [pygame.joystick.Joystick(id).get_name() for id in range(pygame.joystick.get_count())]\r
+ joyOffset = joys.index(joyNames[0])\r
+ pygame.joystick.Joystick(joyOffset).init()\r
+ print(joyOffset) \r
+ \r
+ ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]\r
+ portOffset = ports.index(portNames[0])\r
+ print(portOffset)\r
+ \r
+ window = pygame.display.set_mode((1280, 1024),pygame.FULLSCREEN)\r
+ screen = pygame.display.get_surface()\r
+ clock = pygame.time.Clock() \r
+ cursorImages = createImageListFromPath('../cursor/cursorImages/black', 11)\r
+ durations = [75 for i in range(len(cursorImages))]\r
+ \r
+ extsc = False\r
+ casc = False\r
+ \r
+ jadbt = [3, 4, 5, 3, 4, 4, 5, 6, 6, 5, 3, 3, 4, 5, 3, 4, 4, 5, 6, 7, 3]\r
+ song = None\r
+ \r
+ cursors = [WarpingCursor(None, cursorImages, durations, (300 * i, 300 * i),'../cursor/cursorImages/black/flash.png') for i in range(1)]\r
+ wiimotes = [Wiimote(i, i + portOffset, pygame.midi.Output(i+portOffset,latency = 20), None, cursors[i]) for i in range(1)]\r
+ familiarize = Familiarizer(instruments, wiimotes, window, screen, clock, joyOffset, portOffset)\r
+ for wiimote in wiimotes:\r
+ del wiimote.port \r
+ \r
+ pygame.midi.quit()\r
+ sys.exit()\r
--- /dev/null
+'''\r
+Created on 12 nov. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+from logging.FamiliarizerLog import FamiliarizerLog\r
+\r
+import os\r
+import sys\r
+import subprocess\r
+\r
+import pygame\r
+import pygame.midi\r
+import pickle\r
+\r
+from pygame.locals import *\r
+\r
+from pgu import gui\r
+\r
+from StaticFamiliarizer import StaticFamiliarizer\r
+from SongFamiliarizer import SongFamiliarizer\r
+from instruments.Instrument import Instrument\r
+from cursor.WarpingCursor import *\r
+from controllers.Wiimote import Wiimote\r
+from logging.FamiliarizerLog import FamiliarizerLog\r
+from songs.Song import Song\r
+from constants import *\r
+from SongPlayingScreen import SongPlayingScreen\r
+from DummyInstrumentChoice import DummyInstrumentChoice\r
+\r
+class FamiliarizerPGUConfiguration(gui.Desktop):\r
+ '''\r
+ classdocs\r
+ '''\r
+\r
+\r
+ def __init__(self,window,defaultParams = False):\r
+ '''\r
+ Constructor\r
+ '''\r
+ gui.Desktop.__init__(self)\r
+ self.done = False\r
+ self.level = 0\r
+ self.scale = scaleDict["majorScale"]\r
+ self.fileName = fileName\r
+ self.log = None\r
+ self.activeWiimotes = [False for i in range(4)] \r
+ \r
+ pygame.font.init()\r
+\r
+ self.titleFont = pygame.font.Font(None,100)\r
+ self.font = pygame.font.Font(None,70)\r
+ self.spaceSize = (100,100)\r
+ \r
+ self.goButton = gui.Button(self.createLabel("Go"))\r
+ self.goButton.connect(gui.CLICK,self.goButtonClicked,None)\r
+ \r
+ self.quitButton = gui.Button(self.createLabel("Fin"))\r
+ self.quitButton.connect(gui.CLICK,self.quitButtonClicked,None)\r
+ \r
+ self.levelSelect = gui.Select()\r
+ for i in range(3) :\r
+ self.levelSelect.add(self.createLabel(str(i+1)),i)\r
+ self.levelSelect.connect(gui.CHANGE,self.levelSelectChanged,None)\r
+ \r
+ self.activeWiimoteSwitches = [gui.Switch(False) for i in range(4)]\r
+ for i in range(len(self.activeWiimoteSwitches)) :\r
+ self.activeWiimoteSwitches[i].connect(gui.CHANGE,self.activeWiimoteSwitchesChanged,i) \r
+ \r
+ self.connect(gui.QUIT,self.quit,None)\r
+ \r
+ self.window = window\r
+ \r
+ ##The table code is entered much like HTML.\r
+ ##::\r
+ mainContainer = gui.Table() \r
+ \r
+ c = gui.Table()\r
+ \r
+# c.tr()\r
+# c.td(self.createLabel("MINWii",self.titleFont),colspan = 4)\r
+ \r
+ c.tr()\r
+ c.td(gui.Spacer(*self.spaceSize))\r
+ \r
+ c.tr()\r
+ c.td(self.createLabel("Niveau :"))\r
+ c.td(self.levelSelect,colspan=3)\r
+ \r
+ c.tr()\r
+ c.td(gui.Spacer(*self.spaceSize))\r
+ \r
+ c.tr()\r
+ c.td(self.createLabel("Joueurs :", self.font))\r
+ playerTable = gui.Table()\r
+ for i in range(len(self.activeWiimoteSwitches)):\r
+ playerTable.td(self.createLabel(" " + str(i+1)+" ", self.font))\r
+ playerTable.td(self.activeWiimoteSwitches[i])\r
+ c.td(playerTable,colspan = 3)\r
+ \r
+ c.tr()\r
+ c.td(gui.Spacer(*self.spaceSize))\r
+ \r
+ c.tr()\r
+ c.td(self.goButton)\r
+ c.td(self.quitButton,colspan=3) \r
+ \r
+ c.tr()\r
+ c.td(gui.Spacer(500,500))\r
+ \r
+ mainContainer.add(c,0,0)\r
+ \r
+ if defaultParams :\r
+ self.goButtonClicked()\r
+ else :\r
+ self.run(mainContainer)\r
+ \r
+ def open_file_browser(self,data=None):\r
+ d = gui.FileDialog()\r
+ d.connect(gui.CHANGE, self.handle_file_browser_closed, d)\r
+ d.open()\r
+ \r
+\r
+ def handle_file_browser_closed(self,dlg):\r
+ if dlg.value:\r
+ self.file = dlg.value\r
+ \r
+ def createLabel(self,text,font = None):\r
+ if font == None :\r
+ font = self.font\r
+ w,h = self.font.size(text)\r
+ label = gui.Label(text,width=w,height=h,font = font)\r
+ return(label)\r
+ \r
+ def levelSelectChanged(self,data=None):\r
+ self.level = self.levelSelect.value\r
+ \r
+ def quitButtonClicked(self,data = None):\r
+ self.done = True\r
+ self.quit()\r
+ \r
+ def activeWiimoteSwitchesChanged(self,data = None):\r
+ if self.activeWiimoteSwitches[data].value :\r
+ for i in range(len(self.activeWiimoteSwitches)) :\r
+ if self.activeWiimoteSwitches[i].value and data != i :\r
+ self.activeWiimoteSwitches[i].click() \r
+ for i in range(len(self.activeWiimoteSwitches)) :\r
+ self.activeWiimotes[i] = self.activeWiimoteSwitches[i].value\r
+ \r
+ def hasActiveWiimote(self):\r
+ hasActive = False\r
+ for active in self.activeWiimotes :\r
+ if active :\r
+ hasActive = True\r
+ break\r
+ return(hasActive)\r
+ \r
+\r
+ \r
+ \r
+ def goButtonClicked(self,data = None): \r
+ \r
+ pygame.midi.init()\r
+ instruments = [Instrument(self.scale, i + 1, "".join(["../instruments/instrumentImages/", instrumentImagePathList[i], ".jpg"]), octaves[i]) for i in range(9)]\r
+ \r
+ joys = [[id,pygame.joystick.Joystick(id).get_name()] for id in range(pygame.joystick.get_count())]\r
+ for joy in joys:\r
+ if joy[1] in joyNames:\r
+ pygame.joystick.Joystick(joy[0]).init() \r
+ \r
+ ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]\r
+ portOffset = ports.index(portNames[0])\r
+ print(portOffset)\r
+ \r
+ screen = pygame.display.get_surface()\r
+ clock = pygame.time.Clock() \r
+ cursorImages=[['../cursor/cursorImages/black/10.png'],['../cursor/cursorImages/red/10.png'],['../cursor/cursorImages/blue/10.png'],['../cursor/cursorImages/green/10.png']]\r
+ durations = [75 for i in range(len(cursorImages))]\r
+ \r
+ extsc = True\r
+ casc = False\r
+ easyMode = True\r
+ \r
+ song = Song(scaleDict["majorScale"],[3,9,6,4,1,8,5,7,2,10],True)\r
+\r
+ wiimoteCount = 4\r
+ \r
+ cursors = [WarpingCursor(None, cursorImages[i], durations, (300 * i, 300 * i),'../cursor/cursorImages/black/flash.png') for i in range(wiimoteCount)]\r
+ wiimotes = [Wiimote(i, i + portOffset, None, instruments[0], cursors[i]) for i in range(wiimoteCount)]\r
+ dummyInstrumentChoice = DummyInstrumentChoice(wiimotes, self.window, screen, clock, joys, portOffset, self.activeWiimotes)\r
+ if not self.hasActiveWiimote():\r
+ self.activeWiimotes[0] = True\r
+ if self.level < 2 :\r
+ familiarize = StaticFamiliarizer(wiimotes, self.window, screen, clock, joys, portOffset,self.activeWiimotes,level = self.level)\r
+ elif familiarize.nextLevel == 2 :\r
+ familiarize = SongFamiliarizer(wiimotes, self.window, screen, clock, joys, portOffset,song,self.activeWiimotes,casc,extsc,easyMode)\r
+ else :\r
+ familiarize = SongPlayingScreen(dummyInstrumentChoice,songDict["clairdelalune"],easyMode = True)\r
+ \r
+ while familiarize.nextLevel != None :\r
+ if familiarize.nextLevel < 2 :\r
+ familiarize = StaticFamiliarizer(wiimotes, self.window, screen, clock, joys, portOffset,self.activeWiimotes,level = familiarize.nextLevel,eventLog = familiarize.eventLog)\r
+ elif familiarize.nextLevel == 2 :\r
+ familiarize = SongFamiliarizer(wiimotes, self.window, screen, clock, joys, portOffset,song,self.activeWiimotes,casc,extsc,easyMode,eventLog = familiarize.eventLog)\r
+ else :\r
+ familiarize = SongPlayingScreen(dummyInstrumentChoice,songDict["clairdelalune"],easyMode = True,eventLog = familiarize.eventLog)\r
+ \r
+ for wiimote in wiimotes:\r
+ del wiimote.port \r
+ \r
+ pygame.midi.quit()\r
+ \r
+ i = 1 \r
+ filePath = "".join([self.fileName,str(i),".fmwi"]) \r
+ while os.path.exists(filePath):\r
+ i += 1\r
+ filePath = "".join([self.fileName,str(i),".fmwi"])\r
+ \r
+ f = file(filePath, 'w')\r
+ self.log = FamiliarizerLog(familiarize.eventLog,self.level,self.activeWiimotes)\r
+ pickler = pickle.Pickler(f)\r
+ pickler.dump(self.log)\r
+ \r
+ f.close()\r
+ \r
+ self.repaint()\r
+ \r
+if __name__ == "__main__" :\r
+ pygame.init()\r
+ modeResolution = (1024,768)\r
+ window = pygame.display.set_mode(modeResolution,pygame.FULLSCREEN)\r
+ familiarizer = FamiliarizerPGUConfiguration(window)\r
+ pygame.quit() \r
+ \r
+ \r
+ \r
+
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 15 juil. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+\r
+import pygame\r
+import pygame.midi\r
+import sys\r
+import time\r
+import pickle\r
+\r
+from numpy import array\r
+from numpy.linalg import norm\r
+\r
+from math import floor\r
+\r
+from gui.constants import *\r
+from PlayingScreen import PlayingScreen\r
+from instruments.Instrument import Instrument\r
+from cursor.WarpingCursor import *\r
+from controllers.Wiimote import Wiimote\r
+from logging.EventLog import EventLog\r
+from logging.PickleableEvent import PickleableEvent \r
+\r
+class InstrumentChoice:\r
+ '''\r
+ The screen for choosing instruments\r
+ \r
+ instruments: \r
+ The available instruments \r
+ wiimotes: \r
+ The wiimotes used in this session\r
+ window:\r
+ The main display window\r
+ screen:\r
+ The main display surface\r
+ clock:\r
+ The clock used to animate the screen\r
+ savedScreen:\r
+ The background that is painted every time\r
+ playerScreen:\r
+ The buffer for painting everything before bliting\r
+ width:\r
+ The width of the window in pixels\r
+ height:\r
+ The height of the window in pixels\r
+ done:\r
+ Goes to True when all instruments have been selected\r
+ cursorPositions:\r
+ The positions of the cursors on the screen, in pixels\r
+ imageRects:\r
+ The rectangles where the images of the instruments are located\r
+ focus:\r
+ The numbers of the instruments currently in focus\r
+ '''\r
+ \r
+ def __init__(self, instruments, wiimotes, window, screen, clock, joys, portOffset, activeWiimotes, eventLog=None, replay = False, logFilePath = None, scaleFactor = 1):\r
+ '''\r
+ Constructor\r
+ \r
+ instruments: \r
+ The instruments for this session \r
+ wiimotes: \r
+ The wiimotes used in this session\r
+ '''\r
+ self.scaleFactor = scaleFactor\r
+ \r
+ self.instruments = instruments\r
+ self.wiimotes = wiimotes\r
+ self.window = window\r
+ self.screen = screen\r
+ self.clock = clock\r
+ self.width = int(floor(screen.get_width()*self.scaleFactor))\r
+ self.height = int(floor(screen.get_height()*self.scaleFactor))\r
+ self.blitOrigin = ((self.screen.get_width()-self.width)/2,(self.screen.get_height()-self.height)/2) \r
+ self.joys = joys\r
+ self.portOffset = portOffset\r
+ \r
+ self.activeWiimotes = activeWiimotes\r
+ \r
+ self.currentWiimote = 0\r
+ while not self.activeWiimotes[self.currentWiimote] :\r
+ self.currentWiimote += 1\r
+ self.done = False \r
+ \r
+ self.cursorPositions = []\r
+ self.imageRects = []\r
+ self.savedImageRects = []\r
+ self.focus = []\r
+ self.zoomed = []\r
+ \r
+ if eventLog == None:\r
+ self.eventLog = EventLog()\r
+ self.replay = False\r
+ else:\r
+ self.eventLog = eventLog\r
+ self.replay = replay\r
+ print self.replay\r
+ \r
+ #There are 3 instruments per row, up to 9 instruments\r
+ #Draw their images on the screen\r
+ self.savedScreen = pygame.Surface(self.screen.get_size())\r
+ self.savedScreen.fill((255, 255, 255))\r
+ for i in range(len(self.instruments)) :\r
+ drawPos = array([(self.width / 3) * (i % 3), (self.height / 3) * (i / 3)])\r
+ curImage = pygame.image.load(self.instruments[i].image).convert_alpha()\r
+ scaledImage = pygame.transform.smoothscale(curImage, (self.width / 3, self.height / 3))\r
+ self.imageRects.append(self.savedScreen.blit(scaledImage, drawPos + self.blitOrigin))\r
+ self.savedImageRects = self.imageRects[:]\r
+ #Draw the initial cursor on the buffer\r
+ self.playerScreen = pygame.Surface(self.savedScreen.get_size())\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ #Create the list of instrument focus (one focus per wiimote)\r
+ self.focus.append(0)\r
+ self.zoomed.append(None)\r
+ #Set the screen for the cursors (it can't be set before)\r
+ self.wiimotes[i].cursor.screen = self.playerScreen\r
+ self.cursorPositions.append(self.wiimotes[i].cursor.centerPosition)\r
+ \r
+ self.wiimotes[self.currentWiimote].cursor.blit(self.playerScreen)\r
+ \r
+ #The main loop\r
+ while self.done == False :\r
+ \r
+ #Clear the cursors from the screen\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ \r
+ # Limit frame speed to 50 FPS\r
+ #\r
+ timePassed = self.clock.tick(50)\r
+ \r
+ if self.replay:\r
+ self.eventLog.update(timePassed)\r
+ pickledEventsToPost = self.eventLog.getPickledEvents() \r
+ for pickledEvent in pickledEventsToPost:\r
+ pygame.event.post(pickledEvent.event)\r
+ \r
+ events = pygame.event.get()\r
+ \r
+ if not self.replay:\r
+ pickledEvents = [PickleableEvent(event.type,event.dict) for event in events if self.eventFilter(event)]\r
+ if pickledEvents != [] :\r
+ self.eventLog.appendEventGroup(pickledEvents)\r
+ \r
+ for event in events:\r
+ self.input(event)\r
+ \r
+ \r
+ if self.zoomed[self.currentWiimote] != None :\r
+ self.imageRects = self.savedImageRects[:]\r
+ #inflate the chosen rect\r
+ zoomedRectNumber = self.zoomed[self.currentWiimote]\r
+ newRect = zoomRect(self.imageRects[zoomedRectNumber], 1.3)\r
+ self.imageRects[zoomedRectNumber] = newRect\r
+ curImage = pygame.image.load(self.instruments[zoomedRectNumber].image).convert_alpha()\r
+ self.scaledImage = pygame.transform.smoothscale(curImage, newRect.size)\r
+ self.playerScreen.blit(self.scaledImage, newRect.topleft)\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])\r
+ \r
+ self.wiimotes[self.currentWiimote].cursor.blit(self.playerScreen) \r
+ \r
+ if self.zoomed[self.currentWiimote] != None and self.imageRects[self.zoomed[self.currentWiimote]].collidepoint(self.cursorPositions[self.currentWiimote]):\r
+ self.focus[self.currentWiimote] = self.zoomed[self.currentWiimote]\r
+ pygame.draw.rect(self.playerScreen, pygame.Color(0, 255, 0, 255), self.imageRects[self.zoomed[self.currentWiimote]], 10)\r
+ else:\r
+ for i in range(len(self.imageRects)) :\r
+ if self.imageRects[i].collidepoint(self.cursorPositions[self.currentWiimote]):\r
+ self.focus[self.currentWiimote] = i\r
+ pygame.draw.rect(self.playerScreen, pygame.Color(0, 255, 0, 255), self.imageRects[i], 10)\r
+ if self.zoomed[self.currentWiimote] != None:\r
+ self.playerScreen.blit(self.scaledImage, self.imageRects[self.zoomed[self.currentWiimote]].topleft)\r
+ \r
+ self.screen.blit(self.playerScreen, (0, 0))\r
+ \r
+ pygame.display.flip()\r
+ \r
+ def input(self, event):\r
+ if event.type == pygame.QUIT:\r
+ pygame.midi.quit()\r
+ sys.exit()\r
+ if event.type == pygame.JOYAXISMOTION:\r
+ self.updateCursorPositionFromJoy(event)\r
+ if event.type == pygame.JOYBUTTONDOWN :\r
+ self.assignInstrumentToWiimote(event)\r
+ if event.type == pygame.MOUSEBUTTONDOWN:\r
+ if self.zoomed[self.currentWiimote] == self.focus[self.currentWiimote]:\r
+ self.assignInstrumentToMouse(event)\r
+ else:\r
+ self.zoomed[self.currentWiimote] = self.focus[self.currentWiimote]\r
+ if event.type == pygame.MOUSEMOTION:\r
+ self.updateCursorPositionFromMouse(event) \r
+ \r
+ def updateCursorPositionFromJoy(self, joyEvent):\r
+ joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()\r
+ print joyName\r
+ correctedJoyId = joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]: \r
+ if correctedJoyId < len(self.cursorPositions):\r
+ if joyEvent.axis == 0 :\r
+ self.cursorPositions[correctedJoyId] = (int((joyEvent.value + 1) / 2 * self.screen.get_width()), self.cursorPositions[correctedJoyId][1])\r
+ if joyEvent.axis == 1 :\r
+ self.cursorPositions[correctedJoyId] = (self.cursorPositions[correctedJoyId][0], int((joyEvent.value + 1) / 2 * self.screen.get_height())) \r
+ \r
+ def assignInstrumentToWiimote(self, joyEvent):\r
+ joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()\r
+ correctedJoyId = joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ if self.zoomed[correctedJoyId] == self.focus[correctedJoyId]:\r
+ self.wiimotes[correctedJoyId].instrument = self.instruments[self.focus[correctedJoyId]]\r
+ self.zoomed[correctedJoyId] = None\r
+ self.imageRects = self.savedImageRects[:]\r
+ if self.currentWiimote<len(self.wiimotes)-1:\r
+ self.currentWiimote = self.currentWiimote+1\r
+ else:\r
+ self.zoomed[correctedJoyId] = self.focus[correctedJoyId]\r
+ if self.hasFinished():\r
+ self.done = True\r
+ \r
+ def updateCursorPositionFromMouse(self, mouseEvent):\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ self.cursorPositions[correctedJoyId] = mouseEvent.pos\r
+ \r
+ def assignInstrumentToMouse(self, mouseEvent):\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ self.wiimotes[correctedJoyId].instrument = self.instruments[self.focus[correctedJoyId]]\r
+ if self.hasFinished():\r
+ self.done = True\r
+ \r
+ def hasFinished(self):\r
+ finished = True\r
+ for i in range(len(self.wiimotes)):\r
+ if self.wiimotes[i].instrument == None and self.activeWiimotes[i]:\r
+ finished = False\r
+ return(finished)\r
+ \r
+ def eventFilter(self, event):\r
+ c = event.type\r
+ if c == 17:\r
+ return False\r
+ elif c == pygame.MOUSEMOTION or pygame.MOUSEBUTTONDOWN or pygame.MOUSEBUTTONUP or pygame.JOYAXISMOTION or pygame.JOYBUTTONDOWN or pygame.JOYBUTTONUP or pygame.KEYDOWN:\r
+ return True\r
+ else:\r
+ return False\r
+\r
+def zoomRect(rect, ratio):\r
+ zoomedRect = rect.inflate(int(floor((ratio - 1) * rect.width)), int(floor((ratio - 1) * rect.height)))\r
+ return(zoomedRect) \r
+\r
+if __name__ == "__main__":\r
+ pygame.init()\r
+ #pygame.event.set_blocked([pygame.MOUSEBUTTONDOWN,pygame.MOUSEBUTTONUP,pygame.MOUSEMOTION])\r
+ \r
+ pygame.midi.init()\r
+ instruments = [Instrument(majorScale, i + 1, "".join(["../instruments/instrumentImages/", instrumentImagePathList[i], ".jpg"]), octaves[i]) for i in range(9)]\r
+ \r
+ joys = [pygame.joystick.Joystick(id).get_name() for id in range(pygame.joystick.get_count())]\r
+ joyOffset = joys.index(joyNames[0])\r
+ pygame.joystick.Joystick(joyOffset).init()\r
+ print(joyOffset) \r
+ \r
+ ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]\r
+ portOffset = ports.index(portNames[0])\r
+ print(portOffset)\r
+ \r
+ window = pygame.display.set_mode((1280, 1024),pygame.FULLSCREEN)\r
+ screen = pygame.display.get_surface()\r
+ clock = pygame.time.Clock() \r
+ cursorImages = createImageListFromPath('../cursor/cursorImages/black', 11)\r
+ durations = [75 for i in range(len(cursorImages))]\r
+ \r
+ extsc = False\r
+ casc = False\r
+ \r
+ jadbt = [3, 4, 5, 3, 4, 4, 5, 6, 6, 5, 3, 3, 4, 5, 3, 4, 4, 5, 6, 7, 3]\r
+ song = None\r
+ \r
+ cursors = [WarpingCursor(None, cursorImages, durations, (300 * i, 300 * i)) for i in range(1)]\r
+ wiimotes = [Wiimote(i, i + portOffset, None, None, cursors[i]) for i in range(1)]\r
+ choice = InstrumentChoice(instruments, wiimotes, window, screen, clock, joyOffset, portOffset)\r
+ play = PlayingScreen(choice, song, casc, extsc)\r
+ for wiimote in wiimotes:\r
+ del wiimote.port \r
+ \r
+ f = file('temp.pkl', 'w')\r
+ pickler = pickle.Pickler(f)\r
+ pickler.dump(play.eventLog.eventGroups)\r
+ pickler.dump(play.eventLog.times)\r
+ f.close()\r
+ \r
+ f = file('temp.pkl', 'r')\r
+ unpickler = pickle.Unpickler(f)\r
+ eventGroups = unpickler.load()\r
+ times = unpickler.load()\r
+ f.close()\r
+ eventLog = EventLog(eventGroups,times)\r
+ \r
+ cursors = [WarpingCursor(None, cursorImages, durations, (300 * i, 300 * i)) for i in range(1)]\r
+ wiimotes = [Wiimote(i, i + portOffset, None, None, cursors[i]) for i in range(1)]\r
+ choice2 = InstrumentChoice(instruments, wiimotes, window, screen, clock, joyOffset, portOffset, eventLog)\r
+ play = PlayingScreen(choice2, song, casc, extsc)\r
+ \r
+ for wiimote in wiimotes:\r
+ del wiimote.port\r
+ \r
+ pygame.midi.quit()\r
+ sys.exit()\r
--- /dev/null
+'''\r
+Created on 17 dec. 2009\r
+\r
+@author: samsam\r
+'''\r
+\r
+import pgu.gui as pguGui\r
+import os\r
+import constants\r
+\r
+class MINWiiDialog(pguGui.FileDialog):\r
+ '''\r
+ classdocs\r
+ '''\r
+ \r
+ def __init__(self, font = None, width = 800, height = 600,path=None):\r
+ pguGui.FileDialog.__init__(self,title_txt = "Choisir une chanson", customFont = font,customWidth = width, customHeight = height,folderText = "", fileText = "",path =path,showCurDir = False)\r
+ \r
+ def _list_dir_(self):\r
+ self.input_dir.value = self.curdir\r
+ self.input_dir.pos = len(self.curdir)\r
+ self.input_dir.vpos = 0\r
+ dirs = []\r
+ files = []\r
+ try:\r
+ for i in os.listdir(self.curdir):\r
+ if os.path.isdir(os.path.join(self.curdir, i)): dirs.append(i)\r
+ else: files.append(i)\r
+ except:\r
+ self.input_file.value = "Opps! no access"\r
+ #if '..' not in dirs: dirs.append('..')\r
+ dirs.sort()\r
+ dirs = ['..'] + dirs\r
+ \r
+ files.sort()\r
+ for i in dirs:\r
+ #item = ListItem(image=self.dir_img, text=i, value=i)\r
+ if self.customFont == None :\r
+ self.list.add(i,image=self.dir_img,value=i)\r
+ else :\r
+ if i == ".." or i[0] != ".":\r
+ label = pguGui.basic.Label(i,font = self.customFont)\r
+ self.list.add(label,image=self.dir_img,value=i)\r
+ for i in files:\r
+ #item = ListItem(image=None, text=i, value=i)\r
+ if self.customFont == None :\r
+ self.list.add(i,value=i)\r
+ else:\r
+ if i.endswith(".smwi"):\r
+ key = i[:-5]\r
+ if key in constants.reversedReadabilityDict : \r
+ label = pguGui.basic.Label(constants.reversedReadabilityDict[key],font = self.customFont)\r
+ else :\r
+ label = pguGui.basic.Label(key,font = self.customFont)\r
+ self.list.add(label,value=i)\r
+ #self.list.resize()\r
+ self.list.set_vertical_scroll(0)\r
+ #self.list.repaintall()\r
+
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 12 nov. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+\r
+import os\r
+import sys\r
+import subprocess\r
+\r
+import pygame\r
+import pygame.midi\r
+import pickle\r
+\r
+from pygame.locals import *\r
+\r
+from pgu import gui as pguGui\r
+\r
+from PlayingScreen import PlayingScreen\r
+from SongPlayingScreen import SongPlayingScreen\r
+from InstrumentChoice import InstrumentChoice\r
+from instruments.Instrument import Instrument\r
+from cursor.WarpingCursor import *\r
+from controllers.Wiimote import Wiimote\r
+from logging.Log import Log\r
+from songs.Song import Song,loadSong\r
+from constants import *\r
+from MINWiiDialog import MINWiiDialog\r
+\r
+class PGUConfiguration(pguGui.Desktop):\r
+ '''\r
+ classdocs\r
+ '''\r
+\r
+\r
+ def __init__(self,window):\r
+ '''\r
+ Constructor\r
+ '''\r
+ pguGui.Desktop.__init__(self)\r
+ self.extendedScale = False\r
+ self.cascade = False\r
+ self.song = None\r
+ self.scale = scaleDict["majorScale"]\r
+ self.log = None\r
+ self.done = False\r
+ self.easyMode = True\r
+ self.mode = 0\r
+ self.activeWiimotes = [False for i in range(4)]\r
+ self.alwaysDown = False\r
+ \r
+ self.file = None\r
+ #fileName is the path for the log file\r
+ self.fileName = fileName\r
+ self.titleFont = pygame.font.Font(None,100)\r
+ self.font = pygame.font.Font(None,70)\r
+ self.spaceSize = (100,100)\r
+ \r
+ self.browseButton = pguGui.Button(self.createLabel("Choisir..."))\r
+ self.browseButton.connect(pguGui.CLICK, self.open_file_browser, None)\r
+ \r
+ self.songSwitch = pguGui.Switch(False)\r
+ \r
+ self.modeSelect = pguGui.Select()\r
+ for key in modeDict.keys() :\r
+ self.modeSelect.add(self.createLabel(reversedReadabilityDict[key]),key)\r
+ self.modeSelect.connect(pguGui.CHANGE,self.modeSelectChanged,None)\r
+ \r
+ self.activeWiimoteSwitches = [pguGui.Switch(False) for i in range(4)]\r
+ for i in range(len(self.activeWiimoteSwitches)) :\r
+ self.activeWiimoteSwitches[i].connect(pguGui.CHANGE,self.activeWiimoteSwitchesChanged,i)\r
+ \r
+ self.goButton = pguGui.Button(self.createLabel("Go"))\r
+ self.goButton.connect(pguGui.CLICK,self.goButtonClicked,None)\r
+ \r
+ self.quitButton = pguGui.Button(self.createLabel("Fin"))\r
+ self.quitButton.connect(pguGui.CLICK,self.quitButtonClicked,None)\r
+ \r
+ self.connect(pguGui.QUIT,self.quit,None)\r
+ \r
+ self.window = window\r
+ \r
+ ##The table code is entered much like HTML.\r
+ ##::\r
+ \r
+ self.mainTable = pguGui.Table()\r
+ \r
+ self.fillMainTable()\r
+ \r
+# self.mainTable.tr()\r
+# self.mainTable.td(self.createLabel("MINWii",self.titleFont),colspan = 4)\r
+ \r
+ self.run(self.mainTable)\r
+ \r
+ def open_file_browser(self,data=None):\r
+ d = MINWiiDialog(font = self.font,width = 800, height = 600,path = "../songs/smwis")\r
+ d.connect(pguGui.CHANGE, self.handle_file_browser_closed, d)\r
+ d.open()\r
+ \r
+\r
+ def handle_file_browser_closed(self,dlg):\r
+ if dlg.value:\r
+ if os.path.isfile(dlg.value):\r
+ self.file = dlg.value\r
+ self.song = loadSong(self.file)\r
+ key = os.path.basename(self.file)[:-5]\r
+ if key in reversedReadabilityDict : \r
+ label = self.createLabel(reversedReadabilityDict[key])\r
+ else :\r
+ label = self.createLabel(key)\r
+ self.browseButton = pguGui.Button(label)\r
+ self.browseButton.connect(pguGui.CLICK, self.open_file_browser, None)\r
+ if not self.songSwitch.value :\r
+ self.songSwitch.click()\r
+ self.mainTable.clear()\r
+ self.fillMainTable()\r
+ \r
+ def fillMainTable(self):\r
+ \r
+ self.mainTable.tr()\r
+ self.mainTable.td(pguGui.Spacer(*self.spaceSize))\r
+ \r
+ self.mainTable.tr()\r
+ self.mainTable.td(self.createLabel("Chanson :"))\r
+ self.mainTable.td(self.browseButton,colspan=2)\r
+ self.mainTable.td(self.songSwitch)\r
+ \r
+ self.mainTable.tr()\r
+ self.mainTable.td(pguGui.Spacer(*self.spaceSize))\r
+ \r
+ self.mainTable.tr()\r
+ self.mainTable.td(self.createLabel("Niveau :"))\r
+ self.mainTable.td(self.modeSelect,colspan=3)\r
+ \r
+ self.mainTable.tr()\r
+ self.mainTable.td(pguGui.Spacer(*self.spaceSize))\r
+ \r
+ self.mainTable.tr()\r
+ self.mainTable.td(self.createLabel("Joueurs :", self.font))\r
+ playerTable = pguGui.Table()\r
+ for i in range(len(self.activeWiimoteSwitches)):\r
+ playerTable.td(self.createLabel(" " + str(i+1)+" ", self.font))\r
+ playerTable.td(self.activeWiimoteSwitches[i])\r
+ self.mainTable.td(playerTable,colspan = 3)\r
+ \r
+ self.mainTable.tr()\r
+ self.mainTable.td(pguGui.Spacer(*self.spaceSize))\r
+ \r
+ self.mainTable.tr()\r
+ self.mainTable.td(self.goButton)\r
+ self.mainTable.td(self.quitButton,colspan=3) \r
+ \r
+ self.mainTable.tr()\r
+ self.mainTable.td(pguGui.Spacer(500,500)) \r
+ \r
+ def createLabel(self,text,font = None):\r
+ if font == None :\r
+ font = self.font\r
+ w,h = self.font.size(text)\r
+ label = pguGui.Label(text,width=w,height=h,font = font)\r
+ return(label)\r
+ \r
+ def songSelectChanged(self,data=None):\r
+ self.song = songDict[self.songSelect.value]\r
+ \r
+ def activeWiimoteSwitchesChanged(self,data = None):\r
+ if self.activeWiimoteSwitches[data].value :\r
+ for i in range(len(self.activeWiimoteSwitches)) :\r
+ if self.activeWiimoteSwitches[i].value and data != i :\r
+ self.activeWiimoteSwitches[i].click() \r
+ for i in range(len(self.activeWiimoteSwitches)) :\r
+ self.activeWiimotes[i] = self.activeWiimoteSwitches[i].value\r
+ \r
+ def modeSelectChanged(self,data = None):\r
+ self.mode = modeDict[self.modeSelect.value]\r
+ \r
+ def hasActiveWiimote(self):\r
+ hasActive = False\r
+ for i in self.activeWiimotes:\r
+ if i :\r
+ hasActive = True\r
+ return(hasActive)\r
+ \r
+ def quitButtonClicked(self,data = None):\r
+ self.done = True\r
+ print 'puti'\r
+ for isActive in self.activeWiimotes :\r
+ print isActive\r
+ self.quit()\r
+ \r
+ def goButtonClicked(self,data = None):\r
+ pygame.font.init()\r
+ \r
+ if not self.hasActiveWiimote():\r
+ self.activeWiimotes[0] = True\r
+ pygame.midi.init()\r
+ instruments = [Instrument(self.scale, i + 1, "".join(["../instruments/instrumentImages/", instrumentImagePathList[i], ".jpg"]), octaves[i]) for i in range(9)]\r
+ \r
+ joys = [[id,pygame.joystick.Joystick(id).get_name()] for id in range(pygame.joystick.get_count())]\r
+ for joy in joys:\r
+ print joy[1]\r
+ if joy[1] in joyNames:\r
+ print "On"\r
+ pygame.joystick.Joystick(joy[0]).init()\r
+ else :\r
+ print "off" \r
+ \r
+ ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]\r
+ portOffset = ports.index(portNames[0])\r
+ print(portOffset)\r
+ \r
+ screen = pygame.display.get_surface()\r
+ clock = pygame.time.Clock() \r
+ cursorImages=[['../cursor/cursorImages/black/10.png'],['../cursor/cursorImages/red/10.png'],['../cursor/cursorImages/blue/10.png'],['../cursor/cursorImages/green/10.png']]\r
+ durations = [75 for i in range(len(cursorImages[0]))]\r
+ \r
+ wiimoteCount = 4\r
+ cursors = [WarpingCursor(None, cursorImages[i], durations, (300 * i, 300 * i),flashImage = '../cursor/cursorImages/black/flash.png' ) for i in range(wiimoteCount)]\r
+ wiimotes = [Wiimote(i, i + portOffset, None, None, cursors[i]) for i in range(wiimoteCount)]\r
+ \r
+ if self.song != None and self.songSwitch.value :\r
+ \r
+ if self.mode == 0 :\r
+ self.extendedScale = self.song.requiresExtendedScale\r
+ self.cascade = True\r
+ self.easyMode = True\r
+ self.alwaysDown = True\r
+ elif self.mode == 1 :\r
+ self.extendedScale = self.song.requiresExtendedScale\r
+ self.cascade = True\r
+ self.easyMode = True\r
+ elif self.mode == 2:\r
+ self.extendedScale = self.song.requiresExtendedScale\r
+ self.cascade = False\r
+ self.easyMode = True\r
+ elif self.mode == 3:\r
+ self.extendedScale = True\r
+ self.cascade = False\r
+ self.easyMode = False\r
+ \r
+ choice = InstrumentChoice(instruments, wiimotes, self.window, screen, clock, joys, portOffset,self.activeWiimotes, scaleFactor = songScaleFactor)\r
+ play = SongPlayingScreen(choice, self.song,self.cascade, self.extendedScale,self.easyMode,self.alwaysDown)\r
+ \r
+ else:\r
+ \r
+ if self.mode == 0 :\r
+ self.extendedScale = False\r
+ self.cascade = False\r
+ elif self.mode == 1 :\r
+ self.extendedScale = True\r
+ self.cascade = False\r
+ elif self.mode == 2:\r
+ self.extendedScale = False\r
+ self.cascade = True\r
+ elif self.mode == 3:\r
+ self.extendedScale = True\r
+ self.cascade = True\r
+ \r
+ choice = InstrumentChoice(instruments, wiimotes, self.window, screen, clock, joys, portOffset,self.activeWiimotes)\r
+ play = PlayingScreen(choice, None,self.cascade, self.extendedScale) \r
+ \r
+ while play.backToInstrumentChoice == True :\r
+ \r
+ for wiimote in wiimotes:\r
+ del wiimote.port\r
+ \r
+ wiimotes = [Wiimote(i, i + portOffset, None, None, cursors[i]) for i in range(wiimoteCount)]\r
+ previousEventLog = choice.eventLog\r
+\r
+ if self.song != None :\r
+ choice = InstrumentChoice(instruments, wiimotes, self.window, screen, clock, joys, portOffset, self.activeWiimotes,eventLog = previousEventLog, replay = False, scaleFactor = songScaleFactor)\r
+ play = SongPlayingScreen(choice, self.song, False, self.extendedScale,self.easyMode)\r
+ else:\r
+ choice = InstrumentChoice(instruments, wiimotes, self.window, screen, clock, joys, portOffset,self.activeWiimotes, eventLog = previousEventLog, replay = False)\r
+ play = PlayingScreen(choice, None, self.cascade, self.extendedScale)\r
+ \r
+ for wiimote in wiimotes:\r
+ del wiimote.port\r
+ \r
+ i = 1 \r
+ filePath = "".join([self.fileName,str(i),".mwi"]) \r
+ while os.path.exists(filePath):\r
+ i += 1\r
+ filePath = "".join([self.fileName,str(i),".mwi"]) \r
+ \r
+ f = file(filePath, 'w')\r
+ self.log = Log(play.eventLog,self.scale,self.extendedScale,self.cascade,self.song,self.mode,self.activeWiimotes)\r
+ pickler = pickle.Pickler(f)\r
+ pickler.dump(self.log)\r
+ \r
+ f.close()\r
+ \r
+ pygame.midi.quit()\r
+ \r
+ self.repaint()\r
+ \r
+if __name__ == "__main__" :\r
+ pygame.init()\r
+ modeResolution = (1024,768)\r
+ window = pygame.display.set_mode(modeResolution,pygame.FULLSCREEN)\r
+ pgu = PGUConfiguration(window)\r
+ pygame.quit() \r
+ \r
+ \r
+ \r
+
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 23 juil. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+from math import floor, ceil\r
+import pygame\r
+import sys\r
+import colorsys\r
+import constants\r
+from gradients import gradients\r
+from logging.PickleableEvent import PickleableEvent\r
+\r
+\r
+class PlayingScreen:\r
+ '''\r
+ The screen on which the game is played\r
+ \r
+ wiimotes: \r
+ The wiimotes used in this session\r
+ window:\r
+ The main display window\r
+ screen:\r
+ The main display surface\r
+ clock:\r
+ The clock used to animate the screen\r
+ savedScreen:\r
+ The background that is painted every time\r
+ playerScreen:\r
+ The buffer for painting everything before bliting\r
+ width:\r
+ The width of the window in pixels\r
+ height:\r
+ The height of the window in pixels\r
+ extendScale :\r
+ True if the scale is G to C instead of C to C\r
+ cascade:\r
+ True if crossing from note to note with a button pressed triggers a new note\r
+ scaleSize:\r
+ The size of the scale used\r
+ cursorPositions:\r
+ The positions of the cursors on the screen, in pixels\r
+ '''\r
+ \r
+ \r
+ \r
+ def __init__(self, instrumentChoice=None, song = None, cascade=False, extendedScale=False, defaultInstrumentChannel = 16, defaultNote = 60):\r
+ '''\r
+ Constructor\r
+ ''' \r
+ self.blinkLength = 200\r
+ self.minimalVelocity = 64\r
+ self.shortScaleSize = 8\r
+ self.longScaleSize = 11\r
+ if not extendedScale:\r
+ self.offset = self.longScaleSize - self.shortScaleSize\r
+ else:\r
+ self.offset = 0\r
+ self.borderSize = 5\r
+ self.savedHighlightedNote = 0\r
+ \r
+ self.wiimotes = instrumentChoice.wiimotes\r
+ self.activeWiimotes = instrumentChoice.activeWiimotes\r
+ self.window = instrumentChoice.window\r
+ self.screen = instrumentChoice.screen\r
+ self.blitOrigin = instrumentChoice.blitOrigin\r
+ self.clock = instrumentChoice.clock\r
+ self.width = instrumentChoice.width\r
+ self.height = instrumentChoice.height\r
+ self.cursorPositions = instrumentChoice.cursorPositions\r
+ self.savedScreen = instrumentChoice.savedScreen\r
+ self.playerScreen = instrumentChoice.playerScreen\r
+ self.extendedScale = extendedScale\r
+ self.cascade = cascade\r
+ self.joys = instrumentChoice.joys\r
+ self.portOffset = instrumentChoice.portOffset\r
+ self.eventLog = instrumentChoice.eventLog\r
+ self.cursorPositions = instrumentChoice.cursorPositions\r
+ self.song = song\r
+ self.songIterator = self.moveToNextNote()\r
+ self.replay = instrumentChoice.replay\r
+ \r
+ self.defaultInstrumentChannel = defaultInstrumentChannel\r
+ self.defaultNote = defaultNote\r
+ \r
+ self.done = False\r
+ self.backToInstrumentChoice = False\r
+ self.easyMode = False\r
+ \r
+ self.highlightedNote = self.songIterator.next()\r
+ \r
+ self.blinkOn = False\r
+ self.savedBlinkOn = False\r
+ ##Will prevent the song to move on if two consecutive notes are identical and the buttons have not been released in between the two\r
+ ##i.e. it guarantees that there will be an attack between two identical consecutive notes\r
+ self.highlightIsFree = True\r
+ \r
+ self.noteRects = []\r
+ self.boundingRect = None\r
+ self.notes = []\r
+ self.buttonDown = []\r
+ self.velocityLock = []\r
+ \r
+ self._blinkOffset = 0\r
+ \r
+ self.font = pygame.font.Font(None,50)\r
+ self.firstWiimote = 0\r
+ while not self.activeWiimotes[self.firstWiimote] :\r
+ self.firstWiimote += 1\r
+ self.renderedNoteNames = [self.font.render(constants.noteNumberToName(note),False,(0,0,0)) for note in self.wiimotes[self.firstWiimote].instrument.notes[self.offset:]]\r
+ \r
+ self.drawBackground()\r
+ self.initializeWiimotes()\r
+ \r
+ #The main loop\r
+ while not self.done :\r
+ \r
+ #Clear the cursors from the screen\r
+ if self.hasChanged():\r
+ self.drawBackground()\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ \r
+ # Limit frame speed to 50 FPS\r
+ #\r
+ timePassed = self.clock.tick(50)\r
+ \r
+ self._blinkOffset += timePassed\r
+ if self._blinkOffset > self.blinkLength:\r
+ self._blinkOffset -= self.blinkLength\r
+ self.blinkOn = not self.blinkOn\r
+ \r
+ if self.replay:\r
+ self.eventLog.update(timePassed)\r
+ pickledEventsToPost = self.eventLog.getPickledEvents() \r
+ for pickledEvent in pickledEventsToPost:\r
+ pygame.event.post(pickledEvent.event)\r
+ \r
+ events = pygame.event.get()\r
+ \r
+ if not self.replay:\r
+ pickledEvents = [PickleableEvent(event.type,event.dict) for event in events]\r
+ if pickledEvents != [] :\r
+ self.eventLog.appendEventGroup(pickledEvents)\r
+ \r
+ for event in events:\r
+ self.input(event)\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ if self.activeWiimotes[i]:\r
+ self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])\r
+ if self.buttonDown[i] :\r
+ self.wiimotes[i].cursor.flash()\r
+ self.wiimotes[i].cursor.blit(self.playerScreen)\r
+ \r
+ self.screen.blit(self.playerScreen, (0,0))\r
+ \r
+ pygame.display.flip()\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ if self.activeWiimotes[i]:\r
+ self.wiimotes[i].stopNote(self.notes[i]) \r
+ \r
+ def drawBackground(self):\r
+ self.savedScreen.fill((255,255,255))\r
+ \r
+ if self.extendedScale :\r
+ self.scaleSize = self.longScaleSize\r
+ else:\r
+ self.scaleSize = self.shortScaleSize\r
+ \r
+ self.noteRects = [pygame.Rect(i * self.width / self.scaleSize+self.blitOrigin[0], self.blitOrigin[1], self.width / self.scaleSize + 1, self.height+1) for i in range(self.scaleSize)]\r
+ #inflate last noteRect to cover the far right pixels\r
+ self.noteRects[-1].width = self.noteRects[-1].width + 1\r
+ \r
+ #create bounding rect\r
+ self.boundingRect = self.noteRects[0].unionall(self.noteRects)\r
+ \r
+ #fill the rectangles with a color gradient\r
+ #We start with blue\r
+ startingHue = 0.66666666666666663\r
+ \r
+ for rectNumber in range(self.scaleSize):\r
+ colorRatio = float(rectNumber) / (self.scaleSize - 1)\r
+ #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up\r
+ hue = startingHue * (1 - colorRatio)\r
+ if self.song != None:\r
+ if rectNumber + self.offset == self.highlightedNote:\r
+ #The color of the bottom of the rectangle in hls coordinates\r
+ bottomColorHls = (hue, 0.6, 1)\r
+ #The color of the top of the rectangle in hls coordinates\r
+ topColorHls = (hue, 0.9, 1)\r
+ else:\r
+ #The color of the bottom of the rectangle in hls coordinates\r
+ bottomColorHls = (hue, 0.2, 1)\r
+ #The color of the top of the rectangle in hls coordinates\r
+ topColorHls = (hue, 0.4, 1)\r
+ else:\r
+ #The color of the bottom of the rectangle in hls coordinates\r
+ bottomColorHls = (hue, 0.6, 1)\r
+ #The color of the top of the rectangle in hls coordinates\r
+ topColorHls = (hue, 0.9, 1)\r
+ \r
+ #convert to rgb ranging from 0 to 255\r
+ bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]\r
+ topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]\r
+ #add transparency\r
+ bottomColorRgb.append(255)\r
+ topColorRgb.append(255)\r
+ #convert to tuple\r
+ bottomColorRgb = tuple(bottomColorRgb)\r
+ topColorRgb = tuple(topColorRgb) \r
+ \r
+ self.savedScreen.blit(gradients.vertical(self.noteRects[rectNumber].size, topColorRgb, bottomColorRgb), self.noteRects[rectNumber])\r
+ \r
+ textBlitPoint = (self.noteRects[rectNumber].left+(self.noteRects[rectNumber].width-self.renderedNoteNames[rectNumber].get_width())/2,\r
+ self.noteRects[rectNumber].bottom-self.renderedNoteNames[rectNumber].get_height())\r
+ \r
+ self.savedScreen.blit(self.renderedNoteNames[rectNumber], textBlitPoint)\r
+ \r
+ pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[rectNumber], 2)\r
+ \r
+ \r
+ if self.song != None and self.blinkOn:\r
+ borderSize = self.borderSize\r
+ pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 0), self.noteRects[self.highlightedNote-self.offset].inflate(borderSize/2,borderSize/2), borderSize)\r
+ \r
+ def initializeWiimotes(self):\r
+ for loop in self.wiimotes:\r
+ if loop.port == None :\r
+ loop.port = pygame.midi.Output(loop.portNumber)\r
+ self.notes.append(0)\r
+ self.buttonDown.append(False)\r
+ self.velocityLock.append(False)\r
+ \r
+ def updateCursorPositionFromJoy(self, joyEvent):\r
+ joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if correctedJoyId < len(self.cursorPositions):\r
+ if joyEvent.axis == 0 :\r
+ self.cursorPositions[correctedJoyId] = (int((joyEvent.value + 1) / 2 * self.screen.get_width()), self.cursorPositions[correctedJoyId][1])\r
+ if joyEvent.axis == 1 :\r
+ self.cursorPositions[correctedJoyId] = (self.cursorPositions[correctedJoyId][0], int((joyEvent.value + 1) / 2 * self.screen.get_height()))\r
+ \r
+ def heightToVelocity(self, pos, controllerNumber):\r
+ if self.song != None:\r
+ if self.boundingRect.collidepoint(pos) and (self.highlightedNote == self.notes[controllerNumber] or self.velocityLock[controllerNumber]):\r
+ velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)\r
+ else :\r
+ if self.easyMode:\r
+ velocity = None\r
+ else:\r
+ velocity = self.minimalVelocity/3\r
+ else:\r
+ if self.boundingRect.collidepoint(pos):\r
+ velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)\r
+ else :\r
+ velocity = self.minimalVelocity\r
+ return(velocity)\r
+ \r
+ def widthToNote(self, pos):\r
+ nn = 0\r
+ try :\r
+ while self.noteRects[nn].collidepoint(pos) == False:\r
+ nn = nn + 1\r
+ return(nn + self.offset)\r
+ except(IndexError):\r
+ return(None)\r
+ \r
+ def input(self, event): \r
+ \r
+ if event.type == pygame.QUIT:\r
+ for loop in self.wiimotes:\r
+ del loop.port\r
+ pygame.midi.quit()\r
+ sys.exit(0) \r
+ \r
+ if event.type == pygame.KEYDOWN:\r
+ if event.key == pygame.K_q:\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_i:\r
+ self.backToInstrumentChoice = True\r
+ self.done = True \r
+ \r
+ if event.type == pygame.JOYAXISMOTION:\r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ self.updateCursorPositionFromJoy(event) \r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ \r
+ if self.buttonDown[correctedJoyId]:\r
+ if self.notes[correctedJoyId] != None:\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ CCHexCode = wiimote.getCCHexCode()\r
+ wiimote.port.write_short(CCHexCode, 07, velocity)\r
+ if self.cascade:\r
+ n = self.widthToNote(pos)\r
+ if n != self.notes[correctedJoyId]:\r
+ wiimote.stopNote(self.notes[correctedJoyId])\r
+ self.notes[correctedJoyId] = n\r
+ \r
+ if self.song != None :\r
+ if self.highlightedNote == self.notes[correctedJoyId]:\r
+ self.highlightedNote = self.songIterator.next()\r
+ self.velocityLock[correctedJoyId] = True\r
+ else:\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ \r
+ wiimote.playNote(self.notes[correctedJoyId],velocity)\r
+ \r
+ if event.type == pygame.JOYBUTTONDOWN :\r
+ \r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ \r
+ if not self.buttonDown[correctedJoyId]:\r
+ savedNote = self.notes[correctedJoyId]\r
+ self.notes[correctedJoyId] = self.widthToNote(pos)\r
+ \r
+ if self.song != None :\r
+ if self.highlightedNote == self.notes[correctedJoyId]:\r
+ self.highlightedNote = self.songIterator.next()\r
+ self.velocityLock[correctedJoyId] = True\r
+ \r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ \r
+ if velocity != None : \r
+ if self.easyMode :\r
+ wiimote.stopNote(savedNote)\r
+ wiimote.playNote(self.notes[correctedJoyId],velocity)\r
+ self.buttonDown[correctedJoyId] = True\r
+ \r
+ if event.type == pygame.JOYBUTTONUP:\r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ self.buttonDown[correctedJoyId] = False\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ if not self.easyMode:\r
+ wiimote.stopNote(self.notes[correctedJoyId])\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ if event.type == pygame.MOUSEMOTION:\r
+ \r
+ self.updateCursorPositionFromMouse(event)\r
+\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+\r
+ if self.buttonDown[correctedJoyId]:\r
+ if self.notes[correctedJoyId] != None:\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ CCHexCode = wiimote.getCCHexCode()\r
+ wiimote.port.write_short(CCHexCode, 07, velocity)\r
+ if self.cascade:\r
+ n = self.widthToNote(pos)\r
+ if n != self.notes[correctedJoyId]:\r
+ wiimote.stopNote(self.notes[correctedJoyId])\r
+ self.notes[correctedJoyId] = n\r
+ \r
+ if self.song != None :\r
+ if self.highlightedNote == self.notes[correctedJoyId]:\r
+ self.highlightedNote = self.songIterator.next()\r
+ self.velocityLock[correctedJoyId] = True\r
+ else:\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ \r
+ wiimote.playNote(self.notes[correctedJoyId],velocity) \r
+ \r
+ if event.type == pygame.MOUSEBUTTONDOWN:\r
+ \r
+ if event.button == 1:\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ \r
+ if not self.buttonDown[correctedJoyId]:\r
+ self.notes[correctedJoyId] = self.widthToNote(pos)\r
+ \r
+ if self.song != None :\r
+ if self.highlightedNote == self.notes[correctedJoyId]:\r
+ self.highlightedNote = self.songIterator.next()\r
+ self.velocityLock[correctedJoyId] = True\r
+ \r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ \r
+ wiimote.playNote(self.notes[correctedJoyId],velocity)\r
+ self.buttonDown[correctedJoyId] = True\r
+ \r
+ if event.button == 2:\r
+ \r
+ self.done = True\r
+ \r
+ if event.type == pygame.MOUSEBUTTONUP:\r
+ \r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ wiimote.stopNote(self.notes[correctedJoyId])\r
+ self.buttonDown[correctedJoyId] = False\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ def hasChanged(self):\r
+ changed = False\r
+ if self.song != None:\r
+ if self.blinkOn != self.savedBlinkOn or self.highlightedNote != self.savedHighlightedNote:\r
+ self.savedBlinkOn = self.blinkOn\r
+ self.savedHighlightedNote = self.highlightedNote\r
+ changed = True\r
+ return(changed)\r
+ \r
+ def updateCursorPositionFromMouse(self, mouseEvent):\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ self.cursorPositions[correctedJoyId] = mouseEvent.pos\r
+ \r
+ def moveToNextNote(self):\r
+ while True:\r
+ if self.song == None:\r
+ yield(None)\r
+ else:\r
+ for note in self.song:\r
+ yield note\r
--- /dev/null
+'''\r
+Created on 23 juil. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+from math import floor, ceil\r
+import pygame\r
+import sys\r
+import colorsys\r
+import constants\r
+from gradients import gradients\r
+from logging.PickleableEvent import PickleableEvent\r
+from logging.EventLog import EventLog\r
+\r
+\r
+class SongFamiliarizer:\r
+ '''\r
+ The screen on which the game is played\r
+ \r
+ wiimotes: \r
+ The wiimotes used in this session\r
+ window:\r
+ The main display window\r
+ screen:\r
+ The main display surface\r
+ clock:\r
+ The clock used to animate the screen\r
+ savedScreen:\r
+ The background that is painted every time\r
+ playerScreen:\r
+ The buffer for painting everything before bliting\r
+ width:\r
+ The width of the window in pixels\r
+ height:\r
+ The height of the window in pixels\r
+ extendScale :\r
+ True if the scale is G to C instead of C to C\r
+ cascade:\r
+ True if crossing from note to note with a button pressed triggers a new note\r
+ scaleSize:\r
+ The size of the scale used\r
+ cursorPositions:\r
+ The positions of the cursors on the screen, in pixels\r
+ '''\r
+ \r
+ \r
+ \r
+ def __init__(self, wiimotes, window, screen, clock, joys, portOffset, song, activeWiimotes, cascade=False, extendedScale=False, easyMode = False, replay = False, eventLog = None, defaultInstrumentChannel = 16, defaultNote = 60):\r
+ '''\r
+ Constructor\r
+ ''' \r
+ self.firstClickTime = None\r
+ self.firstClickInTime = None\r
+ self.duration = None\r
+ self.clicks = 0\r
+ self.clicksIn = 0\r
+ \r
+ pygame.font.init()\r
+ self.font = pygame.font.Font(None,60)\r
+ self.congratulations = ["Bien !","Tres Bien !","Bravo !","Excellent !","Felicitations !"]\r
+ self.renderedCongratulations = [self.font.render(congratulation,False,(0,0,0)) for congratulation in self.congratulations]\r
+ self.congratulationCount = None\r
+ self.isCongratulating = False\r
+ self.congratulationTimer = 0\r
+ self.congratulationLength = 2000\r
+ self.congratulationPos = None\r
+ \r
+ self.blinkLength = 200\r
+ self.minimalVelocity = 90\r
+ self.shortScaleSize = 8\r
+ self.longScaleSize = 11\r
+ if not extendedScale:\r
+ self.offset = self.longScaleSize - self.shortScaleSize\r
+ else:\r
+ self.offset = 0\r
+ self.borderSize = 5\r
+ self.highlightedNote = 0\r
+ self.highlightedNoteNumber = 0\r
+ self.syllabus = None\r
+ self.savedHighlightedNote = 0\r
+ self.scaleFactor = 1\r
+ self.level = 3\r
+ \r
+ self.wiimotes = wiimotes\r
+ self.activeWiimotes = activeWiimotes\r
+ self.window = window\r
+ self.screen = screen\r
+ self.width = int(floor(screen.get_width()*self.scaleFactor))\r
+ self.height = int(floor(screen.get_height()*self.scaleFactor))\r
+ self.blitOrigin = ((self.screen.get_width()-self.width)/2,(self.screen.get_height()-self.height)/2) \r
+ self.joys = joys\r
+ self.clock = clock\r
+ self.cursorPositions = []\r
+ self.savedScreen = pygame.Surface(self.screen.get_size())\r
+ self.savedScreen.fill((255,255,255))\r
+ self.playerScreen = pygame.Surface(self.savedScreen.get_size())\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ self.extendedScale = extendedScale\r
+ self.cascade = cascade\r
+ self.portOffset =portOffset\r
+ self.eventLog = eventLog\r
+ self.song = song\r
+ self.songIterator = self.song.getSongIterator()\r
+ self.midiNoteNumbers = self.song.scale\r
+ self.replay = replay\r
+ self.quarterNoteLength = 800\r
+ self.cascadeLockLengthMultiplier = 1\r
+ self.cascadeLockLength = self.quarterNoteLength * self.cascadeLockLengthMultiplier\r
+ \r
+ self.defaultInstrumentChannel = defaultInstrumentChannel\r
+ self.defaultNote = defaultNote\r
+ \r
+ self.done = False\r
+ self.backToInstrumentChoice = False\r
+ self.easyMode = easyMode\r
+ \r
+ if eventLog == None:\r
+ self.eventLog = EventLog()\r
+ self.replay = False\r
+ else:\r
+ self.eventLog = eventLog\r
+ self.replay = replay\r
+ \r
+ #Initializes the highlightedNote and highlightedNoteNumber etc...\r
+ self.moveToNextNote()\r
+ \r
+ self.blinkOn = False\r
+ self.savedBlinkOn = False\r
+ ##Will prevent the song to move on if two consecutive notes are identical and the buttons have not been released in between the two\r
+ ##i.e. it guarantees that there will be an attack between two identical consecutive notes\r
+ self.highlightIsFree = True\r
+ \r
+ self.noteRects = []\r
+ self.boundingRect = None\r
+ self.notes = []\r
+ \r
+ self.buttonDown = []\r
+ self.velocityLock = []\r
+ \r
+ self._blinkOffset = 0\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = True\r
+ \r
+ self.font = pygame.font.Font(None,50)\r
+ self.renderedNoteNames = [self.font.render(constants.noteNumberToName(note),False,(0,0,0)) for note in self.midiNoteNumbers]\r
+ \r
+ self.drawBackground()\r
+ self.initializeWiimotes()\r
+ \r
+ events = pygame.event.get()\r
+ \r
+ #The main loop\r
+ while not self.done :\r
+ \r
+ #Clear the cursors from the screen\r
+ if self.hasChanged():\r
+ self.drawBackground()\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ \r
+ # Limit frame speed to 50 FPS\r
+ #\r
+ timePassed = self.clock.tick(10000)\r
+ \r
+ self._blinkOffset += timePassed\r
+ if self.buttonDown and not self.cascadeIsFree :\r
+ self._cascadeLockTimer += timePassed\r
+ if self._cascadeLockTimer > self.cascadeLockLength :\r
+ self.cascadeIsFree = True\r
+ \r
+ \r
+ if self._blinkOffset > self.blinkLength:\r
+ self._blinkOffset -= self.blinkLength\r
+ self.blinkOn = not self.blinkOn\r
+ \r
+ if self.replay:\r
+ self.eventLog.update(timePassed)\r
+ pickledEventsToPost = self.eventLog.getPickledEvents() \r
+ for pickledEvent in pickledEventsToPost:\r
+ pygame.event.post(pickledEvent.event)\r
+ \r
+ events = pygame.event.get()\r
+ \r
+ if not self.replay:\r
+ pickledEvents = [PickleableEvent(event.type,event.dict) for event in events]\r
+ if pickledEvents != [] :\r
+ self.eventLog.appendEventGroup(pickledEvents)\r
+ \r
+ for event in events:\r
+ self.input(event)\r
+ \r
+ if self.isCongratulating :\r
+ self.congratulationTimer += timePassed\r
+ if self.congratulationTimer < self.congratulationLength :\r
+ self.blitCongratulation()\r
+ else :\r
+ self.isCongratulating = False\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ if self.activeWiimotes[i]:\r
+ self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])\r
+ if self.buttonDown[i] :\r
+ self.wiimotes[i].cursor.flash()\r
+ self.wiimotes[i].cursor.blit(self.playerScreen)\r
+ \r
+ self.screen.blit(self.playerScreen, (0,0))\r
+ \r
+ pygame.display.flip()\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ if self.activeWiimotes[i]:\r
+ self.wiimotes[i].stopNoteByNoteNumber(self.midiNoteNumbers[self.notes[i]]) \r
+ if self.replay :\r
+ self.duration = self.eventLog.getCurrentTime()\r
+ \r
+ def drawBackground(self):\r
+ self.savedScreen.fill((255,255,255))\r
+ \r
+ if self.extendedScale :\r
+ self.scaleSize = self.longScaleSize\r
+ else:\r
+ self.scaleSize = self.shortScaleSize\r
+ \r
+ self.noteRects = [pygame.Rect(i * self.width / self.scaleSize+self.blitOrigin[0], self.blitOrigin[1], self.width / self.scaleSize + 1, self.height+1) for i in range(self.scaleSize)]\r
+ #inflate last noteRect to cover the far right pixels\r
+ self.noteRects[-1].width = self.noteRects[-1].width + 1\r
+ \r
+ self.noteRects[self.highlightedNote-self.offset].inflate_ip(self.noteRects[self.highlightedNote-self.offset].width*2,0)\r
+ \r
+ #create bounding rect\r
+ self.boundingRect = self.noteRects[0].unionall(self.noteRects)\r
+ \r
+ self.renderedNoteNames = [self.font.render(constants.noteNumberToName(note),False,(0,0,0)) for note in self.midiNoteNumbers]\r
+ \r
+ #fill the rectangles with a color gradient\r
+ #We start with blue\r
+ startingHue = 0.66666666666666663\r
+ \r
+# for rectNumber in range(self.scaleSize):\r
+# colorRatio = float(rectNumber) / (self.scaleSize - 1)\r
+# #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up\r
+# hue = startingHue * (1 - colorRatio)\r
+# if rectNumber + self.offset != self.highlightedNote:\r
+# #The color of the bottom of the rectangle in hls coordinates\r
+# bottomColorHls = (hue, 0.1, 1)\r
+# #The color of the top of the rectangle in hls coordinates\r
+# topColorHls = (hue, 0.1, 1)\r
+# \r
+# #convert to rgb ranging from 0 to 255\r
+# bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]\r
+# topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]\r
+# #add transparency\r
+# bottomColorRgb.append(255)\r
+# topColorRgb.append(255)\r
+# #convert to tuple\r
+# bottomColorRgb = tuple(bottomColorRgb)\r
+# topColorRgb = tuple(topColorRgb) \r
+# \r
+# self.savedScreen.blit(gradients.vertical(self.noteRects[rectNumber].size, topColorRgb, bottomColorRgb), self.noteRects[rectNumber])\r
+# \r
+# noteNameBlitPoint = (self.noteRects[rectNumber].left+(self.noteRects[rectNumber].width-self.renderedNoteNames[rectNumber].get_width())/2,\r
+# self.noteRects[rectNumber].bottom-self.renderedNoteNames[rectNumber].get_height())\r
+# \r
+# self.savedScreen.blit(self.renderedNoteNames[rectNumber], noteNameBlitPoint)\r
+# \r
+# pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[rectNumber], 2)\r
+ \r
+ colorRatio = float(self.highlightedNote-self.offset) / (self.scaleSize - 1)\r
+ #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up\r
+ hue = startingHue * (1 - colorRatio)\r
+ #The color of the bottom of the rectangle in hls coordinates\r
+ bottomColorHls = (hue, 0.6, 1)\r
+ #The color of the top of the rectangle in hls coordinates\r
+ topColorHls = (hue, 0.9, 1)\r
+ \r
+ #convert to rgb ranging from 0 to 255\r
+ bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]\r
+ topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]\r
+ #add transparency\r
+ bottomColorRgb.append(255)\r
+ topColorRgb.append(255)\r
+ #convert to tuple\r
+ bottomColorRgb = tuple(bottomColorRgb)\r
+ topColorRgb = tuple(topColorRgb) \r
+ \r
+ self.savedScreen.blit(gradients.vertical(self.noteRects[self.highlightedNote-self.offset].size, topColorRgb, bottomColorRgb), self.noteRects[self.highlightedNote-self.offset])\r
+ \r
+# noteNameBlitPoint = (self.noteRects[self.highlightedNote-self.offset].left+(self.noteRects[self.highlightedNote-self.offset].width-self.renderedNoteNames[self.highlightedNote-self.offset].get_width())/2,\r
+# self.noteRects[self.highlightedNote-self.offset].bottom-self.renderedNoteNames[self.highlightedNote-self.offset].get_height())\r
+# \r
+# self.savedScreen.blit(self.renderedNoteNames[self.highlightedNote-self.offset], noteNameBlitPoint)\r
+# \r
+# if self.syllabus :\r
+# renderedSyllabus = self.font.render(self.syllabus,False,(0,0,0))\r
+# \r
+# syllabusBlitPoint = (self.noteRects[self.highlightedNote-self.offset].left+(self.noteRects[self.highlightedNote-self.offset].width-renderedSyllabus.get_width())/2,\r
+# self.noteRects[self.highlightedNote-self.offset].centery-renderedSyllabus.get_height()/2)\r
+# \r
+# self.savedScreen.blit(renderedSyllabus, syllabusBlitPoint)\r
+ \r
+ pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[self.highlightedNote-self.offset], 2) \r
+ \r
+# if self.song != None and self.blinkOn:\r
+# borderSize = self.borderSize\r
+# pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 0), self.noteRects[self.highlightedNote-self.offset].inflate(borderSize/2,borderSize/2), borderSize)\r
+ \r
+ def initializeWiimotes(self):\r
+ for loop in self.wiimotes:\r
+ if loop.port == None :\r
+ loop.port = pygame.midi.Output(loop.portNumber)\r
+ self.notes.append(0)\r
+ self.cursorPositions.append(loop.cursor.centerPosition)\r
+ self.buttonDown.append(False)\r
+ self.velocityLock.append(False)\r
+ \r
+ def updateCursorPositionFromJoy(self, joyEvent):\r
+ joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if correctedJoyId < len(self.cursorPositions):\r
+ if joyEvent.axis == 0 :\r
+ self.cursorPositions[correctedJoyId] = (int((joyEvent.value + 1) / 2 * self.screen.get_width()), self.cursorPositions[correctedJoyId][1])\r
+ if joyEvent.axis == 1 :\r
+ self.cursorPositions[correctedJoyId] = (self.cursorPositions[correctedJoyId][0], int((joyEvent.value + 1) / 2 * self.screen.get_height()))\r
+ \r
+ def heightToVelocity(self, pos, controllerNumber):\r
+ if self.song != None:\r
+ if self.boundingRect.collidepoint(pos) and (self.highlightedNote == self.notes[controllerNumber] or self.velocityLock[controllerNumber]):\r
+ velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)\r
+ else :\r
+ if self.easyMode:\r
+ velocity = None\r
+ else:\r
+ velocity = 60\r
+ else:\r
+ if self.boundingRect.collidepoint(pos):\r
+ velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)\r
+ else :\r
+ velocity = self.minimalVelocity\r
+ return(velocity)\r
+ \r
+ def widthToNote(self, pos):\r
+ nn = 0\r
+ try :\r
+ if self.noteRects[self.highlightedNote-self.offset].collidepoint(pos) :\r
+ return self.highlightedNote\r
+ else :\r
+ while self.noteRects[nn].collidepoint(pos) == False:\r
+ nn = nn + 1\r
+ return(nn + self.offset)\r
+ except(IndexError):\r
+ return(None)\r
+ \r
+ def congratulate(self,targetRect,posy):\r
+ if self.congratulationCount != None :\r
+ if self.congratulationCount < len(self.congratulations)-1:\r
+ self.congratulationCount += 1\r
+ else :\r
+ self.congratulationCount = 0\r
+ self.congratulationTimer = 0\r
+ self.congratulationPos = (targetRect.left+(targetRect.width-self.renderedCongratulations[self.congratulationCount].get_width())/2,posy)\r
+ self.isCongratulating = True\r
+ \r
+ def resetCongratulation(self):\r
+ self.congratulationCount = None\r
+ self.congratulationPos = None\r
+ self.isCongratulating = False\r
+ \r
+ def blitCongratulation(self):\r
+ self.playerScreen.blit(self.renderedCongratulations[self.congratulationCount],self.congratulationPos)\r
+ \r
+ def input(self, event): \r
+ \r
+ if event.type == pygame.QUIT:\r
+ for loop in self.wiimotes:\r
+ del loop.port\r
+ pygame.midi.quit()\r
+ sys.exit(0) \r
+ \r
+ if event.type == pygame.KEYDOWN:\r
+ if event.key == pygame.K_q:\r
+ self.nextLevel = None\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_w:\r
+ self.nextLevel = 0\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_e:\r
+ self.nextLevel = 1\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_r:\r
+ self.nextLevel = 2\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_t:\r
+ self.nextLevel = 3\r
+ self.done = True\r
+ \r
+ if event.type == pygame.JOYAXISMOTION:\r
+ \r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ self.updateCursorPositionFromJoy(event)\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ \r
+ if self.buttonDown[correctedJoyId]:\r
+ if self.notes[correctedJoyId] != None:\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ if velocity != None :\r
+ CCHexCode = wiimote.getCCHexCode()\r
+ wiimote.port.write_short(CCHexCode, 07, velocity)\r
+ if self.cascade and self.cascadeIsFree :\r
+ n = self.widthToNote(pos)\r
+ if self.highlightedNote == n:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ self.velocityLock[correctedJoyId] = True\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)\r
+ self.moveToNextNote()\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ \r
+ if event.type == pygame.JOYBUTTONDOWN :\r
+ \r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ self.wiimotes[correctedJoyId].cursor.flash()\r
+ if self.replay:\r
+ self.clicks += 1\r
+ if self.firstClickTime == None :\r
+ self.firstClickTime = self.eventLog.getCurrentTime()\r
+ \r
+ if not self.buttonDown[correctedJoyId]:\r
+ n = self.widthToNote(pos)\r
+ if self.highlightedNote == n:\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ if self.easyMode:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ self.velocityLock[correctedJoyId] = True\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)\r
+ self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])\r
+ if self.replay:\r
+ self.clicksIn += 1\r
+ if self.firstClickInTime == None :\r
+ self.firstClickInTime = self.eventLog.getCurrentTime()\r
+ \r
+ self.moveToNextNote()\r
+ else :\r
+ self.resetCongratulation()\r
+ if not self.easyMode :\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId) \r
+ if velocity != None :\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity) \r
+ self.buttonDown[correctedJoyId] = True\r
+ \r
+ if event.type == pygame.JOYBUTTONUP:\r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ self.buttonDown[correctedJoyId] = False\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ if not self.easyMode:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ if event.type == pygame.MOUSEMOTION:\r
+ \r
+ self.updateCursorPositionFromMouse(event)\r
+ \r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+\r
+ if self.buttonDown[correctedJoyId]:\r
+ self.wiimotes[correctedJoyId].cursor.flash()\r
+ if self.notes[correctedJoyId] != None:\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ if velocity != None :\r
+ CCHexCode = wiimote.getCCHexCode()\r
+ wiimote.port.write_short(CCHexCode, 07, velocity)\r
+ if self.cascade and self.cascadeIsFree :\r
+ n = self.widthToNote(pos)\r
+ if self.highlightedNote == n:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ self.velocityLock[correctedJoyId] = True\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)\r
+ self.moveToNextNote()\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False \r
+ \r
+ if event.type == pygame.MOUSEBUTTONDOWN:\r
+ \r
+ if event.button == 1:\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ self.wiimotes[correctedJoyId].cursor.flash()\r
+ if self.replay:\r
+ self.clicks += 1\r
+ if self.firstClickTime == None :\r
+ self.firstClickTime = self.eventLog.getCurrentTime()\r
+ \r
+ if not self.buttonDown[correctedJoyId]:\r
+ n = self.widthToNote(pos)\r
+ if self.highlightedNote == n:\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ if self.easyMode:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ self.velocityLock[correctedJoyId] = True\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)\r
+ self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])\r
+ if self.replay:\r
+ self.clicksIn += 1\r
+ if self.firstClickInTime == None :\r
+ self.firstClickInTime = self.eventLog.getCurrentTime()\r
+ \r
+ self.moveToNextNote()\r
+ else :\r
+ self.resetCongratulation()\r
+ if not self.easyMode :\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId) \r
+ if velocity != None :\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity) \r
+ self.buttonDown[correctedJoyId] = True \r
+ \r
+ if event.button == 2:\r
+ \r
+ self.done = True\r
+ \r
+ if event.type == pygame.MOUSEBUTTONUP:\r
+ if event.button == 1 :\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ self.buttonDown[correctedJoyId] = False\r
+ if not self.easyMode:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ def hasChanged(self):\r
+ changed = False\r
+ if self.song != None:\r
+ if self.blinkOn != self.savedBlinkOn or self.highlightedNote != self.savedHighlightedNote:\r
+ self.savedBlinkOn = self.blinkOn\r
+ self.savedHighlightedNote = self.highlightedNote\r
+ changed = True\r
+ return(changed)\r
+ \r
+ def updateCursorPositionFromMouse(self, mouseEvent):\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ self.cursorPositions[correctedJoyId] = mouseEvent.pos\r
+ \r
+ def moveToNextNote(self):\r
+ self.savedMidiNoteNumbers = self.midiNoteNumbers[:]\r
+ self.highlightedNote, self.highlightedNoteNumber, self.syllabus, self.cascadeLockLengthMultiplier = self.songIterator.next()\r
+ self.midiNoteNumbers[self.highlightedNote] = self.highlightedNoteNumber\r
--- /dev/null
+'''\r
+Created on 23 juil. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+from math import floor, ceil\r
+import pygame\r
+import sys\r
+import colorsys\r
+import constants\r
+from gradients import gradients\r
+from logging.PickleableEvent import PickleableEvent\r
+\r
+\r
+class SongPlayingScreen:\r
+ '''\r
+ The screen on which the game is played\r
+ \r
+ wiimotes: \r
+ The wiimotes used in this session\r
+ window:\r
+ The main display window\r
+ screen:\r
+ The main display surface\r
+ clock:\r
+ The clock used to animate the screen\r
+ savedScreen:\r
+ The background that is painted every time\r
+ playerScreen:\r
+ The buffer for painting everything before bliting\r
+ width:\r
+ The width of the window in pixels\r
+ height:\r
+ The height of the window in pixels\r
+ extendScale :\r
+ True if the scale is G to C instead of C to C\r
+ cascade:\r
+ True if crossing from note to note with a button pressed triggers a new note\r
+ scaleSize:\r
+ The size of the scale used\r
+ cursorPositions:\r
+ The positions of the cursors on the screen, in pixels\r
+ '''\r
+ \r
+ \r
+ \r
+ def __init__(self, instrumentChoice, song, cascade=False, extendedScale=False, easyMode = False, alwaysDown = False, eventLog = None, replay = None, defaultInstrumentChannel = 16, defaultNote = 60):\r
+ '''\r
+ Constructor\r
+ ''' \r
+ self.songDurations = []\r
+ self.totalDuration = None\r
+ self.clicks = [0]\r
+ self.clicksIn = [0]\r
+ self.clicksPerMinute = [0]\r
+ self.clicksInPerMinute = [0]\r
+ self.meanTimeBetweenNotes = []\r
+ self.firstClick = None\r
+ self.firstClickIn = None\r
+ \r
+ self.blinkLength = 200\r
+ self.minimalVelocity = 90\r
+ self.shortScaleSize = 8\r
+ self.longScaleSize = 11\r
+ if not extendedScale:\r
+ self.offset = self.longScaleSize - self.shortScaleSize\r
+ else:\r
+ self.offset = 0\r
+ self.borderSize = 5\r
+ self.highlightedNote = 0\r
+ self.highlightedNoteNumber = 0\r
+ self.syllabus = None\r
+ self.savedHighlightedNote = 0\r
+ self.alwaysDown = alwaysDown\r
+ self.nextLevel = None\r
+ \r
+ self.wiimotes = instrumentChoice.wiimotes\r
+ self.activeWiimotes = instrumentChoice.activeWiimotes\r
+ self.window = instrumentChoice.window\r
+ self.screen = instrumentChoice.screen\r
+ self.blitOrigin = instrumentChoice.blitOrigin\r
+ self.clock = instrumentChoice.clock\r
+ self.width = instrumentChoice.width\r
+ self.height = instrumentChoice.height\r
+ self.cursorPositions = instrumentChoice.cursorPositions\r
+ self.savedScreen = instrumentChoice.savedScreen\r
+ self.playerScreen = instrumentChoice.playerScreen\r
+ self.extendedScale = extendedScale\r
+ self.cascade = cascade\r
+ self.joys = instrumentChoice.joys\r
+ self.portOffset = instrumentChoice.portOffset\r
+ if eventLog == None :\r
+ self.eventLog = instrumentChoice.eventLog\r
+ else :\r
+ self.eventLog = eventLog\r
+ self.cursorPositions = instrumentChoice.cursorPositions\r
+ self.song = song\r
+ self.songIterator = self.song.getSongIterator()\r
+ self.midiNoteNumbers = self.song.scale\r
+ if replay == None :\r
+ self.replay = instrumentChoice.replay\r
+ else :\r
+ self.replay = replay\r
+ self.quarterNoteLength = song.quarterNoteLength\r
+ self.cascadeLockLengthMultiplier = 1\r
+ self.nextCascadeLockLengthMultiplier = 1\r
+ self.cascadeLockLength = self.quarterNoteLength * self.cascadeLockLengthMultiplier\r
+ \r
+ self.defaultInstrumentChannel = defaultInstrumentChannel\r
+ self.defaultNote = defaultNote\r
+ \r
+ self.done = False\r
+ self.backToInstrumentChoice = False\r
+ self.easyMode = easyMode\r
+ \r
+ #Initializes the highlightedNote and highlightedNoteNumber etc...\r
+ self.moveToNextNote()\r
+ self.cascadeLockLengthMultiplier = self.nextCascadeLockLengthMultiplier\r
+ \r
+ self.blinkOn = False\r
+ self.savedBlinkOn = False\r
+ ##Will prevent the song to move on if two consecutive notes are identical and the buttons have not been released in between the two\r
+ ##i.e. it guarantees that there will be an attack between two identical consecutive notes\r
+ self.highlightIsFree = True\r
+ \r
+ self.noteRects = []\r
+ self.boundingRect = None\r
+ self.notes = []\r
+ \r
+ self.buttonDown = []\r
+ self.velocityLock = []\r
+ \r
+ self._blinkOffset = 0\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = True\r
+ \r
+ self.font = pygame.font.Font(None,80)\r
+ self.renderedNoteNames = [self.font.render(constants.noteNumberToName(note),False,(0,0,0)) for note in self.midiNoteNumbers]\r
+ \r
+ self.drawBackground()\r
+ self.initializeWiimotes()\r
+ \r
+ self.songStartTime = self.eventLog.getCurrentTime()\r
+ \r
+ #The main loop\r
+ while not self.done :\r
+ \r
+ #Clear the cursors from the screen\r
+ if self.hasChanged():\r
+ self.drawBackground()\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ \r
+ # Limit frame speed to 50 FPS\r
+ #\r
+ timePassed = self.clock.tick(10000)\r
+ \r
+ self._blinkOffset += timePassed\r
+ if (self.buttonDown or self.alwaysDown) and not self.cascadeIsFree :\r
+ self._cascadeLockTimer += timePassed\r
+ if self._cascadeLockTimer > self.cascadeLockLengthMultiplier*self.quarterNoteLength :\r
+ self.cascadeIsFree = True\r
+ self.cascadeLockLengthMultiplier = self.nextCascadeLockLengthMultiplier\r
+ \r
+ \r
+ if self._blinkOffset > self.blinkLength:\r
+ self._blinkOffset -= self.blinkLength\r
+ self.blinkOn = not self.blinkOn\r
+ \r
+ if self.replay:\r
+ self.eventLog.update(timePassed)\r
+ pickledEventsToPost = self.eventLog.getPickledEvents() \r
+ for pickledEvent in pickledEventsToPost:\r
+ pygame.event.post(pickledEvent.event)\r
+ \r
+ events = pygame.event.get()\r
+ \r
+ if not self.replay:\r
+ pickledEvents = [PickleableEvent(event.type,event.dict) for event in events]\r
+ if pickledEvents != [] :\r
+ self.eventLog.appendEventGroup(pickledEvents)\r
+ \r
+ for event in events:\r
+ self.input(event)\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ if self.activeWiimotes[i]:\r
+ self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])\r
+ if self.buttonDown[i] or self.alwaysDown:\r
+ self.wiimotes[i].cursor.flash()\r
+ self.wiimotes[i].cursor.blit(self.playerScreen)\r
+ \r
+ self.screen.blit(self.playerScreen, (0,0))\r
+ \r
+ pygame.display.flip()\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ if self.activeWiimotes[i]:\r
+ self.wiimotes[i].stopNoteByNoteNumber(self.midiNoteNumbers[self.notes[i]])\r
+ if self.replay : \r
+ self.totalDuration = self.eventLog.getCurrentTime() \r
+ \r
+ def drawBackground(self):\r
+ self.savedScreen.fill((255,255,255))\r
+ \r
+ if self.extendedScale :\r
+ self.scaleSize = self.longScaleSize\r
+ else:\r
+ self.scaleSize = self.shortScaleSize\r
+ \r
+ self.noteRects = [pygame.Rect(i * self.width / self.scaleSize+self.blitOrigin[0], self.blitOrigin[1], self.width / self.scaleSize + 1, self.height+1) for i in range(self.scaleSize)]\r
+ #inflate last noteRect to cover the far right pixels\r
+ self.noteRects[-1].width = self.noteRects[-1].width + 1\r
+ \r
+ self.noteRects[self.highlightedNote-self.offset].inflate_ip(self.noteRects[self.highlightedNote-self.offset].width*2,0)\r
+ \r
+ #create bounding rect\r
+ self.boundingRect = self.noteRects[0].unionall(self.noteRects)\r
+ \r
+ self.renderedNoteNames = [self.font.render(constants.noteNumberToName(note),False,(0,0,0)) for note in self.midiNoteNumbers]\r
+ \r
+ #fill the rectangles with a color gradient\r
+ #We start with blue\r
+ startingHue = 0.66666666666666663\r
+ \r
+ for rectNumber in range(self.scaleSize):\r
+ colorRatio = float(rectNumber) / (self.scaleSize - 1)\r
+ #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up\r
+ hue = startingHue * (1 - colorRatio)\r
+ if rectNumber + self.offset != self.highlightedNote:\r
+ #The color of the bottom of the rectangle in hls coordinates\r
+ bottomColorHls = (hue, 0.1, 1)\r
+ #The color of the top of the rectangle in hls coordinates\r
+ topColorHls = (hue, 0.1, 1)\r
+ \r
+ #convert to rgb ranging from 0 to 255\r
+ bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]\r
+ topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]\r
+ #add transparency\r
+ bottomColorRgb.append(255)\r
+ topColorRgb.append(255)\r
+ #convert to tuple\r
+ bottomColorRgb = tuple(bottomColorRgb)\r
+ topColorRgb = tuple(topColorRgb) \r
+ \r
+ self.savedScreen.blit(gradients.vertical(self.noteRects[rectNumber].size, topColorRgb, bottomColorRgb), self.noteRects[rectNumber])\r
+ \r
+ noteNameBlitPoint = (self.noteRects[rectNumber].left+(self.noteRects[rectNumber].width-self.renderedNoteNames[rectNumber+self.offset].get_width())/2,\r
+ self.noteRects[rectNumber].bottom-self.renderedNoteNames[rectNumber+self.offset].get_height())\r
+ \r
+ self.savedScreen.blit(self.renderedNoteNames[rectNumber+self.offset], noteNameBlitPoint)\r
+ \r
+ pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[rectNumber], 2)\r
+ \r
+ colorRatio = float(self.highlightedNote-self.offset) / (self.scaleSize - 1)\r
+ #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up\r
+ hue = startingHue * (1 - colorRatio)\r
+ #The color of the bottom of the rectangle in hls coordinates\r
+ bottomColorHls = (hue, 0.6, 1)\r
+ #The color of the top of the rectangle in hls coordinates\r
+ topColorHls = (hue, 0.9, 1)\r
+ \r
+ #convert to rgb ranging from 0 to 255\r
+ bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]\r
+ topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]\r
+ #add transparency\r
+ bottomColorRgb.append(255)\r
+ topColorRgb.append(255)\r
+ #convert to tuple\r
+ bottomColorRgb = tuple(bottomColorRgb)\r
+ topColorRgb = tuple(topColorRgb) \r
+ \r
+ self.savedScreen.blit(gradients.vertical(self.noteRects[self.highlightedNote-self.offset].size, topColorRgb, bottomColorRgb), self.noteRects[self.highlightedNote-self.offset])\r
+ \r
+ noteNameBlitPoint = (self.noteRects[self.highlightedNote-self.offset].left+(self.noteRects[self.highlightedNote-self.offset].width-self.renderedNoteNames[self.highlightedNote].get_width())/2,\r
+ self.noteRects[self.highlightedNote-self.offset].bottom-self.renderedNoteNames[self.highlightedNote].get_height())\r
+ \r
+ self.savedScreen.blit(self.renderedNoteNames[self.highlightedNote], noteNameBlitPoint)\r
+ \r
+ if self.syllabus :\r
+ renderedSyllabus = self.font.render(self.syllabus,False,(0,0,0))\r
+ \r
+ syllabusBlitPoint = (self.noteRects[self.highlightedNote-self.offset].left+(self.noteRects[self.highlightedNote-self.offset].width-renderedSyllabus.get_width())/2,\r
+ self.noteRects[self.highlightedNote-self.offset].centery-renderedSyllabus.get_height()/2)\r
+ \r
+ self.savedScreen.blit(renderedSyllabus, syllabusBlitPoint)\r
+ \r
+ pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[self.highlightedNote-self.offset], 2) \r
+ \r
+ if self.song != None and self.blinkOn:\r
+ borderSize = self.borderSize\r
+ pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 0), self.noteRects[self.highlightedNote-self.offset].inflate(borderSize/2,borderSize/2), borderSize)\r
+ \r
+ def initializeWiimotes(self):\r
+ for loop in self.wiimotes:\r
+ if loop.port == None :\r
+ loop.port = pygame.midi.Output(loop.portNumber)\r
+ self.notes.append(0)\r
+ self.buttonDown.append(False)\r
+ self.velocityLock.append(False)\r
+ \r
+ def updateCursorPositionFromJoy(self, joyEvent):\r
+ joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if correctedJoyId < len(self.cursorPositions):\r
+ if joyEvent.axis == 0 :\r
+ self.cursorPositions[correctedJoyId] = (int((joyEvent.value + 1) / 2 * self.screen.get_width()), self.cursorPositions[correctedJoyId][1])\r
+ if joyEvent.axis == 1 :\r
+ self.cursorPositions[correctedJoyId] = (self.cursorPositions[correctedJoyId][0], int((joyEvent.value + 1) / 2 * self.screen.get_height()))\r
+ \r
+ def heightToVelocity(self, pos, controllerNumber):\r
+ if self.song != None:\r
+ if self.boundingRect.collidepoint(pos) and (self.highlightedNote == self.notes[controllerNumber] or self.velocityLock[controllerNumber]):\r
+ velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)\r
+ else :\r
+ if self.easyMode:\r
+ velocity = None\r
+ else:\r
+ velocity = 60\r
+ else:\r
+ if self.boundingRect.collidepoint(pos):\r
+ velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)\r
+ else :\r
+ velocity = self.minimalVelocity\r
+ return(velocity)\r
+ \r
+ def widthToNote(self, pos):\r
+ nn = 0\r
+ try :\r
+ if self.noteRects[self.highlightedNote-self.offset].collidepoint(pos) :\r
+ return self.highlightedNote\r
+ else :\r
+ while self.noteRects[nn].collidepoint(pos) == False:\r
+ nn = nn + 1\r
+ return(nn + self.offset)\r
+ except(IndexError):\r
+ return(None)\r
+ \r
+ def logClick(self):\r
+ self.clicks[-1] += 1\r
+ if self.firstClick == None :\r
+ self.firstClick = self.eventLog.getCurrentTime()\r
+ minute = int(floor((self.eventLog.getCurrentTime()-self.songStartTime)/60000))\r
+ if minute > len(self.clicksPerMinute)-1:\r
+ self.clicksPerMinute.append(0)\r
+ self.clicksPerMinute[-1] += 1\r
+ \r
+ def logClickIn(self):\r
+ self.clicksIn[-1] += 1\r
+ if self.clicksIn[-1] > len(self.song.notes)-1 :\r
+ self.clicksIn.append(0)\r
+ self.clicks.append(0)\r
+ self.songDurations.append(self.eventLog.getCurrentTime())\r
+ if self.firstClickIn == None :\r
+ self.firstClickIn = self.eventLog.getCurrentTime()\r
+ minute = int(floor((self.eventLog.getCurrentTime()-self.songStartTime)/60000))\r
+ if minute > len(self.clicksInPerMinute)-1:\r
+ self.clicksInPerMinute.append(0)\r
+ self.clicksInPerMinute[-1]+=1\r
+ \r
+ def input(self, event): \r
+ \r
+ if event.type == pygame.QUIT:\r
+ for loop in self.wiimotes:\r
+ del loop.port\r
+ pygame.midi.quit()\r
+ sys.exit(0) \r
+ \r
+ if event.type == pygame.KEYDOWN:\r
+ if event.key == pygame.K_q:\r
+ self.nextLevel = None\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_i:\r
+ self.backToInstrumentChoice = True\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_w:\r
+ self.nextLevel = 0\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_e:\r
+ self.nextLevel = 1\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_r:\r
+ self.nextLevel = 2\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_t:\r
+ self.nextLevel = 3\r
+ self.done = True \r
+ \r
+ if event.type == pygame.JOYAXISMOTION:\r
+ \r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ self.updateCursorPositionFromJoy(event)\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ \r
+ if (self.buttonDown[correctedJoyId] or self.alwaysDown):\r
+ if self.notes[correctedJoyId] != None:\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ if velocity != None :\r
+ CCHexCode = wiimote.getCCHexCode()\r
+ wiimote.port.write_short(CCHexCode, 07, velocity)\r
+ if self.cascade and self.cascadeIsFree :\r
+ n = self.widthToNote(pos)\r
+ if self.highlightedNote == n:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ self.velocityLock[correctedJoyId] = True\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)\r
+ self.moveToNextNote()\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ \r
+ if event.type == pygame.JOYBUTTONDOWN :\r
+ \r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ self.wiimotes[correctedJoyId].cursor.flash()\r
+ if self.replay:\r
+ self.logClick()\r
+ \r
+ if not (self.buttonDown[correctedJoyId] or self.alwaysDown):\r
+ n = self.widthToNote(pos)\r
+ if self.highlightedNote == n:\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ if self.easyMode:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ self.velocityLock[correctedJoyId] = True\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)\r
+ if self.replay :\r
+ self.logClickIn()\r
+ self.moveToNextNote()\r
+ else :\r
+ if not self.easyMode :\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId) \r
+ if velocity != None and self.notes[correctedJoyId] != None :\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity) \r
+ self.buttonDown[correctedJoyId] = True\r
+ \r
+ if event.type == pygame.JOYBUTTONUP:\r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ self.buttonDown[correctedJoyId] = False\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ if not self.easyMode:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ if event.type == pygame.MOUSEMOTION:\r
+ \r
+ self.updateCursorPositionFromMouse(event)\r
+ \r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+\r
+ if (self.buttonDown[correctedJoyId] or self.alwaysDown):\r
+ self.wiimotes[correctedJoyId].cursor.flash()\r
+ if self.notes[correctedJoyId] != None:\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ if velocity != None :\r
+ CCHexCode = wiimote.getCCHexCode()\r
+ wiimote.port.write_short(CCHexCode, 07, velocity)\r
+ if self.cascade and self.cascadeIsFree :\r
+ n = self.widthToNote(pos)\r
+ if self.highlightedNote == n:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ self.velocityLock[correctedJoyId] = True\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)\r
+ self.moveToNextNote()\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False \r
+ \r
+ if event.type == pygame.MOUSEBUTTONDOWN:\r
+ \r
+ if event.button == 1:\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ self.wiimotes[correctedJoyId].cursor.flash()\r
+ if self.replay:\r
+ self.logClick()\r
+ \r
+ if not (self.buttonDown[correctedJoyId] or self.alwaysDown):\r
+ n = self.widthToNote(pos)\r
+ if self.highlightedNote == n:\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ if self.easyMode:\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ self.velocityLock[correctedJoyId] = True\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity)\r
+ if self.replay :\r
+ self.logClickIn()\r
+ self.moveToNextNote()\r
+ else :\r
+ if not self.easyMode :\r
+ self._cascadeLockTimer = 0\r
+ self.cascadeIsFree = False\r
+ self.notes[correctedJoyId] = n\r
+ velocity = self.heightToVelocity(pos, correctedJoyId) \r
+ if velocity != None and self.notes[correctedJoyId] != None :\r
+ wiimote.playNoteByNoteNumber(self.midiNoteNumbers[self.notes[correctedJoyId]],velocity) \r
+ self.buttonDown[correctedJoyId] = True \r
+ \r
+ if event.button == 2:\r
+ \r
+ self.done = True\r
+ \r
+ if event.type == pygame.MOUSEBUTTONUP:\r
+ if event.button == 1 :\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ self.buttonDown[correctedJoyId] = False\r
+ if not self.easyMode:\r
+ if self.notes[correctedJoyId] != None :\r
+ wiimote.stopNoteByNoteNumber(self.savedMidiNoteNumbers[self.notes[correctedJoyId]])\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ def hasChanged(self):\r
+ changed = False\r
+ if self.song != None:\r
+ if self.blinkOn != self.savedBlinkOn or self.highlightedNote != self.savedHighlightedNote:\r
+ self.savedBlinkOn = self.blinkOn\r
+ self.savedHighlightedNote = self.highlightedNote\r
+ changed = True\r
+ return(changed)\r
+ \r
+ def updateCursorPositionFromMouse(self, mouseEvent):\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ self.cursorPositions[correctedJoyId] = mouseEvent.pos\r
+ \r
+ def moveToNextNote(self):\r
+ self.savedMidiNoteNumbers = self.midiNoteNumbers[:]\r
+ self.highlightedNote, self.highlightedNoteNumber, self.syllabus, self.nextCascadeLockLengthMultiplier = self.songIterator.next()\r
+ self.midiNoteNumbers[self.highlightedNote] = self.highlightedNoteNumber\r
--- /dev/null
+'''\r
+Created on 23 juil. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+from math import floor, ceil\r
+import pygame\r
+import pygame.midi\r
+import sys\r
+import colorsys\r
+import constants\r
+from gradients import gradients\r
+from logging.PickleableEvent import PickleableEvent\r
+from instruments.Instrument import Instrument\r
+from cursor.WarpingCursor import *\r
+from controllers.Wiimote import Wiimote\r
+from logging.EventLog import EventLog\r
+\r
+\r
+class StaticFamiliarizer:\r
+ '''\r
+ The screen on which the game is played\r
+ \r
+ wiimotes: \r
+ The wiimotes used in this session\r
+ window:\r
+ The main display window\r
+ screen:\r
+ The main display surface\r
+ clock:\r
+ The clock used to animate the screen\r
+ savedScreen:\r
+ The background that is painted every time\r
+ playerScreen:\r
+ The buffer for painting everything before bliting\r
+ width:\r
+ The width of the window in pixels\r
+ height:\r
+ The height of the window in pixels\r
+ extendScale :\r
+ True if the scale is G to C instead of C to C\r
+ cascade:\r
+ True if crossing from note to note with a button pressed triggers a new note\r
+ scaleSize:\r
+ The size of the scale used\r
+ cursorPositions:\r
+ The positions of the cursors on the screen, in pixels\r
+ '''\r
+ \r
+ \r
+ \r
+ def __init__(self, wiimotes, window, screen, clock, joys, portOffset,activeWiimotes,replay = False, level = 0, defaultInstrumentChannel = 16, defaultNote = 60, eventLog = None):\r
+ '''\r
+ Constructor\r
+ '''\r
+ self.firstClickTime = None\r
+ self.firstClickInTime = None\r
+ self.duration = None\r
+ self.clicks = 0\r
+ self.clicksIn = 0\r
+ \r
+ pygame.font.init()\r
+ self.font = pygame.font.Font(None,60)\r
+ self.congratulations = ["Bien !","Tres Bien !","Bravo !","Excellent !","Felicitations !"]\r
+ self.renderedCongratulations = [self.font.render(congratulation,False,(0,0,0)) for congratulation in self.congratulations]\r
+ self.congratulationCount = None\r
+ self.isCongratulating = False\r
+ self.congratulationTimer = 0\r
+ self.congratulationLength = 2000\r
+ self.congratulationPos = None\r
+ \r
+ self.blinkLength = 200\r
+ self.minimalVelocity = 64\r
+ self.shortScaleSize = 8\r
+ self.longScaleSize = 11\r
+ self.borderSize = 5\r
+ self.savedHighlightedNote = 0\r
+ self.scaleFactor = 1\r
+ self.wiimotes = wiimotes\r
+ self.window = window\r
+ self.screen = screen\r
+ self.clock = clock\r
+ self.width = int(floor(screen.get_width()*self.scaleFactor))\r
+ self.height = int(floor(screen.get_height()*self.scaleFactor))\r
+ self.blitOrigin = ((self.screen.get_width()-self.width)/2,(self.screen.get_height()-self.height)/2) \r
+ self.joys = joys\r
+ self.portOffset = portOffset\r
+ self.savedScreen = pygame.Surface(self.screen.get_size())\r
+ self.savedScreen.fill((255,255,255))\r
+ self.playerScreen = pygame.Surface(self.savedScreen.get_size())\r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ self.cursorPositions = []\r
+ self.level = level\r
+ self.nextLevel = None\r
+ self.activeWiimotes = activeWiimotes\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ #Set the screen for the cursors (it can't be set before)\r
+ self.wiimotes[i].cursor.screen = self.playerScreen\r
+ self.cursorPositions.append(self.wiimotes[i].cursor.centerPosition)\r
+ \r
+ if eventLog == None:\r
+ self.eventLog = EventLog()\r
+ self.replay = False\r
+ else:\r
+ self.eventLog = eventLog\r
+ self.replay = replay\r
+ \r
+ self.defaultInstrumentChannel = defaultInstrumentChannel\r
+ self.defaultNote = defaultNote\r
+ \r
+ self.done = False\r
+ self.backToInstrumentChoice = False\r
+ self.easyMode = False\r
+\r
+ self.noteRects = []\r
+ self.boundingRect = None\r
+ self.notes = []\r
+ self.buttonDown = []\r
+ self.velocityLock = []\r
+ \r
+ self.drawBackground()\r
+ self.initializeWiimotes()\r
+ events = pygame.event.get()\r
+ \r
+ #The main loop\r
+ while not self.done :\r
+ \r
+ self.playerScreen.blit(self.savedScreen, (0, 0))\r
+ \r
+ # Limit frame speed to 50 FPS\r
+ #\r
+ timePassed = self.clock.tick(10000)\r
+ \r
+ if self.replay:\r
+ self.eventLog.update(timePassed)\r
+ pickledEventsToPost = self.eventLog.getPickledEvents() \r
+ for pickledEvent in pickledEventsToPost:\r
+ pygame.event.post(pickledEvent.event)\r
+ \r
+ events = pygame.event.get()\r
+ \r
+ if not self.replay:\r
+ pickledEvents = [PickleableEvent(event.type,event.dict) for event in events]\r
+ if pickledEvents != [] :\r
+ self.eventLog.appendEventGroup(pickledEvents)\r
+ \r
+ for event in events:\r
+ self.input(event)\r
+ \r
+ if self.isCongratulating :\r
+ self.congratulationTimer += timePassed\r
+ if self.congratulationTimer < self.congratulationLength :\r
+ self.blitCongratulation()\r
+ else :\r
+ self.isCongratulating = False\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ if self.activeWiimotes[i]:\r
+ self.wiimotes[i].cursor.update(timePassed, self.cursorPositions[i])\r
+ if self.buttonDown[i] :\r
+ self.wiimotes[i].cursor.flash()\r
+ self.wiimotes[i].cursor.blit(self.playerScreen)\r
+ \r
+ self.screen.blit(self.playerScreen, (0,0))\r
+ \r
+ pygame.display.flip()\r
+ \r
+ for i in range(len(self.wiimotes)):\r
+ if self.activeWiimotes[i]:\r
+ if self.notes[i] != None :\r
+ self.wiimotes[i].stopNote(self.notes[i])\r
+ if self.replay :\r
+ self.duration = self.eventLog.getCurrentTime() \r
+ \r
+ def drawBackground(self):\r
+ self.savedScreen.fill((255,255,255))\r
+ if self.level == 0 :\r
+ A = [4] \r
+ else :\r
+ A = [1,7]\r
+ \r
+ self.noteRects = [pygame.Rect(i * self.width / 11+self.blitOrigin[0], self.blitOrigin[1], (self.width / 11 + 1)*3, self.height+1) for i in A]\r
+ \r
+ #create bounding rect\r
+ self.boundingRect = self.noteRects[0].unionall(self.noteRects)\r
+ \r
+ #fill the rectangles with a color gradient\r
+ #We start with blue\r
+ startingHue = 0.66666666666666663\r
+ \r
+ for rectNumber in range(len(self.noteRects)) :\r
+ colorRatio = float(A[rectNumber]) / (11 - 1)\r
+ #hue will go from 0.6666... (blue) to 0 (red) as colorRation goes up\r
+ hue = startingHue * (1 - colorRatio)\r
+ #The color of the bottom of the rectangle in hls coordinates\r
+ bottomColorHls = (hue, 0.6, 1)\r
+ #The color of the top of the rectangle in hls coordinates\r
+ topColorHls = (hue, 0.9, 1)\r
+ \r
+ #convert to rgb ranging from 0 to 255\r
+ bottomColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*bottomColorHls)]\r
+ topColorRgb = [floor(255 * i) for i in colorsys.hls_to_rgb(*topColorHls)]\r
+ #add transparency\r
+ bottomColorRgb.append(255)\r
+ topColorRgb.append(255)\r
+ #convert to tuple\r
+ bottomColorRgb = tuple(bottomColorRgb)\r
+ topColorRgb = tuple(topColorRgb) \r
+ \r
+ self.savedScreen.blit(gradients.vertical(self.noteRects[rectNumber].size, topColorRgb, bottomColorRgb), self.noteRects[rectNumber])\r
+ \r
+ pygame.draw.rect(self.savedScreen, pygame.Color(0, 0, 0, 255), self.noteRects[rectNumber], 2)\r
+ \r
+ def initializeWiimotes(self):\r
+ for loop in self.wiimotes:\r
+ if loop.port == None :\r
+ loop.port = pygame.midi.Output(loop.portNumber)\r
+ self.notes.append(0)\r
+ self.buttonDown.append(False)\r
+ self.velocityLock.append(False)\r
+ \r
+ def updateCursorPositionFromJoy(self, joyEvent):\r
+ joyName = pygame.joystick.Joystick(joyEvent.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if correctedJoyId < len(self.cursorPositions):\r
+ if joyEvent.axis == 0 :\r
+ self.cursorPositions[correctedJoyId] = (int((joyEvent.value + 1) / 2 * self.screen.get_width()), self.cursorPositions[correctedJoyId][1])\r
+ if joyEvent.axis == 1 :\r
+ self.cursorPositions[correctedJoyId] = (self.cursorPositions[correctedJoyId][0], int((joyEvent.value + 1) / 2 * self.screen.get_height()))\r
+ \r
+ def heightToVelocity(self, pos, controllerNumber):\r
+ velocity = int(floor((1 - (float(pos[1])-self.blitOrigin[1]) / self.height) * (127-self.minimalVelocity))+self.minimalVelocity)\r
+ return(velocity)\r
+ \r
+ def widthToNote(self, pos):\r
+ nn = 0\r
+ try :\r
+ while self.noteRects[nn].collidepoint(pos) == False:\r
+ nn = nn + 1\r
+ return(nn)\r
+ except(IndexError):\r
+ return(None)\r
+ \r
+ def congratulate(self,targetRect,posy):\r
+ if self.congratulationCount != None :\r
+ if self.congratulationCount < len(self.congratulations)-1:\r
+ self.congratulationCount += 1\r
+ else :\r
+ self.congratulationCount = 0\r
+ self.congratulationTimer = 0\r
+ self.congratulationPos = (targetRect.left+(targetRect.width-self.renderedCongratulations[self.congratulationCount].get_width())/2,posy)\r
+ self.isCongratulating = True\r
+ \r
+ def resetCongratulation(self):\r
+ self.congratulationCount = None\r
+ self.congratulationPos = None\r
+ self.isCongratulating = False\r
+ \r
+ def blitCongratulation(self):\r
+ self.playerScreen.blit(self.renderedCongratulations[self.congratulationCount],self.congratulationPos) \r
+ \r
+ def input(self, event): \r
+ \r
+ print event\r
+ \r
+ if event.type == pygame.QUIT:\r
+ for loop in self.wiimotes:\r
+ del loop.port\r
+ pygame.midi.quit()\r
+ sys.exit(0) \r
+ \r
+ if event.type == pygame.KEYDOWN:\r
+ if event.key == pygame.K_q:\r
+ self.nextLevel = None\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_w:\r
+ self.nextLevel = 0\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_e:\r
+ self.nextLevel = 1\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_r:\r
+ self.nextLevel = 2\r
+ self.done = True\r
+ \r
+ if event.key == pygame.K_t:\r
+ self.nextLevel = 3\r
+ self.done = True \r
+ \r
+ if event.type == pygame.JOYAXISMOTION:\r
+ \r
+ \r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ self.updateCursorPositionFromJoy(event)\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ \r
+ if self.buttonDown[correctedJoyId]:\r
+ wiimote.cursor.flash()\r
+ if self.notes[correctedJoyId] != None:\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ CCHexCode = wiimote.getCCHexCode()\r
+ wiimote.port.write_short(CCHexCode, 07, velocity)\r
+ \r
+ if event.type == pygame.JOYBUTTONDOWN :\r
+ \r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ wiimote.cursor.flash()\r
+ if self.replay :\r
+ self.clicks += 1\r
+ if self.firstClickTime == None :\r
+ self.firstClickTime = self.eventLog.getCurrentTime()\r
+ \r
+ if not self.buttonDown[correctedJoyId]:\r
+ self.notes[correctedJoyId] = self.widthToNote(pos)\r
+ \r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ \r
+ if self.notes[correctedJoyId] != None : \r
+ wiimote.playNote(self.notes[correctedJoyId],velocity)\r
+ self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])\r
+ if self.replay :\r
+ self.clicksIn += 1\r
+ if self.firstClickInTime == None :\r
+ self.firstClickInTime = self.eventLog.getCurrentTime()\r
+ else :\r
+ self.resetCongratulation()\r
+ \r
+ self.buttonDown[correctedJoyId] = True\r
+ \r
+ if event.type == pygame.JOYBUTTONUP:\r
+ joyName = pygame.joystick.Joystick(event.joy).get_name()\r
+ correctedJoyId = constants.joyNames.index(joyName)\r
+ if self.activeWiimotes[correctedJoyId]:\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ wiimote.stopNote(self.notes[correctedJoyId])\r
+ self.buttonDown[correctedJoyId] = False\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ if event.type == pygame.MOUSEMOTION:\r
+ \r
+ self.updateCursorPositionFromMouse(event)\r
+ \r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+\r
+ if self.buttonDown[correctedJoyId]:\r
+ wiimote.cursor.flash()\r
+ if self.notes[correctedJoyId] != None:\r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ CCHexCode = wiimote.getCCHexCode()\r
+ wiimote.port.write_short(CCHexCode, 07, velocity) \r
+ \r
+ if event.type == pygame.MOUSEBUTTONDOWN:\r
+ \r
+ if event.button == 1:\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ pos = self.cursorPositions[correctedJoyId]\r
+ wiimote.cursor.flash()\r
+ if self.replay :\r
+ self.clicks += 1\r
+ if self.firstClickTime == None :\r
+ self.firstClickTime = self.eventLog.getCurrentTime()\r
+ \r
+ if not self.buttonDown[correctedJoyId]:\r
+ self.notes[correctedJoyId] = self.widthToNote(pos)\r
+ \r
+ velocity = self.heightToVelocity(pos, correctedJoyId)\r
+ \r
+ if self.notes[correctedJoyId] != None : \r
+ wiimote.playNote(self.notes[correctedJoyId],velocity)\r
+ self.congratulate(self.noteRects[self.notes[correctedJoyId]],pos[1])\r
+ if self.replay :\r
+ self.clicksIn += 1\r
+ if self.firstClickInTime == None :\r
+ self.firstClickInTime = self.eventLog.getCurrentTime()\r
+ else :\r
+ self.resetCongratulation()\r
+ \r
+ self.buttonDown[correctedJoyId] = True\r
+ \r
+ if event.button == 2:\r
+ \r
+ self.done = True\r
+ \r
+ if event.type == pygame.MOUSEBUTTONUP:\r
+ \r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ wiimote = self.wiimotes[correctedJoyId]\r
+ wiimote.stopNote(self.notes[correctedJoyId])\r
+ self.buttonDown[correctedJoyId] = False\r
+ self.velocityLock[correctedJoyId] = False\r
+ \r
+ def hasChanged(self):\r
+ return(True)\r
+ \r
+ def updateCursorPositionFromMouse(self, mouseEvent):\r
+ correctedJoyId = 0\r
+ while not self.activeWiimotes[correctedJoyId] :\r
+ correctedJoyId += 1\r
+ self.cursorPositions[correctedJoyId] = mouseEvent.pos\r
+ \r
+if __name__ == "__main__" :\r
+ pygame.init()\r
+ modeResolution = (1024,768)\r
+ window = pygame.display.set_mode(modeResolution,pygame.FULLSCREEN)\r
+ pygame.font.init()\r
+ \r
+ pygame.midi.init()\r
+ instruments = [Instrument(constants.scaleDict["majorScale"], i + 1, "".join(["../instruments/instrumentImages/", constants.instrumentImagePathList[i], ".jpg"]), constants.octaves[i]) for i in range(9)]\r
+ \r
+ joys = [[id,pygame.joystick.Joystick(id).get_name()] for id in range(pygame.joystick.get_count())]\r
+ for joy in joys:\r
+ if joy[1] in constants.joyNames:\r
+ pygame.joystick.Joystick(joy[0]).init() \r
+ \r
+ ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]\r
+ portOffset = ports.index(constants.portNames[0])\r
+ print(portOffset)\r
+ \r
+ events = pygame.event.get()\r
+ \r
+ screen = pygame.display.get_surface()\r
+ clock = pygame.time.Clock() \r
+ cursorImages = [createImageListFromPath('../cursor/cursorImages/black', 11),createImageListFromPath('../cursor/cursorImages/red', 11)]\r
+ durations = [75 for i in range(len(cursorImages[0]))]\r
+ \r
+ wiimoteCount = 1\r
+ cursors = [WarpingCursor(None, cursorImages[i], durations, (300 * i, 300 * i),flashImage = '../cursor/cursorImages/black/flash.png' ) for i in range(wiimoteCount)]\r
+ wiimotes = [Wiimote(i, i + portOffset, None, instruments[i], cursors[i]) for i in range(wiimoteCount)]\r
+ \r
+ fam = StaticFamiliarizer(instruments, wiimotes, window, screen, clock, joys, portOffset)\r
+ \r
+ for loop in fam.wiimotes:\r
+ del loop.port\r
+ \r
+ pygame.midi.quit()\r
+ \r
+ pygame.quit() \r
--- /dev/null
+from songs.Song import Song,loadSongFromMidi\r
+from dataTools.odict import OrderedDict\r
+\r
+joyNames = ["PPJoy Virtual joystick 1", "PPJoy Virtual joystick 2", "PPJoy Virtual joystick 3", "PPJoy Virtual joystick 4"]\r
+portNames = ["Out To MIDI Yoke: 1","Out To MIDI Yoke: 2","Out To MIDI Yoke: 3","Out To MIDI Yoke: 4"]\r
+\r
+readabilityDict = OrderedDict([("majeure" , "majorScale"),\r
+ ("mineure naturelle" , "minorScale"),\r
+ ("majeure myxolydienne" , "myxolydianScale"),\r
+ ("mineure dorienne" , "dorianScale"),\r
+ ("phrygienne espagnole" , "spanishPhrygianScale"),\r
+ ("lydienne" , "lydianScale"),\r
+ ("phrygienne" , "phrygianScale"),\r
+ ("J'ai du bon tabac" , "jadbt"),\r
+ ("L'eau vive" , "eauvive"),\r
+ ("Le penitencier" , "penitencier"),\r
+ ("La foule" , "foule"),\r
+ ("Petit papa noel" , "papanoel"),\r
+ ("La marseillaise" , "marseillaise"),\r
+ ("A la claire fontaine" , "clairefontaine"),\r
+ ("Au clair de la lune" , "clairdelalune"),\r
+ ("Frere jacques" , "frerejacques"),\r
+ ("Le petit vin blanc" , "vinblanc"),\r
+ ("La vie en rose","vierose"),\r
+ ("Les feuilles mortes","feuillesmortes"),\r
+ ("Il pleut bergere","bergere"),\r
+ ("Le temps des cerises","cerises"),\r
+ ("La boheme","boheme"),\r
+ ("Chanson Test","test"),\r
+ ("Improvisation" , "none"),\r
+ ("Do/Do","C/C"),\r
+ ("Sol/Do","G/C"),\r
+ ("Oui","Yes"),\r
+ ("Non","No"),\r
+ ("Tres facile","veryEasy"),\r
+ ("Facile","easy"),\r
+ ("Normal","normal"),\r
+ ("Expert","expert")])\r
+\r
+reversedReadabilityDict = dict(zip(readabilityDict.values(),readabilityDict.keys()))\r
+\r
+rangeDict = {"C/C":False,"G/C":True}\r
+\r
+cascadeDict = {"Yes":True,"No":False}\r
+\r
+modeDict = OrderedDict([("veryEasy",0),("easy",1),("normal",2),("expert",3)])\r
+print modeDict['veryEasy']\r
+\r
+scaleDict = OrderedDict ([("majorScale" , [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]),\r
+ ("minorScale" , [55, 56, 58, 60, 62, 63, 65, 67, 68, 70, 72]),\r
+ ("myxolydianScale" , [55, 57, 58, 60, 62, 64, 65, 67, 69, 70, 72]),\r
+ ("dorianScale" , [55, 57, 58, 60, 62, 63, 65, 67, 69, 70, 72]),\r
+ ("spanishPhrygianScale" , [55,57,58,60,62,63,66,67,69,70,72]),\r
+ ("lydianScale" , [55, 57, 59, 60, 62, 64, 66, 67, 69, 71, 72]),\r
+ ("phrygianScale" , [55, 56, 58, 60, 61, 63, 65, 67, 68, 70, 72])])\r
+\r
+songDict = OrderedDict([("jadbt" , Song(scaleDict["majorScale"],[3, 4, 5, 3, 4, 4, 5, 6, 6, 5, 5, 3, 4, 5, 3, 4, 4, 5, 6, 7, 3,7,7,6,5,4,5,6,7,4,7,7,6,5,4,5,6,7,4],False,\r
+ lyrics = ["J'ai","du","bon","ta-","-bac","dans","ma","ta-","-ba-","-tie-","-re","J'ai","du","bon","ta-","-bac","tu","n'en","au-","-ras","pas","j'en","ai","du","fin","et","du","bien","ra-","-pe","mais","ce","n'est","pas","pour","ton","vi-","-lain","nez"],\r
+ noteLengths = [1,1,1,1,2,1,1,2,2,2,2,1,1,1,1,2,1,1,2,2,4,2,1,1,2,1,1,2,2,4,2,1,1,2,1,1,2,2,4],\r
+ quarterNoteLength = 400)),\r
+ ("eauvive" , Song(scaleDict["majorScale"],[5,3,5,3,5,3,4,4,5,6,4,5,3,4,5,3,5,3,5,3,4,4,5,6,4,5,3,4,7,8,7,6,6,4,6,5,3,5,4,3,4,5,6,5,4,3,4,3,2,3],\r
+ True,\r
+ lyrics = ["Ma","pe-","-tite","est","co-","-mme","l'eau","elle","est","co-","-mme","l'eau","vi-","-ve","e-","-lle","court","comme","un","rui-","-sseau","que","les","en-","-fants","pour-","-sui-","-vent","ve-","-nez","ve-","-nez","mes","a-","-gneaux","mes","a-","-gne-","-lets","ja-","-mais","ja-","-mais","vous","ne","la","ra-","-ttra-","-pe-","-rez"],\r
+ noteLengths = [2,1,2,1,2,1,3,1,1,1,2,1,3,3,2,1,2,1,2,1,3,1,1,1,2,1,3,3,3,3,3,3,1,1,1,1,1,1,6,3,3,3,3,1,1,1,1,1,1,6],\r
+ quarterNoteLength = 300)),\r
+ ("penitencier" , \r
+ Song(scaleDict["dorianScale"],[3,3,3,5,7,6,3,3,10,10,10,9,7,6,7,10,10,10,3,5,6,7,6,3,5,3,3,3,3,2,2,3],\r
+ True,\r
+ alterationIndexes = [-2,-3],\r
+ alterations = [1,1],\r
+ lyrics = ["Les","por-","-tes","du","pe-","-ni-","-ten-","-cier","bien-","-tot","vont","se","re-","-fer-","-mer","et","c'est","la","que","je","fi-","-ni-","-rai","ma","vie","comme","d'au-","-tres","gars","l'ont","fi-","-nie"],\r
+ noteLengths =[1,5,1,5,1,1,6,4,1,5,1,4,1,1,10,1,1,5,1,4,1,1,5,1,5,1,1,1,4,5,1,12],\r
+ quarterNoteLength = 250)),\r
+ ("papanoel" , Song(scaleDict["myxolydianScale"],[3,6,6,6,7,6,6,7,8,8,8,9,8,7,6,6,6,6,5,4,3,3,3,6,6,6,7,7,6],False,\r
+ lyrics = ["pe-","-ti","Pa-","-pa","No-","-el","quand","tu","de-","-scen-","-dras","du","ciel","a-","-vec","tes","jou-","-ets","par","mi-","-lliers","n'ou-","-blie","pas","mes","pe-","-tits","sou-","-liers"],\r
+ noteLengths = [1,1,1,1,1,3,0.5,0.5,1,1,1,1,3,1,1.5,0.5,0.5,0.5,0.5,0.5,3,0.5,0.5,1,0.5,0.5,0.5,0.5,3],\r
+ quarterNoteLength = 500)),\r
+ ("foule" , Song(scaleDict["myxolydianScale"],[7,7,6,5,6,8,7,7,6,8,7,7,6,9,7,7,6,9,7,7,6,8,7,7,6,8,7,6,5,4,4,4,4,4,4,6,6,6,6,8,8,7,6,8,7,7,7,7,7,7,7,7,7,7,7,7,6,9,8,7,8,8,7,8,8,7,8,8,7,8,8,6,8,8,6,8,8,7],False,modulationIndexes = [28],modulationScales = [scaleDict["spanishPhrygianScale"]],\r
+ lyrics = ["Em-","-por-","-tes","par","la","fou-","-le","qui","nous","trai-","-ne","nous","en-","-trai-","-ne","e-","-cra-","-ses","l'un","con-","-tre","l'au-","-tre","nous","ne","for-","-mons","qu'un","seul","corps","et","le","flot","sans","e-","-ffort","nous","pousse","en-","-chai-","-nes","l'un","et","l'au-","-tre","et","nous","lai-","-sse","tous","deux","e-","-pa-","-nouis-","en-","-i-","-vres","et","heu-","-reux","ta-","-dam","ta","ta-","-dam","ta","ta-","-dam","ta","ta-","-dam","ta","ta-","-dam","ta","ta-","-dam","ta"],\r
+ noteLengths = [1,1.5,.5,.5,.5,1.5,.5,.5,.5,1.5,.5,.5,.5,1.5,.5,.5,.5,1.5,.5,.5,.5,1.5,.5,.5,.5,1.5,.5,.5,.5,3,.5,.5,.5,.5,.5,.5,.5,.5,.5,1,1,.5,.5,1.5,3,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,1,1,1.5,.5,1,1.5,.5,1,1.5,.5,1,1.5,.5,1,1.5,.5,1,1.5,.5,1,3],\r
+ quarterNoteLength = 400)),\r
+ ("clairefontaine" , Song(scaleDict["majorScale"],\r
+ [3,3,5,5,4,5,4,3,3,5,5,4,5,5,5,4,3,5,7,5,7,7,5,3,5,4,3,3,5,5,5,4,5,5,5,5,3,5,4,3],\r
+ False,\r
+ lyrics = ["A","la","clai-","-re","fon-","-tai-","-ne","m'en","a-","-llant","pro-","-me-","-ner","j'ai","trou-","-ve","l'eau","si","be-","-lle","que","je","m'y","suis","bai-","-gne","il","y-a","long-","-temps","que","je","t'aime","ja-","-mais","je","ne","t'ou-","-blie-","-rai"],\r
+ noteLengths = [2,1,1,1,1,1,1,2,1,1,1,1,2,2,1,1,1,1,1,1,2,1,1,1,1,2,2,1,1,1,0.5,0.5,2,2,1,0.5,0.5,1,1,4],\r
+ quarterNoteLength = 400)),\r
+ ("clairdelalune", Song(scaleDict["lydianScale"], [7,7,7,8,9,8,7,9,8,8,7,7,7,7,8,9,8,7,9,8,8,7,8,8,8,8,5,5,8,7,6,5,4,7,7,7,8,9,8,7,9,8,8,7],False,\r
+ lyrics = ["Au","clair","de","la","lu-","-ne","mon","a-","-mi","Pie-","-rrot","pre-","-te","moi","ta","plu-","-me","pour","e-","-crire","un","mot","ma","chan-","-delle","est","mor-","-te","je","n'ai","plus","de","feu","ou-","-vre","moi","ta","po-","-rte","pour","l'a-","-mour","de","Dieu"],\r
+ noteLengths = [1,1,1,1,2,2,1,1,1,1,4,1,1,1,1,2,2,1,1,1,1,4,1,1,1,1,2,2,1,1,1,1,4,1,1,1,1,2,2,1,1,1,1,4],\r
+ quarterNoteLength = 500)),\r
+ ("frerejacques" , \r
+ Song(scaleDict["majorScale"],\r
+ [3,4,5,3,3,4,5,3,5,6,7,5,6,7,7,8,7,6,5,3,7,8,7,6,5,3,3,0,3,3,0,3],\r
+ True,\r
+ lyrics = ["Fre-","-re","Ja-","-cques","fre-","-re","-Ja","-cques","dor-","-mez","vous","dor-","-mez","vous","so-","-nnez","les","ma-","-ti-","-nes","so-","-nnez","les","ma-","-ti-","-nes","ding","ding","dong","ding","ding","dong"],\r
+ noteLengths = [1,1,1,1,1,1,1,1,1,1,2,1,1,2,1.5,0.5,1,1,2,2,1.5,0.5,1,1,2,2,1,1,2,1,1,2],\r
+ quarterNoteLength = 600)),\r
+ ("marseillaise" , Song(scaleDict["majorScale"],[0, 0, 0, 3, 3, 4, 4, 7, 5, 3, 3, 5, 3, 1, 6, 4, 2, 3, 3, 4, 5, 5, 5, 6, 5, 5, 4, 4, 5, 6, 6, 6, 7, 6, 5, 7, 7, 7, 5, 3, 7, 5, 3, 0, 0, 0, 2, 4, 6, 4, 2, 4, 3, 2, 1, 3, 3, 3, 2, 3, 4, 4, 5, 5, 5, 5, 6, 7, 4, 5, 4, 3, 3, 3, 5, 4, 3, 3,2,7,7,7,5,3,4,7,7,7,5,3,4,0,3,4,5,6,7,8,4,8,7,5,6,4,3], True, modulationIndexes = [53,54,61,77], modulationScales = [scaleDict["dorianScale"],scaleDict["majorScale"],scaleDict["dorianScale"],scaleDict["majorScale"]],\r
+ lyrics = ["A-","-llons","en-","-fants","de","la","pa","tri-","-i-","-e","le","jour","de","gloire","est","a-","-rri-","-ve","con-","-tre","nous","de","la","ty-","-ra-","-nni-","-e","l'e-","-ten-","-dard","san-","-glant","est","le-","-ve","l'e-","-ten-","-dard","san-","-an-","-glant","est","le-","-ve","en-","-ten-","-dez","vous","dans","nos","cam-","-pa-","-gnes","mu-","-gir","ces","fe-","-ro-","-ces","sol-","-dats","qui","vie-","-nnent","ju-","-sque","dans","nos","bras","e-","-gor-","-ger","nos","fils","et","nos","com-","-pa-","-gnes","aux","ar-","-mes","ci-","-toy-","-yen","for-","-mez","vos","ba-","-ta-","-illons","mar-","-chons","mar-","-chons","qu'un","sang","im-","-pur","a-","-breu-","-ve","nos","si-","-illons","pon","pon","pon","pon"],\r
+ noteLengths = [1,2,1,3,3,3,3,5,1,2,1,2,1,3,6,2,1,9,2,1,3,3,3,2,1,3,6,2,1,3,3,3,2,1,6,2,1,3,2,1,3,2,1,6,1,2,1,6,3,2,1,3,3,6,3,2,1,3,2,1,6,3,5,1,2,1,2,1,6,2,1,5,1,2,1,2,1,6,5,1,7,1,2,1,8,1,7,1,2,1,8,3,9,3,9,6,3,3,9,3,8,1,2,1,2,1,2,1,8],\r
+ quarterNoteLength = 300)),\r
+ ("vinblanc" , \r
+ Song(scaleDict["phrygianScale"],[5, 5, 5, 3, 5, 6, 5, 5, 5, 3, 7, 6, 6, 6, 6, 4, 8, 7, 7, 7, 8, 9, 7, 5, 5, 5, 5, 3, 5, 6, 8, 9, 10, 9, 8, 10, 9, 8, 6, 6, 8, 7, 6, 8, 6, 5, 3, 5, 6, 3, 5, 6, 3, 5, 6, 3, 5, 6, 3, 5, 7, 6, 5, 7, 6, 5, 8],\r
+ False,\r
+ lyrics = ["Ah","le","pe-","-tit","vin","blanc","qu'on","boit","sous","les","to-","-nelles","quand","les","fi-","-lles","sont","belles","du","co-","-te","de","No-","-geant","et","puis","de","temps","en","temps","un","air","de","vie-","-lle","ro-","-man-","-ce","sem-","-ble","do-","-nner","la","ca-","-den-","-ce","pour","fau-","-ter","pour","fau-","-ter","dans","les","bois","dans","les","pres","du","co-","-te","du","co-","-te","de","No-","-geant"],\r
+ noteLengths = [1,1,1,2,1,6,1,1,1,2,1,6,1,1,1,2,1,6,1,1,1,2,1,6,1,1,1,2,1,6,1,1,1,1,1,1,3,3,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,6],\r
+ quarterNoteLength = 300)),\r
+ ("none" , None)])\r
+\r
+instrumentImagePathList = ["piano", "guitare", "accordeon", "violon", "flute", "tuba", "orgue", "violoncelle", "celesta"]\r
+octaves = [0, -1, 0, 1, 1, -2, 0, -1, 0]\r
+\r
+defaultInstrumentChannel = 16\r
+defaultInstrumentNote = 60\r
+defaultCCHexCode = 0xB0+defaultInstrumentChannel - 1\r
+defaultNoteOnHexCode = 0x90+defaultInstrumentChannel - 1\r
+\r
+songScaleFactor = 0.99\r
+\r
+fileName = "../../../saves/22-01-2009-coll1-v65-"\r
+\r
+def noteNumberToName(noteNumber):\r
+ names = ["Do","Do#","R\xe9","Mib","Mi","Fa","Fa#","Sol","Sol#","La","Sib","Si"]\r
+ return(names[noteNumber%12])\r
+\r
+if __name__ == "__main__":\r
+ key = "papanoel"\r
+ if songDict[key] != None :\r
+ songDict[key].save("../songs/smwis/"+str(key)+".smwi")\r
--- /dev/null
+'''\r
+Created on 15 juil. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+\r
+class Instrument:\r
+ '''\r
+ Object representing an instrument.\r
+ \r
+ notes:\r
+ The MIDI numbers of the notes played by this instrument (usually a scale)\r
+ channel:\r
+ The channel corresponding to the instrument in the synthesizer\r
+ image:\r
+ The image for the instrument\r
+ '''\r
+\r
+ def __init__(self, notes, channel, image, octave = 0):\r
+ '''\r
+ Constructor\r
+ \r
+ notes:\r
+ The MIDI numbers of the notes played by this instrument (usually a scale)\r
+ channel:\r
+ The channel corresponding to the instrument in the synthesizer\r
+ image:\r
+ The image for the instrument\r
+ '''\r
+ \r
+ self.notes = [loop+12*octave for loop in notes]\r
+ self.octave = octave\r
+ self.channel = channel\r
+ self.image = image\r
+ \r
+ def getNote(self,noteNumber):\r
+ if noteNumber == None :\r
+ return(None)\r
+ else :\r
+ return(self.notes[noteNumber])\r
+ \r
+ def getNoteByNoteNumber(self,baseMidiNoteNumber):\r
+ if baseMidiNoteNumber == None:\r
+ return(None)\r
+ else :\r
+ return(baseMidiNoteNumber+self.octave*12)
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 21 aout 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+import time\r
+\r
+class EventLog():\r
+ '''\r
+ classdocs\r
+ '''\r
+ \r
+ def __init__(self,eventGroups = [], times = []):\r
+ '''\r
+ Constructor\r
+ '''\r
+ self.eventGroups = eventGroups\r
+ self.times = times\r
+ self.rate = 1\r
+ self.yielder = self.eventGroupYielder()\r
+ self.yieldPointer = 0\r
+ \r
+ self._timeCounter = 0\r
+ \r
+ def __getstate__(self):\r
+ d = []\r
+ d.append(self.eventGroups)\r
+ d.append(self.times)\r
+ return d\r
+ \r
+ def __setstate__(self,d):\r
+ self.eventGroups = d[0]\r
+ self.times = d[1]\r
+ self.yielder = self.eventGroupYielder()\r
+ \r
+ self._timeCounter = 0\r
+ \r
+ def appendEventGroup(self, eventGroup):\r
+ t = time.clock()*1000\r
+ self.times.append(t)\r
+ self.eventGroups.append(eventGroup)\r
+ \r
+ def update(self,timePassed):\r
+ self._timeCounter += timePassed\r
+ \r
+ def setReplayRate(self,rate):\r
+ self.rate = rate\r
+ self.yielder = self.eventGroupYielder()\r
+ \r
+ def getPickledEvents(self):\r
+ return(self.yielder.next())\r
+ \r
+ def getCurrentTime(self):\r
+ return(self.times[self.yieldPointer])\r
+ \r
+ def eventGroupYielder(self):\r
+ '''\r
+ Will return the next event to post if enough time has passed and [] otherwise\r
+ '''\r
+ i = 0\r
+ while i in range(len(self.eventGroups)):\r
+ print "rate is " + str(self.rate)\r
+ if self._timeCounter*self.rate>self.times[i]:\r
+ print str(self._timeCounter*self.rate)+" > "+ str(self.times[i])\r
+ self.yieldPointer = i\r
+ yield self.eventGroups[i]\r
+ i += 1\r
+ else:\r
+ yield []\r
--- /dev/null
+'''\r
+Created on 28 aout 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+\r
+import os\r
+import sys\r
+import subprocess\r
+import re\r
+\r
+import pygame\r
+import pygame.midi\r
+import pickle\r
+\r
+from gui.constants import *\r
+\r
+from gui.PlayingScreen import PlayingScreen\r
+from gui.InstrumentChoice import InstrumentChoice\r
+from instruments.Instrument import Instrument\r
+from cursor.WarpingCursor import *\r
+from controllers.Wiimote import Wiimote\r
+from songs.Song import Song\r
+from gui.StaticFamiliarizer import StaticFamiliarizer\r
+from gui.SongFamiliarizer import SongFamiliarizer\r
+from gui.SongPlayingScreen import SongPlayingScreen\r
+from gui.DummyInstrumentChoice import DummyInstrumentChoice\r
+\r
+class FamiliarizerLog():\r
+ '''\r
+ classdocs\r
+ '''\r
+\r
+ def __init__(self,eventLog,level,activeWiimotes):\r
+ '''\r
+ Constructor\r
+ '''\r
+ self.eventLog = eventLog\r
+ self.level = level\r
+ self.activeWiimotes = activeWiimotes\r
+ self.scale = scaleDict["majorScale"] \r
+ \r
+if __name__ == '__main__':\r
+ \r
+ f = file('../../../saves/19-01-2009-testcoll1-v65-1.fmwi', 'r')\r
+ unpickler = pickle.Unpickler(f)\r
+ log = unpickler.load()\r
+ f.close()\r
+ \r
+ pygame.init()\r
+ modeResolution = (1024,768)\r
+ window = pygame.display.set_mode(modeResolution,pygame.FULLSCREEN)\r
+ pygame.midi.init()\r
+ instruments = [Instrument(log.scale, i + 1, "".join(["../instruments/instrumentImages/", instrumentImagePathList[i], ".jpg"]), octaves[i]) for i in range(9)]\r
+ \r
+ joys = [[id,pygame.joystick.Joystick(id).get_name()] for id in range(pygame.joystick.get_count())]\r
+ for joy in joys:\r
+ if joy[1] in joyNames:\r
+ pygame.joystick.Joystick(joy[0]).init() \r
+ \r
+ ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]\r
+ portOffset = ports.index(portNames[0])\r
+ print(portOffset)\r
+ \r
+ screen = pygame.display.get_surface()\r
+ clock = pygame.time.Clock() \r
+ cursorImages=[['../cursor/cursorImages/black/10.png'],['../cursor/cursorImages/red/10.png'],['../cursor/cursorImages/blue/10.png'],['../cursor/cursorImages/green/10.png']]\r
+ durations = [75 for i in range(len(cursorImages))]\r
+ \r
+ extsc = True\r
+ casc = False\r
+ easyMode = True\r
+ \r
+ song = Song(scaleDict["majorScale"],[3,9,6,4,1,8,5,7,2,10],True)\r
+\r
+ wiimoteCount = 4\r
+ \r
+ cursors = [WarpingCursor(None, cursorImages[i], durations, (300 * i, 300 * i),'../cursor/cursorImages/black/flash.png') for i in range(wiimoteCount)]\r
+ wiimotes = [Wiimote(i, i + portOffset, None, instruments[0], cursors[i]) for i in range(wiimoteCount)]\r
+ dummyInstrumentChoice = DummyInstrumentChoice(wiimotes, window, screen, clock, joys, portOffset, log.activeWiimotes)\r
+ if log.level < 2 :\r
+ familiarize = StaticFamiliarizer(wiimotes, window, screen, clock, joys, portOffset,log.activeWiimotes,level = log.level,eventLog = log.eventLog,replay = True)\r
+ elif log.level == 2 :\r
+ familiarize = SongFamiliarizer(wiimotes, window, screen, clock, joys, portOffset,song,log.activeWiimotes,casc,extsc,easyMode,eventLog = log.eventLog,replay = True)\r
+ else :\r
+ familiarize = SongPlayingScreen(dummyInstrumentChoice,songDict["clairdelalune"],easyMode = True,eventLog = log.eventLog,replay = True)\r
+ \r
+ while familiarize.nextLevel != None :\r
+ if familiarize.nextLevel < 2 :\r
+ familiarize = StaticFamiliarizer(wiimotes, window, screen, clock, joys, portOffset,log.activeWiimotes,level = familiarize.nextLevel,eventLog = familiarize.eventLog,replay = True)\r
+ elif familiarize.nextLevel == 2 :\r
+ familiarize = SongFamiliarizer(wiimotes, window, screen, clock, joys, portOffset,song,log.activeWiimotes,casc,extsc,easyMode,eventLog = familiarize.eventLog,replay = True)\r
+ else :\r
+ familiarize = SongPlayingScreen(dummyInstrumentChoice,songDict["clairdelalune"],easyMode = True,eventLog = familiarize.eventLog,replay = True)\r
+ \r
+ for wiimote in wiimotes:\r
+ del wiimote.port \r
+ \r
+ pygame.midi.quit()\r
+ pygame.quit()
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 28 aout 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+\r
+import os\r
+import sys\r
+import subprocess\r
+import re\r
+\r
+import pygame\r
+import pygame.midi\r
+import pickle\r
+\r
+from gui.constants import *\r
+\r
+from gui.PlayingScreen import PlayingScreen\r
+from gui.SongPlayingScreen import SongPlayingScreen\r
+from gui.InstrumentChoice import InstrumentChoice\r
+from instruments.Instrument import Instrument\r
+from cursor.WarpingCursor import *\r
+from controllers.Wiimote import Wiimote\r
+from songs.Song import Song\r
+\r
+\r
+class Log():\r
+ '''\r
+ classdocs\r
+ '''\r
+\r
+\r
+ def __init__(self,eventLog,scale,extendedScale,cascade,song,mode,activeWiimotes,easyMode = True):\r
+ '''\r
+ Constructor\r
+ '''\r
+ self.eventLog = eventLog\r
+ self.scale = scale\r
+ self.extendedScale = extendedScale\r
+ self.cascade = cascade\r
+ self.song = song\r
+ self.activeWiimotes = activeWiimotes\r
+ self.mode = mode \r
+ \r
+if __name__ == '__main__':\r
+ \r
+ f = file('../../saves/4-12-2009-B1-v50-1.mwi', 'r')\r
+ unpickler = pickle.Unpickler(f)\r
+ log = unpickler.load()\r
+ f.close()\r
+ \r
+ pygame.init()\r
+ pygame.midi.init()\r
+ instruments = [Instrument(scaleDict["majorScale"], i + 1, "".join(["../instruments/instrumentImages/", instrumentImagePathList[i], ".jpg"]), octaves[i]) for i in range(9)]\r
+ \r
+ joys = [[id,pygame.joystick.Joystick(id).get_name()] for id in range(pygame.joystick.get_count())]\r
+ for joy in joys:\r
+ if joy[1] in joyNames:\r
+ pygame.joystick.Joystick(joy[0]).init() \r
+ \r
+ ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]\r
+ portOffset = ports.index(portNames[0])\r
+ print(portOffset)\r
+ \r
+ modeResolution = (1024,768)\r
+ window = pygame.display.set_mode(modeResolution,pygame.FULLSCREEN)\r
+ screen = pygame.display.get_surface()\r
+ clock = pygame.time.Clock() \r
+ cursorImages=[['../cursor/cursorImages/black/10.png'],['../cursor/cursorImages/red/10.png'],['../cursor/cursorImages/blue/10.png'],['../cursor/cursorImages/green/10.png']]\r
+ durations = [75 for i in range(len(cursorImages[0]))]\r
+ \r
+ wiimoteCount = 4\r
+ cursors = [WarpingCursor(None, cursorImages[i], durations, (300 * i, 300 * i),flashImage = '../cursor/cursorImages/black/flash.png' ) for i in range(wiimoteCount)]\r
+ wiimotes = [Wiimote(i, i + portOffset, None, None, cursors[i]) for i in range(wiimoteCount)]\r
+ \r
+ if log.song != None :\r
+ \r
+ if log.mode == 0 :\r
+ log.extendedScale = log.song.requiresExtendedScale\r
+ log.cascade = True\r
+ log.easyMode = True\r
+ elif log.mode == 1 :\r
+ log.extendedScale = log.song.requiresExtendedScale\r
+ log.cascade = True\r
+ log.easyMode = True\r
+ elif log.mode == 2:\r
+ log.extendedScale = log.song.requiresExtendedScale\r
+ log.cascade = False\r
+ log.easyMode = True\r
+ elif log.mode == 3:\r
+ log.extendedScale = True\r
+ log.cascade = False\r
+ log.easyMode = False\r
+ \r
+ choice = InstrumentChoice(instruments, wiimotes, window, screen, clock, joys, portOffset,log.activeWiimotes, eventLog = log.eventLog,scaleFactor = songScaleFactor,replay = True)\r
+ play = SongPlayingScreen(choice, log.song,log.cascade, log.extendedScale,log.easyMode)\r
+ \r
+ else:\r
+ \r
+ if log.mode == 0 :\r
+ log.extendedScale = False\r
+ log.cascade = False\r
+ elif log.mode == 1 :\r
+ log.extendedScale = True\r
+ log.cascade = False\r
+ elif log.mode == 2:\r
+ log.extendedScale = False\r
+ log.cascade = True\r
+ elif log.mode == 3:\r
+ log.extendedScale = True\r
+ log.cascade = True\r
+ \r
+ choice = InstrumentChoice(instruments, wiimotes, window, screen, clock, joys, portOffset,log.activeWiimotes,eventLog = log.eventLog,replay = True)\r
+ play = PlayingScreen(choice, None,log.cascade, log.extendedScale) \r
+ \r
+ while play.backToInstrumentChoice == True :\r
+ \r
+ for wiimote in wiimotes:\r
+ del wiimote.port\r
+ \r
+ wiimotes = [Wiimote(i, i + portOffset, None, None, cursors[i]) for i in range(wiimoteCount)]\r
+ previousEventLog = play.eventLog\r
+\r
+ if log.song != None :\r
+ choice = InstrumentChoice(instruments, wiimotes,window, screen, clock, joys, portOffset, log.activeWiimotes,eventLog = previousEventLog, replay = True, scaleFactor = songScaleFactor)\r
+ play = SongPlayingScreen(choice, log.song, False, log.extendedScale,log.easyMode)\r
+ else:\r
+ choice = InstrumentChoice(instruments, wiimotes, log.window, screen, clock, joys, portOffset,log.activeWiimotes, eventLog = previousEventLog, replay = True)\r
+ play = PlayingScreen(choice, None, log.cascade, log.extendedScale)\r
+ \r
+ for wiimote in wiimotes:\r
+ del wiimote.port\r
+ \r
+ pygame.midi.quit()\r
+ pygame.quit()
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 25 janv. 2010\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+\r
+from LogPGUPlayer import LogPGUPlayer\r
+\r
+class LogPGUAnalyzer(LogPGUPlayer):\r
+ '''\r
+ classdocs\r
+ '''\r
+\r
+\r
+ def __init__(self):\r
+ '''\r
+ Constructor\r
+ '''\r
+ \r
+ self.firstStepDurations = []\r
+ self.firstStepClicks = []\r
+ self.firstStepClicksIn = []\r
+ \r
+ self.secondStepDurations = []\r
+ self.secondStepClicks = []\r
+ self.secondStepClicksIn = []\r
+ \r
+ self.thirdStepDurations = []\r
+ self.thirdStepClicks = []\r
+ self.thirdStepClicksIn = []\r
+ \r
+ self.songDurations = []\r
+ self.songClicks = []\r
+ self.songClicksIn = []\r
+ self.songClicksPerMinute = []\r
+ self.songClicksInPerMinute = []\r
+ self.songTotalDurations = []\r
+ \r
+ self.meanTimeBetweenNotes = []\r
+ \r
+ LogPGUPlayer.__init__(self,20)\r
+
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 25 janv. 2010\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+\r
+import pygame\r
+import pickle\r
+\r
+from pgu import gui as pguGui\r
+\r
+from gui import constants\r
+\r
+from instruments.Instrument import Instrument\r
+from songs.Song import Song\r
+from cursor.WarpingCursor import *\r
+from gui.StaticFamiliarizer import StaticFamiliarizer\r
+from gui.SongFamiliarizer import SongFamiliarizer\r
+from gui.SongPlayingScreen import SongPlayingScreen\r
+from gui.DummyInstrumentChoice import DummyInstrumentChoice\r
+from controllers.Wiimote import Wiimote\r
+\r
+class LogPGUPlayer(pguGui.Desktop):\r
+ '''\r
+ classdocs\r
+ '''\r
+\r
+\r
+ def __init__(self,rate):\r
+ '''\r
+ Constructor\r
+ '''\r
+ self.firstStepDurations = []\r
+ self.firstStepClicks = []\r
+ self.firstStepClicksIn = []\r
+ \r
+ self.secondStepDurations = []\r
+ self.secondStepClicks = []\r
+ self.secondStepClicksIn = []\r
+ \r
+ self.thirdStepDurations = []\r
+ self.thirdStepClicks = []\r
+ self.thirdStepClicksIn = []\r
+ \r
+ self.songDurations = []\r
+ self.songClicks = []\r
+ self.songClicksIn = []\r
+ self.songClicksPerMinute = []\r
+ self.songClicksInPerMinute = []\r
+ self.songTotalDurations = []\r
+ \r
+ self.meanTimeBetweenNotes = []\r
+ \r
+ pguGui.Desktop.__init__(self)\r
+ \r
+ self.replayRate = rate\r
+ #pguGui.theme.load('../data/themes/default')\r
+ \r
+ self.connect(pguGui.QUIT,self.quit,None)\r
+ \r
+ main = pguGui.Container(width=500, height=400) #, background=(220, 220, 220) )\r
+ \r
+ \r
+ main.add(pguGui.Label("File Dialog Example", cls="h1"), 20, 20)\r
+ \r
+ \r
+ td_style = {'padding_right': 10}\r
+ t = pguGui.Table()\r
+ t.tr()\r
+ t.td( pguGui.Label('File Name:') , style=td_style )\r
+ self.input_file = pguGui.Input()\r
+ t.td( self.input_file, style=td_style )\r
+ self.browseButton = pguGui.Button("Browse...")\r
+ t.td( self.browseButton, style=td_style )\r
+ self.browseButton.connect(pguGui.CLICK, self.open_file_browser, None)\r
+ \r
+ self.goButton = pguGui.Button("Go")\r
+ \r
+ self.goButton.connect(pguGui.CLICK, self.goButtonClicked,None)\r
+ \r
+ self.quitButton = pguGui.Button("Fin")\r
+ self.quitButton.connect(pguGui.CLICK,self.quit,None)\r
+ \r
+ t.td( self.browseButton, style=td_style )\r
+ t.td( self.goButton, style=td_style )\r
+ t.td( self.quitButton, style=td_style )\r
+ \r
+ main.add(t, 20, 100)\r
+ \r
+ self.run(main)\r
+ #import profile\r
+ #profile.run('app.run(main)')\r
+ \r
+ def open_file_browser(self,data=None):\r
+ d = pguGui.FileDialog(path = "../../../saves")\r
+ d.connect(pguGui.CHANGE, self.handle_file_browser_closed, d)\r
+ d.open()\r
+ \r
+ \r
+ def handle_file_browser_closed(self,dlg):\r
+ if dlg.value: self.input_file.value = dlg.value\r
+ \r
+ def goButtonClicked(self,data=None):\r
+ if self.input_file.value.endswith(".fmwi"):\r
+ f = file(self.input_file.value, 'r')\r
+ unpickler = pickle.Unpickler(f)\r
+ log = unpickler.load()\r
+ f.close()\r
+ \r
+ log.eventLog.setReplayRate(self.replayRate)\r
+ \r
+ pygame.midi.init()\r
+ instruments = [Instrument(log.scale, i + 1, "".join(["../instruments/instrumentImages/", constants.instrumentImagePathList[i], ".jpg"]), constants.octaves[i]) for i in range(9)]\r
+ \r
+ joys = [[id,pygame.joystick.Joystick(id).get_name()] for id in range(pygame.joystick.get_count())]\r
+ for joy in joys:\r
+ if joy[1] in constants.joyNames:\r
+ pygame.joystick.Joystick(joy[0]).init() \r
+ \r
+ ports = [pygame.midi.get_device_info(id)[1] for id in range(pygame.midi.get_count())]\r
+ portOffset = ports.index(constants.portNames[0])\r
+ print(portOffset)\r
+ \r
+ screen = pygame.display.get_surface()\r
+ clock = pygame.time.Clock() \r
+ cursorImages=[['../cursor/cursorImages/black/10.png'],['../cursor/cursorImages/red/10.png'],['../cursor/cursorImages/blue/10.png'],['../cursor/cursorImages/green/10.png']]\r
+ durations = [75 for i in range(len(cursorImages))]\r
+ \r
+ extsc = True\r
+ casc = False\r
+ easyMode = True\r
+ \r
+ song = Song(constants.scaleDict["majorScale"],[3,9,6,4,1,8,5,7,2,10],True)\r
+ \r
+ wiimoteCount = 4\r
+ \r
+ cursors = [WarpingCursor(None, cursorImages[i], durations, (300 * i, 300 * i),'../cursor/cursorImages/black/flash.png') for i in range(wiimoteCount)]\r
+ wiimotes = [Wiimote(i, i + portOffset, None, instruments[0], cursors[i]) for i in range(wiimoteCount)]\r
+ dummyInstrumentChoice = DummyInstrumentChoice(wiimotes, window, screen, clock, joys, portOffset, log.activeWiimotes)\r
+ if log.level < 2 :\r
+ familiarize = StaticFamiliarizer(wiimotes, window, screen, clock, joys, portOffset,log.activeWiimotes,level = log.level,eventLog = log.eventLog,replay = True)\r
+ self.fillStaticFamiliarizerStats(familiarize)\r
+ elif log.level == 2 :\r
+ familiarize = SongFamiliarizer(wiimotes, window, screen, clock, joys, portOffset,song,log.activeWiimotes,casc,extsc,easyMode,eventLog = log.eventLog,replay = True)\r
+ self.fillSongFamiliarizerStats(familiarize)\r
+ else :\r
+ familiarize = SongPlayingScreen(dummyInstrumentChoice,constants.songDict["clairdelalune"],easyMode = True,eventLog = log.eventLog,replay = True)\r
+ self.fillSongStats(familiarize) \r
+ \r
+ while familiarize.nextLevel != None :\r
+ if familiarize.nextLevel < 2 :\r
+ familiarize = StaticFamiliarizer(wiimotes, window, screen, clock, joys, portOffset,log.activeWiimotes,level = familiarize.nextLevel,eventLog = familiarize.eventLog,replay = True)\r
+ self.fillStaticFamiliarizerStats(familiarize)\r
+ elif familiarize.nextLevel == 2 :\r
+ familiarize = SongFamiliarizer(wiimotes, window, screen, clock, joys, portOffset,song,log.activeWiimotes,casc,extsc,easyMode,eventLog = familiarize.eventLog,replay = True)\r
+ self.fillSongFamiliarizerStats(familiarize)\r
+ else :\r
+ familiarize = SongPlayingScreen(dummyInstrumentChoice,constants.songDict["clairdelalune"],easyMode = True,eventLog = familiarize.eventLog,replay = True)\r
+ self.fillSongStats(familiarize)\r
+ \r
+ for wiimote in wiimotes:\r
+ del wiimote.port \r
+ \r
+ pygame.midi.quit()\r
+ self.printStatsToFile()\r
+ \r
+ def fillStaticFamiliarizerStats(self,familiarizer):\r
+ if familiarizer.level == 0 :\r
+ self.firstStepClicks.append(familiarizer.clicks)\r
+ self.firstStepClicksIn.append(familiarizer.clicksIn)\r
+ self.firstStepDurations.append(familiarizer.duration)\r
+ \r
+ if familiarizer.level == 1 :\r
+ self.secondStepClicks.append(familiarizer.clicks)\r
+ self.secondStepClicksIn.append(familiarizer.clicksIn)\r
+ self.secondStepDurations.append(familiarizer.duration)\r
+ \r
+ def fillSongFamiliarizerStats(self,familiarizer):\r
+ self.thirdStepClicks.append(familiarizer.clicks)\r
+ self.thirdStepClicksIn.append(familiarizer.clicksIn)\r
+ self.thirdStepDurations.append(familiarizer.duration)\r
+ \r
+ def fillSongStats(self,familiarizer):\r
+ self.songClicks.append(familiarizer.clicks)\r
+ self.songClicksIn.append(familiarizer.clicksIn)\r
+ self.songClicksPerMinute.append(familiarizer.clicksPerMinute)\r
+ self.songClicksInPerMinute.append(familiarizer.clicksInPerMinute)\r
+ self.songDurations.append(familiarizer.songDurations)\r
+ self.songTotalDurations.append(familiarizer.totalDuration)\r
+ \r
+ def statsToFormattedString(self):\r
+ return("First step durations :\n"+"\n"+\r
+ str(self.firstStepDurations)+"\n"+"\n"+\r
+ "First step clicks :\n"+"\n"+\r
+ str(self.firstStepClicks)+"\n"+"\n"+\r
+ "First step clicksIn :\n"+"\n"+\r
+ str(self.firstStepClicksIn)+"\n"+"\n"+\r
+ "Second step durations :\n"+"\n"+\r
+ str(self.secondStepDurations)+"\n"+"\n"+\r
+ "Second step clicks :\n"+"\n"+\r
+ str(self.secondStepClicks)+"\n"+"\n"+\r
+ "Second step clicksIn :\n"+"\n"+\r
+ str(self.secondStepClicksIn)+"\n"+"\n"+\r
+ "Third step durations :\n"+"\n"+\r
+ str(self.thirdStepDurations)+"\n"+"\n"+\r
+ "Third step clicks :\n"+"\n"+\r
+ str(self.thirdStepClicks)+"\n"+"\n"+\r
+ "Third step clicksIn :\n"+"\n"+\r
+ str(self.thirdStepClicksIn)+"\n"+"\n"+\r
+ "song durations :\n"+"\n"+\r
+ str(self.songDurations)+"\n"+"\n"+\r
+ "song clicks :\n"+"\n"+\r
+ str(self.songClicks)+"\n"+"\n"+\r
+ "song clicksIn :\n"+"\n"+\r
+ str(self.songClicksIn)+"\n"+"\n"+\r
+ "song clicks per minute:\n"+"\n"+\r
+ str(self.songClicksPerMinute)+"\n"+"\n"+\r
+ "song clicksIn per minute :\n"+"\n"+\r
+ str(self.songClicksInPerMinute)+"\n"+"\n"+\r
+ "song total durations :\n"+"\n"+\r
+ str(self.songTotalDurations)+"\n"+"\n")\r
+ \r
+ def printStatsToFile(self,path=None):\r
+ if path == None :\r
+ path = self.input_file.value.replace(".fmwi",".txt")\r
+ file = open(path,"w")\r
+ file.write("Log ID : "+self.input_file.value+"\n"+"\n")\r
+ file.write(self.statsToFormattedString())\r
+ file.close()\r
+ \r
+if __name__ == "__main__":\r
+ pygame.init()\r
+ modeResolution = (1024,768)\r
+ window = pygame.display.set_mode(modeResolution,pygame.FULLSCREEN)\r
+ logConfig = LogPGUPlayer(10000)\r
+ pygame.quit()\r
+
\ No newline at end of file
--- /dev/null
+import pygame\r
+import copy\r
+import pickle\r
+\r
+from pygame.event import Event\r
+\r
+class PickleableEvent(object):\r
+ "A pygame.Event that can be serialized."\r
+ \r
+ def __init__(self,type,dict):\r
+ self.__dict__ = copy.copy(dict)\r
+ self.type = type\r
+ self.event = Event(self.type,dict)\r
+\r
+ def __getstate__(self):\r
+ d = []\r
+ d.append(self.type)\r
+ d.append(copy.copy(self.event.dict))\r
+ return d\r
+\r
+ def __setstate__(self, d):\r
+ self.__dict__ = copy.copy(d[1])\r
+ self.type = d[0]\r
+ self.event = Event(d[0],d[1])
\ No newline at end of file
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+from struct import pack, unpack\r
+\r
+"""\r
+This module contains functions for reading and writing the special data types\r
+that a midi file contains.\r
+"""\r
+\r
+"""\r
+nibbles are four bits. A byte consists of two nibles.\r
+hiBits==0xF0, loBits==0x0F Especially used for setting\r
+channel and event in 1. byte of musical midi events\r
+"""\r
+\r
+\r
+\r
+def getNibbles(byte):\r
+ """\r
+ Returns hi and lo bits in a byte as a tuple\r
+ >>> getNibbles(142)\r
+ (8, 14)\r
+ \r
+ Asserts byte value in byte range\r
+ >>> getNibbles(256)\r
+ Traceback (most recent call last):\r
+ ...\r
+ ValueError: Byte value out of range 0-255: 256\r
+ """\r
+ if not 0 <= byte <= 255:\r
+ raise ValueError('Byte value out of range 0-255: %s' % byte)\r
+ return (byte >> 4 & 0xF, byte & 0xF)\r
+\r
+\r
+def setNibbles(hiNibble, loNibble):\r
+ """\r
+ Returns byte with value set according to hi and lo bits\r
+ Asserts hiNibble and loNibble in range(16)\r
+ >>> setNibbles(8, 14)\r
+ 142\r
+ \r
+ >>> setNibbles(8, 16)\r
+ Traceback (most recent call last):\r
+ ...\r
+ ValueError: Nible value out of range 0-15: (8, 16)\r
+ """\r
+ if not (0 <= hiNibble <= 15) or not (0 <= loNibble <= 15):\r
+ raise ValueError('Nible value out of range 0-15: (%s, %s)' % (hiNibble, loNibble))\r
+ return (hiNibble << 4) + loNibble\r
+\r
+\r
+\r
+def readBew(value):\r
+ """\r
+ Reads string as big endian word, (asserts len(value) in [1,2,4])\r
+ >>> readBew('aáâã')\r
+ 1642193635L\r
+ >>> readBew('aá')\r
+ 25057\r
+ """\r
+ return unpack('>%s' % {1:'B', 2:'H', 4:'L'}[len(value)], value)[0]\r
+\r
+\r
+def writeBew(value, length):\r
+ """\r
+ Write int as big endian formatted string, (asserts length in [1,2,4])\r
+ Difficult to print the result in doctest, so I do a simple roundabout test.\r
+ >>> readBew(writeBew(25057, 2))\r
+ 25057\r
+ >>> readBew(writeBew(1642193635L, 4))\r
+ 1642193635L\r
+ """\r
+ return pack('>%s' % {1:'B', 2:'H', 4:'L'}[length], value)\r
+\r
+\r
+\r
+"""\r
+Variable Length Data (varlen) is a data format sprayed liberally throughout\r
+a midi file. It can be anywhere from 1 to 4 bytes long.\r
+If the 8'th bit is set in a byte another byte follows. The value is stored\r
+in the lowest 7 bits of each byte. So max value is 4x7 bits = 28 bits.\r
+"""\r
+\r
+\r
+def readVar(value):\r
+ """\r
+ Converts varlength format to integer. Just pass it 0 or more chars that\r
+ might be a varlen and it will only use the relevant chars.\r
+ use varLen(readVar(value)) to see how many bytes the integer value takes.\r
+ asserts len(value) >= 0\r
+ >>> readVar('\80@')\r
+ 64\r
+ >>> readVar('áâãa')\r
+ 205042145\r
+ """\r
+ sum = 0\r
+ for byte in unpack('%sB' % len(value), value):\r
+ sum = (sum << 7) + (byte & 0x7F)\r
+ if not 0x80 & byte: break # stop after last byte\r
+ return sum\r
+\r
+\r
+\r
+def varLen(value):\r
+ """\r
+ Returns the the number of bytes an integer will be when\r
+ converted to varlength\r
+ """\r
+ if value <= 127:\r
+ return 1\r
+ elif value <= 16383:\r
+ return 2\r
+ elif value <= 2097151:\r
+ return 3\r
+ else:\r
+ return 4\r
+\r
+\r
+def writeVar(value):\r
+ "Converts an integer to varlength format"\r
+ sevens = to_n_bits(value, varLen(value))\r
+ for i in range(len(sevens)-1):\r
+ sevens[i] = sevens[i] | 0x80\r
+ return fromBytes(sevens)\r
+\r
+\r
+def to_n_bits(value, length=1, nbits=7):\r
+ "returns the integer value as a sequence of nbits bytes"\r
+ bytes = [(value >> (i*nbits)) & 0x7F for i in range(length)]\r
+ bytes.reverse()\r
+ return bytes\r
+\r
+\r
+def toBytes(value):\r
+ "Turns a string into a list of byte values"\r
+ return unpack('%sB' % len(value), value)\r
+\r
+\r
+def fromBytes(value):\r
+ "Turns a list of bytes into a string"\r
+ if not value:\r
+ return ''\r
+ return pack('%sB' % len(value), *value)\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+# print to7bits(0, 3)\r
+# print to7bits(127, 3)\r
+# print to7bits(255, 3)\r
+# print to7bits(65536, 3)\r
+\r
+ # simple test cases\r
+ \r
+# print 'getHiLoHex', getNibbles(16)\r
+# print 'setHiLoHex', setNibbles(1,0)\r
+# \r
+# print 'readBew', readBew('aáâã')\r
+# print 'writeBew', writeBew(1642193635, 4)\r
+#\r
+# print 'varLen', varLen(1)\r
+#\r
+ print 'readVar', readVar('\80@')\r
+ print 'writeVar', writeVar(8192)\r
+ \r
+ print 'readVar', readVar('áâãa')\r
+ print 'writeVar', writeVar(205058401)\r
+# \r
+# vartest = '\x82\xF7\x80\x00'\r
+# print 'toBytes', toBytes(vartest)\r
+# print 'fromBytes', fromBytes([48, 49, 50,])\r
+ \r
+ \r
+# instr = '\xFF\xFF\xFF\x00'\r
+# print 'readVar', readVar(instr)\r
+# inst2 = 268435455\r
+# print inst2\r
+# print writeVar(inst2)\r
+# print writeVar(readVar(instr))\r
+\r
+ s1 = 0x00000000\r
+ print '%08X -' % s1, '00', writeVar(s1)\r
+ s2 = 0x00000040\r
+ print '%08X -' % s2, '40', writeVar(s2)\r
+ s3 = 0x0000007F\r
+ print '%08X -' % s3, '7F', writeVar(s3)\r
+ s4 = 0x00000080\r
+ print '%08X -' % s4, '81 00', writeVar(s4)\r
+ s5 = 0x00002000\r
+ print '%08X -' % s5, 'C0 00', writeVar(s5)\r
+ s6 = 0x00003FFF\r
+ print '%08X -' % s6, 'FF 7F', writeVar(s6)\r
+ s7 = 0x00004000\r
+ print '%08X -' % s7, '81 80 00', writeVar(s7)\r
+ s8 = 0x00100000\r
+ print '%08X -' % s8, 'C0 80 00', writeVar(s8)\r
+ s9 = 0x001FFFFF\r
+ print '%08X -' % s9, 'FF FF 7F', writeVar(s9)\r
+ s10 = 0x00200000\r
+ print '%08X -' % s10, '81 80 80 00', writeVar(s10)\r
+ s11 = 0x08000000\r
+ print '%08X -' % s11, 'C0 80 80 00', writeVar(s11)\r
+ s12 = 0x0FFFFFFF\r
+ print '%08X -' % s12, 'FF FF FF 7F', writeVar(s12)\r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+ \r
+
\ No newline at end of file
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+# std library\r
+from struct import unpack\r
+\r
+# custom\r
+from DataTypeConverters import readBew, readVar, varLen, toBytes\r
+\r
+# uhh I don't really like this, but there are so many constants to \r
+# import otherwise\r
+from constants import *\r
+\r
+\r
+class EventDispatcher:\r
+\r
+\r
+ def __init__(self, outstream):\r
+ \r
+ """\r
+ \r
+ The event dispatcher generates events on the outstream.\r
+ \r
+ """\r
+ \r
+ # internal values, don't mess with 'em directly\r
+ self.outstream = outstream\r
+ \r
+ # public flags\r
+\r
+ # A note_on with a velocity of 0x00 is actually the same as a \r
+ # note_off with a velocity of 0x40. When \r
+ # "convert_zero_velocity" is set, the zero velocity note_on's \r
+ # automatically gets converted into note_off's. This is a less \r
+ # suprising behaviour for those that are not into the intimate \r
+ # details of the midi spec.\r
+ self.convert_zero_velocity = 1\r
+ \r
+ # If dispatch_continuos_controllers is true, continuos \r
+ # controllers gets dispatched to their defined handlers. Else \r
+ # they just trigger the "continuous_controller" event handler.\r
+ self.dispatch_continuos_controllers = 1 # NOT IMPLEMENTED YET\r
+ \r
+ # If dispatch_meta_events is true, meta events get's dispatched \r
+ # to their defined events. Else they all they trigger the \r
+ # "meta_event" handler.\r
+ self.dispatch_meta_events = 1\r
+\r
+\r
+\r
+ def header(self, format, nTracks, division):\r
+ "Triggers the header event"\r
+ self.outstream.header(format, nTracks, division)\r
+\r
+\r
+ def start_of_track(self, current_track):\r
+ "Triggers the start of track event"\r
+ \r
+ # I do this twice so that users can overwrite the \r
+ # start_of_track event handler without worrying whether the \r
+ # track number is updated correctly.\r
+ self.outstream.set_current_track(current_track)\r
+ self.outstream.start_of_track(current_track)\r
+ \r
+ \r
+ def sysex_event(self, data):\r
+ "Dispatcher for sysex events"\r
+ self.outstream.sysex_event(data)\r
+ \r
+ \r
+ def eof(self):\r
+ "End of file!"\r
+ self.outstream.eof()\r
+\r
+\r
+ def update_time(self, new_time=0, relative=1):\r
+ "Updates relative/absolute time."\r
+ self.outstream.update_time(new_time, relative)\r
+ \r
+ \r
+ def reset_time(self):\r
+ "Updates relative/absolute time."\r
+ self.outstream.reset_time()\r
+ \r
+ \r
+ # Event dispatchers for similar types of events\r
+ \r
+ \r
+ def channel_messages(self, hi_nible, channel, data):\r
+ \r
+ "Dispatches channel messages"\r
+ \r
+ stream = self.outstream\r
+ data = toBytes(data)\r
+ \r
+ if (NOTE_ON & 0xF0) == hi_nible:\r
+ note, velocity = data\r
+ # note_on with velocity 0x00 are same as note \r
+ # off with velocity 0x40 according to spec!\r
+ if velocity==0 and self.convert_zero_velocity:\r
+ stream.note_off(channel, note, 0x40)\r
+ else:\r
+ stream.note_on(channel, note, velocity)\r
+ \r
+ elif (NOTE_OFF & 0xF0) == hi_nible:\r
+ note, velocity = data\r
+ stream.note_off(channel, note, velocity)\r
+ \r
+ elif (AFTERTOUCH & 0xF0) == hi_nible:\r
+ note, velocity = data\r
+ stream.aftertouch(channel, note, velocity)\r
+ \r
+ elif (CONTINUOUS_CONTROLLER & 0xF0) == hi_nible:\r
+ controller, value = data\r
+ # A lot of the cc's are defined, so we trigger those directly\r
+ if self.dispatch_continuos_controllers:\r
+ self.continuous_controllers(channel, controller, value)\r
+ else:\r
+ stream.continuous_controller(channel, controller, value)\r
+ \r
+ elif (PATCH_CHANGE & 0xF0) == hi_nible:\r
+ program = data[0]\r
+ stream.patch_change(channel, program)\r
+ \r
+ elif (CHANNEL_PRESSURE & 0xF0) == hi_nible:\r
+ pressure = data[0]\r
+ stream.channel_pressure(channel, pressure)\r
+ \r
+ elif (PITCH_BEND & 0xF0) == hi_nible:\r
+ hibyte, lobyte = data\r
+ value = (hibyte<<7) + lobyte\r
+ stream.pitch_bend(channel, value)\r
+\r
+ else:\r
+\r
+ raise ValueError, 'Illegal channel message!'\r
+\r
+\r
+\r
+ def continuous_controllers(self, channel, controller, value):\r
+ \r
+ "Dispatches channel messages"\r
+\r
+ stream = self.outstream\r
+ \r
+ # I am not really shure if I ought to dispatch continuous controllers\r
+ # There's so many of them that it can clutter up the OutStream \r
+ # classes.\r
+ \r
+ # So I just trigger the default event handler\r
+ stream.continuous_controller(channel, controller, value)\r
+\r
+\r
+\r
+ def system_commons(self, common_type, common_data):\r
+ \r
+ "Dispatches system common messages"\r
+ \r
+ stream = self.outstream\r
+ \r
+ # MTC Midi time code Quarter value\r
+ if common_type == MTC:\r
+ data = readBew(common_data)\r
+ msg_type = (data & 0x07) >> 4\r
+ values = (data & 0x0F)\r
+ stream.midi_time_code(msg_type, values)\r
+ \r
+ elif common_type == SONG_POSITION_POINTER:\r
+ hibyte, lobyte = toBytes(common_data)\r
+ value = (hibyte<<7) + lobyte\r
+ stream.song_position_pointer(value)\r
+\r
+ elif common_type == SONG_SELECT:\r
+ data = readBew(common_data)\r
+ stream.song_select(data)\r
+\r
+ elif common_type == TUNING_REQUEST:\r
+ # no data then\r
+ stream.tuning_request(time=None)\r
+\r
+\r
+\r
+ def meta_event(self, meta_type, data):\r
+ \r
+ "Dispatches meta events"\r
+ \r
+ stream = self.outstream\r
+ \r
+ # SEQUENCE_NUMBER = 0x00 (00 02 ss ss (seq-number))\r
+ if meta_type == SEQUENCE_NUMBER:\r
+ number = readBew(data)\r
+ stream.sequence_number(number)\r
+ \r
+ # TEXT = 0x01 (01 len text...)\r
+ elif meta_type == TEXT:\r
+ stream.text(data)\r
+ \r
+ # COPYRIGHT = 0x02 (02 len text...)\r
+ elif meta_type == COPYRIGHT:\r
+ stream.copyright(data)\r
+ \r
+ # SEQUENCE_NAME = 0x03 (03 len text...)\r
+ elif meta_type == SEQUENCE_NAME:\r
+ stream.sequence_name(data)\r
+ \r
+ # INSTRUMENT_NAME = 0x04 (04 len text...)\r
+ elif meta_type == INSTRUMENT_NAME:\r
+ stream.instrument_name(data)\r
+ \r
+ # LYRIC = 0x05 (05 len text...)\r
+ elif meta_type == LYRIC:\r
+ stream.lyric(data)\r
+ \r
+ # MARKER = 0x06 (06 len text...)\r
+ elif meta_type == MARKER:\r
+ stream.marker(data)\r
+ \r
+ # CUEPOINT = 0x07 (07 len text...)\r
+ elif meta_type == CUEPOINT:\r
+ stream.cuepoint(data)\r
+ \r
+ # PROGRAM_NAME = 0x08 (05 len text...)\r
+ elif meta_type == PROGRAM_NAME:\r
+ stream.program_name(data)\r
+ \r
+ # DEVICE_NAME = 0x09 (09 len text...)\r
+ elif meta_type == DEVICE_NAME:\r
+ stream.device_name(data)\r
+ \r
+ # MIDI_CH_PREFIX = 0x20 (20 01 channel)\r
+ elif meta_type == MIDI_CH_PREFIX:\r
+ channel = readBew(data)\r
+ stream.midi_ch_prefix(channel)\r
+ \r
+ # MIDI_PORT = 0x21 (21 01 port (legacy stuff))\r
+ elif meta_type == MIDI_PORT:\r
+ port = readBew(data)\r
+ stream.midi_port(port)\r
+ \r
+ # END_OFF_TRACK = 0x2F (2F 00)\r
+ elif meta_type == END_OF_TRACK:\r
+ stream.end_of_track()\r
+ \r
+ # TEMPO = 0x51 (51 03 tt tt tt (tempo in us/quarternote))\r
+ elif meta_type == TEMPO:\r
+ b1, b2, b3 = toBytes(data)\r
+ # uses 3 bytes to represent time between quarter \r
+ # notes in microseconds\r
+ stream.tempo((b1<<16) + (b2<<8) + b3)\r
+ \r
+ # SMTP_OFFSET = 0x54 (54 05 hh mm ss ff xx)\r
+ elif meta_type == SMTP_OFFSET:\r
+ hour, minute, second, frame, framePart = toBytes(data)\r
+ stream.smtp_offset(\r
+ hour, minute, second, frame, framePart)\r
+ \r
+ # TIME_SIGNATURE = 0x58 (58 04 nn dd cc bb)\r
+ elif meta_type == TIME_SIGNATURE:\r
+ nn, dd, cc, bb = toBytes(data)\r
+ stream.time_signature(nn, dd, cc, bb)\r
+ \r
+ # KEY_SIGNATURE = 0x59 (59 02 sf mi)\r
+ elif meta_type == KEY_SIGNATURE:\r
+ sf, mi = toBytes(data)\r
+ stream.key_signature(sf, mi)\r
+ \r
+ # SPECIFIC = 0x7F (Sequencer specific event)\r
+ elif meta_type == SPECIFIC:\r
+ meta_data = toBytes(data)\r
+ stream.sequencer_specific(meta_data)\r
+ \r
+ # Handles any undefined meta events\r
+ else: # undefined meta type\r
+ meta_data = toBytes(data)\r
+ stream.meta_event(meta_type, meta_data)\r
+\r
+\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+\r
+ from MidiToText import MidiToText\r
+ \r
+ outstream = MidiToText()\r
+ dispatcher = EventDispatcher(outstream)\r
+ dispatcher.channel_messages(NOTE_ON, 0x00, '\x40\x40')
\ No newline at end of file
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+# std library\r
+from struct import unpack\r
+\r
+# uhh I don't really like this, but there are so many constants to \r
+# import otherwise\r
+from constants import *\r
+\r
+from EventDispatcher import EventDispatcher\r
+\r
+class MidiFileParser:\r
+\r
+ """\r
+ \r
+ The MidiFileParser is the lowest level parser that see the data as \r
+ midi data. It generates events that gets triggered on the outstream.\r
+ \r
+ """\r
+\r
+ def __init__(self, raw_in, outstream):\r
+\r
+ """\r
+ raw_data is the raw content of a midi file as a string.\r
+ """\r
+\r
+ # internal values, don't mess with 'em directly\r
+ self.raw_in = raw_in\r
+ self.dispatch = EventDispatcher(outstream)\r
+\r
+ # Used to keep track of stuff\r
+ self._running_status = None\r
+\r
+\r
+\r
+\r
+ def parseMThdChunk(self):\r
+ \r
+ "Parses the header chunk"\r
+ \r
+ raw_in = self.raw_in\r
+\r
+ header_chunk_type = raw_in.nextSlice(4)\r
+ header_chunk_zise = raw_in.readBew(4)\r
+\r
+ # check if it is a proper midi file\r
+ if header_chunk_type != 'MThd':\r
+ raise TypeError, "It is not a valid midi file!"\r
+\r
+ # Header values are at fixed locations, so no reason to be clever\r
+ self.format = raw_in.readBew(2)\r
+ self.nTracks = raw_in.readBew(2)\r
+ self.division = raw_in.readBew(2)\r
+ \r
+ # Theoretically a header larger than 6 bytes can exist\r
+ # but no one has seen one in the wild\r
+ # But correctly ignore unknown data if it is though\r
+ if header_chunk_zise > 6:\r
+ raw_in.moveCursor(header_chunk_zise-6)\r
+\r
+ # call the header event handler on the stream\r
+ self.dispatch.header(self.format, self.nTracks, self.division)\r
+\r
+\r
+\r
+ def parseMTrkChunk(self):\r
+ \r
+ "Parses a track chunk. This is the most important part of the parser."\r
+ \r
+ # set time to 0 at start of a track\r
+ self.dispatch.reset_time()\r
+ \r
+ dispatch = self.dispatch\r
+ raw_in = self.raw_in\r
+ \r
+ # Trigger event at the start of a track\r
+ dispatch.start_of_track(self._current_track)\r
+ # position cursor after track header\r
+ raw_in.moveCursor(4)\r
+ # unsigned long is 4 bytes\r
+ tracklength = raw_in.readBew(4)\r
+ track_endposition = raw_in.getCursor() + tracklength # absolute position!\r
+\r
+ while raw_in.getCursor() < track_endposition:\r
+ \r
+ # find relative time of the event\r
+ time = raw_in.readVarLen()\r
+ dispatch.update_time(time)\r
+ \r
+ # be aware of running status!!!!\r
+ peak_ahead = raw_in.readBew(move_cursor=0)\r
+ if (peak_ahead & 0x80): \r
+ # the status byte has the high bit set, so it\r
+ # was not running data but proper status byte\r
+ status = self._running_status = raw_in.readBew()\r
+ else:\r
+ # use that darn running status\r
+ status = self._running_status\r
+ # could it be illegal data ?? Do we need to test for that?\r
+ # I need more example midi files to be shure.\r
+ \r
+ # Also, while I am almost certain that no realtime \r
+ # messages will pop up in a midi file, I might need to \r
+ # change my mind later.\r
+\r
+ # we need to look at nibbles here\r
+ hi_nible, lo_nible = status & 0xF0, status & 0x0F\r
+ \r
+ # match up with events\r
+\r
+ # Is it a meta_event ??\r
+ # these only exists in midi files, not in transmitted midi data\r
+ # In transmitted data META_EVENT (0xFF) is a system reset\r
+ if status == META_EVENT:\r
+ meta_type = raw_in.readBew()\r
+ meta_length = raw_in.readVarLen()\r
+ meta_data = raw_in.nextSlice(meta_length)\r
+ dispatch.meta_event(meta_type, meta_data)\r
+\r
+\r
+ # Is it a sysex_event ??\r
+ elif status == SYSTEM_EXCLUSIVE:\r
+ # ignore sysex events\r
+ sysex_length = raw_in.readVarLen()\r
+ # don't read sysex terminator\r
+ sysex_data = raw_in.nextSlice(sysex_length-1)\r
+ # only read last data byte if it is a sysex terminator\r
+ # It should allways be there, but better safe than sorry\r
+ if raw_in.readBew(move_cursor=0) == END_OFF_EXCLUSIVE:\r
+ eo_sysex = raw_in.readBew()\r
+ dispatch.sysex_event(sysex_data)\r
+ # the sysex code has not been properly tested, and might be fishy!\r
+\r
+\r
+ # is it a system common event?\r
+ elif hi_nible == 0xF0: # Hi bits are set then\r
+ data_sizes = {\r
+ MTC:1,\r
+ SONG_POSITION_POINTER:2,\r
+ SONG_SELECT:1,\r
+ }\r
+ data_size = data_sizes.get(hi_nible, 0)\r
+ common_data = raw_in.nextSlice(data_size)\r
+ common_type = lo_nible\r
+ dispatch.system_common(common_type, common_data)\r
+ \r
+\r
+ # Oh! Then it must be a midi event (channel voice message)\r
+ else:\r
+ data_sizes = {\r
+ PATCH_CHANGE:1,\r
+ CHANNEL_PRESSURE:1,\r
+ NOTE_OFF:2,\r
+ NOTE_ON:2,\r
+ AFTERTOUCH:2,\r
+ CONTINUOUS_CONTROLLER:2,\r
+ PITCH_BEND:2,\r
+ }\r
+ data_size = data_sizes.get(hi_nible, 0)\r
+ channel_data = raw_in.nextSlice(data_size)\r
+ event_type, channel = hi_nible, lo_nible\r
+ dispatch.channel_messages(event_type, channel, channel_data)\r
+\r
+\r
+ def parseMTrkChunks(self):\r
+ "Parses all track chunks."\r
+ for t in range(self.nTracks):\r
+ self._current_track = t\r
+ self.parseMTrkChunk() # this is where it's at!\r
+ self.dispatch.eof()\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+ # get data\r
+ test_file = 'test/midifiles/minimal.mid'\r
+ test_file = 'test/midifiles/cubase-minimal.mid'\r
+ test_file = 'test/midifiles/Lola.mid'\r
+# f = open(test_file, 'rb')\r
+# raw_data = f.read()\r
+# f.close()\r
+# \r
+# \r
+# # do parsing\r
+ from MidiToText import MidiToText\r
+ from RawInstreamFile import RawInstreamFile\r
+\r
+ midi_in = MidiFileParser(RawInstreamFile(test_file), MidiToText())\r
+ midi_in.parseMThdChunk()\r
+ midi_in.parseMTrkChunks()\r
+
\ No newline at end of file
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+from RawInstreamFile import RawInstreamFile\r
+from MidiFileParser import MidiFileParser\r
+\r
+\r
+class MidiInFile:\r
+\r
+ """\r
+ \r
+ Parses a midi file, and triggers the midi events on the outStream \r
+ object.\r
+ \r
+ Get example data from a minimal midi file, generated with cubase.\r
+ >>> test_file = 'C:/Documents and Settings/maxm/Desktop/temp/midi/src/midi/tests/midifiles/minimal-cubase-type0.mid'\r
+ \r
+ Do parsing, and generate events with MidiToText,\r
+ so we can see what a minimal midi file contains\r
+ >>> from MidiToText import MidiToText\r
+ >>> midi_in = MidiInFile(MidiToText(), test_file)\r
+ >>> midi_in.read()\r
+ format: 0, nTracks: 1, division: 480\r
+ ----------------------------------\r
+ <BLANKLINE>\r
+ Start - track #0\r
+ sequence_name: Type 0\r
+ tempo: 500000\r
+ time_signature: 4 2 24 8\r
+ note_on - ch:00, note:48, vel:64 time:0\r
+ note_off - ch:00, note:48, vel:40 time:480\r
+ End of track\r
+ <BLANKLINE>\r
+ End of file\r
+ \r
+ \r
+ """\r
+\r
+ def __init__(self, outStream, infile):\r
+ # these could also have been mixins, would that be better? Nah!\r
+ self.raw_in = RawInstreamFile(infile)\r
+ self.parser = MidiFileParser(self.raw_in, outStream)\r
+\r
+\r
+ def read(self):\r
+ "Start parsing the file"\r
+ p = self.parser\r
+ p.parseMThdChunk()\r
+ p.parseMTrkChunks()\r
+\r
+\r
+ def setData(self, data=''):\r
+ "Sets the data from a plain string"\r
+ self.raw_in.setData(data)\r
+ \r
+ \r
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+from MidiOutStream import MidiOutStream\r
+\r
+class MidiInStream:\r
+\r
+ """\r
+ Takes midi events from the midi input and calls the apropriate\r
+ method in the eventhandler object\r
+ """\r
+\r
+ def __init__(self, midiOutStream, device):\r
+\r
+ """\r
+\r
+ Sets a default output stream, and sets the device from where\r
+ the input comes\r
+\r
+ """\r
+\r
+ if midiOutStream is None:\r
+ self.midiOutStream = MidiOutStream()\r
+ else:\r
+ self.midiOutStream = midiOutStream\r
+\r
+\r
+ def close(self):\r
+\r
+ """\r
+ Stop the MidiInstream\r
+ """\r
+\r
+\r
+ def read(self, time=0):\r
+\r
+ """\r
+\r
+ Start the MidiInstream.\r
+\r
+ "time" sets timer to specific start value.\r
+\r
+ """\r
+\r
+\r
+ def resetTimer(self, time=0):\r
+ """\r
+\r
+ Resets the timer, probably a good idea if there is some kind\r
+ of looping going on\r
+\r
+ """\r
+\r
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+from MidiOutStream import MidiOutStream\r
+from RawOutstreamFile import RawOutstreamFile\r
+\r
+from constants import *\r
+from DataTypeConverters import fromBytes, writeVar\r
+\r
+class MidiOutFile(MidiOutStream):\r
+\r
+\r
+ """\r
+ MidiOutFile is an eventhandler that subclasses MidiOutStream.\r
+ """\r
+\r
+\r
+ def __init__(self, raw_out=''):\r
+\r
+ self.raw_out = RawOutstreamFile(raw_out)\r
+ MidiOutStream.__init__(self)\r
+ \r
+ \r
+ def write(self):\r
+ self.raw_out.write()\r
+\r
+\r
+ def event_slice(self, slc):\r
+ """\r
+ Writes the slice of an event to the current track. Correctly \r
+ inserting a varlen timestamp too.\r
+ """\r
+ trk = self._current_track_buffer\r
+ trk.writeVarLen(self.rel_time())\r
+ trk.writeSlice(slc)\r
+ \r
+ \r
+ #####################\r
+ ## Midi events\r
+\r
+\r
+ def note_on(self, channel=0, note=0x40, velocity=0x40):\r
+\r
+ """\r
+ channel: 0-15\r
+ note, velocity: 0-127\r
+ """\r
+ slc = fromBytes([NOTE_ON + channel, note, velocity])\r
+ self.event_slice(slc)\r
+\r
+\r
+ def note_off(self, channel=0, note=0x40, velocity=0x40):\r
+\r
+ """\r
+ channel: 0-15\r
+ note, velocity: 0-127\r
+ """\r
+ slc = fromBytes([NOTE_OFF + channel, note, velocity])\r
+ self.event_slice(slc)\r
+\r
+\r
+ def aftertouch(self, channel=0, note=0x40, velocity=0x40):\r
+\r
+ """\r
+ channel: 0-15\r
+ note, velocity: 0-127\r
+ """\r
+ slc = fromBytes([AFTERTOUCH + channel, note, velocity])\r
+ self.event_slice(slc)\r
+\r
+\r
+ def continuous_controller(self, channel, controller, value):\r
+\r
+ """\r
+ channel: 0-15\r
+ controller, value: 0-127\r
+ """\r
+ slc = fromBytes([CONTINUOUS_CONTROLLER + channel, controller, value])\r
+ self.event_slice(slc)\r
+ # These should probably be implemented\r
+ # http://users.argonet.co.uk/users/lenny/midi/tech/spec.html#ctrlnums\r
+\r
+\r
+ def patch_change(self, channel, patch):\r
+\r
+ """\r
+ channel: 0-15\r
+ patch: 0-127\r
+ """\r
+ slc = fromBytes([PATCH_CHANGE + channel, patch])\r
+ self.event_slice(slc)\r
+\r
+\r
+ def channel_pressure(self, channel, pressure):\r
+\r
+ """\r
+ channel: 0-15\r
+ pressure: 0-127\r
+ """\r
+ slc = fromBytes([CHANNEL_PRESSURE + channel, pressure])\r
+ self.event_slice(slc)\r
+\r
+\r
+ def pitch_bend(self, channel, value):\r
+\r
+ """\r
+ channel: 0-15\r
+ value: 0-16383\r
+ """\r
+ msb = (value>>7) & 0xFF\r
+ lsb = value & 0xFF\r
+ slc = fromBytes([PITCH_BEND + channel, msb, lsb])\r
+ self.event_slice(slc)\r
+\r
+\r
+\r
+\r
+ #####################\r
+ ## System Exclusive\r
+\r
+# def sysex_slice(sysex_type, data):\r
+# ""\r
+# sysex_len = writeVar(len(data)+1)\r
+# self.event_slice(SYSTEM_EXCLUSIVE + sysex_len + data + END_OFF_EXCLUSIVE)\r
+#\r
+ def system_exclusive(self, data):\r
+\r
+ """\r
+ data: list of values in range(128)\r
+ """\r
+ sysex_len = writeVar(len(data)+1)\r
+ self.event_slice(chr(SYSTEM_EXCLUSIVE) + sysex_len + data + chr(END_OFF_EXCLUSIVE))\r
+\r
+\r
+ #####################\r
+ ## Common events\r
+\r
+ def midi_time_code(self, msg_type, values):\r
+ """\r
+ msg_type: 0-7\r
+ values: 0-15\r
+ """\r
+ value = (msg_type<<4) + values\r
+ self.event_slice(fromBytes([MIDI_TIME_CODE, value]))\r
+\r
+\r
+ def song_position_pointer(self, value):\r
+\r
+ """\r
+ value: 0-16383\r
+ """\r
+ lsb = (value & 0x7F)\r
+ msb = (value >> 7) & 0x7F\r
+ self.event_slice(fromBytes([SONG_POSITION_POINTER, lsb, msb]))\r
+\r
+\r
+ def song_select(self, songNumber):\r
+\r
+ """\r
+ songNumber: 0-127\r
+ """\r
+ self.event_slice(fromBytes([SONG_SELECT, songNumber]))\r
+\r
+\r
+ def tuning_request(self):\r
+\r
+ """\r
+ No values passed\r
+ """\r
+ self.event_slice(chr(TUNING_REQUEST))\r
+\r
+ \r
+ #########################\r
+ # header does not really belong here. But anyhoo!!!\r
+ \r
+ def header(self, format=0, nTracks=1, division=96):\r
+\r
+ """\r
+ format: type of midi file in [0,1,2]\r
+ nTracks: number of tracks. 1 track for type 0 file\r
+ division: timing division ie. 96 ppq.\r
+ \r
+ """ \r
+ raw = self.raw_out\r
+ raw.writeSlice('MThd')\r
+ bew = raw.writeBew\r
+ bew(6, 4) # header size\r
+ bew(format, 2)\r
+ bew(nTracks, 2)\r
+ bew(division, 2)\r
+\r
+\r
+ def eof(self):\r
+\r
+ """\r
+ End of file. No more events to be processed.\r
+ """\r
+ # just write the file then.\r
+ self.write()\r
+\r
+\r
+ #####################\r
+ ## meta events\r
+\r
+\r
+ def meta_slice(self, meta_type, data_slice):\r
+ "Writes a meta event"\r
+ slc = fromBytes([META_EVENT, meta_type]) + \\r
+ writeVar(len(data_slice)) + data_slice\r
+ self.event_slice(slc)\r
+\r
+\r
+ def meta_event(self, meta_type, data):\r
+ """\r
+ Handles any undefined meta events\r
+ """\r
+ self.meta_slice(meta_type, fromBytes(data))\r
+\r
+\r
+ def start_of_track(self, n_track=0):\r
+ """\r
+ n_track: number of track\r
+ """\r
+ self._current_track_buffer = RawOutstreamFile()\r
+ self.reset_time()\r
+ self._current_track += 1\r
+\r
+\r
+ def end_of_track(self):\r
+ """\r
+ Writes the track to the buffer.\r
+ """\r
+ raw = self.raw_out\r
+ raw.writeSlice(TRACK_HEADER)\r
+ track_data = self._current_track_buffer.getvalue()\r
+ # wee need to know size of track data.\r
+ eot_slice = writeVar(self.rel_time()) + fromBytes([META_EVENT, END_OF_TRACK, 0])\r
+ raw.writeBew(len(track_data)+len(eot_slice), 4)\r
+ # then write\r
+ raw.writeSlice(track_data)\r
+ raw.writeSlice(eot_slice)\r
+ \r
+\r
+\r
+ def sequence_number(self, value):\r
+\r
+ """\r
+ value: 0-65535\r
+ """\r
+ self.meta_slice(meta_type, writeBew(value, 2))\r
+\r
+\r
+ def text(self, text):\r
+ """\r
+ Text event\r
+ text: string\r
+ """\r
+ self.meta_slice(TEXT, text)\r
+\r
+\r
+ def copyright(self, text):\r
+\r
+ """\r
+ Copyright notice\r
+ text: string\r
+ """\r
+ self.meta_slice(COPYRIGHT, text)\r
+\r
+\r
+ def sequence_name(self, text):\r
+ """\r
+ Sequence/track name\r
+ text: string\r
+ """\r
+ self.meta_slice(SEQUENCE_NAME, text)\r
+\r
+\r
+ def instrument_name(self, text):\r
+\r
+ """\r
+ text: string\r
+ """\r
+ self.meta_slice(INSTRUMENT_NAME, text)\r
+\r
+\r
+ def lyric(self, text):\r
+\r
+ """\r
+ text: string\r
+ """\r
+ self.meta_slice(LYRIC, text)\r
+\r
+\r
+ def marker(self, text):\r
+\r
+ """\r
+ text: string\r
+ """\r
+ self.meta_slice(MARKER, text)\r
+\r
+\r
+ def cuepoint(self, text):\r
+\r
+ """\r
+ text: string\r
+ """\r
+ self.meta_slice(CUEPOINT, text)\r
+\r
+\r
+ def midi_ch_prefix(self, channel):\r
+\r
+ """\r
+ channel: midi channel for subsequent data\r
+ (deprecated in the spec)\r
+ """\r
+ self.meta_slice(MIDI_CH_PREFIX, chr(channel))\r
+\r
+\r
+ def midi_port(self, value):\r
+\r
+ """\r
+ value: Midi port (deprecated in the spec)\r
+ """\r
+ self.meta_slice(MIDI_CH_PREFIX, chr(value))\r
+\r
+\r
+ def tempo(self, value):\r
+\r
+ """\r
+ value: 0-2097151\r
+ tempo in us/quarternote\r
+ (to calculate value from bpm: int(60,000,000.00 / BPM))\r
+ """\r
+ hb, mb, lb = (value>>16 & 0xff), (value>>8 & 0xff), (value & 0xff)\r
+ self.meta_slice(TEMPO, fromBytes([hb, mb, lb]))\r
+\r
+\r
+ def smtp_offset(self, hour, minute, second, frame, framePart):\r
+\r
+ """\r
+ hour,\r
+ minute,\r
+ second: 3 bytes specifying the hour (0-23), minutes (0-59) and \r
+ seconds (0-59), respectively. The hour should be \r
+ encoded with the SMPTE format, just as it is in MIDI \r
+ Time Code.\r
+ frame: A byte specifying the number of frames per second (one \r
+ of : 24, 25, 29, 30).\r
+ framePart: A byte specifying the number of fractional frames, \r
+ in 100ths of a frame (even in SMPTE-based tracks \r
+ using a different frame subdivision, defined in the \r
+ MThd chunk).\r
+ """\r
+ self.meta_slice(SMTP_OFFSET, fromBytes([hour, minute, second, frame, framePart]))\r
+\r
+\r
+\r
+ def time_signature(self, nn, dd, cc, bb):\r
+\r
+ """\r
+ nn: Numerator of the signature as notated on sheet music\r
+ dd: Denominator of the signature as notated on sheet music\r
+ The denominator is a negative power of 2: 2 = quarter \r
+ note, 3 = eighth, etc.\r
+ cc: The number of MIDI clocks in a metronome click\r
+ bb: The number of notated 32nd notes in a MIDI quarter note \r
+ (24 MIDI clocks) \r
+ """\r
+ self.meta_slice(TIME_SIGNATURE, fromBytes([nn, dd, cc, bb]))\r
+\r
+\r
+\r
+\r
+ def key_signature(self, sf, mi):\r
+\r
+ """\r
+ sf: is a byte specifying the number of flats (-ve) or sharps \r
+ (+ve) that identifies the key signature (-7 = 7 flats, -1 \r
+ = 1 flat, 0 = key of C, 1 = 1 sharp, etc).\r
+ mi: is a byte specifying a major (0) or minor (1) key.\r
+ """\r
+ self.meta_slice(KEY_SIGNATURE, fromBytes([sf, mi]))\r
+\r
+\r
+\r
+ def sequencer_specific(self, data):\r
+\r
+ """\r
+ data: The data as byte values\r
+ """\r
+ self.meta_slice(SEQUENCER_SPECIFIC, data)\r
+\r
+\r
+\r
+\r
+\r
+# #####################\r
+# ## realtime events\r
+\r
+# These are of no use in a midi file, so they are ignored!!!\r
+\r
+# def timing_clock(self):\r
+# def song_start(self):\r
+# def song_stop(self):\r
+# def song_continue(self):\r
+# def active_sensing(self):\r
+# def system_reset(self):\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+ out_file = 'test/midifiles/midiout.mid'\r
+ midi = MidiOutFile(out_file)\r
+\r
+#format: 0, nTracks: 1, division: 480\r
+#----------------------------------\r
+#\r
+#Start - track #0\r
+#sequence_name: Type 0\r
+#tempo: 500000\r
+#time_signature: 4 2 24 8\r
+#note_on - ch:00, note:48, vel:64 time:0\r
+#note_off - ch:00, note:48, vel:40 time:480\r
+#End of track\r
+#\r
+#End of file\r
+\r
+\r
+ midi.header(0, 1, 480)\r
+ \r
+ midi.start_of_track()\r
+ midi.sequence_name('Type 0')\r
+ midi.tempo(750000)\r
+ midi.time_signature(4, 2, 24, 8)\r
+ ch = 0\r
+ for i in range(127):\r
+ midi.note_on(ch, i, 0x64)\r
+ midi.update_time(96)\r
+ midi.note_off(ch, i, 0x40)\r
+ midi.update_time(0)\r
+ \r
+ midi.update_time(0)\r
+ midi.end_of_track()\r
+ \r
+ midi.eof() # currently optional, should it do the write instead of write??\r
+\r
+\r
+ midi.write()
\ No newline at end of file
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+class MidiOutStream:\r
+\r
+\r
+ """\r
+\r
+ MidiOutstream is Basically an eventhandler. It is the most central\r
+ class in the Midi library. You use it both for writing events to\r
+ an output stream, and as an event handler for an input stream.\r
+\r
+ This makes it extremely easy to take input from one stream and\r
+ send it to another. Ie. if you want to read a Midi file, do some\r
+ processing, and send it to a midiport.\r
+\r
+ All time values are in absolute values from the opening of a\r
+ stream. To calculate time values, please use the MidiTime and\r
+ MidiDeltaTime classes.\r
+\r
+ """\r
+\r
+ def __init__(self):\r
+ \r
+ # the time is rather global, so it needs to be stored \r
+ # here. Otherwise there would be no really simple way to \r
+ # calculate it. The alternative would be to have each event \r
+ # handler do it. That sucks even worse!\r
+ self._absolute_time = 0\r
+ self._relative_time = 0\r
+ self._current_track = 0\r
+ self._running_status = None\r
+\r
+ # time handling event handlers. They should be overwritten with care\r
+\r
+ def update_time(self, new_time=0, relative=1):\r
+ """\r
+ Updates the time, if relative is true, new_time is relative, \r
+ else it's absolute.\r
+ """\r
+ if relative:\r
+ self._relative_time = new_time\r
+ self._absolute_time += new_time\r
+ else:\r
+ self._relative_time = new_time - self._absolute_time\r
+ self._absolute_time = new_time\r
+\r
+ def reset_time(self):\r
+ """\r
+ reset time to 0\r
+ """\r
+ self._relative_time = 0\r
+ self._absolute_time = 0\r
+ \r
+ def rel_time(self):\r
+ "Returns the relative time"\r
+ return self._relative_time\r
+\r
+ def abs_time(self):\r
+ "Returns the absolute time"\r
+ return self._absolute_time\r
+\r
+ # running status methods\r
+ \r
+ def reset_run_stat(self):\r
+ "Invalidates the running status"\r
+ self._running_status = None\r
+\r
+ def set_run_stat(self, new_status):\r
+ "Set the new running status"\r
+ self._running_status = new_status\r
+\r
+ def get_run_stat(self):\r
+ "Set the new running status"\r
+ return self._running_status\r
+\r
+ # track handling event handlers\r
+ \r
+ def set_current_track(self, new_track):\r
+ "Sets the current track number"\r
+ self._current_track = new_track\r
+ \r
+ def get_current_track(self):\r
+ "Returns the current track number"\r
+ return self._current_track\r
+ \r
+ \r
+ #####################\r
+ ## Midi events\r
+\r
+\r
+ def channel_message(self, message_type, channel, data):\r
+ """The default event handler for channel messages"""\r
+ pass\r
+\r
+\r
+ def note_on(self, channel=0, note=0x40, velocity=0x40):\r
+\r
+ """\r
+ channel: 0-15\r
+ note, velocity: 0-127\r
+ """\r
+ pass\r
+\r
+\r
+ def note_off(self, channel=0, note=0x40, velocity=0x40):\r
+\r
+ """\r
+ channel: 0-15\r
+ note, velocity: 0-127\r
+ """\r
+ pass\r
+\r
+\r
+ def aftertouch(self, channel=0, note=0x40, velocity=0x40):\r
+\r
+ """\r
+ channel: 0-15\r
+ note, velocity: 0-127\r
+ """\r
+ pass\r
+\r
+\r
+ def continuous_controller(self, channel, controller, value):\r
+\r
+ """\r
+ channel: 0-15\r
+ controller, value: 0-127\r
+ """\r
+ pass\r
+\r
+\r
+ def patch_change(self, channel, patch):\r
+\r
+ """\r
+ channel: 0-15\r
+ patch: 0-127\r
+ """\r
+ pass\r
+\r
+\r
+ def channel_pressure(self, channel, pressure):\r
+\r
+ """\r
+ channel: 0-15\r
+ pressure: 0-127\r
+ """\r
+ pass\r
+\r
+\r
+ def pitch_bend(self, channel, value):\r
+\r
+ """\r
+ channel: 0-15\r
+ value: 0-16383\r
+\r
+ """\r
+ pass\r
+\r
+\r
+\r
+\r
+ #####################\r
+ ## System Exclusive\r
+\r
+ def system_exclusive(self, data):\r
+\r
+ """\r
+ data: list of values in range(128)\r
+ """\r
+ pass\r
+\r
+\r
+ #####################\r
+ ## Common events\r
+\r
+ def song_position_pointer(self, value):\r
+\r
+ """\r
+ value: 0-16383\r
+ """\r
+ pass\r
+\r
+\r
+ def song_select(self, songNumber):\r
+\r
+ """\r
+ songNumber: 0-127\r
+ """\r
+ pass\r
+\r
+\r
+ def tuning_request(self):\r
+\r
+ """\r
+ No values passed\r
+ """\r
+ pass\r
+\r
+ \r
+ def midi_time_code(self, msg_type, values):\r
+ """\r
+ msg_type: 0-7\r
+ values: 0-15\r
+ """\r
+ pass\r
+\r
+\r
+ #########################\r
+ # header does not really belong here. But anyhoo!!!\r
+ \r
+ def header(self, format=0, nTracks=1, division=96):\r
+\r
+ """\r
+ format: type of midi file in [1,2]\r
+ nTracks: number of tracks\r
+ division: timing division\r
+ """\r
+ pass\r
+\r
+\r
+ def eof(self):\r
+\r
+ """\r
+ End of file. No more events to be processed.\r
+ """\r
+ pass\r
+\r
+\r
+ #####################\r
+ ## meta events\r
+\r
+\r
+ def meta_event(self, meta_type, data):\r
+ \r
+ """\r
+ Handles any undefined meta events\r
+ """\r
+ pass\r
+\r
+\r
+ def start_of_track(self, n_track=0):\r
+\r
+ """\r
+ n_track: number of track\r
+ """\r
+ pass\r
+\r
+\r
+ def end_of_track(self):\r
+\r
+ """\r
+ n_track: number of track\r
+ """\r
+ pass\r
+\r
+\r
+ def sequence_number(self, value):\r
+\r
+ """\r
+ value: 0-16383\r
+ """\r
+ pass\r
+\r
+\r
+ def text(self, text):\r
+\r
+ """\r
+ Text event\r
+ text: string\r
+ """\r
+ pass\r
+\r
+\r
+ def copyright(self, text):\r
+\r
+ """\r
+ Copyright notice\r
+ text: string\r
+ """\r
+ pass\r
+\r
+\r
+ def sequence_name(self, text):\r
+\r
+ """\r
+ Sequence/track name\r
+ text: string\r
+ """\r
+ pass\r
+\r
+\r
+ def instrument_name(self, text):\r
+\r
+ """\r
+ text: string\r
+ """\r
+ pass\r
+\r
+\r
+ def lyric(self, text):\r
+\r
+ """\r
+ text: string\r
+ """\r
+ pass\r
+\r
+\r
+ def marker(self, text):\r
+\r
+ """\r
+ text: string\r
+ """\r
+ pass\r
+\r
+\r
+ def cuepoint(self, text):\r
+\r
+ """\r
+ text: string\r
+ """\r
+ pass\r
+\r
+\r
+ def midi_ch_prefix(self, channel):\r
+\r
+ """\r
+ channel: midi channel for subsequent data (deprecated in the spec)\r
+ """\r
+ pass\r
+\r
+\r
+ def midi_port(self, value):\r
+\r
+ """\r
+ value: Midi port (deprecated in the spec)\r
+ """\r
+ pass\r
+\r
+\r
+ def tempo(self, value):\r
+\r
+ """\r
+ value: 0-2097151\r
+ tempo in us/quarternote\r
+ (to calculate value from bpm: int(60,000,000.00 / BPM))\r
+ """\r
+ pass\r
+\r
+\r
+ def smtp_offset(self, hour, minute, second, frame, framePart):\r
+\r
+ """\r
+ hour,\r
+ minute,\r
+ second: 3 bytes specifying the hour (0-23), minutes (0-59) and \r
+ seconds (0-59), respectively. The hour should be \r
+ encoded with the SMPTE format, just as it is in MIDI \r
+ Time Code.\r
+ frame: A byte specifying the number of frames per second (one \r
+ of : 24, 25, 29, 30).\r
+ framePart: A byte specifying the number of fractional frames, \r
+ in 100ths of a frame (even in SMPTE-based tracks \r
+ using a different frame subdivision, defined in the \r
+ MThd chunk).\r
+ """\r
+ pass\r
+\r
+\r
+\r
+ def time_signature(self, nn, dd, cc, bb):\r
+\r
+ """\r
+ nn: Numerator of the signature as notated on sheet music\r
+ dd: Denominator of the signature as notated on sheet music\r
+ The denominator is a negative power of 2: 2 = quarter \r
+ note, 3 = eighth, etc.\r
+ cc: The number of MIDI clocks in a metronome click\r
+ bb: The number of notated 32nd notes in a MIDI quarter note \r
+ (24 MIDI clocks) \r
+ """\r
+ pass\r
+\r
+\r
+\r
+ def key_signature(self, sf, mi):\r
+\r
+ """\r
+ sf: is a byte specifying the number of flats (-ve) or sharps \r
+ (+ve) that identifies the key signature (-7 = 7 flats, -1 \r
+ = 1 flat, 0 = key of C, 1 = 1 sharp, etc).\r
+ mi: is a byte specifying a major (0) or minor (1) key.\r
+ """\r
+ pass\r
+\r
+\r
+\r
+ def sequencer_specific(self, data):\r
+\r
+ """\r
+ data: The data as byte values\r
+ """\r
+ pass\r
+\r
+\r
+\r
+\r
+ #####################\r
+ ## realtime events\r
+\r
+ def timing_clock(self):\r
+\r
+ """\r
+ No values passed\r
+ """\r
+ pass\r
+\r
+\r
+\r
+ def song_start(self):\r
+\r
+ """\r
+ No values passed\r
+ """\r
+ pass\r
+\r
+\r
+\r
+ def song_stop(self):\r
+\r
+ """\r
+ No values passed\r
+ """\r
+ pass\r
+\r
+\r
+\r
+ def song_continue(self):\r
+\r
+ """\r
+ No values passed\r
+ """\r
+ pass\r
+\r
+\r
+\r
+ def active_sensing(self):\r
+\r
+ """\r
+ No values passed\r
+ """\r
+ pass\r
+\r
+\r
+\r
+ def system_reset(self):\r
+\r
+ """\r
+ No values passed\r
+ """\r
+ pass\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+ midiOut = MidiOutStream()\r
+ midiOut.update_time(0,0)\r
+ midiOut.note_on(0, 63, 127)\r
+ midiOut.note_off(0, 63, 127)\r
+\r
+
\ No newline at end of file
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+from MidiOutStream import MidiOutStream\r
+class MidiToText(MidiOutStream):\r
+\r
+\r
+ """\r
+ This class renders a midi file as text. It is mostly used for debugging\r
+ """\r
+\r
+ #############################\r
+ # channel events\r
+ \r
+ def channel_message(self, message_type, channel, data):\r
+ """The default event handler for channel messages"""\r
+ print 'message_type:%X, channel:%X, data size:%X' % (message_type, channel, len(data))\r
+\r
+\r
+ def note_on(self, channel=0, note=0x40, velocity=0x40):\r
+ print 'note_on - ch:%02X, note:%02X, vel:%02X time:%s' % (channel, note, velocity, self.timeInMs())\r
+\r
+ def note_off(self, channel=0, note=0x40, velocity=0x40):\r
+ print 'note_off - ch:%02X, note:%02X, vel:%02X time:%s' % (channel, note, velocity, self.abs_time())\r
+\r
+ def aftertouch(self, channel=0, note=0x40, velocity=0x40):\r
+ print 'aftertouch', channel, note, velocity\r
+\r
+\r
+ def continuous_controller(self, channel, controller, value):\r
+ print 'controller - ch: %02X, cont: #%02X, value: %02X' % (channel, controller, value)\r
+\r
+\r
+ def patch_change(self, channel, patch):\r
+ print 'patch_change - ch:%02X, patch:%02X' % (channel, patch)\r
+\r
+\r
+ def channel_pressure(self, channel, pressure):\r
+ print 'channel_pressure', channel, pressure\r
+\r
+\r
+ def pitch_bend(self, channel, value):\r
+ print 'pitch_bend ch:%s, value:%s' % (channel, value)\r
+\r
+\r
+\r
+ #####################\r
+ ## Common events\r
+\r
+\r
+ def system_exclusive(self, data):\r
+ print 'system_exclusive - data size: %s' % len(data)\r
+\r
+\r
+ def song_position_pointer(self, value):\r
+ print 'song_position_pointer: %s' % value\r
+\r
+\r
+ def song_select(self, songNumber):\r
+ print 'song_select: %s' % songNumber\r
+\r
+\r
+ def tuning_request(self):\r
+ print 'tuning_request'\r
+\r
+\r
+ def midi_time_code(self, msg_type, values):\r
+ print 'midi_time_code - msg_type: %s, values: %s' % (msg_type, values)\r
+\r
+\r
+\r
+ #########################\r
+ # header does not really belong here. But anyhoo!!!\r
+\r
+ def header(self, format=0, nTracks=1, division=96):\r
+ print 'format: %s, nTracks: %s, division: %s' % (format, nTracks, division)\r
+ print '----------------------------------'\r
+ print ''\r
+ print division\r
+ self.division = division\r
+\r
+ def eof(self):\r
+ print 'End of file'\r
+\r
+\r
+ def start_of_track(self, n_track=0):\r
+ print 'Start - track #%s' % n_track\r
+\r
+\r
+ def end_of_track(self):\r
+ print 'End of track'\r
+ print ''\r
+\r
+\r
+\r
+ ###############\r
+ # sysex event\r
+\r
+ def sysex_event(self, data):\r
+ print 'sysex_event - datasize: %X' % len(data)\r
+\r
+\r
+ #####################\r
+ ## meta events\r
+\r
+ def meta_event(self, meta_type, data):\r
+ print 'undefined_meta_event:', meta_type, len(data)\r
+\r
+\r
+ def sequence_number(self, value):\r
+ print 'sequence_number', value\r
+\r
+\r
+ def text(self, text):\r
+ print 'text', text\r
+\r
+\r
+ def copyright(self, text):\r
+ print 'copyright', text\r
+\r
+\r
+ def sequence_name(self, text):\r
+ print 'sequence_name:', text\r
+\r
+\r
+ def instrument_name(self, text):\r
+ print 'instrument_name:', text\r
+\r
+\r
+ def lyric(self, text):\r
+ print 'lyric', text\r
+\r
+\r
+ def marker(self, text):\r
+ print 'marker', text\r
+\r
+\r
+ def cuepoint(self, text):\r
+ print 'cuepoint', text\r
+\r
+\r
+ def midi_ch_prefix(self, channel):\r
+ print 'midi_ch_prefix', channel\r
+\r
+\r
+ def midi_port(self, value):\r
+ print 'midi_port:', value\r
+\r
+\r
+ def tempo(self, value):\r
+ print 'tempo:', value\r
+ self.tempo = value\r
+\r
+\r
+ def smtp_offset(self, hour, minute, second, frame, framePart):\r
+ print 'smtp_offset', hour, minute, second, frame, framePart\r
+\r
+\r
+ def time_signature(self, nn, dd, cc, bb):\r
+ print 'time_signature:', nn, dd, cc, bb\r
+\r
+\r
+ def key_signature(self, sf, mi):\r
+ print 'key_signature', sf, mi\r
+\r
+\r
+ def sequencer_specific(self, data):\r
+ print 'sequencer_specific', len(data)\r
+ \r
+ def timeInMs(self):\r
+ return(long(self.abs_time())*1000000000/(long(self.tempo)*long(self.division)))\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+ # get data\r
+ test_file = '../songs/midis/test.mid'\r
+ f = open(test_file, 'rb')\r
+ \r
+ # do parsing\r
+ from MidiInFile import MidiInFile\r
+ midiIn = MidiInFile(MidiToText(), f)\r
+ midiIn.read()\r
+ f.close()\r
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+# standard library imports\r
+from types import StringType\r
+from struct import unpack\r
+\r
+# custom import\r
+from DataTypeConverters import readBew, readVar, varLen\r
+\r
+\r
+class RawInstreamFile:\r
+ \r
+ """\r
+ \r
+ It parses and reads data from an input file. It takes care of big \r
+ endianess, and keeps track of the cursor position. The midi parser \r
+ only reads from this object. Never directly from the file.\r
+ \r
+ """\r
+ \r
+ def __init__(self, infile=''):\r
+ """ \r
+ If 'file' is a string we assume it is a path and read from \r
+ that file.\r
+ If it is a file descriptor we read from the file, but we don't \r
+ close it.\r
+ Midi files are usually pretty small, so it should be safe to \r
+ copy them into memory.\r
+ """\r
+ if infile:\r
+ if isinstance(infile, StringType):\r
+ infile = open(infile, 'rb')\r
+ self.data = infile.read()\r
+ infile.close()\r
+ else:\r
+ # don't close the f\r
+ self.data = infile.read()\r
+ else:\r
+ self.data = ''\r
+ # start at beginning ;-)\r
+ self.cursor = 0\r
+\r
+\r
+ # setting up data manually\r
+ \r
+ def setData(self, data=''):\r
+ "Sets the data from a string."\r
+ self.data = data\r
+ \r
+ # cursor operations\r
+\r
+ def setCursor(self, position=0):\r
+ "Sets the absolute position if the cursor"\r
+ self.cursor = position\r
+\r
+\r
+ def getCursor(self):\r
+ "Returns the value of the cursor"\r
+ return self.cursor\r
+ \r
+ \r
+ def moveCursor(self, relative_position=0):\r
+ "Moves the cursor to a new relative position"\r
+ self.cursor += relative_position\r
+\r
+ # native data reading functions\r
+ \r
+ def nextSlice(self, length, move_cursor=1):\r
+ "Reads the next text slice from the raw data, with length"\r
+ c = self.cursor\r
+ slc = self.data[c:c+length]\r
+ if move_cursor:\r
+ self.moveCursor(length)\r
+ return slc\r
+ \r
+ \r
+ def readBew(self, n_bytes=1, move_cursor=1):\r
+ """\r
+ Reads n bytes of date from the current cursor position.\r
+ Moves cursor if move_cursor is true\r
+ """\r
+ return readBew(self.nextSlice(n_bytes, move_cursor))\r
+\r
+\r
+ def readVarLen(self):\r
+ """\r
+ Reads a variable length value from the current cursor position.\r
+ Moves cursor if move_cursor is true\r
+ """\r
+ MAX_VARLEN = 4 # Max value varlen can be\r
+ var = readVar(self.nextSlice(MAX_VARLEN, 0))\r
+ # only move cursor the actual bytes in varlen\r
+ self.moveCursor(varLen(var))\r
+ return var\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+ test_file = 'test/midifiles/minimal.mid'\r
+ fis = RawInstreamFile(test_file)\r
+ print fis.nextSlice(len(fis.data))\r
+\r
+ test_file = 'test/midifiles/cubase-minimal.mid'\r
+ cubase_minimal = open(test_file, 'rb')\r
+ fis2 = RawInstreamFile(cubase_minimal)\r
+ print fis2.nextSlice(len(fis2.data))\r
+ cubase_minimal.close()\r
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+# standard library imports\r
+import sys\r
+from types import StringType\r
+from struct import unpack\r
+from cStringIO import StringIO\r
+\r
+# custom import\r
+from DataTypeConverters import writeBew, writeVar, fromBytes\r
+\r
+class RawOutstreamFile:\r
+ \r
+ """\r
+ \r
+ Writes a midi file to disk.\r
+ \r
+ """\r
+\r
+ def __init__(self, outfile=''):\r
+ self.buffer = StringIO()\r
+ self.outfile = outfile\r
+\r
+\r
+ # native data reading functions\r
+\r
+\r
+ def writeSlice(self, str_slice):\r
+ "Writes the next text slice to the raw data"\r
+ self.buffer.write(str_slice)\r
+ \r
+ \r
+ def writeBew(self, value, length=1):\r
+ "Writes a value to the file as big endian word"\r
+ self.writeSlice(writeBew(value, length))\r
+\r
+\r
+ def writeVarLen(self, value):\r
+ "Writes a variable length word to the file"\r
+ var = self.writeSlice(writeVar(value))\r
+\r
+\r
+ def write(self):\r
+ "Writes to disc"\r
+ if self.outfile:\r
+ if isinstance(self.outfile, StringType):\r
+ outfile = open(self.outfile, 'wb')\r
+ outfile.write(self.getvalue())\r
+ outfile.close()\r
+ else:\r
+ self.outfile.write(self.getvalue())\r
+ else:\r
+ sys.stdout.write(self.getvalue())\r
+ \r
+ def getvalue(self):\r
+ return self.buffer.getvalue()\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+ out_file = 'test/midifiles/midiout.mid'\r
+ out_file = ''\r
+ rawOut = RawOutstreamFile(out_file)\r
+ rawOut.writeSlice('MThd')\r
+ rawOut.writeBew(6, 4)\r
+ rawOut.writeBew(1, 2)\r
+ rawOut.writeBew(2, 2)\r
+ rawOut.writeBew(15360, 2)\r
+ rawOut.write()\r
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+#import MidiOutStream\r
+#import MidiInStream\r
+#import MidiInFile\r
+#import MidiToText
\ No newline at end of file
--- /dev/null
+------------------------------------------------------------------------\r
+r409 | maxm | 2006-01-05 16:37:29 +0100 (to, 05 jan 2006) | 1 line\r
+\r
+Made RawOutstreamFile.py write to std out if no outfile is given.\r
+------------------------------------------------------------------------\r
+r403 | maxm | 2006-01-05 13:34:11 +0100 (to, 05 jan 2006) | 1 line\r
+\r
+\r
+------------------------------------------------------------------------\r
+r402 | maxm | 2006-01-05 13:33:56 +0100 (to, 05 jan 2006) | 1 line\r
+\r
+- Fixed minor bugs, added coding headers\r
+------------------------------------------------------------------------\r
+r401 | maxm | 2006-01-01 23:09:17 +0100 (s_, 01 jan 2006) | 1 line\r
+\r
+Fixed sysex dispathcer bug.\r
+------------------------------------------------------------------------\r
+r268 | maxm | 2005-02-04 12:26:59 +0100 (fr, 04 feb 2005) | 1 line\r
+\r
+\r
+------------------------------------------------------------------------\r
+r128 | maxm | 2004-12-18 14:05:27 +0100 (l_, 18 dec 2004) | 1 line\r
+\r
+Fixed bug when using relative time\r
+------------------------------------------------------------------------\r
+r15 | maxm | 2004-03-09 15:01:41 +0100 (ti, 09 mar 2004) | 1 line\r
+\r
+made a copy to meta folder\r
+------------------------------------------------------------------------\r
+r13 | maxm | 2004-03-09 09:17:23 +0100 (ti, 09 mar 2004) | 1 line\r
+\r
+Deleted .pyc files\r
+------------------------------------------------------------------------\r
+r12 | maxm | 2004-03-09 09:15:54 +0100 (ti, 09 mar 2004) | 1 line\r
+\r
+Removed file/folder\r
+------------------------------------------------------------------------\r
+r3 | maxm | 2004-03-08 23:16:25 +0100 (ma, 08 mar 2004) | 1 line\r
+\r
+Adde midi\r
+------------------------------------------------------------------------\r
+r1 | maxm | 2004-03-08 22:49:23 +0100 (ma, 08 mar 2004) | 1 line\r
+\r
+Initial Import\r
+------------------------------------------------------------------------\r
--- /dev/null
+# -*- coding: ISO-8859-1 -*-\r
+\r
+###################################################\r
+## Definitions of the different midi events\r
+\r
+\r
+\r
+###################################################\r
+## Midi channel events (The most usual events)\r
+## also called "Channel Voice Messages"\r
+\r
+NOTE_OFF = 0x80\r
+# 1000cccc 0nnnnnnn 0vvvvvvv (channel, note, velocity)\r
+\r
+NOTE_ON = 0x90\r
+# 1001cccc 0nnnnnnn 0vvvvvvv (channel, note, velocity)\r
+\r
+AFTERTOUCH = 0xA0\r
+# 1010cccc 0nnnnnnn 0vvvvvvv (channel, note, velocity)\r
+\r
+CONTINUOUS_CONTROLLER = 0xB0 # see Channel Mode Messages!!!\r
+# 1011cccc 0ccccccc 0vvvvvvv (channel, controller, value)\r
+\r
+PATCH_CHANGE = 0xC0\r
+# 1100cccc 0ppppppp (channel, program)\r
+\r
+CHANNEL_PRESSURE = 0xD0\r
+# 1101cccc 0ppppppp (channel, pressure)\r
+\r
+PITCH_BEND = 0xE0\r
+# 1110cccc 0vvvvvvv 0wwwwwww (channel, value-lo, value-hi)\r
+\r
+\r
+###################################################\r
+## Channel Mode Messages (Continuous Controller)\r
+## They share a status byte.\r
+## The controller makes the difference here\r
+\r
+# High resolution continuous controllers (MSB)\r
+\r
+BANK_SELECT = 0x00\r
+MODULATION_WHEEL = 0x01\r
+BREATH_CONTROLLER = 0x02\r
+FOOT_CONTROLLER = 0x04\r
+PORTAMENTO_TIME = 0x05\r
+DATA_ENTRY = 0x06\r
+CHANNEL_VOLUME = 0x07\r
+BALANCE = 0x08\r
+PAN = 0x0A\r
+EXPRESSION_CONTROLLER = 0x0B\r
+EFFECT_CONTROL_1 = 0x0C\r
+EFFECT_CONTROL_2 = 0x0D\r
+GEN_PURPOSE_CONTROLLER_1 = 0x10\r
+GEN_PURPOSE_CONTROLLER_2 = 0x11\r
+GEN_PURPOSE_CONTROLLER_3 = 0x12\r
+GEN_PURPOSE_CONTROLLER_4 = 0x13\r
+\r
+# High resolution continuous controllers (LSB)\r
+\r
+BANK_SELECT = 0x20\r
+MODULATION_WHEEL = 0x21\r
+BREATH_CONTROLLER = 0x22\r
+FOOT_CONTROLLER = 0x24\r
+PORTAMENTO_TIME = 0x25\r
+DATA_ENTRY = 0x26\r
+CHANNEL_VOLUME = 0x27\r
+BALANCE = 0x28\r
+PAN = 0x2A\r
+EXPRESSION_CONTROLLER = 0x2B\r
+EFFECT_CONTROL_1 = 0x2C\r
+EFFECT_CONTROL_2 = 0x2D\r
+GENERAL_PURPOSE_CONTROLLER_1 = 0x30\r
+GENERAL_PURPOSE_CONTROLLER_2 = 0x31\r
+GENERAL_PURPOSE_CONTROLLER_3 = 0x32\r
+GENERAL_PURPOSE_CONTROLLER_4 = 0x33\r
+\r
+# Switches\r
+\r
+SUSTAIN_ONOFF = 0x40\r
+PORTAMENTO_ONOFF = 0x41\r
+SOSTENUTO_ONOFF = 0x42\r
+SOFT_PEDAL_ONOFF = 0x43\r
+LEGATO_ONOFF = 0x44\r
+HOLD_2_ONOFF = 0x45\r
+\r
+# Low resolution continuous controllers\r
+\r
+SOUND_CONTROLLER_1 = 0x46 # (TG: Sound Variation; FX: Exciter On/Off)\r
+SOUND_CONTROLLER_2 = 0x47 # (TG: Harmonic Content; FX: Compressor On/Off)\r
+SOUND_CONTROLLER_3 = 0x48 # (TG: Release Time; FX: Distortion On/Off)\r
+SOUND_CONTROLLER_4 = 0x49 # (TG: Attack Time; FX: EQ On/Off)\r
+SOUND_CONTROLLER_5 = 0x4A # (TG: Brightness; FX: Expander On/Off)75 SOUND_CONTROLLER_6 (TG: Undefined; FX: Reverb OnOff)\r
+SOUND_CONTROLLER_7 = 0x4C # (TG: Undefined; FX: Delay OnOff)\r
+SOUND_CONTROLLER_8 = 0x4D # (TG: Undefined; FX: Pitch Transpose OnOff)\r
+SOUND_CONTROLLER_9 = 0x4E # (TG: Undefined; FX: Flange/Chorus OnOff)\r
+SOUND_CONTROLLER_10 = 0x4F # (TG: Undefined; FX: Special Effects OnOff)\r
+GENERAL_PURPOSE_CONTROLLER_5 = 0x50\r
+GENERAL_PURPOSE_CONTROLLER_6 = 0x51\r
+GENERAL_PURPOSE_CONTROLLER_7 = 0x52\r
+GENERAL_PURPOSE_CONTROLLER_8 = 0x53\r
+PORTAMENTO_CONTROL = 0x54 # (PTC) (0vvvvvvv is the source Note number) (Detail)\r
+EFFECTS_1 = 0x5B # (Ext. Effects Depth)\r
+EFFECTS_2 = 0x5C # (Tremelo Depth)\r
+EFFECTS_3 = 0x5D # (Chorus Depth)\r
+EFFECTS_4 = 0x5E # (Celeste Depth)\r
+EFFECTS_5 = 0x5F # (Phaser Depth)\r
+DATA_INCREMENT = 0x60 # (0vvvvvvv is n/a; use 0)\r
+DATA_DECREMENT = 0x61 # (0vvvvvvv is n/a; use 0)\r
+NON_REGISTERED_PARAMETER_NUMBER = 0x62 # (LSB)\r
+NON_REGISTERED_PARAMETER_NUMBER = 0x63 # (MSB)\r
+REGISTERED_PARAMETER_NUMBER = 0x64 # (LSB)\r
+REGISTERED_PARAMETER_NUMBER = 0x65 # (MSB)\r
+\r
+# Channel Mode messages - (Detail)\r
+\r
+ALL_SOUND_OFF = 0x78\r
+RESET_ALL_CONTROLLERS = 0x79\r
+LOCAL_CONTROL_ONOFF = 0x7A\r
+ALL_NOTES_OFF = 0x7B\r
+OMNI_MODE_OFF = 0x7C # (also causes ANO)\r
+OMNI_MODE_ON = 0x7D # (also causes ANO)\r
+MONO_MODE_ON = 0x7E # (Poly Off; also causes ANO)\r
+POLY_MODE_ON = 0x7F # (Mono Off; also causes ANO)\r
+\r
+\r
+\r
+###################################################\r
+## System Common Messages, for all channels\r
+\r
+SYSTEM_EXCLUSIVE = 0xF0\r
+# 11110000 0iiiiiii 0ddddddd ... 11110111\r
+\r
+MTC = 0xF1 # MIDI Time Code Quarter Frame\r
+# 11110001\r
+\r
+SONG_POSITION_POINTER = 0xF2\r
+# 11110010 0vvvvvvv 0wwwwwww (lo-position, hi-position)\r
+\r
+SONG_SELECT = 0xF3\r
+# 11110011 0sssssss (songnumber)\r
+\r
+#UNDEFINED = 0xF4\r
+## 11110100\r
+\r
+#UNDEFINED = 0xF5\r
+## 11110101\r
+\r
+TUNING_REQUEST = 0xF6\r
+# 11110110\r
+\r
+END_OFF_EXCLUSIVE = 0xF7 # terminator\r
+# 11110111 # End of system exclusive\r
+\r
+\r
+###################################################\r
+## Midifile meta-events\r
+\r
+SEQUENCE_NUMBER = 0x00 # 00 02 ss ss (seq-number)\r
+TEXT = 0x01 # 01 len text...\r
+COPYRIGHT = 0x02 # 02 len text...\r
+SEQUENCE_NAME = 0x03 # 03 len text...\r
+INSTRUMENT_NAME = 0x04 # 04 len text...\r
+LYRIC = 0x05 # 05 len text...\r
+MARKER = 0x06 # 06 len text...\r
+CUEPOINT = 0x07 # 07 len text...\r
+PROGRAM_NAME = 0x08 # 08 len text...\r
+DEVICE_NAME = 0x09 # 09 len text...\r
+\r
+MIDI_CH_PREFIX = 0x20 # MIDI channel prefix assignment (unofficial)\r
+\r
+MIDI_PORT = 0x21 # 21 01 port, legacy stuff but still used\r
+END_OF_TRACK = 0x2F # 2f 00\r
+TEMPO = 0x51 # 51 03 tt tt tt (tempo in us/quarternote)\r
+SMTP_OFFSET = 0x54 # 54 05 hh mm ss ff xx\r
+TIME_SIGNATURE = 0x58 # 58 04 nn dd cc bb\r
+KEY_SIGNATURE = 0x59 # ??? len text...\r
+SPECIFIC = 0x7F # Sequencer specific event\r
+\r
+FILE_HEADER = 'MThd'\r
+TRACK_HEADER = 'MTrk'\r
+\r
+###################################################\r
+## System Realtime messages\r
+## I don't supose these are to be found in midi files?!\r
+\r
+TIMING_CLOCK = 0xF8\r
+# undefined = 0xF9\r
+SONG_START = 0xFA\r
+SONG_CONTINUE = 0xFB\r
+SONG_STOP = 0xFC\r
+# undefined = 0xFD\r
+ACTIVE_SENSING = 0xFE\r
+SYSTEM_RESET = 0xFF\r
+\r
+\r
+###################################################\r
+## META EVENT, it is used only in midi files.\r
+## In transmitted data it means system reset!!!\r
+\r
+META_EVENT = 0xFF\r
+# 11111111\r
+\r
+\r
+###################################################\r
+## Helper functions\r
+\r
+def is_status(byte):\r
+ return (byte & 0x80) == 0x80 # 1000 0000\r
+\r
+\r
--- /dev/null
+from MidiOutFile import MidiOutFile\r
+\r
+"""\r
+This is an example of the smallest possible type 0 midi file, where \r
+all the midi events are in the same track.\r
+"""\r
+\r
+out_file = 'midiout/minimal_type0.mid'\r
+midi = MidiOutFile(out_file)\r
+\r
+# non optional midi framework\r
+midi.header()\r
+midi.start_of_track() \r
+\r
+\r
+# musical events\r
+\r
+midi.update_time(0)\r
+midi.note_on(channel=0, note=0x40)\r
+\r
+midi.update_time(192)\r
+midi.note_off(channel=0, note=0x40)\r
+\r
+\r
+# non optional midi framework\r
+midi.update_time(0)\r
+midi.end_of_track()\r
+\r
+midi.eof()\r
--- /dev/null
+from MidiOutStream import MidiOutStream\r
+from MidiInFile import MidiInFile\r
+\r
+"""\r
+This prints all note on events on midi channel 0\r
+"""\r
+\r
+\r
+class Transposer(MidiOutStream):\r
+ \r
+ "Transposes all notes by 1 octave"\r
+ \r
+ def note_on(self, channel=0, note=0x40, velocity=0x40):\r
+ if channel == 0:\r
+ print channel, note, velocity, self.rel_time()\r
+\r
+\r
+event_handler = Transposer()\r
+\r
+in_file = 'midiout/minimal_type0.mid'\r
+midi_in = MidiInFile(event_handler, in_file)\r
+midi_in.read()\r
+\r
--- /dev/null
+from MidiToText import MidiToText\r
+\r
+"""\r
+This is an example that uses the MidiToText eventhandler. When an \r
+event is triggered on it, it prints the event to the console.\r
+"""\r
+\r
+midi = MidiToText()\r
+\r
+# non optional midi framework\r
+midi.header()\r
+midi.start_of_track() \r
+\r
+\r
+# musical events\r
+\r
+midi.update_time(0)\r
+midi.note_on(channel=0, note=0x40)\r
+\r
+midi.update_time(192)\r
+midi.note_off(channel=0, note=0x40)\r
+\r
+\r
+# non optional midi framework\r
+midi.update_time(0)\r
+midi.end_of_track() # not optional!\r
+\r
+midi.eof()\r
--- /dev/null
+"""\r
+This is an example that uses the MidiToText eventhandler. When an \r
+event is triggered on it, it prints the event to the console.\r
+\r
+It gets the events from the MidiInFile.\r
+\r
+So it prints all the events from the infile to the console. great for \r
+debugging :-s\r
+"""\r
+\r
+\r
+# get data\r
+test_file = 'test/midifiles/minimal-cubase-type0.mid'\r
+\r
+# do parsing\r
+from MidiInFile import MidiInFile\r
+from MidiToText import MidiToText # the event handler\r
+midiIn = MidiInFile(MidiToText(), test_file)\r
+midiIn.read()\r
--- /dev/null
+from MidiOutFile import MidiOutFile\r
+from MidiInFile import MidiInFile\r
+\r
+"""\r
+This is an example of the smallest possible type 0 midi file, where \r
+all the midi events are in the same track.\r
+"""\r
+\r
+\r
+class Transposer(MidiOutFile):\r
+ \r
+ "Transposes all notes by 1 octave"\r
+ \r
+ def _transp(self, ch, note):\r
+ if ch != 9: # not the drums!\r
+ note += 12\r
+ if note > 127:\r
+ note = 127\r
+ return note\r
+\r
+\r
+ def note_on(self, channel=0, note=0x40, velocity=0x40):\r
+ note = self._transp(channel, note)\r
+ MidiOutFile.note_on(self, channel, note, velocity)\r
+ \r
+ \r
+ def note_off(self, channel=0, note=0x40, velocity=0x40):\r
+ note = self._transp(channel, note)\r
+ MidiOutFile.note_off(self, channel, note, velocity)\r
+\r
+\r
+out_file = 'midiout/transposed.mid'\r
+midi_out = Transposer(out_file)\r
+\r
+#in_file = 'midiout/minimal_type0.mid'\r
+#in_file = 'test/midifiles/Lola.mid'\r
+in_file = 'test/midifiles/tennessee_waltz.mid'\r
+midi_in = MidiInFile(midi_out, in_file)\r
+midi_in.read()\r
+\r
--- /dev/null
+class EventDispatcherBase:\r
+\r
+\r
+ def __init__(self, outstream):\r
+ """\r
+ The event dispatcher generates events on the outstream. This \r
+ is the base implementation. It is more like an interface for \r
+ how the EventDispatcher. It has the methods that are used by \r
+ the Midi Parser.\r
+ """\r
+ # internal values, don't mess with 'em directly\r
+ self.outstream = outstream\r
+\r
+\r
+ def eof(self):\r
+ "End of file!"\r
+ self.outstream.eof()\r
+\r
+\r
+ def update_time(self, new_time=0, relative=1):\r
+ "Updates relative/absolute time."\r
+ self.outstream.update_time(new_time, relative)\r
+\r
+ # 'official' midi events\r
+\r
+ def header(self, format, nTracks, division):\r
+ "Triggers the header event"\r
+ self.outstream.header(format, nTracks, division)\r
+\r
+\r
+ def start_of_track(self, current_track):\r
+ "Triggers the start of track event"\r
+ \r
+ # I do this twice so that users can overwrite the \r
+ # start_of_track event handler without worrying whether the \r
+ # track number is updated correctly.\r
+ self.outstream.set_current_track(current_track)\r
+ self.outstream.start_of_track(current_track)\r
+\r
+ # Event dispatchers for midi events\r
+\r
+ def channel_messages(self, hi_nible, channel, data):\r
+ "Dispatches channel messages"\r
+ self.outstream.channel_message(hi_nible, channel, data)\r
+\r
+\r
+ def continuous_controllers(self, channel, controller, value):\r
+ "Dispatches channel messages"\r
+ self.outstream.continuous_controller(channel, controller, value)\r
+ \r
+ \r
+ def system_commons(self, common_type, common_data):\r
+ "Dispatches system common messages"\r
+ self.outstream.system_common(common_type, common_data)\r
+\r
+\r
+ def meta_event(self, meta_type, data):\r
+ "Dispatches meta events"\r
+ self.outstream.meta_event(meta_type, data)\r
+\r
+\r
+ def sysex_events(self, data):\r
+ "Dispatcher for sysex events"\r
+ self.outstream.sysex_event(data)\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+\r
+ from MidiToText import MidiToText\r
+ from constants import NOTE_ON\r
+ \r
+ outstream = MidiToText()\r
+ dispatcher = EventDispatcherBase(outstream)\r
+ dispatcher.channel_messages(NOTE_ON, 0x00, '\x40\x40')
\ No newline at end of file
--- /dev/null
+from MidiOutStream import MidiOutStream\r
+\r
+class MidiOutPassThrough(MidiOutStream):\r
+\r
+\r
+ """\r
+\r
+ This class i mainly used for testing the event dispatcher. The \r
+ methods just returns the passed parameters as a tupple.\r
+\r
+ """\r
+\r
+\r
+ #####################\r
+ ## Midi channel events\r
+\r
+\r
+ def note_on(self, channel, note, velocity, time=None):\r
+ return channel, note, velocity, time\r
+\r
+\r
+ def note_off(self, channel, note, velocity, time=None):\r
+ return channel, note, velocity, time\r
+\r
+\r
+ def aftertouch(self, channel, note, velocity, time=None):\r
+ return channel, note, velocity, time\r
+\r
+ \r
+ def continuous_controller(self, channel, controller, value, time=None):\r
+ return channel, controller, value, time\r
+\r
+\r
+ def patch_change(self, channel, patch, time=None):\r
+ return channel, patch, time\r
+\r
+\r
+ def channel_pressure(self, channel, pressure, time=None):\r
+ return channel, pressure, time\r
+\r
+\r
+ #####################\r
+ ## defined continuous controller events\r
+ \r
+# def cc_\r
+\r
+ #####################\r
+ ## Common events\r
+\r
+ def system_exclusive(self, data, time=None):\r
+ return data, time\r
+\r
+\r
+ def song_position_pointer(self, hiPos, loPos, time=None):\r
+ return hiPos, loPos, time\r
+\r
+\r
+ def song_select(self, songNumber, time=None):\r
+ return songNumber, time\r
+\r
+\r
+ def tuning_request(self, time=None):\r
+ return time\r
+\r
+\r
+\r
+ #########################\r
+ # header does not really belong here. But anyhoo!!!\r
+ \r
+ def header(self, format, nTracks, division):\r
+ return format, nTracks, division\r
+\r
+\r
+ def eof(self):\r
+ return 'eof'\r
+\r
+\r
+ #####################\r
+ ## meta events\r
+\r
+ def start_of_track(self, n_track=0):\r
+ return n_track\r
+\r
+\r
+ def end_of_track(self, n_track=0, time=None):\r
+ return n_track, time\r
+\r
+\r
+ def sequence_number(self, hiVal, loVal, time=None):\r
+ return hiVal, loVal, time\r
+\r
+\r
+ def text(self, text, time=None):\r
+ return text, time\r
+\r
+\r
+ def copyright(self, text, time=None):\r
+ return text, time\r
+\r
+\r
+ def sequence_name(self, text, time=None):\r
+ return text, time\r
+\r
+\r
+ def instrument_name(self, text, time=None):\r
+ return text, time\r
+\r
+\r
+ def lyric(self, text, time=None):\r
+ return text, time\r
+\r
+\r
+ def marker(self, text, time=None):\r
+ return text, time\r
+\r
+\r
+ def cuepoint(self, text, time=None):\r
+ return text, time\r
+\r
+\r
+ def midi_port(self, value, time=None):\r
+ return value, time\r
+\r
+\r
+ def tempo(self, value, time=None):\r
+ return value, time\r
+\r
+ def smtp_offset(self, hour, minute, second, frame, framePart, time=None):\r
+ return hour, minute, second, frame, framePart, time\r
+\r
+\r
+ def time_signature(self, nn, dd, cc, bb, time=None):\r
+ return nn, dd, cc, bb, time\r
+\r
+\r
+ def key_signature(self, sf, mi, time=None):\r
+ return sf, mi, time\r
+\r
+\r
+ def sequencer_specific(self, data, time=None):\r
+ return data, time\r
+\r
+\r
+\r
+\r
+ #####################\r
+ ## realtime events\r
+\r
+ def timing_clock(self, time=None):\r
+ return time\r
+\r
+\r
+ def song_start(self, time=None):\r
+ return time\r
+\r
+\r
+ def song_stop(self, time=None):\r
+ return time\r
+\r
+\r
+ def song_continue(self, time=None):\r
+ return time\r
+\r
+\r
+ def active_sensing(self, time=None):\r
+ return time\r
+\r
+\r
+ def system_reset(self, time=None):\r
+ return time\r
+\r
+\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+ midiOut = MidiOutStream()\r
+ midiOut.note_on(0, 63, 127, 0)\r
+ midiOut.note_off(0, 63, 127, 384)\r
+\r
+
\ No newline at end of file
--- /dev/null
+class MidiOutStreamBase:\r
+\r
+\r
+ """\r
+\r
+ MidiOutStreamBase is Basically an eventhandler. It is the most central\r
+ class in the Midi library. You use it both for writing events to\r
+ an output stream, and as an event handler for an input stream.\r
+\r
+ This makes it extremely easy to take input from one stream and\r
+ send it to another. Ie. if you want to read a Midi file, do some\r
+ processing, and send it to a midiport.\r
+\r
+ All time values are in absolute values from the opening of a\r
+ stream. To calculate time values, please use the MidiTime and\r
+ MidiDeltaTime classes.\r
+\r
+ """\r
+\r
+ def __init__(self):\r
+ \r
+ # the time is rather global, so it needs to be stored \r
+ # here. Otherwise there would be no really simple way to \r
+ # calculate it. The alternative would be to have each event \r
+ # handler do it. That sucks even worse!\r
+ self._absolute_time = 0\r
+ self._relative_time = 0\r
+ self._current_track = 0\r
+\r
+ # time handling event handlers. They should overwritten with care\r
+\r
+ def update_time(self, new_time=0, relative=1):\r
+ """\r
+ Updates the time, if relative is true, new_time is relative, \r
+ else it's absolute.\r
+ """\r
+ if relative:\r
+ self._relative_time = new_time\r
+ self._absolute_time += new_time\r
+ else:\r
+ self._absolute_time = new_time\r
+ self._relative_time = new_time - self._absolute_time\r
+\r
+ def rel_time(self):\r
+ "Returns the relative time"\r
+ return self._relative_time\r
+\r
+ def abs_time(self):\r
+ "Returns the absolute time"\r
+ return self._absolute_time\r
+\r
+ # track handling event handlers\r
+ \r
+ def set_current_track(self, new_track):\r
+ "Sets the current track number"\r
+ self._current_track = new_track\r
+ \r
+ def get_current_track(self):\r
+ "Returns the current track number"\r
+ return self._current_track\r
+ \r
+ \r
+ #####################\r
+ ## Midi events\r
+\r
+\r
+ def channel_message(self, message_type, channel, data):\r
+ """The default event handler for channel messages"""\r
+ pass\r
+\r
+\r
+ #####################\r
+ ## Common events\r
+\r
+ def system_exclusive(self, data):\r
+\r
+ """The default event handler for system_exclusive messages"""\r
+ pass\r
+\r
+\r
+ def system_common(self, common_type, common_data):\r
+\r
+ """The default event handler for system common messages"""\r
+ pass\r
+\r
+\r
+ #########################\r
+ # header does not really belong here. But anyhoo!!!\r
+ \r
+ def header(self, format, nTracks, division):\r
+\r
+ """\r
+ format: type of midi file in [1,2]\r
+ nTracks: number of tracks\r
+ division: timing division\r
+ """\r
+ pass\r
+\r
+\r
+ def start_of_track(self, n_track=0):\r
+\r
+ """\r
+ n_track: number of track\r
+ """\r
+ pass\r
+\r
+\r
+ def eof(self):\r
+\r
+ """\r
+ End of file. No more events to be processed.\r
+ """\r
+ pass\r
+\r
+\r
+ #####################\r
+ ## meta events\r
+\r
+\r
+ def meta_event(self, meta_type, data, time):\r
+ \r
+ """The default event handler for meta_events"""\r
+ pass\r
+\r
+\r
+\r
+\r
+if __name__ == '__main__':\r
+\r
+ midiOut = MidiOutStreamBase()\r
+ midiOut.update_time(0,0)\r
+ midiOut.note_on(0, 63, 127)\r
+ midiOut.note_off(0, 63, 127)\r
+\r
+
\ No newline at end of file
--- /dev/null
+Stuff that I am just playing around with
\ No newline at end of file
--- /dev/null
+This is the documentation for the midi package\r
+==============================================\r
+\r
+\r
+The modules follows the following naming convention:\r
+\r
+\r
+MidiIn<StreamType>.py\r
+---------------------\r
+\r
+The MidiIn modules reads midi content for a specific type of stream. Ie. a file or a midi port. It then generates events and triggers them on a MidiOutStream.\r
+\r
+\r
+MidiOut<StreamType>.py\r
+----------------------\r
+\r
+The MidiOut modules are event handlers, that reacts to events generated by a a Midi in module.\r
+\r
+\r
+MidiInBase.py\r
+---------------\r
+\r
+The base class for input streams.\r
+\r
+\r
+MidiOutBase.py\r
+----------------\r
+\r
+The base class for the output streams.\r
+\r
+\r
+\r
+\r
+\r
+\r
+Internal modules\r
+================\r
+\r
+\r
+DataTypeConverters.py\r
+---------------------\r
+\r
+A collection of functions that converts the special data types used in midi files to and from strings.\r
+\r
+\r
+constants.py\r
+------------\r
+\r
+A collection of constants from the midi spec.\r
+\r
--- /dev/null
+MThd | Format=1 | # of Tracks=2 | Division=15360\r
+\r
+\r
+\r
+\r
+Track #0 ******************************************\r
+ Time Event\r
+ \r
+ 1: 1: 0 |Time Sig | 4/4 | MIDI-clocks\click=24 | 32nds\quarter=8\r
+ |Tempo | BPM=120 | micros\quarter=500000\r
+ 101: 1: 0 |End of track| \r
+\r
+\r
+\r
+\r
+\r
+Track #1 ******************************************\r
+ Time Event\r
+ \r
+ 1: 1: 0 |Track Name | len=7 |\r
+ 0x53 0x79 0x6E 0x74 0x68 0x20 0x31 <Synth 1>\r
+ |Instrument | len=7 |\r
+ 0x53 0x79 0x6E 0x74 0x68 0x20 0x31 <Synth 1>\r
+ |On Note | chan= 1 | pitch=C 1 | vol=127\r
+ 2: 1: 0 |Off Note | chan= 1 | pitch=c 1 | vol=0\r
+ 101: 1: 0 |End of track| \r
--- /dev/null
+4D 54 68 64 MThd\r
+00 00 00 06 len: 6\r
+00 01 Type: 1\r
+00 02 tracks: 2\r
+3C 00 tempo: 15360\r
+\r
+\r
+\r
+4D 54 72 6B MTrk\r
+00 00 00 16 len: 22\r
+\r
+00 time\r
+ FF 58 04 Time Signature\r
+ 04 02 18 08 4/4 24 8\r
+ \r
+00 time\r
+ FF 51 03 TEMPO:\r
+ 07 A1 20 500.000 mySec\r
+\r
+82 F7 80 00 time ## oh bugger, it's buggy.\r
+ FF 2F 00 End Of Track\r
+ \r
+\r
+\r
+4D 54 72 6B MTrk\r
+00 00 00 2C len: 44\r
+\r
+00 time\r
+ FF 03 Sequence/Track Name\r
+ 07 len: 7\r
+ 53 79 6E 74\r
+ 68 20 31 'Synth 1'\r
+\r
+00 time\r
+ FF 04 Instrument\r
+ 07 len: 7\r
+ 53 79 6E 74\r
+ 68 20 31 'Synth 1'\r
+\r
+00 time\r
+ FF 21 01 Midi port\r
+ 04 Port #5\r
+\r
+00 time\r
+ 90 24 7F Note ON\r
+ 83 E0 00 Note OFF\r
+ \r
+00 time\r
+ 80 24 00 Note OFF\r
+\r
+00 82 F3 A0 \r
+\r
+00\r
+ FF 2F 00 End Of Track
\ No newline at end of file
--- /dev/null
+These files are used for testing the midi package\r
+=================================================\r
+\r
+minimal.mid\r
+-----------\r
+\r
+A minimal working midifile. Plays a one bar "middle C" at 120 bpm. The absolute simplest file I could get to play in midi devices.
\ No newline at end of file
--- /dev/null
+Embarrasingly empty.\r
+\r
+Why don't you write some tests?
\ No newline at end of file
--- /dev/null
+0.1.4
\ No newline at end of file
--- /dev/null
+"""Phil's pyGame Utilities
+
+
+"""
+__version__ = '0.12.3'
+
+# vim: set filetype=python sts=4 sw=4 noet si :
--- /dev/null
+"""Some handy algorithms for use in games, etc.
+
+<p>please note that this file is alpha, and is subject to modification in
+future versions of pgu!</p>
+"""
+
+# The manhattan distance metric
+def manhattan_dist(a,b):
+ return abs(a[0]-b[0]) + abs(a[1]-b[1])
+
+class node:
+ def __init__(self, prev, pos, dest, dist):
+ self.prev,self.pos,self.dest = prev,pos,dest
+ if self.prev == None: self.g = 0
+ else: self.g = self.prev.g + 1
+ self.h = dist(pos,dest)
+ self.f = self.g+self.h
+
+
+def astar(start,end,layer,dist=manhattan_dist):
+ """uses the a* algorithm to find a path
+
+ <pre>astar(start,end,layer,dist): return [list of positions]</pre>
+
+ <dl>
+ <dt>start<dd>start position
+ <dt>end<dd>end position
+ <dt>layer<dd>a grid where zero cells are open and non-zero cells are walls
+ <dt>dist<dd>a distance function dist(a,b) - manhattan distance is used by default
+ </dl>
+
+ <p>returns a list of positions from start to end</p>
+ """
+
+ w,h = len(layer[0]),len(layer)
+ if start[0] < 0 or start[1] < 0 or start[0] >= w or start[1] >= h:
+ return [] #start outside of layer
+ if end[0] < 0 or end[1] < 0 or end[0] >= w or end[1] >= h:
+ return [] #end outside of layer
+
+ if layer[start[1]][start[0]]:
+ return [] #start is blocked
+ if layer[end[1]][end[0]]:
+ return [] #end is blocked
+
+ opens = []
+ open = {}
+ closed = {}
+ cur = node(None, start, end, dist)
+ open[cur.pos] = cur
+ opens.append(cur)
+ while len(open):
+ cur = opens.pop(0)
+ if cur.pos not in open: continue
+ del open[cur.pos]
+ closed[cur.pos] = cur
+ if cur.pos == end: break
+ for dx,dy in [(0,-1),(1,0),(0,1),(-1,0)]:#(-1,-1),(1,-1),(-1,1),(1,1)]:
+ pos = cur.pos[0]+dx,cur.pos[1]+dy
+ # Check if the point lies in the grid
+ if (pos[0] < 0 or pos[1] < 0 or
+ pos[0] >= w or pos[1] >= h or
+ layer[pos[0]][pos[1]]):
+ continue
+ #check for blocks of diagonals
+ if layer[cur.pos[1]+dy][cur.pos[0]]: continue
+ if layer[cur.pos[1]][cur.pos[0]+dx]: continue
+ new = node(cur, pos, end, dist)
+ if pos in open and new.f >= open[pos].f: continue
+ if pos in closed and new.f >= closed[pos].f: continue
+ if pos in open: del open[pos]
+ if pos in closed: del closed[pos]
+ open[pos] = new
+ lo = 0
+ hi = len(opens)
+ while lo < hi:
+ mid = (lo+hi)/2
+ if new.f < opens[mid].f: hi = mid
+ else: lo = mid + 1
+ opens.insert(lo,new)
+
+ if cur.pos != end:
+ return []
+
+ path = []
+ while cur.prev != None:
+ path.append(cur.pos)
+ cur = cur.prev
+ path.reverse()
+ return path
+
+
+def getline(a,b):
+ """returns a path of points from a to b
+
+ <pre>getline(a,b): return [list of points]</pre>
+
+ <dl>
+ <dt>a<dd>starting point
+ <dt>b<dd>ending point
+ </dl>
+
+ <p>returns a list of points from a to b</p>
+ """
+
+ path = []
+
+ x1,y1 = a
+ x2,y2 = b
+ dx,dy = abs(x2-x1),abs(y2-y1)
+
+ if x2 >= x1: xi1,xi2 = 1,1
+ else: xi1,xi2 = -1,-1
+
+ if y2 >= y1: yi1,yi2 = 1,1
+ else: yi1,yi2 = -1,-1
+
+ if dx >= dy:
+ xi1,yi2 = 0,0
+ d = dx
+ n = dx/2
+ a = dy
+ p = dx
+ else:
+ xi2,yi1 = 0,0
+ d = dy
+ n = dy/2
+ a = dx
+ p = dy
+
+ x,y = x1,y1
+ c = 0
+ while c <= p:
+ path.append((x,y))
+ n += a
+ if n > d:
+ n -= d
+ x += xi1
+ y += yi1
+ x += xi2
+ y += yi2
+ c += 1
+ return path
--- /dev/null
+"""animation loading and manipulating functions.
+
+<p>please note that this file is alpha, and is subject to modification in
+future versions of pgu!</p>
+"""
+
+print 'pgu.ani','This module is alpha, and is subject to change.'
+
+import math
+import pygame
+
+def _ani_load(tv,name,parts,frames,shape):
+ l = len(frames)
+ #print name,parts,l
+ n = parts.pop()
+ if len(parts):
+ s = l/n
+ for i in xrange(0,n):
+ _ani_load(tv,name + ".%d"%i,parts[:],frames[s*i:s*(i+1)],shape)
+ return
+
+ for i in xrange(0,n):
+ tv.images[name+".%d"%i] = frames[i],shape
+
+def ani_load(tv,name,img,size,shape,parts):
+ """load an animation from an image
+
+ <pre>ani_load(tv,name,image,size,shape,parts)</pre>
+
+ <dl>
+ <dt>tv<dd>vid to load into
+ <dt>name <dd>prefix name to give the images
+ <dt>image <dd>image to load anis from
+ <dt>size <dd>w,h size of image
+ <dt>shape <dd>shape of image (usually a subset of 0,0,w,h) used for collision detection
+ <dt>parts <dd>list of parts to divide the animation into
+ <br>for example parts = [4,5] would yield 4 animations 5 frames long, 20 total
+ <br>for example parts = [a,b,c] would yield ... images['name.a.b.c'] ..., a*b*c total
+ </dl>
+
+ """
+ parts = parts[:]
+ parts.reverse()
+ w,h = size
+ frames = []
+ for y in xrange(0,img.get_height(),h):
+ for x in xrange(0,img.get_width(),w):
+ frames.append(img.subsurface(x,y,w,h))
+ _ani_load(tv,name,parts,frames,shape)
+
+
+def image_rotate(tv,name,img,shape,angles,diff=0):
+ """rotate an image and put it into tv.images
+
+ <pre>image_rotate(tv,name,image,shape,angles,diff=0)</pre>
+
+ <dl>
+ <dt>tv <dd>vid to load into
+ <dt>name <dd>prefix name to give the images
+ <dt>image <dd>image to load anis from
+ <dt>shape <dd>shape fimage (usually a subset of 0,0,w,h) used for collision detection
+ <dt>angles <dd>a list of angles to render in degrees
+ <dt>diff <dd>a number to add to the angles, to correct for source image not actually being at 0 degrees
+ </dl>
+ """
+ w1,h1 = img.get_width(),img.get_height()
+ shape = pygame.Rect(shape)
+ ps = shape.topleft,shape.topright,shape.bottomleft,shape.bottomright
+ for a in angles:
+ img2 = pygame.transform.rotate(img,a+diff)
+ w2,h2 = img2.get_width(),img2.get_height()
+ minx,miny,maxx,maxy = 1024,1024,0,0
+ for x,y in ps:
+ x,y = x-w1/2,y-h1/2
+ a2 = math.radians(a+diff)
+ #NOTE: the + and - are switched from the normal formula because of
+ #the weird way that pygame does the angle...
+ x2 = x*math.cos(a2) + y*math.sin(a2)
+ y2 = y*math.cos(a2) - x*math.sin(a2)
+ x2,y2 = x2+w2/2,y2+h2/2
+ minx = min(minx,x2)
+ miny = min(miny,y2)
+ maxx = max(maxx,x2)
+ maxy = max(maxy,y2)
+ r = pygame.Rect(minx,miny,maxx-minx,maxy-miny)
+ #print r
+ #((ww-w)/2,(hh-h)/2,w,h)
+ tv.images["%s.%d"%(name,a)] = img2,r
+
+
--- /dev/null
+"""a state engine.
+"""
+import pygame
+from pygame.locals import *
+
+class State:
+ """Template Class -- for a state.
+
+ <pre>State(game,value...)</pre>
+
+ <dl>
+ <dt>game<dd>The state engine.
+ <dt>value<dd>I usually pass in a custom value to a state
+ </dl>
+
+ <p>For all of the template methods, they should return None unless they return
+ a new State to switch the engine to.</p>
+ """
+ def __init__(self,game,value=None):
+ self.game,self.value = game,value
+ def init(self):
+ """Template Method - Initialize the state, called once the first time a state is selected.
+
+ <pre>State.init()</pre>
+ """
+ return
+ def paint(self,screen):
+ """Template Method - Paint the screen. Called once after the state is selected.
+
+ <p>State is responsible for calling <tt>pygame.display.flip()</tt> or whatever.</p>
+
+ <pre>State.paint(screen)</pre>
+ """
+ return
+
+ def repaint(self):
+ """Template Method - Request a repaint of this state.
+
+ <pre>State.repaint()</pre>
+ """
+ self._paint = 1
+ def update(self,screen):
+ """Template Method - Update the screen.
+
+ <p>State is responsible for calling <tt>pygame.display.update(updates)</tt> or whatever.</p>
+
+ <pre>State.update(screen)</pre>
+ """
+ return
+ def loop(self):
+ """Template Method - Run a logic loop, called once per frame.
+
+ <pre>State.loop()</pre>
+ """
+ return
+ def event(self,e):
+ """Template Method - Recieve an event.
+
+ <pre>State.event(e)</pre>
+ """
+ return
+
+class Quit(State):
+ """A state to quit the state engine.
+
+ <pre>Quit(game,value)</pre>
+ """
+
+ def init(self):
+ self.game.quit = 1
+
+class Game:
+ """Template Class - The state engine.
+ """
+ def fnc(self,f,v=None):
+ s = self.state
+ if not hasattr(s,f): return 0
+ f = getattr(s,f)
+ if v != None: r = f(v)
+ else: r = f()
+ if r != None:
+ self.state = r
+ self.state._paint = 1
+ return 1
+ return 0
+
+ def run(self,state,screen=None):
+ """Run the state engine, this is a infinite loop (until a quit occurs).
+
+ <pre>Game.run(state,screen=None)</pre>
+
+ <dl>
+ <dt>game<dd>a state engine
+ <dt>screen<dd>the screen
+ </dl>
+ """
+ self.quit = 0
+ self.state = state
+ if screen != None: self.screen = screen
+
+ self.init()
+
+ while not self.quit:
+ self.loop()
+
+ def loop(self):
+ s = self.state
+ if not hasattr(s,'_init') or s._init:
+ s._init = 0
+ if self.fnc('init'): return
+ else:
+ if self.fnc('loop'): return
+ if not hasattr(s,'_paint') or s._paint:
+ s._paint = 0
+ if self.fnc('paint',self.screen): return
+ else:
+ if self.fnc('update',self.screen): return
+
+ for e in pygame.event.get():
+ #NOTE: this might break API?
+ #if self.event(e): return
+ if not self.event(e):
+ if self.fnc('event',e): return
+
+ self.tick()
+ return
+
+ def init(self):
+ """Template Method - called at the beginning of State.run() to initialize things.
+
+ <pre>Game.init()</pre>
+ """
+ return
+
+ def tick(self):
+ """Template Method - called once per frame, usually for timer purposes.
+
+ <pre>Game.tick()</pre>
+ """
+ pygame.time.wait(10)
+
+ def event(self,e):
+ """Template Method - called with each event, so the engine can capture special events.
+
+ <pre>Game.event(e): return captured</pre>
+
+ <p>return a True value if the event is captured and does not need to be passed onto the current
+ state</p>
+ """
+ if e.type is QUIT:
+ self.state = Quit(self)
+ return 1
+
+# vim: set filetype=python sts=4 sw=4 noet si :
--- /dev/null
+"""Some handy font-like objects.
+
+<p>please note that this file is alpha, and is subject to modification in
+future versions of pgu!</p>
+"""
+
+print 'pgu.fonts','This module is alpha, and is subject to change.'
+
+import pygame
+from pygame.locals import *
+
+class TileFont:
+ """Creates an instance of the TileFont class. Interface compatible with pygame.Font
+
+ <p>TileFonts are fonts that are stored in a tiled image. Where the image opaque, it assumed that the font is visible. Font color is changed automatically, so it does not work with
+ fonts with stylized coloring.</p>
+
+ <pre>TileFont(fname,size,hints,scale=None,sensitive=False)</pre>
+
+ <dl>
+ <dt>size <dd>the dimensions of the characters
+ <dt>hints <dd>a string of hints "abcdefg..."
+ <dt>scale <dd>size to scale font to
+ <dt>sensitive <dd>case sensitivity
+ </dl>
+ """
+
+ def __init__(self,fname,size,hints,scale=None,sensitive=False):
+
+ self.image = pygame.image.load(fname)
+
+ w,h = self.image.get_width(),self.image.get_height()
+ tw,th = size
+ if not scale: scale = size
+ self._size = size
+ self.scale = scale
+
+ self.chars = {}
+ x,y = 0,0
+ self.sensitive = sensitive
+ if not self.sensitive: hints = hints.lower()
+ for c in hints:
+ if c not in ('\r','\n','\t'):
+ img = self.image.subsurface(x,y,tw,th)
+ self.chars[c] = img
+ x += tw
+ if x >= w: x,y = 0,y+th
+
+ self.colors = {}
+
+ def size(self,text):
+ tw,th = self.scale
+ return len(text)*tw,th
+
+ def render(self,text,antialias=0,color=(255,255,255),background=None):
+ size = self.size(text)
+ scale = self.scale
+ tw,th = self._size
+ if background == None:
+ s = pygame.Surface(size).convert_alpha()
+ s.fill((0,0,0,0))
+ else:
+ s = pygame.Surface(size).convert()
+ s.fill(background)
+
+ if not self.sensitive: text = text.lower()
+
+ if color not in self.colors: self.colors[color] = {}
+ colored = self.colors[color]
+
+ x,y = 0,0
+ for c in text:
+ if c in self.chars:
+ if c not in colored:
+ img = self.chars[c].convert_alpha()
+ for yy in xrange(0,th):
+ for xx in xrange(0,tw):
+ r,g,b,a = img.get_at((xx,yy))
+ if a > 128:
+ img.set_at((xx,yy),color)
+ colored[c] = img
+ img = colored[c]
+ if scale != (tw,th): img = pygame.transform.scale(img,scale)
+ s.blit(img,(x,y))
+ x += scale[0]
+ return s
+
+
+class BorderFont:
+ """a decorator for normal fonts, adds a border. Interface compatible with pygame.Font.
+
+ <pre>BorderFont(font,size=1,color=(0,0,0))</pre>
+
+ <dl>
+ <dt>size <dd>width of border; defaults 0
+ <dt>color <dd>color of border; default (0,0,0)
+ </dl>
+ """
+ def __init__(self,font,size=1,color=(0,0,0)):
+
+ self.font = font
+ self._size = size
+ self.color = color
+
+ def size(self,text):
+ w,h = self.font.size(text)
+ s = self._size
+ return w+s*2,h+s*2
+
+ def render(self,text,antialias=0,color=(255,255,255),background=None):
+ size = self.size(text)
+
+ if background == None:
+ s = pygame.Surface(size).convert_alpha()
+ s.fill((0,0,0,0))
+ else:
+ s = pygame.Surface(size).convert()
+ s.fill(background)
+
+ bg = self.font.render(text,antialias,self.color)
+ fg = self.font.render(text,antialias,color)
+
+ si = self._size
+ dirs = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]
+ for dx,dy in dirs: s.blit(bg,(si+dx*si,si+dy*si))
+ s.blit(fg,(si,si))
+
+ return s
+
+
--- /dev/null
+import pygame
+from pygame.locals import *
+
+from theme import Theme
+from style import Style
+from widget import Widget
+from surface import subsurface, ProxySurface
+from const import *
+
+from container import Container
+from app import App, Desktop
+from table import Table
+from document import Document
+#html
+from area import SlideBox, ScrollArea, List
+
+from form import Form
+from group import Group
+
+from basic import Spacer, Color, Label, Image, parse_color
+from button import Icon, Button, Switch, Checkbox, Radio, Tool, Link
+from input import Input, Password
+from keysym import Keysym
+from slider import VSlider, HSlider, VScrollBar, HScrollBar
+from select import Select
+from misc import ProgressBar
+
+from menus import Menus
+from dialog import Dialog, FileDialog
+from textarea import TextArea
+
+from deprecated import Toolbox, action_open, action_setvalue, action_quit, action_exec
--- /dev/null
+"""
+"""
+import pygame
+from pygame.locals import *
+
+import pguglobals
+import container
+from const import *
+
+class App(container.Container):
+ """The top-level widget for an application.
+
+ <pre>App(theme=None)</pre>
+
+ <dl>
+ <dt>theme<dd>an instance of a Theme, optional as it will use the default Theme class.
+ </dl>
+
+ <strong>Basic Example</strong>
+ <code>
+ app = gui.App()
+ app.run(widget=widget,screen=screen)
+ </code>
+
+ <strong>Integrated Example</strong>
+ <code>
+ app = gui.App()
+ gui.init(widget=widget)
+ while 1:
+ for e in pygame.event.get():
+ app.event(e)
+ app.update(screen)
+ </code>
+
+
+
+ """
+ def __init__(self,theme=None,**params):
+ self.set_global_app()
+
+ if theme == None:
+ from theme import Theme
+ theme = Theme()
+ self.theme = theme
+
+ params['decorate'] = 'app'
+ container.Container.__init__(self,**params)
+ self._quit = False
+ self.widget = None
+ self._chsize = False
+ self._repaint = False
+
+ self.screen = None
+ self.container = None
+ self.events = []
+
+ def set_global_app(self):
+ # Keep a global reference to this application instance so that PGU
+ # components can easily find it.
+ pguglobals.app = self
+ # For backwards compatibility we keep a reference in the class
+ # itself too.
+ App.app = self
+
+ def resize(self):
+
+ screen = self.screen
+ w = self.widget
+ wsize = 0
+
+ #5 cases
+
+ #input screen is already set use its size
+ if screen:
+ self.screen = screen
+ width,height = screen.get_width(),screen.get_height()
+
+ #display.screen
+ elif pygame.display.get_surface():
+ screen = pygame.display.get_surface()
+ self.screen = screen
+ width,height = screen.get_width(),screen.get_height()
+
+ #app has width,height
+ elif self.style.width != 0 and self.style.height != 0:
+ screen = pygame.display.set_mode((self.style.width,self.style.height),SWSURFACE)
+ self.screen = screen
+ width,height = screen.get_width(),screen.get_height()
+
+ #widget has width,height, or its own size..
+ else:
+ wsize = 1
+ width,height = w.rect.w,w.rect.h = w.resize()
+ #w._resize()
+ screen = pygame.display.set_mode((width,height),SWSURFACE)
+ self.screen = screen
+
+ #use screen to set up size of this widget
+ self.style.width,self.style.height = width,height
+ self.rect.w,self.rect.h = width,height
+ self.rect.x,self.rect.y = 0,0
+
+ w.rect.x,w.rect.y = 0,0
+ w.rect.w,w.rect.h = w.resize(width,height)
+
+ for w in self.windows:
+ w.rect.w,w.rect.h = w.resize()
+
+ self._chsize = False
+
+
+ def init(self,widget=None,screen=None): #TODO widget= could conflict with module widget
+ """Initialize the application.
+
+ <pre>App.init(widget=None,screen=None)</pre>
+
+ <dl>
+ <dt>widget<dd>main widget
+ <dt>screen<dd>pygame.Surface to render to
+ </dl>
+ """
+
+ self.set_global_app()
+
+ if widget: self.widget = widget
+ if screen: self.screen = screen
+
+ self.resize()
+
+ w = self.widget
+
+ self.widgets = []
+ self.widgets.append(w)
+ w.container = self
+ self.focus(w)
+
+ pygame.key.set_repeat(500,30)
+
+ self._repaint = True
+ self._quit = False
+
+ self.send(INIT)
+
+ def event(self,e):
+ """Pass an event to the main widget.
+
+ <pre>App.event(e)</pre>
+
+ <dl>
+ <dt>e<dd>event
+ </dl>
+ """
+ self.set_global_app()
+
+ #NOTE: might want to deal with ACTIVEEVENT in the future.
+ self.send(e.type,e)
+ container.Container.event(self,e)
+ if e.type == MOUSEBUTTONUP:
+ if e.button not in (4,5): #ignore mouse wheel
+ sub = pygame.event.Event(CLICK,{
+ 'button':e.button,
+ 'pos':e.pos})
+ self.send(sub.type,sub)
+ container.Container.event(self,sub)
+
+
+ def loop(self):
+ self.set_global_app()
+
+ s = self.screen
+ for e in pygame.event.get():
+ if not (e.type == QUIT and self.mywindow):
+ self.event(e)
+ us = self.update(s)
+ pygame.display.update(us)
+
+
+ def paint(self,screen):
+ self.screen = screen
+ if self._chsize:
+ self.resize()
+ self._chsize = False
+ if hasattr(self,'background'):
+ self.background.paint(screen)
+ container.Container.paint(self,screen)
+
+ def update(self,screen):
+ """Update the screen.
+
+ <dl>
+ <dt>screen<dd>pygame surface
+ </dl>
+ """
+ self.screen = screen
+ if self._chsize:
+ self.resize()
+ self._chsize = False
+ if self._repaint:
+ self.paint(screen)
+ self._repaint = False
+ return [pygame.Rect(0,0,screen.get_width(),screen.get_height())]
+ else:
+ us = container.Container.update(self,screen)
+ return us
+
+ def run(self,widget=None,screen=None):
+ """Run an application.
+
+ <p>Automatically calls <tt>App.init</tt> and then forever loops <tt>App.event</tt> and <tt>App.update</tt></p>
+
+ <dl>
+ <dt>widget<dd>main widget
+ <dt>screen<dd>pygame.Surface to render to
+ </dl>
+ """
+ self.init(widget,screen)
+ while not self._quit:
+ self.loop()
+ pygame.time.wait(10)
+
+ def reupdate(self,w=None): pass
+ def repaint(self,w=None): self._repaint = True
+ def repaintall(self): self._repaint = True
+ def chsize(self):
+ self._chsize = True
+ self._repaint = True
+
+ def quit(self,value=None): self._quit = True
+
+ def open(self, w, pos=None):
+ w.container = self
+
+ if (w.rect.w == 0 or w.rect.h == 0):
+ w.rect.size = w.resize()
+
+ if (not pos):
+ # Auto-center the window
+ w.rect.center = self.rect.center
+ #w.rect.topleft = ((self.rect.w - w.rect.w)/2,
+ # (self.rect.h - w.rect.h)/2)
+ else:
+ # Show the window in a particular location
+ w.rect.topleft = pos
+
+ self.windows.append(w)
+ self.mywindow = w
+ self.focus(w)
+ self.repaint(w)
+ w.send(OPEN)
+
+ def close(self, w):
+ if self.myfocus is w: self.blur(w)
+
+ if w not in self.windows: return #no need to remove it twice! happens.
+
+ self.windows.remove(w)
+
+ self.mywindow = None
+ if self.windows:
+ self.mywindow = self.windows[-1]
+ self.focus(self.mywindow)
+
+ if not self.mywindow:
+ self.myfocus = self.widget #HACK: should be done fancier, i think..
+ if not self.myhover:
+ self.enter(self.widget)
+
+ self.repaintall()
+ w.send(CLOSE)
+
+
+class Desktop(App):
+ """Create an App using the <tt>desktop</tt> theme class.
+
+ <pre>Desktop()</pre>
+ """
+ def __init__(self,**params):
+ params.setdefault('cls','desktop')
+ App.__init__(self,**params)
--- /dev/null
+"""
+"""
+import os
+
+import pguglobals
+from const import *
+import surface
+import container, table
+import group
+import basic, button, slider
+from pygame.font import Font
+
+class SlideBox(container.Container):
+ """A scrollable area with no scrollbars.
+
+ <pre>SlideBox(widget,width,height)</pre>
+
+ <dl>
+ <dt>widget<dd>widget to be able to scroll around
+ <dt>width, height<dd>size of scrollable area
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ c = SlideBox(w,100,100)
+ c.offset = (10,10)
+ c.repaint()
+ </code>
+
+ """
+
+ def __init__(self, widget, width, height, **params):
+ params.setdefault('width', width)
+ params.setdefault('height', height)
+ container.Container.__init__(self, **params)
+ self.offset = [0, 0]
+ self.widget = widget
+
+ def __setattr__(self,k,v):
+ if k == 'widget':
+ if hasattr(self,'widget'):
+ self.remove(self.widget)
+ self.add(v,0,0)
+ self.__dict__[k] = v
+
+
+ def paint(self, s):
+ #if not hasattr(self,'surface'):
+ self.surface = pygame.Surface((self.max_rect.w,self.max_rect.h),0,s)
+ #self.surface.fill((0,0,0,0))
+ pguglobals.app.theme.render(self.surface,self.style.background,pygame.Rect(0,0,self.max_rect.w,self.max_rect.h))
+ self.bkgr = pygame.Surface((s.get_width(),s.get_height()),0,s)
+ self.bkgr.blit(s,(0,0))
+ container.Container.paint(self,self.surface)
+ s.blit(self.surface,(-self.offset[0],-self.offset[1]))
+ self._offset = self.offset[:]
+ return
+
+ def paint_for_when_pygame_supports_other_tricks(self,s):
+ #this would be ideal if pygame had support for it!
+ #and if pgu also had a paint(self,s,rect) method to paint small parts
+ sr = (self.offset[0],self.offset[1],self.max_rect.w,self.max_rect.h)
+ cr = (-self.offset[0],-self.offset[1],s.get_width(),s.get_height())
+ s2 = s.subsurface(sr)
+ s2.set_clip(cr)
+ container.Container.paint(self,s2)
+
+ def proxy_paint(self, s):
+ container.Container.paint(self, surface.ProxySurface(parent=None,
+ rect=self.max_rect,
+ real_surface=s,
+ offset=self.offset))
+ def update(self, s):
+ rects = container.Container.update(self,self.surface)
+
+ rets = []
+ s_rect = pygame.Rect(0,0,s.get_width(),s.get_height())
+
+ if self.offset == self._offset:
+ for r in rects:
+ r2 = r.move((-self.offset[0],-self.offset[1]))
+ if r2.colliderect(s_rect):
+ s.blit(self.surface.subsurface(r),r2)
+ rets.append(r2)
+ else:
+ s.blit(self.bkgr,(0,0))
+ sub = pygame.Rect(self.offset[0],self.offset[1],min(s.get_width(),self.max_rect.w-self.offset[0]),min(s.get_height(),self.max_rect.h-self.offset[1]))
+# print sub
+# print self.surface.get_width(),self.surface.get_height()
+# print s.get_width(),s.get_height()
+# print self.offset
+# print self.style.width,self.style.height
+ s.blit(self.surface.subsurface(sub),(0,0))
+ rets.append(s_rect)
+ self._offset = self.offset[:]
+ return rets
+
+ def proxy_update(self, s):
+ rects = container.Container.update(self, surface.ProxySurface(parent=None,
+ rect=self.max_rect,
+ real_surface=s,
+ offset=self.offset))
+ result = []
+ for r in rects: result.append(pygame.Rect(r).move(self.offset))
+ return result
+
+ def resize(self, width=None, height=None):
+ container.Container.resize(self)
+ self.max_rect = pygame.Rect(self.widget.rect)
+ #self.max_rect.w = max(self.max_rect.w,self.style.width)
+ #self.max_rect.h = max(self.max_rect.h,self.style.height)
+ return self.style.width,self.style.height
+ #self.rect = pygame.Rect(self.rect[0], self.rect[1], self.style.width, self.style.height)
+
+ def event(self, e):
+ if e.type in [MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEMOTION]:
+ pos = (e.pos[0] + self.offset[0], e.pos[1] + self.offset[1])
+ if self.max_rect.collidepoint(pos):
+ e_params = {'pos': pos }
+ if e.type == MOUSEMOTION:
+ e_params['buttons'] = e.buttons
+ e_params['rel'] = e.rel
+ else:
+ e_params['button'] = e.button
+ e = pygame.event.Event(e.type, e_params)
+ container.Container.event(self, e)
+
+#class SlideBox(Area):
+# def __init__(self,*args,**params):
+# print 'gui.SlideBox','Scheduled to be renamed to Area.'
+# Area.__init__(self,*args,**params)
+
+class ScrollArea(table.Table):
+ """A scrollable area with scrollbars.
+
+ <pre>ScrollArea(widget,width,height,hscrollbar=True)</pre>
+
+ <dl>
+ <dt>widget<dd>widget to be able to scroll around
+ <dt>width, height<dd>size of scrollable area. Set either to 0 to default to size of widget.
+ <dt>hscrollbar<dd>set to False if you do not wish to have a horizontal scrollbar
+ <dt>vscrollbar<dd>set to False if you do not wish to have a vertical scrollbar
+ <dt>step<dd>set to how far clicks on the icons will step
+ </dl>
+ """
+ def __init__(self, widget, width=0, height=0, hscrollbar=True, vscrollbar=True,step=24, **params):
+ w= widget
+ params.setdefault('cls', 'scrollarea')
+ table.Table.__init__(self, width=width,height=height,**params)
+
+ self.sbox = SlideBox(w, width=width, height=height, cls=self.cls+".content")
+ self.widget = w
+ self.vscrollbar = vscrollbar
+ self.hscrollbar = hscrollbar
+
+ self.step = step
+
+ def __setattr__(self,k,v):
+ if k == 'widget':
+ self.sbox.widget = v
+ self.__dict__[k] = v
+
+ def resize(self,width=None,height=None):
+ widget = self.widget
+ box = self.sbox
+
+ #self.clear()
+ table.Table.clear(self)
+ #print 'resize',self,self._rows
+
+ self.tr()
+ self.td(box)
+
+ widget.rect.w, widget.rect.h = widget.resize()
+ my_width,my_height = self.style.width,self.style.height
+ if not my_width:
+ my_width = widget.rect.w
+ self.hscrollbar = False
+ if not my_height:
+ my_height = widget.rect.h
+ self.vscrollbar = False
+
+ box.style.width,box.style.height = my_width,my_height #self.style.width,self.style.height
+
+ box.rect.w,box.rect.h = box.resize()
+
+ #print widget.rect
+ #print box.rect
+ #r = table.Table.resize(self,width,height)
+ #print r
+ #return r
+
+ #print box.offset
+
+# #this old code automatically adds in a scrollbar if needed
+# #but it doesn't always work
+# self.vscrollbar = None
+# if widget.rect.h > box.rect.h:
+# self.vscrollbar = slider.VScrollBar(box.offset[1],0, 65535, 0,step=self.step)
+# self.td(self.vscrollbar)
+# self.vscrollbar.connect(CHANGE, self._vscrollbar_changed, None)
+#
+# vs = self.vscrollbar
+# vs.rect.w,vs.rect.h = vs.resize()
+# box.style.width = self.style.width - vs.rect.w
+#
+#
+# self.hscrollbar = None
+# if widget.rect.w > box.rect.w:
+# self.hscrollbar = slider.HScrollBar(box.offset[0], 0,65535, 0,step=self.step)
+# self.hscrollbar.connect(CHANGE, self._hscrollbar_changed, None)
+# self.tr()
+# self.td(self.hscrollbar)
+#
+# hs = self.hscrollbar
+# hs.rect.w,hs.rect.h = hs.resize()
+# box.style.height = self.style.height - hs.rect.h
+
+ xt,xr,xb,xl = pguglobals.app.theme.getspacing(box)
+
+
+ if self.vscrollbar:
+ self.vscrollbar = slider.VScrollBar(box.offset[1],0, 65535, 0,step=self.step)
+ self.td(self.vscrollbar)
+ self.vscrollbar.connect(CHANGE, self._vscrollbar_changed, None)
+
+ vs = self.vscrollbar
+ vs.rect.w,vs.rect.h = vs.resize()
+ if self.style.width:
+ box.style.width = self.style.width - (vs.rect.w + xl+xr)
+
+ if self.hscrollbar:
+ self.hscrollbar = slider.HScrollBar(box.offset[0], 0,65535, 0,step=self.step)
+ self.hscrollbar.connect(CHANGE, self._hscrollbar_changed, None)
+ self.tr()
+ self.td(self.hscrollbar)
+
+ hs = self.hscrollbar
+ hs.rect.w,hs.rect.h = hs.resize()
+ if self.style.height:
+ box.style.height = self.style.height - (hs.rect.h + xt + xb)
+
+ if self.hscrollbar:
+ hs = self.hscrollbar
+ hs.min = 0
+ hs.max = widget.rect.w - box.style.width
+ hs.style.width = box.style.width
+ hs.size = hs.style.width * box.style.width / max(1,widget.rect.w)
+ else:
+ box.offset[0] = 0
+
+ if self.vscrollbar:
+ vs = self.vscrollbar
+ vs.min = 0
+ vs.max = widget.rect.h - box.style.height
+ vs.style.height = box.style.height
+ vs.size = vs.style.height * box.style.height / max(1,widget.rect.h)
+ else:
+ box.offset[1] = 0
+
+ #print self.style.width,box.style.width, hs.style.width
+
+ r = table.Table.resize(self,width,height)
+ return r
+
+ def x_resize(self, width=None, height=None):
+ w,h = table.Table.resize(self, width, height)
+ if self.hscrollbar:
+ if self.widget.rect.w <= self.sbox.rect.w:
+ self.hscrollbar.size = self.hscrollbar.style.width
+ else:
+ self.hscrollbar.size = max(20,self.hscrollbar.style.width * self.sbox.rect.w / self.widget.rect.w)
+ self._hscrollbar_changed(None)
+ if self.widget.rect.h <= self.sbox.rect.h:
+ self.vscrollbar.size = self.vscrollbar.style.height
+ else:
+ self.vscrollbar.size = max(20,self.vscrollbar.style.height * self.sbox.rect.h / self.widget.rect.h)
+ self._vscrollbar_changed(None)
+ return w,h
+
+ def _vscrollbar_changed(self, xxx):
+ #y = (self.widget.rect.h - self.sbox.rect.h) * self.vscrollbar.value / 1000
+ #if y >= 0: self.sbox.offset[1] = -y
+ self.sbox.offset[1] = self.vscrollbar.value
+ self.sbox.reupdate()
+
+ def _hscrollbar_changed(self, xxx):
+ #x = (self.widget.rect.w - self.sbox.rect.w) * self.hscrollbar.value / 1000
+ #if x >= 0: self.sbox.offset[0] = -x
+ self.sbox.offset[0] = self.hscrollbar.value
+ self.sbox.reupdate()
+
+
+ def set_vertical_scroll(self, percents):
+ #if not self.vscrollbar: return
+ if not hasattr(self.vscrollbar,'value'): return
+ self.vscrollbar.value = percents #min(max(percents*10, 0), 1000)
+ self._vscrollbar_changed(None)
+
+ def set_horizontal_scroll(self, percents):
+ #if not self.hscrollbar: return
+ if not hasattr(self.hscrollbar,'value'): return
+ self.hscrollbar.value = percents #min(max(percents*10, 0), 1000)
+ self._hscrollbar_changed(None)
+
+ def event(self, e):
+ #checking for event recipient
+ if (table.Table.event(self, e)):
+ return True
+
+ #mouse wheel scrolling
+ if self.vscrollbar:
+ if not hasattr(self.vscrollbar,'value'):
+ return False
+
+ if e.type == pygame.locals.MOUSEBUTTONDOWN:
+ if e.button == 4: #wheel up
+ self.vscrollbar._click(-1)
+ return True
+ elif e.button == 5: #wheel down
+ self.vscrollbar._click(1)
+ return True
+ return False
+
+
+
+
+class _List_Item(button._button):
+ def __init__(self,label=None,image=None,value=None,**params): #TODO label= could conflict with the module label
+ #param image: an imagez.Image object (optional)
+ #param text: a string object
+ params.setdefault('cls','list.item')
+ button._button.__init__(self,**params)
+ self.group = None
+ self.value = value #(self, value)
+ self.widget = None
+
+ if type(label) == str:
+ label = basic.Label(label, cls=self.cls+".label")
+
+ if image and label:
+ self.widget = container.Container()
+ self.widget.add(image, 0, 0)
+ #HACK: improper use of .resize()
+ image.rect.w,image.rect.h = image.resize()
+ self.widget.add(label, image.rect.w, 0)
+ elif image: self.widget = image
+ elif label: self.widget = label
+
+ self.pcls = ""
+
+ def resize(self,width=None,height=None):
+ self.widget.rect.w,self.widget.rect.h = self.widget.resize()
+ return self.widget.rect.w,self.widget.rect.h
+# self.widget._resize()
+# self.rect.w,self.rect.h = self.widget.rect_margin.w,self.widget.rect_margin.h
+
+ def event(self,e):
+ button._button.event(self,e)
+ if self.group.value == self.value: self.pcls = "down"
+
+ def paint(self,s):
+ if self.group.value == self.value: self.pcls = "down"
+ self.widget.paint(surface.subsurface(s,self.widget.rect))
+
+ def click(self):
+ self.group.value = self.value
+ for w in self.group.widgets:
+ if w != self: w.pcls = ""
+
+
+
+class List(ScrollArea):
+ """A list of items in an area.
+
+ <p>This widget can be a form element, it has a value set to whatever item is selected.</p>
+
+ <pre>List(width,height)</pre>
+ """
+ def _change(self, value):
+ self.value = self.group.value
+ self.send(CHANGE)
+
+ def __init__(self, width, height, **params):
+ params.setdefault('cls', 'list')
+ self.table = table.Table(width=width)
+ ScrollArea.__init__(self, self.table, width, height,hscrollbar=False ,**params)
+
+ self.items = []
+
+ g = group.Group()
+ self.group = g
+ g.connect(CHANGE,self._change,None)
+ self.value = self.group.value = None
+
+ self.add = self._add
+ self.remove = self._remove
+
+ def clear(self):
+ """Clear the list.
+
+ <pre>List.clear()</pre>
+ """
+ self.items = []
+ self.group = group.Group()
+ self.group.connect(CHANGE,self._change,None)
+ self.table.clear()
+ self.set_vertical_scroll(0)
+ self.blur(self.myfocus)
+
+ def _docs(self): #HACK: nasty hack to get the docs in "my way"
+ def add(self, label, image=None, value=None):
+ """Add an item to the list.
+
+ <pre>List.add(label,image=None,value=None)</pre>
+
+ <dl>
+ <dt>label<dd>a label for the item
+ <dt>image<dd>an image for the item
+ <dt>value<dd>a value for the item
+ </dl>
+ """
+
+ def remove(self,value):
+ """Remove an item from the list.
+
+ <pre>List.remove(value)</pre>
+
+ <dl>
+ <dt>value<dd>a value of an item to remove from the list
+ </dl>
+ """
+
+ def _add(self, label, image = None, value=None):
+ item = _List_Item(label,image=image,value=value)
+ self.table.tr()
+ self.table.add(item)
+ self.items.append(item)
+ item.group = self.group
+ item.group.add(item)
+
+ def _remove(self, item):
+ for i in self.items:
+ if i.value == item: item = i
+ if item not in self.items: return
+ item.blur()
+ self.items.remove(item)
+ self.group.widgets.remove(item)
+ self.table.remove_row(item.style.row)
+
+#class List(ListArea):
+# def __init__(self,*args,**params):
+# print 'gui.List','Scheduled to be renamed to ListArea. API may also be changed in the future.'
+# ListArea.__init__(self,*args,**params)
--- /dev/null
+"""These widgets are all grouped together because they are non-interactive widgets.
+"""
+
+import pygame
+
+from const import *
+import widget
+
+# Turns a descriptive string or a tuple into a pygame color
+def parse_color(desc):
+ if (is_color(desc)):
+ # Already a color
+ return desc
+ elif (desc and desc[0] == "#"):
+ # Because of a bug in pygame 1.8.1 we need to explicitly define the
+ # alpha value otherwise it will default to transparent.
+ if (len(desc) == 7):
+ desc += "FF"
+ return pygame.Color(desc)
+
+# Determines if the given object is a pygame-compatible color or not
+def is_color(col):
+ # In every version of pygame (up to 1.8.1 so far) will interpret
+ # a tuple as a color.
+ if (type(col) == tuple):
+ return col
+ if (hasattr(pygame, "Color") and type(pygame.Color) == type):
+ # This is a recent version of pygame that uses a proper type
+ # instance for colors.
+ return (isinstance(col, pygame.Color))
+ # Otherwise, this version of pygame only supports tuple colors
+ return False
+
+class Spacer(widget.Widget):
+ """A invisible space.
+
+ <pre>Spacer(width,height)</pre>
+
+ """
+ def __init__(self,width,height,**params):
+ params.setdefault('focusable',False)
+ widget.Widget.__init__(self,width=width,height=height,**params)
+
+
+class Color(widget.Widget):
+ """A block of color.
+
+ <p>The color can be changed at run-time.</p>
+
+ <pre>Color(value=None)</pre>
+
+ <strong>Example</strong>
+ <code>
+ c = Color()
+ c.value = (255,0,0)
+ c.value = (0,255,0)
+ </code>
+ """
+
+
+ def __init__(self,value=None,**params):
+ params.setdefault('focusable',False)
+ if value != None: params['value']=value
+ widget.Widget.__init__(self,**params)
+
+ def paint(self,s):
+ if hasattr(self,'value'): s.fill(self.value)
+
+ def __setattr__(self,k,v):
+ if k == 'value' and type(v) == str:
+ v = parse_color(v)
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self.send(CHANGE)
+ self.repaint()
+
+class Label(widget.Widget):
+ """A text label.
+
+ <pre>Label(value)</pre>
+
+ <dl>
+ <dt>value<dd>text to be displayed
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = Label(value="I own a rubber chicken!")
+
+ w = Label("3 rubber chickens")
+ </code>
+ """
+ def __init__(self,value,**params):
+ params.setdefault('focusable',False)
+ params.setdefault('cls','label')
+ widget.Widget.__init__(self,**params)
+ self.value = value
+ self.font = self.style.font
+ self.style.width, self.style.height = self.font.size(self.value)
+
+ def paint(self,s):
+ s.blit(self.font.render(self.value, 1, self.style.color),(0,0))
+
+class Image(widget.Widget):
+ """An image.
+
+ <pre>Image(value)</pre>
+
+ <dl>
+ <dt>value<dd>a file name or a pygame.Surface
+ </dl>
+
+ """
+ def __init__(self,value,**params):
+ params.setdefault('focusable',False)
+ widget.Widget.__init__(self,**params)
+ if type(value) == str: value = pygame.image.load(value)
+
+ ow,oh = iw,ih = value.get_width(),value.get_height()
+ sw,sh = self.style.width,self.style.height
+
+ if sw and not sh:
+ iw,ih = sw,ih*sw/iw
+ elif sh and not sw:
+ iw,ih = iw*sh/ih,sh
+ elif sw and sh:
+ iw,ih = sw,sh
+
+ if (ow,oh) != (iw,ih):
+ value = pygame.transform.scale(value,(iw,ih))
+ self.style.width,self.style.height = iw,ih
+ self.value = value
+
+ def paint(self,s):
+ s.blit(self.value,(0,0))
--- /dev/null
+"""
+"""
+
+from pygame.locals import *
+
+from const import *
+import widget, surface
+import basic
+
+class _button(widget.Widget):
+ def __init__(self,**params):
+ widget.Widget.__init__(self,**params)
+ self.state = 0
+
+ def event(self,e):
+ if e.type == ENTER: self.repaint()
+ elif e.type == EXIT: self.repaint()
+ elif e.type == FOCUS: self.repaint()
+ elif e.type == BLUR: self.repaint()
+ elif e.type == KEYDOWN:
+ if e.key == K_SPACE or e.key == K_RETURN:
+ self.state = 1
+ self.repaint()
+ elif e.type == MOUSEBUTTONDOWN:
+ self.state = 1
+ self.repaint()
+ elif e.type == KEYUP:
+ if self.state == 1:
+ sub = pygame.event.Event(CLICK,{'pos':(0,0),'button':1})
+ #self.send(sub.type,sub)
+ self._event(sub)
+
+ self.state = 0
+ self.repaint()
+ elif e.type == MOUSEBUTTONUP:
+ self.state = 0
+ self.repaint()
+ elif e.type == CLICK:
+ self.click()
+
+ self.pcls = ""
+ if self.state == 0 and self.container.myhover is self:
+ self.pcls = "hover"
+ if self.state == 1 and self.container.myhover is self:
+ self.pcls = "down"
+
+ def click(self):
+ pass
+
+
+class Button(_button):
+ """A button, buttons can be clicked, they are usually used to set up callbacks.
+
+ <pre>Button(value=None)</pre>
+
+ <dl>
+ <dt>value<dd>either a widget or a string
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = gui.Button("Click Me")
+ w.connect(gui.CLICK,fnc,value)
+ </code>
+ """
+ def __init__(self,value=None,**params):
+ params.setdefault('cls','button')
+ _button.__init__(self,**params)
+ self.value = value
+
+ def __setattr__(self,k,v):
+ if k == 'value' and type(v) == str: v = basic.Label(v,cls=self.cls+".label")
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and v != None:
+ pass
+
+ if k == 'value' and _v != NOATTR and _v != None and _v != v:
+ self.send(CHANGE)
+ self.chsize()
+
+ def resize(self,width=None,height=None):
+ self.value.rect.x,self.value.rect.y = 0,0
+ self.value.rect.w,self.value.rect.h = self.value.resize(width,height)
+ return self.value.rect.w,self.value.rect.h
+#
+# self.value._resize()
+# self.rect.w,self.rect.h = self.value.rect_margin.w,self.value.rect_margin.h
+#
+# if self.style.width: self.rect.w = max(self.rect.w,self.style.width)
+# if self.style.height: self.rect.w = max(self.rect.w,self.style.height)
+#
+# xt,xr,xb,xl = self.value.getspacing()
+#
+# self.value._resize(self.rect.w-(xl+xr),self.rect.h-(xt+xb))
+#
+ def paint(self,s):
+ self.value.pcls = self.pcls
+ self.value.paint(surface.subsurface(s,self.value.rect))
+
+class Switch(_button):
+ """A switch can have two states, True or False.
+
+ <pre>Switch(value=False)</pre>
+
+ <dl>
+ <dt>value<dd>initial value, (True, False)
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = gui.Switch(True)
+ w.connect(gui.CHANGE,fnc,value)
+ </code>
+ """
+ def __init__(self,value=False,**params):
+ params.setdefault('cls','switch')
+ _button.__init__(self,**params)
+ self.value = value
+
+ img = self.style.off
+ self.style.width = img.get_width()
+ self.style.height = img.get_height()
+
+ def paint(self,s):
+ #self.pcls = ""
+ #if self.container.myhover is self: self.pcls = "hover"
+ if self.value: img = self.style.on
+ else: img = self.style.off
+ s.blit(img,(0,0))
+
+ def __setattr__(self,k,v):
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self.send(CHANGE)
+ self.repaint()
+
+ def click(self):
+ self.value = not self.value
+
+class Checkbox(_button):
+ """Within a Group of Checkbox widgets several may be selected at a time.
+
+ <pre>Checkbox(group,value=None)</pre>
+
+ <dl>
+ <dt>group<dd>a gui.Group for the Checkbox to belong to
+ <dt>value<dd>the value
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ g = gui.Group(name='colors',value=['r','b'])
+
+ t = gui.Table()
+ t.tr()
+ t.td(gui.Label('Red'))
+ t.td(gui.Checkbox(g,'r'))
+ t.tr()
+ t.td(gui.Label('Green'))
+ t.td(gui.Checkbox(g,'g'))
+ t.tr()
+ t.td(gui.Label('Blue'))
+ t.td(gui.Checkbox(g,'b'))
+ </code>
+ """
+
+ def __init__(self,group,value=None,**params):
+ params.setdefault('cls','checkbox')
+ _button.__init__(self,**params)
+ self.group = group
+ self.group.add(self)
+ if self.group.value == None:
+ self.group.value = []
+ self.value = value
+
+ img = self.style.off
+ self.style.width = img.get_width()
+ self.style.height = img.get_height()
+
+ def paint(self,s):
+ #self.pcls = ""
+ #if self.container.myhover is self: self.pcls = "hover"
+ if self.value in self.group.value: img = self.style.on
+ else: img = self.style.off
+
+ s.blit(img,(0,0))
+
+ def click(self):
+ if self.value in self.group.value:
+ self.group.value.remove(self.value)
+ else:
+ self.group.value.append(self.value)
+ self.group._change()
+
+class Radio(_button):
+ """Within a Group of Radio widgets only one may be selected at a time.
+
+ <pre>Radio(group,value=None)</pre>
+
+ <dl>
+ <dt>group<dd>a gui.Group for the Radio to belong to
+ <dt>value<dd>the value
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ g = gui.Group(name='colors',value='g')
+
+ t = gui.Table()
+ t.tr()
+ t.td(gui.Label('Red'))
+ t.td(gui.Radio(g,'r'))
+ t.tr()
+ t.td(gui.Label('Green'))
+ t.td(gui.Radio(g,'g'))
+ t.tr()
+ t.td(gui.Label('Blue'))
+ t.td(gui.Radio(g,'b'))
+ </code>
+ """
+
+
+ def __init__(self,group=None,value=None,**params):
+ params.setdefault('cls','radio')
+ _button.__init__(self,**params)
+ self.group = group
+ self.group.add(self)
+ self.value = value
+
+ img = self.style.off
+ self.style.width = img.get_width()
+ self.style.height = img.get_height()
+
+ def paint(self,s):
+ #self.pcls = ""
+ #if self.container.myhover is self: self.pcls = "hover"
+ if self.group.value == self.value: img = self.style.on
+ else: img = self.style.off
+ s.blit(img,(0,0))
+
+ def click(self):
+ self.group.value = self.value
+
+class Tool(_button):
+ """Within a Group of Tool widgets only one may be selected at a time.
+
+ <pre>Tool(group,widget=None,value=None)</pre>
+
+ <dl>
+ <dt>group<dd>a gui.Group for the Tool to belong to
+ <dt>widget<dd>a widget to appear on the Tool (similar to a Button)
+ <dt>value<dd>the value
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ g = gui.Group(name='colors',value='g')
+
+ t = gui.Table()
+ t.tr()
+ t.td(gui.Tool(g,'Red','r'))
+ t.tr()
+ t.td(gui.Tool(g,'Green','g'))
+ t.tr()
+ t.td(gui.Tool(g,'Blue','b'))
+ </code>
+ """
+
+ def __init__(self,group,widget=None,value=None,**params): #TODO widget= could conflict with module widget
+ params.setdefault('cls','tool')
+ _button.__init__(self,**params)
+ self.group = group
+ self.group.add(self)
+ self.value = value
+
+ if widget:
+ self.setwidget(widget)
+
+ if self.group.value == self.value: self.pcls = "down"
+
+ def setwidget(self,w):
+ self.widget = w
+
+ def resize(self,width=None,height=None):
+ self.widget.rect.w,self.widget.rect.h = self.widget.resize()
+ #self.widget._resize()
+ #self.rect.w,self.rect.h = self.widget.rect_margin.w,self.widget.rect_margin.h
+
+ return self.widget.rect.w,self.widget.rect.h
+
+ def event(self,e):
+ _button.event(self,e)
+ if self.group.value == self.value: self.pcls = "down"
+
+ def paint(self,s):
+ if self.group.value == self.value: self.pcls = "down"
+ self.widget.paint(surface.subsurface(s,self.widget.rect))
+
+ def click(self):
+ self.group.value = self.value
+ for w in self.group.widgets:
+ if w != self: w.pcls = ""
+
+
+class Icon(_button):
+ """TODO - might be deprecated
+ """
+ def __init__(self,cls,**params):
+ params['cls'] = cls
+ _button.__init__(self,**params)
+ s = self.style.image
+ self.style.width = s.get_width()
+ self.style.height = s.get_height()
+ self.state = 0
+
+ def paint(self,s):
+ #self.pcls = ""
+ #if self.state == 0 and hasattr(self.container,'myhover') and self.container.myhover is self: self.pcls = "hover"
+ #if self.state == 1 and hasattr(self.container,'myhover') and self.container.myhover is self: self.pcls = "down"
+ s.blit(self.style.image,(0,0))
+
+class Link(_button):
+ """A link, links can be clicked, they are usually used to set up callbacks.
+ Basically the same as the button widget, just text only with a different cls. Made for
+ convenience.
+
+ <pre>Link(value=None)</pre>
+
+ <dl>
+ <dt>value<dd>a string
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = gui.Link("Click Me")
+ w.connect(gui.CLICK,fnc,value)
+ </code>
+ """
+ def __init__(self,value,**params):
+ params.setdefault('focusable',True)
+ params.setdefault('cls','link')
+ _button.__init__(self,**params)
+ self.value = value
+ self.font = self.style.font
+ self.style.width, self.style.height = self.font.size(self.value)
+
+ def paint(self,s):
+ s.blit(self.font.render(self.value, 1, self.style.color),(0,0))
+
--- /dev/null
+"""Constants.
+<br><br>
+<strong>Event Types</strong>
+
+<p>from pygame</p>
+<dl>
+<dt>QUIT
+<dt>MOUSEBUTTONDOWN
+<dt>MOUSEBUTTONUP
+<dt>MOUSEMOTION
+<dt>KEYDOWN
+</dl>
+
+<p>gui specific</p>
+<dl>
+<dt>ENTER
+<dt>EXIT
+<dt>BLUR
+<dt>FOCUS
+<dt>CLICK
+<dt>CHANGE
+<dt>OPEN
+<dt>CLOSE
+<dt>INIT
+</dl>
+
+<strong>Other</strong>
+<dl>
+<dt>NOATTR
+</dl>
+"""
+import pygame
+
+from pygame.locals import QUIT, MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEMOTION, KEYDOWN, USEREVENT
+ENTER = pygame.locals.USEREVENT + 0
+EXIT = pygame.locals.USEREVENT + 1
+BLUR = pygame.locals.USEREVENT + 2
+FOCUS = pygame.locals.USEREVENT + 3
+CLICK = pygame.locals.USEREVENT + 4
+CHANGE = pygame.locals.USEREVENT + 5
+OPEN = pygame.locals.USEREVENT + 6
+CLOSE = pygame.locals.USEREVENT + 7
+INIT = 'init'
+
+class NOATTR: pass
\ No newline at end of file
--- /dev/null
+"""
+"""
+import pygame
+from pygame.locals import *
+
+from const import *
+import widget, surface
+import pguglobals
+
+class Container(widget.Widget):
+ """The base container widget, can be used as a template as well as stand alone.
+
+ <pre>Container()</pre>
+ """
+ def __init__(self,**params):
+ widget.Widget.__init__(self,**params)
+ self.myfocus = None
+ self.mywindow = None
+ self.myhover = None
+ #self.background = 0
+ self.widgets = []
+ self.windows = []
+ self.toupdate = {}
+ self.topaint = {}
+
+ def update(self,s):
+ updates = []
+
+ if self.myfocus: self.toupdate[self.myfocus] = self.myfocus
+
+ for w in self.topaint:
+ if w is self.mywindow:
+ continue
+ else:
+ sub = surface.subsurface(s,w.rect)
+ #if (hasattr(w, "_container_bkgr")):
+ # sub.blit(w._container_bkgr,(0,0))
+ w.paint(sub)
+ updates.append(pygame.rect.Rect(w.rect))
+
+ for w in self.toupdate:
+ if w is self.mywindow:
+ continue
+ else:
+ us = w.update(surface.subsurface(s,w.rect))
+ if us:
+ for u in us:
+ updates.append(pygame.rect.Rect(u.x + w.rect.x,u.y+w.rect.y,u.w,u.h))
+
+ for w in self.topaint:
+ if w is self.mywindow:
+ w.paint(self.top_surface(s,w))
+ updates.append(pygame.rect.Rect(w.rect))
+ else:
+ continue
+
+ for w in self.toupdate:
+ if w is self.mywindow:
+ us = w.update(self.top_surface(s,w))
+ else:
+ continue
+ if us:
+ for u in us:
+ updates.append(pygame.rect.Rect(u.x + w.rect.x,u.y+w.rect.y,u.w,u.h))
+
+ self.topaint = {}
+ self.toupdate = {}
+
+ return updates
+
+ def repaint(self,w=None):
+ if not w:
+ return widget.Widget.repaint(self)
+ self.topaint[w] = w
+ self.reupdate()
+
+ def reupdate(self,w=None):
+ if not w:
+ return widget.Widget.reupdate(self)
+ self.toupdate[w] = w
+ self.reupdate()
+
+ def paint(self,s):
+ self.toupdate = {}
+ self.topaint = {}
+ for w in self.widgets:
+ try:
+ sub = surface.subsurface(s, w.rect)
+ except:
+ print 'container.paint(): %s not inside %s' % (
+ w.__class__.__name__,self.__class__.__name__)
+ print s.get_width(), s.get_height(), w.rect
+ print ""
+ else:
+# if (not hasattr(w,'_container_bkgr') or
+# w._container_bkgr.get_size() != sub.get_size()):
+# #w._container_bkgr.get_width() == sub.get_width() and
+# #w._container_bkgr.get_height() == sub.get_height())):
+# w._container_bkgr = sub.copy()
+# w._container_bkgr.fill((0,0,0,0))
+# w._container_bkgr.blit(sub,(0,0))
+ w.paint(sub)
+
+ for w in self.windows:
+ w.paint(self.top_surface(s,w))
+
+ def top_surface(self,s,w):
+ x,y = s.get_abs_offset()
+ s = s.get_abs_parent()
+ return surface.subsurface(s,(x+w.rect.x,y+w.rect.y,w.rect.w,w.rect.h))
+
+ def event(self,e):
+ used = False
+
+ if self.mywindow and e.type == MOUSEBUTTONDOWN:
+ w = self.mywindow
+ if self.myfocus is w:
+ if not w.rect.collidepoint(e.pos): self.blur(w)
+ if not self.myfocus:
+ if w.rect.collidepoint(e.pos): self.focus(w)
+
+ if not self.mywindow:
+ #### by Gal Koren
+ ##
+ ## if e.type == FOCUS:
+ if e.type == FOCUS and not self.myfocus:
+ #self.first()
+ pass
+ elif e.type == EXIT:
+ if self.myhover: self.exit(self.myhover)
+ elif e.type == BLUR:
+ if self.myfocus: self.blur(self.myfocus)
+ elif e.type == MOUSEBUTTONDOWN:
+ h = None
+ for w in self.widgets:
+ if not w.disabled: #focusable not considered, since that is only for tabs
+ if w.rect.collidepoint(e.pos):
+ h = w
+ if self.myfocus is not w: self.focus(w)
+ if not h and self.myfocus:
+ self.blur(self.myfocus)
+ elif e.type == MOUSEMOTION:
+ if 1 in e.buttons:
+ if self.myfocus: ws = [self.myfocus]
+ else: ws = []
+ else: ws = self.widgets
+
+ h = None
+ for w in ws:
+ if w.rect.collidepoint(e.pos):
+ h = w
+ if self.myhover is not w: self.enter(w)
+ if not h and self.myhover:
+ self.exit(self.myhover)
+ w = self.myhover
+
+ if w and w is not self.myfocus:
+ sub = pygame.event.Event(e.type,{
+ 'buttons':e.buttons,
+ 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y),
+ 'rel':e.rel})
+ used = w._event(sub)
+
+ w = self.myfocus
+ if w:
+ sub = e
+
+ if e.type == MOUSEBUTTONUP or e.type == MOUSEBUTTONDOWN:
+ sub = pygame.event.Event(e.type,{
+ 'button':e.button,
+ 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y)})
+ used = w._event(sub)
+ elif e.type == CLICK and self.myhover is w:
+ sub = pygame.event.Event(e.type,{
+ 'button':e.button,
+ 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y)})
+ used = w._event(sub)
+ elif e.type == CLICK: #a dead click
+ pass
+ elif e.type == MOUSEMOTION:
+ sub = pygame.event.Event(e.type,{
+ 'buttons':e.buttons,
+ 'pos':(e.pos[0]-w.rect.x,e.pos[1]-w.rect.y),
+ 'rel':e.rel})
+ used = w._event(sub)
+ else:
+ used = w._event(sub)
+
+ if not used:
+ if e.type is KEYDOWN:
+ if e.key is K_TAB and self.myfocus:
+ if (e.mod&KMOD_SHIFT) == 0:
+ self.myfocus.next()
+ else:
+ self.myfocus.previous()
+ return True
+ elif e.key == K_UP:
+ self._move_focus(0,-1)
+ return True
+ elif e.key == K_RIGHT:
+ self._move_focus(1,0)
+ return True
+ elif e.key == K_DOWN:
+ self._move_focus(0,1)
+ return True
+ elif e.key == K_LEFT:
+ self._move_focus(-1,0)
+ return True
+ return used
+
+ def _move_focus(self,dx_,dy_):
+ myfocus = self.myfocus
+ if not self.myfocus: return
+
+ widgets = self._get_widgets(pguglobals.app)
+ #if myfocus not in widgets: return
+ #widgets.remove(myfocus)
+ if myfocus in widgets:
+ widgets.remove(myfocus)
+ rect = myfocus.get_abs_rect()
+ fx,fy = rect.centerx,rect.centery
+
+ def sign(v):
+ if v < 0: return -1
+ if v > 0: return 1
+ return 0
+
+ dist = []
+ for w in widgets:
+ wrect = w.get_abs_rect()
+ wx,wy = wrect.centerx,wrect.centery
+ dx,dy = wx-fx,wy-fy
+ if dx_ > 0 and wrect.left < rect.right: continue
+ if dx_ < 0 and wrect.right > rect.left: continue
+ if dy_ > 0 and wrect.top < rect.bottom: continue
+ if dy_ < 0 and wrect.bottom > rect.top: continue
+ dist.append((dx*dx+dy*dy,w))
+ if not len(dist): return
+ dist.sort()
+ d,w = dist.pop(0)
+ w.focus()
+
+ def _get_widgets(self,c):
+ widgets = []
+ if c.mywindow:
+ widgets.extend(self._get_widgets(c.mywindow))
+ else:
+ for w in c.widgets:
+ if isinstance(w,Container):
+ widgets.extend(self._get_widgets(w))
+ elif not w.disabled and w.focusable:
+ widgets.append(w)
+ return widgets
+
+ def remove(self,w):
+ """Remove a widget from the container.
+
+ <pre>Container.remove(w)</pre>
+ """
+ self.blur(w)
+ self.widgets.remove(w)
+ #self.repaint()
+ self.chsize()
+
+ def add(self,w,x,y):
+ """Add a widget to the container.
+
+ <pre>Container.add(w,x,y)</pre>
+
+ <dl>
+ <dt>x, y<dd>position of the widget
+ </dl>
+ """
+ w.style.x = x
+ w.style.y = y
+ w.container = self
+ #NOTE: this might fix it, sort of...
+ #but the thing is, we don't really want to resize
+ #something if it is going to get resized again later
+ #for no reason...
+ #w.rect.x,w.rect.y = w.style.x,w.style.y
+ #w.rect.w, w.rect.h = w.resize()
+ self.widgets.append(w)
+ self.chsize()
+
+ def open(self,w=None,x=None,y=None):
+ if (not w):
+ w = self
+
+ if (x != None):
+ # The position is relative to this container
+ rect = self.get_abs_rect()
+ pos = (rect.x + x, rect.y + y)
+ else:
+ pos = None
+ # Have the application open the window
+ pguglobals.app.open(w, pos)
+
+ def focus(self,w=None):
+ widget.Widget.focus(self) ### by Gal koren
+# if not w:
+# return widget.Widget.focus(self)
+ if not w: return
+ if self.myfocus: self.blur(self.myfocus)
+ if self.myhover is not w: self.enter(w)
+ self.myfocus = w
+ w._event(pygame.event.Event(FOCUS))
+
+ #print self.myfocus,self.myfocus.__class__.__name__
+
+ def blur(self,w=None):
+ if not w:
+ return widget.Widget.blur(self)
+ if self.myfocus is w:
+ if self.myhover is w: self.exit(w)
+ self.myfocus = None
+ w._event(pygame.event.Event(BLUR))
+
+ def enter(self,w):
+ if self.myhover: self.exit(self.myhover)
+ self.myhover = w
+ w._event(pygame.event.Event(ENTER))
+
+ def exit(self,w):
+ if self.myhover and self.myhover is w:
+ self.myhover = None
+ w._event(pygame.event.Event(EXIT))
+
+
+# def first(self):
+# for w in self.widgets:
+# if w.focusable:
+# self.focus(w)
+# return
+# if self.container: self.container.next(self)
+
+# def next(self,w):
+# if w not in self.widgets: return #HACK: maybe. this happens in windows for some reason...
+#
+# for w in self.widgets[self.widgets.index(w)+1:]:
+# if w.focusable:
+# self.focus(w)
+# return
+# if self.container: return self.container.next(self)
+
+
+ def _next(self,orig=None):
+ start = 0
+ if orig in self.widgets: start = self.widgets.index(orig)+1
+ for w in self.widgets[start:]:
+ if not w.disabled and w.focusable:
+ if isinstance(w,Container):
+ if w._next():
+ return True
+ else:
+ self.focus(w)
+ return True
+ return False
+
+ def _previous(self,orig=None):
+ end = len(self.widgets)
+ if orig in self.widgets: end = self.widgets.index(orig)
+ ws = self.widgets[:end]
+ ws.reverse()
+ for w in ws:
+ if not w.disabled and w.focusable:
+ if isinstance(w,Container):
+ if w._previous():
+ return True
+ else:
+ self.focus(w)
+ return True
+ return False
+
+ def next(self,w=None):
+ if w != None and w not in self.widgets: return #HACK: maybe. this happens in windows for some reason...
+
+ if self._next(w): return True
+ if self.container: return self.container.next(self)
+
+
+ def previous(self,w=None):
+ if w != None and w not in self.widgets: return #HACK: maybe. this happens in windows for some reason...
+
+ if self._previous(w): return True
+ if self.container: return self.container.previous(self)
+
+ def resize(self,width=None,height=None):
+ #r = self.rect
+ #r.w,r.h = 0,0
+ ww,hh = 0,0
+ if self.style.width: ww = self.style.width
+ if self.style.height: hh = self.style.height
+
+ for w in self.widgets:
+ #w.rect.w,w.rect.h = 0,0
+ w.rect.x,w.rect.y = w.style.x,w.style.y
+ w.rect.w, w.rect.h = w.resize()
+ #w._resize()
+
+ ww = max(ww,w.rect.right)
+ hh = max(hh,w.rect.bottom)
+ return ww,hh
+
+ # Returns the widget with the given name
+ def find(self, name):
+ for w in self.widgets:
+ if (w.name == name):
+ return w
+ elif (isinstance(w, Container)):
+ tmp = w.find(name)
+ if (tmp): return tmp
+ return None
+
--- /dev/null
+import pygame
+
+from const import *
+import table
+import group
+import button, basic
+import pguglobals
+
+def action_open(value):
+ print 'gui.action_open',"Scheduled to be deprecated."
+ value.setdefault('x',None)
+ value.setdefault('y',None)
+ value['container'].open(value['window'],value['x'],value['y'])
+
+def action_setvalue(value):
+ print 'gui.action_setvalue',"Scheduled to be deprecated."
+ a,b = value
+ b.value = a.value
+
+def action_quit(value):
+ print 'gui.action_quit',"Scheduled to be deprecated."
+ value.quit()
+
+def action_exec(value):
+ print 'gui.action_exec',"Scheduled to be deprecated."
+ exec(value['script'],globals(),value['dict'])
+
+class Toolbox(table.Table):
+ def __setattr__(self,k,v):
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self.group.value = v
+ for w in self.group.widgets:
+ if w.value != v: w.pcls = ""
+ else: w.pcls = "down"
+ self.repaint()
+
+ def _change(self,value):
+ self.value = self.group.value
+ self.send(CHANGE)
+
+ def __init__(self,data,cols=0,rows=0,tool_cls='tool',value=None,**params):
+ print 'gui.Toolbox','Scheduled to be deprecated.'
+ params.setdefault('cls','toolbox')
+ table.Table.__init__(self,**params)
+
+ if cols == 0 and rows == 0: cols = len(data)
+ if cols != 0 and rows != 0: rows = 0
+
+ self.tools = {}
+
+ _value = value
+
+ g = group.Group()
+ self.group = g
+ g.connect(CHANGE,self._change,None)
+ self.group.value = _value
+
+ x,y,p,s = 0,0,None,1
+ for ico,value in data:
+ #from __init__ import theme
+ img = pguglobals.app.theme.get(tool_cls+"."+ico,"","image")
+ if img:
+ i = basic.Image(img)
+ else: i = basic.Label(ico,cls=tool_cls+".label")
+ p = button.Tool(g,i,value,cls=tool_cls)
+ self.tools[ico] = p
+ #p.style.hexpand = 1
+ #p.style.vexpand = 1
+ self.add(p,x,y)
+ s = 0
+ if cols != 0: x += 1
+ if cols != 0 and x == cols: x,y = 0,y+1
+ if rows != 0: y += 1
+ if rows != 0 and y == rows: x,y = x+1,0
--- /dev/null
+"""
+"""
+import os
+
+from const import *
+import table, area
+import basic, input, button
+import pguglobals
+
+class Dialog(table.Table):
+ """A dialog window with a title bar and an "close" button on the bar.
+
+ <pre>Dialog(title,main)</pre>
+
+ <dl>
+ <dt>title<dd>title widget, usually a label
+ <dt>main<dd>main widget, usually a container
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ title = gui.Label("My Title")
+ main = gui.Container()
+ #add stuff to the container...
+
+ d = gui.Dialog(title,main)
+ d.open()
+ </code>
+ """
+ def __init__(self,title,main,**params):
+ params.setdefault('cls','dialog')
+ table.Table.__init__(self,**params)
+
+
+ self.tr()
+ self.td(title,align=-1,cls=self.cls+'.bar')
+ clos = button.Icon(self.cls+".bar.close")
+ clos.connect(CLICK,self.close,None)
+ self.td(clos,align=1,cls=self.cls+'.bar')
+
+ self.tr()
+ self.td(main,colspan=2,cls=self.cls+".main")
+
+
+# self.tr()
+#
+#
+# t = table.Table(cls=self.cls+".bar")
+# t.tr()
+# t.td(title)
+# clos = button.Icon(self.cls+".bar.close")
+# t.td(clos,align=1)
+# clos.connect(CLICK,self.close,None)
+# self.add(t,0,0)
+#
+# main.rect.w,main.rect.h = main.resize()
+# clos.rect.w,clos.rect.h = clos.resize()
+# title.container.style.width = main.rect.w - clos.rect.w
+#
+# self.tr()
+# self.td(main,cls=self.cls+".main")
+#
+
+
+class FileDialog(Dialog):
+ """A file picker dialog window.
+
+ <pre>FileDialog()</pre>
+ <p>Some optional parameters:</p>
+ <dl>
+ <dt>title_txt<dd>title text
+ <dt>button_txt<dd>button text
+ <dt>path<dd>initial path
+ </dl>
+ """
+
+ def __init__(self, title_txt="File Browser", button_txt="Okay", cls="dialog", folderText = "Folder", fileText = "File", path=None, customFont = None, showCurDir = True, customWidth = 350, customHeight = 150):
+
+ self.customFont = customFont
+ self.showCurDir= showCurDir
+ cls1 = 'filedialog'
+ if not path: self.curdir = os.getcwd()
+ else: self.curdir = path
+ self.dir_img = basic.Image(
+ pguglobals.app.theme.get(cls1+'.folder', '', 'image'))
+ td_style = {'padding_left': 4,
+ 'padding_right': 4,
+ 'padding_top': 2,
+ 'padding_bottom': 2}
+ self.title = basic.Label(title_txt, cls=cls+".title.label")
+ self.body = table.Table()
+ self.list = area.List(width=customWidth, height=customHeight)
+ self.input_dir = input.Input(customFont = self.customFont)
+ self.input_file = input.Input(customFont = self.customFont)
+ self._list_dir_()
+ self.button_ok = button.Button(button_txt)
+ self.body.tr()
+ if self.showCurDir :
+ self.body.td(basic.Label(folderText), style=td_style, align=-1)
+ self.body.td(self.input_dir, style=td_style)
+ self.body.tr()
+ self.body.td(self.list, colspan=3, style=td_style)
+ self.list.connect(CHANGE, self._item_select_changed_, None)
+ self.button_ok.connect(CLICK, self._button_okay_clicked_, None)
+ self.body.tr()
+ self.body.td(basic.Label(fileText), style=td_style, align=-1)
+ self.body.td(self.input_file, style=td_style)
+ self.body.td(self.button_ok, style=td_style)
+ self.value = None
+ Dialog.__init__(self, self.title, self.body)
+
+ def _list_dir_(self):
+ self.input_dir.value = self.curdir
+ self.input_dir.pos = len(self.curdir)
+ self.input_dir.vpos = 0
+ dirs = []
+ files = []
+ try:
+ for i in os.listdir(self.curdir):
+ if os.path.isdir(os.path.join(self.curdir, i)): dirs.append(i)
+ else: files.append(i)
+ except:
+ self.input_file.value = "Opps! no access"
+ #if '..' not in dirs: dirs.append('..')
+ dirs.sort()
+ dirs = ['..'] + dirs
+
+ files.sort()
+ for i in dirs:
+ #item = ListItem(image=self.dir_img, text=i, value=i)
+ if self.customFont == None :
+ self.list.add(i,image=self.dir_img,value=i)
+ else :
+ label = basic.Label(i,font = self.customFont)
+ self.list.add(label,image=self.dir_img,value=i)
+ for i in files:
+ #item = ListItem(image=None, text=i, value=i)
+ if self.customFont == None :
+ self.list.add(i,value=i)
+ else:
+ label = basic.Label(i,font = self.customFont)
+ self.list.add(label,value=i)
+ #self.list.resize()
+ self.list.set_vertical_scroll(0)
+ #self.list.repaintall()
+
+
+ def _item_select_changed_(self, arg):
+ self.input_file.value = self.list.value
+ fname = os.path.abspath(os.path.join(self.curdir, self.input_file.value))
+ if os.path.isdir(fname):
+ self.input_file.value = ""
+ self.curdir = fname
+ self.list.clear()
+ self._list_dir_()
+
+
+ def _button_okay_clicked_(self, arg):
+ if self.input_dir.value != self.curdir:
+ if os.path.isdir(self.input_dir.value):
+ self.input_file.value = ""
+ self.curdir = os.path.abspath(self.input_dir.value)
+ self.list.clear()
+ self._list_dir_()
+ else:
+ self.value = os.path.join(self.curdir, self.input_file.value)
+ self.send(CHANGE)
+ self.close()
--- /dev/null
+"""
+"""
+import pygame
+
+import container
+import layout
+
+class _document_widget:
+ def __init__(self,w,align=None):
+ #w.rect.w,w.rect.h = w.resize()
+ #self.rect = w.rect
+ self.widget = w
+ if align != None: self.align = align
+
+class Document(container.Container):
+ """A document container contains many widgets strung together in a document format. (How informative!)
+
+ <pre>Document()</pre>
+
+ """
+ def __init__(self,**params):
+ params.setdefault('cls','document')
+ container.Container.__init__(self,**params)
+ self.layout = layout.Layout(pygame.Rect(0,0,self.rect.w,self.rect.h))
+
+ def add(self,e,align=None):
+ """Add a widget.
+
+ <pre>Document.add(e,align=None)</pre>
+
+ <dl>
+ <dt>e<dd>widget
+ <dt>align<dd>alignment (None,-1,0,1)
+ </dl>
+ """
+ dw = _document_widget(e,align)
+ self.layout.add(dw)
+ e.container = self
+ e._c_dw = dw
+ self.widgets.append(e)
+ self.chsize()
+
+ def remove(self,e):
+ self.layout._widgets.remove(e._c_dw)
+ self.widgets.remove(e)
+ self.chsize()
+
+
+ def block(self,align):
+ """Start a new block.
+
+ <pre>Document.block(align)</pre>
+
+ <dl>
+ <dt>align<dd>alignment of block (-1,0,1)
+ </dl>
+ """
+ self.layout.add(align)
+
+ def space(self,e):
+ """Add a spacer.
+
+ <pre>Document.space(e)</pre>
+
+ <dl>
+ <dt>e<dd>a (w,h) size for the spacer
+ </dl>
+ """
+ self.layout.add(e)
+
+ def br(self,height):
+ """Add a line break.
+
+ <pre>Document.br(height)</pre>
+
+ <dl>
+ <dt>height<dd>height of line break
+ </dl>
+ """
+ self.layout.add((0,height))
+
+ def resize(self,width=None,height=None):
+ if self.style.width: width = self.style.width
+ if self.style.height: height = self.style.height
+
+ for w in self.widgets:
+ w.rect.w,w.rect.h = w.resize()
+
+ if (width != None and w.rect.w > width) or (height != None and w.rect.h > height):
+ w.rect.w,w.rect.h = w.resize(width,height)
+
+ dw = w._c_dw
+ dw.rect = pygame.Rect(0,0,w.rect.w,w.rect.h)
+
+ if width == None: width = 65535
+ self.layout.rect = pygame.Rect(0,0,width,0)
+ self.layout.resize()
+
+ _max_w = 0
+
+ for w in self.widgets:
+ #xt,xl,xb,xr = w.getspacing()
+ dw = w._c_dw
+ w.style.x,w.style.y,w.rect.w,w.rect.h = dw.rect.x,dw.rect.y,dw.rect.w,dw.rect.h
+ #w.resize()
+ w.rect.x,w.rect.y = w.style.x,w.style.y
+ _max_w = max(_max_w,w.rect.right)
+
+ #self.rect.w = _max_w #self.layout.rect.w
+ #self.rect.h = self.layout.rect.h
+ #print 'document',_max_w,self.layout.rect.h
+ return _max_w,self.layout.rect.h
--- /dev/null
+"""
+"""
+import widget
+
+class Form(widget.Widget):
+ """A form that automatically will contain all named widgets.
+
+ <p>After a form is created, all named widget that are subsequently created are added
+ to that form. You may use dict style access to access named widgets.</p>
+
+ <pre>Form()</pre>
+
+ <strong>Example</strong>
+ <code>
+ f = gui.Form()
+
+ w = gui.Input("Phil",name="firstname")
+ w = gui.Input("Hassey",name="lastname")
+
+ print f.results()
+ print ''
+ print f.items()
+ print ''
+ print f['firstname'].value
+ print f['lastname'].value
+ </code>
+ """
+
+ def __init__(self):
+ widget.Widget.__init__(self,decorate=False)
+ self._elist = []
+ self._emap = {}
+ self._dirty = 0
+ Form.form = self
+
+ def add(self,e,name=None,value=None):
+ if name != None: e.name = name
+ if value != None: e.value = value
+ self._elist.append(e)
+ self._dirty = 1
+
+ def _clean(self):
+ for e in self._elist[:]:
+ if not hasattr(e,'name') or e.name == None:
+ self._elist.remove(e)
+ self._emap = {}
+ for e in self._elist:
+ self._emap[e.name] = e
+ self._dirty = 0
+
+ def __getitem__(self,k):
+ if self._dirty: self._clean()
+ return self._emap[k]
+
+ def __contains__(self,k):
+ if self._dirty: self._clean()
+ if k in self._emap: return True
+ return False
+
+ def results(self):
+ """Return a dict of name => values.
+
+ <pre>Form.results(): return dict</pre>
+ """
+ if self._dirty: self._clean()
+ r = {}
+ for e in self._elist:
+ r[e.name] = e.value
+ return r
+
+ def items(self):
+ """Return a list of name, value keys.
+
+ <pre>Form.items(): return list</pre>
+ """
+ return self.results().items()
+
+ #def start(self):
+ # Object.start(self,-1)
--- /dev/null
+"""
+"""
+from const import *
+import widget
+
+class Group(widget.Widget):
+ """An object for grouping together Form elements.
+
+ <pre>Group(name=None,value=None)</pre>
+
+ <dl>
+ <dt>name<dd>name as used in the Form
+ <dt>value<dd>values that are currently selected in the group
+ </dl>
+
+ <p>See [[gui-button]] for several examples.</p>
+
+ <p>When the value changes, an <tt>gui.CHANGE</tt> event is sent.
+ Although note, that when the value is a list, it may have to be sent by hand via
+ <tt>g.send(gui.CHANGE)</tt></p>
+ """
+
+ def __init__(self,name=None,value=None):
+ widget.Widget.__init__(self,name=name,value=value)
+ self.widgets = []
+
+ def add(self,w):
+ """Add a widget to this group.
+
+ <pre>Group.add(w)</pre>
+ """
+ self.widgets.append(w)
+
+ def __setattr__(self,k,v):
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k] = v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self._change()
+
+ def _change(self):
+ self.send(CHANGE)
+ for w in self.widgets:
+ w.repaint()
--- /dev/null
+"""
+"""
+import pygame
+from pygame.locals import *
+
+from const import *
+import widget
+
+class Input(widget.Widget):
+ """A single line text input.
+
+ <pre>Input(value="",size=20)</pre>
+
+ <dl>
+ <dt>value<dd>initial text
+ <dt>size<dd>size for the text box, in characters
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = Input(value="Cuzco the Goat",size=20)
+
+ w = Input("Marbles")
+ </code>
+
+ """
+ def __init__(self,value="",size=20,customFont = None,**params):
+ params.setdefault('cls','input')
+ widget.Widget.__init__(self,**params)
+ self.value = value
+ self.pos = len(str(value))
+ self.vpos = 0
+ if customFont != None:
+ self.font = customFont
+ else:
+ self.font = self.style.font
+ w,h = self.font.size("e"*size)
+ if not self.style.height: self.style.height = h
+ if not self.style.width: self.style.width = w
+ #self.style.height = max(self.style.height,h)
+ #self.style.width = max(self.style.width,w)
+ #self.rect.w=w+self.style.padding_left+self.style.padding_right;
+ #self.rect.h=h+self.style.padding_top+self.style.padding_bottom;
+
+ def paint(self,s):
+
+ r = pygame.Rect(0,0,self.rect.w,self.rect.h)
+
+ cs = 2 #NOTE: should be in a style
+
+ w,h = self.font.size(self.value[0:self.pos])
+ x = w-self.vpos
+ if x < 0: self.vpos -= -x
+ if x+cs > s.get_width(): self.vpos += x+cs-s.get_width()
+
+ s.blit(self.font.render(self.value, 1, self.style.color),(-self.vpos,0))
+
+ if self.container.myfocus is self:
+ w,h = self.font.size(self.value[0:self.pos])
+ r.x = w-self.vpos
+ r.w = cs
+ r.h = h
+ s.fill(self.style.color,r)
+
+ def _setvalue(self,v):
+ self.__dict__['value'] = v
+ self.send(CHANGE)
+
+ def event(self,e):
+ used = None
+ if e.type == KEYDOWN:
+ if e.key == K_BACKSPACE:
+ if self.pos:
+ self._setvalue(self.value[:self.pos-1] + self.value[self.pos:])
+ self.pos -= 1
+ elif e.key == K_DELETE:
+ if len(self.value) > self.pos:
+ self._setvalue(self.value[:self.pos] + self.value[self.pos+1:])
+ elif e.key == K_HOME:
+ self.pos = 0
+ elif e.key == K_END:
+ self.pos = len(self.value)
+ elif e.key == K_LEFT:
+ if self.pos > 0: self.pos -= 1
+ used = True
+ elif e.key == K_RIGHT:
+ if self.pos < len(self.value): self.pos += 1
+ used = True
+ elif e.key == K_RETURN:
+ self.next()
+ elif e.key == K_TAB:
+ pass
+ else:
+ #c = str(e.unicode)
+ try:
+ c = (e.unicode).encode('latin-1')
+ if c:
+ self._setvalue(self.value[:self.pos] + c + self.value[self.pos:])
+ self.pos += 1
+ except: #ignore weird characters
+ pass
+ self.repaint()
+ elif e.type == FOCUS:
+ self.repaint()
+ elif e.type == BLUR:
+ self.repaint()
+
+ self.pcls = ""
+ if self.container.myfocus is self: self.pcls = "focus"
+
+ return used
+
+ def __setattr__(self,k,v):
+ if k == 'value':
+ if v == None: v = ''
+ v = str(v)
+ self.pos = len(v)
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self.send(CHANGE)
+ self.repaint()
+
+class Password(Input):
+ """A password input, text is *-ed out.
+
+ <pre>Password(value="",size=20)</pre>
+
+ <dl>
+ <dt>value<dd>initial text
+ <dt>size<dd>size for the text box, in characters
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = Password(value="password",size=20)
+
+ w = Password("53[r3+")
+ </code>
+
+ """
+
+ def paint(self,s):
+ hidden="*"
+ show=len(self.value)*hidden
+
+ #print "self.value:",self.value
+
+ if self.pos == None: self.pos = len(self.value)
+
+ r = pygame.Rect(0,0,self.rect.w,self.rect.h)
+
+ cs = 2 #NOTE: should be in a style
+
+ w,h = self.font.size(show)
+ x = w-self.vpos
+ if x < 0: self.vpos -= -x
+ if x+cs > s.get_width(): self.vpos += x+cs-s.get_width()
+
+ s.blit(self.font.render(show, 1, self.style.color),(-self.vpos,0))
+
+ if self.container.myfocus is self:
+ #w,h = self.font.size(self.value[0:self.pos])
+ w,h = self.font.size(show[0:self.pos])
+ r.x = w-self.vpos
+ r.w = cs
+ r.h = h
+ s.fill(self.style.color,r)
+
--- /dev/null
+"""
+"""
+import pygame
+from pygame.locals import *
+
+from const import *
+import widget
+
+class Keysym(widget.Widget):
+ """A keysym input.
+
+ <p>This widget records the keysym of the key pressed while this widget is in focus.</p>
+
+ <pre>Keysym(value=None)</pre>
+
+ <dl>
+ <dt>value<dd>initial keysym, see <a href="http://www.pygame.org/docs/ref/key.html">pygame keysyms</a> </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = Input(value=pygame.locals.K_g)
+
+ w = Input(pygame.locals.K_g)
+
+ w = Input()
+ </code>
+
+ """
+
+ def __init__(self,value=None,**params):
+ params.setdefault('cls','keysym')
+ widget.Widget.__init__(self,**params)
+ self.value = value
+
+ self.font = self.style.font
+ w,h = self.font.size("Right Super") #"Right Shift")
+ self.style.width,self.style.height = w,h
+ #self.rect.w=w+self.style.padding_left+self.style.padding_right
+ #self.rect.h=h+self.style.padding_top+self.style.padding_bottom
+
+ def event(self,e):
+ used = None
+ if e.type == FOCUS or e.type == BLUR: self.repaint()
+ elif e.type == KEYDOWN:
+ if e.key != K_TAB:
+ self.value = e.key
+ self.repaint()
+ self.send(CHANGE)
+ used = True
+ self.next()
+ self.pcls = ""
+ if self.container.myfocus is self: self.pcls = "focus"
+ return used
+
+ def paint(self,s):
+ r = pygame.rect.Rect(0,0,self.rect.w,self.rect.h)
+ #render_box(s,self.style.background,r)
+ if self.value == None: return
+ name = ""
+ for p in pygame.key.name(self.value).split(): name += p.capitalize()+" "
+ #r.x = self.style.padding_left;
+ #r.y = self.style.padding_bottom;
+ s.blit(self.style.font.render(name, 1, self.style.color), r)
+
+ def __setattr__(self,k,v):
+ if k == 'value' and v != None:
+ v = int(v)
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self.send(CHANGE)
+ self.repaint()
--- /dev/null
+"""document layout engine."""
+class Layout:
+ """the document layout engine
+
+ .widgets -- elements are kept in this list. read-only, use add to add items to it.
+ """
+
+ def __init__(self,rect=None):
+ """initialize the object with the size of the box."""
+ self._widgets = []
+ self.rect = rect
+
+ def add(self,e):
+ """add a document element to the layout.
+
+ a document element may be
+ - a tuple (w,h) if it is a whitespace element
+ - a tuple (0,h) if it is a linebreak element
+ - an integer -1,0,1 if it is a command to start a new block of elements that are aligned either left,center, or right.
+ - an object with a .rect (for size) -- such as a word element
+ - an object with a .rect (for size) and .align -- such as an image element
+ """
+
+ self._widgets.append(e)
+
+
+ def resize(self):
+ """resize the layout
+ this method recalculates the position of all document elements
+ after they have been added to the document. .rect.x,y will be updated for all
+ objects.
+ """
+ self.init()
+ self.widgets = []
+ for e in self._widgets:
+ if type(e) is tuple and e[0] != 0:
+ self.do_space(e)
+ elif type(e) is tuple and e[0] == 0:
+ self.do_br(e[1])
+ elif type(e) is int:
+ self.do_block(align=e)
+ elif hasattr(e,'align'):
+ self.do_align(e)
+ else:
+ self.do_item(e)
+ self.line()
+ self.rect.h = max(self.y,self.left_bottom,self.right_bottom)
+
+ def init(self):
+ self.x,self.y = self.rect.x,self.rect.y
+ self.left = self.rect.left
+ self.right = self.rect.right
+ self.left_bottom = 0
+ self.right_bottom = 0
+ self.y = self.rect.y
+ self.x = self.rect.x
+ self.h = 0
+
+ self.items = []
+ self.align = -1
+
+ def getleft(self):
+ if self.y > self.left_bottom:
+ self.left = self.rect.left
+ return self.left
+
+ def getright(self):
+ if self.y > self.right_bottom:
+ self.right = self.rect.right
+ return self.right
+
+ def do_br(self,h):
+ self.line()
+ self.h = h
+
+ def do_block(self,align=-1):
+ self.line()
+ self.align = align
+
+ def do_align(self,e):
+ align = e.align
+ ox,oy,oh = self.x,self.y,self.h
+ w,h = e.rect.w,e.rect.h
+
+ if align == 0:
+ self.line()
+ self.x = self.rect.left + (self.rect.width-w)/2
+ self.fit = 0
+ elif align == -1:
+ self.line()
+ self.y = max(self.left_bottom,self.y + self.h)
+ self.h = 0
+ self.x = self.rect.left
+ elif align == 1:
+ self.line()
+ self.y = max(self.right_bottom,self.y + self.h)
+ self.h = 0
+ self.x = self.rect.left + (self.rect.width-w)
+
+ e.rect.x,e.rect.y = self.x,self.y
+
+ self.x = self.x + w
+ self.y = self.y
+
+ if align == 0:
+ self.h = max(self.h,h)
+ self.y = self.y + self.h
+ self.x = self.getleft()
+ self.h = 0
+ elif align == -1:
+ self.left = self.x
+ self.left_bottom = self.y + h
+ self.x,self.y,self.h = ox + w,oy,oh
+ elif align == 1:
+ self.right = self.x - w
+ self.right_bottom = self.y + h
+ self.x,self.y,self.h = ox,oy,oh
+
+ self.widgets.append(e)
+
+ def do_space(self,e):
+ w,h = e
+ if self.x+w >= self.getright():
+ self.line()
+ else:
+ self.items.append(e)
+ self.h = max(self.h,h)
+ self.x += w
+
+ def do_item(self,e):
+ w,h = e.rect.w,e.rect.h
+ if self.x+w >= self.getright():
+ self.line()
+ self.items.append(e)
+ self.h = max(self.h,h)
+ self.x += w
+
+ def line(self):
+ x1 = self.getleft()
+ x2 = self.getright()
+ align = self.align
+ y = self.y
+
+ if len(self.items) != 0 and type(self.items[-1]) == tuple:
+ del self.items[-1]
+
+ w = 0
+ for e in self.items:
+ if type(e) == tuple: w += e[0]
+ else: w += e.rect.w
+
+ if align == -1: x = x1
+ elif align == 0:
+ x = x1 + ((x2-x1)-w)/2
+ self.fit = 0
+ elif align == 1: x = x2 - w
+
+ for e in self.items:
+ if type(e) == tuple: x += e[0]
+ else:
+ e.rect.x,e.rect.y = x,y
+ self.widgets.append(e)
+ x += e.rect.w
+
+ self.items = []
+ self.y = self.y + self.h
+ self.x = self.getleft()
+ self.h = 0
+
+
+
+# vim: set filetype=python sts=4 sw=4 noet si :
--- /dev/null
+"""
+"""
+from const import *
+import table
+import basic, button
+
+class _Menu_Options(table.Table):
+ def __init__(self,menu,**params):
+ table.Table.__init__(self,**params)
+
+ self.menu = menu
+
+ def event(self,e):
+ handled = False
+ arect = self.get_abs_rect()
+
+ if e.type == MOUSEMOTION:
+ abspos = e.pos[0]+arect.x,e.pos[1]+arect.y
+ for w in self.menu.container.widgets:
+ if not w is self.menu:
+ mrect = w.get_abs_rect()
+ if mrect.collidepoint(abspos):
+ self.menu._close(None)
+ w._open(None)
+ handled = True
+
+ if not handled: table.Table.event(self,e)
+
+class _Menu(button.Button):
+ def __init__(self,parent,widget=None,**params): #TODO widget= could conflict with module widget
+ params.setdefault('cls','menu')
+ button.Button.__init__(self,widget,**params)
+
+ self.parent = parent
+
+ self._cls = self.cls
+ self.options = _Menu_Options(self, cls=self.cls+".options")
+
+ self.connect(CLICK,self._open,None)
+
+ self.pos = 0
+
+ def _open(self,value):
+ self.parent.value = self
+ self.pcls = 'down'
+
+ self.repaint()
+ self.container.open(self.options,self.rect.x,self.rect.bottom)
+ self.options.connect(BLUR,self._close,None)
+ self.options.focus()
+ self.repaint()
+
+ def _pass(self,value):
+ pass
+
+ def _close(self,value):
+ self.pcls = ''
+ self.parent.value = None
+ self.repaint()
+ self.options.close()
+
+ def _value(self,value):
+ self._close(None)
+ if value['fnc'] != None:
+ value['fnc'](value['value'])
+
+ def event(self,e):
+ button.Button.event(self,e)
+
+ if self.parent.value == self:
+ self.pcls = 'down'
+
+ def add(self,w,fnc=None,value=None):
+ w.style.align = -1
+ b = button.Button(w,cls=self.cls+".option")
+ b.connect(CLICK,self._value,{'fnc':fnc,'value':value})
+
+ self.options.tr()
+ self.options.add(b)
+
+ return b
+
+class Menus(table.Table):
+ """A drop down menu bar.
+
+ <pre>Menus(data)</pre>
+
+ <dl>
+ <dt>data<dd>Menu data, a list of (path,fnc,value), see example below
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ data = [
+ ('File/Save',fnc_save,None),
+ ('File/New',fnc_new,None),
+ ('Edit/Copy',fnc_copy,None),
+ ('Edit/Cut',fnc_cut,None),
+ ('Help/About',fnc_help,help_about_content),
+ ('Help/Reference',fnc_help,help_reference_content),
+ ]
+ w = Menus(data)
+ """
+
+ def __init__(self,data,menu_cls='menu',**params):
+ params.setdefault('cls','menus')
+ table.Table.__init__(self,**params)
+
+ self.value = None
+
+ n,m,mt = 0,None,None
+ for path,cmd,value in data:
+ parts = path.split("/")
+ if parts[0] != mt:
+ mt = parts[0]
+ m = _Menu(self,basic.Label(mt,cls=menu_cls+".label"),cls=menu_cls)
+ self.add(m,n,0)
+ n += 1
+ m.add(basic.Label(parts[1],cls=m.cls+".option.label"),cmd,value)
--- /dev/null
+from const import *
+import widget
+import pguglobals
+
+class ProgressBar(widget.Widget):
+ """A progress bar.
+
+ <pre>ProgressBar(value,min,max)</pre>
+
+ <dl>
+ <dt>value<dd>starting value
+ <dt>min<dd>minimum value rendered on the screen (usually 0)
+ <dt>max<dd>maximum value
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = gui.ProgressBar(0,0,100)
+ w.value = 25
+ </code>
+ """
+
+ def __init__(self,value,min,max,**params):
+ params.setdefault('cls','progressbar')
+ widget.Widget.__init__(self,**params)
+ self.min,self.max,self.value = min,max,value
+
+ def paint(self,s):
+ r = pygame.rect.Rect(0,0,self.rect.w,self.rect.h)
+ r.w = r.w*(self.value-self.min)/(self.max-self.min)
+ self.bar = r
+ pguglobals.app.theme.render(s,self.style.bar,r)
+
+ def __setattr__(self,k,v):
+ if k == 'value':
+ v = int(v)
+ v = max(v,self.min)
+ v = min(v,self.max)
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self.send(CHANGE)
+ self.repaint()
--- /dev/null
+# pguglobals.py - A place to stick global variables that need to be accessed
+# from other modules. To avoid problems with circular imports
+# this module should not import any other PGU module.
+
+# A global reference to the application instance (App class)
+app = None
+
--- /dev/null
+"""
+"""
+
+import traceback
+
+from const import *
+from button import Button
+from basic import Label, Image
+from table import Table
+
+class Select(Table):
+ """A select input.
+
+ <pre>Select(value=None)</pre>
+
+ <dl>
+ <dt>value<dd>initial value
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = Select(value="goats")
+ w.add("Cats","cats")
+ w.add("Goats","goats")
+ w.add("Dogs","Dogs")
+
+ w.value = 'dogs' #changes the value from goats to dogs
+ </code>
+
+ """
+
+ # The drop-down arrow button for the selection widget
+ top_arrow = None
+ # A button displaying the currently selected item
+ top_selection = None
+ # The first option added to the selector
+ firstOption = None
+ # The PGU table of options
+ options = None
+
+ def __init__(self,value=None,**params):
+ params.setdefault('cls','select')
+ Table.__init__(self,**params)
+
+ label = Label(" ",cls=self.cls+".option.label")
+ self.top_selected = Button(label, cls=self.cls+".selected")
+ Table.add(self,self.top_selected) #,hexpand=1,vexpand=1)#,0,0)
+
+ self.top_arrow = Button(Image(self.style.arrow), cls=self.cls+".arrow")
+ Table.add(self,self.top_arrow) #,hexpand=1,vexpand=1) #,1,0)
+
+ self.options = Table(cls=self.cls+".options")
+ self.options.connect(BLUR,self._close,None)
+ self.options.name = "pulldown-table"
+
+ self.values = []
+ self.value = value
+
+ def resize(self,width=None,height=None):
+ max_w,max_h = 0,0
+ for w in self.options.widgets:
+ w.rect.w,w.rect.h = w.resize()
+ max_w,max_h = max(max_w,w.rect.w),max(max_h,w.rect.h)
+
+ #xt,xr,xb,xl = self.top_selected.getspacing()
+ self.top_selected.style.width = max_w #+ xl + xr
+ self.top_selected.style.height = max_h #+ xt + xb
+
+ self.top_arrow.connect(CLICK,self._open,None)
+ self.top_selected.connect(CLICK,self._open,None)
+
+ w,h = Table.resize(self,width,height)
+
+ self.options.style.width = w
+ #HACK: sort of, but not a big one..
+ self.options.resize()
+
+ return w,h
+
+ def _open(self,value):
+ opts = self.options
+
+ opts.rect.w, opts.rect.h = opts.resize()
+
+# y = self.rect.y
+# c = self.container
+# while hasattr(c, 'container'):
+# y += c.rect.y
+# if (not c.container):
+# break
+# c = c.container
+
+# if y + self.rect.h + opts.rect.h <= c.rect.h: #down
+# dy = self.rect.y + self.rect.h
+# else: #up
+# dy = self.rect.y - self.rect.h
+
+ opts.rect.w, opts.rect.h = opts.resize()
+
+ # TODO - make sure there is enough space to open down
+ # ...
+ yp = self.rect.bottom-1
+
+ self.container.open(opts, self.rect.x, yp)
+ self.firstOption.focus()
+
+ # TODO - this is a hack
+ for opt in self.options.widgets:
+ opt.repaint()
+
+ def _close(self,value):
+ self.options.close()
+ self.top_selected.focus()
+
+ def _setvalue(self,value):
+ self.value = value._value
+ if hasattr(self,'container'):
+ #self.chsize()
+ #HACK: improper use of resize()
+ #self.resize() #to recenter the new value, etc.
+ pass
+ # #self._resize()
+
+ self._close(None)
+ #self.repaint() #this will happen anyways
+
+
+
+ def __setattr__(self,k,v):
+ mywidget = None
+ if k == 'value':
+ for w in self.values:
+ if w._value == v:
+ mywidget = w
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self.send(CHANGE)
+ self.repaint()
+ if k == 'value':
+ if not mywidget:
+ mywidget = Label(" ",cls=self.cls+".option.label")
+ self.top_selected.value = mywidget
+
+ def add(self,w,value=None):
+ """Add a widget, value item to the Select.
+
+ <pre>Select.add(widget,value=None)</pre>
+
+ <dl>
+ <dt>widget<dd>Widget or string to represent the item
+ <dt>value<dd>value for this item
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = Select()
+ w.add("Goat") #adds a Label
+ w.add("Goat","goat") #adds a Label with the value goat
+ w.add(gui.Label("Cuzco"),"goat") #adds a Label with value goat
+ </code>
+ """
+
+ if type(w) == str: w = Label(w,cls=self.cls+".option.label")
+
+ w.style.align = -1
+ btn = Button(w,cls=self.cls+".option")
+ btn.connect(CLICK,self._setvalue,w)
+
+ self.options.tr()
+ self.options.add(btn)
+
+ if (not self.firstOption):
+ self.firstOption = btn
+
+ if value != None: w._value = value
+ else: w._value = w
+ if self.value == w._value:
+ self.top_selected.value = w
+ self.values.append(w)
--- /dev/null
+"""All sliders and scroll bar widgets have the same parameters.
+
+<pre>Slider(value,min,max,size)</pre>
+<dl>
+<dt>value<dd>initial value
+<dt>min<dd>minimum value
+<dt>max<dd>maximum value
+<dt>size<dd>size of bar in pixels
+</dl>
+"""
+import pygame
+from pygame.locals import *
+
+from const import *
+import widget
+import table
+import basic
+import pguglobals
+
+_SLIDER_HORIZONTAL = 0
+_SLIDER_VERTICAL = 1
+
+class _slider(widget.Widget):
+ def __init__(self,value,orient,min,max,size,step=1,**params):
+ params.setdefault('cls','slider')
+ widget.Widget.__init__(self,**params)
+ self.min,self.max,self.value,self.orient,self.size,self.step = min,max,value,orient,size,step
+
+
+ def paint(self,s):
+
+ self.value = self.value
+ r = pygame.rect.Rect(0,0,self.style.width,self.style.height)
+ if self.orient == _SLIDER_HORIZONTAL:
+ r.x = (self.value-self.min) * (r.w-self.size) / max(1,self.max-self.min);
+ r.w = self.size;
+ else:
+ r.y = (self.value-self.min) * (r.h-self.size) / max(1,self.max-self.min);
+ r.h = self.size;
+
+ self.bar = r
+
+ pguglobals.app.theme.render(s,self.style.bar,r)
+
+ def event(self,e):
+ used = None
+ r = pygame.rect.Rect(0,0,self.style.width,self.style.height)
+ adj = 0
+ if e.type == ENTER: self.repaint()
+ elif e.type == EXIT: self.repaint()
+ elif e.type == MOUSEBUTTONDOWN:
+ if self.bar.collidepoint(e.pos):
+ self.grab = e.pos[0],e.pos[1]
+ self.grab_value = self.value
+ else:
+ x,y,adj = e.pos[0],e.pos[1],1
+ self.grab = None
+ self.repaint()
+ elif e.type == MOUSEBUTTONUP:
+ #x,y,adj = e.pos[0],e.pos[1],1
+ self.repaint()
+ elif e.type == MOUSEMOTION:
+ if 1 in e.buttons and self.container.myfocus is self:
+ if self.grab != None:
+ rel = e.pos[0]-self.grab[0],e.pos[1]-self.grab[1]
+ if self.orient == _SLIDER_HORIZONTAL:
+ d = (r.w - self.size)
+ if d != 0: self.value = self.grab_value + ((self.max-self.min) * rel[0] / d)
+ else:
+ d = (r.h - self.size)
+ if d != 0: self.value = self.grab_value + ((self.max-self.min) * rel[1] / d)
+ else:
+ x,y,adj = e.pos[0],e.pos[1],1
+
+ elif e.type is KEYDOWN:
+ if self.orient == _SLIDER_HORIZONTAL and e.key == K_LEFT:
+ self.value -= self.step
+ used = True
+ elif self.orient == _SLIDER_HORIZONTAL and e.key == K_RIGHT:
+ self.value += self.step
+ used = True
+ elif self.orient == _SLIDER_VERTICAL and e.key == K_UP:
+ self.value -= self.step
+ used = True
+ elif self.orient == _SLIDER_VERTICAL and e.key == K_DOWN:
+ self.value += self.step
+ used = True
+
+ if adj:
+ if self.orient == _SLIDER_HORIZONTAL:
+ d = self.size/2 - (r.w/(self.max-self.min+1))/2
+ self.value = (x-d) * (self.max-self.min) / (r.w-self.size+1) + self.min
+ else:
+ d = self.size/2 - (r.h/(self.max-self.min+1))/2
+ self.value = (y-d) * (self.max-self.min) / (r.h-self.size+1) + self.min
+
+ self.pcls = ""
+ if self.container.myhover is self: self.pcls = "hover"
+ if (self.container.myfocus is self and 1 in pygame.mouse.get_pressed()): self.pcls = "down"
+
+ return used
+
+
+ def __setattr__(self,k,v):
+ if k == 'value':
+ v = int(v)
+ v = max(v,self.min)
+ v = min(v,self.max)
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self.send(CHANGE)
+ self.repaint()
+
+ if hasattr(self,'size'):
+ sz = min(self.size,max(self.style.width,self.style.height))
+ sz = max(sz,min(self.style.width,self.style.height))
+ self.__dict__['size'] = sz
+
+ if hasattr(self,'max') and hasattr(self,'min'):
+ if self.max < self.min: self.max = self.min
+
+class VSlider(_slider):
+ """A verticle slider.
+
+ <pre>VSlider(value,min,max,size)</pre>
+ """
+ def __init__(self,value,min,max,size,step=1,**params):
+ params.setdefault('cls','vslider')
+ _slider.__init__(self,value,_SLIDER_VERTICAL,min,max,size,step,**params)
+
+class HSlider(_slider):
+ """A horizontal slider.
+
+ <pre>HSlider(value,min,max,size)</pre>
+ """
+ def __init__(self,value,min,max,size,step=1,**params):
+ params.setdefault('cls','hslider')
+ _slider.__init__(self,value,_SLIDER_HORIZONTAL,min,max,size,step,**params)
+
+class HScrollBar(table.Table):
+ """A horizontal scroll bar.
+
+ <pre>HScrollBar(value,min,max,size,step=1)</pre>
+ """
+ def __init__(self,value,min,max,size,step=1,**params):
+ params.setdefault('cls','hscrollbar')
+
+ table.Table.__init__(self,**params)
+
+ self.slider = _slider(value,_SLIDER_HORIZONTAL,min,max,size,step=step,cls=self.cls+'.slider')
+
+ self.minus = basic.Image(self.style.minus)
+ self.minus.connect(MOUSEBUTTONDOWN,self._click,-1)
+ self.slider.connect(CHANGE,self.send,CHANGE)
+
+ self.minus2 = basic.Image(self.style.minus)
+ self.minus2.connect(MOUSEBUTTONDOWN,self._click,-1)
+
+ self.plus = basic.Image(self.style.plus)
+ self.plus.connect(MOUSEBUTTONDOWN,self._click,1)
+
+ self.size = size
+
+ def _click(self,value):
+ self.slider.value += self.slider.step*value
+
+ def resize(self,width=None,height=None):
+ self.clear()
+ self.tr()
+
+ w = self.style.width
+ h = self.slider.style.height
+ ww = 0
+
+ if w > (h*2 + self.minus.style.width+self.plus.style.width):
+ self.td(self.minus)
+ ww += self.minus.style.width
+
+ self.td(self.slider)
+
+ if w > (h*2 + self.minus.style.width+self.minus2.style.width+self.plus.style.width):
+ self.td(self.minus2)
+ ww += self.minus2.style.width
+
+ if w > (h*2 + self.minus.style.width+self.plus.style.width):
+ self.td(self.plus)
+ ww += self.plus.style.width
+
+
+ #HACK: handle theme sizing properly
+ xt,xr,xb,xl = pguglobals.app.theme.getspacing(self.slider)
+ ww += xr+xl
+
+ self.slider.style.width = self.style.width - ww
+ setattr(self.slider,'size',self.size * self.slider.style.width / max(1,self.style.width))
+ return table.Table.resize(self,width,height)
+
+
+ def __setattr__(self,k,v):
+ if k in ('min','max','value','step'):
+ return setattr(self.slider,k,v)
+ self.__dict__[k]=v
+
+ def __getattr__(self,k):
+ if k in ('min','max','value','step'):
+ return getattr(self.slider,k)
+ return table.Table.__getattr__(self,k) #self.__dict__[k]
+
+class VScrollBar(table.Table):
+ """A vertical scroll bar.
+
+ <pre>VScrollBar(value,min,max,size,step=1)</pre>
+ """
+ def __init__(self,value,min,max,size,step=1,**params):
+ params.setdefault('cls','vscrollbar')
+
+ table.Table.__init__(self,**params)
+
+ self.minus = basic.Image(self.style.minus)
+ self.minus.connect(MOUSEBUTTONDOWN,self._click,-1)
+
+ self.minus2 = basic.Image(self.style.minus)
+ self.minus2.connect(MOUSEBUTTONDOWN,self._click,-1)
+
+ self.plus = basic.Image(self.style.plus)
+ self.plus.connect(MOUSEBUTTONDOWN,self._click,1)
+
+ self.slider = _slider(value,_SLIDER_VERTICAL,min,max,size,step=step,cls=self.cls+'.slider')
+ self.slider.connect(CHANGE,self.send,CHANGE)
+
+ self.size = size
+
+ def _click(self,value):
+ self.slider.value += self.slider.step*value
+
+ def resize(self,width=None,height=None):
+ self.clear()
+
+ h = self.style.height
+ w = self.slider.style.width
+ hh = 0
+
+ if h > (w*2 + self.minus.style.height+self.plus.style.height):
+ self.tr()
+ self.td(self.minus)
+ hh += self.minus.style.height
+
+ self.tr()
+ self.td(self.slider)
+
+ if h > (w*2 + self.minus.style.height+self.minus2.style.height+self.plus.style.height):
+ self.tr()
+ self.td(self.minus2)
+ hh += self.minus2.style.height
+
+ if h > (w*2 + self.minus.style.height+self.plus.style.height):
+ self.tr()
+ self.td(self.plus)
+ hh += self.plus.style.height
+
+
+ #HACK: handle theme sizing properly
+ xt,xr,xb,xl = pguglobals.app.theme.getspacing(self.slider)
+ hh += xt+xb
+
+ self.slider.style.height = self.style.height - hh
+ setattr(self.slider,'size',self.size * self.slider.style.height / max(1,self.style.height))
+ return table.Table.resize(self,width,height)
+
+ def __setattr__(self,k,v):
+ if k in ('min','max','value','step'):
+ return setattr(self.slider,k,v)
+ self.__dict__[k]=v
+
+ def __getattr__(self,k):
+ if k in ('min','max','value','step'):
+ return getattr(self.slider,k)
+ return table.Table.__getattr__(self,k)
--- /dev/null
+"""
+"""
+
+import pguglobals
+
+class Style:
+ """The class used by widget for the widget.style
+
+ <p>This object is used mainly as a dictionary, accessed via <tt>widget.style.attr</tt>, as opposed to
+ <tt>widget.style['attr']</tt>. It automatically grabs information from the theme via <tt>value = theme.get(widget.cls,widget.pcls,attr)</tt>.</p>
+
+ """
+ def __init__(self,o,dict):
+ self.obj = o
+ for k,v in dict.items(): self.__dict__[k]=v
+ self._cache = {}
+
+ def __getattr__(self,k):
+ key = self.obj.cls,self.obj.pcls,k
+ if key not in self._cache:
+ self._cache[key] = Style_get(self.obj.cls,self.obj.pcls,k)
+ v = self._cache[key]
+ if k in (
+ 'border_top','border_right','border_bottom','border_left',
+ 'padding_top','padding_right','padding_bottom','padding_left',
+ 'margin_top','margin_right','margin_bottom','margin_left',
+ 'align','valign','width','height',
+ ): self.__dict__[k] = v
+ return v
+
+ def __setattr__(self,k,v):
+ self.__dict__[k] = v
+
+
+Style_cache = {}
+def Style_get(cls,pcls,k):
+ key = cls,pcls,k
+ if key not in Style_cache:
+ Style_cache[key] = pguglobals.app.theme.get(cls,pcls,k)
+ return Style_cache[key]
+
--- /dev/null
+"""
+"""
+import pygame
+
+def subsurface(s,r):
+ """Return the subsurface of a surface, with some help, checks.
+
+ <pre>subsurface(s,r): return surface</pre>
+ """
+ r = pygame.Rect(r)
+ if r.x < 0 or r.y < 0:
+ raise "gui.subsurface: %d %d %s"%(s.get_width(),s.get_height(),r)
+ w,h = s.get_width(),s.get_height()
+ if r.right > w:
+ r.w -= r.right-w
+ if r.bottom > h:
+ r.h -= r.bottom-h
+ assert(r.w >= 0 and r.h >= 0)
+ return s.subsurface(r)
+
+class ProxySurface:
+ """
+ A surface-like object which smartly handle out-of-area blitting.
+
+ <pre>ProxySurface(parent, rect, real_surface=None, offset=(0, 0))</pre>
+
+ <p>only one of parent and real_surface should be supplied (non None)</p>
+ <dl>
+ <dt>parent<dd>a ProxySurface object
+ <dt>real_surface<dd>a pygame Surface object
+ </dl>
+
+ <strong>Variables</strong>
+
+ <dl>
+ <dt>mysubsurface<dd>a real and valid pygame.Surface object to be used
+ for blitting.
+ <dt>x, y<dd>if the proxy surface is lefter or higher than the parent,
+ x, y hold the diffs.
+ <dt>offset<dd>an optional feature which let you scroll the whole blitted
+ content.
+ </dl>
+ """
+ def __init__(self, parent, rect, real_surface, offset=(0, 0)):
+ self.offset = offset
+ self.x = self.y = 0
+ if rect.x < 0: self.x = rect.x
+ if rect.y < 0: self.y = rect.y
+ self.real_surface = real_surface
+ if real_surface == None:
+ self.mysubsurface = parent.mysubsurface.subsurface(
+ parent.mysubsurface.get_rect().clip(rect))
+ else:
+ self.mysubsurface = real_surface.subsurface(
+ real_surface.get_rect().clip(rect))
+ rect.topleft = (0, 0)
+ self.rect = rect
+
+ def blit(self, s, pos, rect=None):
+ if rect == None: rect = s.get_rect()
+ pos = (pos[0] + self.offset[0] + self.x, pos[1] + self.offset[1] + self.y)
+ self.mysubsurface.blit(s, pos, rect)
+
+ def subsurface(self, rect):
+ r = pygame.Rect(rect).move(self.offset[0] + self.x,
+ self.offset[1] + self.y)
+ return ProxySurface(self, r, self.real_surface)
+
+ def fill(self, color, rect=None):
+ if rect != None: self.mysubsurface.fill(color, rect)
+ else: self.mysubsurface.fill(color)
+ def get_rect(self): return self.rect
+ def get_width(self): return self.rect[2]
+ def get_height(self): return self.rect[3]
+ def get_abs_offset(): return self.rect[:2]
+ def get_abs_parent(): return self.mysubsurface.get_abs_parent()
+ def set_clip(self, rect=None):
+ if rect == None: self.mysubsurface.set_clip()
+ else:
+ rect = [rect[0] + self.offset[0] + self.x, rect[1] + self.offset[0] + self.y, rect[2], rect[3]]
+ self.mysubsurface.set_clip(rect)
+
+
+
+
+
+
+class xProxySurface:
+ """
+ A surface-like object which smartly handle out-of-area blitting.
+
+ <pre>ProxySurface(parent, rect, real_surface=None, offset=(0, 0))</pre>
+
+ <p>only one of parent and real_surface should be supplied (non None)</p>
+ <dl>
+ <dt>parent<dd>a ProxySurface object
+ <dt>real_surface<dd>a pygame Surface object
+ </dl>
+
+ <strong>Variables</strong>
+
+ <dl>
+ <dt>mysubsurface<dd>a real and valid pygame.Surface object to be used
+ for blitting.
+ <dt>x, y<dd>if the proxy surface is lefter or higher than the parent,
+ x, y hold the diffs.
+ <dt>offset<dd>an optional feature which let you scroll the whole blitted
+ content.
+ </dl>
+ """
+ def __init__(self, parent, rect, real_surface, offset=(0, 0)):
+ self.offset = offset
+ self.x = self.y = 0
+ if rect[0] < 0: self.x = rect[0]
+ if rect[1] < 0: self.y = rect[1]
+ self.real_surface = real_surface
+ if real_surface == None:
+ self.mysubsurface = parent.mysubsurface.subsurface(parent.mysubsurface.get_rect().clip(rect))
+ else:
+ self.mysubsurface = real_surface.subsurface(real_surface.get_rect().clip(rect))
+ rect[0], rect[1] = 0, 0
+ self.rect = rect
+
+ def blit(self, s, pos, rect=None):
+ if rect == None: rect = s.get_rect()
+ pos = (pos[0] + self.offset[0] + self.x, pos[1] + self.offset[1] + self.y)
+ self.mysubsurface.blit(s, pos, rect)
+
+ def subsurface(self, rect): return ProxySurface(self, pygame.Rect(rect).move(self.offset[0] + self.x, self.offset[1] + self.y),self.real_surface)
+ def fill(self, color, rect=None):
+ if rect != None: self.mysubsurface.fill(color, rect)
+ else: self.mysubsurface.fill(color)
+ def get_rect(self): return self.rect
+ def get_width(self): return self.rect[2]
+ def get_height(self): return self.rect[3]
+ def get_abs_offset(): return self.rect[:2]
+ def get_abs_parent(): return self.mysubsurface.get_abs_parent()
+ def set_clip(self, rect=None):
+ if rect == None: self.mysubsurface.set_clip()
+ else:
+ rect = [rect[0] + self.offset[0] + self.x, rect[1] + self.offset[0] + self.y, rect[2], rect[3]]
+ self.mysubsurface.set_clip(rect)
+
--- /dev/null
+"""
+"""
+from const import *
+import container
+
+class Table(container.Container):
+ """A table style container.
+
+ <p>If you know HTML, this should all work roughly how you would expect. If you are not
+ familiar with HTML, please read <a href="http://www.w3.org/TR/REC-html40/struct/tables.html">Tables in HTML Documents</a>. Pay attention to TABLE, TR, TD related parts of the document.</p>
+
+ <pre>Table()</pre>
+
+ <strong>Example</strong>
+ <code>
+ t = gui.Table()
+
+ t.tr()
+ t.td(gui.Label("First Name"), align=-1)
+ t.td(gui.Input())
+
+ t.tr()
+ t.td(gui.Label("Last Name"), align=-1)
+ t.td(gui.Input())
+ </code>
+
+ """
+
+
+ def __init__(self, **params):
+ params.setdefault('cls','table')
+ container.Container.__init__(self, **params)
+ self._rows = []
+ self._curRow = 0
+ self._trok = False
+
+ def getRows(self):
+ return len(self._rows)
+
+ def getColumns(self):
+ if self._rows:
+ return len(self._rows[0])
+ else:
+ return 0
+
+ def remove_row(self, n): #NOTE: won't work in all cases.
+ if n >= self.getRows():
+ print "Trying to remove a nonexistant row:", n, "there are only", self.getRows(), "rows"
+ return
+
+ for cell in self._rows[n]:
+ if isinstance(cell, dict) and cell["widget"] in self.widgets:
+ #print 'removing widget'
+ self.widgets.remove(cell["widget"])
+ del self._rows[n]
+ #print "got here"
+
+ for w in self.widgets:
+ if w.style.row > n: w.style.row -= 1
+
+ if self._curRow >= n:
+ self._curRow -= 1
+
+ #self.rect.w, self.rect.h = self.resize()
+ #self.repaint()
+
+ self.chsize()
+
+ def clear(self):
+ self._rows = []
+ self._curRow = 0
+ self._trok = False
+
+ self.widgets = []
+
+ self.chsize()
+
+ #print 'clear',self,self._rows
+
+ def _addRow(self):
+ self._rows.append([None for x in xrange(self.getColumns())])
+
+ def tr(self):
+ """Start on the next row."""
+ if not self._trok:
+ self._trok = True
+ return
+ self._curRow += 1
+ if self.getRows() <= self._curRow:
+ self._addRow()
+
+ def _addColumn(self):
+ if not self._rows:
+ self._addRow()
+ for row in self._rows:
+ row.append(None)
+
+ def _setCell(self, w, col, row, colspan=1, rowspan=1):
+ #make room for the widget by adding columns and rows
+ while self.getColumns() < col + colspan:
+ self._addColumn()
+ while self.getRows() < row + rowspan:
+ self._addRow()
+
+ #print w.__class__.__name__,col,row,colspan,rowspan
+
+ #actual widget setting and modification stuff
+ w.container = self
+ w.style.row = row #HACK - to work with gal's list
+ w.style.col = col #HACK - to work with gal's list
+ self._rows[row][col] = {"widget":w, "colspan":colspan, "rowspan":rowspan}
+ self.widgets.append(self._rows[row][col]["widget"])
+
+ #set the spanned columns
+ #for acell in xrange(col + 1, col + colspan):
+ # self._rows[row][acell] = True
+
+ #set the spanned rows and the columns on them
+ #for arow in xrange(row + 1, row + rowspan):
+ # for acell in xrange(col, col + colspan): #incorrect?
+ # self._rows[arow][acell] = True
+
+ for arow in xrange(row, row + rowspan):
+ for acell in xrange(col, col + colspan): #incorrect?
+ if row != arow or col != acell:
+ self._rows[arow][acell] = True
+
+
+ def td(self, w, col=None, row=None, colspan=1, rowspan=1, **params):
+ """Add a widget to a table after wrapping it in a TD container.
+
+ <pre>Table.td(w,col=None,row=None,colspan=1,rowspan=1,**params)</pre>
+
+ <dl>
+ <dt>w<dd>widget
+ <dt>col<dd>column
+ <dt>row<dd>row
+ <dt>colspan<dd>colspan
+ <dt>rowspan<dd>rowspan
+ <dt>align<dd>horizontal alignment (-1,0,1)
+ <dt>valign<dd>vertical alignment (-1,0,1)
+ <dt>params<dd>other params for the TD container, style information, etc
+ </dl>
+ """
+
+ Table.add(self,_Table_td(w, **params), col=col, row=row, colspan=colspan, rowspan=rowspan)
+
+ def add(self, w, col=None, row=None, colspan=1, rowspan=1):
+ """Add a widget directly into the table, without wrapping it in a TD container.
+
+ <pre>Table.add(w,col=None,row=None,colspan=1,rowspan=1)</pre>
+
+ <p>See Table.td for an explanation of the parameters.</p>
+ """
+ self._trok = True
+ #if no row was specifically specified, set it to the current row
+ if row is None:
+ row = self._curRow
+ #print row
+
+ #if its going to be a new row, have it be on the first column
+ if row >= self.getRows():
+ col = 0
+
+ #try to find an open cell for the widget
+ if col is None:
+ for cell in xrange(self.getColumns()):
+ if col is None and not self._rows[row][cell]:
+ col = cell
+ break
+
+ #otherwise put the widget in a new column
+ if col is None:
+ col = self.getColumns()
+
+ self._setCell(w, col, row, colspan=colspan, rowspan=rowspan)
+
+ self.chsize()
+ return
+
+ def remove(self,w):
+ if hasattr(w,'_table_td'): w = w._table_td
+ row,col = w.style.row,w.style.col
+ cell = self._rows[row][col]
+ colspan,rowspan = cell['colspan'],cell['rowspan']
+
+ for arow in xrange(row , row + rowspan):
+ for acell in xrange(col, col + colspan): #incorrect?
+ self._rows[arow][acell] = False
+ self.widgets.remove(w)
+ self.chsize()
+
+
+
+ def resize(self, width=None, height=None):
+ #if 1 or self.getRows() == 82:
+ #print ''
+ #print 'resize',self.getRows(),self.getColumns(),width,height
+ #import inspect
+ #for obj,fname,line,fnc,code,n in inspect.stack()[9:20]:
+ # print fname,line,':',fnc,code[0].strip()
+
+
+ #resize the widgets to their smallest size
+ for w in self.widgets:
+ w.rect.w, w.rect.h = w.resize()
+
+ #calculate row heights and column widths
+ rowsizes = [0 for y in xrange(self.getRows())]
+ columnsizes = [0 for x in xrange(self.getColumns())]
+ for row in xrange(self.getRows()):
+ for cell in xrange(self.getColumns()):
+ if self._rows[row][cell] and self._rows[row][cell] is not True:
+ if not self._rows[row][cell]["colspan"] > 1:
+ columnsizes[cell] = max(columnsizes[cell], self._rows[row][cell]["widget"].rect.w)
+ if not self._rows[row][cell]["rowspan"] > 1:
+ rowsizes[row] = max(rowsizes[row], self._rows[row][cell]["widget"].rect.h)
+
+ #distribute extra space if necessary for wide colspanning/rowspanning
+ for row in xrange(self.getRows()):
+ for cell in xrange(self.getColumns()):
+ if self._rows[row][cell] and self._rows[row][cell] is not True:
+ if self._rows[row][cell]["colspan"] > 1:
+ columns = xrange(cell, cell + self._rows[row][cell]["colspan"])
+ totalwidth = 0
+ for acol in columns:
+ totalwidth += columnsizes[acol]
+ if totalwidth < self._rows[row][cell]["widget"].rect.w:
+ for acol in columns:
+ columnsizes[acol] += _table_div(self._rows[row][cell]["widget"].rect.w - totalwidth, self._rows[row][cell]["colspan"],acol)
+ if self._rows[row][cell]["rowspan"] > 1:
+ rows = xrange(row, row + self._rows[row][cell]["rowspan"])
+ totalheight = 0
+ for arow in rows:
+ totalheight += rowsizes[arow]
+ if totalheight < self._rows[row][cell]["widget"].rect.h:
+ for arow in rows:
+ rowsizes[arow] += _table_div(self._rows[row][cell]["widget"].rect.h - totalheight, self._rows[row][cell]["rowspan"],arow)
+
+ #make everything fill out to self.style.width, self.style.heigh, not exact, but pretty close...
+ w, h = sum(columnsizes), sum(rowsizes)
+ if w > 0 and w < self.style.width and len(columnsizes):
+ d = (self.style.width - w)
+ for n in xrange(0, len(columnsizes)):
+ v = columnsizes[n]
+ columnsizes[n] += v * d / w
+ if h > 0 and h < self.style.height and len(rowsizes):
+ d = (self.style.height - h) / len(rowsizes)
+ for n in xrange(0, len(rowsizes)):
+ v = rowsizes[n]
+ rowsizes[n] += v * d / h
+
+ #set the widget's position by calculating their row/column x/y offset
+ cellpositions = [[[sum(columnsizes[0:cell]), sum(rowsizes[0:row])] for cell in xrange(self.getColumns())] for row in xrange(self.getRows())]
+ for row in xrange(self.getRows()):
+ for cell in xrange(self.getColumns()):
+ if self._rows[row][cell] and self._rows[row][cell] is not True:
+ x, y = cellpositions[row][cell]
+ w = sum(columnsizes[cell:cell+self._rows[row][cell]["colspan"]])
+ h = sum(rowsizes[row:row+self._rows[row][cell]["rowspan"]])
+
+ widget = self._rows[row][cell]["widget"]
+ widget.rect.x = x
+ widget.rect.y = y
+ if 1 and (w,h) != (widget.rect.w,widget.rect.h):
+# if h > 20:
+# print widget.widget.__class__.__name__, (widget.rect.w,widget.rect.h),'=>',(w,h)
+ widget.rect.w, widget.rect.h = widget.resize(w, h)
+
+ #print self._rows[row][cell]["widget"].rect
+
+ #print columnsizes
+ #print sum(columnsizes)
+ #size = sum(columnsizes), sum(rowsizes); print size
+
+ #return the tables final size
+ return sum(columnsizes),sum(rowsizes)
+
+
+def _table_div(a,b,c):
+ v,r = a/b, a%b
+ if r != 0 and (c%b)<r: v += 1
+ return v
+
+class _Table_td(container.Container):
+ def __init__(self,widget,**params):#hexpand=0,vexpand=0,
+ container.Container.__init__(self,**params)
+ self.widget = widget
+ #self.hexpand=hexpand
+ #self.vexpand=vexpand
+ widget._table_td = self
+ self.add(widget,0,0)
+
+ def resize(self,width=None,height=None):
+ w = self.widget
+
+ #expansion code, but i didn't like the idea that much..
+ #a bit obscure, fairly useless when a user can just
+ #add a widget to a table instead of td it in.
+ #ww,hh=None,None
+ #if self.hexpand: ww = self.style.width
+ #if self.vexpand: hh = self.style.height
+ #if self.hexpand and width != None: ww = max(ww,width)
+ #if self.vexpand and height != None: hh = max(hh,height)
+ #w.rect.w,w.rect.h = w.resize(ww,hh)
+
+ #why bother, just do the lower mentioned item...
+ w.rect.w,w.rect.h = w.resize()
+
+ #this should not be needed, widgets should obey their sizing on their own.
+
+# if (self.style.width!=0 and w.rect.w > self.style.width) or (self.style.height!=0 and w.rect.h > self.style.height):
+# ww,hh = None,None
+# if self.style.width: ww = self.style.width
+# if self.style.height: hh = self.style.height
+# w.rect.w,w.rect.h = w.resize(ww,hh)
+
+
+ #in the case that the widget is too big, we try to resize it
+ if (width != None and width < w.rect.w) or (height != None and height < w.rect.h):
+ w.rect.w,w.rect.h = w.resize(width,height)
+
+ width = max(width,w.rect.w,self.style.width) #,self.style.cell_width)
+ height = max(height,w.rect.h,self.style.height) #,self.style.cell_height)
+
+ dx = width-w.rect.w
+ dy = height-w.rect.h
+ w.rect.x = (self.style.align+1)*dx/2
+ w.rect.y = (self.style.valign+1)*dy/2
+
+ return width,height
--- /dev/null
+"""
+"""
+import pygame
+from pygame.locals import *
+
+from const import *
+import widget
+
+class TextArea(widget.Widget):
+ """A multi-line text input.
+
+ <pre>TextArea(value="",width = 120, height = 30, size=20)</pre>
+
+ <dl>
+ <dt>value<dd>initial text
+ <dt>size<dd>size for the text box, in characters
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ w = TextArea(value="Cuzco the Goat",size=20)
+
+ w = TextArea("Marbles")
+
+ w = TextArea("Groucho\nHarpo\nChico\nGummo\nZeppo\n\nMarx", 200, 400, 12)
+ </code>
+
+ """
+ def __init__(self,value="",width = 120, height = 30, size=20,**params):
+ params.setdefault('cls','input')
+ params.setdefault('width', width)
+ params.setdefault('height', height)
+
+ widget.Widget.__init__(self,**params)
+ self.value = value # The value of the TextArea
+ self.pos = len(str(value)) # The position of the cursor
+ self.vscroll = 0 # The number of lines that the TextArea is currently scrolled
+ self.font = self.style.font # The font used for rendering the text
+ self.cursor_w = 2 # Cursor width (NOTE: should be in a style)
+ w,h = self.font.size("e"*size)
+ if not self.style.height: self.style.height = h
+ if not self.style.width: self.style.width = w
+
+ def resize(self,width=None,height=None):
+ if (width != None) and (height != None):
+ self.rect = pygame.Rect(self.rect.x, self.rect.y, width, height)
+ return self.rect.w, self.rect.h
+
+ def paint(self,s):
+
+ # TODO: What's up with this 20 magic number? It's the margin of the left and right sides, but I'm not sure how this should be gotten other than by trial and error.
+ max_line_w = self.rect.w - 20
+
+ # Update the line allocation for the box's value
+ self.doLines(max_line_w)
+
+ # Make sure that the vpos and hpos of the cursor is set properly
+ self.updateCursorPos()
+
+ # Make sure that we're scrolled vertically such that the cursor is visible
+ if (self.vscroll < 0):
+ self.vscroll = 0
+ if (self.vpos < self.vscroll):
+ self.vscroll = self.vpos
+ elif ((self.vpos - self.vscroll + 1) * self.line_h > self.rect.h):
+ self.vscroll = - (self.rect.h / self.line_h - self.vpos - 1)
+
+ # Blit each of the lines in turn
+ cnt = 0
+ for line in self.lines:
+ line_pos = (0, (cnt - self.vscroll) * self.line_h)
+ if (line_pos[1] >= 0) and (line_pos[1] < self.rect.h):
+ s.blit( self.font.render(line, 1, self.style.color), line_pos )
+ cnt += 1
+
+ # If the textarea is focused, then also show the cursor
+ if self.container.myfocus is self:
+ r = self.getCursorRect()
+ s.fill(self.style.color,r)
+
+ # This function updates self.vpos and self.hpos based on self.pos
+ def updateCursorPos(self):
+ self.vpos = 0 # Reset the current line that the cursor is on
+ self.hpos = 0
+
+ line_cnt = 0
+ char_cnt = 0
+
+ for line in self.lines:
+ line_char_start = char_cnt # The number of characters at the start of the line
+
+ # Keep track of the character count for words
+ char_cnt += len(line)
+
+ # If our cursor count is still less than the cursor position, then we can update our cursor line to assume that it's at least on this line
+ if (char_cnt > self.pos):
+ self.vpos = line_cnt
+ self.hpos = self.pos - line_char_start
+
+ break # Now that we know where our cursor is, we exit the loop
+
+ line_cnt += 1
+
+ if (char_cnt <= self.pos) and (len(self.lines) > 0):
+ self.vpos = len(self.lines) - 1
+ self.hpos = len(self.lines[ self.vpos ] )
+
+ # Returns a rectangle that is of the size and position of where the cursor is drawn
+ def getCursorRect(self):
+ lw = 0
+ if (len(self.lines) > 0):
+ lw, lh = self.font.size( self.lines[ self.vpos ][ 0:self.hpos ] )
+
+ r = pygame.Rect(lw, (self.vpos - self.vscroll) * self.line_h, self.cursor_w, self.line_h)
+ return r
+
+ # This function sets the cursor position according to an x/y value (such as by from a mouse click)
+ def setCursorByXY(self, (x, y)):
+ self.vpos = ((int) (y / self.line_h)) + self.vscroll
+ if (self.vpos >= len(self.lines)):
+ self.vpos = len(self.lines) - 1
+
+ currentLine = self.lines[ self.vpos ]
+
+ for cnt in range(0, len(currentLine) ):
+ self.hpos = cnt
+ lw, lh = self.font.size( currentLine[ 0:self.hpos + 1 ] )
+ if (lw > x):
+ break
+
+ lw, lh = self.font.size( currentLine )
+ if (lw < x):
+ self.hpos = len(currentLine)
+
+ self.setCursorByHVPos()
+
+ # This function sets the cursor position by the horizontal/vertical cursor position.
+ def setCursorByHVPos(self):
+ line_cnt = 0
+ char_cnt = 0
+
+ for line in self.lines:
+ line_char_start = char_cnt # The number of characters at the start of the line
+
+ # Keep track of the character count for words
+ char_cnt += len(line)
+
+ # If we're on the proper line
+ if (line_cnt == self.vpos):
+ # Make sure that we're not trying to go over the edge of the current line
+ if ( self.hpos >= len(line) ):
+ self.hpos = len(line) - 1
+ # Set the cursor position
+ self.pos = line_char_start + self.hpos
+ break # Now that we've set our cursor position, we exit the loop
+
+ line_cnt += 1
+
+ # Splits up the text found in the control's value, and assigns it into the lines array
+ def doLines(self, max_line_w):
+ self.line_h = 10
+ self.lines = [] # Create an empty starter list to start things out.
+
+ inx = 0
+ line_start = 0
+ while inx >= 0:
+ # Find the next breakable whitespace
+ # HACK: Find a better way to do this to include tabs and system characters and whatnot.
+ prev_word_start = inx # Store the previous whitespace
+ spc_inx = self.value.find(' ', inx+1)
+ nl_inx = self.value.find('\n', inx+1)
+
+ if (min(spc_inx, nl_inx) == -1):
+ inx = max(spc_inx, nl_inx)
+ else:
+ inx = min(spc_inx, nl_inx)
+
+ # Measure the current line
+ lw, self.line_h = self.font.size( self.value[ line_start : inx ] )
+
+ # If we exceeded the max line width, then create a new line
+ if (lw > max_line_w):
+ #Fall back to the previous word start
+ self.lines.append(self.value[ line_start : prev_word_start + 1 ])
+ line_start = prev_word_start + 1
+ # TODO: Check for extra-long words here that exceed the length of a line, to wrap mid-word
+
+ # If we reached the end of our text
+ if (inx < 0):
+ # Then make sure we added the last of the line
+ if (line_start < len( self.value ) ):
+ self.lines.append( self.value[ line_start : len( self.value ) ] )
+ # If we reached a hard line break
+ elif (self.value[inx] == "\n"):
+ # Then make a line break here as well.
+ newline = self.value[ line_start : inx + 1 ]
+ newline = newline.replace("\n", " ") # HACK: We know we have a newline character, which doesn't print nicely, so make it into a space. Comment this out to see what I mean.
+ self.lines.append( newline )
+
+ line_start = inx + 1
+ else:
+ # Otherwise, we just continue progressing to the next space
+ pass
+
+ def _setvalue(self,v):
+ self.__dict__['value'] = v
+ self.send(CHANGE)
+
+ def event(self,e):
+ used = None
+ if e.type == KEYDOWN:
+ if e.key == K_BACKSPACE:
+ if self.pos:
+ self._setvalue(self.value[:self.pos-1] + self.value[self.pos:])
+ self.pos -= 1
+ elif e.key == K_DELETE:
+ if len(self.value) > self.pos:
+ self._setvalue(self.value[:self.pos] + self.value[self.pos+1:])
+ elif e.key == K_HOME:
+ # Find the previous newline
+ newPos = self.value.rfind('\n', 0, self.pos)
+ if (newPos >= 0):
+ self.pos = newPos
+ elif e.key == K_END:
+ # Find the previous newline
+ newPos = self.value.find('\n', self.pos, len(self.value) )
+ if (newPos >= 0):
+ self.pos = newPos
+ elif e.key == K_LEFT:
+ if self.pos > 0: self.pos -= 1
+ used = True
+ elif e.key == K_RIGHT:
+ if self.pos < len(self.value): self.pos += 1
+ used = True
+ elif e.key == K_UP:
+ self.vpos -= 1
+ self.setCursorByHVPos()
+ elif e.key == K_DOWN:
+ self.vpos += 1
+ self.setCursorByHVPos()
+ # The following return/tab keys are standard for PGU widgets, but I took them out here to facilitate multi-line text editing
+# elif e.key == K_RETURN:
+# self.next()
+# elif e.key == K_TAB:
+# pass
+ else:
+ #c = str(e.unicode)
+ try:
+ if (e.key == K_RETURN):
+ c = "\n"
+ elif (e.key == K_TAB):
+ c = " "
+ else:
+ c = (e.unicode).encode('latin-1')
+ if c:
+ self._setvalue(self.value[:self.pos] + c + self.value[self.pos:])
+ self.pos += len(c)
+ except: #ignore weird characters
+ pass
+ self.repaint()
+ elif e.type == MOUSEBUTTONDOWN:
+ self.setCursorByXY(e.pos)
+ self.repaint()
+
+ elif e.type == FOCUS:
+ self.repaint()
+ elif e.type == BLUR:
+ self.repaint()
+
+ self.pcls = ""
+ if self.container.myfocus is self: self.pcls = "focus"
+
+ return used
+
+ def __setattr__(self,k,v):
+ if k == 'value':
+ if v == None: v = ''
+ v = str(v)
+ self.pos = len(v)
+ _v = self.__dict__.get(k,NOATTR)
+ self.__dict__[k]=v
+ if k == 'value' and _v != NOATTR and _v != v:
+ self.send(CHANGE)
+ self.repaint()
+
+# The first version of this code was done by Clint Herron, and is a modified version of input.py (by Phil Hassey).
+# It is under the same license as the rest of the PGU library.
\ No newline at end of file
--- /dev/null
+# theme.py
+
+"""
+"""
+import os, re
+import pygame
+
+from const import *
+import widget
+import surface
+from basic import parse_color, is_color
+
+__file__ = os.path.abspath(__file__)
+
+def _list_themes(dir):
+ d = {}
+ for entry in os.listdir(dir):
+ if os.path.exists(os.path.join(dir, entry, 'config.txt')):
+ d[entry] = os.path.join(dir, entry)
+ return d
+
+class Theme:
+ """Theme interface.
+
+ <p>If you wish to create your own theme, create a class with this interface, and
+ pass it to gui.App via <tt>gui.App(theme=MyTheme())</tt>.</p>
+
+ <strong>Default Theme</strong>
+
+ <pre>Theme(dirs='default')</pre>
+ <dl>
+ <dt>dirs<dd>Name of the theme dir to load a theme from. May be an absolute path to a theme, if pgu is not installed, or if you created your own theme. May include several dirs in a list if data is spread across several themes.
+ </dl>
+
+ <strong>Example</strong>
+
+ <code>
+ theme = gui.Theme("default")
+ theme = gui.Theme(["mytheme","mytheme2"])
+ </code>
+ """
+ def __init__(self,dirs='default'):
+ self.config = {}
+ self.dict = {}
+ self._loaded = []
+ self.cache = {}
+ self._preload(dirs)
+ pygame.font.init()
+
+ def _preload(self,ds):
+ if not isinstance(ds, list):
+ ds = [ds]
+ for d in ds:
+ if d not in self._loaded:
+ self._load(d)
+ self._loaded.append(d)
+
+ def _load(self, name):
+ #theme_dir = themes[name]
+
+ #try to load the local dir, or absolute path
+ dnames = [name]
+
+ #if the package isn't installed and people are just
+ #trying out the scripts or examples
+ dnames.append(os.path.join(os.path.dirname(__file__),"..","..","data","themes",name))
+
+ #if the package is installed, and the package is installed
+ #in /usr/lib/python2.3/site-packages/pgu/
+ #or c:\python23\lib\site-packages\pgu\
+ #the data is in ... lib/../share/ ...
+ dnames.append(os.path.join(os.path.dirname(__file__),"..","..","..","..","share","pgu","themes",name))
+ dnames.append(os.path.join(os.path.dirname(__file__),"..","..","..","..","..","share","pgu","themes",name))
+ dnames.append(os.path.join(os.path.dirname(__file__),"..","..","share","pgu","themes",name))
+ for dname in dnames:
+ if os.path.isdir(dname): break
+ if not os.path.isdir(dname):
+ raise 'could not find theme '+name
+
+ fname = os.path.join(dname,"config.txt")
+ if os.path.isfile(fname):
+ try:
+ f = open(fname)
+ for line in f.readlines():
+ vals = line.strip().split()
+ if len(vals) < 3: continue
+ cls = vals[0]
+ del vals[0]
+ pcls = ""
+ if cls.find(":")>=0:
+ cls,pcls = cls.split(":")
+ attr = vals[0]
+ del vals[0]
+ self.config[cls+":"+pcls+" "+attr] = (dname, vals)
+ finally:
+ f.close()
+ fname = os.path.join(dname,"style.ini")
+ if os.path.isfile(fname):
+ import ConfigParser
+ cfg = ConfigParser.ConfigParser()
+ f = open(fname,'r')
+ cfg.readfp(f)
+ for section in cfg.sections():
+ cls = section
+ pcls = ''
+ if cls.find(":")>=0:
+ cls,pcls = cls.split(":")
+ for attr in cfg.options(section):
+ vals = cfg.get(section,attr).strip().split()
+ self.config[cls+':'+pcls+' '+attr] = (dname,vals)
+
+ is_image = re.compile('\.(gif|jpg|bmp|png|tga)$', re.I)
+ def _get(self,key):
+ if not key in self.config: return
+ if key in self.dict: return self.dict[key]
+ dvals = self.config[key]
+ dname, vals = dvals
+ #theme_dir = themes[name]
+ v0 = vals[0]
+ if v0[0] == '#':
+ v = parse_color(v0)
+ #if (len(v0) == 7):
+ # # Due to a bug in pygame 1.8 (?) we need to explicitly
+ # # specify the alpha value (otherwise it defaults to zero)
+ # v0 += "FF"
+ #v = pygame.color.Color(v0)
+ elif v0.endswith(".ttf") or v0.endswith(".TTF"):
+ v = pygame.font.Font(os.path.join(dname, v0),int(vals[1]))
+ elif self.is_image.search(v0) is not None:
+ v = pygame.image.load(os.path.join(dname, v0))
+ else:
+ try: v = int(v0)
+ except: v = pygame.font.SysFont(v0, int(vals[1]))
+ self.dict[key] = v
+ return v
+
+ def get(self,cls,pcls,attr):
+ """Interface method -- get the value of a style attribute.
+
+ <pre>Theme.get(cls,pcls,attr): return value</pre>
+
+ <dl>
+ <dt>cls<dd>class, for example "checkbox", "button", etc.
+ <dt>pcls<dd>pseudo class, for example "hover", "down", etc.
+ <dt>attr<dd>attribute, for example "image", "background", "font", "color", etc.
+ </dl>
+
+ <p>returns the value of the attribute.</p>
+
+ <p>This method is called from [[gui-style]].</p>
+ """
+
+ if not self._loaded: self._preload("default")
+
+ o = cls+":"+pcls+" "+attr
+
+ #if not hasattr(self,'_count'):
+ # self._count = {}
+ #if o not in self._count: self._count[o] = 0
+ #self._count[o] += 1
+
+ if o in self.cache:
+ return self.cache[o]
+
+ v = self._get(cls+":"+pcls+" "+attr)
+ if v:
+ self.cache[o] = v
+ return v
+
+ pcls = ""
+ v = self._get(cls+":"+pcls+" "+attr)
+ if v:
+ self.cache[o] = v
+ return v
+
+ cls = "default"
+ v = self._get(cls+":"+pcls+" "+attr)
+ if v:
+ self.cache[o] = v
+ return v
+
+ v = 0
+ self.cache[o] = v
+ return v
+
+ def box(self,w,s):
+ style = w.style
+
+ c = (0,0,0)
+ if style.border_color != 0: c = style.border_color
+ w,h = s.get_width(),s.get_height()
+
+ s.fill(c,(0,0,w,style.border_top))
+ s.fill(c,(0,h-style.border_bottom,w,style.border_bottom))
+ s.fill(c,(0,0,style.border_left,h))
+ s.fill(c,(w-style.border_right,0,style.border_right,h))
+
+
+ def getspacing(self,w):
+ # return the top, right, bottom, left spacing around the widget
+ if not hasattr(w,'_spacing'): #HACK: assume spacing doesn't change re pcls
+ s = w.style
+ xt = s.margin_top+s.border_top+s.padding_top
+ xr = s.padding_right+s.border_right+s.margin_right
+ xb = s.padding_bottom+s.border_bottom+s.margin_bottom
+ xl = s.margin_left+s.border_left+s.padding_left
+ w._spacing = xt,xr,xb,xl
+ return w._spacing
+
+
+ def resize(self,w,m):
+ # Returns the rectangle expanded in each direction
+ def expand_rect(rect, left, top, right, bottom):
+ return pygame.Rect(rect.x - left,
+ rect.y - top,
+ rect.w + left + right,
+ rect.h + top + bottom)
+
+ def func(width=None,height=None):
+ s = w.style
+
+ pt,pr,pb,pl = s.padding_top,s.padding_right,s.padding_bottom,s.padding_left
+ bt,br,bb,bl = s.border_top,s.border_right,s.border_bottom,s.border_left
+ mt,mr,mb,ml = s.margin_top,s.margin_right,s.margin_bottom,s.margin_left
+ # Calculate the total space on each side
+ top = pt+bt+mt
+ right = pr+br+mr
+ bottom = pb+bb+mb
+ left = pl+bl+ml
+ ttw = left+right
+ tth = top+bottom
+
+ ww,hh = None,None
+ if width != None: ww = width-ttw
+ if height != None: hh = height-tth
+ ww,hh = m(ww,hh)
+
+ if width == None: width = ww
+ if height == None: height = hh
+
+ #if the widget hasn't respected the style.width,
+ #style height, we'll add in the space for it...
+ width = max(width-ttw, ww, w.style.width)
+ height = max(height-tth, hh, w.style.height)
+
+ #width = max(ww,w.style.width-tw)
+ #height = max(hh,w.style.height-th)
+
+ r = pygame.Rect(left,top,width,height)
+
+ w._rect_padding = expand_rect(r, pl, pt, pr, pb)
+ w._rect_border = expand_rect(w._rect_padding, bl, bt, br, bb)
+ w._rect_margin = expand_rect(w._rect_border, ml, mt, mr, mb)
+
+ #w._rect_padding = pygame.Rect(r.x-pl,r.y-pt,r.w+pl+pr,r.h+pt+pb)
+ #r = w._rect_padding
+ #w._rect_border = pygame.Rect(r.x-bl,r.y-bt,r.w+bl+br,r.h+bt+bb)
+ #r = w._rect_border
+ #w._rect_margin = pygame.Rect(r.x-ml,r.y-mt,r.w+ml+mr,r.h+mt+mb)
+
+ # align it within it's zone of power.
+ rect = pygame.Rect(left, top, ww, hh)
+ dx = width-rect.w
+ dy = height-rect.h
+ rect.x += (w.style.align+1)*dx/2
+ rect.y += (w.style.valign+1)*dy/2
+
+ w._rect_content = rect
+
+ return (w._rect_margin.w, w._rect_margin.h)
+ return func
+
+
+ def paint(self,w,m):
+ def func(s):
+# if w.disabled:
+# if not hasattr(w,'_disabled_bkgr'):
+# w._disabled_bkgr = s.convert()
+# orig = s
+# s = w._disabled_bkgr.convert()
+
+# if not hasattr(w,'_theme_paint_bkgr'):
+# w._theme_paint_bkgr = s.convert()
+# else:
+# s.blit(w._theme_paint_bkgr,(0,0))
+#
+# if w.disabled:
+# orig = s
+# s = w._theme_paint_bkgr.convert()
+
+ if w.disabled:
+ if (not (hasattr(w,'_theme_bkgr') and
+ w._theme_bkgr.get_width() == s.get_width() and
+ w._theme_bkgr.get_height() == s.get_height())):
+ w._theme_bkgr = s.copy()
+ orig = s
+ s = w._theme_bkgr
+ s.fill((0,0,0,0))
+ s.blit(orig,(0,0))
+
+ if hasattr(w,'background'):
+ w.background.paint(surface.subsurface(s,w._rect_border))
+ self.box(w,surface.subsurface(s,w._rect_border))
+ r = m(surface.subsurface(s,w._rect_content))
+
+ if w.disabled:
+ s.set_alpha(128)
+ orig.blit(s,(0,0))
+
+# if w.disabled:
+# orig.blit(w._disabled_bkgr,(0,0))
+# s.set_alpha(128)
+# orig.blit(s,(0,0))
+
+ w._painted = True
+ return r
+ return func
+
+ def event(self,w,m):
+ def func(e):
+ rect = w._rect_content
+ if e.type == MOUSEBUTTONUP or e.type == MOUSEBUTTONDOWN:
+ sub = pygame.event.Event(e.type,{
+ 'button':e.button,
+ 'pos':(e.pos[0]-rect.x,e.pos[1]-rect.y)})
+ elif e.type == CLICK:
+ sub = pygame.event.Event(e.type,{
+ 'button':e.button,
+ 'pos':(e.pos[0]-rect.x,e.pos[1]-rect.y)})
+ elif e.type == MOUSEMOTION:
+ sub = pygame.event.Event(e.type,{
+ 'buttons':e.buttons,
+ 'pos':(e.pos[0]-rect.x,e.pos[1]-rect.y),
+ 'rel':e.rel})
+ else:
+ sub = e
+ r = m(sub)
+ return r
+ return func
+
+ def update(self,w,m):
+ def func(s):
+ if w.disabled: return []
+ r = m(surface.subsurface(s,w._rect_content))
+ if type(r) == list:
+ dx,dy = w._rect_content.topleft
+ for rr in r:
+ rr.x,rr.y = rr.x+dx,rr.y+dy
+ return r
+ return func
+
+ def open(self,w,m):
+ def func(widget=None,x=None,y=None):
+ if not hasattr(w,'_rect_content'): w.rect.w,w.rect.h = w.resize() #HACK: so that container.open won't resize again!
+ rect = w._rect_content
+ ##print w.__class__.__name__, rect
+ if x != None: x += rect.x
+ if y != None: y += rect.y
+ return m(widget,x,y)
+ return func
+
+ #def open(self,w,m):
+ # def func(widget=None):
+ # return m(widget)
+ # return func
+
+ def decorate(self,widget,level):
+ """Interface method -- decorate a widget.
+
+ <p>The theme system is given the opportunity to decorate a widget methods at the
+ end of the Widget initializer.</p>
+
+ <pre>Theme.decorate(widget,level)</pre>
+
+ <dl>
+ <dt>widget<dd>the widget to be decorated
+ <dt>level<dd>the amount of decoration to do, False for none, True for normal amount, 'app' for special treatment of App objects.
+ </dl>
+ """
+
+ w = widget
+ if level == False: return
+
+ if type(w.style.background) != int:
+ w.background = Background(w,self)
+
+ if level == 'app': return
+
+ for k,v in w.style.__dict__.items():
+ if k in ('border','margin','padding'):
+ for kk in ('top','bottom','left','right'):
+ setattr(w.style,'%s_%s'%(k,kk),v)
+
+ w.paint = self.paint(w,w.paint)
+ w.event = self.event(w,w.event)
+ w.update = self.update(w,w.update)
+ w.resize = self.resize(w,w.resize)
+ w.open = self.open(w,w.open)
+
+ def render(self,s,box,r):
+ """Interface method - render a special widget feature.
+
+ <pre>Theme.render(s,box,r)</pre>
+
+ <dl>
+ <dt>s<dt>pygame.Surface
+ <dt>box<dt>box data, a value returned from Theme.get, typically a pygame.Surface
+ <dt>r<dt>pygame.Rect with the size that the box data should be rendered
+ </dl>
+
+ """
+
+ if box == 0: return
+
+ if is_color(box):
+ s.fill(box,r)
+ return
+
+ x,y,w,h=r.x,r.y,r.w,r.h
+ ww,hh=box.get_width()/3,box.get_height()/3
+ xx,yy=x+w,y+h
+ src = pygame.rect.Rect(0,0,ww,hh)
+ dest = pygame.rect.Rect(0,0,ww,hh)
+
+ s.set_clip(pygame.Rect(x+ww,y+hh,w-ww*2,h-hh*2))
+ src.x,src.y = ww,hh
+ for dest.y in xrange(y+hh,yy-hh,hh):
+ for dest.x in xrange(x+ww,xx-ww,ww): s.blit(box,dest,src)
+
+ s.set_clip(pygame.Rect(x+ww,y,w-ww*3,hh))
+ src.x,src.y,dest.y = ww,0,y
+ for dest.x in xrange(x+ww,xx-ww*2,ww): s.blit(box,dest,src)
+ dest.x = xx-ww*2
+ s.set_clip(pygame.Rect(x+ww,y,w-ww*2,hh))
+ s.blit(box,dest,src)
+
+ s.set_clip(pygame.Rect(x+ww,yy-hh,w-ww*3,hh))
+ src.x,src.y,dest.y = ww,hh*2,yy-hh
+ for dest.x in xrange(x+ww,xx-ww*2,ww): s.blit(box,dest,src)
+ dest.x = xx-ww*2
+ s.set_clip(pygame.Rect(x+ww,yy-hh,w-ww*2,hh))
+ s.blit(box,dest,src)
+
+ s.set_clip(pygame.Rect(x,y+hh,xx,h-hh*3))
+ src.y,src.x,dest.x = hh,0,x
+ for dest.y in xrange(y+hh,yy-hh*2,hh): s.blit(box,dest,src)
+ dest.y = yy-hh*2
+ s.set_clip(pygame.Rect(x,y+hh,xx,h-hh*2))
+ s.blit(box,dest,src)
+
+ s.set_clip(pygame.Rect(xx-ww,y+hh,xx,h-hh*3))
+ src.y,src.x,dest.x=hh,ww*2,xx-ww
+ for dest.y in xrange(y+hh,yy-hh*2,hh): s.blit(box,dest,src)
+ dest.y = yy-hh*2
+ s.set_clip(pygame.Rect(xx-ww,y+hh,xx,h-hh*2))
+ s.blit(box,dest,src)
+
+ s.set_clip()
+ src.x,src.y,dest.x,dest.y = 0,0,x,y
+ s.blit(box,dest,src)
+
+ src.x,src.y,dest.x,dest.y = ww*2,0,xx-ww,y
+ s.blit(box,dest,src)
+
+ src.x,src.y,dest.x,dest.y = 0,hh*2,x,yy-hh
+ s.blit(box,dest,src)
+
+ src.x,src.y,dest.x,dest.y = ww*2,hh*2,xx-ww,yy-hh
+ s.blit(box,dest,src)
+
+
+class Background(widget.Widget):
+ def __init__(self,value,theme,**params):
+ params['decorate'] = False
+ widget.Widget.__init__(self,**params)
+ self.value = value
+ self.theme = theme
+
+ def paint(self,s):
+ r = pygame.Rect(0,0,s.get_width(),s.get_height())
+ v = self.value.style.background
+ if is_color(v):
+ s.fill(v)
+ else:
+ self.theme.render(s,v,r)
--- /dev/null
+"""
+"""
+import pygame
+
+import pguglobals
+import style
+
+class SignalCallback:
+ # The function to call
+ func = None
+ # The parameters to pass to the function (as a list)
+ params = None
+
+class Widget:
+ """Template object - base for all widgets.
+
+ <pre>Widget(**params)</pre>
+
+ <p>A number of optional params may be passed to the Widget initializer.</p>
+
+ <dl>
+ <dt>decorate<dd>defaults to True. If true, will call <tt>theme.decorate(self)</tt> to allow the theme a chance to decorate the widget.
+ <dt>style<dd>a dict of style parameters.
+ <dt>x, y, width, height<dd>position and size parameters, passed along to style
+ <dt>align, valign<dd>alignment parameters, passed along to style
+ <dt>font, color, background<dd>other common parameters that are passed along to style
+ <dt>cls<dd>class name as used by Theme
+ <dt>name<dd>name of widget as used by Form. If set, will call <tt>form.add(self,name)</tt> to add the widget to the most recently created Form.
+ <dt>focusable<dd>True if this widget can receive focus via Tab, etc. Defaults to True.
+ <dt>disabled<dd>True of this widget is disabled. Defaults to False.
+ <dt>value<dd>initial value
+ </dl>
+
+ <strong>Example - Creating your own Widget</strong>
+ <p>This example shows which methods are template methods.</p>
+ <code>
+ class Draw(gui.Widget):
+ def paint(self,s):
+ #paint the pygame.Surface
+ return
+
+ def update(self,s):
+ #update the pygame.Surface and return the update rects
+ return [pygame.Rect(0,0,self.rect.w,self.rect.h)]
+
+ def event(self,e):
+ #handle the pygame.Event
+ return
+
+ def resize(self,width=None,height=None):
+ #return the width and height of this widget
+ return 256,256
+ </code>
+ """
+
+ # The name of the widget (or None if not defined)
+ name = None
+
+ def __init__(self,**params):
+ #object.Object.__init__(self)
+ self.connects = {}
+ params.setdefault('decorate',True)
+ params.setdefault('style',{})
+ params.setdefault('focusable',True)
+ params.setdefault('disabled',False)
+
+ self.focusable = params['focusable']
+ self.disabled = params['disabled']
+
+ self.rect = pygame.Rect(params.get('x',0),params.get('y',0),params.get('width',0),params.get('height',0))
+
+ s = params['style']
+ #some of this is a bit "theme-ish" but it is very handy, so these
+ #things don't have to be put directly into the style.
+ for att in ('align','valign','x','y','width','height','color','font','background'):
+ if att in params: s[att] = params[att]
+ self.style = style.Style(self,s)
+
+ self.cls = 'default'
+ if 'cls' in params: self.cls = params['cls']
+ if 'name' in params:
+ import form
+ self.name = params['name']
+ if hasattr(form.Form,'form') and form.Form.form != None:
+ form.Form.form.add(self)
+ self.form = form.Form.form
+ if 'value' in params: self.value = params['value']
+ self.pcls = ""
+
+ if params['decorate'] != False:
+ if (not pguglobals.app):
+ # TODO - fix this somehow
+ import app
+ print 'gui.widget: creating an App'
+ app.App()
+ pguglobals.app.theme.decorate(self,params['decorate'])
+
+ def focus(self):
+ """Focus this Widget.
+
+ <pre>Widget.focus()</pre>
+ """
+ if getattr(self,'container',None) != None:
+ if self.container.myfocus != self: ## by Gal Koren
+ self.container.focus(self)
+
+ def blur(self):
+ """Blur this Widget.
+
+ <pre>Widget.blur()</pre>
+ """
+ if getattr(self,'container',None) != None: self.container.blur(self)
+
+ def open(self):
+ """Open this Widget as a modal dialog.
+
+ <pre>Widget.open()</pre>
+ """
+ #if getattr(self,'container',None) != None: self.container.open(self)
+ pguglobals.app.open(self)
+
+ def close(self, w=None):
+ """Close this Widget (if it is a modal dialog.)
+
+ <pre>Widget.close()</pre>
+ """
+ #if getattr(self,'container',None) != None: self.container.close(self)
+ if (not w):
+ w = self
+ pguglobals.app.close(w)
+
+ def resize(self,width=None,height=None):
+ """Template method - return the size and width of this widget.
+
+ <p>Responsible for also resizing all sub-widgets.</p>
+
+ <pre>Widget.resize(width,height): return width,height</pre>
+
+ <dl>
+ <dt>width<dd>suggested width
+ <dt>height<dd>suggested height
+ </dl>
+
+ <p>If not overridden, will return self.style.width, self.style.height</p>
+ """
+ return self.style.width, self.style.height
+
+ def chsize(self):
+ """Change the size of this widget.
+
+ <p>Calling this method will cause a resize on all the widgets,
+ including this one.</p>
+
+ <pre>Widget.chsize()</pre>
+ """
+
+ if not hasattr(self,'_painted'): return
+
+ if not hasattr(self,'container'): return
+
+ if pguglobals.app:
+ if pguglobals.app._chsize:
+ return
+ pguglobals.app.chsize()
+ return
+
+ #if hasattr(app.App,'app'):
+ # w,h = self.rect.w,self.rect.h
+ # w2,h2 = self.resize()
+ # if w2 != w or h2 != h:
+ # app.App.app.chsize()
+ # else:
+ # self.repaint()
+
+
+ def update(self,s):
+ """Template method - update the surface
+
+ <pre>Widget.update(s): return list of pygame.Rect(s)</pre>
+
+ <dl>
+ <dt>s<dd>pygame.Surface to update
+ </dl>
+
+ <p>return - a list of the updated areas as pygame.Rect(s).</p>
+ """
+ return
+
+ def paint(self,s):
+ """Template method - paint the surface
+
+ <pre>Widget.paint(s)</pre>
+
+ <dl>
+ <dt>s<dd>pygame.Surface to paint
+ </dl>
+ """
+ return
+
+ def repaint(self):
+ """Request a repaint of this Widget.
+
+ <pre>Widget.repaint()</pre>
+ """
+ if getattr(self,'container',None) != None: self.container.repaint(self)
+ def repaintall(self):
+ """Request a repaint of all Widgets.
+
+ <pre>Widget.repaintall()</pre>
+ """
+ if getattr(self,'container',None) != None: self.container.repaintall()
+ def reupdate(self):
+ """Request a reupdate of this Widget
+
+ <pre>Widget.reupdate()</pre>
+ """
+ if getattr(self,'container',None) != None: self.container.reupdate(self)
+ def next(self):
+ """Pass focus to next Widget.
+
+ <p>Widget order determined by the order they were added to their container.</p>
+
+ <pre>Widget.next()</pre>
+ """
+ if getattr(self,'container',None) != None: self.container.next(self)
+ def previous(self):
+ """Pass focus to previous Widget.
+
+ <p>Widget order determined by the order they were added to their container.</p>
+
+ <pre>Widget.previous()</pre>
+ """
+
+ if getattr(self,'container',None) != None: self.container.previous(self)
+
+ def get_abs_rect(self):
+ """Get the absolute rect of this widget on the App screen
+
+ <pre>Widget.get_abs_rect(): return pygame.Rect</pre>
+ """
+ x, y = self.rect.x, self.rect.y
+ x += self._rect_content.x
+ y += self._rect_content.y
+ c = getattr(self,'container',None)
+ while c:
+ x += c.rect.x
+ y += c.rect.y
+ if hasattr(c,'_rect_content'):
+ x += c._rect_content.x
+ y += c._rect_content.y
+ c = getattr(c,'container',None)
+ return pygame.Rect(x, y, self.rect.w, self.rect.h)
+
+ def connect(self,code,func,*params):
+ """Connect a event code to a callback function.
+
+ <p>There may be multiple callbacks per event code.</p>
+
+ <pre>Object.connect(code,fnc,value)</pre>
+
+ <dl>
+ <dt>code<dd>event type [[gui-const]]
+ <dt>fnc<dd>callback function
+ <dt>*values<dd>values to pass to callback. Please note that callbacks may also have "magicaly" parameters. Such as:
+ <dl>
+ <dt>_event<dd>receive the event
+ <dt>_code<dd>receive the event code
+ <dt>_widget<dd>receive the sending widget
+ </dl>
+ </dl>
+
+ <strong>Example</strong>
+ <code>
+ def onclick(value):
+ print 'click',value
+
+ w = Button("PGU!")
+ w.connect(gui.CLICK,onclick,'PGU Button Clicked')
+ </code>
+ """
+ if (not code in self.connects):
+ self.connects[code] = []
+ for cb in self.connects[code]:
+ if (cb.func == func):
+ # Already connected to this callback function
+ return
+ # Wrap the callback function and add it to the list
+ cb = SignalCallback()
+ cb.func = func
+ cb.params = params
+ self.connects[code].append(cb)
+
+ # Remove signal handlers from the given event code. If func is specified,
+ # only those handlers will be removed. If func is None, all handlers
+ # will be removed.
+ def disconnect(self, code, func=None):
+ if (not code in self.connects):
+ return
+ if (not func):
+ # Remove all signal handlers
+ del self.connects[code]
+ else:
+ # Remove handlers that call 'func'
+ n = 0
+ callbacks = self.connects[code]
+ while (n < len(callbacks)):
+ if (callbacks[n].func == func):
+ # Remove this callback
+ del callbacks[n]
+ else:
+ n += 1
+
+ def send(self,code,event=None):
+ """Send a code, event callback trigger.
+
+ <pre>Object.send(code,event=None)</pre>
+
+ <dl>
+ <dt>code<dd>event code
+ <dt>event<dd>event
+ </dl>
+ """
+ if (not code in self.connects):
+ return
+ # Trigger all connected signal handlers
+ for cb in self.connects[code]:
+ func = cb.func
+ values = list(cb.params)
+
+ nargs = func.func_code.co_argcount
+ names = list(func.func_code.co_varnames)[:nargs]
+ if hasattr(func,'im_class'): names.pop(0)
+
+ args = []
+ magic = {'_event':event,'_code':code,'_widget':self}
+ for name in names:
+ if name in magic.keys():
+ args.append(magic[name])
+ elif len(values):
+ args.append(values.pop(0))
+ else:
+ break
+ args.extend(values)
+ func(*args)
+
+ def _event(self,e):
+ if self.disabled: return
+ self.send(e.type,e)
+ return self.event(e)
+# return
+# import app
+# if hasattr(app.App,'app'):
+# app.App.app.events.append((self,e))
+
+ def event(self,e):
+ """Template method - called when an event is passed to this object.
+
+ <p>Please note that if you use an event, returning the value True
+ will stop parent containers from also using the event. (For example, if
+ your widget handles TABs or arrow keys, and you don't want those to
+ also alter the focus.)</p>
+
+ <dl>
+ <dt>e<dd>event
+ </dl>
+ """
+
+ return
+
+ # Returns the top-level widget (usually the Desktop) by following the
+ # chain of 'container' references.
+ def get_toplevel(self):
+ top = self
+ while (getattr(top, "container", None)):
+ top = top.container
+ return top
+
--- /dev/null
+"""Hexagonal tile engine.
+
+<p>Note -- this engine is not finished. Sprites are not supported. It
+can still be useful for using the level editor, and for rendering hex
+terrains, however. If you are able to update it and use it in a real game,
+help would be greatly appreciated!</p>
+
+<p>please note that this file is alpha, and is subject to modification in
+future versions of pgu!</p>
+
+"""
+print 'pgu.hexvid','This module is alpha, and is subject to change.'
+
+from pgu.vid import *
+import pygame
+
+
+class Hexvid(Vid):
+ """Create an hex vid engine. See [[vid]]"""
+ def update(self,screen):
+ return self.paint(screen)
+
+ def paint(self,screen):
+ sw,sh = screen.get_width(),screen.get_height()
+ self.view.w,self.view.h = sw,sh
+
+ tlayer = self.tlayer
+ blayer = self.blayer
+ #zlayer = self.zlayer
+ w,h = len(tlayer[0]),len(tlayer)
+
+ #iso_w,iso_h,iso_z,tile_w,tile_h,base_w,base_h = self.iso_w,self.iso_h,self.iso_z,self.tile_w,self.tile_h,self.base_w,self.base_h
+
+ tile_w,tile_h = self.tile_w,self.tile_h
+ tile_w2,tile_h2 = tile_w/2,tile_h/2
+
+ view = self.view
+ adj = self.adj = pygame.Rect(-self.view.x,-self.view.y,0,0)
+
+ w,h = len(tlayer[0]),len(tlayer)
+ tiles = self.tiles
+
+ #""
+ if self.bounds == None:
+ tmp,y1 = self.tile_to_view((0,0))
+ x1,tmp = self.tile_to_view((0,h+1))
+ tmp,y2 = self.tile_to_view((w+1,h+1))
+ x2,tmp = self.tile_to_view((w+1,0))
+ self.bounds = pygame.Rect(x1,y1,x2-x1,y2-y1)
+ print self.bounds
+ #""
+
+ if self.bounds != None: self.view.clamp_ip(self.bounds)
+
+ ox,oy = self.screen_to_tile((0,0))
+ sx,sy = self.tile_to_view((ox,oy))
+ dx,dy = sx - self.view.x,sy - self.view.y
+
+ bot = 1
+
+ tile_wi = tile_w + tile_w/2
+ tile_wi2 = tile_wi/2
+
+ #dx += tile_w/2
+
+ for i2 in xrange(-bot,self.view.h/tile_h2+bot*3): #NOTE: 3 seems a bit much, but it works.
+ tx,ty = ox + i2/2 + i2%2,oy + i2/2
+ x,y = (i2%2)*tile_wi2 + dx,i2*tile_h2 + dy
+
+ #to adjust for the -1 in i1
+ x,tx,ty = x-tile_wi,tx-1,ty+1
+
+ x -= tile_w/2
+ for i1 in xrange(-1,self.view.w/tile_wi+1):
+ if ty >= 0 and ty < h and tx >= 0 and tx < w:
+ if blayer != None:
+ n = blayer[ty][tx]
+ if n != 0:
+ t = tiles[n]
+ if t != None and t.image != None:
+ screen.blit(t.image,(x,y))
+ n = tlayer[ty][tx]
+ if n != 0:
+ t = tiles[n]
+ if t != None and t.image != None:
+ screen.blit(t.image,(x,y))
+
+
+ tx += 1
+ ty -= 1
+ x += tile_wi
+
+ return [pygame.Rect(0,0,screen.get_width(),screen.get_height())]
+
+ def view_to_tile(self,pos):
+ x,y = pos
+ #x = x + (self.tile_w*1/2)
+
+ x,y = int(x*4/(self.tile_w*3)), y*2/self.tile_h
+ nx = (x + y) / 2
+ ny = (y - x) / 2
+ return nx,ny
+
+ def tile_to_view(self,pos):
+ x,y = pos
+ nx = x - y
+ ny = x + y
+ nx,ny = int(nx*(self.tile_w*3)/4), ny*self.tile_h/2
+
+ #nx = nx - (self.tile_w*1/2)
+ return nx,ny
+
+ def screen_to_tile(self,pos): #NOTE HACK : not sure if the 3/8 is right or not, but it is pretty close...
+ pos = pos[0]+self.view.x + self.tile_w*3/8,pos[1]+self.view.y
+ pos = self.view_to_tile(pos)
+ return pos
+
+ def tile_to_screen(self,pos):
+ pos = self.tile_to_view(pos)
+ pos = pos[0]-self.view.x,pos[1]-self.view.y
+ return pos
+
+
+ def tga_load_tiles(self,fname,size,tdata={}):
+ Vid.tga_load_tiles(self,fname,size,tdata)
+
+ self.tile_w,self.tile_h = size
\ No newline at end of file
--- /dev/null
+"""Classes for handling high score tables.
+"""
+
+import os
+
+def High(fname,limit=10):
+ """Create a Highs object and returns the default high score table.
+
+ <pre>High(fname,limit=10)</pre>
+
+ <dl>
+ <dt>fname <dd>filename to store high scores in
+ <dt>limit <dd>limit of scores to be recorded, defaults to 10
+ </dl>
+ """
+ return Highs(fname,limit)['default']
+
+class _Score:
+ def __init__(self,score,name,data=None):
+ self.score,self.name,self.data=score,name,data
+
+class _High:
+ """A high score table. These objects are passed to the user, but should not be created directly.
+
+ <p>You can iterate them:</p>
+ <code>
+ for e in myhigh:
+ print e.score,e.name,e.data
+ </code>
+
+ <p>You can modify them:</p>
+ <code>
+ myhigh[0].name = 'Cuzco'
+ </code>
+
+ <p>You can find out their length:</p>
+ <code>
+ print len(myhigh)
+ </code>
+ """
+
+ def __init__(self,highs,limit=10):
+ self.highs = highs
+ self._list = []
+ self.limit = limit
+
+ def save(self):
+ """Save the high scores.
+
+ <pre>_High.save()</pre>
+ """
+ self.highs.save()
+
+ def submit(self,score,name,data=None):
+ """Submit a high score to this table.
+
+ <pre>_High.submit(score,name,data=None)</pre>
+
+ <p>return -- the position in the table that the score attained. None if the score did not attain a position in the table.</p>
+ """
+ n = 0
+ for e in self._list:
+ if score > e.score:
+ self._list.insert(n,_Score(score,name,data))
+ self._list = self._list[0:self.limit]
+ return n
+ n += 1
+ if len(self._list) < self.limit:
+ self._list.append(_Score(score,name,data))
+ return len(self._list)-1
+
+ def check(self,score):
+ """Check if a score will attain a position in the table.
+
+ <pre>_High.check(score)</pre>
+
+ <p>return -- the position the score will attain, else None</p>
+ """
+ n = 0
+ for e in self._list:
+ if score > e.score:
+ return n
+ n += 1
+ if len(self._list) < self.limit:
+ return len(self._list)
+
+
+ def __iter__(self):
+ return self._list.__iter__()
+
+ def __getitem__(self,key):
+ return self._list[key]
+
+ def __len__(self):
+ return self._list.__len__()
+
+
+class Highs:
+ """The high score object.
+
+ <pre>Highs(fname,limit=10)</pre>
+ <ul>
+ <dt>fname <dd>filename to store high scores in
+ <dt>limit <dd>limit of scores to be recorded, defaults to 10
+ </ul>
+
+ <p>You may access _High objects through this object:</p>
+
+ <code>
+ my_easy_hs = highs['easy']
+ my_hard_hs = highs['hard']
+ </code>
+
+ """
+ def __init__(self,fname,limit=10):
+ self.fname = fname
+ self.limit = limit
+ self.load()
+
+ def load(self):
+ """Re-load the high scores.
+
+ <pre>Highs.load()</pre>
+ """
+
+ self._dict = {}
+ try:
+ f = open(self.fname)
+ for line in f.readlines():
+ key,score,name,data = line.strip().split("\t")
+ if key not in self._dict:
+ self._dict[key] = _High(self,self.limit)
+ high = self._dict[key]
+ high.submit(int(score),name,data)
+ f.close()
+ except:
+ pass
+
+ def save(self):
+ """Save the high scores.
+
+ <pre>Highs.save()</pre>
+ """
+
+ f = open(self.fname,"w")
+ for key,high in self._dict.items():
+ for e in high:
+ f.write("%s\t%d\t%s\t%s\n"%(key,e.score,e.name,str(e.data)))
+ f.close()
+
+ def __getitem__(self,key):
+ if key not in self._dict:
+ self._dict[key] = _High(self,self.limit)
+ return self._dict[key]
--- /dev/null
+"""a html renderer
+"""
+
+import sys
+import htmllib
+import re
+import pygame
+from pygame.locals import *
+
+from pgu import gui
+
+_amap = {'left':-1,'right':1,'center':0,None:None,'':None,}
+_vamap = {'top':-1,'bottom':1,'center':0,'middle':0,None:None,'':None,}
+
+# Used by the HTML parser to load external resources (like images). This
+# class loads content from the local file system. But you can pass your own
+# resource loader to the HTML parser to find images by other means.
+class ResourceLoader(object):
+ # Loads an image and returns it as a pygame image
+ def load_image(this, path):
+ return pygame.image.load(path)
+
+class _dummy:
+ pass
+
+class _flush:
+ def __init__(self):
+ self.style = _dummy()
+ self.style.font = None
+ self.style.color = None
+ self.cls = None
+ def add(self,w): pass
+ def space(self,v): pass
+
+class _hr(gui.Color):
+ def __init__(self,**params):
+ gui.Color.__init__(self,(0,0,0),**params)
+ def resize(self,width=None,height=None):
+ w,h = self.style.width,self.style.height
+ #if width != None: self.rect.w = width
+ #else: self.rect.w = 1
+
+ #xt,xr,xb,xl = self.getspacing()
+
+ if width != None: w = max(w,width)
+ if height != None: h = max(h,height)
+ w = max(w,1)
+ h = max(h,1)
+
+ return w,h #self.container.rect.w,h
+
+ #self.rect.w = max(1,width,self.container.rect.w-(xl+xr))
+
+ #print self.rect
+ #self.rect.w = 1
+
+class _html(htmllib.HTMLParser):
+ def init(self,doc,font,color,_globals,_locals,loader=None):
+ self.mystack = []
+ self.document = doc
+ if (loader):
+ self.loader = loader
+ else:
+ # Use the default resource loader
+ self.loader = ResourceLoader()
+ self.myopen('document',self.document)
+
+ self.myfont = self.font = font
+ self.mycolor = self.color = color
+
+ self.form = None
+
+ self._globals = _globals
+ self._locals = _locals
+
+ def myopen(self,type_,w):
+
+ self.mystack.append((type_,w))
+ self.type,self.item = type_,w
+
+ self.font = self.item.style.font
+ self.color = self.item.style.color
+
+ if not self.font: self.font = self.myfont
+ if not self.color: self.color = self.mycolor
+
+ def myclose(self,type_):
+ t = None
+ self.mydone()
+ while t != type_:
+ #if len(self.mystack)==0: return
+ t,w = self.mystack.pop()
+ t,w = self.mystack.pop()
+ self.myopen(t,w)
+
+ def myback(self,type_):
+ if type(type_) == str: type_ = [type_,]
+ self.mydone()
+ #print 'myback',type_
+ t = None
+ while t not in type_:
+ #if len(self.mystack)==0: return
+ t,w = self.mystack.pop()
+ self.myopen(t,w)
+
+ def mydone(self):
+ #clearing out the last </p>
+ if not hasattr(self.item,'layout'): return
+ if len(self.item.layout._widgets) == 0: return
+ w = self.item.layout._widgets[-1]
+ if type(w) == tuple:
+ del self.item.layout._widgets[-1]
+
+
+ def start_b(self,attrs): self.font.set_bold(1)
+ def end_b(self): self.font.set_bold(0)
+ def start_i(self,attrs): self.font.set_italic(1)
+ def end_i(self): self.font.set_italic(0)
+ def start_u(self,attrs): self.font.set_underline(1)
+ def end_u(self): self.font.set_underline(0)
+ def start_br(self,attrs): self.do_br(attrs)
+ def do_br(self,attrs): self.item.br(self.font.size(" ")[1])
+ def attrs_to_map(self,attrs):
+ k = None
+ r = {}
+ for k,v in attrs: r[k] = v
+ return r
+
+ def map_to_params(self,r):
+ anum = re.compile("\D")
+
+ params = {'style':{}}
+ style = params['style']
+
+ if 'bgcolor' in r:
+ style['background'] = gui.parse_color(r['bgcolor'])
+ if 'background' in r:
+ style['background'] = self.loader.load_image(r['background'])
+ if 'border' in r: style['border'] = int(r['border'])
+
+ for k in ['width','height','colspan','rowspan','size','min','max']:
+ if k in r: params[k] = int(anum.sub("",r[k]))
+
+ for k in ['name','value']:
+ if k in r: params[k] = r[k]
+
+ if 'class' in r: params['cls'] = r['class']
+
+ if 'align' in r:
+ params['align'] = _amap[r['align']]
+ if 'valign' in r:
+ params['valign'] = _vamap[r['valign']]
+
+ if 'style' in r:
+ for st in r['style'].split(";"):
+ #print st
+ if ":" in st:
+ #print st.split(":")
+ k,v = st.split(":")
+ k = k.replace("-","_")
+ k = k.replace(" ","")
+ v = v.replace(" ","")
+ if k == 'color' or k == 'border_color' or k == 'background':
+ v = gui.parse_color(v)
+ else:
+ v = int(anum.sub("",v))
+ style[k] = v
+ return params
+
+ def map_to_connects(self,e,r):
+ for k,evt in [('onclick',gui.CLICK),('onchange',gui.CHANGE)]: #blah blah blah
+
+ if k in r:
+ #print k,r[k]
+ e.connect(evt,self.myexec,(e,r[k]))
+
+ def start_p(self,attrs):
+ r = self.attrs_to_map(attrs)
+ align = r.get("align","left")
+
+ self.check_p()
+ self.item.block(_amap[align])
+
+ def check_p(self):
+ if len(self.item.layout._widgets) == 0: return
+ if type(self.item.layout._widgets[-1]) == tuple:
+ w,h = self.item.layout._widgets[-1]
+ if w == 0: return
+ self.do_br(None)
+
+ def end_p(self):
+ #print 'end p'
+ self.check_p()
+
+
+ def start_block(self,t,attrs,align=-1):
+ r = self.attrs_to_map(attrs)
+ params = self.map_to_params(r)
+ if 'cls' in params: params['cls'] = t+"."+params['cls']
+ else: params['cls'] = t
+ b = gui.Document(**params)
+ b.style.font = self.item.style.font
+ if 'align' in params:
+ align = params['align']
+ self.item.block(align)
+ self.item.add(b)
+ self.myopen(t,b)
+
+
+
+ def end_block(self,t):
+ self.myclose(t)
+ self.item.block(-1)
+
+ def start_div(self,attrs): self.start_block('div',attrs)
+ def end_div(self): self.end_block('div')
+ def start_center(self,attrs): self.start_block('div',attrs,0)
+ def end_center(self): self.end_block('div')
+
+ def start_h1(self,attrs): self.start_block('h1',attrs)
+ def end_h1(self): self.end_block('h1')
+ def start_h2(self,attrs): self.start_block('h2',attrs)
+ def end_h2(self): self.end_block('h2')
+ def start_h3(self,attrs): self.start_block('h3',attrs)
+ def end_h3(self): self.end_block('h3')
+ def start_h4(self,attrs): self.start_block('h4',attrs)
+ def end_h4(self): self.end_block('h4')
+ def start_h5(self,attrs): self.start_block('h5',attrs)
+ def end_h5(self): self.end_block('h5')
+ def start_h6(self,attrs): self.start_block('h6',attrs)
+ def end_h6(self): self.end_block('h6')
+
+ def start_ul(self,attrs): self.start_block('ul',attrs)
+ def end_ul(self): self.end_block('ul')
+ def start_ol(self,attrs):
+ self.start_block('ol',attrs)
+ self.item.counter = 0
+ def end_ol(self): self.end_block('ol')
+ def start_li(self,attrs):
+ self.myback(['ul','ol'])
+ cur = self.item
+ self.start_block('li',attrs)
+ if hasattr(cur,'counter'):
+ cur.counter += 1
+ self.handle_data("%d. "%cur.counter)
+ else:
+ self.handle_data("- ")
+ #def end_li(self): self.end_block('li') #this isn't needed because of how the parser works
+
+ def start_pre(self,attrs): self.start_block('pre',attrs)
+ def end_pre(self): self.end_block('pre')
+ def start_code(self,attrs): self.start_block('code',attrs)
+ def end_code(self): self.end_block('code')
+
+ def start_table(self,attrs):
+ r = self.attrs_to_map(attrs)
+ params = self.map_to_params(r)
+
+ align = r.get("align","left")
+ self.item.block(_amap[align])
+
+ t = gui.Table(**params)
+ self.item.add(t)
+
+ self.myopen('table',t)
+
+ def start_tr(self,attrs):
+ self.myback('table')
+ self.item.tr()
+
+ def _start_td(self,t,attrs):
+ r = self.attrs_to_map(attrs)
+ params = self.map_to_params(r)
+ if 'cls' in params: params['cls'] = t+"."+params['cls']
+ else: params['cls'] = t
+ b = gui.Document(cls=t)
+
+ self.myback('table')
+ self.item.td(b,**params)
+ self.myopen(t,b)
+
+ self.font = self.item.style.font
+ self.color = self.item.style.color
+
+ def start_td(self,attrs):
+ self._start_td('td',attrs)
+
+ def start_th(self,attrs):
+ self._start_td('th',attrs)
+
+ def end_table(self):
+ self.myclose('table')
+ self.item.block(-1)
+
+ def start_form(self,attrs):
+ r = self.attrs_to_map(attrs)
+ e = self.form = gui.Form()
+ e.groups = {}
+
+ self._locals[r.get('id',None)] = e
+
+ def start_input(self,attrs):
+ r = self.attrs_to_map(attrs)
+ params = self.map_to_params(r) #why bother
+ #params = {}
+
+ type_,name,value = r.get('type','text'),r.get('name',None),r.get('value',None)
+ f = self.form
+ if type_ == 'text':
+ e = gui.Input(**params)
+ self.map_to_connects(e,r)
+ self.item.add(e)
+ elif type_ == 'radio':
+ if name not in f.groups:
+ f.groups[name] = gui.Group(name=name)
+ g = f.groups[name]
+ del params['name']
+ e = gui.Radio(group=g,**params)
+ self.map_to_connects(e,r)
+ self.item.add(e)
+ if 'checked' in r: g.value = value
+ elif type_ == 'checkbox':
+ if name not in f.groups:
+ f.groups[name] = gui.Group(name=name)
+ g = f.groups[name]
+ del params['name']
+ e = gui.Checkbox(group=g,**params)
+ self.map_to_connects(e,r)
+ self.item.add(e)
+ if 'checked' in r: g.value = value
+
+ elif type_ == 'button':
+ e = gui.Button(**params)
+ self.map_to_connects(e,r)
+ self.item.add(e)
+ elif type_ == 'submit':
+ e = gui.Button(**params)
+ self.map_to_connects(e,r)
+ self.item.add(e)
+ elif type_ == 'file':
+ e = gui.Input(**params)
+ self.map_to_connects(e,r)
+ self.item.add(e)
+ b = gui.Button(value='Browse...')
+ self.item.add(b)
+ def _browse(value):
+ d = gui.FileDialog();
+ d.connect(gui.CHANGE,gui.action_setvalue,(d,e))
+ d.open();
+ b.connect(gui.CLICK,_browse,None)
+
+ self._locals[r.get('id',None)] = e
+
+ def start_object(self,attrs):
+ r = self.attrs_to_map(attrs)
+ params = self.map_to_params(r)
+ code = "e = %s(**params)"%r['type']
+ #print code
+ #print params
+ exec(code)
+ #print e
+ #print e.style.width,e.style.height
+ self.map_to_connects(e,r)
+ self.item.add(e)
+
+ self._locals[r.get('id',None)] = e
+
+ def start_select(self,attrs):
+ r = self.attrs_to_map(attrs)
+ params = {}
+
+ name,value = r.get('name',None),r.get('value',None)
+ e = gui.Select(name=name,value=value,**params)
+ self.map_to_connects(e,r)
+ self.item.add(e)
+ self.myopen('select',e)
+
+ def start_option(self,attrs):
+ r = self.attrs_to_map(attrs)
+ params = {} #style = self.map_to_style(r)
+
+ self.myback('select')
+ e = gui.Document(**params)
+ self.item.add(e,value=r.get('value',None))
+ self.myopen('option',e)
+
+
+ def end_select(self):
+ self.myclose('select')
+
+ def start_hr(self,attrs):
+ self.do_hr(attrs)
+ def do_hr(self,attrs):
+ h = self.font.size(" ")[1]/2
+
+ r = self.attrs_to_map(attrs)
+ params = self.map_to_params(r)
+ params['style']['padding'] = h
+ print params
+
+ self.item.block(0)
+ self.item.add(_hr(**params))
+ self.item.block(-1)
+
+ def anchor_begin(self,href,name,type_):
+ pass
+
+ def anchor_end(self):
+ pass
+
+ def start_title(self,attrs): self.myopen('title',_flush())
+ def end_title(self): self.myclose('title')
+
+ def myexec(self,value):
+ w,code = value
+ g = self._globals
+ l = self._locals
+ l['self'] = w
+ exec(code,g,l)
+
+ def handle_image(self,src,alt,ismap,align,width,height):
+ try:
+ w = gui.Image(self.loader.load_image(src))
+ if align != '':
+ self.item.add(w,_amap[align])
+ else:
+ self.item.add(w)
+ except:
+ print 'handle_image: missing %s'%src
+
+ def handle_data(self,txt):
+ if self.type == 'table': return
+ elif self.type in ('pre','code'):
+ txt = txt.replace("\t"," ")
+ ss = txt.split("\n")
+ if ss[-1] == "": del ss[-1]
+ for sentence in ss:
+ img = self.font.render(sentence,1,self.color)
+ w = gui.Image(img)
+ self.item.add(w)
+ self.item.block(-1)
+ return
+
+ txt = re.compile("^[\t\r\n]+").sub("",txt)
+ txt = re.compile("[\t\r\n]+$").sub("",txt)
+
+ tst = re.compile("[\t\r\n]+").sub("",txt)
+ if tst == "": return
+
+ txt = re.compile("\s+").sub(" ",txt)
+ if txt == "": return
+
+ if txt == " ":
+ self.item.space(self.font.size(" "))
+ return
+
+ for word in txt.split(" "):
+ word = word.replace(chr(160)," ") #
+ #print self.item.cls
+ w = gui.Image(self.font.render(word,1,self.color))
+ self.item.add(w)
+ self.item.space(self.font.size(" "))
+
+
+class HTML(gui.Document):
+ """a gui HTML object
+
+ <pre>HTML(data,globals=None,locals=None)</pre>
+
+ <dl>
+ <dt>data <dd>html data
+ <dt>globals <dd>global variables (for scripting)
+ <dt>locals <dd>local variables (for scripting)
+ <dt>loader <dd>the resource loader
+ </dl>
+
+ <p>you may access html elements that have an id via widget[id]</p>
+ """
+ def __init__(self,data,globals=None,locals=None,loader=None,**params):
+ gui.Document.__init__(self,**params)
+ # This ensures that the whole HTML document is left-aligned within
+ # the rendered surface.
+ self.style.align = -1
+
+ _globals,_locals = globals,locals
+
+ if _globals == None: _globals = {}
+ if _locals == None: _locals = {}
+ self._globals = _globals
+ self._locals = _locals
+
+ #font = gui.theme.get("label","","font")
+ p = _html(htmllib.AS_IS,0)
+ p.init(self,self.style.font,self.style.color,_globals,_locals,
+ loader=loader)
+ p.feed(data)
+ p.close()
+ p.mydone()
+
+
+ def __getitem__(self,k):
+ return self._locals[k]
+
+ # Returns a box (pygame rectangle) surrounding all widgets in this document
+ def get_bounding_box(this):
+ minx = miny = sys.maxint
+ maxx = maxy = -sys.maxint
+ for e in this.layout.widgets:
+ minx = min(minx, e.rect.left)
+ miny = min(miny, e.rect.top)
+ maxx = max(maxx, e.rect.right+1)
+ maxy = max(maxy, e.rect.bottom+1)
+ return pygame.Rect(minx, miny, maxx-minx, maxy-miny)
+
+
+def render_ext(font, rect, text, aa, color, bgcolor=(0,0,0,0), **params):
+ """Renders some html and returns the rendered surface, plus the
+ HTML instance that produced it.
+ """
+
+ htm = HTML(text, font=font, color=color, **params)
+
+ if (rect == -1):
+ # Make the surface large enough to fit the rendered text
+ htm.resize(width=sys.maxint)
+ (width, height) = htm.get_bounding_box().size
+ # Now set the proper document width (computed from the bounding box)
+ htm.resize(width=width)
+ elif (type(rect) == int):
+ # Fix the width of the document, while the height is variable
+ width = rect
+ height = htm.resize(width=width)[1]
+ else:
+ # Otherwise the width and height of the document is fixed
+ (width, height) = rect.size
+ htm.resize(width=width)
+
+ # Now construct a surface and paint to it
+ surf = pygame.Surface((width, height)).convert_alpha()
+ surf.fill(bgcolor)
+ htm.paint(surf)
+ return (surf, htm)
+
+def render(font, rect, text, aa, color, bgcolor=(0,0,0,0), **params):
+ """Renders some html
+
+ <pre>render(font,rect,text,aa,color,bgcolor=(0,0,0,0))</pre>
+ """
+ return render_ext(font, rect, text, aa, color, bgcolor, **params)[0]
+
+def rendertrim(font,rect,text,aa,color,bgcolor=(0,0,0,0),**params):
+ """render html, and make sure to trim the size
+
+ rendertrim(font,rect,text,aa,color,bgcolor=(0,0,0,0))
+ """
+ # Render the HTML
+ (surf, htm) = render_ext(font, rect, text, aa, color, bgcolor, **params)
+ return surf.subsurface(htm.get_bounding_box())
+
+
+def write(s,font,rect,text,aa=0,color=(0,0,0), **params):
+ """write html to a surface
+
+ write(s,font,rect,text,aa=0,color=(0,0,0))
+ """
+ htm = HTML(text, font=font, color=color, **params)
+ htm.resize(width=rect.w)
+ s = s.subsurface(rect)
+ htm.paint(s)
+
+# vim: set filetype=python sts=4 sw=4 noet si :
--- /dev/null
+"""Isometric tile engine.
+
+<p>Note -- this engine is not finished, any may not work for your
+particular needs. If you are able to update it, help would be
+greatly appreciated!</p>
+
+<p>please note that this file is alpha, and is subject to modification in
+future versions of pgu!</p>
+
+"""
+print 'pgu.isovid','This module is alpha, and is subject to change.'
+
+from pgu.vid import *
+import pygame
+
+class Isovid(Vid):
+ """Create an iso vid engine. See [[vid]]"""
+ def update(self,screen):
+ return self.paint(screen)
+
+ def paint(self,screen):
+ sw,sh = screen.get_width(),screen.get_height()
+
+ tlayer = self.tlayer
+ blayer = self.blayer
+ zlayer = self.zlayer
+ w,h = len(tlayer[0]),len(tlayer)
+
+ iso_w,iso_h,iso_z,tile_w,tile_h,base_w,base_h = self.iso_w,self.iso_h,self.iso_z,self.tile_w,self.tile_h,self.base_w,self.base_h
+
+ base_h2 = base_h/2
+ base_w2 = base_w/2
+
+ bot = tile_h/base_h2
+ todo_max = sh/base_h2+bot
+ todo = [[] for y in xrange(0,todo_max)]
+
+ self.view.w,self.view.h = sw,sh
+ view = self.view
+ adj = self.adj = pygame.Rect(-self.view.x,-self.view.y,0,0)
+
+ for s in self.sprites:
+ self.sprite_calc_irect(s)
+ x,y = self.iso_to_view((s.rect.centerx,s.rect.centery))
+ v = (y+adj.y)/base_h2 - 1
+ if v >= 0 and v < todo_max:
+ todo[v].append((s.image,s.irect))
+ #else: print 'doesnt fit',v
+
+ w,h = len(tlayer[0]),len(tlayer)
+ tiles = self.tiles
+
+ #""
+ if self.bounds == None:
+ tmp,y1 = self.tile_to_view((0,0))
+ x1,tmp = self.tile_to_view((0,h+1))
+ tmp,y2 = self.tile_to_view((w+1,h+1))
+ x2,tmp = self.tile_to_view((w+1,0))
+ self.bounds = pygame.Rect(x1,y1,x2-x1,y2-y1)
+ #""
+
+ if self.bounds != None: self.view.clamp_ip(self.bounds)
+
+ ox,oy = self.screen_to_tile((0,0))
+ sx,sy = self.iso_to_view((ox*iso_w,oy*iso_h))
+ dx,dy = sx - self.view.x,sy - self.view.y
+
+ for i2 in xrange(-bot,self.view.h/base_h2+bot):
+ tx,ty = ox + i2/2 + i2%2,oy + i2/2
+ x,y = (i2%2)*base_w2 + dx,i2*base_h2 + dy
+
+ #to adjust for the -1 in i1
+ x,tx,ty = x-base_w,tx-1,ty+1
+ for i1 in xrange(-1,self.view.w/base_w+2): #NOTE: not sure why +2
+ if ty >= 0 and ty < h and tx >= 0 and tx < w:
+ z = zlayer[ty][tx]*iso_z
+ if blayer != None:
+ n = blayer[ty][tx]
+ if n != 0:
+ t = tiles[n]
+ if t != None and t.image != None:
+ screen.blit(t.image,(x-base_w2,y+z))
+ n = tlayer[ty][tx]
+ if n != 0:
+ t = tiles[n]
+ if t != None and t.image != None:
+ screen.blit(t.image,(x-base_w2,y-(t.image_h-base_h)+z))
+
+ tx += 1
+ ty -= 1
+ x += base_w
+ for img,irect in todo[y/base_h2]:
+ screen.blit(img,(irect.x+adj.x,irect.y+adj.y))
+
+ return [pygame.Rect(0,0,screen.get_width(),screen.get_height())]
+
+ def iso_to_view(self,pos):
+ tlayer = self.tlayer
+ w,h = len(tlayer[0]),len(tlayer)
+
+ x,y = pos
+
+ #nx,ny = (h*self.iso_w + x - y)/2, (0 + x + y)/2
+ nx,ny = (x - y)/2, (0 + x + y)/2
+
+ return (nx * self.base_w / self.iso_w), (ny * self.base_h / self.iso_h)
+
+ def view_to_iso(self,pos):
+ tlayer = self.tlayer
+ w,h = len(tlayer[0]),len(tlayer)
+
+ x,y = pos
+
+ x,y = x*self.iso_w/self.base_w, y*self.iso_h/self.base_h
+
+ #x -= (self.iso_w/2) * h
+ #x -= (self.iso_w/2) * h
+
+ nx = (x+y)
+ ny = y*2-nx
+
+ return nx,ny
+
+ def tile_to_view(self,pos):
+ return self.iso_to_view((pos[0]*self.iso_w,pos[1]*self.iso_h))
+
+ def screen_to_tile(self,pos):
+ x,y = pos
+ x += self.view.x
+ y += self.view.y
+ x,y = self.view_to_iso((x,y))
+ return x/self.iso_w,y/self.iso_h
+
+ def tile_to_screen(self,pos):
+ x,y = self.iso_to_view((pos[0]*self.iso_w,pos[1]*self.iso_h))
+ return x-self.view.x,y-self.view.y
+
+ def tga_load_tiles(self,fname,size,tdata={}):
+ Vid.tga_load_tiles(self,fname,size,tdata)
+
+ self.tile_w,self.tile_h = size
+ self.iso_w,self.iso_h,self.iso_z = self.tile_w,self.tile_w,1
+ self.base_w,self.base_h = self.tile_w,self.tile_w/2
+
+
+
+ def resize(self,size,bg=0):
+ Vid.resize(self,size,bg)
+
+ tlayer = self.tlayer
+ w,h = len(tlayer[0]),len(tlayer)
+
+ self.zlayer = [[0 for x in xrange(0,w)] for y in xrange(0,h)]
+
+
+
+
+ def sprite_calc_irect(self,s):
+ tlayer = self.tlayer
+ w,h = len(tlayer[0]),len(tlayer)
+ zlayer = self.zlayer
+
+ x,y = self.iso_to_view((s.rect.centerx,s.rect.centery))
+ tx,ty = s.rect.centerx/self.iso_w,s.rect.centery/self.iso_h
+ z = 0
+ if ty >= 0 and ty < h and tx >= 0 and tx < w:
+ z = zlayer[ty][tx]*self.iso_z
+
+ nx,ny = x - s.shape.centerx, y - s.shape.centery + z
+
+ s.irect.x,s.irect.y = nx,ny
+
+ def run_codes(self,cdata,rect):
+ #HACK to make run_codes work
+ w,h = self.iso_w,self.iso_h
+
+ img = self.tiles[0].image
+
+ self.tiles[0].image = pygame.Surface((w,h))
+ r = Vid.run_codes(self,cdata,rect)
+ self.tiles[0].image = img
+ return r
--- /dev/null
+print 'pgu.layout','Scheduled to be deprecated.'
+
+from pgu.gui.layout import *
+
--- /dev/null
+"""a collection of text rendering functions
+"""
+def write(s,font,pos,color,text,border=1):
+ """write text to a surface with a black border
+
+ <pre>write(s,font,pos,color,text,border=1)</pre>
+ """
+ i = font.render(text,1,(0,0,0))
+ si = border
+ dirs = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]
+ for dx,dy in dirs: s.blit(i,(pos[0]+dx*si,pos[1]+dy*si))
+ i = font.render(text,1,color)
+ s.blit(i,pos)
+
+def writec(s,font,color,text,border=1):
+ """write centered text to a surface with a black border
+
+ <pre>writec(s,font,color,text,border=1)</pre>
+ """
+ w,h = font.size(text)
+ x = (s.get_width()-w)/2
+ y = (s.get_height()-h)/2
+ write(s,font,(x,y),color,text,border)
+
+def writepre(s,font,rect,color,text):
+ """write preformatted text
+
+ <pre>writepre(s,font,rect,color,text)</pre>
+ """
+ r,c,txt = rect,color,text
+ txt = txt.replace("\t"," ")
+ i = font.render(" ",1,c)
+ sw,sh = i.get_width(),i.get_height()
+ y = r.top
+ for sentence in txt.split("\n"):
+ x = r.left
+ i = font.render(sentence,1,c)
+ s.blit(i,(x,y))
+ y += sh
+
+def writewrap(s,font,rect,color,text):
+ """write wrapped text
+
+ <pre>writewrap(s,font,rect,color,text)</pre>
+ """
+ r,c,txt = rect,color,text
+ txt = txt.replace("\t"," ")
+ i = font.render(" ",1,c)
+ sw,sh = i.get_width(),i.get_height()
+ y = r.top
+ for sentence in txt.split("\n"):
+ x = r.left
+ for word in sentence.split(" "):
+ i = font.render(word,1,c)
+ iw,ih = i.get_width(),i.get_height()
+ if x+iw > r.right: x,y = r.left,y+sh
+ s.blit(i,(x,y))
+ x += iw+sw
+ y += sh
+
+# vim: set filetype=python sts=4 sw=4 noet si :
--- /dev/null
+"""Square tile based engine."""
+
+from pgu.vid import *
+import pygame
+
+class Tilevid(Vid):
+ """Based on [[vid]] -- see for reference."""
+ def paint(self,s):
+ sw,sh = s.get_width(),s.get_height()
+ self.view.w,self.view.h = sw,sh
+
+ tiles = self.tiles
+ tw,th = tiles[0].image.get_width(),tiles[0].image.get_height()
+ w,h = self.size
+
+ if self.bounds != None: self.view.clamp_ip(self.bounds)
+
+ ox,oy = self.view.x,self.view.y
+ tlayer = self.tlayer
+ blayer = self.blayer
+ alayer = self.alayer
+ sprites = self.sprites
+
+ blit = s.blit
+ yy = - (self.view.y%th)
+ my = (oy+sh)/th
+ if (oy+sh)%th: my += 1
+
+ if blayer != None:
+ for y in xrange(oy/th,my):
+ if y >=0 and y < h:
+ trow = tlayer[y]
+ brow = blayer[y]
+ arow = alayer[y]
+ xx= - (self.view.x%tw)
+ mx = (ox+sw)/tw
+ #if (ox+sh)%tw: mx += 1
+ for x in xrange(ox/tw,mx+1):
+ if x >=0and x<w:
+ blit(tiles[brow[x]].image,(xx,yy))
+ blit(tiles[trow[x]].image,(xx,yy))
+ arow[x]=0
+ xx += tw
+ yy+=th
+ else:
+ for y in xrange(oy/th,my):
+ if y >=0 and y<h:
+ trow = tlayer[y]
+ arow = alayer[y]
+ xx= - (self.view.x%tw)
+ mx = (ox+sw)/tw
+ #if (ox+sh)%tw: mx += 1
+ for x in xrange(ox/tw,mx+1):
+ if x >=0 and x<w:
+ blit(tiles[trow[x]].image,(xx,yy))
+ arow[x]=0
+ xx += tw
+ yy+=th
+
+ for s in sprites:
+ s.irect.x = s.rect.x-s.shape.x
+ s.irect.y = s.rect.y-s.shape.y
+ blit(s.image,(s.irect.x-ox,s.irect.y-oy))
+ s.updated=0
+ s._irect = Rect(s.irect)
+ #s._rect = Rect(s.rect)
+
+ self.updates = []
+ self._view = pygame.Rect(self.view)
+ return [Rect(0,0,sw,sh)]
+
+ def update(self,s):
+ sw,sh = s.get_width(),s.get_height()
+ self.view.w,self.view.h = sw,sh
+
+ if self.bounds != None: self.view.clamp_ip(self.bounds)
+ if self.view.x != self._view.x or self.view.y != self._view.y:
+ return self.paint(s)
+
+ ox,oy = self.view.x,self.view.y
+ sw,sh = s.get_width(),s.get_height()
+ w,h = self.size
+ tlayer = self.tlayer
+ blayer = self.blayer
+ alayer = self.alayer
+ tiles = self.tiles
+ tw,th = tiles[0].image.get_width(),tiles[0].image.get_height()
+ sprites = self.sprites
+ blit = s.blit
+
+ us = []
+
+ #mark places where sprites have moved, or been removed
+
+ ss = self.sprites.removed
+ self.sprites.removed = []
+ ss.extend(sprites)
+ for s in ss:
+ #figure out what has been updated.
+ s.irect.x = s.rect.x-s.shape.x
+ s.irect.y = s.rect.y-s.shape.y
+ if (s.irect.x != s._irect.x or s.irect.y != s._irect.y
+ or s.image != s._image):
+ #w,h can be skipped, image covers that...
+ s.updated = 1
+ if s.updated:
+ r = s._irect
+ y = max(0,r.y/th)
+ yy = min(h,r.bottom/th+1)
+ while y < yy:
+ x = max(0,r.x/tw)
+ xx = min(w,r.right/tw+1)
+ while x < xx:
+ if alayer[y][x] == 0:
+ self.updates.append((x,y))
+ alayer[y][x]=1
+ x += 1
+ y += 1
+
+ r = s.irect
+ y = max(0,r.y/th)
+ yy = min(h,r.bottom/th+1)
+ while y < yy:
+ x = r.x/tw
+ xx = min(w,r.right/tw+1)
+ while x < xx:
+ if alayer[y][x]==0:
+ alayer[y][x]=2
+ self.updates.append((x,y))
+ x += 1
+ y += 1
+
+
+ #mark sprites that are not being updated that need to be updated because
+ #they are being overwritte by sprites / tiles
+ for s in sprites:
+ if s.updated==0:
+ r = s.irect
+ y = max(0,r.y/th)
+ yy = min(h,r.bottom/th+1)
+ while y < yy:
+ x = max(0,r.x/tw)
+ xx = min(w,r.right/tw+1)
+ while x < xx:
+ if alayer[y][x]==1:
+ s.updated=1
+ x += 1
+ y += 1
+
+
+ for u in self.updates:
+ x,y=u
+ xx,yy=x*tw-ox,y*th-oy
+ if alayer[y][x] == 1:
+ if blayer != None: blit(tiles[blayer[y][x]].image,(xx,yy))
+ blit(tiles[tlayer[y][x]].image,(xx,yy))
+ alayer[y][x]=0
+ us.append(Rect(xx,yy,tw,th))
+
+ for s in sprites:
+ if s.updated:
+ blit(s.image,(s.irect.x-ox, s.irect.y-oy))
+ s.updated=0
+ s._irect = Rect(s.irect)
+ s._image = s.image
+
+ self.updates = []
+ return us
+
+ def view_to_tile(self,pos):
+ x,y = pos
+ tiles = self.tiles
+ tw,th = tiles[0].image.get_width(),tiles[0].image.get_height()
+ return x/tw,y/th
+
+ def tile_to_view(self,pos):
+ x,y = pos
+ tiles = self.tiles
+ tw,th = tiles[0].image.get_width(),tiles[0].image.get_height()
+ x,y = x*tw, y*th
+ return x,y
+
+
+ def screen_to_tile(self,pos):
+ x,y = pos
+ x,y = x+self.view.x,y+self.view.y
+ return self.view_to_tile((x,y))
+
+ def tile_to_screen(self,pos):
+ x,y = pos
+ x,y = self.tile_to_view(pos)
+ x,y = x - self.view.x, y - self.view.y
+ return x,y
+
+# vim: set filetype=python sts=4 sw=4 noet si :
--- /dev/null
+"""A timer for games with set-rate FPS.
+"""
+
+import pygame
+
+class Timer:
+ """A timer for games with set-rate FPS.
+
+ <pre>Timer(fps)</pre>
+ """
+
+ def __init__(self,fps):
+ if fps == 0:
+ self.tick = self._blank
+ return
+ self.wait = 1000/fps
+ self.nt = pygame.time.get_ticks()
+ pygame.time.wait(0)
+
+ def _blank(self):
+ pass
+
+ def tick(self):
+ """Wait correct amount of time each frame. Call this once per frame.
+
+ <pre>Timer.tick()</pre>
+ """
+ self.ct = pygame.time.get_ticks()
+ if self.ct < self.nt:
+ pygame.time.wait(self.nt-self.ct)
+ self.nt+=self.wait
+ else:
+ self.nt = pygame.time.get_ticks()+self.wait
+
+
+class Speedometer:
+ """A timer replacement that returns out FPS once a second.
+ <pre>Speedometer()</pre>
+
+ <strong>Attributes</strong>
+ <dl>
+ <dt>fps <dd>always set to the current FPS
+ </dl>
+ """
+ def __init__(self):
+ self.frames = 0
+ self.st = pygame.time.get_ticks()
+ self.fps = 0
+
+ def tick(self):
+ """ Call this once per frame.
+
+ <pre>Speedometer.tick()</pre>
+ """
+ r = None
+ self.frames += 1
+ self.ct = pygame.time.get_ticks()
+ if (self.ct - self.st) >= 1000:
+ r = self.fps = self.frames
+ #print "%s: %d fps"%(self.__class__.__name__,self.fps)
+ self.frames = 0
+ self.st += 1000
+ pygame.time.wait(0) #NOTE: not sure why, but you gotta call this now and again
+ return r
+
+
+
+# vim: set filetype=python sts=4 sw=4 noet si :
--- /dev/null
+"""Sprite and tile engine.
+
+<p>[[tilevid]], [[isovid]], [[hexvid]] are all subclasses of
+this interface.</p>
+
+<p>Includes support for:</p>
+
+<ul>
+<li> Foreground Tiles
+<li> Background Tiles
+<li> Sprites
+<li> Sprite-Sprite Collision handling
+<li> Sprite-Tile Collision handling
+<li> Scrolling
+<li> Loading from PGU tile and sprite formats (optional)
+<li> Set rate FPS (optional)
+</ul>
+
+<p>This code was previously known as the King James Version (named after the
+Bible of the same name for historical reasons.)</p>
+"""
+
+import pygame
+from pygame.rect import Rect
+from pygame.locals import *
+import math
+
+class Sprite:
+ """The object used for Sprites.
+
+ <pre>Sprite(ishape,pos)</pre>
+
+ <dl>
+ <dt>ishape <dd>an image, or an image, rectstyle. The rectstyle will
+ describe the shape of the image, used for collision
+ detection.
+ <dt>pos <dd>initial (x,y) position of the Sprite.
+ </dl>
+
+ <strong>Attributes</strong>
+ <dl>
+ <dt>rect <dd>the current position of the Sprite
+ <dt>_rect <dd>the previous position of the Sprite
+ <dt>groups <dd>the groups the Sprite is in
+ <dt>agroups <dd>the groups the Sprite can hit in a collision
+ <dt>hit <dd>the handler for hits -- hit(g,s,a)
+ <dt>loop <dd>the loop handler, called once a frame
+ </dl>
+ """
+ def __init__(self,ishape,pos):
+ if not isinstance(ishape, tuple):
+ ishape = ishape,None
+ image,shape = ishape
+ if shape == None:
+ shape = pygame.Rect(0,0,image.get_width(),image.get_height())
+ if isinstance(shape, tuple): shape = pygame.Rect(shape)
+ self.image = image
+ self._image = self.image
+ self.shape = shape
+ self.rect = pygame.Rect(pos[0],pos[1],shape.w,shape.h)
+ self._rect = pygame.Rect(self.rect)
+ self.irect = pygame.Rect(pos[0]-self.shape.x,pos[1]-self.shape.y,
+ image.get_width(),image.get_height())
+ self._irect = pygame.Rect(self.irect)
+ self.groups = 0
+ self.agroups = 0
+ self.updated = 1
+
+ def setimage(self,ishape):
+ """Set the image of the Sprite.
+
+ <pre>Sprite.setimage(ishape)</pre>
+
+ <dl>
+ <dt>ishape <dd>an image, or an image, rectstyle. The rectstyle will
+ describe the shape of the image, used for collision detection.
+ </dl>
+ """
+ if not isinstance(ishape, tuple):
+ ishape = ishape,None
+ image,shape = ishape
+ if shape == None:
+ shape = pygame.Rect(0,0,image.get_width(),image.get_height())
+ if isinstance(shape, tuple):
+ shape = pygame.Rect(shape)
+ self.image = image
+ self.shape = shape
+ self.rect.w,self.rect.h = shape.w,shape.h
+ self.irect.w,self.irect.h = image.get_width(),image.get_height()
+ self.updated = 1
+
+
+class Tile:
+ """Tile Object used by TileCollide.
+
+ <pre>Tile(image=None)</pre>
+ <dl>
+ <dt>image <dd>an image for the Tile.
+ </dl>
+
+ <strong>Attributes</strong>
+ <dl>
+ <dt>agroups <dd>the groups the Tile can hit in a collision
+ <dt>hit <dd>the handler for hits -- hit(g,t,a)
+ </dl>
+ """
+ def __init__(self,image=None):
+ self.image = image
+ self.agroups = 0
+
+ def __setattr__(self,k,v):
+ if k == 'image' and v != None:
+ self.image_h = v.get_height()
+ self.image_w = v.get_width()
+ self.__dict__[k] = v
+
+class _Sprites(list):
+ def __init__(self):
+ list.__init__(self)
+ self.removed = []
+
+ def append(self,v):
+ list.append(self,v)
+ v.updated = 1
+
+ def remove(self,v):
+ list.remove(self,v)
+ v.updated = 1
+ self.removed.append(v)
+
+class Vid:
+ """An engine for rendering Sprites and Tiles.
+
+ <pre>Vid()</pre>
+
+ <strong>Attributes</strong>
+ <dl>
+ <dt>sprites <dd>a list of the Sprites to be displayed. You may append and
+ remove Sprites from it.
+ <dt>images <dd>a dict for images to be put in.
+ <dt>size <dd>the width, height in Tiles of the layers. Do not modify.
+ <dt>view <dd>a pygame.Rect of the viewed area. You may change .x, .y,
+ etc to move the viewed area around.
+ <dt>bounds <dd>a pygame.Rect (set to None by default) that sets the bounds
+ of the viewable area. Useful for setting certain borders
+ as not viewable.
+ <dt>tlayer <dd>the foreground tiles layer
+ <dt>clayer <dd>the code layer (optional)
+ <dt>blayer <dd>the background tiles layer (optional)
+ <dt>groups <dd>a hash of group names to group values (32 groups max, as a tile/sprites
+ membership in a group is determined by the bits in an integer)
+ </dl>
+ """
+
+ def __init__(self):
+ self.tiles = [None for x in xrange(0,256)]
+ self.sprites = _Sprites()
+ self.images = {} #just a store for images.
+ self.layers = None
+ self.size = None
+ self.view = pygame.Rect(0,0,0,0)
+ self._view = pygame.Rect(self.view)
+ self.bounds = None
+ self.updates = []
+ self.groups = {}
+
+
+ def resize(self,size,bg=0):
+ """Resize the layers.
+
+ <pre>Vid.resize(size,bg=0)</pre>
+
+ <dl>
+ <dt>size <dd>w,h in Tiles of the layers
+ <dt>bg <dd>set to 1 if you wish to use both a foreground layer and a
+ background layer
+ </dl>
+ """
+ self.size = size
+ w,h = size
+ self.layers = [[[0 for x in xrange(0,w)] for y in xrange(0,h)]
+ for z in xrange(0,4)]
+ self.tlayer = self.layers[0]
+ self.blayer = self.layers[1]
+ if not bg: self.blayer = None
+ self.clayer = self.layers[2]
+ self.alayer = self.layers[3]
+
+ self.view.x, self.view.y = 0,0
+ self._view.x, self.view.y = 0,0
+ self.bounds = None
+
+ self.updates = []
+
+ def set(self,pos,v):
+ """Set a tile in the foreground to a value.
+
+ <p>Use this method to set tiles in the foreground, as it will make
+ sure the screen is updated with the change. Directly changing
+ the tlayer will not guarantee updates unless you are using .paint()
+ </p>
+
+ <pre>Vid.set(pos,v)</pre>
+
+ <dl>
+ <dt>pos <dd>(x,y) of tile
+ <dt>v <dd>value
+ </dl>
+ """
+ if self.tlayer[pos[1]][pos[0]] == v: return
+ self.tlayer[pos[1]][pos[0]] = v
+ self.alayer[pos[1]][pos[0]] = 1
+ self.updates.append(pos)
+
+ def get(self,pos):
+ """Get the tlayer at pos.
+
+ <pre>Vid.get(pos): return value</pre>
+
+ <dl>
+ <dt>pos <dd>(x,y) of tile
+ </dl>
+ """
+ return self.tlayer[pos[1]][pos[0]]
+
+ def paint(self,s):
+ """Paint the screen.
+
+ <pre>Vid.paint(screen): return [updates]</pre>
+
+ <dl>
+ <dt>screen <dd>a pygame.Surface to paint to
+ </dl>
+
+ <p>returns the updated portion of the screen (all of it)</p>
+ """
+ return []
+
+ def update(self,s):
+ """Update the screen.
+
+ <pre>Vid.update(screen): return [updates]</pre>
+
+ <dl>
+ <dt>screen <dd>a pygame.Rect to update
+ </dl>
+
+ <p>returns a list of updated rectangles.</p>
+ """
+ self.updates = []
+ return []
+
+ def tga_load_level(self,fname,bg=0):
+ """Load a TGA level.
+
+ <pre>Vid.tga_load_level(fname,bg=0)</pre>
+
+ <dl>
+ <dt>g <dd>a Tilevid instance
+ <dt>fname <dd>tga image to load
+ <dt>bg <dd>set to 1 if you wish to load the background layer
+ </dl>
+ """
+ if type(fname) == str: img = pygame.image.load(fname)
+ else: img = fname
+ w,h = img.get_width(),img.get_height()
+ self.resize((w,h),bg)
+ for y in range(0,h):
+ for x in range(0,w):
+ t,b,c,_a = img.get_at((x,y))
+ self.tlayer[y][x] = t
+ if bg: self.blayer[y][x] = b
+ self.clayer[y][x] = c
+
+ def tga_save_level(self,fname):
+ """Save a TGA level.
+
+ <pre>Vid.tga_save_level(fname)</pre>
+
+ <dl>
+ <dt>fname <dd>tga image to save to
+ </dl>
+ """
+ w,h = self.size
+ img = pygame.Surface((w,h),SWSURFACE,32)
+ img.fill((0,0,0,0))
+ for y in range(0,h):
+ for x in range(0,w):
+ t = self.tlayer[y][x]
+ b = 0
+ if self.blayer:
+ b = self.blayer[y][x]
+ c = self.clayer[y][x]
+ _a = 0
+ img.set_at((x,y),(t,b,c,_a))
+ pygame.image.save(img,fname)
+
+
+
+ def tga_load_tiles(self,fname,size,tdata={}):
+ """Load a TGA tileset.
+
+ <pre>Vid.tga_load_tiles(fname,size,tdata={})</pre>
+
+ <dl>
+ <dt>g <dd>a Tilevid instance
+ <dt>fname <dd>tga image to load
+ <dt>size <dd>(w,h) size of tiles in pixels
+ <dt>tdata <dd>tile data, a dict of tile:(agroups, hit handler, config)
+ </dl>
+ """
+ TW,TH = size
+ if type(fname) == str: img = pygame.image.load(fname).convert_alpha()
+ else: img = fname
+ w,h = img.get_width(),img.get_height()
+
+ n = 0
+ for y in range(0,h,TH):
+ for x in range(0,w,TW):
+ i = img.subsurface((x,y,TW,TH))
+ tile = Tile(i)
+ self.tiles[n] = tile
+ if n in tdata:
+ agroups,hit,config = tdata[n]
+ tile.agroups = self.string2groups(agroups)
+ tile.hit = hit
+ tile.config = config
+ n += 1
+
+
+ def load_images(self,idata):
+ """Load images.
+
+ <pre>Vid.load_images(idata)</pre>
+
+ <dl>
+ <dt>idata <dd>a list of (name, fname, shape)
+ </dl>
+ """
+ for name,fname,shape in idata:
+ self.images[name] = pygame.image.load(fname).convert_alpha(),shape
+
+ def run_codes(self,cdata,rect):
+ """Run codes.
+
+ <pre>Vid.run_codes(cdata,rect)</pre>
+
+ <dl>
+ <dt>cdata <dd>a dict of code:(handler function, value)
+ <dt>rect <dd>a tile rect of the parts of the layer that should have
+ their codes run
+ </dl>
+ """
+ tw,th = self.tiles[0].image.get_width(),self.tiles[0].image.get_height()
+
+ x1,y1,w,h = rect
+ clayer = self.clayer
+ t = Tile()
+ for y in range(y1,y1+h):
+ for x in range(x1,x1+w):
+ n = clayer[y][x]
+ if n in cdata:
+ fnc,value = cdata[n]
+ t.tx,t.ty = x,y
+ t.rect = pygame.Rect(x*tw,y*th,tw,th)
+ fnc(self,t,value)
+
+
+ def string2groups(self,str):
+ """Convert a string to groups.
+
+ <pre>Vid.string2groups(str): return groups</pre>
+ """
+ if str == None: return 0
+ return self.list2groups(str.split(","))
+
+ def list2groups(self,igroups):
+ """Convert a list to groups.
+ <pre>Vid.list2groups(igroups): return groups</pre>
+ """
+ for s in igroups:
+ if not s in self.groups:
+ self.groups[s] = 2**len(self.groups)
+ v = 0
+ for s,n in self.groups.items():
+ if s in igroups: v|=n
+ return v
+
+ def groups2list(self,groups):
+ """Convert a groups to a list.
+ <pre>Vid.groups2list(groups): return list</pre>
+ """
+ v = []
+ for s,n in self.groups.items():
+ if (n&groups)!=0: v.append(s)
+ return v
+
+ def hit(self,x,y,t,s):
+ tiles = self.tiles
+ tw,th = tiles[0].image.get_width(),tiles[0].image.get_height()
+ t.tx = x
+ t.ty = y
+ t.rect = Rect(x*tw,y*th,tw,th)
+ t._rect = t.rect
+ if hasattr(t,'hit'):
+ t.hit(self,t,s)
+
+ def loop(self):
+ """Update and hit testing loop. Run this once per frame.
+ <pre>Vid.loop()</pre>
+ """
+ self.loop_sprites() #sprites may move
+ self.loop_tilehits() #sprites move
+ self.loop_spritehits() #no sprites should move
+ for s in self.sprites:
+ s._rect = pygame.Rect(s.rect)
+
+ def loop_sprites(self):
+ as_ = self.sprites[:]
+ for s in as_:
+ if hasattr(s,'loop'):
+ s.loop(self,s)
+
+ def loop_tilehits(self):
+ tiles = self.tiles
+ tw,th = tiles[0].image.get_width(),tiles[0].image.get_height()
+
+ layer = self.layers[0]
+
+ as_ = self.sprites[:]
+ for s in as_:
+ self._tilehits(s)
+
+ def _tilehits(self,s):
+ tiles = self.tiles
+ tw,th = tiles[0].image.get_width(),tiles[0].image.get_height()
+ layer = self.layers[0]
+
+ for _z in (0,):
+ if s.groups != 0:
+
+ _rect = s._rect
+ rect = s.rect
+
+ _rectx = _rect.x
+ _recty = _rect.y
+ _rectw = _rect.w
+ _recth = _rect.h
+
+ rectx = rect.x
+ recty = rect.y
+ rectw = rect.w
+ recth = rect.h
+
+ rect.y = _rect.y
+ rect.h = _rect.h
+
+ hits = []
+ ct,cb,cl,cr = rect.top,rect.bottom,rect.left,rect.right
+ #nasty ol loops
+ y = ct/th*th
+ while y < cb:
+ x = cl/tw*tw
+ yy = y/th
+ while x < cr:
+ xx = x/tw
+ t = tiles[layer[yy][xx]]
+ if (s.groups & t.agroups)!=0:
+ #self.hit(xx,yy,t,s)
+ d = math.hypot(rect.centerx-(xx*tw+tw/2),
+ rect.centery-(yy*th+th/2))
+ hits.append((d,t,xx,yy))
+
+ x += tw
+ y += th
+
+ hits.sort()
+ #if len(hits) > 0: print self.frame,hits
+ for d,t,xx,yy in hits:
+ self.hit(xx,yy,t,s)
+
+ #switching directions...
+ _rect.x = rect.x
+ _rect.w = rect.w
+ rect.y = recty
+ rect.h = recth
+
+ hits = []
+ ct,cb,cl,cr = rect.top,rect.bottom,rect.left,rect.right
+ #nasty ol loops
+ y = ct/th*th
+ while y < cb:
+ x = cl/tw*tw
+ yy = y/th
+ while x < cr:
+ xx = x/tw
+ t = tiles[layer[yy][xx]]
+ if (s.groups & t.agroups)!=0:
+ d = math.hypot(rect.centerx-(xx*tw+tw/2),
+ rect.centery-(yy*th+th/2))
+ hits.append((d,t,xx,yy))
+ #self.hit(xx,yy,t,s)
+ x += tw
+ y += th
+
+ hits.sort()
+ #if len(hits) > 0: print self.frame,hits
+ for d,t,xx,yy in hits:
+ self.hit(xx,yy,t,s)
+
+ #done with loops
+ _rect.x = _rectx
+ _rect.y = _recty
+
+
+ def loop_spritehits(self):
+ as_ = self.sprites[:]
+
+ groups = {}
+ for n in range(0,31):
+ groups[1<<n] = []
+ for s in as_:
+ g = s.groups
+ n = 1
+ while g:
+ if (g&1)!=0: groups[n].append(s)
+ g >>= 1
+ n <<= 1
+
+ for s in as_:
+ if s.agroups!=0:
+ rect1,rect2 = s.rect,Rect(s.rect)
+ #if rect1.centerx < 320: rect2.x += 640
+ #else: rect2.x -= 640
+ g = s.agroups
+ n = 1
+ while g:
+ if (g&1)!=0:
+ for b in groups[n]:
+ if (s != b and (s.agroups & b.groups)!=0
+ and s.rect.colliderect(b.rect)):
+ s.hit(self,s,b)
+
+ g >>= 1
+ n <<= 1
+
+
+ def screen_to_tile(self,pos):
+ """Convert a screen position to a tile position.
+ <pre>Vid.screen_to_tile(pos): return pos</pre>
+ """
+ return pos
+
+ def tile_to_screen(self,pos):
+ """Convert a tile position to a screen position.
+ <pre>Vid.tile_to_screen(pos): return pos</pre>
+ """
+ return pos
+
+# vim: set filetype=python sts=4 sw=4 noet si :
--- /dev/null
+'''\r
+Created on 8 dec. 2009\r
+\r
+@author: Samuel Benveniste\r
+'''\r
+\r
+from mxmMidi.MidiOutStream import MidiOutStream\r
+\r
+class MidiToSong(MidiOutStream):\r
+ '''\r
+ Creates songs from midi files using mxmMidi package\r
+ '''\r
+ def __init__(self):\r
+ self.midiNoteNumbers = []\r
+ self.noteLengths = []\r
+ self.quarterNoteLength = 500\r
+ self.firstNoteOn = True\r
+ self._lastNoteOnTime = 0\r
+\r
+ def header(self, format=0, nTracks=1, division=96):\r
+ print 'format: %s, nTracks: %s, division: %s' % (format, nTracks, division)\r
+ print '----------------------------------'\r
+ print ''\r
+ self.division = division\r
+ \r
+ def tempo(self,value):\r
+ self.quarterNoteLength = value/1000\r
+ print "quarterNoteLength in ms :" + str(self.quarterNoteLength) \r
+ \r
+ def note_on(self, channel=0, note=0x40, velocity=0x40):\r
+ self.midiNoteNumbers.append(note)\r
+ # if it's the first note_on take note of time (the song begins here)\r
+ # from the second note_on and after, mark the length of the preceding note\r
+ if self.firstNoteOn :\r
+ self.firstNoteOn = False\r
+ self._lastNoteOnTime = self.abs_time()\r
+ else :\r
+ self.noteLengths.append(float(self.abs_time()-self._lastNoteOnTime)/float(self.division))\r
+ self._lastNoteOnTime = self.abs_time()\r
+ \r
+ def eof(self):\r
+ self.noteLengths.append(4)\r
+ for i in range(len(self.midiNoteNumbers)):\r
+ print "note number :" + str(self.midiNoteNumbers[i]) + ", length in quarter Notes :" + str(self.noteLengths[i])\r
+ print"--------------"\r
+ print "end of file"\r
+ \r
+if __name__ == '__main__':\r
+\r
+ # get data\r
+ test_file = '../songs/midis/test.mid'\r
+ f = open(test_file, 'rb')\r
+ \r
+ # do parsing\r
+ from mxmMidi.MidiInFile import MidiInFile\r
+ mts = MidiToSong()\r
+ midiIn = MidiInFile(mts, f)\r
+ midiIn.read()\r
+ f.close()\r
+
\ No newline at end of file
--- /dev/null
+'''\r
+Created on 2 oct. 2009\r
+\r
+@author: samsam\r
+'''\r
+import pickle\r
+import os.path\r
+from MidiToSong import MidiToSong\r
+from mxmMidi.MidiInFile import MidiInFile\r
+\r
+class Song:\r
+ '''\r
+ classdocs\r
+ '''\r
+\r
+\r
+ def __init__(self,scale,notesInExtendedScale=[], requiresExtendedScale = False,midiNoteNumbers = None, alterationIndexes = None, alterations = None, modulationIndexes = None, modulationScales = None, lyrics =None, noteLengths = None, quarterNoteLength = 750, name = "unknownSong"):\r
+ '''\r
+ Constructor\r
+ '''\r
+ self.name = name\r
+ self.scale = scale\r
+ self.notes = notesInExtendedScale\r
+ self.lyrics = lyrics\r
+ self.noteLengths = noteLengths\r
+ self.quarterNoteLength = quarterNoteLength\r
+ if midiNoteNumbers == None :\r
+ self.midiNoteNumbers = [self.scale[note] for note in self.notes]\r
+ else:\r
+ self.midiNoteNumbers = midiNoteNumbers\r
+ if self.notes == [] :\r
+ self.assignNotesFromMidiNoteNumbers()\r
+ self.requiresExtendedScale = requiresExtendedScale\r
+ if alterationIndexes != None and alterations != None :\r
+ self.alterNotes(alterationIndexes, alterations)\r
+ if modulationIndexes != None and modulationScales != None :\r
+ self.modulate(modulationIndexes, modulationScales)\r
+ \r
+ def getSongIterator(self):\r
+ while True:\r
+ for i in range(len(self.notes)):\r
+ if self.lyrics :\r
+ lyrics = self.lyrics[i]\r
+ else :\r
+ lyrics = None\r
+ \r
+ if self.noteLengths :\r
+ noteLength = self.noteLengths[i]\r
+ else :\r
+ noteLength = 1\r
+ \r
+ yield [self.notes[i],self.midiNoteNumbers[i],lyrics,noteLength]\r
+ \r
+ def alterNotes(self,noteIndexes,alterations):\r
+ for i in range(len(noteIndexes)) :\r
+ self.midiNoteNumbers[noteIndexes[i]] = self.midiNoteNumbers[noteIndexes[i]] + alterations[i]\r
+ \r
+ def modulate(self,modulationIndexes,scales):\r
+ for i in range(len(scales)):\r
+ if i < len(scales)-1 :\r
+ bound = modulationIndexes[i+1]\r
+ else :\r
+ bound = len(self.notes)\r
+ for j in range(modulationIndexes[i],bound):\r
+ self.midiNoteNumbers[j]=scales[i][self.notes[j]]\r
+ \r
+ def assignNotesFromMidiNoteNumbers(self):\r
+ for i in range(len(self.midiNoteNumbers)):\r
+ noteInExtendedScale = 0\r
+ while self.midiNoteNumbers[i] > self.scale[noteInExtendedScale] and noteInExtendedScale < len(self.scale)-1:\r
+ noteInExtendedScale += 1\r
+ if self.midiNoteNumbers[i]<self.scale[noteInExtendedScale]:\r
+ noteInExtendedScale -= 1\r
+ self.notes.append(noteInExtendedScale)\r
+ \r
+ def save(self,path = None):\r
+ if path == None :\r
+ path = "".join(os.path.join("smwis",self.name),".swmi") \r
+ f = file(path, 'w')\r
+ pickler = pickle.Pickler(f)\r
+ pickler.dump(self) \r
+ f.close()\r
+ \r
+def loadSongFromMidi(filePath,scale):\r
+ # get data\r
+ test_file = '../songs/midis/test.mid'\r
+ f = open(test_file, 'rb')\r
+ \r
+ # do parsing\r
+ mts = MidiToSong()\r
+ midiIn = MidiInFile(mts, f)\r
+ midiIn.read()\r
+ f.close()\r
+ \r
+ s = Song(scale,midiNoteNumbers = mts.midiNoteNumbers,quarterNoteLength = mts.quarterNoteLength,noteLengths = mts.noteLengths)\r
+ for nn in s.notes :\r
+ if nn < 3 :\r
+ s.requiresExtendedScale = True \r
+ return(s) \r
+\r
+def loadSong(filePath):\r
+ f = file(filePath, 'r')\r
+ unpickler = pickle.Unpickler(f)\r
+ s = unpickler.load()\r
+ f.close()\r
+ return(s)\r
+\r
+if __name__ == '__main__':\r
+\r
+ # get data\r
+ test_file = '../songs/midis/boheme.mid'\r
+ f = open(test_file, 'rb')\r
+ \r
+ # do parsing\r
+ from mxmMidi.MidiInFile import MidiInFile\r
+ mts = MidiToSong()\r
+ midiIn = MidiInFile(mts, f)\r
+ midiIn.read()\r
+ f.close()\r
+ \r
+ majorScale = [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]\r
+ \r
+ s = Song(majorScale,requiresExtendedScale = True, midiNoteNumbers = mts.midiNoteNumbers,quarterNoteLength = mts.quarterNoteLength,noteLengths = mts.noteLengths)\r
+ for i in range(len(s.midiNoteNumbers)):\r
+ print "note number :" + str(s.midiNoteNumbers[i]) + ", assigned note :" + str(s.notes[i]) + ", duration :" + str(s.noteLengths[i])\r
+ \r
+ s.lyrics = ["je","vous","par-","-le","d'un","temps","que","les","moins","de","vingt","ans","ne","peu-","-vent","pas","con-","-nai-","-tre","Mont-","-martre","en","ce","temps","la","ac-","-cro-","-chait","ses","li-","-las","jus-","-que","sous","nos","fe-","-netres","et","si","l'hum-","-ble","gar-","-ni","qui","nous","ser-","-vait","de","nid","ne","pay-","-yait","pas","de","mi-","-ne","c'est","la","qu'on","s'est","con-","-nus","moi","qui","cri-","-ait","fa-","-mine","et","toi","qui","po-","-sait","nue","la","bo-","-he-","-me","la","bo-","-he-","-me","ca","vou-","-lait","dire","on","est","heu-","-reux","la","bo-","-he-","-me","la","bo-","-he-","-me","nous","ne","man-","-gions","qu'un","jour","sur","deux"]\r
+ \r
+ #s.lyrics = ["quand","nous","chan-","-te-","-rons","le","temps","des","ce-","-rises","et","gai","ros-","-si-","-gnol","et","mer-","-le","mo-","-queur","se-","-ront","tous","en","fe-","-e-","-te","les","bel-","-les","au-","-ront","la","fo-","-lie","en","te","te","et","les","a-","-mou-","-reux","du","so-","-leil","au","coeur","quand","vous","chan-","-te-","-rez","le","temps","des","ce-","-rises","sif-","-fle-","-ra","bien","mieux","le","mer-","-le","mo-","-queur"] \r
+ \r
+ #s.lyrics=["il","pleut","il","pleut","ber-","-ge-","-re","ren-","-tre","tes","blancs","mou-","-tons","al-","-lons","a","ma","chau-","-mie-","-re","ber-","-ge-","-re","vite","al-","-lons","j'en-","-tends","sur","le","feu-","-illa-","-ge","l'eau","qui","tombe","a","grand","bruit","voi-","-ci","ve-","-nir","l'o-","-ra-","-a-","-ge","voi-","-ci","l'e-","-clair","qui","luit"]\r
+ \r
+ #s.lyrics = ["c'est","une","chan-","-son","qui","nous","res-","-sem-","-ble","toi","tu","m'ai-","-mais","et","je","t'ai-","-mais","nous","e-","-tions","tout","les","deux","en-","-sem-","-ble","toi","qui","m'ai-","-mais","moi","qui","t'ai-","-mais","mais","la","vie","se-","-pare","ceux","qui","s'aiment","tout","dou-","-ce-","-ment","sans","faire","de","bruit","et","la","mer","ef-","-fa-","-ce","sur","le","sa-","-ble","les","pas","des","a-","-mants","de-","-su-","-nis"]\r
+\r
+ # s.lyrics = ["quand","il","me","prend","dans","ses","bras","qu'il","me","par-","-le","tout","bas","je","vois","la","vie","en","ro-","-se","il","me","dit","des","mots","d'a-","-mour","des","mots","de","tous","les","jours","et","ca","m'fait","quel-","-que","cho-","-se","il","est","en-","-tre","dans","mon","coeur","u-","-ne","part","de","bon-","-heur","dont","je","con-","-nais","la","cau-","-se","c'est","lmui","pour","moi","moi","pour","lui","dans","la","vie","il","m'a","pro-","-mis","m'a","ju-","-re","pour","la","vie","et","des","que","je","l'a-","-per-","-cois","a-","-lors","je","sens","en","moi","mon","coeur","qui","bat"]\r
+\r
+ s.save("smwis/boheme.smwi")
\ No newline at end of file
--- /dev/null
+(i__main__\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I58\r
+aI58\r
+aI60\r
+aI58\r
+aI60\r
+aI61\r
+aI61\r
+aI61\r
+aI63\r
+aI61\r
+aI63\r
+aI65\r
+aI65\r
+aI65\r
+aI67\r
+aI65\r
+aI67\r
+aI68\r
+aI60\r
+aI68\r
+aI67\r
+aI65\r
+aI68\r
+aI67\r
+aI61\r
+aI68\r
+aI67\r
+aI65\r
+aI68\r
+aI67\r
+aI60\r
+aI60\r
+aI60\r
+aI60\r
+aI63\r
+aI62\r
+aI61\r
+aI58\r
+aI58\r
+aI60\r
+aI58\r
+aI60\r
+aI61\r
+aI61\r
+aI61\r
+aI63\r
+aI61\r
+aI63\r
+aI65\r
+aI65\r
+aI65\r
+aI67\r
+aI65\r
+aI67\r
+aI68\r
+aI68\r
+aI68\r
+aI68\r
+aI68\r
+aI72\r
+aI70\r
+aI70\r
+aI68\r
+aI68\r
+aI67\r
+aI70\r
+aI68\r
+aI68\r
+aI67\r
+aI67\r
+aI65\r
+aI64\r
+aI64\r
+aI65\r
+aI65\r
+aI64\r
+aI65\r
+aI58\r
+aI67\r
+aI65\r
+aI68\r
+aI60\r
+aI60\r
+aI63\r
+aI62\r
+aI61\r
+aI60\r
+aI55\r
+aI56\r
+aI60\r
+aI65\r
+aI64\r
+aI65\r
+aI58\r
+aI67\r
+aI65\r
+aI68\r
+aI60\r
+aI68\r
+aI67\r
+aI65\r
+aI63\r
+aI61\r
+aI60\r
+aI58\r
+aI60\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'je'\r
+p10\r
+aS'vous'\r
+p11\r
+aS'par-'\r
+p12\r
+aS'-le'\r
+p13\r
+aS"d'un"\r
+p14\r
+aS'temps'\r
+p15\r
+aS'que'\r
+p16\r
+aS'les'\r
+p17\r
+aS'moins'\r
+p18\r
+aS'de'\r
+p19\r
+aS'vingt'\r
+p20\r
+aS'ans'\r
+p21\r
+aS'ne'\r
+p22\r
+aS'peu-'\r
+p23\r
+aS'-vent'\r
+p24\r
+aS'pas'\r
+p25\r
+aS'con-'\r
+p26\r
+aS'-nai-'\r
+p27\r
+aS'-tre'\r
+p28\r
+aS'Mont-'\r
+p29\r
+aS'-martre'\r
+p30\r
+aS'en'\r
+p31\r
+aS'ce'\r
+p32\r
+ag15\r
+aS'la'\r
+p33\r
+aS'ac-'\r
+p34\r
+aS'-cro-'\r
+p35\r
+aS'-chait'\r
+p36\r
+aS'ses'\r
+p37\r
+aS'li-'\r
+p38\r
+aS'-las'\r
+p39\r
+aS'jus-'\r
+p40\r
+aS'-que'\r
+p41\r
+aS'sous'\r
+p42\r
+aS'nos'\r
+p43\r
+aS'fe-'\r
+p44\r
+aS'-netres'\r
+p45\r
+aS'et'\r
+p46\r
+aS'si'\r
+p47\r
+aS"l'hum-"\r
+p48\r
+aS'-ble'\r
+p49\r
+aS'gar-'\r
+p50\r
+aS'-ni'\r
+p51\r
+aS'qui'\r
+p52\r
+aS'nous'\r
+p53\r
+aS'ser-'\r
+p54\r
+aS'-vait'\r
+p55\r
+ag19\r
+aS'nid'\r
+p56\r
+ag22\r
+aS'pay-'\r
+p57\r
+aS'-yait'\r
+p58\r
+ag25\r
+ag19\r
+aS'mi-'\r
+p59\r
+aS'-ne'\r
+p60\r
+aS"c'est"\r
+p61\r
+ag33\r
+aS"qu'on"\r
+p62\r
+aS"s'est"\r
+p63\r
+ag26\r
+aS'-nus'\r
+p64\r
+aS'moi'\r
+p65\r
+ag52\r
+aS'cri-'\r
+p66\r
+aS'-ait'\r
+p67\r
+aS'fa-'\r
+p68\r
+aS'-mine'\r
+p69\r
+ag46\r
+aS'toi'\r
+p70\r
+ag52\r
+aS'po-'\r
+p71\r
+aS'-sait'\r
+p72\r
+aS'nue'\r
+p73\r
+ag33\r
+aS'bo-'\r
+p74\r
+aS'-he-'\r
+p75\r
+aS'-me'\r
+p76\r
+ag33\r
+ag74\r
+ag75\r
+ag76\r
+aS'ca'\r
+p77\r
+aS'vou-'\r
+p78\r
+aS'-lait'\r
+p79\r
+aS'dire'\r
+p80\r
+aS'on'\r
+p81\r
+aS'est'\r
+p82\r
+aS'heu-'\r
+p83\r
+aS'-reux'\r
+p84\r
+ag33\r
+ag74\r
+ag75\r
+ag76\r
+ag33\r
+ag74\r
+ag75\r
+ag76\r
+ag53\r
+ag22\r
+aS'man-'\r
+p85\r
+aS'-gions'\r
+p86\r
+aS"qu'un"\r
+p87\r
+aS'jour'\r
+p88\r
+aS'sur'\r
+p89\r
+aS'deux'\r
+p90\r
+asS'noteLengths'\r
+p91\r
+(lp92\r
+F0.48229166666666667\r
+aF0.49166666666666664\r
+aF0.47395833333333331\r
+aF0.50104166666666672\r
+aF0.53229166666666672\r
+aF0.51666666666666672\r
+aF0.49270833333333336\r
+aF0.49375000000000002\r
+aF0.50624999999999998\r
+aF0.49791666666666667\r
+aF0.48229166666666667\r
+aF0.52812499999999996\r
+aF0.515625\r
+aF0.47708333333333336\r
+aF0.45729166666666665\r
+aF0.51770833333333333\r
+aF0.51458333333333328\r
+aF0.9916666666666667\r
+aF2.6270833333333332\r
+aF0.375\r
+aF0.50624999999999998\r
+aF0.49479166666666669\r
+aF0.55000000000000004\r
+aF0.49687500000000001\r
+aF3.4739583333333335\r
+aF0.48854166666666665\r
+aF0.48229166666666667\r
+aF0.5239583333333333\r
+aF0.47708333333333336\r
+aF0.515625\r
+aF0.53229166666666672\r
+aF0.51458333333333328\r
+aF0.46250000000000002\r
+aF0.50104166666666672\r
+aF0.46770833333333334\r
+aF0.51354166666666667\r
+aF0.49791666666666667\r
+aF0.47604166666666664\r
+aF0.52604166666666663\r
+aF0.49687500000000001\r
+aF0.48020833333333335\r
+aF0.5083333333333333\r
+aF0.50416666666666665\r
+aF0.48645833333333333\r
+aF0.54583333333333328\r
+aF0.47083333333333333\r
+aF0.48645833333333333\r
+aF0.5239583333333333\r
+aF0.484375\r
+aF0.49583333333333335\r
+aF0.49895833333333334\r
+aF0.50208333333333333\r
+aF0.48749999999999999\r
+aF0.53229166666666672\r
+aF0.5541666666666667\r
+aF2.9604166666666667\r
+aF0.51666666666666672\r
+aF0.48645833333333333\r
+aF0.48541666666666666\r
+aF0.49375000000000002\r
+aF0.48229166666666667\r
+aF0.49895833333333334\r
+aF0.52187499999999998\r
+aF0.49479166666666669\r
+aF0.5\r
+aF0.46250000000000002\r
+aF0.52187499999999998\r
+aF0.52604166666666663\r
+aF0.47083333333333333\r
+aF0.51458333333333328\r
+aF0.50937500000000002\r
+aF0.48645833333333333\r
+aF0.49062499999999998\r
+aF4.7041666666666666\r
+aF0.8989583333333333\r
+aF0.51666666666666672\r
+aF0.41979166666666667\r
+aF3.9927083333333333\r
+aF0.97916666666666663\r
+aF0.49270833333333336\r
+aF0.47916666666666669\r
+aF4.1531250000000002\r
+aF0.92812499999999998\r
+aF0.28541666666666665\r
+aF0.29999999999999999\r
+aF2.7749999999999999\r
+aF0.56562500000000004\r
+aF0.49270833333333336\r
+aF0.54583333333333328\r
+aF4.5125000000000002\r
+aF0.95625000000000004\r
+aF0.55000000000000004\r
+aF0.46875\r
+aF4.0250000000000004\r
+aF0.90312499999999996\r
+aF0.55729166666666663\r
+aF0.55000000000000004\r
+aF4.0281250000000002\r
+aF0.43125000000000002\r
+aF0.49479166666666669\r
+aF0.51458333333333328\r
+aF2.4812500000000002\r
+aF0.58125000000000004\r
+aF2.4520833333333334\r
+aF0.515625\r
+aI4\r
+asS'notes'\r
+p93\r
+(lp94\r
+I1\r
+aI1\r
+aI3\r
+aI1\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI4\r
+aI3\r
+aI4\r
+aI6\r
+aI6\r
+aI6\r
+aI7\r
+aI6\r
+aI7\r
+aI7\r
+aI3\r
+aI7\r
+aI7\r
+aI6\r
+aI7\r
+aI7\r
+aI3\r
+aI7\r
+aI7\r
+aI6\r
+aI7\r
+aI7\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI4\r
+aI4\r
+aI3\r
+aI1\r
+aI1\r
+aI3\r
+aI1\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI4\r
+aI3\r
+aI4\r
+aI6\r
+aI6\r
+aI6\r
+aI7\r
+aI6\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI10\r
+aI8\r
+aI8\r
+aI7\r
+aI7\r
+aI7\r
+aI8\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI6\r
+aI5\r
+aI5\r
+aI6\r
+aI6\r
+aI5\r
+aI6\r
+aI1\r
+aI7\r
+aI6\r
+aI7\r
+aI3\r
+aI3\r
+aI4\r
+aI4\r
+aI3\r
+aI3\r
+aI0\r
+aI0\r
+aI3\r
+aI6\r
+aI5\r
+aI6\r
+aI1\r
+aI7\r
+aI6\r
+aI7\r
+aI3\r
+aI7\r
+aI7\r
+aI6\r
+aI4\r
+aI3\r
+aI3\r
+aI1\r
+aI3\r
+asS'requiresExtendedScale'\r
+p95\r
+I01\r
+sS'quarterNoteLength'\r
+p96\r
+I750\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I67\r
+aI67\r
+aI65\r
+aI64\r
+aI65\r
+aI69\r
+aI67\r
+aI67\r
+aI65\r
+aI69\r
+aI67\r
+aI67\r
+aI65\r
+aI70\r
+aI67\r
+aI67\r
+aI65\r
+aI70\r
+aI67\r
+aI67\r
+aI65\r
+aI69\r
+aI67\r
+aI67\r
+aI65\r
+aI69\r
+aI67\r
+aI65\r
+aI63\r
+aI62\r
+aI62\r
+aI62\r
+aI62\r
+aI62\r
+aI62\r
+aI66\r
+aI66\r
+aI66\r
+aI66\r
+aI69\r
+aI69\r
+aI67\r
+aI66\r
+aI69\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI66\r
+aI70\r
+aI69\r
+aI67\r
+aI69\r
+aI69\r
+aI67\r
+aI69\r
+aI69\r
+aI67\r
+aI69\r
+aI69\r
+aI67\r
+aI69\r
+aI69\r
+aI66\r
+aI69\r
+aI69\r
+aI66\r
+aI69\r
+aI69\r
+aI67\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI58\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI70\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'Em-'\r
+p10\r
+aS'-por-'\r
+p11\r
+aS'-tes'\r
+p12\r
+aS'par'\r
+p13\r
+aS'la'\r
+p14\r
+aS'fou-'\r
+p15\r
+aS'-le'\r
+p16\r
+aS'qui'\r
+p17\r
+aS'nous'\r
+p18\r
+aS'trai-'\r
+p19\r
+aS'-ne'\r
+p20\r
+ag18\r
+aS'en-'\r
+p21\r
+aS'-trai-'\r
+p22\r
+ag20\r
+aS'e-'\r
+p23\r
+aS'-cra-'\r
+p24\r
+aS'-ses'\r
+p25\r
+aS"l'un"\r
+p26\r
+aS'con-'\r
+p27\r
+aS'-tre'\r
+p28\r
+aS"l'au-"\r
+p29\r
+ag28\r
+ag18\r
+aS'ne'\r
+p30\r
+aS'for-'\r
+p31\r
+aS'-mons'\r
+p32\r
+aS"qu'un"\r
+p33\r
+aS'seul'\r
+p34\r
+aS'corps'\r
+p35\r
+aS'et'\r
+p36\r
+aS'le'\r
+p37\r
+aS'flot'\r
+p38\r
+aS'sans'\r
+p39\r
+ag23\r
+aS'-ffort'\r
+p40\r
+ag18\r
+aS'pousse'\r
+p41\r
+ag21\r
+aS'-chai-'\r
+p42\r
+aS'-nes'\r
+p43\r
+ag26\r
+ag36\r
+ag29\r
+ag28\r
+ag36\r
+ag18\r
+aS'lai-'\r
+p44\r
+aS'-sse'\r
+p45\r
+aS'tous'\r
+p46\r
+aS'deux'\r
+p47\r
+ag23\r
+aS'-pa-'\r
+p48\r
+aS'-nouis-'\r
+p49\r
+ag21\r
+aS'-i-'\r
+p50\r
+aS'-vres'\r
+p51\r
+ag36\r
+aS'heu-'\r
+p52\r
+aS'-reux'\r
+p53\r
+aS'ta-'\r
+p54\r
+aS'-dam'\r
+p55\r
+aS'ta'\r
+p56\r
+ag54\r
+ag55\r
+ag56\r
+ag54\r
+ag55\r
+ag56\r
+ag54\r
+ag55\r
+ag56\r
+ag54\r
+ag55\r
+ag56\r
+ag54\r
+ag55\r
+ag56\r
+asS'noteLengths'\r
+p57\r
+(lp58\r
+I1\r
+aF1.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF1.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF1.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF1.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF1.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF1.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF1.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aI3\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aI1\r
+aI1\r
+aF0.5\r
+aF0.5\r
+aF1.5\r
+aI3\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aI1\r
+aI1\r
+aF1.5\r
+aF0.5\r
+aI1\r
+aF1.5\r
+aF0.5\r
+aI1\r
+aF1.5\r
+aF0.5\r
+aI1\r
+aF1.5\r
+aF0.5\r
+aI1\r
+aF1.5\r
+aF0.5\r
+aI1\r
+aF1.5\r
+aF0.5\r
+aI1\r
+aI3\r
+asS'notes'\r
+p59\r
+(lp60\r
+I7\r
+aI7\r
+aI6\r
+aI5\r
+aI6\r
+aI8\r
+aI7\r
+aI7\r
+aI6\r
+aI8\r
+aI7\r
+aI7\r
+aI6\r
+aI9\r
+aI7\r
+aI7\r
+aI6\r
+aI9\r
+aI7\r
+aI7\r
+aI6\r
+aI8\r
+aI7\r
+aI7\r
+aI6\r
+aI8\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI4\r
+aI4\r
+aI4\r
+aI4\r
+aI4\r
+aI6\r
+aI6\r
+aI6\r
+aI6\r
+aI8\r
+aI8\r
+aI7\r
+aI6\r
+aI8\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI6\r
+aI9\r
+aI8\r
+aI7\r
+aI8\r
+aI8\r
+aI7\r
+aI8\r
+aI8\r
+aI7\r
+aI8\r
+aI8\r
+aI7\r
+aI8\r
+aI8\r
+aI6\r
+aI8\r
+aI8\r
+aI6\r
+aI8\r
+aI8\r
+aI7\r
+asS'requiresExtendedScale'\r
+p61\r
+I00\r
+sS'quarterNoteLength'\r
+p62\r
+I400\r
+sb.
\ No newline at end of file
--- /dev/null
+(i__main__\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I67\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI67\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI55\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI55\r
+aI55\r
+aI66\r
+aI64\r
+aI62\r
+aI69\r
+aI67\r
+aI66\r
+aI64\r
+aI60\r
+aI67\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI55\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI55\r
+aI55\r
+aI66\r
+aI64\r
+aI62\r
+aI67\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI67\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI55\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI55\r
+aI55\r
+aI55\r
+aI67\r
+aI67\r
+aI69\r
+aI69\r
+aI67\r
+aI69\r
+aI69\r
+aI67\r
+aI69\r
+aI69\r
+aI67\r
+aI62\r
+aI69\r
+aI69\r
+aI67\r
+aI69\r
+aI69\r
+aI67\r
+aI69\r
+aI69\r
+aI67\r
+aI71\r
+aI67\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI67\r
+aI66\r
+aI64\r
+aI62\r
+aI59\r
+aI55\r
+aI66\r
+aI64\r
+aI62\r
+aI64\r
+aI66\r
+aI67\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'quand'\r
+p10\r
+aS'il'\r
+p11\r
+aS'me'\r
+p12\r
+aS'prend'\r
+p13\r
+aS'dans'\r
+p14\r
+aS'ses'\r
+p15\r
+aS'bras'\r
+p16\r
+aS"qu'il"\r
+p17\r
+ag12\r
+aS'par-'\r
+p18\r
+aS'-le'\r
+p19\r
+aS'tout'\r
+p20\r
+aS'bas'\r
+p21\r
+aS'je'\r
+p22\r
+aS'vois'\r
+p23\r
+aS'la'\r
+p24\r
+aS'vie'\r
+p25\r
+aS'en'\r
+p26\r
+aS'ro-'\r
+p27\r
+aS'-se'\r
+p28\r
+ag11\r
+ag12\r
+aS'dit'\r
+p29\r
+aS'des'\r
+p30\r
+aS'mots'\r
+p31\r
+aS"d'a-"\r
+p32\r
+aS'-mour'\r
+p33\r
+ag30\r
+ag31\r
+aS'de'\r
+p34\r
+aS'tous'\r
+p35\r
+aS'les'\r
+p36\r
+aS'jours'\r
+p37\r
+aS'et'\r
+p38\r
+aS'ca'\r
+p39\r
+aS"m'fait"\r
+p40\r
+aS'quel-'\r
+p41\r
+aS'-que'\r
+p42\r
+aS'cho-'\r
+p43\r
+ag28\r
+ag11\r
+aS'est'\r
+p44\r
+aS'en-'\r
+p45\r
+aS'-tre'\r
+p46\r
+ag14\r
+aS'mon'\r
+p47\r
+aS'coeur'\r
+p48\r
+aS'u-'\r
+p49\r
+aS'-ne'\r
+p50\r
+aS'part'\r
+p51\r
+ag34\r
+aS'bon-'\r
+p52\r
+aS'-heur'\r
+p53\r
+aS'dont'\r
+p54\r
+ag22\r
+aS'con-'\r
+p55\r
+aS'-nais'\r
+p56\r
+ag24\r
+aS'cau-'\r
+p57\r
+ag28\r
+aS"c'est"\r
+p58\r
+aS'lmui'\r
+p59\r
+aS'pour'\r
+p60\r
+aS'moi'\r
+p61\r
+ag61\r
+ag60\r
+aS'lui'\r
+p62\r
+ag14\r
+ag24\r
+ag25\r
+ag11\r
+aS"m'a"\r
+p63\r
+aS'pro-'\r
+p64\r
+aS'-mis'\r
+p65\r
+ag63\r
+aS'ju-'\r
+p66\r
+aS'-re'\r
+p67\r
+ag60\r
+ag24\r
+ag25\r
+ag38\r
+ag30\r
+aS'que'\r
+p68\r
+ag22\r
+aS"l'a-"\r
+p69\r
+aS'-per-'\r
+p70\r
+aS'-cois'\r
+p71\r
+aS'a-'\r
+p72\r
+aS'-lors'\r
+p73\r
+ag22\r
+aS'sens'\r
+p74\r
+ag26\r
+ag61\r
+ag47\r
+ag48\r
+aS'qui'\r
+p75\r
+aS'bat'\r
+p76\r
+asS'noteLengths'\r
+p77\r
+(lp78\r
+F2.5385416666666667\r
+aF0.52812499999999996\r
+aF0.88645833333333335\r
+aF1.0291666666666666\r
+aF0.52187499999999998\r
+aF0.453125\r
+aF2.4583333333333335\r
+aF0.5864583333333333\r
+aF0.95937499999999998\r
+aF1.0364583333333333\r
+aF0.52812499999999996\r
+aF0.4375\r
+aF2.6343749999999999\r
+aF0.43125000000000002\r
+aF0.93541666666666667\r
+aF0.99479166666666663\r
+aF0.51666666666666672\r
+aF0.46666666666666667\r
+aF3.2052083333333332\r
+aF2.8843749999999999\r
+aF2.1812499999999999\r
+aF0.6645833333333333\r
+aF1.0562499999999999\r
+aF1.0552083333333333\r
+aF0.484375\r
+aF0.53437500000000004\r
+aF2.3062499999999999\r
+aF0.69791666666666663\r
+aF0.96666666666666667\r
+aF0.95729166666666665\r
+aF0.5625\r
+aF0.48229166666666667\r
+aF2.5791666666666666\r
+aF0.43437500000000001\r
+aF0.95416666666666672\r
+aF1.03125\r
+aF0.62395833333333328\r
+aF0.40625\r
+aF3.0270833333333331\r
+aF8.90625\r
+aF2.2927083333333331\r
+aF0.75624999999999998\r
+aF0.95208333333333328\r
+aF1.0135416666666666\r
+aF0.453125\r
+aF0.55729166666666663\r
+aF2.2895833333333333\r
+aF0.71562499999999996\r
+aF0.95729166666666665\r
+aF1.0447916666666666\r
+aF0.50624999999999998\r
+aF0.47604166666666664\r
+aF2.3885416666666668\r
+aF0.671875\r
+aF0.90416666666666667\r
+aF1.0802083333333334\r
+aF0.46666666666666667\r
+aF0.54166666666666663\r
+aF3.0093749999999999\r
+aF2.8510416666666667\r
+aF0.57604166666666667\r
+aF0.50520833333333337\r
+aF0.54166666666666663\r
+aF0.5\r
+aF0.43333333333333335\r
+aF0.578125\r
+aF0.484375\r
+aF0.40625\r
+aF0.59583333333333333\r
+aF1.5260416666666667\r
+aF0.46458333333333335\r
+aF0.484375\r
+aF0.49583333333333335\r
+aF0.50312500000000004\r
+aF0.51458333333333328\r
+aF0.5385416666666667\r
+aF0.52187499999999998\r
+aF0.72499999999999998\r
+aF0.90729166666666672\r
+aF3.9052083333333334\r
+aF2.0145833333333334\r
+aF0.70104166666666667\r
+aF1.01875\r
+aF1.0927083333333334\r
+aF0.57499999999999996\r
+aF0.48020833333333335\r
+aF2.2312500000000002\r
+aF0.64583333333333337\r
+aF1.0395833333333333\r
+aF1.0791666666666666\r
+aF0.51458333333333328\r
+aF0.50416666666666665\r
+aF2.3979166666666667\r
+aF1.421875\r
+aF1.2749999999999999\r
+aF1.4989583333333334\r
+aI4\r
+asS'notes'\r
+p79\r
+(lp80\r
+I7\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI0\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI0\r
+aI0\r
+aI6\r
+aI5\r
+aI4\r
+aI8\r
+aI7\r
+aI6\r
+aI5\r
+aI3\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI0\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI0\r
+aI0\r
+aI6\r
+aI5\r
+aI4\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI0\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI0\r
+aI0\r
+aI0\r
+aI7\r
+aI7\r
+aI8\r
+aI8\r
+aI7\r
+aI8\r
+aI8\r
+aI7\r
+aI8\r
+aI8\r
+aI7\r
+aI4\r
+aI8\r
+aI8\r
+aI7\r
+aI8\r
+aI8\r
+aI7\r
+aI8\r
+aI8\r
+aI7\r
+aI9\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI2\r
+aI0\r
+aI6\r
+aI5\r
+aI4\r
+aI5\r
+aI6\r
+aI7\r
+asS'requiresExtendedScale'\r
+p81\r
+I01\r
+sS'quarterNoteLength'\r
+p82\r
+I666\r
+sb.
\ No newline at end of file
--- /dev/null
+(i__main__\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I64\r
+aI67\r
+aI64\r
+aI67\r
+aI64\r
+aI60\r
+aI55\r
+aI60\r
+aI59\r
+aI60\r
+aI62\r
+aI62\r
+aI64\r
+aI64\r
+aI62\r
+aI64\r
+aI65\r
+aI64\r
+aI67\r
+aI64\r
+aI67\r
+aI69\r
+aI67\r
+aI65\r
+aI64\r
+aI62\r
+aI62\r
+aI61\r
+aI62\r
+aI65\r
+aI65\r
+aI64\r
+aI67\r
+aI65\r
+aI64\r
+aI62\r
+aI64\r
+aI60\r
+aI62\r
+aI64\r
+aI67\r
+aI64\r
+aI67\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI67\r
+aI69\r
+aI67\r
+aI62\r
+aI64\r
+aI60\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'il'\r
+p10\r
+aS'pleut'\r
+p11\r
+ag10\r
+ag11\r
+aS'ber-'\r
+p12\r
+aS'-ge-'\r
+p13\r
+aS'-re'\r
+p14\r
+aS'ren-'\r
+p15\r
+aS'-tre'\r
+p16\r
+aS'tes'\r
+p17\r
+aS'blancs'\r
+p18\r
+aS'mou-'\r
+p19\r
+aS'-tons'\r
+p20\r
+aS'al-'\r
+p21\r
+aS'-lons'\r
+p22\r
+aS'a'\r
+p23\r
+aS'ma'\r
+p24\r
+aS'chau-'\r
+p25\r
+aS'-mie-'\r
+p26\r
+ag14\r
+ag12\r
+ag13\r
+ag14\r
+aS'vite'\r
+p27\r
+ag21\r
+ag22\r
+aS"j'en-"\r
+p28\r
+aS'-tends'\r
+p29\r
+aS'sur'\r
+p30\r
+aS'le'\r
+p31\r
+aS'feu-'\r
+p32\r
+aS'-illa-'\r
+p33\r
+aS'-ge'\r
+p34\r
+aS"l'eau"\r
+p35\r
+aS'qui'\r
+p36\r
+aS'tombe'\r
+p37\r
+ag23\r
+aS'grand'\r
+p38\r
+aS'bruit'\r
+p39\r
+aS'voi-'\r
+p40\r
+aS'-ci'\r
+p41\r
+aS've-'\r
+p42\r
+aS'-nir'\r
+p43\r
+aS"l'o-"\r
+p44\r
+aS'-ra-'\r
+p45\r
+aS'-a-'\r
+p46\r
+ag34\r
+ag40\r
+ag41\r
+aS"l'e-"\r
+p47\r
+aS'-clair'\r
+p48\r
+ag36\r
+aS'luit'\r
+p49\r
+asS'noteLengths'\r
+p50\r
+(lp51\r
+F0.54479166666666667\r
+aF0.94999999999999996\r
+aF0.54374999999999996\r
+aF0.94999999999999996\r
+aF0.56770833333333337\r
+aF1.5270833333333333\r
+aF1.48125\r
+aF0.47708333333333336\r
+aF0.48749999999999999\r
+aF0.50624999999999998\r
+aF0.98541666666666672\r
+aF0.56145833333333328\r
+aF2.9812500000000002\r
+aF0.5072916666666667\r
+aF0.48541666666666666\r
+aF0.484375\r
+aF0.95625000000000004\r
+aF0.5854166666666667\r
+aF1.4989583333333334\r
+aF1.45625\r
+aF0.5\r
+aF0.51458333333333328\r
+aF0.5072916666666667\r
+aF0.90312499999999996\r
+aF0.6166666666666667\r
+aF2.9989583333333334\r
+aF0.47395833333333331\r
+aF0.52916666666666667\r
+aF0.51249999999999996\r
+aF0.93333333333333335\r
+aF0.52812499999999996\r
+aF1.5135416666666666\r
+aF1.4791666666666667\r
+aF0.51354166666666667\r
+aF0.52812499999999996\r
+aF0.49166666666666664\r
+aF0.97499999999999998\r
+aF0.5229166666666667\r
+aF2.3718750000000002\r
+aF0.61145833333333333\r
+aF1.0093749999999999\r
+aF0.52708333333333335\r
+aF0.8822916666666667\r
+aF0.58958333333333335\r
+aF0.96458333333333335\r
+aF0.53020833333333328\r
+aF1.5562499999999999\r
+aF0.48541666666666666\r
+aF0.48333333333333334\r
+aF0.49583333333333335\r
+aF0.94270833333333337\r
+aF0.5385416666666667\r
+aI4\r
+asS'notes'\r
+p52\r
+(lp53\r
+I5\r
+aI7\r
+aI5\r
+aI7\r
+aI5\r
+aI3\r
+aI0\r
+aI3\r
+aI2\r
+aI3\r
+aI4\r
+aI4\r
+aI5\r
+aI5\r
+aI4\r
+aI5\r
+aI6\r
+aI5\r
+aI7\r
+aI5\r
+aI7\r
+aI8\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI4\r
+aI3\r
+aI4\r
+aI6\r
+aI6\r
+aI5\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI5\r
+aI3\r
+aI4\r
+aI5\r
+aI7\r
+aI5\r
+aI7\r
+aI5\r
+aI6\r
+aI7\r
+aI8\r
+aI7\r
+aI8\r
+aI7\r
+aI4\r
+aI5\r
+aI3\r
+asS'requiresExtendedScale'\r
+p54\r
+I01\r
+sS'quarterNoteLength'\r
+p55\r
+I600\r
+sb.
\ No newline at end of file
--- /dev/null
+(i__main__\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I55\r
+aI60\r
+aI60\r
+aI60\r
+aI60\r
+aI60\r
+aI62\r
+aI62\r
+aI62\r
+aI62\r
+aI60\r
+aI64\r
+aI64\r
+aI64\r
+aI64\r
+aI60\r
+aI65\r
+aI65\r
+aI65\r
+aI65\r
+aI65\r
+aI64\r
+aI64\r
+aI65\r
+aI64\r
+aI62\r
+aI60\r
+aI60\r
+aI65\r
+aI65\r
+aI65\r
+aI65\r
+aI65\r
+aI65\r
+aI69\r
+aI65\r
+aI65\r
+aI64\r
+aI64\r
+aI64\r
+aI64\r
+aI64\r
+aI67\r
+aI64\r
+aI64\r
+aI62\r
+aI60\r
+aI62\r
+aI55\r
+aI60\r
+aI60\r
+aI60\r
+aI60\r
+aI60\r
+aI62\r
+aI62\r
+aI62\r
+aI62\r
+aI60\r
+aI64\r
+aI64\r
+aI64\r
+aI67\r
+aI64\r
+aI64\r
+aI62\r
+aI60\r
+aI60\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'quand'\r
+p10\r
+aS'nous'\r
+p11\r
+aS'chan-'\r
+p12\r
+aS'-te-'\r
+p13\r
+aS'-rons'\r
+p14\r
+aS'le'\r
+p15\r
+aS'temps'\r
+p16\r
+aS'des'\r
+p17\r
+aS'ce-'\r
+p18\r
+aS'-rises'\r
+p19\r
+aS'et'\r
+p20\r
+aS'gai'\r
+p21\r
+aS'ros-'\r
+p22\r
+aS'-si-'\r
+p23\r
+aS'-gnol'\r
+p24\r
+ag20\r
+aS'mer-'\r
+p25\r
+aS'-le'\r
+p26\r
+aS'mo-'\r
+p27\r
+aS'-queur'\r
+p28\r
+aS'se-'\r
+p29\r
+aS'-ront'\r
+p30\r
+aS'tous'\r
+p31\r
+aS'en'\r
+p32\r
+aS'fe-'\r
+p33\r
+aS'-e-'\r
+p34\r
+aS'-te'\r
+p35\r
+aS'les'\r
+p36\r
+aS'bel-'\r
+p37\r
+aS'-les'\r
+p38\r
+aS'au-'\r
+p39\r
+ag30\r
+aS'la'\r
+p40\r
+aS'fo-'\r
+p41\r
+aS'-lie'\r
+p42\r
+ag32\r
+aS'te'\r
+p43\r
+ag43\r
+ag20\r
+ag36\r
+aS'a-'\r
+p44\r
+aS'-mou-'\r
+p45\r
+aS'-reux'\r
+p46\r
+aS'du'\r
+p47\r
+aS'so-'\r
+p48\r
+aS'-leil'\r
+p49\r
+aS'au'\r
+p50\r
+aS'coeur'\r
+p51\r
+ag10\r
+aS'vous'\r
+p52\r
+ag12\r
+ag13\r
+aS'-rez'\r
+p53\r
+ag15\r
+ag16\r
+ag17\r
+ag18\r
+ag19\r
+aS'sif-'\r
+p54\r
+aS'-fle-'\r
+p55\r
+aS'-ra'\r
+p56\r
+aS'bien'\r
+p57\r
+aS'mieux'\r
+p58\r
+ag15\r
+ag25\r
+ag26\r
+ag27\r
+ag28\r
+asS'noteLengths'\r
+p59\r
+(lp60\r
+F0.41666666666666669\r
+aF0.47395833333333331\r
+aF0.44583333333333336\r
+aF0.49270833333333336\r
+aF1.0104166666666667\r
+aF0.57604166666666667\r
+aF0.45729166666666665\r
+aF0.45729166666666665\r
+aF0.51770833333333333\r
+aF0.92604166666666665\r
+aF0.55937499999999996\r
+aF0.515625\r
+aF0.46250000000000002\r
+aF0.49375000000000002\r
+aF0.9916666666666667\r
+aF0.53125\r
+aF0.50104166666666672\r
+aF0.5229166666666667\r
+aF0.49270833333333336\r
+aF1.0260416666666667\r
+aF0.421875\r
+aF0.51041666666666663\r
+aF0.48645833333333333\r
+aF0.53229166666666672\r
+aF1.5\r
+aF1.4770833333333333\r
+aF1.1104166666666666\r
+aF0.42291666666666666\r
+aF0.48229166666666667\r
+aF0.49895833333333334\r
+aF0.5229166666666667\r
+aF1.0385416666666667\r
+aF0.49479166666666669\r
+aF0.47708333333333336\r
+aF0.59687500000000004\r
+aF0.43958333333333333\r
+aF0.46041666666666664\r
+aF0.48749999999999999\r
+aF0.53333333333333333\r
+aF0.46562500000000001\r
+aF0.52083333333333337\r
+aF0.49791666666666667\r
+aF0.98124999999999996\r
+aF0.51145833333333335\r
+aF0.49583333333333335\r
+aF0.53645833333333337\r
+aF0.46770833333333334\r
+aF1.0270833333333333\r
+aF0.5552083333333333\r
+aF0.41354166666666664\r
+aF0.47708333333333336\r
+aF0.51145833333333335\r
+aF1.1114583333333334\r
+aF0.45000000000000001\r
+aF0.46458333333333335\r
+aF0.51041666666666663\r
+aF0.46770833333333334\r
+aF1.0427083333333333\r
+aF0.49583333333333335\r
+aF0.5\r
+aF0.46770833333333334\r
+aF0.52604166666666663\r
+aF1.0479166666666666\r
+aF0.50104166666666672\r
+aF0.46770833333333334\r
+aF0.484375\r
+aF0.49166666666666664\r
+aI4\r
+asS'notes'\r
+p61\r
+(lp62\r
+I0\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI4\r
+aI4\r
+aI4\r
+aI4\r
+aI3\r
+aI5\r
+aI5\r
+aI5\r
+aI5\r
+aI3\r
+aI6\r
+aI6\r
+aI6\r
+aI6\r
+aI6\r
+aI5\r
+aI5\r
+aI6\r
+aI5\r
+aI4\r
+aI3\r
+aI3\r
+aI6\r
+aI6\r
+aI6\r
+aI6\r
+aI6\r
+aI6\r
+aI8\r
+aI6\r
+aI6\r
+aI5\r
+aI5\r
+aI5\r
+aI5\r
+aI5\r
+aI7\r
+aI5\r
+aI5\r
+aI4\r
+aI3\r
+aI4\r
+aI0\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI4\r
+aI4\r
+aI4\r
+aI4\r
+aI3\r
+aI5\r
+aI5\r
+aI5\r
+aI7\r
+aI5\r
+aI5\r
+aI4\r
+aI3\r
+aI3\r
+asS'requiresExtendedScale'\r
+p63\r
+I01\r
+sS'quarterNoteLength'\r
+p64\r
+I857\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I67\r
+aI67\r
+aI67\r
+aI69\r
+aI71\r
+aI69\r
+aI67\r
+aI71\r
+aI69\r
+aI69\r
+aI67\r
+aI67\r
+aI67\r
+aI67\r
+aI69\r
+aI71\r
+aI69\r
+aI67\r
+aI71\r
+aI69\r
+aI69\r
+aI67\r
+aI69\r
+aI69\r
+aI69\r
+aI69\r
+aI64\r
+aI64\r
+aI69\r
+aI67\r
+aI66\r
+aI64\r
+aI62\r
+aI67\r
+aI67\r
+aI67\r
+aI69\r
+aI71\r
+aI69\r
+aI67\r
+aI71\r
+aI69\r
+aI69\r
+aI67\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI66\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'Au'\r
+p10\r
+aS'clair'\r
+p11\r
+aS'de'\r
+p12\r
+aS'la'\r
+p13\r
+aS'lu-'\r
+p14\r
+aS'-ne'\r
+p15\r
+aS'mon'\r
+p16\r
+aS'a-'\r
+p17\r
+aS'-mi'\r
+p18\r
+aS'Pie-'\r
+p19\r
+aS'-rrot'\r
+p20\r
+aS'pre-'\r
+p21\r
+aS'-te'\r
+p22\r
+aS'moi'\r
+p23\r
+aS'ta'\r
+p24\r
+aS'plu-'\r
+p25\r
+aS'-me'\r
+p26\r
+aS'pour'\r
+p27\r
+aS'e-'\r
+p28\r
+aS'-crire'\r
+p29\r
+aS'un'\r
+p30\r
+aS'mot'\r
+p31\r
+aS'ma'\r
+p32\r
+aS'chan-'\r
+p33\r
+aS'-delle'\r
+p34\r
+aS'est'\r
+p35\r
+aS'mor-'\r
+p36\r
+ag22\r
+aS'je'\r
+p37\r
+aS"n'ai"\r
+p38\r
+aS'plus'\r
+p39\r
+ag12\r
+aS'feu'\r
+p40\r
+aS'ou-'\r
+p41\r
+aS'-vre'\r
+p42\r
+ag23\r
+ag24\r
+aS'po-'\r
+p43\r
+aS'-rte'\r
+p44\r
+ag27\r
+aS"l'a-"\r
+p45\r
+aS'-mour'\r
+p46\r
+ag12\r
+aS'Dieu'\r
+p47\r
+asS'noteLengths'\r
+p48\r
+(lp49\r
+I1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI4\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI4\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI4\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI4\r
+asS'notes'\r
+p50\r
+(lp51\r
+I7\r
+aI7\r
+aI7\r
+aI8\r
+aI9\r
+aI8\r
+aI7\r
+aI9\r
+aI8\r
+aI8\r
+aI7\r
+aI7\r
+aI7\r
+aI7\r
+aI8\r
+aI9\r
+aI8\r
+aI7\r
+aI9\r
+aI8\r
+aI8\r
+aI7\r
+aI8\r
+aI8\r
+aI8\r
+aI8\r
+aI5\r
+aI5\r
+aI8\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI7\r
+aI7\r
+aI7\r
+aI8\r
+aI9\r
+aI8\r
+aI7\r
+aI9\r
+aI8\r
+aI8\r
+aI7\r
+asS'requiresExtendedScale'\r
+p52\r
+I00\r
+sS'quarterNoteLength'\r
+p53\r
+I500\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I60\r
+aI60\r
+aI64\r
+aI64\r
+aI62\r
+aI64\r
+aI62\r
+aI60\r
+aI60\r
+aI64\r
+aI64\r
+aI62\r
+aI64\r
+aI64\r
+aI64\r
+aI62\r
+aI60\r
+aI64\r
+aI67\r
+aI64\r
+aI67\r
+aI67\r
+aI64\r
+aI60\r
+aI64\r
+aI62\r
+aI60\r
+aI60\r
+aI64\r
+aI64\r
+aI64\r
+aI62\r
+aI64\r
+aI64\r
+aI64\r
+aI64\r
+aI60\r
+aI64\r
+aI62\r
+aI60\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'A'\r
+p10\r
+aS'la'\r
+p11\r
+aS'clai-'\r
+p12\r
+aS'-re'\r
+p13\r
+aS'fon-'\r
+p14\r
+aS'-tai-'\r
+p15\r
+aS'-ne'\r
+p16\r
+aS"m'en"\r
+p17\r
+aS'a-'\r
+p18\r
+aS'-llant'\r
+p19\r
+aS'pro-'\r
+p20\r
+aS'-me-'\r
+p21\r
+aS'-ner'\r
+p22\r
+aS"j'ai"\r
+p23\r
+aS'trou-'\r
+p24\r
+aS'-ve'\r
+p25\r
+aS"l'eau"\r
+p26\r
+aS'si'\r
+p27\r
+aS'be-'\r
+p28\r
+aS'-lle'\r
+p29\r
+aS'que'\r
+p30\r
+aS'je'\r
+p31\r
+aS"m'y"\r
+p32\r
+aS'suis'\r
+p33\r
+aS'bai-'\r
+p34\r
+aS'-gne'\r
+p35\r
+aS'il'\r
+p36\r
+aS'y-a'\r
+p37\r
+aS'long-'\r
+p38\r
+aS'-temps'\r
+p39\r
+ag30\r
+ag31\r
+aS"t'aime"\r
+p40\r
+aS'ja-'\r
+p41\r
+aS'-mais'\r
+p42\r
+ag31\r
+aS'ne'\r
+p43\r
+aS"t'ou-"\r
+p44\r
+aS'-blie-'\r
+p45\r
+aS'-rai'\r
+p46\r
+asS'noteLengths'\r
+p47\r
+(lp48\r
+I2\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI1\r
+aI1\r
+aI1\r
+aF0.5\r
+aF0.5\r
+aI2\r
+aI2\r
+aI1\r
+aF0.5\r
+aF0.5\r
+aI1\r
+aI1\r
+aI4\r
+asS'notes'\r
+p49\r
+(lp50\r
+I3\r
+aI3\r
+aI5\r
+aI5\r
+aI4\r
+aI5\r
+aI4\r
+aI3\r
+aI3\r
+aI5\r
+aI5\r
+aI4\r
+aI5\r
+aI5\r
+aI5\r
+aI4\r
+aI3\r
+aI5\r
+aI7\r
+aI5\r
+aI7\r
+aI7\r
+aI5\r
+aI3\r
+aI5\r
+aI4\r
+aI3\r
+aI3\r
+aI5\r
+aI5\r
+aI5\r
+aI4\r
+aI5\r
+aI5\r
+aI5\r
+aI5\r
+aI3\r
+aI5\r
+aI4\r
+aI3\r
+asS'requiresExtendedScale'\r
+p51\r
+I00\r
+sS'quarterNoteLength'\r
+p52\r
+I400\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I64\r
+aI60\r
+aI64\r
+aI60\r
+aI64\r
+aI60\r
+aI62\r
+aI62\r
+aI64\r
+aI65\r
+aI62\r
+aI64\r
+aI60\r
+aI62\r
+aI64\r
+aI60\r
+aI64\r
+aI60\r
+aI64\r
+aI60\r
+aI62\r
+aI62\r
+aI64\r
+aI65\r
+aI62\r
+aI64\r
+aI60\r
+aI62\r
+aI67\r
+aI69\r
+aI67\r
+aI65\r
+aI65\r
+aI62\r
+aI65\r
+aI64\r
+aI60\r
+aI64\r
+aI62\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI64\r
+aI62\r
+aI60\r
+aI62\r
+aI60\r
+aI59\r
+aI60\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'Ma'\r
+p10\r
+aS'pe-'\r
+p11\r
+aS'-tite'\r
+p12\r
+aS'est'\r
+p13\r
+aS'co-'\r
+p14\r
+aS'-mme'\r
+p15\r
+aS"l'eau"\r
+p16\r
+aS'elle'\r
+p17\r
+ag13\r
+ag14\r
+ag15\r
+ag16\r
+aS'vi-'\r
+p18\r
+aS'-ve'\r
+p19\r
+aS'e-'\r
+p20\r
+aS'-lle'\r
+p21\r
+aS'court'\r
+p22\r
+aS'comme'\r
+p23\r
+aS'un'\r
+p24\r
+aS'rui-'\r
+p25\r
+aS'-sseau'\r
+p26\r
+aS'que'\r
+p27\r
+aS'les'\r
+p28\r
+aS'en-'\r
+p29\r
+aS'-fants'\r
+p30\r
+aS'pour-'\r
+p31\r
+aS'-sui-'\r
+p32\r
+aS'-vent'\r
+p33\r
+aS've-'\r
+p34\r
+aS'-nez'\r
+p35\r
+ag34\r
+ag35\r
+aS'mes'\r
+p36\r
+aS'a-'\r
+p37\r
+aS'-gneaux'\r
+p38\r
+ag36\r
+ag37\r
+aS'-gne-'\r
+p39\r
+aS'-lets'\r
+p40\r
+aS'ja-'\r
+p41\r
+aS'-mais'\r
+p42\r
+ag41\r
+ag42\r
+aS'vous'\r
+p43\r
+aS'ne'\r
+p44\r
+aS'la'\r
+p45\r
+aS'ra-'\r
+p46\r
+aS'-ttra-'\r
+p47\r
+aS'-pe-'\r
+p48\r
+aS'-rez'\r
+p49\r
+asS'noteLengths'\r
+p50\r
+(lp51\r
+I2\r
+aI1\r
+aI2\r
+aI1\r
+aI2\r
+aI1\r
+aI3\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI3\r
+aI3\r
+aI2\r
+aI1\r
+aI2\r
+aI1\r
+aI2\r
+aI1\r
+aI3\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI6\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI6\r
+asS'notes'\r
+p52\r
+(lp53\r
+I5\r
+aI3\r
+aI5\r
+aI3\r
+aI5\r
+aI3\r
+aI4\r
+aI4\r
+aI5\r
+aI6\r
+aI4\r
+aI5\r
+aI3\r
+aI4\r
+aI5\r
+aI3\r
+aI5\r
+aI3\r
+aI5\r
+aI3\r
+aI4\r
+aI4\r
+aI5\r
+aI6\r
+aI4\r
+aI5\r
+aI3\r
+aI4\r
+aI7\r
+aI8\r
+aI7\r
+aI6\r
+aI6\r
+aI4\r
+aI6\r
+aI5\r
+aI3\r
+aI5\r
+aI4\r
+aI3\r
+aI4\r
+aI5\r
+aI6\r
+aI5\r
+aI4\r
+aI3\r
+aI4\r
+aI3\r
+aI2\r
+aI3\r
+asS'requiresExtendedScale'\r
+p54\r
+I01\r
+sS'quarterNoteLength'\r
+p55\r
+I300\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I60\r
+aI62\r
+aI64\r
+aI60\r
+aI60\r
+aI62\r
+aI64\r
+aI60\r
+aI64\r
+aI65\r
+aI67\r
+aI64\r
+aI65\r
+aI67\r
+aI67\r
+aI69\r
+aI67\r
+aI65\r
+aI64\r
+aI60\r
+aI67\r
+aI69\r
+aI67\r
+aI65\r
+aI64\r
+aI60\r
+aI60\r
+aI55\r
+aI60\r
+aI60\r
+aI55\r
+aI60\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'Fre-'\r
+p10\r
+aS'-re'\r
+p11\r
+aS'Ja-'\r
+p12\r
+aS'-cques'\r
+p13\r
+aS'fre-'\r
+p14\r
+ag11\r
+aS'-Ja'\r
+p15\r
+ag13\r
+aS'dor-'\r
+p16\r
+aS'-mez'\r
+p17\r
+aS'vous'\r
+p18\r
+ag16\r
+ag17\r
+ag18\r
+aS'so-'\r
+p19\r
+aS'-nnez'\r
+p20\r
+aS'les'\r
+p21\r
+aS'ma-'\r
+p22\r
+aS'-ti-'\r
+p23\r
+aS'-nes'\r
+p24\r
+ag19\r
+ag20\r
+ag21\r
+ag22\r
+ag23\r
+ag24\r
+aS'ding'\r
+p25\r
+ag25\r
+aS'dong'\r
+p26\r
+ag25\r
+ag25\r
+ag26\r
+asS'noteLengths'\r
+p27\r
+(lp28\r
+I1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI1\r
+aI2\r
+aF1.5\r
+aF0.5\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aF1.5\r
+aF0.5\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI1\r
+aI2\r
+asS'notes'\r
+p29\r
+(lp30\r
+I3\r
+aI4\r
+aI5\r
+aI3\r
+aI3\r
+aI4\r
+aI5\r
+aI3\r
+aI5\r
+aI6\r
+aI7\r
+aI5\r
+aI6\r
+aI7\r
+aI7\r
+aI8\r
+aI7\r
+aI6\r
+aI5\r
+aI3\r
+aI7\r
+aI8\r
+aI7\r
+aI6\r
+aI5\r
+aI3\r
+aI3\r
+aI0\r
+aI3\r
+aI3\r
+aI0\r
+aI3\r
+asS'requiresExtendedScale'\r
+p31\r
+I01\r
+sS'quarterNoteLength'\r
+p32\r
+I600\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I60\r
+aI62\r
+aI64\r
+aI60\r
+aI62\r
+aI62\r
+aI64\r
+aI65\r
+aI65\r
+aI64\r
+aI64\r
+aI60\r
+aI62\r
+aI64\r
+aI60\r
+aI62\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI60\r
+aI67\r
+aI67\r
+aI65\r
+aI64\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI62\r
+aI67\r
+aI67\r
+aI65\r
+aI64\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI62\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S"J'ai"\r
+p10\r
+aS'du'\r
+p11\r
+aS'bon'\r
+p12\r
+aS'ta-'\r
+p13\r
+aS'-bac'\r
+p14\r
+aS'dans'\r
+p15\r
+aS'ma'\r
+p16\r
+ag13\r
+aS'-ba-'\r
+p17\r
+aS'-tie-'\r
+p18\r
+aS'-re'\r
+p19\r
+ag10\r
+ag11\r
+ag12\r
+ag13\r
+ag14\r
+aS'tu'\r
+p20\r
+aS"n'en"\r
+p21\r
+aS'au-'\r
+p22\r
+aS'-ras'\r
+p23\r
+aS'pas'\r
+p24\r
+aS"j'en"\r
+p25\r
+aS'ai'\r
+p26\r
+ag11\r
+aS'fin'\r
+p27\r
+aS'et'\r
+p28\r
+ag11\r
+aS'bien'\r
+p29\r
+aS'ra-'\r
+p30\r
+aS'-pe'\r
+p31\r
+aS'mais'\r
+p32\r
+aS'ce'\r
+p33\r
+aS"n'est"\r
+p34\r
+ag24\r
+aS'pour'\r
+p35\r
+aS'ton'\r
+p36\r
+aS'vi-'\r
+p37\r
+aS'-lain'\r
+p38\r
+aS'nez'\r
+p39\r
+asS'noteLengths'\r
+p40\r
+(lp41\r
+I1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI2\r
+aI2\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI4\r
+aI2\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI4\r
+aI2\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI1\r
+aI2\r
+aI2\r
+aI4\r
+asS'notes'\r
+p42\r
+(lp43\r
+I3\r
+aI4\r
+aI5\r
+aI3\r
+aI4\r
+aI4\r
+aI5\r
+aI6\r
+aI6\r
+aI5\r
+aI5\r
+aI3\r
+aI4\r
+aI5\r
+aI3\r
+aI4\r
+aI4\r
+aI5\r
+aI6\r
+aI7\r
+aI3\r
+aI7\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI5\r
+aI6\r
+aI7\r
+aI4\r
+aI7\r
+aI7\r
+aI6\r
+aI5\r
+aI4\r
+aI5\r
+aI6\r
+aI7\r
+aI4\r
+asS'requiresExtendedScale'\r
+p44\r
+I00\r
+sS'quarterNoteLength'\r
+p45\r
+I400\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I55\r
+aI55\r
+aI55\r
+aI60\r
+aI60\r
+aI62\r
+aI62\r
+aI67\r
+aI64\r
+aI60\r
+aI60\r
+aI64\r
+aI60\r
+aI57\r
+aI65\r
+aI62\r
+aI59\r
+aI60\r
+aI60\r
+aI62\r
+aI64\r
+aI64\r
+aI64\r
+aI65\r
+aI64\r
+aI64\r
+aI62\r
+aI62\r
+aI64\r
+aI65\r
+aI65\r
+aI65\r
+aI67\r
+aI65\r
+aI64\r
+aI67\r
+aI67\r
+aI67\r
+aI64\r
+aI60\r
+aI67\r
+aI64\r
+aI60\r
+aI55\r
+aI55\r
+aI55\r
+aI59\r
+aI62\r
+aI65\r
+aI62\r
+aI59\r
+aI62\r
+aI60\r
+aI58\r
+aI57\r
+aI60\r
+aI60\r
+aI60\r
+aI59\r
+aI60\r
+aI62\r
+aI62\r
+aI63\r
+aI63\r
+aI63\r
+aI63\r
+aI65\r
+aI67\r
+aI62\r
+aI63\r
+aI62\r
+aI60\r
+aI60\r
+aI60\r
+aI63\r
+aI62\r
+aI60\r
+aI60\r
+aI59\r
+aI67\r
+aI67\r
+aI67\r
+aI64\r
+aI60\r
+aI62\r
+aI67\r
+aI67\r
+aI67\r
+aI64\r
+aI60\r
+aI62\r
+aI55\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI62\r
+aI69\r
+aI67\r
+aI64\r
+aI65\r
+aI62\r
+aI60\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'A-'\r
+p10\r
+aS'-llons'\r
+p11\r
+aS'en-'\r
+p12\r
+aS'-fants'\r
+p13\r
+aS'de'\r
+p14\r
+aS'la'\r
+p15\r
+aS'pa'\r
+p16\r
+aS'tri-'\r
+p17\r
+aS'-i-'\r
+p18\r
+aS'-e'\r
+p19\r
+aS'le'\r
+p20\r
+aS'jour'\r
+p21\r
+ag14\r
+aS'gloire'\r
+p22\r
+aS'est'\r
+p23\r
+aS'a-'\r
+p24\r
+aS'-rri-'\r
+p25\r
+aS'-ve'\r
+p26\r
+aS'con-'\r
+p27\r
+aS'-tre'\r
+p28\r
+aS'nous'\r
+p29\r
+ag14\r
+ag15\r
+aS'ty-'\r
+p30\r
+aS'-ra-'\r
+p31\r
+aS'-nni-'\r
+p32\r
+ag19\r
+aS"l'e-"\r
+p33\r
+aS'-ten-'\r
+p34\r
+aS'-dard'\r
+p35\r
+aS'san-'\r
+p36\r
+aS'-glant'\r
+p37\r
+ag23\r
+aS'le-'\r
+p38\r
+ag26\r
+ag33\r
+ag34\r
+ag35\r
+ag36\r
+aS'-an-'\r
+p39\r
+ag37\r
+ag23\r
+ag38\r
+ag26\r
+ag12\r
+ag34\r
+aS'-dez'\r
+p40\r
+aS'vous'\r
+p41\r
+aS'dans'\r
+p42\r
+aS'nos'\r
+p43\r
+aS'cam-'\r
+p44\r
+aS'-pa-'\r
+p45\r
+aS'-gnes'\r
+p46\r
+aS'mu-'\r
+p47\r
+aS'-gir'\r
+p48\r
+aS'ces'\r
+p49\r
+aS'fe-'\r
+p50\r
+aS'-ro-'\r
+p51\r
+aS'-ces'\r
+p52\r
+aS'sol-'\r
+p53\r
+aS'-dats'\r
+p54\r
+aS'qui'\r
+p55\r
+aS'vie-'\r
+p56\r
+aS'-nnent'\r
+p57\r
+aS'ju-'\r
+p58\r
+aS'-sque'\r
+p59\r
+ag42\r
+ag43\r
+aS'bras'\r
+p60\r
+aS'e-'\r
+p61\r
+aS'-gor-'\r
+p62\r
+aS'-ger'\r
+p63\r
+ag43\r
+aS'fils'\r
+p64\r
+aS'et'\r
+p65\r
+ag43\r
+aS'com-'\r
+p66\r
+ag45\r
+ag46\r
+aS'aux'\r
+p67\r
+aS'ar-'\r
+p68\r
+aS'-mes'\r
+p69\r
+aS'ci-'\r
+p70\r
+aS'-toy-'\r
+p71\r
+aS'-yen'\r
+p72\r
+aS'for-'\r
+p73\r
+aS'-mez'\r
+p74\r
+aS'vos'\r
+p75\r
+aS'ba-'\r
+p76\r
+aS'-ta-'\r
+p77\r
+aS'-illons'\r
+p78\r
+aS'mar-'\r
+p79\r
+aS'-chons'\r
+p80\r
+ag79\r
+ag80\r
+aS"qu'un"\r
+p81\r
+aS'sang'\r
+p82\r
+aS'im-'\r
+p83\r
+aS'-pur'\r
+p84\r
+ag24\r
+aS'-breu-'\r
+p85\r
+ag26\r
+ag43\r
+aS'si-'\r
+p86\r
+ag78\r
+aS'pon'\r
+p87\r
+ag87\r
+ag87\r
+ag87\r
+asS'noteLengths'\r
+p88\r
+(lp89\r
+I1\r
+aI2\r
+aI1\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI5\r
+aI1\r
+aI2\r
+aI1\r
+aI2\r
+aI1\r
+aI3\r
+aI6\r
+aI2\r
+aI1\r
+aI9\r
+aI2\r
+aI1\r
+aI3\r
+aI3\r
+aI3\r
+aI2\r
+aI1\r
+aI3\r
+aI6\r
+aI2\r
+aI1\r
+aI3\r
+aI3\r
+aI3\r
+aI2\r
+aI1\r
+aI6\r
+aI2\r
+aI1\r
+aI3\r
+aI2\r
+aI1\r
+aI3\r
+aI2\r
+aI1\r
+aI6\r
+aI1\r
+aI2\r
+aI1\r
+aI6\r
+aI3\r
+aI2\r
+aI1\r
+aI3\r
+aI3\r
+aI6\r
+aI3\r
+aI2\r
+aI1\r
+aI3\r
+aI2\r
+aI1\r
+aI6\r
+aI3\r
+aI5\r
+aI1\r
+aI2\r
+aI1\r
+aI2\r
+aI1\r
+aI6\r
+aI2\r
+aI1\r
+aI5\r
+aI1\r
+aI2\r
+aI1\r
+aI2\r
+aI1\r
+aI6\r
+aI5\r
+aI1\r
+aI7\r
+aI1\r
+aI2\r
+aI1\r
+aI8\r
+aI1\r
+aI7\r
+aI1\r
+aI2\r
+aI1\r
+aI8\r
+aI3\r
+aI9\r
+aI3\r
+aI9\r
+aI6\r
+aI3\r
+aI3\r
+aI9\r
+aI3\r
+aI8\r
+aI1\r
+aI2\r
+aI1\r
+aI2\r
+aI1\r
+aI2\r
+aI1\r
+aI8\r
+asS'notes'\r
+p90\r
+(lp91\r
+I0\r
+aI0\r
+aI0\r
+aI3\r
+aI3\r
+aI4\r
+aI4\r
+aI7\r
+aI5\r
+aI3\r
+aI3\r
+aI5\r
+aI3\r
+aI1\r
+aI6\r
+aI4\r
+aI2\r
+aI3\r
+aI3\r
+aI4\r
+aI5\r
+aI5\r
+aI5\r
+aI6\r
+aI5\r
+aI5\r
+aI4\r
+aI4\r
+aI5\r
+aI6\r
+aI6\r
+aI6\r
+aI7\r
+aI6\r
+aI5\r
+aI7\r
+aI7\r
+aI7\r
+aI5\r
+aI3\r
+aI7\r
+aI5\r
+aI3\r
+aI0\r
+aI0\r
+aI0\r
+aI2\r
+aI4\r
+aI6\r
+aI4\r
+aI2\r
+aI4\r
+aI3\r
+aI2\r
+aI1\r
+aI3\r
+aI3\r
+aI3\r
+aI2\r
+aI3\r
+aI4\r
+aI4\r
+aI5\r
+aI5\r
+aI5\r
+aI5\r
+aI6\r
+aI7\r
+aI4\r
+aI5\r
+aI4\r
+aI3\r
+aI3\r
+aI3\r
+aI5\r
+aI4\r
+aI3\r
+aI3\r
+aI2\r
+aI7\r
+aI7\r
+aI7\r
+aI5\r
+aI3\r
+aI4\r
+aI7\r
+aI7\r
+aI7\r
+aI5\r
+aI3\r
+aI4\r
+aI0\r
+aI3\r
+aI4\r
+aI5\r
+aI6\r
+aI7\r
+aI8\r
+aI4\r
+aI8\r
+aI7\r
+aI5\r
+aI6\r
+aI4\r
+aI3\r
+asS'requiresExtendedScale'\r
+p92\r
+I01\r
+sS'quarterNoteLength'\r
+p93\r
+I300\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I60\r
+aI65\r
+aI65\r
+aI65\r
+aI67\r
+aI65\r
+aI65\r
+aI67\r
+aI69\r
+aI69\r
+aI69\r
+aI70\r
+aI69\r
+aI67\r
+aI65\r
+aI65\r
+aI65\r
+aI65\r
+aI64\r
+aI62\r
+aI60\r
+aI60\r
+aI60\r
+aI65\r
+aI65\r
+aI65\r
+aI67\r
+aI67\r
+aI65\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI58\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI70\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'pe-'\r
+p10\r
+aS'-ti'\r
+p11\r
+aS'Pa-'\r
+p12\r
+aS'-pa'\r
+p13\r
+aS'No-'\r
+p14\r
+aS'-el'\r
+p15\r
+aS'quand'\r
+p16\r
+aS'tu'\r
+p17\r
+aS'de-'\r
+p18\r
+aS'-scen-'\r
+p19\r
+aS'-dras'\r
+p20\r
+aS'du'\r
+p21\r
+aS'ciel'\r
+p22\r
+aS'a-'\r
+p23\r
+aS'-vec'\r
+p24\r
+aS'tes'\r
+p25\r
+aS'jou-'\r
+p26\r
+aS'-ets'\r
+p27\r
+aS'par'\r
+p28\r
+aS'mi-'\r
+p29\r
+aS'-lliers'\r
+p30\r
+aS"n'ou-"\r
+p31\r
+aS'-blie'\r
+p32\r
+aS'pas'\r
+p33\r
+aS'mes'\r
+p34\r
+ag10\r
+aS'-tits'\r
+p35\r
+aS'sou-'\r
+p36\r
+aS'-liers'\r
+p37\r
+asS'noteLengths'\r
+p38\r
+(lp39\r
+I1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI3\r
+aF0.5\r
+aF0.5\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI3\r
+aI1\r
+aF1.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aI3\r
+aF0.5\r
+aF0.5\r
+aI1\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aF0.5\r
+aI3\r
+asS'notes'\r
+p40\r
+(lp41\r
+I3\r
+aI6\r
+aI6\r
+aI6\r
+aI7\r
+aI6\r
+aI6\r
+aI7\r
+aI8\r
+aI8\r
+aI8\r
+aI9\r
+aI8\r
+aI7\r
+aI6\r
+aI6\r
+aI6\r
+aI6\r
+aI5\r
+aI4\r
+aI3\r
+aI3\r
+aI3\r
+aI6\r
+aI6\r
+aI6\r
+aI7\r
+aI7\r
+aI6\r
+asS'requiresExtendedScale'\r
+p42\r
+I00\r
+sS'quarterNoteLength'\r
+p43\r
+I500\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I63\r
+aI63\r
+aI63\r
+aI60\r
+aI63\r
+aI65\r
+aI63\r
+aI63\r
+aI63\r
+aI60\r
+aI67\r
+aI65\r
+aI65\r
+aI65\r
+aI65\r
+aI61\r
+aI68\r
+aI67\r
+aI67\r
+aI67\r
+aI68\r
+aI70\r
+aI67\r
+aI63\r
+aI63\r
+aI63\r
+aI63\r
+aI60\r
+aI63\r
+aI65\r
+aI68\r
+aI70\r
+aI72\r
+aI70\r
+aI68\r
+aI72\r
+aI70\r
+aI68\r
+aI65\r
+aI65\r
+aI68\r
+aI67\r
+aI65\r
+aI68\r
+aI65\r
+aI63\r
+aI60\r
+aI63\r
+aI65\r
+aI60\r
+aI63\r
+aI65\r
+aI60\r
+aI63\r
+aI65\r
+aI60\r
+aI63\r
+aI65\r
+aI60\r
+aI63\r
+aI67\r
+aI65\r
+aI63\r
+aI67\r
+aI65\r
+aI63\r
+aI68\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI56\r
+aI58\r
+aI60\r
+aI61\r
+aI63\r
+aI65\r
+aI67\r
+aI68\r
+aI70\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'Ah'\r
+p10\r
+aS'le'\r
+p11\r
+aS'pe-'\r
+p12\r
+aS'-tit'\r
+p13\r
+aS'vin'\r
+p14\r
+aS'blanc'\r
+p15\r
+aS"qu'on"\r
+p16\r
+aS'boit'\r
+p17\r
+aS'sous'\r
+p18\r
+aS'les'\r
+p19\r
+aS'to-'\r
+p20\r
+aS'-nelles'\r
+p21\r
+aS'quand'\r
+p22\r
+ag19\r
+aS'fi-'\r
+p23\r
+aS'-lles'\r
+p24\r
+aS'sont'\r
+p25\r
+aS'belles'\r
+p26\r
+aS'du'\r
+p27\r
+aS'co-'\r
+p28\r
+aS'-te'\r
+p29\r
+aS'de'\r
+p30\r
+aS'No-'\r
+p31\r
+aS'-geant'\r
+p32\r
+aS'et'\r
+p33\r
+aS'puis'\r
+p34\r
+ag30\r
+aS'temps'\r
+p35\r
+aS'en'\r
+p36\r
+ag35\r
+aS'un'\r
+p37\r
+aS'air'\r
+p38\r
+ag30\r
+aS'vie-'\r
+p39\r
+aS'-lle'\r
+p40\r
+aS'ro-'\r
+p41\r
+aS'-man-'\r
+p42\r
+aS'-ce'\r
+p43\r
+aS'sem-'\r
+p44\r
+aS'-ble'\r
+p45\r
+aS'do-'\r
+p46\r
+aS'-nner'\r
+p47\r
+aS'la'\r
+p48\r
+aS'ca-'\r
+p49\r
+aS'-den-'\r
+p50\r
+ag43\r
+aS'pour'\r
+p51\r
+aS'fau-'\r
+p52\r
+aS'-ter'\r
+p53\r
+ag51\r
+ag52\r
+ag53\r
+aS'dans'\r
+p54\r
+ag19\r
+aS'bois'\r
+p55\r
+ag54\r
+ag19\r
+aS'pres'\r
+p56\r
+ag27\r
+ag28\r
+ag29\r
+ag27\r
+ag28\r
+ag29\r
+ag30\r
+ag31\r
+ag32\r
+asS'noteLengths'\r
+p57\r
+(lp58\r
+I1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI6\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI6\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI6\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI6\r
+aI1\r
+aI1\r
+aI1\r
+aI2\r
+aI1\r
+aI6\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI3\r
+aI3\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI3\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI1\r
+aI6\r
+asS'notes'\r
+p59\r
+(lp60\r
+I5\r
+aI5\r
+aI5\r
+aI3\r
+aI5\r
+aI6\r
+aI5\r
+aI5\r
+aI5\r
+aI3\r
+aI7\r
+aI6\r
+aI6\r
+aI6\r
+aI6\r
+aI4\r
+aI8\r
+aI7\r
+aI7\r
+aI7\r
+aI8\r
+aI9\r
+aI7\r
+aI5\r
+aI5\r
+aI5\r
+aI5\r
+aI3\r
+aI5\r
+aI6\r
+aI8\r
+aI9\r
+aI10\r
+aI9\r
+aI8\r
+aI10\r
+aI9\r
+aI8\r
+aI6\r
+aI6\r
+aI8\r
+aI7\r
+aI6\r
+aI8\r
+aI6\r
+aI5\r
+aI3\r
+aI5\r
+aI6\r
+aI3\r
+aI5\r
+aI6\r
+aI3\r
+aI5\r
+aI6\r
+aI3\r
+aI5\r
+aI6\r
+aI3\r
+aI5\r
+aI7\r
+aI6\r
+aI5\r
+aI7\r
+aI6\r
+aI5\r
+aI8\r
+asS'requiresExtendedScale'\r
+p61\r
+I00\r
+sS'quarterNoteLength'\r
+p62\r
+I300\r
+sb.
\ No newline at end of file
--- /dev/null
+(isongs.Song\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I60\r
+aI60\r
+aI60\r
+aI63\r
+aI67\r
+aI65\r
+aI60\r
+aI60\r
+aI72\r
+aI72\r
+aI72\r
+aI70\r
+aI67\r
+aI65\r
+aI67\r
+aI72\r
+aI72\r
+aI72\r
+aI60\r
+aI63\r
+aI65\r
+aI67\r
+aI65\r
+aI60\r
+aI63\r
+aI60\r
+aI60\r
+aI60\r
+aI60\r
+aI59\r
+aI59\r
+aI60\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI58\r
+aI60\r
+aI62\r
+aI63\r
+aI65\r
+aI67\r
+aI69\r
+aI70\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S'Les'\r
+p10\r
+aS'por-'\r
+p11\r
+aS'-tes'\r
+p12\r
+aS'du'\r
+p13\r
+aS'pe-'\r
+p14\r
+aS'-ni-'\r
+p15\r
+aS'-ten-'\r
+p16\r
+aS'-cier'\r
+p17\r
+aS'bien-'\r
+p18\r
+aS'-tot'\r
+p19\r
+aS'vont'\r
+p20\r
+aS'se'\r
+p21\r
+aS're-'\r
+p22\r
+aS'-fer-'\r
+p23\r
+aS'-mer'\r
+p24\r
+aS'et'\r
+p25\r
+aS"c'est"\r
+p26\r
+aS'la'\r
+p27\r
+aS'que'\r
+p28\r
+aS'je'\r
+p29\r
+aS'fi-'\r
+p30\r
+ag15\r
+aS'-rai'\r
+p31\r
+aS'ma'\r
+p32\r
+aS'vie'\r
+p33\r
+aS'comme'\r
+p34\r
+aS"d'au-"\r
+p35\r
+aS'-tres'\r
+p36\r
+aS'gars'\r
+p37\r
+aS"l'ont"\r
+p38\r
+ag30\r
+aS'-nie'\r
+p39\r
+asS'noteLengths'\r
+p40\r
+(lp41\r
+I1\r
+aI5\r
+aI1\r
+aI5\r
+aI1\r
+aI1\r
+aI6\r
+aI4\r
+aI1\r
+aI5\r
+aI1\r
+aI4\r
+aI1\r
+aI1\r
+aI10\r
+aI1\r
+aI1\r
+aI5\r
+aI1\r
+aI4\r
+aI1\r
+aI1\r
+aI5\r
+aI1\r
+aI5\r
+aI1\r
+aI1\r
+aI1\r
+aI4\r
+aI5\r
+aI1\r
+aI12\r
+asS'notes'\r
+p42\r
+(lp43\r
+I3\r
+aI3\r
+aI3\r
+aI5\r
+aI7\r
+aI6\r
+aI3\r
+aI3\r
+aI10\r
+aI10\r
+aI10\r
+aI9\r
+aI7\r
+aI6\r
+aI7\r
+aI10\r
+aI10\r
+aI10\r
+aI3\r
+aI5\r
+aI6\r
+aI7\r
+aI6\r
+aI3\r
+aI5\r
+aI3\r
+aI3\r
+aI3\r
+aI3\r
+aI2\r
+aI2\r
+aI3\r
+asS'requiresExtendedScale'\r
+p44\r
+I01\r
+sS'quarterNoteLength'\r
+p45\r
+I250\r
+sb.
\ No newline at end of file
--- /dev/null
+(i__main__\r
+Song\r
+p0\r
+(dp1\r
+S'midiNoteNumbers'\r
+p2\r
+(lp3\r
+I64\r
+aI66\r
+aI67\r
+aI72\r
+aI62\r
+aI64\r
+aI66\r
+aI71\r
+aI71\r
+aI60\r
+aI62\r
+aI64\r
+aI69\r
+aI59\r
+aI61\r
+aI63\r
+aI67\r
+aI64\r
+aI66\r
+aI67\r
+aI72\r
+aI62\r
+aI64\r
+aI66\r
+aI71\r
+aI71\r
+aI60\r
+aI62\r
+aI64\r
+aI69\r
+aI66\r
+aI69\r
+aI67\r
+aI64\r
+aI64\r
+aI64\r
+aI63\r
+aI64\r
+aI66\r
+aI66\r
+aI64\r
+aI67\r
+aI67\r
+aI66\r
+aI67\r
+aI69\r
+aI62\r
+aI74\r
+aI72\r
+aI71\r
+aI69\r
+aI71\r
+aI72\r
+aI72\r
+aI69\r
+aI69\r
+aI66\r
+aI72\r
+aI71\r
+aI71\r
+aI64\r
+aI69\r
+aI69\r
+aI67\r
+aI66\r
+aI67\r
+aI59\r
+aI64\r
+asS'scale'\r
+p4\r
+(lp5\r
+I55\r
+aI57\r
+aI59\r
+aI60\r
+aI62\r
+aI64\r
+aI65\r
+aI67\r
+aI69\r
+aI71\r
+aI72\r
+asS'name'\r
+p6\r
+S'unknownSong'\r
+p7\r
+sS'lyrics'\r
+p8\r
+(lp9\r
+S"c'est"\r
+p10\r
+aS'une'\r
+p11\r
+aS'chan-'\r
+p12\r
+aS'-son'\r
+p13\r
+aS'qui'\r
+p14\r
+aS'nous'\r
+p15\r
+aS'res-'\r
+p16\r
+aS'-sem-'\r
+p17\r
+aS'-ble'\r
+p18\r
+aS'toi'\r
+p19\r
+aS'tu'\r
+p20\r
+aS"m'ai-"\r
+p21\r
+aS'-mais'\r
+p22\r
+aS'et'\r
+p23\r
+aS'je'\r
+p24\r
+aS"t'ai-"\r
+p25\r
+ag22\r
+ag15\r
+aS'e-'\r
+p26\r
+aS'-tions'\r
+p27\r
+aS'tout'\r
+p28\r
+aS'les'\r
+p29\r
+aS'deux'\r
+p30\r
+aS'en-'\r
+p31\r
+ag17\r
+ag18\r
+ag19\r
+ag14\r
+ag21\r
+ag22\r
+aS'moi'\r
+p32\r
+ag14\r
+ag25\r
+ag22\r
+aS'mais'\r
+p33\r
+aS'la'\r
+p34\r
+aS'vie'\r
+p35\r
+aS'se-'\r
+p36\r
+aS'-pare'\r
+p37\r
+aS'ceux'\r
+p38\r
+ag14\r
+aS"s'aiment"\r
+p39\r
+ag28\r
+aS'dou-'\r
+p40\r
+aS'-ce-'\r
+p41\r
+aS'-ment'\r
+p42\r
+aS'sans'\r
+p43\r
+aS'faire'\r
+p44\r
+aS'de'\r
+p45\r
+aS'bruit'\r
+p46\r
+ag23\r
+ag34\r
+aS'mer'\r
+p47\r
+aS'ef-'\r
+p48\r
+aS'-fa-'\r
+p49\r
+aS'-ce'\r
+p50\r
+aS'sur'\r
+p51\r
+aS'le'\r
+p52\r
+aS'sa-'\r
+p53\r
+ag18\r
+ag29\r
+aS'pas'\r
+p54\r
+aS'des'\r
+p55\r
+aS'a-'\r
+p56\r
+aS'-mants'\r
+p57\r
+aS'de-'\r
+p58\r
+aS'-su-'\r
+p59\r
+aS'-nis'\r
+p60\r
+asS'noteLengths'\r
+p61\r
+(lp62\r
+F0.6010416666666667\r
+aF0.59791666666666665\r
+aF0.67812499999999998\r
+aF6.0229166666666663\r
+aF0.6322916666666667\r
+aF0.64583333333333337\r
+aF0.63958333333333328\r
+aF0.65104166666666663\r
+aF5.4302083333333337\r
+aF0.64270833333333333\r
+aF0.51249999999999996\r
+aF0.75\r
+aF6.1322916666666663\r
+aF0.63645833333333335\r
+aF0.62708333333333333\r
+aF0.69270833333333337\r
+aF6.083333333333333\r
+aF0.54374999999999996\r
+aF0.69791666666666663\r
+aF0.67500000000000004\r
+aF6.0302083333333334\r
+aF0.57187500000000002\r
+aF0.62083333333333335\r
+aF0.68854166666666672\r
+aF0.6645833333333333\r
+aF5.4333333333333336\r
+aF0.61354166666666665\r
+aF0.55104166666666665\r
+aF0.72291666666666665\r
+aF6.1260416666666666\r
+aF0.69687500000000002\r
+aF0.71979166666666672\r
+aF0.63541666666666663\r
+aF5.9104166666666664\r
+aF0.5708333333333333\r
+aF0.35312500000000002\r
+aF0.6177083333333333\r
+aF0.484375\r
+aF5.7979166666666666\r
+aF1.6114583333333334\r
+aF0.43229166666666669\r
+aF5.9343750000000002\r
+aF1.2916666666666667\r
+aF0.39895833333333336\r
+aF0.34375\r
+aF5.0062499999999996\r
+aF0.9927083333333333\r
+aF1.003125\r
+aF0.97083333333333333\r
+aF6.104166666666667\r
+aF1.6208333333333333\r
+aF0.41666666666666669\r
+aF0.67083333333333328\r
+aF0.38437500000000002\r
+aF0.59583333333333333\r
+aF0.41875000000000001\r
+aF1.4927083333333333\r
+aF0.44583333333333336\r
+aF0.6020833333333333\r
+aF6.291666666666667\r
+aF1.028125\r
+aF3.4343750000000002\r
+aF0.27083333333333331\r
+aF0.36875000000000002\r
+aF3.2697916666666669\r
+aF0.33958333333333335\r
+aF0.33124999999999999\r
+aI4\r
+asS'notes'\r
+p63\r
+(lp64\r
+I5\r
+aI6\r
+aI7\r
+aI10\r
+aI4\r
+aI5\r
+aI6\r
+aI9\r
+aI9\r
+aI3\r
+aI4\r
+aI5\r
+aI8\r
+aI2\r
+aI3\r
+aI4\r
+aI7\r
+aI5\r
+aI6\r
+aI7\r
+aI10\r
+aI4\r
+aI5\r
+aI6\r
+aI9\r
+aI9\r
+aI3\r
+aI4\r
+aI5\r
+aI8\r
+aI6\r
+aI8\r
+aI7\r
+aI5\r
+aI5\r
+aI5\r
+aI4\r
+aI5\r
+aI6\r
+aI6\r
+aI5\r
+aI7\r
+aI7\r
+aI6\r
+aI7\r
+aI8\r
+aI4\r
+aI10\r
+aI10\r
+aI9\r
+aI8\r
+aI9\r
+aI10\r
+aI10\r
+aI8\r
+aI8\r
+aI6\r
+aI10\r
+aI9\r
+aI9\r
+aI5\r
+aI8\r
+aI8\r
+aI7\r
+aI6\r
+aI7\r
+aI2\r
+aI5\r
+asS'requiresExtendedScale'\r
+p65\r
+I01\r
+sS'quarterNoteLength'\r
+p66\r
+I666\r
+sb.
\ No newline at end of file