Can't draw() sprites in pyglet - python

For some reason, I can't get pyglet to draw sprites. Here's my code:
import pyglet
game = pyglet.window.Window(640, 480, "I'm a window")
batch = pyglet.graphics.Batch()
pyglet.resource.path = ["."]
pyglet.resource.reindex()
image = pyglet.resource.image("hextile.png")
pyglet.sprite.Sprite(image, x=200, y=300, batch=batch)
pyglet.text.Label('DING', font_name='Arial', font_size=24, x=100, y=100, batch=batch)
#game.event
def on_draw():
game.clear()
batch.draw()
#image.blit(0, 0)
pyglet.app.run()
Now, when I draw the batch, the text label is shown correctly. I see "DING" on the window. However, the image "hextile.png" is not shown. I tried drawing the sprite independently, but that didn't work either. Blitting the image (as shown in the commented line), however, seems to work just fine, but obviously that's not quite the functionality I'm after here. I can't figure this one out. What am I missing?

Assuming you and your friends have ATI graphics cards:
Sprite.draw() uses the v2i format and VertexDomain.draw() internally. For some reason this combination doesn't work on Windows Vista/7 Catalyst drivers 11.9 and above, and consequently Sprite drawing fails as well. See also: pyglet vertex list not rendered (AMD driver?)
There is a pyglet issue you might want to follow: http://code.google.com/p/pyglet/issues/detail?id=544
Your options for the moment seem to be either to patch pyglet.sprite.Sprite as mentioned in the third comment on that issue or downgrade your video driver.
Update: No need to patch Sprite or downgrade your video driver. This problem seems to be fixed with Catalyst 12.4 (video driver 8.961.0.0).

The sprite is getting garbage collected because you don't hold a reference to it. Do this:
sprite = pyglet.sprite.Sprite(image, x=200, y=300, batch=batch)
For what it's worth, I prefer using a subclass of Window, like this: (this code works for me too)
import pyglet
class Window(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
self.batch = pyglet.graphics.Batch()
image = pyglet.resource.image('hextile.png')
self.sprite = pyglet.sprite.Sprite(image, batch=self.batch)
def on_draw(self):
self.clear()
self.batch.draw()
def main():
window = Window(width=640, height=480, caption='Pyglet')
pyglet.app.run()
if __name__ == '__main__':
main()

Related

Show image on screen with ursina

I'm making a 3D FPS in Ursina, and I'd like to have my arm skin with the weapon as an image, not an actual 3D model. Does anyone know how to do this ? I tried with Animate from the documentation, but this loads an image as an object in my scene.
What I could do is define a quad with player as parent, and positional arguments, so that it follows me and I see it at the right place, but even this wouldn't work as the texture argument doesn't accept gifs.
So, does anyone know how to do that ?
You can load an animated gif with Animation() which creates an entity. As part of the interface you'll want to attach it to the UI:
from ursina import *
app = Ursina()
gif = 'animation.gif'
a = Animation(gif, parent=camera.ui)
a.scale /= 5 # adjust right size to your needs
a.position = (0.5, -0.5) # lower right of the screen
app.run()
You will need the imageio Python package installed to load gifs.
OLD ANSWER
The Entity you use for the gun has to be anchored to the interface using its parent parameter. Here's an example for a Minecraft-style hand (just a block really):
class Hand(Entity):
def __init__(self):
super().__init__(
parent=camera.ui,
model='cube',
position=Vec2(0.5, -0.3),
scale=(0.2, 0.2, 0.5),
rotation=(150, -30, 0)
)
The important part is parent=camera.ui
Sadly, I cannot seem to find a way to play gifs. I know, #Jan Wilamowski updated his answer but, in my project, that does NOT work. You CAN show static images on this screen, though, by making a quad entity with an image texture, as shown:
class yourimage(Entity):
def __init__(self):
super().__init__(
parent=camera.ui,
model='quad',
#size, position, and rotate your image here
texture = 'yourimage')
yourimage()

Make Window In PyGlet

I started to learn PyGlet (Python Framework for games). I made default window, but when I slide it in the edge of the monitor, it looks something like this:
import pyglet
window = pyglet.window.Window(800, 600, "PyGlet Window")
pyglet.app.run()
What's the problem? Thanks for all support!
This has to do with buffers and how windows handle windows that are outside of the screen. IIRC they get optimized away and the buffer or area of the window will not be automatically cleared. And if you don't clear it or no event triggers a re-render, whatever is stuck in the buffer will be what's visible.
Easiest solution around this is to manually call .clear() the window object:
import pyglet
window = pyglet.window.Window(800, 600, "PyGlet Window")
def callback(dt):
print('%f seconds since last callback' % dt)
#window.event
def on_draw():
window.clear()
window.flip() # Not necessary, but probably doesn't hurt
pyglet.clock.schedule_interval(callback, 0.5) # called twice a second
pyglet.app.run()
Any time you cause an event, the draw function should be triggered, and if your on_draw contains window.clear() it should clear any old artifacts.

My pyglet application cant show other slides

I'm new to pyglet, but have needed to learn it quickly in order to complete a school project. Basically, I have my first slide with the images menu and preview on it, and a second slide that has the end_screen image on it. The showing of the first slide works just fine, though the program wont let me transition into the second slide when I press a key. No Error message, It just doesnt do anything when I try to draw the second slide.
import pyglet
from pyglet.window import key
import time
slide = 1
preview_image = pyglet.image.load('untitled (3).jpg')
menu_image = pyglet.image.load('untitled (4).jpg')
end_screen_image = pyglet.image.load('untitled (5).jpg')
preview = pyglet.sprite.Sprite(preview_image, x=0, y=0)
menu = pyglet.sprite.Sprite(menu_image, x=540, y=0)
end_screen = pyglet.sprite.Sprite(end_screen_image, x=270, y=0)
window = pyglet.window.Window(1180, 630)
def update():
global menu
global window
global preview
global end_screen
global slide
if slide == 1:
window.clear()
preview.draw()
menu.draw()
elif slide == 2:
window.clear()
end_screen.draw()
#window.event
def on_key_press(symbol, modifiers):
global slide
slide = 2
update()
#window.event
def on_draw():
window.clear()
preview.draw()
menu.draw()
pyglet.app.run()
For some reason it just wont work. I've tried different kinds of update functioons but none of them worked either. Thanks in advance!
Main issue here is the concept of update and how you'd normally expect code to execute.
I'm guessing you're used to code being called as you write it, meaning that you think "If I press a button, I want update() to be called instead". And that's true, partially.
The problem you're experiencing is that Pyglet is trying to render things as fast as possible, and it does so by calling on_draw() as often as needed. So when you execute update() - a fraction of a second later on_draw() will be called again, and again.. and again. And the first line you have in on_draw is window.clear() - which clears the window and any changes made in update(). Hopefully that makes sense.
Instead, you probably want your "which thing am i showing"-logic in the on_draw every render. Altho this will be pretty slow, hopefully this convey the logic of how the automatic rendering process works and why your logic from school assignments doesn't necessarily translate well into projects where there's "background tasks" running.
import pyglet
from pyglet.window import key
import time
preview = pyglet.sprite.Sprite(pyglet.image.load('untitled (3).jpg'), x=0, y=0)
menu = pyglet.sprite.Sprite(pyglet.image.load('untitled (4).jpg'), x=540, y=0)
end_screen = pyglet.sprite.Sprite(pyglet.image.load('untitled (5).jpg'), x=270, y=0)
window = pyglet.window.Window(1180, 630)
#window.event
def on_key_press(symbol, modifiers):
global slide
slide = 2
#window.event
def on_draw():
window.clear()
if slide == 1:
window.clear()
preview.draw()
menu.draw()
elif slide == 2:
window.clear()
end_screen.draw()
pyglet.app.run()
Oh and btw, no need for global on variables in functions like your update() function. Unless you assign something to variables (menu = ...) they're going to be global by default (For instance, see in on_key_press where you update slide with slide = 2, in those cases you need to globalize first). Just a friendly reminder how global scope works in Python and it's functions - giving you some more wiggle room to write less code and achieve the same thing. So remember, = means you need to make them global, otherwise don't bother :)
I also made the code a bit "smaller", since you're not using preview_image i went ahead and passed the image-loading-result straight into Sprite() for preview for instance, saving 3 lines of code making it arguably slightly less code and thus more readable. It's a matter of taste tho, can revert it if you feel like it.

Tkinter does not update while withdrawn

I have a program that will show an image on screen when a hotkey is pressed on my keyboard. Depending on which key was pressed a different image will be shown.
After 3 seconds of no input my root Tk gets withdraw()n so there is nothing on screen.
Now, whenever I want to show an image I call this function:
def drawOverlay(self, index):
self.canvas.itemconfig(self.overlayWidget, image=self.overlayImages[index])
self.deiconify()
The problem is that I see the old image for a few milliseconds before it is replaced.
I tried to find out why there is the delay eventhough I deiconify() only after i switched the image and came across this answer which suggested to call deiconify() using the root's after(). I tried that and when it still didn't work I played around with the call and found out that calling
self.after(1000, self.deiconify())
causes the old image to appear for exactly 1 second after which it is replaced with the new one.
This leads me to believe that the Tk or the Canvas is unable to update while it is withdraw()n. Is there any way around this? I'd rather have a short delay before the image is displayed than the old image flashing on the screen for a few frames but I have no idea how to accomplish this.
Edit: So I'm an idiot for actually calling deiconify instead of just passing it to after because of the parantheses.
That does fix the old image appearing for the duration but does not fix the flashing.
Edit2: I managed to reproduce this problem with the following code.
Pressing any key on the keyboard will make a green rectagle appear. Waiting for it to vanish and then pressing another key will make a red rectangle appear.
Only sometimes can you see the flashes happening so try for a couple times. I did not manage to reproduce when -transparentcolor isnt set but I can't tell if this is due to the option being the problem or due to the reduced rendering times making the problem almost imperceptable. It is also a bit easier to see then overrideredirect is set.
Edit3: I have worked the code down further by using actual images. Even without the keypress or additional event callbacks this code produces a black flash before displaing the image. -transparentcolor and overrideredirect(True) seem to be vital to reproducing this. Manually calling update() before reduces the frequency of this ocurring but still causes flashing consistently on larger images. This points to rendering time being one of the factors.
overlay.png
overlayRaw.png
from tkinter import Tk, Canvas
from PIL import ImageTk, Image
TRANSCOLOR = "blue"
IMAGE_SMALL = "overlay.png"
IMAGE_LARGE = "overlayRaw.png"
class Overlay(Tk):
def __init__(self):
Tk.__init__(self)
self.rawImage = Image.open(IMAGE_SMALL)
self.image = ImageTk.PhotoImage(self.rawImage)
self.canvas = Canvas(self, width = self.rawImage.size[0], height = self.rawImage.size[1])
self.canvas.pack()
self.wm_attributes('-transparentcolor', TRANSCOLOR) # If disabled stops the flashes from ocurring even on large images.
self.overrideredirect(True) # If disabled the Windows animation for opening windows plays. Stops the flashing from ocurring
self.withdraw()
self.overlayWidget = self.canvas.create_image(0, 0, image = self.image, anchor = "nw")
self.deiconify() # Flashes Clearly Everytime
## self.update_idletasks()
## self.deiconify() # Only Flashes Sometimes. Always flashes on large images
## self.update()
## self.deiconify() # Only Flashes Sometimes. Always flashes on large images
## self.after(0, self.deiconify) # Flashes Clearly everytime
## self.after(200, self.deiconify) # Only Flashes Sometimes. Always flashes on large images
## self.update()
## self.after(200, self.deiconify) # Flashes Clearly Everytime
o = Overlay()
o.mainloop()
I don't see much that could cause a flicker. My recommendation would be to add an explicit call to self.update before the call to deiconify happens. That should instruct tkinter to redraw everything on the canvas. However, that won't help if tkinter on your platform defers drawing until the window is mapped.
Try replacing this:
self.after(0,self.deiconify)
self.after(2000, self.hideOverlay)
with this:
self.update()
self.deiconify()

What is the most efficient way to display multiple pixmaps into a scroll area?

I am trying to make an application that displays a PDF file, using PyQt4 and python-poppler-qt4.
So far I have managed to display the entire document by loading pixmaps generated with Poppler, set on a QLabel and appended to a QFrame. The QFrame is displayed in a QScrollArea.
It looks pretty good, until implementing zooming, which is done by regenerating the pixmaps all over again, with an incremented resolution. This process requires the entire document to be rendered into pixmaps, which obviously takes time and results into an unwanted lag.
Logic wants that I should display images of the pages I am seeing only (it sounds like quantum physics). I have two options in mind:
create blank pages with QLabels and load the image onto them when they become visible in the scroll area;
create only one page and add or remove precedent or subsequent pages right before it should be displayed.
I am not sure I am on the right track or whether there is an alternative.
The first option seems more feasible, because the visibility of a blank page determines when the pixmap has to be uploaded (although I have no idea how to delete that pixmap when the page is hidden). Yet I am not sure that zooming will be faster this way, since a document of, say, 600 pages, will have to be regenerated, albeit with blank pages.
The second option should definitely improve zooming since 1 to 4 pages at a time would have to be regenerated when zooming. In that second case however, I am not sure how to trigger the construction of pages.
What would you suggest?
wouldn't it be simple to forget the QLabels and directly draw the image:
from PyQt4.QtGui import *
import sys
app = QApplication(sys.argv)
class Test(QWidget):
def __init__(self):
super(Test, self).__init__()
self.painter = QPainter()
# placeholder for the real stuff to draw
self.image = QImage("/tmp/test.jpg")
def paintEvent(self, evt):
rect = evt.rect()
evt.accept()
print rect
self.painter.begin(self)
zoomedImage = self.image # ... calculate this for your images
sourceRect = rect # ... caluclate this ...
# draw it directly
self.painter.drawImage(rect, self.image, sourceRect)
self.painter.end()
t = Test()
t.setGeometry(0,0,600,800)
s = QScrollArea()
s.setWidget(t)
s.setGeometry(0,0,300,400)
s.show()
app.exec_()
I've worked out an answer, using option 1 in the question:
def moveEvent(self, event):
self.checkVisibility()
event.ignore()
def resizeEvent(self, event):
self.checkVisibility()
event.ignore()
def checkVisibility(self):
print "Checking visibility"
for page in self.getPages():
if not page.visibleRegion().isEmpty():
if page.was_visible:
pass
else:
print page.page_number, "became visible"
page.was_visible = True
self.applyImageToPage(page)
else:
if page.was_visible:
print page.page_number, "became invisible"
page.was_visible = False
else:
pass
def applyImageToPage(self, page):
print "applying image to page", page.page_number
source = self.getSourcePage(self.getPageNumber(page))
scale = self.display.scale
# this is where the error occurs
image = source.renderToImage(72 * scale, 72 * scale)
pixmap = QtGui.QPixmap.fromImage(image)
page.setPixmap(pixmap)

Categories