I am trying to learn pyglet and practice some python coding with a questionnaire thing, but I am unable to find a way to make the background picture be removed or drawn on top of or something for 10 seconds. I am new and am lacking in a lot of the knowledge I would need, thank you for helping!
import pyglet
from pyglet.window import Window
from pyglet.window import key
from pyglet import image
import time
card1 = False
cat_image = pyglet.image.load("cat.png")
dog_image = pyglet.image.load("dog.png")
image = pyglet.image.load("backg.png")
background_sprite = pyglet.sprite.Sprite(image)
cat = pyglet.sprite.Sprite(cat_image)
dog = pyglet.sprite.Sprite(dog_image)
window = pyglet.window.Window(638, 404, "Life")
mouse_pos_x = 0
mouse_pos_y = 0
catmeme = pyglet.image.load("catmeme.png")
sprite_catmeme = pyglet.sprite.Sprite(catmeme)
#window.event
def on_draw():
window.clear()
background_sprite.draw()
card_draw1(63, 192, 385, 192)
def card1():
while time.time() < (time.time() + 10):
window.clear()
sprite_catmeme.draw()
#window.event
def card_draw1(x1, y1, x2, y2):
cat.set_position(x1, y1)
dog.set_position(x2, y2)
cat.draw()
dog.draw()
def card_draw2():
pass
#window.event
def on_mouse_press(x, y, button, modifiers):
if x > cat.x and x < (cat.x + cat.width):
if y > cat.y and y < (cat.y + cat.height):
card1()
game = True
while game:
on_draw()
pyglet.app.run()
There's a few flaws in the order and in which you do things.
I will try my best to describe them and give you a piece of code that might work better for what your need is.
I also think your description of the problem is a bit of an XY Problem which is quite common when asking for help on complex matters where you think you're close to a solution, so you're asking for help on the solution you've come up with and not the problem.
I'm assuming you want to show a "Splash screen" for 10 seconds, which happens to be your background? And then present the cat.png and dog.png ontop of it, correct?
If that's the case, here's where you probably need to change things in order for it to work:
The draw() function
It doesn't really update the screen much, it simply adds things to the graphical memory. What updates the screen is you or something telling the graphics library that you're done adding things to the screen and it's time to update everything you've .draw()'n. So the last thing you need in the loop would be window.flip() in order for the things you've drawn to actually show.
Your things might show if you try to wiggle the window around, it should trigger a re-draw of the scene because of how the internal mechanics of pyglet work..
If you don't call .flip() - odds are probable that the redraw() call will never occur - which again, is a internal mechanism of Pyglet/GL that tells the graphics card that something has been updated, we're done updating and it's time to redraw the scene.
a scene
This is the word most commonly used for what the user is seeing.
I'll probably throw this around a lot in my text, so it's good to know that this is what the user is seeing, not what you've .draw()'n or what's been deleted, it's the last current rendering of the graphics card to the monitor.
But because of how graphical buffers work we've might have removed or added content to the memory without actually drawing it yet. Keep this in mind.
The pyglet.app.run() call
This is a never ending loop in itself, so having that in a while game: loop doesn't really make sense because .run() will "hang" your entire application, any code you want to execute needs to be in def on_draw or an event that is generated from within the graphical code itself.
To better understand this, have a look at my code, i've pasted it around a couple of times here on SO over the years and it's a basic model of two custom classes that inherits the behavior of Pyglet but lets you design your own classes to behave slightly differently.
And most of the functionality is under on_??? functions, which is almost always a function used to catch Events. Pyglet has a lot of these built in, and we're going to override them with our own (but the names must be the same)
import pyglet
from pyglet.gl import *
key = pyglet.window.key
class CustomSprite(pyglet.sprite.Sprite):
def __init__(self, texture_file, x=0, y=0):
## Must load the texture as a image resource before initializing class:Sprite()
self.texture = pyglet.image.load(texture_file)
super(CustomSprite, self).__init__(self.texture)
self.x = x
self.y = y
def _draw(self):
self.draw()
class MainScreen(pyglet.window.Window):
def __init__ (self):
super(MainScreen, self).__init__(800, 600, fullscreen = False)
self.x, self.y = 0, 0
self.bg = CustomSprite('bg.jpg')
self.sprites = {}
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
elif symbol == key.C:
print('Rendering cat')
self.sprites['cat'] = CustomSprite('cat.png', x=10, y=10)
elif symbol == key.D:
self.sprites['dog'] = CustomSprite('dog.png', x=100, y=100)
def render(self):
self.clear()
self.bg.draw()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj._draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
x = MainScreen()
x.run()
Now, this code is kept simple on purpose, the full code I usually paste on SO can be found at Torxed/PygletGui, the gui.py is where most of this comes from and it's the main loop.
What I do here is simply replace the Decorators by using "actual" functions inside a class. The class itself inherits the functions from a traditional pyglet.window.Window, and as soon as you name the functions the same as the inherited onces, you replace the core functionality of Window() with whatever you decide.. In this case, i mimic the same functions but add a few of my own.
on_key_press
One such example is on_key_press(), which normally just contain a pass call and does nothing, here, we check if key.C is pressed, and if so - we add a item to self.sprites.. self.sprites just so happen to be in our render() loop, anything in there will be rendered ontop of a background.
Here's the pictures I used:
(named bg.jpg, cat.png, dog.png - note the different file endings)
class:CustomSprite
CustomSprite is a very simple class designed to make your life easier at this point, nothing else. It's very limited in functionality but the little it do is awesome.
It's soul purpose is to take a file name, load it as an image and you can treat the object like a traditional pyglet.sprite.Sprite, meaning you can move it around and manipulate it in many ways.
It saves a few lines of code having to load all the images you need and as you can see in gui_classes_generic.py you can add a heap of functions that's "invisible" and normally not readily availbale to a normal sprite class.
I use this a bunch! But the code gets complicated real fast so I kept this post simple on purpose.
the flip function
Even in my class, I still need to use flip() in order to update the contents of the screen. This is because .clear() clears the window as you would expect, that also triggers a redraw of the scene.
bg.draw() might in some cases trigger a redraw if the data is big enough or if something else happens, for instance you move the window.
but calling .flip() will tell the GL backend to force a redraw.
Further optimizations
There's a thing called batched rendering, basically the graphic card is designed to take enormous ammounts of data and render it in one go, so calling .draw() on several items will only clog the CPU before the GPU even gets a chance to shine. Read more about Batched rendering and graphics! It will save you a lot of frame rates.
Another thing is to keep as little functionality as possible in the render() loop and use the event triggers as your main source of coding style.
Pyglet does a good job of being fast, especially if you only do things on event driven tasks.
Try to avoid timers, but if you really do need to use time for things, such as removing cat.png after a certain ammount of time, use the clock/time event to call a function that removes the cat. Do not try to use your own t = time() style of code unless you know where you're putting it and why. There's a good timer, I rarely use it.. But you should if you're starting off.
This has been one hell of a wall of text, I hope it educated you some what in the life of graphics and stuff. Keep going, it's a hurdle to get into this kind of stuff but it's quite rewarding once you've mastered it (I still haven't) :)
Related
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.
I am trying to make a text based rpg game in python. I just started and this is what my code looks so far:
class Game:
def __init__(self):
self.map = [[" " for i in range(50)]for i in range(30)]
def draw_map(self):
for i in range(len(self.map)):
print(self.map[i])
def add_stuff(self, thing, x, y):
self.map[y][x] = thing
game = Game()
class Player:
def __init__(self):
self.x = 1
self.y = 1
self.player = "ME"
def draw(self):
game.add_stuff(self.player, self.x, self.y)
def move(self):
pass
player = Player()
player.draw()
game.draw_map()
I was trying to find a way to implement a game loop in some way. To do this i was thinking of running the draw_map() and clearing the screen right away and printing the map again, a bit like real game loops. However i am having problems doing this. Based on other answers to other similar questions, i managed to produce the following code(it just shows the main loop and subprocess is imported as sp).
while True:
game.draw_map()
sp.call("cls", shell = True)
However this is not working for me. It simply dosent do anything. I also tried using clear function from clear_screen` module in a similar way and i cant figure out why this wouldnt work. Thanks for any help
so based on your previous comments you want to clear the screen in a Python interpreter. There is no "command" to clear the screen just like this. But you can use a loop to print some new lines until your screen is blank again.
def clear(lines):
for l in range(lines):
print()
while True:
game.draw_map()
clear(35)
time.sleep(0.05)
You may need to increase or decrease the amount of lines cleared. I hope this works for you. P.S.: I would use a normal console.
https://en.wikipedia.org/wiki/Interpreter_(computing)#Efficiency:
Efficiency:
The main disadvantage of interpreters is that an interpreted program typically runs slower than if it had been compiled. The difference in speeds could be tiny or great; often an order of magnitude and sometimes more. It generally takes longer to run a program under an interpreter than to run the compiled code but it can take less time to interpret it than the total time required to compile and run it. This is especially important when prototyping and testing code when an edit-interpret-debug cycle can often be much shorter than an edit-compile-run-debug cycle.
If your problem is clearing the console window, you can just use
import os
os.system('cls')
in place of
sp.call("cls", shell = True)
(Assuming you are working on windows)
EDIT for clarity:
This is the new loop after the change:
while True:
game.draw_map()
os.system('cls')
I have implemented a sync block which plots inside its work function using the input_items values. Now the problem is that the plotting mechanism isn't fast enough for the input stream ( the value of input_items keeps on changing ).
I have tried to simplify the code as much as possible and added comments. Here it is:
....
import matplotlib
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
temp = ''
class xyz(gr.sync_block):
def __init__(self,parent,title,order):
a = []
self.count = 1
gr.sync_block.__init__(self,
name="xyz",
in_sig=[numpy.float32,numpy.float32],
out_sig=None)
self.win = xyzPlot(parent) #I have a Top Block(wxFrame). I am just making it the parent of xyzPlot(wxPanel) here.
def work(self, input_items, output_items):
global temp
if (self.count == 1):
temp = input_items+list()
bool = not(np.allclose(temp,input_items))#bool will be true if the value of `input_items` changes.
.......
#the code that evaluates z1,z2 depending on the value of input_items
.......
if ( bool or self.count == 1 ):
#If bool is true or it is the first time then I am calling plot() which plots the graph.
self.win.plot(tf(self.z1,self.z3),None,True,True,True,True)
self.count = 0
temp = input_items+list()
return len(input_items[0])
class xyzPlot(wx.Panel):
def __init__(self, parent, dB=None, Hz=None, deg=None):
wx.Panel.__init__(self , parent , -1 ,size=(600,475))
self.fig = Figure()
self.axes = self.fig.add_subplot(111)
def plot(self, syslist, omega=None, dB=None, Hz=None, deg=None, Plot=True, *args , **kwargs):
self.axes.clear() #I clear the graph here so that new values can be plotted.
.....
self.axes.semilogx(omega,mag,*args,**kwargs)
self.canvas = FigCanvas(self, -1, self.fig)
self.canvas.draw()
As you can see I am working with wxPython but the panel freezes whenever I change the value of input_items too fast ( It works fine if I change it slowly ). Any recommendations? I am new to this.
To cite another answer I gave:
This will quickly also get a multithreading problem. To be clear: What
you're trying to do (call a plotting function from a block thread) is
problematic and usually won't work.
The problem is that you're working in a complex multithreading environment:
each GNU Radio block works in its own thread
The WX Gui main loop runs continously to update the screen.
What you're doing here is, from a GNU Radio block thread, change what is shown in the window. That is a bad thing, because it changes things that are in the context of the WX Gui thread. This can work, if these changes don't conflict, and if the WX Gui thread doesn't access this kind of data while you're changing it (at some point, it has to access it -- otherwise, noone will update your window).
This is a problem common to all kind of updated GUIs, not only to GNU Radio!
Whether or not that happens is a mere case of probability: With a slowly updated display, your probability of conflict is low, but when you update often, it approaches 1.
The existing visualizations are written in C++ and take very great care to do things the right way -- which is, letting your Gui toolkit (WX in your case, though I explicitely recommend, and have recommended, to move away from that) know that things need to be updated, and then offering WX a function to update the display in its own thread.
I'm making a Pong clone for learning purposes, and need to get the ball moving from the middle of the screen (it's sent there when it goes past a paddle) when the mouse is pressed. I've tried the following code, but it does nothing, so I probably don't understand the syntax. Try to keep it as simple as possible please, and explain it, I'd rather not have 50 lines of code for this (I want to understand everything I'm using here). I think this is all the relevant code, sorry if it isn't. Thanks.
def middle(self):
"""Restart the ball in the centre, waiting for mouse click. """
# puts ball stationary in the middle of the screen
self.x = games.screen.width/2
self.y = games.screen.height/2
self.dy = 0
self.dx = 0
# moves the ball if mouse is pressed
if games.mouse.is_pressed(1):
self.dx = -3
It's impossible to know exactly what's happening based on that code fragment, but it looks like you are using the wrong function to detect whether or not the mouse button is pressed.
Screen.is_pressed from the games module wraps pygame.key.get_pressed, which only detects the state of keyboard keys, not mouse buttons. You probably want the function Screen.mouse_buttons, which wraps pygame.mouse.get_pressed. You could use it within a loop like this (I'll pretend you have an instance of games.Screen called 'screen'):
left, middle, right = screen.mouse_buttons()
# value will be True if button is pressed
if left:
self.dx = -3
I am looking at the same issue as a beginner Python coder - Games.py (revision 1.7) includes several is_pressed methods in various classes, including both keyboard and mouse.
class Mouse(object):
#other stuff then
def is_pressed(self, button_number):
return pygame.mouse.get_pressed()[button_number] == 1
since pygame is a compiled module (I have 1.9.1) referring to the documentation rather than source code, I find here that there is a pygame.mouse.get_pressed():
the will get the state of the mouse buttons
get_pressed() -> (button1, button2, button3)
So I think the issue is the use of this in (y)our code rather than the use of the wrong function.....
OK GOT THIS TO WORK - MY FIX:
class myClass(games.Sprite):
def update(self):
if games.mouse.is_pressed(0)==1:
self.x=games.mouse.x
self.y=games.mouse.y
invoking the in Main() causes the selected sprite to move to the mouse location. HTH
people
I have seen some tutorials showing how to create animations with PyGtk, the best one being this:
https://cairographics.org/cookbook/animationrotation/
This tutorial uses gobject.timeout_add() to set some clock to constantly refresh the animation.
What I wanted to do, instead, is to create FINITE animations, which I could trigger by clicking in some button or checkbox or anything.
For example, I could have a Window with a Button and a DrawingArea with a ball drawn.
When I click the button, the ball would go up and down, and stop. If I click the button again, the ball repeats the move.
In another scenario, the ball could be on the left of the screen. When I toggle a CheckBox, the ball goes (not instantly, but rather moves in a transition) to the right. If I uncheck, the ball comes back to its original position.
I am not planning to use anything else than pure Cairo/Gtk (no Clutter, no OpenGL, no PyGame), because I feel that it should be possible, and I want to go on studying Gtk to do some (future) GUI tricks with simple one-file scripts.
Since the question is about a broader problem, I think it is not needed to add code, but I could edit this question to put some code if anyone feels it would be better.
Thanks for reading and for any help! And Cairo/Gtk is great!
EDIT: after the precise explanation from Jeremy Flores, I came out with this code, which is useless (the ball goes to the right each time the button is clicked, untill falling off the window), but contains the elements upon which to build more creative and useful stuff. If anyone (including Jeremy) has anything to say about this code, specially about removing unnecessary parts, I would very gladly like to hear. Thanks!
#!/usr/bin/env python
import gtk, gobject
from math import pi
class Canvas(gtk.DrawingArea):
def __init__(self):
super(Canvas, self).__init__()
self.connect("expose_event", self.expose)
self.set_size_request(400,400)
self.x = 20
self.y = 20
self.counter = 0
def movealittle(self):
self.x += 1
self.counter += 1
self.queue_draw()
if self.counter <= 20:
return True
elif self.counter > 20:
self.counter = 0
return False
def expose(self, widget, event):
cr = widget.window.cairo_create()
rect = self.get_allocation()
w = rect.width
h = rect.height
cr.arc(self.x, self.y, 10, 0, 2*pi)
cr.fill()
def runanimation(widget):
gobject.timeout_add(5, canvas.movealittle)
print "runanimation call"
button = gtk.Button("Move")
button.connect("clicked", runanimation)
window = gtk.Window()
canvas = Canvas()
panel = gtk.VBox()
window.add(panel)
panel.pack_start(canvas)
panel.pack_start(button)
window.set_position(gtk.WIN_POS_CENTER)
window.show_all()
gtk.main()
gobject.timeout_add() can be used for finite animations. The callback you set up will keep being called until it returns False, at which point your method won't be called again.
For example, if you want a ball to be animated for 5 seconds, you would be responsible for determining how long the animation has been going, and then once it has passed 5 seconds, your method would return False to end the animation. When you want the animation to start again, simply re-register the callback with timeout_add and reset your time counter.
See: http://www.pygtk.org/pygtk2reference/gobject-functions.html#function-gobject--timeout-add for more information.