2 import wx.lib.scrolledpanel as scrolled
12 def __init__(self, x,y):
16 return "pixel at (%d,%d)" % (self.x,self.y)
20 class PixelColorCheck(Check):
21 def __init__(self, x,y, color):
22 Check.__init__(self,x,y)
25 return "is of color 0x%06x" % self.color
27 class TwoPixelCheck(Check):
28 def __init__(self, x,y, x2,y2):
29 Check.__init__(self,x,y)
30 self.x2,self.y2 = x2,y2
32 return "pixel at (%d,%d)" % (self.x2,self.y2)
34 class PixelBrighterThan(TwoPixelCheck):
37 class PixelDarkerThan(TwoPixelCheck):
40 class PixelEqualTo(TwoPixelCheck):
43 class AreaCheck(Check):
44 def __init__(self, x,y, x2,y2):
45 Check.__init__(self,x,y)
46 self.x2,self.y2 = x2,y2
48 return "area at (%d,%d,%d,%d)" % (self.x,self.y,self.x2,self.y2)
50 class AreaPlain(AreaCheck):
53 class AreaNotPlain(AreaCheck):
57 def __init__(self, filename, checks):
58 self.filename = filename
63 path = os.path.splitext(filename)[0]+".rb"
65 r_file = re.compile(r"^convert_file \"([^\"]*)\"")
66 r_pixelcolor = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_of_color (0x[0-9a-fA-F]+)")
67 r_pixelbrighter = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_brighter_than pixel_at\(([0-9]+),([0-9]+)\)")
68 r_pixeldarker = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_darker_than pixel_at\(([0-9]+),([0-9]+)\)")
69 r_pixelequalto = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_the_same_as pixel_at\(([0-9]+),([0-9]+)\)")
70 r_areaplain = re.compile(r"^area_at\(([0-9]+),([0-9]+),([0-9]+),([0-9]+)\).should_be_plain_colored")
71 r_areanotplain = re.compile(r"^area_at\(([0-9]+),([0-9]+),([0-9]+),([0-9]+)\).should_not_be_plain_colored")
72 r_width = re.compile(r"^width.should be ([0-9]+)")
73 r_height = re.compile(r"^height.should be ([0-9]+)")
74 r_describe = re.compile(r"^describe \"pdf conversion\"")
75 r_header = re.compile(r"^require File.dirname")
76 r_end = re.compile(r"^end$")
79 for nr,line in enumerate(fi.readlines()):
83 m = r_file.match(line)
86 raise Exception("can't load multi-file specs (in line %d)" % (nr+1))
87 filename = m.group(1);
89 m = r_pixelcolor.match(line)
90 if m: checks += [PixelColorCheck(int(m.group(1)),int(m.group(2)),int(m.group(3),16))];continue
91 m = r_pixelbrighter.match(line)
92 if m: checks += [PixelBrighterThan(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
93 m = r_pixeldarker.match(line)
94 if m: checks += [PixelDarkerThan(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
95 m = r_pixelequalto.match(line)
96 if m: checks += [PixelEqualTo(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
97 m = r_areaplain.match(line)
98 if m: checks += [AreaPlain(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
99 m = r_areanotplain.match(line)
100 if m: checks += [AreaNotPlain(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)))];continue
101 if r_width.match(line) or r_height.match(line):
102 continue # compatibility
103 if r_describe.match(line) or r_end.match(line) or r_header.match(line):
106 raise Exception("invalid file format: can't load this file (in line %d)" % (nr+1))
109 return Model(filename, checks)
112 path = os.path.splitext(self.filename)[0]+".rb"
113 fi = open(path, "wb")
114 fi.write("require File.dirname(__FILE__) + '/spec_helper'\n\ndescribe \"pdf conversion\" do\n")
115 fi.write(" convert_file \"%s\" do\n" % self.filename)
116 for check in self.checks:
118 if c == PixelColorCheck:
119 fi.write(" pixel_at(%d,%d).should_be_of_color 0x%06x\n" % (check.x,check.y,check.color))
120 elif c == PixelBrighterThan:
121 fi.write(" pixel_at(%d,%d).should_be_brighter_than pixel_at(%d,%d)\n" % (check.x,check.y,check.x2,check.y2))
122 elif c == PixelDarkerThan:
123 fi.write(" pixel_at(%d,%d).should_be_darker_than pixel_at(%d,%d)\n" % (check.x,check.y,check.x2,check.y2))
124 elif c == PixelEqualTo:
125 fi.write(" pixel_at(%d,%d).should_be_the_same_as pixel_at(%d,%d)\n" % (check.x,check.y,check.x2,check.y2))
127 fi.write(" area_at(%d,%d,%d,%d).should_be_plain_colored\n" % (check.x,check.y,check.x2,check.y2))
128 elif c == AreaNotPlain:
129 fi.write(" area_at(%d,%d,%d,%d).should_not_be_plain_colored\n" % (check.x,check.y,check.x2,check.y2))
135 class ImageFrame(wx.Frame):
136 def __init__(self, application, model):
137 wx.Frame.__init__(self, None, -1, style = wx.DEFAULT_FRAME_STYLE, pos=(50,50))
138 self.application = application
140 self.Bind(wx.EVT_PAINT, self.OnPaint)
141 self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
142 self.bitmap = wx.Bitmap(model.filename)
143 self.width = self.bitmap.GetWidth()
144 self.height = self.bitmap.GetHeight()
147 self.SetSize((self.width+self.bitmap_x+32, max(self.height+self.bitmap_y, 15*32)))
148 self.image = wx.ImageFromBitmap(self.bitmap)
153 def createToolbar(self):
155 self.toolbar = self.CreateToolBar(wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT)
156 self.toolbar.AddSimpleTool(wx.ID_OPEN,
157 wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, tsize),
159 self.toolbar.AddSimpleTool(wx.ID_SAVE,
160 wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR, tsize),
161 "Save selected pages")
162 self.toolbar.AddSimpleTool(wx.ID_PREFERENCES,
163 wx.ArtProvider.GetBitmap(wx.ART_LIST_VIEW, wx.ART_TOOLBAR, tsize),
165 #self.toolbar.AddSeparator()
166 self.toolbar.Realize()
168 def OnMouseClick(self, event):
169 x = min(max(event.X - self.bitmap_x, 0), self.width-1)
170 y = min(max(event.Y - self.bitmap_y, 0), self.height-1)
171 if y not in self.model.pixels:
172 self.model.pixels[y] = {}
173 if y in self.model.pixels and x in self.model.pixels[y]:
174 del self.model.pixels[y][x]
176 color = self.image.GetRed(x,y)<<16 | self.image.GetGreen(x,y)<<8 | self.image.GetBlue(x,y)
177 self.model.pixels[y][x] = color
180 def OnMouse(self, event):
181 if event.LeftIsDown():
182 return self.OnMouseClick(event)
185 self.x = min(max(event.X - self.bitmap_x, 0), self.width-1)
186 self.y = min(max(event.Y - self.bitmap_y, 0), self.height-1)
187 if lastx!=self.x or lasty!=self.y:
190 def OnPaint(self, event):
191 dc = wx.PaintDC(self)
194 def Draw(self,dc=None):
196 dc = wx.ClientDC(self)
198 dc.SetBackground(wx.Brush((0,0,0)))
199 dc.DrawBitmap(self.bitmap, self.bitmap_x, self.bitmap_y, False)
201 for yy,row in self.model.pixels.items():
202 for xx in row.keys():
208 dc.DrawLine(x+10*math.sin(l), y+10*math.cos(l), x+10*math.sin(r), y+10*math.cos(r))
210 dc.DrawLine(x,y,x+1,y)
215 marked = self.model.pixels.get(y, {})
218 if 0<=x<self.width and 0<=y<self.height:
219 color = (self.image.GetRed(x,y), self.image.GetGreen(x,y), self.image.GetBlue(x,y))
222 dc.SetBrush(wx.Brush(color))
223 dc.SetPen(wx.Pen(color))
225 dc.DrawRectangle(32*xx, 32*yy, 32, 32)
227 if (xx==8 and yy==8) or m:
228 dc.SetPen(wx.Pen((0, 0, 0)))
229 dc.DrawRectangleRect((32*xx, 32*yy, 32, 32))
230 dc.DrawRectangleRect((32*xx+2, 32*yy+2, 28, 28))
232 if (xx==8 and yy==8):
233 dc.SetPen(wx.Pen((255, 255, 255)))
235 dc.SetPen(wx.Pen((255, 255, 0)))
236 dc.DrawRectangleRect((32*xx+1, 32*yy+1, 30, 30))
237 #dc.SetPen(wx.Pen((0, 0, 0)))
238 #dc.SetPen(wx.Pen(color))
240 class EntryPanel(scrolled.ScrolledPanel):
241 def __init__(self, parent, model):
243 scrolled.ScrolledPanel.__init__(self, parent, -1)
244 vbox = wx.BoxSizer(wx.VERTICAL)
246 for check in model.checks:
247 hbox = wx.BoxSizer(wx.HORIZONTAL)
249 desc = wx.StaticText(self, -1, check.left())
250 hbox.Add(desc, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
252 if isinstance(check,AreaCheck):
253 choices = ["is plain","is not plain"]
254 lb = wx.Choice(self, -1, (100, 50), choices = choices)
255 hbox.Add(lb, 0, wx.ALIGN_LEFT|wx.ALL, 5)
256 elif isinstance(check,TwoPixelCheck):
257 choices = ["is the same as","is brighter than","is darker than"]
258 lb = wx.Choice(self, -1, (100, 50), choices = choices)
259 hbox.Add(lb, 0, wx.ALIGN_LEFT|wx.ALL, 5)
260 elif isinstance(check,PixelColorCheck):
261 # TODO: color control
264 desc = wx.StaticText(self, -1, check.right())
265 hbox.Add(desc, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
268 vbox.Add(wx.StaticLine(self, -1, size=(360,-1)), 0, wx.ALL, 5)
272 self.SetAutoLayout(1)
273 self.SetupScrolling()
275 class MainFrame(wx.Frame):
276 def __init__(self, application, model):
277 wx.Frame.__init__(self, None, -1, style = wx.DEFAULT_FRAME_STYLE, pos=(50,50))
278 self.application = application
279 self.entries = EntryPanel(self, model)
283 #class ScrollFrame(wx.Frame):
285 def getpixels(filename, p):
286 model = Model(filename, p)
287 app = wx.PySimpleApp()
288 main = MainFrame(app, model)
293 if __name__ == "__main__":
294 model = Model.load(sys.argv[1])
295 app = wx.PySimpleApp()
296 main = MainFrame(app, model)