I'm trying to make program in wxPython, that will draw a line in a position in which I clicked on a window but it doesn't work and I actually don't know why. How could I write this, that it will work?
import wx
global coord
coord = (30, 30)
class MyFrame(wx.Frame):
"""create a color frame, inherits from wx.Frame"""
global coord
def __init__(self, parent):
# -1 is the default ID
wx.Frame.__init__(self, parent, -1, "Click for mouse position", size=(400,300),
style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.SetBackgroundColour('Goldenrod')
self.SetCursor(wx.StockCursor(wx.CURSOR_PENCIL))
# hook some mouse events
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnLeftDown(self, event):
global coord
"""left mouse button is pressed"""
pt = event.GetPosition() # position tuple
print pt
coord = pt
self.SetTitle('LeftMouse = ' + str(pt))
def OnRightDown(self, event):
global coord
"""right mouse button is pressed"""
pt = event.GetPosition()
coord = pt
print pt
self.SetTitle('RightMouse = ' + str(pt))
def OnPaint(self, event):
global coord
dc = wx.PaintDC(self)
dc.Clear()
dc.SetPen(wx.Pen(wx.BLACK, 4))
dc.DrawLine(0, 0, int(str(coord[0])), int(str(coord[1])))
app = wx.PySimpleApp()
frame = MyFrame(None)
frame.Show(True)
app.MainLoop()
Call
self.Refresh()
at the end of OnLeftDown/OnRightDown. Otherwise the system does not know it should redraw so it won't redraw...
(also, generally speaking, it may be better to have coord as a member variable, not a global)
Related
I'm trying to make something of a Microsoft Paint-esque app using wxPython.
Currently, the app 'draws' onto the screen with a circular brush during a mouse left-down event. This is great, and the desired behavior. But I also need a circle of the same radius to 'follow' the mouse, without it drawing persistently onto wx.PaintDC.
That is, a circle of some radius follows the mouse around the screen, but only when the left mouse button is held, should the circle be 'permanently' drawn onto the buffer.
The approaches I've taken either (1) have a circle following the mouse around, but draw onto the PaintDC instance regardless of mouse-down, (2) follow the mouse around but never draw persistently onto the PaintDC instance, or (3) do not follow the mouse around, but appear and are drawn persistently on left-mouse down (see the example below).
Thank you!
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
self.draw = False
self.radius = 50
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_MOTION, self.Draw)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
def OnPaint(self, event):
dc = wx.PaintDC(self)
def Draw(self, event):
if self.draw == True:
x = event.GetX()
y = event.GetY()
dc = wx.ClientDC(self)
pen = wx.Pen(wx.Colour(192,192,192,128), 2)
brush = wx.Brush(wx.Colour(192,192,192,128))
dc.SetPen(pen)
dc.SetBrush(brush)
dc.DrawCircle(x,y,self.radius)
def OnLeftDown(self, event):
self.draw = True
def OnLeftUp(self, event):
self.draw = False
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Test",style=wx.DEFAULT_FRAME_STYLE,size=wx.Size(400, 300))
self.main_panel = MyPanel(self)
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
There is the wx.Overlay class that does a pretty good job assisting with drawing temporary things on top of more permanent stuff. See the Overlay sample in the demo: https://github.com/wxWidgets/Phoenix/blob/master/demo/Overlay.py
I'm very new to wxPython and working on a desktop screenshot utility. In short, user selects a rectangular region on their screen from and produce an image from it as most screenshot tools already do. Its all working except for the fact that I cannot get the main frame, and all it contains, to hide or close, so the screenshot image produced contains the overlay mask and dashed selection box.
FYI, screenshots are triggered by pressing the ENTER key. I have tried a vast number of ways to achieve this without luck. Hiding the frame, or destroying the panel first etc. makes no difference. However, if I break things up to two steps with button to proceed it all works fine so I am betting it's something small. I tried Refresh on the frame and panel as well. For what it's worth, my desktop environment if Linux KDE (latest ArchLinux rolling release).
Here is my code in full.
#!/usr/bin/env python2
import wx, time
#--------------------------------------------------------------------------------------------------------#
class SelectableFrame(wx.Frame):
c1 = None
c2 = None
def __init__(self, parent=None, id=-1, title=""):
wx.Frame.__init__(self, parent, id, title, pos=(0, 0), size=wx.DisplaySize(), style=wx.FRAME_NO_TASKBAR | wx.NO_BORDER | wx.STAY_ON_TOP)
self.panel = wx.Panel(self, size=self.GetSize())
self.panel.Bind(wx.EVT_MOTION, self.OnMouseMove)
self.panel.Bind(wx.EVT_LEFT_DOWN, self.OnMouseSelect)
self.panel.Bind(wx.EVT_RIGHT_DOWN, self.OnReset)
self.panel.Bind(wx.EVT_LEFT_UP, self.OnMouseUp)
self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
self.panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
self.panel.SetBackgroundColour(wx.Colour(0, 0, 0))
self.panel.SetBackgroundStyle(wx.BG_STYLE_COLOUR)
self.SetTransparent(150)
def OnClose(self, event):
self.Destroy()
def OnMouseMove(self, event):
if event.Dragging() and event.LeftIsDown():
self.c2 = event.GetPosition()
self.Refresh()
def OnMouseSelect(self, event):
self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
self.c1 = event.GetPosition()
def OnMouseUp(self, event):
self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
def OnKeyDown(self, event):
key = event.GetKeyCode()
if key == wx.WXK_ESCAPE:
self.Close()
elif (key == wx.WXK_RETURN or key == wx.WXK_NUMPAD_ENTER) and self.RegionSelected():
self.TakeScreenshot()
event.Skip()
def OnReset(self, event=None):
wx.PaintDC(self.panel).Clear()
self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
def OnPaint(self, event):
if not self.RegionSelected():
return
dc = wx.PaintDC(self.panel)
dc.SetPen(wx.Pen("white", 1, wx.LONG_DASH))
dc.SetBrush(wx.Brush(wx.Colour(100, 100, 100), wx.SOLID))
dc.DrawRectangle(self.c1.x, self.c1.y, self.c2.x - self.c1.x, self.c2.y - self.c1.y)
def RegionSelected(self):
if self.c1 is None or self.c2 is None:
return False
else:
return True
def TakeScreenshot(self):
if not self.RegionSelected():
return
wx.PaintDC(self.panel).Clear()
self.Hide()
self.Refresh()
dcScreen = wx.ScreenDC()
bmp = wx.EmptyBitmap(self.c2.x - self.c1.x, self.c2.y - self.c1.y)
memDC = wx.MemoryDC()
memDC.SelectObject(bmp)
memDC.Blit(0, 0, self.c2.x - self.c1.x, self.c2.y - self.c1.y, dcScreen, self.c1.x, self.c1.y)
memDC.SelectObject(wx.NullBitmap)
img = bmp.ConvertToImage()
filename = "/tmp/ocr-screenshot-" + time.strftime('%Y%m%d-%H%M%S') + ".png"
img.SaveFile(filename, wx.BITMAP_TYPE_PNG)
self.Close()
#--------------------------------------------------------------------------------------------------------
class SelectableApp(wx.App):
def OnInit(self):
self.frame = SelectableFrame()
self.frame.Show(True)
self.SetTopWindow(self.frame)
return True
#--------------------------------------------------------------------------------------------------------#
app = SelectableApp(False)
app.MainLoop()
I'm facing a problem with python.
i want to draw on my monitor a circle, that can move around.
let's say i have my browser open, i want to be able to make the circle go around on his own AND be able to use the mouse to press any button i want.
the idea is that the circle is connected to my hands movement thanks to Leap Motion, and i want to display the gestures i make while being able to use the mouse.
my worries are that i have to make a trasparent window which doesn't let me to use the mouse because i would clik on the trasparent window.
Thanks!
Found the solution, i used wxPython
Down below the code:
import wx, inspect, os, sys, win32gui
IMAGE_PATH = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + '\cerchio.png'
class Cursor(wx.Frame):
def __init__(self, parent, log):
self.log = log
self.delta = wx.Point(0,0)
wx.Frame.__init__(self, parent, -1, "Shaped Window",
style =
wx.FRAME_SHAPED
| wx.SIMPLE_BORDER
| wx.FRAME_NO_TASKBAR
| wx.STAY_ON_TOP
)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.update, self.timer)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.x=0
self.y=0
self.hasShape = False
self.SetClientSize( (50, 50) )
image = wx.Image(IMAGE_PATH, wx.BITMAP_TYPE_PNG)
image.SetMaskColour(255,255,255)
image.SetMask(True)
self.bmp = wx.BitmapFromImage(image)
self.SetWindowShape()
self.timer.Start(1)
dc = wx.ClientDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
def OnExit(self, evt):
self.Close()
def SetWindowShape(self, *evt):
# Use the bitmap's mask to determine the region
r = wx.RegionFromBitmap(self.bmp)
self.hasShape = self.SetShape(r)
def OnPaint(self, evt):
dc = wx.PaintDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
def OnExit(self, evt):
self.Close()
def update(self, event):
#self.x, self.y = win32gui.GetCursorPos()
self.SetPosition((self.x,self.y))
if __name__ == '__main__':
app = wx.App( False )
frm = Cursor(None, -1)
frm.Show()
app.MainLoop()
This maps 50x50 png image with white background (modify IMAGE_PATH) to the cursor position (you won't be able to click though)
I have been working on this project for some time now - it was originally supposed to be a test to see if, using wxPython, I could build a button 'from scratch.' From scratch means: that i would have full control over all the aspects of the button (i.e. controlling the BMP's that are displayed... what the event handlers did... etc.)
I have run into several problems (as this is my first major python project.) Now, when the all the code is working for the life of me I can't get an image to display.
Basic code - not working
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(wx.Bitmap("/home/wallter/Desktop/Mouseover.bmp"), 100, 100)
self.Refresh()
self.Update()
Full Main.py
import wx
from Custom_Button import Custom_Button
from wxPython.wx import *
ID_ABOUT = 101
ID_EXIT = 102
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wxFrame.__init__(self, parent, ID, title,
wxDefaultPosition, wxSize(400, 400))
self.CreateStatusBar()
self.SetStatusText("Program testing custom button overlays")
menu = wxMenu()
menu.Append(ID_ABOUT, "&About", "More information about this program")
menu.AppendSeparator()
menu.Append(ID_EXIT, "E&xit", "Terminate the program")
menuBar = wxMenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
# The call for the 'Experiential button'
self.Button1 = Custom_Button(parent, -1,
wx.Point(100, 100),
wx.Bitmap("/home/wallter/Desktop/Mouseover.bmp"),
wx.Bitmap("/home/wallter/Desktop/Normal.bmp"),
wx.Bitmap("/home/wallter/Desktop/Click.bmp"))
# The following three lines of code are in place to try to get the
# Button1 to display (trying to trigger the Paint event (the _onPaint.)
# Because that is where the 'draw' functions are.
self.Button1.Show(true)
self.Refresh()
self.Update()
# Because the Above three lines of code did not work, I added the
# following four lines to trigger the 'draw' functions to test if the
# '_onPaint' method actually worked.
# These lines do not work.
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.DrawBitmap(wx.Bitmap("/home/wallter/Desktop/Mouseover.bmp"), 100, 100)
EVT_MENU(self, ID_ABOUT, self.OnAbout)
EVT_MENU(self, ID_EXIT, self.TimeToQuit)
def OnAbout(self, event):
dlg = wxMessageDialog(self, "Testing the functions of custom "
"buttons using pyDev and wxPython",
"About", wxOK | wxICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def TimeToQuit(self, event):
self.Close(true)
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(NULL, -1, "wxPython | Buttons")
frame.Show(true)
self.SetTopWindow(frame)
return true
app = MyApp(0)
app.MainLoop()
Full CustomButton.py
import wx
from wxPython.wx import *
class Custom_Button(wx.PyControl):
def __init__(self, parent, id, Pos, Over_BMP, Norm_BMP, Push_BMP, **kwargs):
wx.PyControl.__init__(self,parent, id, **kwargs)
self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
self.Bind(wx.EVT_LEFT_UP, self._onMouseUp)
self.Bind(wx.EVT_LEAVE_WINDOW, self._onMouseLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self._onMouseEnter)
self.Bind(wx.EVT_ERASE_BACKGROUND,self._onEraseBackground)
self.Bind(wx.EVT_PAINT,self._onPaint)
self.pos = Pos
self.Over_bmp = Over_BMP
self.Norm_bmp = Norm_BMP
self.Push_bmp = Push_BMP
self._mouseIn = False
self._mouseDown = False
def _onMouseEnter(self, event):
self._mouseIn = True
def _onMouseLeave(self, event):
self._mouseIn = False
def _onMouseDown(self, event):
self._mouseDown = True
def _onMouseUp(self, event):
self._mouseDown = False
self.sendButtonEvent()
def sendButtonEvent(self):
event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
event.SetInt(0)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
def _onEraseBackground(self,event):
# reduce flicker
pass
def Iz(self):
dc = wx.BufferedPaintDC(self)
dc.DrawBitmap(self.Norm_bmp, 100, 100)
def _onPaint(self, event):
# The printing functions, they should work... but don't.
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(self.Norm_bmp)
# This never printed... I don't know if that means if the EVT
# is triggering or what.
print '_onPaint'
# draw whatever you want to draw
# draw glossy bitmaps e.g. dc.DrawBitmap
if self._mouseIn: # If the Mouse is over the button
dc.DrawBitmap(self.Over_bmp, self.pos)
else: # Since the mouse isn't over it Print the normal one
# This is adding on the above code to draw the bmp
# in an attempt to get the bmp to display; to no avail.
dc.DrawBitmap(self.Norm_bmp, self.pos)
if self._mouseDown: # If the Mouse clicks the button
dc.DrawBitmap(self.Push_bmp, self.pos)
This code won't work? I get no BMP displayed why? How do i get one? I've gotten the staticBitmap(...) to display one, but it won't move, resize, or anything for that matter... - it's only in the top left corner of the frame?
Note: the frame is 400pxl X 400pxl - and the "/home/wallter/Desktop/Mouseover.bmp"
Are your sure you code is working without exceptions because when I run it i get many errors, read the points below and you should have a button which at least draws correctly
When O run it it gives error because Custom_Button is passed NULL parent instead pass frame e.g. Custom_Button(self, ...)
Your drawBitmap call is also wrong, it throws exception, instead of dc.DrawBitmap(self.Norm_bmp) it should be dc.DrawBitmap(self.Norm_bmp, 0, 0)
dc.DrawBitmap(self.Over_bmp, self.pos) also throws error as pos should be x,y not a tuple so instead do dc.DrawBitmap(self.Over_bmp, *self.pos)
and lastly you do not need to do "from wxPython.wx import *" instead just do "from wx import *" and instead of wxXXX class names use wx.XXX, instead of true use True etc
here is my working code
from wx import *
ID_ABOUT = 101
ID_EXIT = 102
class Custom_Button(wx.PyControl):
def __init__(self, parent, id, Pos, Over_BMP, Norm_BMP, Push_BMP, **kwargs):
wx.PyControl.__init__(self,parent, id, **kwargs)
self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
self.Bind(wx.EVT_LEFT_UP, self._onMouseUp)
self.Bind(wx.EVT_LEAVE_WINDOW, self._onMouseLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self._onMouseEnter)
self.Bind(wx.EVT_ERASE_BACKGROUND,self._onEraseBackground)
self.Bind(wx.EVT_PAINT,self._onPaint)
self.pos = Pos
self.Over_bmp = Over_BMP
self.Norm_bmp = Norm_BMP
self.Push_bmp = Push_BMP
self._mouseIn = False
self._mouseDown = False
def _onMouseEnter(self, event):
self._mouseIn = True
def _onMouseLeave(self, event):
self._mouseIn = False
def _onMouseDown(self, event):
self._mouseDown = True
def _onMouseUp(self, event):
self._mouseDown = False
self.sendButtonEvent()
def sendButtonEvent(self):
event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
event.SetInt(0)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
def _onEraseBackground(self,event):
# reduce flicker
pass
def Iz(self):
dc = wx.BufferedPaintDC(self)
dc.DrawBitmap(self.Norm_bmp, 100, 100)
def _onPaint(self, event):
# The printing functions, they should work... but don't.
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(self.Norm_bmp, 0, 0)
# This never printed... I don't know if that means if the EVT
# is triggering or what.
print '_onPaint'
# draw whatever you want to draw
# draw glossy bitmaps e.g. dc.DrawBitmap
if self._mouseIn: # If the Mouse is over the button
dc.DrawBitmap(self.Over_bmp, *self.pos)
else: # Since the mouse isn't over it Print the normal one
# This is adding on the above code to draw the bmp
# in an attempt to get the bmp to display; to no avail.
dc.DrawBitmap(self.Norm_bmp, *self.pos)
if self._mouseDown: # If the Mouse clicks the button
dc.DrawBitmap(self.Push_bmp, *self.pos)
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title,
wx.DefaultPosition, wx.Size(400, 400))
self.CreateStatusBar()
self.SetStatusText("Program testing custom button overlays")
menu = wx.Menu()
menu.Append(ID_ABOUT, "&About", "More information about this program")
menu.AppendSeparator()
menu.Append(ID_EXIT, "E&xit", "Terminate the program")
menuBar = wx.MenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
# The call for the 'Experiential button'
s = r"D:\virtual_pc\mockup\mockupscreens\embed_images\toolbar\options.png"
self.Button1 = Custom_Button(self, -1,
wx.Point(100, 100),
wx.Bitmap(s),
wx.Bitmap(s),
wx.Bitmap(s))
self.Button1.Show(True)
EVT_MENU(self, ID_ABOUT, self.OnAbout)
EVT_MENU(self, ID_EXIT, self.TimeToQuit)
def OnAbout(self, event):
dlg = wxMessageDialog(self, "Testing the functions of custom "
"buttons using pyDev and wxPython",
"About", wxOK | wxICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def TimeToQuit(self, event):
self.Close(true)
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, "wxPython | Buttons")
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()
I've written this small app that draws lines between two points selected by the user and it works but how do I keep the lines I draw from disappearing whenever the window is minimized or gets covered by another open window?
class SimpleDraw(wx.Frame):
def __init__(self, parent, id, title, size=(640, 480)):
self.points = []
wx.Frame.__init__(self, parent, id, title, size)
self.Bind(wx.EVT_LEFT_DOWN, self.DrawDot)
self.SetBackgroundColour("WHITE")
self.Centre()
self.Show(True)
def DrawDot(self, event):
self.points.append(event.GetPosition())
if len(self.points) == 2:
dc = wx.ClientDC(self)
dc.SetPen(wx.Pen("#000000", 10, wx.SOLID))
x1, y1 = self.points[0]
x2, y2 = self.points[1]
dc.DrawLine(x1, y1, x2, y2)
# reset the list to empty
self.points = []
if __name__ == "__main__":
app = wx.App()
SimpleDraw(None, -1, "Title Here!")
app.MainLoop()
Your issue is that you are only drawing when the user clicks. The resize/erase (when another window covers yours) problems are because your window doesn't maintain a "buffer" which it can redraw.
Here, I've modified your sample, it seems to be working okay.
import wx
class SimpleDraw(wx.Frame):
def __init__(self, parent, id, title, size=(640, 480)):
self.points = []
wx.Frame.__init__(self, parent, id, title, size)
self.Bind(wx.EVT_LEFT_DOWN, self.DrawDot)
self.Bind(wx.EVT_PAINT, self.Paint)
self.SetBackgroundColour("WHITE")
self.Centre()
self.Show(True)
self.buffer = wx.EmptyBitmap(640, 480) # draw to this
dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
dc.Clear() # black window otherwise
def DrawDot(self, event):
self.points.append(event.GetPosition())
if len(self.points) == 2:
dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
dc.Clear()
dc.SetPen(wx.Pen("#000000", 10, wx.SOLID))
x1, y1 = self.points[0]
x2, y2 = self.points[1]
dc.DrawLine(x1, y1, x2, y2)
# reset the list to empty
self.points = []
def Paint(self, event):
wx.BufferedPaintDC(self, self.buffer)
if __name__ == "__main__":
app = wx.App(0)
SimpleDraw(None, -1, "Title Here!")
app.MainLoop()
You have to structure your program differently in a GUI environment. Typically, you maintain a data structure called your model. In your case, you already have a start of one, self.points. Then you only draw on the window in response to a paint event. The windowing system will send you paint events when the window needs painting, including when it is first displayed, when it is maximized, and when it is revealed from beneath another window.
In your program, you'd bind the LeftDown event to a function that modifies self.points and invalidates the window, which would usually cause the windowing system to send you paint events. You'd bind the Paint event to a function that draws on the window.