Track window/control resize in PyQt? - python

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()

Related

Add side panel without resizing parent

I'm using PySide2 to write a gui (witout QtDesigner).
The mainwindow contains plots and some other widgets. Through a menu option I want to open a side panel widget. The way I want it to work is that the whole window simply grows to contain that new widget without changing the size of anything else in the main window. How can this be done?
Currently the widget is just added to the central layout with addWidget, I've also tried making it a QDockWidget but it is still resized (and anyway I would like to avoid the extra fluff that comes with having a DockWidget).
So I have
---------
|content|
---------
which should turn into
-------------
|new|content|
-------------
but currently I get
---------
|new|cnt|
---------
It's hard to do well on the "client" side of Qt; this would really belong within Qt itself. I have implemented a slightly more general variant of this a couple of years ago, and just to make it work well across Windows, Mac and KDE, the code ballooned to over a thousand lines to cover all the icky corner cases, with another thousand for the test harness. It was surprisingly hard to implement the tests - especially on X11, where there was no way around using native X APIs to verify intended behavior. I got rid of that monstrosity soon later - the effort was unnecessary.
You can have the side panel as a separate top-level frameless widget that moves itself so that its top-right corner is aligned with the top-left corner of the content window, and resizes itself vertically to match the vertical size of the content window. You can of course make it slightly shorter (vertically) while still center-aligning it vertically with the content window.
You'd want to capture the resize events of the content window to do this: the side panel should install itself as an event filter for the content window.
You'll want the side panel to be a Qt child of the content window, but you also need to make it a top-level window, i.e. set the Qt::Window flag on it, so that it becomes top-level and not a sub-widget of the content window.

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)

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

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.

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.

pygtk window with box that ignores all X(mouse)events (passes them through)

I'd like to do the following: Create a fullscreen, always on top pygtk window with a webkit widget displaying some html, but with a box that is completely transparent, so that the windows below are visible. (This seems to be possible: Is it possible to render web content over a clear background using WebKit?)
What I'd like is to (sometimes) pass all mouse events that occur in the transparent box down to the windows below my application's window, so that I can interact with them normally. So not just visually transparent, but also transparent to mouse events.
Theoretically, I suppose I could catch all events I am interested in with a pygtk Eventbox, find the window directly below mine with wnck, and pass this event to it with python-xlib.
This doesn't exactly seem like the most elegant solution; is there a better way?
Forwarding the events won't work well as you guessed; it creates a lot of race conditions, and some apps will ignore stuff from XSendEvent anyway.
What you can do is set the input shape mask. See http://www.x.org/releases/current/doc/xextproto/shape.html and then look at XFixesSetWindowShapeRegion() in /usr/include/X11/extensions/Xfixes.h which lets you specify a shape "kind" (here you want ShapeInput).
Something like:
XRectangle rect;
XserverRegion region = XFixesCreateRegion(display, &rect, 1);
XFixesSetWindowShapeRegion(display, window, ShapeInput, 0, 0, region);
XFixesDestroyRegion(display, region);
The ability to set ShapeInput is "only" 5-6 years old so if you care about really crappy old versions of X11, you might be hosed.

Categories

Resources