Using SwapBuffers() with multiple OpenGL canvases and vertical sync? - python

I have a GUI written using wxPython that contains two GLCanvases, a 'display' canvas and a 'preview' canvas onto which I am drawing some very simple geometry using PyOpenGL. The 'preview' and 'display' canvases display the contents of the same framebuffer:
I need both of these canvases to be updated synchronously at a consistent framerate with no tearing.
So far I have just been calling
self.SetCurrent()
# draw stuff...
self.SwapBuffers()
for both the preview and display canvases within my rendering loop. This works reasonably well provided that I disable vsync in my driver settings.
However, if I enable vsync my framerate drops to ~30Hz, half the vertical refresh rate of my display. Presumably this is because the execution of the rendering loop stalls at each SwapBuffers() call waiting for the next vertical refresh - if I comment out one of the two SwapBuffers() calls, my framerate goes back up to 60Hz, but then of course only one of my canvases gets updated.
What I'd really like is for both of my canvases to be updated synchronously, once per vsync interval. Is there any way that I can achieve this using the wx.glcanvas.GLCanvas class?

What Andon implied in his comment is, that you use {wgl|glX}SwapInterval to set the swap interval for the first, synching SwapBuffers to 1, and then right after the synching SwapBuffers returns set the swap interval to 0 and do the other SwapBuffers.

Related

Is there any way to speed up the draw rate of a Scrollable Canvas in Tkinter?

So after posting another question about this issue I realised the problem was only happening with my custom resize bind. When I resize the window by the default edges of the window the Issue does not happen and the contents of the canvas is drawn accurately - however when using my custom resize bind the contents of the canvas is laggy and lags behind the true position of the window.
import tkinter as tk
def resize(self, event=None):
y=root.winfo_height()
x1 = root.winfo_pointerx()
x0 = root.winfo_rootx()
root.geometry("%sx%s" % (x1-x0,y))
root=tk.Tk()
canvas = tk.Canvas(root,bg='red')
canvas.pack(padx=20,pady=20)
inside=tk.Frame(root)
inside.pack(fill='both')
for i in range(10):
x=tk.Label(inside,text='Lsdkfjhaskfjhskljdfhaskjfhksfhkjasdhf')
x.pack()
g=tk.Button(root,text='Drag to resize')
g.bind('<B1-Motion>',resize)
g.pack()
canvas.create_window(5,5,window=inside,anchor='nw')
Original content:
Screenshot while resizing window with manual bind via button - as you can see the content is not visible while resizing the window and lags behind where the canvas is.
The issue is fixed if I call root.update() at the start of my resize function however this then causes a recursion depth error to the thousands of calls being made to update in such a small period of time.
Finally to repeat. When resizing with the default window Resize nudges at the edge of the window the canvas resizes perfectly with perfect draw rate and the content stays visible all the time. Why is my binding not acting the same way?
Geometry calculations are quite complex in general (and Tk has some fairly sophisticated solvers behind the scenes for handling them) so they only run when the GUI is considered to be "idle". Idleness occurs when the event queue would otherwise block, waiting for the next message from the external world, i.e., when there's nothing else queued up for immediate attention. Tk also postpones redrawing (another actually really expensive operation) the same way; it makes the UI seem far faster than it really is. This was critical back 20 years ago and more, and yet is still really useful now.
But it breaks if there there is a total flood of events coming in, and drags can do that, especially when there's resizing involved. That's why you can do update idletasks (in Tcl/Tk notation) and update_idletasks() (in Tkinter notation). That that does is immediately process all the pending idle event callbacks without taking further real events, running the pending resize computations and redraws. It is far less likely to trigger reentrant event processing than a full update(), which is the problem you were hitting (and why update is considered harmful under normal circumstances).
Note that it is important to let the event loop itself have some time to process still, as parts of handling widget resizes are inevitably done via real events (as they impact upon the OS/GUI). By far the best way to do that is to just make sure to return normally from your event handlers as soon as practical.
Are you using the right bind event? Try ''
g.bind('<ButtonRelease-1>',resize)

Getting screenshot via printwindow not redrawing if laptop screen off

My goal is to take screenshots off an application while the laptop screen is off, but instead the screenshot will always be the same as just before turning off the screen. It does not redraw itself once the screen is off, and remains frozen.
I'm obtaining a screenshot using printwindow from Python (using method described here: Python Screenshot of inactive window PrintWindow + win32gui
This method works nicely as long as I have my laptop screen on, but if I turn it off, it simply returns the last image before the screen turned off. I've tried using win32gui.RedrawWindow, hoping that this would force a redraw, but I haven't gotten it to work, even trying all the different flags. I've also tried getting screenshots via pyautogui, but this also has the same problem. Is there any way to redraw the application while the laptop screen is off?
It would be nice if there were a straightforward way to do what you want to do, but unfortunately it's just not possible without some convoluted low-level work.
In the underlying WinAPI, native windows redraw themselves only when they receive WM_PAINT messages, and they only receive WM_PAINT messages if they are visible. They don't get the messages if they're behind another window, or if they're dragged off screen, or minimized, or their visibility is set to False. They also aren't visible when the screen is off. Since they aren't visible, they simply never get told to redraw themselves. That's why the screen capture image doesn't change after the screen is turned off.
There is no command you can issue that will override this -- it is implemented as a fundamental part of the windowing paradigm.
One solution is to have the application paint itself in something that's not a window (e.g., a dialog box) and capture that rather than capturing the screen. You could also modify the application by overriding OnPaint and using a timer to invalidate and call your OnPaint function periodically (since WM_PAINT messages won't be received).
If it were me I'd override OnPaint, make my application draw itself to a background context bitmap rather than the normal window, then use a timer to invalidate and redraw periodically and just take the bitmap of the background context whenever I wanted to capture the output.
If you don't own the code for the applications that are running, you may still be able to get applications to redraw themselves by running everything in a virtual machine. I've done that successfully for headless computing in the past, but it was many OS versions ago and things may be different now, so YMMV...
Information on manual screenshots under VMWare is here.
Information on programmatic screenshots under Hyper-V is here.

Matplotlib tkagg backend performance

I have a tkinter application that can plot a large number of data, where I have noticed poor pan and zoom performance when there is a large number of data on the canvas.
Looking into the tkagg_backend (as suggested by this and several other questions), the function and documentation suggests that the canvas should only be redrawn once the user is idle. However, from current and previous experience the canvas has always been updating(redrawing) mid zoom/pan. Therefore, I was looking at the specific functions that are involved and have a question regarding that.
The dynamic_update function:
def dynamic_update(self):
'update drawing area only if idle'
# legacy method; new method is canvas.draw_idle
self.canvas.draw_idle()
The canvas.draw_idle() function:
def draw_idle(self):
'update drawing area only if idle'
if self._idle is False:
return
self._idle = False
def idle_draw(*args):
try:
self.draw()
finally:
self._idle = True
self._idle_callback = self._tkcanvas.after_idle(idle_draw)
The ._idle parameter is initialized as True in the backend. This point is where I got stuck as I am unable to understand how ._idle is linked to mouse activity (I assume that it is, please correct me if that's wrong).
Interestingly enough, the canvas behaves like I would expect by commenting the self.canvas.draw_idle() line (redrawing once the mouse button is unpressed), and thus not calling the entire draw_idle function.
Therefore, my question is how is _idle set or why does it redraw my entire canvas when I am not idle?
When refering to "being idle" it is not the user or his mouse activity that is meant, but rather the GUI mainloop. The canvas should only be redrawn if the mainloop is not currently busy. Here, of course self._idle only refers to the matplotlib part of the GUI and what this structure inside draw_idle is supposed to do is to prevent the canvas from being drawn while it is being drawn.
This could easily happen when panning or zooming. The mouse moves to a new location, causing a readraw. While this redraw is happening, the mouse has already moved further and caused the next redraw. At that point in time the first redraw may not yet have finished, such that it would queue up. And so forth, such that at some point the GUI might become unresponsive. To prevent that, a new draw is only initialized once the previous one has finished and this behaviour is steered by self._idle being true or false.

Excessive hang time when destroying a PyGTK window

My PyGTK application creates a secondary popup window for displaying a preview of results. This window is fairly elaborate, with Table widgets nested three deep and populated by HBoxes containing one Label each at the lowest level. The number of Labels total can be in the thousands. I am noticing that when I close this window, GTK becomes extremely busy processing something (functions added with gobject.idle_add don't resolve for >10 seconds) and the main window of my application becomes unresponsive in this time. Even with this many widgets, it seems strange to me that the window should take so long to close, longer even than it takes to set up and display. Is there any way to mitigate this? (I tried creating and showing the window in another thread, but apparently with GTK this is a no-no)
How long takes that window to show up? Are all the widgets created at once when it is displayed?
Your problem might be caused by the destruction of your thousands of widgets, all at the same time. Or by a lengthy action perform on on of those widgets destruction. But without some code to look at, there could be thousands of reasons, so a ptomato says, use a profiler...
Apparently it was being caused by my attempt to change the background color of the tables--I was setting the background color of every HBox (and Label), which was responsible for nearly all of the excessive teardown time. All I had to do was set the background color of the Viewports the Tables are contained in.

Track window/control resize in PyQt?

I have a window with 2 QTableWidgets, having their scrolling synchronized.
The 1st one usually has horizontal scroll, while the 2nd usually (automatically) not.
In order for them to show consistent data (row against row) I make the 2nd have the scroll (through property HorizontalScrollBar -> AlwaysOn).
But sometimes the 1st table doesn't have the scroll. What's event worse, it may have it or not depending on the window size (when making window wider the scroll disappears). As far as I see there's no signal for resize of window or control (widget).
I can, of course, use some constantly running timer to check if 1st table has scroll, but I wounder if there's more pure solution.
Thanks!
The answer was to reimplement the resizeEvent and check table.horizontalScrollBar().isVisible()

Categories

Resources