diff --git a/images/control_xy.png b/images/control_xy.png index 51c1fbd..12dcda7 100644 Binary files a/images/control_xy.png and b/images/control_xy.png differ diff --git a/images/control_z.png b/images/control_z.png index 0dc6cb7..56553d7 100644 Binary files a/images/control_z.png and b/images/control_z.png differ diff --git a/pronterface.py b/pronterface.py index 0ad53ae..38df876 100755 --- a/pronterface.py +++ b/pronterface.py @@ -82,12 +82,6 @@ class PronterWindow(wx.Frame,pronsole.pronsole): [_("Extrude"),("extrude"),(5,0),(225,200,200),(1,2)], [_("Reverse"),("reverse"),(6,0),(225,200,200),(1,2)], ] - self.absbuttons=[ - [_("Home"),("home"),(0,380),(250,250,250),(1,2)], - [_("HomeX"),("home X"),(0,100),(205,205,78),(1,2)], - [_("HomeY"),("home Y"),(270, 100),(150,150,205),(1,2)], - [_("HomeZ"),("home Z"),(270, 380),(150,205,150),(1,2)], - ] self.custombuttons=[] self.btndict={} self.parse_cmdline(sys.argv[1:]) @@ -139,6 +133,8 @@ class PronterWindow(wx.Frame,pronsole.pronsole): wx.CallAfter(self.connectbtn.Disable) for i in self.printerControls: wx.CallAfter(i.Enable) + self.xyb.enable() + self.zb.enable() if self.filename: wx.CallAfter(self.printbtn.Enable) @@ -461,22 +457,12 @@ class PronterWindow(wx.Frame,pronsole.pronsole): #lls.Add((200,375)) - xyb = XYButtons(self.panel, self.moveXY) - lls.Add(xyb, pos=(2,0), span=(1,6), flag=wx.ALIGN_CENTER) - zb = ZButtons(self.panel, self.moveZ) - lls.Add(zb, pos=(2,7), span=(1,2), flag=wx.ALIGN_CENTER) - wx.CallAfter(xyb.SetFocus) - - # Absolute-positioned Home buttons that cover the XYButtons control - for i in self.absbuttons: - btn=wx.Button(self.panel,-1,i[0],pos=i[2]) - btn.SetBackgroundColour(i[3]) - btn.SetForegroundColour("black") - btn.properties=i - btn.Bind(wx.EVT_BUTTON,self.procbutton) - self.btndict[i[1]]=btn - self.printerControls.append(btn) - + self.xyb = XYButtons(self.panel, self.moveXY, self.homeButtonClicked) + lls.Add(self.xyb, pos=(2,0), span=(1,6), flag=wx.ALIGN_CENTER) + self.zb = ZButtons(self.panel, self.moveZ) + lls.Add(self.zb, pos=(2,7), span=(1,2), flag=wx.ALIGN_CENTER) + wx.CallAfter(self.xyb.SetFocus) + for i in self.cpbuttons: btn=wx.Button(self.panel,-1,i[0])#,size=(60,-1)) btn.SetBackgroundColour(i[3]) @@ -894,6 +880,16 @@ class PronterWindow(wx.Frame,pronsole.pronsole): else: e.Skip() + def homeButtonClicked(self, corner): + if corner == 0: # upper-left + self.onecmd('home X') + if corner == 1: # upper-right + self.onecmd('home Y') + if corner == 2: # lower-right + self.onecmd('home Z') + if corner == 3: # lower-left + self.onecmd('home') + def moveXY(self, x, y): if x != 0: self.onecmd('move X %s' % x) @@ -903,10 +899,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole): def moveZ(self, z): if z != 0: self.onecmd('move Z %s' % z) - - def home(self): - self.onecmd('home') - + def procbutton(self,e): try: if hasattr(e.GetEventObject(),"custombutton"): diff --git a/xybuttons.py b/xybuttons.py index 53a5123..dcaf2b1 100644 --- a/xybuttons.py +++ b/xybuttons.py @@ -11,63 +11,57 @@ def sign(n): class XYButtons(BufferedCanvas): keypad_positions = { - 0: (126, 126), - 1: (100, 100), - 2: (80, 80), - 3: (60, 60) + 0: (105, 102), + 1: (86, 83), + 2: (68, 65), + 3: (53, 50) } - concentric_circle_radii = [15, 55, 86, 117, 142] - center = (166, 164) + corner_size = (49, 49) + corner_inset = (8, 6) + concentric_circle_radii = [11, 45, 69, 94, 115] + center = (124, 121) + spacer = 7 - def __init__(self, parent, moveCallback=None, ID=-1): + def __init__(self, parent, moveCallback=None, cornerCallback=None, ID=-1): self.bg_bmp = wx.Image(imagefile("control_xy.png"),wx.BITMAP_TYPE_PNG).ConvertToBitmap() self.keypad_bmp = wx.Image(imagefile("arrow_keys.png"),wx.BITMAP_TYPE_PNG).ConvertToBitmap() self.keypad_idx = -1 self.quadrant = None self.concentric = None + self.corner = None self.moveCallback = moveCallback + self.cornerCallback = cornerCallback + self.enabled = False BufferedCanvas.__init__(self, parent, ID) - self.SetSize(wx.Size(335, 328)) + self.SetSize(self.bg_bmp.GetSize()) # Set up mouse and keyboard event capture self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown) self.Bind(wx.EVT_MOTION, self.OnMotion) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - self.Bind(wx.EVT_KEY_UP, self.onKey) - wx.GetTopLevelParent(self).Bind(wx.EVT_CHAR_HOOK, self.onTopLevelKey) + self.Bind(wx.EVT_KEY_UP, self.OnKey) + wx.GetTopLevelParent(self).Bind(wx.EVT_CHAR_HOOK, self.OnTopLevelKey) - def onTopLevelKey(self, evt): - # Let user press escape on any control, and return focus here - if evt.GetKeyCode() == wx.WXK_ESCAPE: - self.SetFocus() - evt.Skip() - - def onKey(self, evt): - if self.keypad_idx >= 0: - if evt.GetKeyCode() == wx.WXK_TAB: - self.setKeypadIndex(self.rotateKeypadIndex()) - elif evt.GetKeyCode() == wx.WXK_UP: - self.quadrant = 1 - elif evt.GetKeyCode() == wx.WXK_DOWN: - self.quadrant = 3 - elif evt.GetKeyCode() == wx.WXK_LEFT: - self.quadrant = 2 - elif evt.GetKeyCode() == wx.WXK_RIGHT: - self.quadrant = 0 - else: - evt.Skip() - return - - if self.moveCallback: - self.concentric = self.keypad_idx - x, y = self.getMovement() - self.moveCallback(x, y) - + def disable(self): + self.enabled = False - def rotateKeypadIndex(self): + def enable(self): + self.enabled = True + + def distanceToLine(self, pos, x1, y1, x2, y2): + xlen = x2 - x1 + ylen = y2 - y1 + pxlen = x1 - pos.x + pylen = y1 - pos.y + return abs(xlen*pylen-ylen*pxlen)/math.sqrt(xlen**2+ylen**2) + + def distanceToPoint(self, x1, y1, x2, y2): + return math.sqrt((x1-x2)**2 + (y1-y2)**2) + + def cycleKeypadIndex(self): idx = self.keypad_idx + 1 if idx > 2: idx = 0 return idx @@ -75,7 +69,6 @@ class XYButtons(BufferedCanvas): def setKeypadIndex(self, idx): self.keypad_idx = idx self.update() - # self.keypad_bmp.Move(XYButtons.keypad_positions[self.keypad_idx]) def getMovement(self): xdir = [1, 0, -1, 0][self.quadrant] @@ -84,12 +77,12 @@ class XYButtons(BufferedCanvas): return (magnitude * xdir, magnitude * ydir) def lookupConcentric(self, radius): - idx = -1 - for r in XYButtons.concentric_circle_radii: + idx = 0 + for r in XYButtons.concentric_circle_radii[1:]: if radius < r: return idx idx += 1 - return None + return len(XYButtons.concentric_circle_radii) def getQuadrantConcentricFromPosition(self, pos): rel_x = pos[0] - XYButtons.center[0] @@ -109,57 +102,12 @@ class XYButtons(BufferedCanvas): def mouseOverKeypad(self, mpos): for idx, kpos in XYButtons.keypad_positions.items(): - rect = wx.Rect(kpos[0], kpos[1], self.keypad_bmp.GetWidth(), self.keypad_bmp.GetHeight()) - if rect.Contains(mpos): + radius = self.distanceToPoint(mpos[0], mpos[1], kpos[0], kpos[1]) + if radius < 9: return idx return None - - def OnMotion(self, event): - oldq, oldc = self.quadrant, self.concentric - - mpos = event.GetPosition() - idx = self.mouseOverKeypad(mpos) - self.quadrant = None - self.concentric = None - if idx == None: - center = wx.Point(XYButtons.center[0], XYButtons.center[1]) - riseDist = self.distanceToLine(mpos, center.x-1, center.y-1, center.x+1, center.y+1) - fallDist = self.distanceToLine(mpos, center.x-1, center.y+1, center.x+1, center.y-1) - if riseDist > 10 and fallDist > 10: - self.quadrant, self.concentric = self.getQuadrantConcentricFromPosition(mpos) - - if oldq != self.quadrant or oldc != self.concentric: - self.update() - - def OnLeftDown(self, event): - # Take focus when clicked so that arrow keys can control movement - self.SetFocus() - - mpos = event.GetPosition() - - idx = self.mouseOverKeypad(mpos) - if idx == None: - self.quadrant, self.concentric = self.getQuadrantConcentricFromPosition(mpos) - if self.quadrant != None and self.concentric != None: - x, y = self.getMovement() - if self.moveCallback: - self.moveCallback(x, y) - else: - if self.keypad_idx == idx: - self.setKeypadIndex(-1) - else: - self.setKeypadIndex(idx) - - def OnLeaveWindow(self, evt): - self.quadrant = None - self.concentric = None - self.update() def drawPartialPie(self, gc, center, r1, r2, angle1, angle2): - parts = 64 - angle_dist = angle2 - angle1 - angle_inc = angle_dist / parts - p1 = wx.Point(center.x + r1*math.cos(angle1), center.y + r1*math.sin(angle1)) path = gc.CreatePath() @@ -169,13 +117,6 @@ class XYButtons(BufferedCanvas): path.AddLineToPoint(p1.x, p1.y) gc.DrawPath(path) - def distanceToLine(self, pos, x1, y1, x2, y2): - xlen = x2 - x1 - ylen = y2 - y1 - pxlen = x1 - pos.x - pylen = y1 - pos.y - return abs(xlen*pylen-ylen*pxlen)/math.sqrt(xlen**2+ylen**2) - def highlightQuadrant(self, gc, quadrant, concentric): assert(quadrant >= 0 and quadrant <= 3) assert(concentric >= 0 and concentric <= 3) @@ -201,22 +142,170 @@ class XYButtons(BufferedCanvas): r2 = XYButtons.concentric_circle_radii[concentric+1] self.drawPartialPie(gc, center, r1-inner_ring_radius, r2-inner_ring_radius, a1+fudge, a2-fudge) + + def drawCorner(self, gc, x, y, angle=0.0): + w, h = XYButtons.corner_size + + gc.PushState() + gc.Translate(x, y) + gc.Rotate(angle) + path = gc.CreatePath() + path.MoveToPoint(-w/2, -h/2) + path.AddLineToPoint(w/2, -h/2) + path.AddLineToPoint(w/2, -h/2+h/3) + path.AddLineToPoint(-w/2+w/3, h/2) + path.AddLineToPoint(-w/2, h/2) + path.AddLineToPoint(-w/2, -h/2) + gc.DrawPath(path) + gc.PopState() + + def highlightCorner(self, gc, corner=0): + w, h = XYButtons.corner_size + cx, cy = XYButtons.center + ww, wh = self.GetSizeTuple() + + inset = 10 + if corner == 0: + x, y = (cx - ww/2 + inset, cy - wh/2 + inset) + self.drawCorner(gc, x+w/2, y+h/2, 0) + elif corner == 1: + x, y = (cx + ww/2 - inset, cy - wh/2 + inset) + self.drawCorner(gc, x-w/2, y+h/2, math.pi/2) + elif corner == 2: + x, y = (cx + ww/2 - inset, cy + wh/2 - inset) + self.drawCorner(gc, x-w/2, y-h/2, math.pi) + elif corner == 3: + x, y = (cx - ww/2 + inset, cy + wh/2 - inset) + self.drawCorner(gc, x+w/2, y-h/2, math.pi*3/2) + def draw(self, dc, w, h): dc.Clear() gc = wx.GraphicsContext.Create(dc) center = wx.Point(XYButtons.center[0], XYButtons.center[1]) - - gc.DrawBitmap(self.bg_bmp, 0, 0, self.bg_bmp.GetWidth(), self.bg_bmp.GetHeight()) - - if self.quadrant != None and self.concentric != None: + w, h = (self.bg_bmp.GetWidth(), self.bg_bmp.GetHeight()) + gc.DrawBitmap(self.bg_bmp, 0, 0, w, h) + + if self.enabled: + # Brush and pen for grey overlay when mouse hovers over gc.SetPen(wx.Pen(wx.Colour(100,100,100,172), 4)) gc.SetBrush(wx.Brush(wx.Colour(0,0,0,128))) - self.highlightQuadrant(gc, self.quadrant, self.concentric) - + + if self.concentric != None: + if self.concentric < len(XYButtons.concentric_circle_radii): + if self.quadrant != None: + self.highlightQuadrant(gc, self.quadrant, self.concentric) + elif self.corner != None: + self.highlightCorner(gc, self.corner) + + if self.keypad_idx >= 0: + pos = XYButtons.keypad_positions[self.keypad_idx] + pos = (pos[0] - w/2 - 3, pos[1] - h/2 - 3) + gc.DrawBitmap(self.keypad_bmp, pos[0], pos[1], w, h) + else: + gc.SetPen(wx.Pen(wx.Colour(255,255,255,0), 4)) + gc.SetBrush(wx.Brush(wx.Colour(255,255,255,128))) + gc.DrawRectangle(0, 0, w, h) + + # Used to check exact position of keypad dots, should we ever resize the bg image + # for idx, kpos in XYButtons.keypad_positions.items(): + # dc.DrawCircle(kpos[0], kpos[1], 6) + + ## ------ ## + ## Events ## + ## ------ ## + + def OnTopLevelKey(self, evt): + # Let user press escape on any control, and return focus here + if evt.GetKeyCode() == wx.WXK_ESCAPE: + self.SetFocus() + evt.Skip() + + def OnKey(self, evt): + if not self.enabled: + return if self.keypad_idx >= 0: - pos = XYButtons.keypad_positions[self.keypad_idx] - gc.DrawBitmap(self.keypad_bmp, pos[0], pos[1], self.keypad_bmp.GetWidth(), self.keypad_bmp.GetHeight()) + if evt.GetKeyCode() == wx.WXK_TAB: + self.setKeypadIndex(self.cycleKeypadIndex()) + elif evt.GetKeyCode() == wx.WXK_UP: + self.quadrant = 1 + elif evt.GetKeyCode() == wx.WXK_DOWN: + self.quadrant = 3 + elif evt.GetKeyCode() == wx.WXK_LEFT: + self.quadrant = 2 + elif evt.GetKeyCode() == wx.WXK_RIGHT: + self.quadrant = 0 + else: + evt.Skip() + return + + if self.moveCallback: + self.concentric = self.keypad_idx + x, y = self.getMovement() + self.moveCallback(x, y) + + def OnMotion(self, event): + if not self.enabled: + return - return True + oldq, oldc = self.quadrant, self.concentric + + mpos = event.GetPosition() + idx = self.mouseOverKeypad(mpos) + self.quadrant = None + self.concentric = None + if idx == None: + center = wx.Point(XYButtons.center[0], XYButtons.center[1]) + riseDist = self.distanceToLine(mpos, center.x-1, center.y-1, center.x+1, center.y+1) + fallDist = self.distanceToLine(mpos, center.x-1, center.y+1, center.x+1, center.y-1) + self.quadrant, self.concentric = self.getQuadrantConcentricFromPosition(mpos) + + # If mouse hovers in space between quadrants, don't commit to a quadrant + if riseDist <= XYButtons.spacer or fallDist <= XYButtons.spacer: + self.quadrant = None + + cx, cy = XYButtons.center + if mpos.x < cx and mpos.y < cy: + self.corner = 0 + if mpos.x >= cx and mpos.y < cy: + self.corner = 1 + if mpos.x >= cx and mpos.y >= cy: + self.corner = 2 + if mpos.x < cx and mpos.y >= cy: + self.corner = 3 + + if oldq != self.quadrant or oldc != self.concentric: + self.update() + + def OnLeftDown(self, event): + if not self.enabled: + return + + # Take focus when clicked so that arrow keys can control movement + self.SetFocus() + + mpos = event.GetPosition() + + idx = self.mouseOverKeypad(mpos) + if idx == None: + self.quadrant, self.concentric = self.getQuadrantConcentricFromPosition(mpos) + if self.concentric != None: + if self.concentric < len(XYButtons.concentric_circle_radii): + if self.quadrant != None: + x, y = self.getMovement() + if self.moveCallback: + self.moveCallback(x, y) + elif self.corner != None: + if self.cornerCallback: + self.cornerCallback(self.corner) + else: + if self.keypad_idx == idx: + self.setKeypadIndex(-1) + else: + self.setKeypadIndex(idx) + + def OnLeaveWindow(self, evt): + self.quadrant = None + self.concentric = None + self.update() \ No newline at end of file diff --git a/zbuttons.py b/zbuttons.py index fa4ac42..39e2181 100644 --- a/zbuttons.py +++ b/zbuttons.py @@ -10,8 +10,8 @@ def sign(n): else: return 0 class ZButtons(BufferedCanvas): - button_ydistances = [8, 30, 56, 84, 118] - center = (32, 146) + button_ydistances = [7, 30, 55, 83, 112] + center = (30, 118) def __init__(self, parent, moveCallback=None, ID=-1): self.bg_bmp = wx.Image(imagefile("control_z.png"),wx.BITMAP_TYPE_PNG).ConvertToBitmap() @@ -19,10 +19,11 @@ class ZButtons(BufferedCanvas): self.direction = None self.orderOfMagnitudeIdx = 0 # 0 means '1', 1 means '10', 2 means '100', etc. self.moveCallback = moveCallback + self.enabled = False BufferedCanvas.__init__(self, parent, ID) - self.SetSize(wx.Size(87, 295)) + self.SetSize(wx.Size(59, 244)) # Set up mouse and keyboard event capture self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) @@ -30,6 +31,12 @@ class ZButtons(BufferedCanvas): self.Bind(wx.EVT_MOTION, self.OnMotion) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) + def disable(self): + self.enabled = False + + def enable(self): + self.enabled = True + def lookupRange(self, ydist): idx = -1 for d in ZButtons.button_ydistances: @@ -44,7 +51,7 @@ class ZButtons(BufferedCanvas): fudge = 11 x = 0 + fudge - w = 72 - fudge*2 + w = 59 - fudge*2 if rng >= 0: k = 1 if dir > 0 else 0 y = ZButtons.center[1] - (dir * ZButtons.button_ydistances[rng+k]) @@ -57,7 +64,32 @@ class ZButtons(BufferedCanvas): ydelta = ZButtons.center[1] - pos[1] return (self.lookupRange(abs(ydelta)), sign(ydelta)) + def draw(self, dc, w, h): + dc.Clear() + gc = wx.GraphicsContext.Create(dc) + w, h = (self.bg_bmp.GetWidth(), self.bg_bmp.GetHeight()) + + gc.DrawBitmap(self.bg_bmp, 0, 0, w, h) + + if self.enabled: + gc.SetPen(wx.Pen(wx.Colour(100,100,100,172), 4)) + gc.SetBrush(wx.Brush(wx.Colour(0,0,0,128))) + + if self.range != None and self.direction != None: + self.highlight(gc, self.range, self.direction) + else: + gc.SetPen(wx.Pen(wx.Colour(255,255,255,0), 4)) + gc.SetBrush(wx.Brush(wx.Colour(255,255,255,128))) + gc.DrawRectangle(0, 0, w, h) + + ## ------ ## + ## Events ## + ## ------ ## + def OnMotion(self, event): + if not self.enabled: + return + oldr, oldd = self.range, self.direction mpos = event.GetPosition() @@ -67,6 +99,9 @@ class ZButtons(BufferedCanvas): self.update() def OnLeftDown(self, event): + if not self.enabled: + return + mpos = event.GetPosition() r, d = self.getRangeDir(mpos) if r >= 0: @@ -78,17 +113,3 @@ class ZButtons(BufferedCanvas): self.range = None self.direction = None self.update() - - def draw(self, dc, w, h): - dc.Clear() - gc = wx.GraphicsContext.Create(dc) - - gc.SetPen(wx.Pen(wx.Colour(100,100,100,172), 4)) - gc.SetBrush(wx.Brush(wx.Colour(0,0,0,128))) - - gc.DrawBitmap(self.bg_bmp, 0, 0, self.bg_bmp.GetWidth(), self.bg_bmp.GetHeight()) - - if self.range != None and self.direction != None: - self.highlight(gc, self.range, self.direction) - - return True