I have a window and I'm rendering an image onto it. Every frame the position changes, so the problem is that (obviously) it doesn't disappear from the last frame. I want to clear the window every frame. I can't find any clear methods for the window object (GraphWin) anywhere. And I can't use undraw() because then I'd have to do it for everything on the screen.
First thing to consider is if you're simply moving graphic objects between frames, then call their move() methods rather than erase and redraw them. If you must clear the screen, then I suggest:
Before dropping down to the Tkinter level, I'd consider using Zelle Graphics' own underpinnings. The reason is that Zelle Graphics keeps it's own parallel records of objects and if you delete them from the Tkinter level, you could get the two out of sync. Here's my suggestion:
def clear(win):
for item in win.items[:]:
item.undraw()
win.update()
However, undrawing items is slow, likely slower than your desired frame rate. So, you'll want to turn off auto flush:
win = GraphWin(..., autoflush=False)
and then call:
update()
whenever you have something to present to your user -- this will speed up the graphics since it won't show all the intermediate steps.
The above advice does not apply to things drawn with the win.plot() method, however. Plotting is implemented at a lower level than other Zelle graphics so you do need to drop down to Tkinter to clear the plot. See How to undraw plot with Zelle graphics?
for an example.
Related
I am working with a simulation of objects (circles) that live in a 2d world (the objects move and can interect with eachother, they colide).
To view the simulation I am using the tkinter module.
All the variables that define each state of the simulation are computed using a recursive function that I have outside the tkinter implementation (that way i can run the simulation without having to waste time drawing).
My question is: What do you think would be better for drawing the simulation states into the tkinter canvas?: to delete all previous state elements and redraw all new state elements for each new state, or to find a way to be able to update the position of the elements using tkinter canvas method move for each new state.
It is better to update elements rather than create new ones. You cannot create unlimited items on a canvas, and the more items you've created (even if you later destroy them), the slower the canvas will be. While the canvas can handle a few thousand items, once you start getting into the tens of thousands you will start to have problems.
I have a python program where I have several matplotlib canvases embedded into a wxpython application. One of the canvases has many crosses in it. When the user right-click in this canvas the closest cross should be removed together with everything belonging to this cross (they are linked through an unique id-tag, and there might be things in every canvas that should be removed). I want the removing (or actually the replotting) to be as fast as possible. The program is quite large so I use several threads etc.
The easiest thing to implement this is to use wx.CallAfter(canvas.draw) for each canvas. However, there is a delay between the rightclick and the refresh of every canvas so I believe that canvas.draw() is too slow.
I saw two other functions for fast redrawing: the matplotlib functions blit() and draw_artist(). As far as I understand, blit() refreshes changed pixels inside some area (I used the axes bbox). I managed to get it to work with blit() in the sense that the program ran without crashing...but not updating what you see on the screen.
Did not manage to get draw_artist() to work when removing a pixel (tried using first line.remove(), then draw_artist(line) but the line was ofcourse already dead so draw_artist did not work).
Note: I called blit() and draw_artist() with wx.CallAfter()!
The feeling I have is that blit() is the best solution, but I did not manage to get it to update to the "screen-level". So my question is: what is the fastest and most resource-saving way of removing artists from matplotlib.canvases (embedded into wxPython) without redrawing more than you need to, but still let the change propagate to the screen?
When I draw a shape using:
canvas.create_rectangle(10, 10, 50, 50, color="green")
Does Tkinter keep track of the fact that it was created?
In a simple game I'm making, my code has one Frame create a bunch of rectangles, and then draw a big black rectangle to clear the screen, and then draw another set of updated rectangles, and so on.
Am I creating thousands of rectangle objects in memory?
I know you can assign the code above to a variable, but if I don't do that and just draw directly to the canvas, does it stay in memory, or does it just draw the pixels, like in the HTML5 canvas?
Every canvas item is an object that Tkinter keeps track of. If you are clearing the screen by just drawing a black rectangle, then you effectively have created a memory leak -- eventually your program will crash due to the millions of items that have been drawn.
To clear a canvas, use the delete method. Give it the special parameter "all" to delete all items on the canvas (the string "all"" is a special tag that represents all items on the canvas):
canvas.delete("all")
If you want to delete only certain items on the canvas (such as foreground objects, while leaving the background objects on the display) you can assign tags to each item. Then, instead of "all", you could supply the name of a tag.
If you're creating a game, you probably don't need to delete and recreate items. For example, if you have an object that is moving across the screen, you can use the move or coords method to move the item.
Items drawn to the canvas are persistent. create_rectangle returns an item id that you need to keep track of. If you don't remove old items your program will eventually slow down.
From Fredrik Lundh's An Introduction to Tkinter:
Note that items added to the canvas are kept until you remove them. If
you want to change the drawing, you can either use methods like
coords, itemconfig, and move to modify the items, or use delete to
remove them.
Yes, I believe you are creating thousands of objects. If you're looking for an easy way to delete a bunch of them at once, use canvas tags described here. This lets you perform the same operation (such as deletion) on a large number of objects.
I am working on an application with two children. One's a widget that functions as a toolbar, the second, below, functions as dashboard, on which information would appear. The latter can be shown/hidden with buttons on the former. Here's a screen-cast of the prototype.
Now I am looking at doing the same but with a motion animation whilst showing/hiding the lower widget.
In short: the effect should be giving the impression the entire application rises or falls progressively when toggling the dashboard.
In details: I would like the height of the lower widget to decrease until it is reduced to 0 and then hidden completely. Likewise it would increase slowly when showing the widget again. In the meanwhile the position of the application should change accordingly so it stays at the bottom of the screen.
How can I do that? I've never done animations on Qt before. If you don't have an answer, do you know of a quality tutorial that could lead me there.
NB: I am using PyQt.
I think you can get what you want by using a QPropertyAnimation that animates the geometry property of your widget.
But IMHO this is the window manager's role to do what you want. Maybe you will have some headaches bypassing it (but I'm maybe wrong).
After better reading of your question, it seems that you want to use your own components to trigger the hiding/showing so the WM shouldn't be a problem.
As a start here is some code that animate a minimizing of a widget (assuming tbw is an instance of the widget you want to animate):
formerGeometry = QtCore.QRect(tbw.geometry()) # storing previous geometry in order to be able to restore it later
hideAnimation = QtCore.QPropertyAnimation(tbw, "geometry")
hideAnimation.setDuration(2000) # chose the value that fits you
hideAnimation.setStartValue(formerGeometry)
#computing final geometry
endTopLeftCorner = QtCore.QPoint(tbw.pos() + QtCore.QPoint(0, tbw.height()))
finalGeometry = QtCore.QRect(endTopLeftCorner, QtCore.QSize(tbw.width(), 0))
hideAnimation.setEndValue(finalGeometry)
hideAnimation.start()
I'm having problems getting my wxPython window to refresh. It's currently plotting a graph using wxmpl which can be zoomed, panned, etc. On occasion the user may plot a large amount of data and zoom in on a small portion, which can cause it to 'freeze up'. By that I mean the plot itself is not updated, and the axis labels are drawn on top of each other. It is modifying the plot, just not displaying the updated info correctly. If you resize the window the plot is redrawn correctly.
I've spend an inordinate amount of time digging through source code and documentation for wx, wxmpl, and matplotlib... The best solution I've come up with is resizing the window to force a repaint (thus displaying the updated plot correctly).
# All of these fail - displays the same, incorrect plot
# (view is a wxmpl.PlotPanel object, which inherits from wx.Window among other things)
view.Refresh()
view.Update()
view.draw()
# This works, but is clearly less than ideal
view.SetSize((view.GetSize().width, view.GetSize().height+1))
view.SetSize((view.GetSize().width, view.GetSize().height-1))
There's got to be a better way - what I really want to know is what wx.Window.SetSize does to redraw the window, and just call that instead. Or, is there another method that I missed somewhere?
The panel.Layout() command is a great option because it is exactly the same method that is called when you resize your window. I also had trouble with the refresh and update methods. Layout seems to work when those two fail.
If you can't place it anywhere else, you could try
wx.Yield()
instead of Refresh or Update.
I would also try Show(False) and then Show(True) on the PlotPanel.
In a computational expensive application, where you are expecting something to be calculated for over 0.1 sec and probably have some user input it is not recommended usually to make those intense drawing in the GUI thread.
Not aware of your specific situation, but general approach if that you move all time consuming tasks (be it computation, image adjustment (e.g. scaling)) to the non GUI thread. Just a normal Python thread is fine, and once you have an long part complete, you refresh your GUI. During computation of course it would be a user friendly to display some sort of "waiting" sign. Also disable other controls, so bored user will not be able to change anything midway to your computation.
I was stuck with that issue since my early days with Java and later with Python, mostly in connection to network operations (which NEVER should be in GUI thread).
In case it is image adjusment (or graphics generation), which takes much time, background thread can prepare image in wxMemoryDC and then wxDC::Blit it to the window of your choice. I am not aware if this can be done with your component wxmpl.PlotPanel, so you will have to research this.