started spec editor
authorMatthias Kramm <kramm@quiss.org>
Tue, 11 Aug 2009 19:14:25 +0000 (21:14 +0200)
committerMatthias Kramm <kramm@quiss.org>
Tue, 11 Aug 2009 19:14:25 +0000 (21:14 +0200)
spec/edit_spec.py [new file with mode: 0644]
spec/spec_helper.rb

diff --git a/spec/edit_spec.py b/spec/edit_spec.py
new file mode 100644 (file)
index 0000000..2ede754
--- /dev/null
@@ -0,0 +1,299 @@
+import wx
+import wx.lib.scrolledpanel as scrolled
+import os
+import re
+import sys
+import time
+import thread
+import traceback
+import math
+
+class Check:
+    def __init__(self, x,y):
+        self.x = x
+        self.y = y
+    def left(self):
+        return "pixel at (%d,%d)" % (self.x,self.y)
+    def right(self):
+        return ""
+
+class PixelColorCheck(Check):
+    def __init__(self, x,y, color):
+        Check.__init__(self,x,y)
+        self.color = color
+    def right(self):
+        return "is of color 0x%06x" % self.color
+
+class TwoPixelCheck(Check): 
+    def __init__(self, x,y, x2,y2):
+        Check.__init__(self,x,y)
+        self.x2,self.y2 = x2,y2
+    def right(self):
+        return "pixel at (%d,%d)" % (self.x2,self.y2)
+
+class PixelBrighterThan(TwoPixelCheck):
+    pass
+
+class PixelDarkerThan(TwoPixelCheck):
+    pass
+
+class PixelEqualTo(TwoPixelCheck):
+    pass
+
+class AreaCheck(Check):
+    def __init__(self, x,y, x2,y2):
+        Check.__init__(self,x,y)
+        self.x2,self.y2 = x2,y2
+    def left(self):
+        return "area at (%d,%d,%d,%d)" % (self.x,self.y,self.x2,self.y2)
+
+class AreaPlain(AreaCheck):
+    pass
+
+class AreaNotPlain(AreaCheck):
+    pass
+
+class Model:
+    def __init__(self, filename, checks):
+        self.filename = filename
+        self.checks = checks
+
+    @staticmethod
+    def load(filename):
+        path = os.path.splitext(filename)[0]+".rb"
+        fi = open(path, "rb")
+        r_file = re.compile(r"^convert_file \"([^\"]*)\"")
+        r_pixelcolor = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_of_color (0x[0-9a-fA-F]+)")
+        r_pixelbrighter = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_brighter_than pixel_at\(([0-9]+),([0-9]+)\)")
+        r_pixeldarker = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_darker_than pixel_at\(([0-9]+),([0-9]+)\)")
+        r_pixelequalto = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_the_same_as pixel_at\(([0-9]+),([0-9]+)\)")
+        r_areaplain = re.compile(r"^area_at\(([0-9]+),([0-9]+),([0-9]+),([0-9]+)\).should_be_plain_colored")
+        r_areanotplain = re.compile(r"^area_at\(([0-9]+),([0-9]+),([0-9]+),([0-9]+)\).should_not_be_plain_colored")
+        r_width = re.compile(r"^width.should be ([0-9]+)")
+        r_height = re.compile(r"^height.should be ([0-9]+)")
+        r_describe = re.compile(r"^describe \"pdf conversion\"")
+        r_header = re.compile(r"^require File.dirname")
+        r_end = re.compile(r"^end$")
+        filename = None
+        checks = []
+        for nr,line in enumerate(fi.readlines()):
+            line = line.strip()
+            if not line:
+                continue
+            m = r_file.match(line)
+            if m: 
+                if filename:
+                    raise Exception("can't load multi-file specs (in line %d)" % (nr+1))
+                filename = m.group(1);
+                continue
+            m = r_pixelcolor.match(line)
+            if m: checks += [PixelColorCheck(int(m.group(1)),int(m.group(2)),int(m.group(3),16))];continue
+            m = r_pixelbrighter.match(line)
+            if m: checks += [PixelBrighterThan(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
+            m = r_pixeldarker.match(line)
+            if m: checks += [PixelDarkerThan(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
+            m = r_pixelequalto.match(line)
+            if m: checks += [PixelEqualTo(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
+            m = r_areaplain.match(line)
+            if m: checks += [AreaPlain(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
+            m = r_areanotplain.match(line)
+            if m: checks += [AreaNotPlain(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
+            if r_width.match(line) or r_height.match(line):
+                continue # compatibility
+            if r_describe.match(line) or r_end.match(line) or r_header.match(line):
+                continue
+            print line
+            raise Exception("invalid file format: can't load this file (in line %d)" % (nr+1))
+
+        fi.close()
+        return Model(filename, checks)
+
+    def save(self):
+        path = os.path.splitext(self.filename)[0]+".rb"
+        fi = open(path, "wb")
+        fi.write("require File.dirname(__FILE__) + '/spec_helper'\n\ndescribe \"pdf conversion\" do\n")
+        fi.write("    convert_file \"%s\" do\n" % self.filename)
+        for check in self.checks:
+            c = check.__class__
+            if c == PixelColorCheck:
+                fi.write("        pixel_at(%d,%d).should_be_of_color 0x%06x\n" % (check.x,check.y,check.color))
+            elif c == PixelBrighterThan:
+                fi.write("        pixel_at(%d,%d).should_be_brighter_than pixel_at(%d,%d)\n" % (check.x,check.y,check.x2,check.y2))
+            elif c == PixelDarkerThan:
+                fi.write("        pixel_at(%d,%d).should_be_darker_than pixel_at(%d,%d)\n" % (check.x,check.y,check.x2,check.y2))
+            elif c == PixelEqualTo:
+                fi.write("        pixel_at(%d,%d).should_be_the_same_as pixel_at(%d,%d)\n" % (check.x,check.y,check.x2,check.y2))
+            elif c == AreaPlain:
+                fi.write("        area_at(%d,%d,%d,%d).should_be_plain_colored\n" % (check.x,check.y,check.x2,check.y2))
+            elif c == AreaNotPlain:
+                fi.write("        area_at(%d,%d,%d,%d).should_not_be_plain_colored\n" % (check.x,check.y,check.x2,check.y2))
+        fi.write("    end\n")
+        fi.write("end\n")
+        fi.close()
+
+
+class ImageFrame(wx.Frame):
+    def __init__(self, application, model):
+        wx.Frame.__init__(self, None, -1, style = wx.DEFAULT_FRAME_STYLE, pos=(50,50))
+        self.application = application
+        self.model = model
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
+        self.bitmap = wx.Bitmap(model.filename)
+        self.width = self.bitmap.GetWidth()
+        self.height = self.bitmap.GetHeight()
+        self.bitmap_x = 500
+        self.bitmap_y = 32
+        self.SetSize((self.width+self.bitmap_x+32, max(self.height+self.bitmap_y, 15*32)))
+        self.image = wx.ImageFromBitmap(self.bitmap)
+        self.x = 0
+        self.y = 0
+        self.createToolbar()
+
+    def createToolbar(self):
+        tsize = (16,16)
+        self.toolbar = self.CreateToolBar(wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT)
+        self.toolbar.AddSimpleTool(wx.ID_OPEN,
+                                   wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, tsize),
+                                   "Open")
+        self.toolbar.AddSimpleTool(wx.ID_SAVE,
+                                   wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR, tsize),
+                                   "Save selected pages")
+        self.toolbar.AddSimpleTool(wx.ID_PREFERENCES,
+                                   wx.ArtProvider.GetBitmap(wx.ART_LIST_VIEW, wx.ART_TOOLBAR, tsize),
+                                   "Options")
+        #self.toolbar.AddSeparator()
+        self.toolbar.Realize()
+
+    def OnMouseClick(self, event):
+        x = min(max(event.X  - self.bitmap_x, 0), self.width-1)
+        y = min(max(event.Y  - self.bitmap_y, 0), self.height-1)
+        if y not in self.model.pixels:
+            self.model.pixels[y] = {}
+        if y in self.model.pixels and x in self.model.pixels[y]:
+            del self.model.pixels[y][x]
+        else:
+            color = self.image.GetRed(x,y)<<16 | self.image.GetGreen(x,y)<<8 | self.image.GetBlue(x,y)
+            self.model.pixels[y][x] = color
+        self.Refresh()
+
+    def OnMouse(self, event):
+        if event.LeftIsDown():
+            return self.OnMouseClick(event)
+        lastx = self.x
+        lasty = self.y
+        self.x = min(max(event.X  - self.bitmap_x, 0), self.width-1)
+        self.y = min(max(event.Y  - self.bitmap_y, 0), self.height-1)
+        if lastx!=self.x or lasty!=self.y:
+            self.Refresh()
+
+    def OnPaint(self, event):
+        dc = wx.PaintDC(self)
+        self.Draw(dc)
+
+    def Draw(self,dc=None):
+        if not dc:
+            dc = wx.ClientDC(self)
+      
+        dc.SetBackground(wx.Brush((0,0,0)))
+        dc.DrawBitmap(self.bitmap, self.bitmap_x, self.bitmap_y, False)
+
+        for yy,row in self.model.pixels.items():
+            for xx in row.keys():
+                x = xx+self.bitmap_x
+                y = yy+self.bitmap_y
+                l = 0
+                for r in range(10):
+                    r = (r+1)*3.141526/5
+                    dc.DrawLine(x+10*math.sin(l), y+10*math.cos(l), x+10*math.sin(r), y+10*math.cos(r))
+                    l = r
+                dc.DrawLine(x,y,x+1,y)
+
+        color = (0,255,0)
+        for yy in range(15):
+            y = self.y+yy-8
+            marked = self.model.pixels.get(y, {})
+            for xx in range(15):
+                x = self.x+xx-8
+                if 0<=x<self.width and 0<=y<self.height:
+                    color = (self.image.GetRed(x,y), self.image.GetGreen(x,y), self.image.GetBlue(x,y))
+                else:
+                    color = (0,0,0)
+                dc.SetBrush(wx.Brush(color))
+                dc.SetPen(wx.Pen(color))
+                m = x in marked
+                dc.DrawRectangle(32*xx, 32*yy, 32, 32)
+
+                if (xx==8 and yy==8) or m:
+                    dc.SetPen(wx.Pen((0, 0, 0)))
+                    dc.DrawRectangleRect((32*xx, 32*yy, 32, 32))
+                    dc.DrawRectangleRect((32*xx+2, 32*yy+2, 28, 28))
+
+                    if (xx==8 and yy==8):
+                        dc.SetPen(wx.Pen((255, 255, 255)))
+                    else:
+                        dc.SetPen(wx.Pen((255, 255, 0)))
+                    dc.DrawRectangleRect((32*xx+1, 32*yy+1, 30, 30))
+                    #dc.SetPen(wx.Pen((0, 0, 0)))
+                    #dc.SetPen(wx.Pen(color))
+
+class EntryPanel(scrolled.ScrolledPanel):
+    def __init__(self, parent, model):
+        self.model = model
+        scrolled.ScrolledPanel.__init__(self, parent, -1)
+        vbox = wx.BoxSizer(wx.VERTICAL)
+
+        for check in model.checks:
+            hbox = wx.BoxSizer(wx.HORIZONTAL) 
+
+            desc = wx.StaticText(self, -1, check.left())
+            hbox.Add(desc, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
+           
+            if isinstance(check,AreaCheck):
+                choices = ["is plain","is not plain"]
+                lb = wx.Choice(self, -1, (100, 50), choices = choices)
+                hbox.Add(lb, 0, wx.ALIGN_LEFT|wx.ALL, 5)
+            elif isinstance(check,TwoPixelCheck):
+                choices = ["is the same as","is brighter than","is darker than"]
+                lb = wx.Choice(self, -1, (100, 50), choices = choices)
+                hbox.Add(lb, 0, wx.ALIGN_LEFT|wx.ALL, 5)
+            elif isinstance(check,PixelColorCheck):
+                # TODO: color control
+                pass
+            
+            desc = wx.StaticText(self, -1, check.right())
+            hbox.Add(desc, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
+
+            vbox.Add(hbox)
+            vbox.Add(wx.StaticLine(self, -1, size=(360,-1)), 0, wx.ALL, 5)
+            #vbox.Add((20,20))
+
+        self.SetSizer(vbox)
+        self.SetAutoLayout(1)
+        self.SetupScrolling()
+
+class MainFrame(wx.Frame):
+    def __init__(self, application, model):
+        wx.Frame.__init__(self, None, -1, style = wx.DEFAULT_FRAME_STYLE, pos=(50,50))
+        self.application = application
+        self.entries = EntryPanel(self, model)
+        self.entries.Show()
+
+
+#class ScrollFrame(wx.Frame):
+
+def getpixels(filename, p):
+    model = Model(filename, p)
+    app = wx.PySimpleApp()
+    main = MainFrame(app, model)
+    main.Show()
+    app.MainLoop()
+    return model.pixels
+
+if __name__ == "__main__":
+    model = Model.load(sys.argv[1])
+    app = wx.PySimpleApp()
+    main = MainFrame(app, model)
+    main.Show()
+    app.MainLoop()
+    model.save()
index 78a25b3..414ced8 100644 (file)
@@ -75,7 +75,7 @@ class Pixel
        gray2 = pixel.rgb.inject(0) {|sum,e| sum+e}
        gray1 > gray2 or raise PixelError.new(self,"is not brighter than",pixel)
     end
-    def should_be_less_bright_than(pixel)
+    def should_be_darker_than(pixel)
        gray1 = @rgb.inject(0) {|sum,e| sum+e}
        gray2 = pixel.rgb.inject(0) {|sum,e| sum+e}
        gray1 < gray2 or raise PixelError.new(self,"is not less bright than",pixel)