Force repaint in wxPython Canvas - python

I've got a Canvas which manipulates objects in the mouse event handler. After modifying the objects, I want to trigger the OnPaint() event for the same Canvas to show (rerender) the changes. What is the right way to do this? It doesn't let me call OnPaint() directly. Also, is triggering an event from another event "wrong" in some sense, or likely to lead to trouble?

I would just call self.Refresh() or maybe RefreshRect() and pass the area that needs to be repainted.

I tried self.refresh() as suggested by Mike Driscoll. That worked partially. I'm moving my own objects with mouse events and then repainting the image - rendering my objects. So the mouse events were updating coordinates and the repaint was updating the display. The problem is that refresh() seems to be called lazily or has lower priority than the mouse events. Refresh() thus produces a choppy display.
Since I am using a buffered display via the wxDemo for Scrolled Area, I took the following line from the Paint event:
dc = wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)
And called my drawing function with that DC from within the mouse event handler. Upon completion dc is freed and (according to the comments in the demo) copied to the screen. It was not previously clear to me how the Paint event was handling DCs, so I was trying to call the event. Turns out you can use that DC and just render from inside the mouse events. This results in smooth dragging of my custom drawn objects.

Related

PyGame blit image disappears when no mouse event is fired?

When using surface.blit during a mouse event, the blit doesn't remain on the screen.
I have two almost identical images. The standard image already exists on the surface and when the mouse hovers over the image, another image needs to be placed on top of the existing image and when the mouse leaves the image it needs to be removed.
My current code works except a mouse event needs to be going on - when the mouse is stationary inside the image, the overlay image is removed. How do I fix this?
def Execute(self):
while self.Running == True:
NewGameButton = pygame.image.load("./Assets/Interface/newgame.png").convert_alpha()
NewGameButtonHover = pygame.image.load("./Assets/Interface/newgame_hover.png").convert_alpha()
self.Display.blit(NewGameButton, (460,260))
pygame.display.flip()
for Event in pygame.event.get():
if NewGameButton.get_rect(topleft=(460,260)).collidepoint(pygame.mouse.get_pos()):
self.Display.blit(NewGameButtonHover, (460,260))
pygame.display.update()
pygame.quit()
Note: The Execute() function is a method within the class App.
Your code currently only does its mouse position checking inside a loop over the events that have been generated during the frame. That means it won't run if there are no events. Try moving that code outside of the for Event in pygame.event.get() loop, and it will run once a frame, regardless of the event status.
Note that you probably do want to deal with events, but you're not doing so now. If you really don't care about any events (not even the QUIT event?), you could replace looping over the events with a call to pygame.events.pump(), which will let Pygame handle any internal events (like dragging or resizing the window), without sending anything to you.

What are the child objects of QGraphicsView in PyQt? How do I capture MouseMove events on top of QGraphicsView/Scene with an event filter?

Alright, so. I'm fiddling around with PyQt and I'm trying to write an application where I can draw figures into a graphics scene with the mouse based on tools from a toolbar.
The way in which I capture events might be a bit strange/not the "right" way to do it, but it seems to be working for most events.
I have a container class for tool methods, which get called by actions on a toolbar. Upon entry to a method, I install the container object as an event filter on the QGraphicsView widget (I've also tried adding it to the scene instead). I'm also storing state to denote the stage of drawing I'm in. First, after clicking on the tool, I'm in a state catching mouse presses. Then, I'm trying to catch mouse movement (a line gets drawn as the user click+drags, gets finalized upon release) and then mouse release.
Events that end up being caught by the filter are mouse presses and releases, even though I'm definitely waiting for QtCore.QEvent.Mousemove (#5) -- the one that gets triggered as I move my mouse on the gfxview area.
I know that mouse movement (type 5) events are being generated, as I tried attaching the event filter to the QApplication as well. It reports the events originating from a source that isn't either one of the ones I'm attaching event filters to (so it can't get caught). If I check said source's parent, it answers back that its parent is the QGraphicsView widget.
If I then ask for the children of QGraphicsView, it lists my Graphics Scene along with three unnamed QWidgets.
Here's a debug printout from the event filter:
Event type: 5
Source:
<PyQt4.QtGui.QWidget object at 0x000000000303A5E8>
Source's parent:
<__main__.Easel object at 0x000000000303A1F8>
Easel:
<__main__.Easel object at 0x000000000303A1F8>
Easel's children: [
<PyQt4.QtGui.QWidget object at 0x000000000303A708>,
<PyQt4.QtGui.QWidget object at 0x000000000303A798>,
<PyQt4.QtGui.QWidget object at 0x000000000303A5E8>,
<PyQt4.QtGui.QGraphicsScene object at 0x000000000303A288>,
<__main__.Canvas object at 0x000000000303A558>]
Easel is what I'm calling my graphics view (inherits QGraphicsView) and Canvas is a re-implementation of the QGraphicsScene.
So, to re-iterate, I'd like to know what these three widgets are, if I could just slap event filters on them instead (for loop through Easel's children, add filter to each) or if there's an easier way to do this.
I'd prefer not to have to implement the mouseEvent type methods inside the Easel and Canvas classes, as I'd like to have the tool logic in one place under this aggregating class. I'm not sure if it would even help, but similar projects I've seen presented as examples in tutorials and such seem to do something like that.
I can edit the relevant methods into this post if I must, but they're really ugly and such.

Refreshing a QWidget

I've been having this issue a lot of times.
When I modify some properties of a QWidget after the widget.show(), the widget won't update. Most of the time, a mouse click or when the mouse leaves or enters the widget, the widget will be updated. However, if I leave the mouse, it won't refresh by itself.
Until now I managed to deal with this by doing :
widget.hide()
widget.show()
But this is a very dirty fix. Is there a better way to tell python to refresh the widget ?
Thank you.
To update the widget, you should repaint() it, but calling repaint() directly is not very good, so try:
widget.update()
From doc:
This function does not cause an immediate repaint; instead it
schedules a paint event for processing when Qt returns to the main
event loop. This permits Qt to optimize for more speed and less
flicker than a call to repaint() does.
Calling update() several times normally results in just one
paintEvent() call.
Qt normally erases the widget's area before the paintEvent() call. If
the Qt::WA_OpaquePaintEvent widget attribute is set, the widget is
responsible for painting all its pixels with an opaque color.
Did you already try the
QWidget.update()
This function updates only the visible parts keeping the invisible parts untouched.

Avoid event grab during motion in Tkinter

Is it possible in Tkinter to avoid the event grab which occures when you press a mouse button over a widget and keep it pressed while you move the mouse?
I want to register the mouse button and then track all widgets the user enters while he moves his mouse with the button pressed. When the user releases the mouse button the application executes the same action for all tracked widgets.
The following code should explain what I want to do.
# Set a tracking flag
widget.bind('<Button>', start_tracking)
# Add the entered widget to the tracked widgets, if the tracking flag is set
widget.bind('<Enter>', add_to_tracked_widgets)
# Execute an action for every tracked widget; unset the flag
widget.bind('<ButtonRelease>', end_tracking)
I took a look at the grab_current and grab_status methods, but they always returned None.
Python version is 3.4.1.
This is probably the most complicated way to do this, but okay.
One thing that makes this more complicated is Tkinter itself, because event.widget still refers to the widget that was clicked on initally. A different event we can use is Motion which is activated when the mouse moves inside a widget.
tk.bind("<Motion>", add_tracked)
I think you can implement the list and state variables yourself, so we come to the add_tracked method (I just renamed it, it's your add_to_tracked_widgets):
def add_tracked(event):
if tracking:
# Get coordinated of the event and use the master window method to determine
# wich widget lays inside these.
widget = tk.winfo_containing(event.x_root, event.y_root)
# Since 'Motion' creates many events repeatedly, you have to convert this
# list into a set to remove duplicates.
widgets.append(widget)

Checking user idle in PyQt by examining mouse clicks on the GUI

I am trying to implement a feature such that if the user havn't interact with the GUI for some time X, I will stop some functionality of the GUI.
Currently I have a time stamp set up so that if any of the button is not clicked in X seconds, the GUI will terminate some functionality
button1.triggered.connect(keep_alive)
button2.triggered.connect(keep_alive)
....
buttonN.triggered.connect(keep_alive)
As you can see, this is not really elegant, and doesn't scale as the button increases. Therefore I am currently investigating another method such that I monitor the mouse clicks
mouse = app.mouseButtons()
if mouse != Qtcore.Qt.NoButton:
#keep_alive
I think this is a little hacky, but it will work for the functionality I envisioned, however, I do not know how to insert this to the execution loop of the QT.
Any suggestions will be appreciated
You must intercept the mouse events by reimplementing the mousePressEvent.
http://qt-project.org/doc/qt-4.8/qwidget.html#mousePressEvent
To make sure that it won't affect your other functionalities you'll need to propagate it to the parent widget. Read more details: https://www.qt.io/blog/2006/05/27/mouse-event-propagation
I would proceed by implementing it in the main window and make sure all mouse events are propagated to it.

Categories