I'm aware that there are literally hundred of examples out there for this task, though I don't manage to apply those examples to my specific problem. As you can see on the code below I am trying to draw a polygon on a bitmap "self.image". This code works absolutely fine on MS Windows. On Linux this code will not draw my polygon.
I tried to play around with different "dcs" like MemoryDC according to this How to draw text in a bitmap using wxpython?
but the result was the same.
My questions are:
Why does my code fail on Linux? Why does this work on MS Windows? (a bit off-topic) Why are people often exclusively drawing in the PaintDC in the OnPaint method bound to EVT_PAINT?
class attributes:
self.dc = wx.ClientDC(self.image)
self.dc.SetPen(wx.Pen(colour='red', width=4, style=wx.SOLID))
self.polygon = list()
this method is being called when I want to start drawing:
def start_drawing(self):
self.image.Bind(event=wx.EVT_LEFT_DOWN, handler=self.draw_polygon)
self.dc.BeginDrawing()
this method handles the binding from above:
def draw_polygon(self, event):
self.polygon.append(event.GetPositionTuple())
if len(self.polygon) > 1:
self.dc.DrawLines(points=self.polygon)
I fixed the bug myself.
The problem was actually not visible in the code I've been providing.
I have a method which changes self.image before any drawing is made. Thus the image which I assign to the ClientDC in the class body is not the same image than self.image when I begin to draw. Adding self.dc = wx.ClientDC(self.image) in the image setter solved my problem.
I don't understand why this code worked when being executed on MS Windows. This should not have never worked in the first place.
Related
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) :)
I am new to Python and have been working with the turtle module as a way of learning the language.
Thanks to stackoverflow, I researched and learned how to copy the image into an encapsulated postscript file and it works great. There is one problem, however. The turtle module allows background color which shows on the screen but does not show in the .eps file. All other colors, i.e. pen color and turtle color, make it through but not the background color.
As a matter of interest, I do not believe the import of Tkinter is necessary since I do not believe I am using any of the Tkinter module here. I included it as a part of trying to diagnose the problem. I had also used bgcolor=Orange rather than the s.bgcolor="orange".
No Joy.
I am including a simple code example:
# Python 2.7.3 on a Mac
import turtle
from Tkinter import *
s=turtle.Screen()
s.bgcolor("orange")
bob = turtle.Turtle()
bob.circle(250)
ts=bob.getscreen()
ts.getcanvas().postscript(file = "turtle.eps")
I tried to post the images of the screen and the .eps file but stackoverflow will not allow me to do so as a new user. Some sort of spam prevention. Simple enough to visualize though, screen has background color of orange and the eps file is white.
I would appreciate any ideas.
Postscript was designed for making marks on some medium like paper or film, not raster graphics. As such it doesn't have a background color per se that can be set to given color because that would normally be the color of the paper or unexposed film being used.
In order to simulate this you need to draw a rectangle the size of the canvas and fill it with the color you want as the background. I didn't see anything in the turtle module to query the canvas object returned by getcanvas() and the only alternative I can think of is to read the turtle.cfg file if there is one, or just hardcode the default 300x400 size. You might be able to look at the source and figure out where the dimensions of the current canvas are stored and access them directly.
Update:
I was just playing around in the Python console with the turtle module and discovered that what the canvas getcanvas() returns has a private attribute called _canvas which is a <Tkinter.Canvas instance>. This object has winfo_width() and winfo_height() methods which seem to contain the dimensions of the current turtle graphics window. So I would try drawing a filled rectangle of that size and see if that gives you what you want.
Update 2:
Here's code showing how to do what I suggested. Note: The background must be drawn before any other graphics are because otherwise the solid filled background rectangle created will cover up everything else on the screen.
Also, the added draw_background() function makes an effort to save and later restore the graphics state to what it was. This may not be necessary depending on your exact usage case.
import turtle
def draw_background(a_turtle):
""" Draw a background rectangle. """
ts = a_turtle.getscreen()
canvas = ts.getcanvas()
height = ts.getcanvas()._canvas.winfo_height()
width = ts.getcanvas()._canvas.winfo_width()
turtleheading = a_turtle.heading()
turtlespeed = a_turtle.speed()
penposn = a_turtle.position()
penstate = a_turtle.pen()
a_turtle.penup()
a_turtle.speed(0) # fastest
a_turtle.goto(-width/2-2, -height/2+3)
a_turtle.fillcolor(turtle.Screen().bgcolor())
a_turtle.begin_fill()
a_turtle.setheading(0)
a_turtle.forward(width)
a_turtle.setheading(90)
a_turtle.forward(height)
a_turtle.setheading(180)
a_turtle.forward(width)
a_turtle.setheading(270)
a_turtle.forward(height)
a_turtle.end_fill()
a_turtle.penup()
a_turtle.setposition(*penposn)
a_turtle.pen(penstate)
a_turtle.setheading(turtleheading)
a_turtle.speed(turtlespeed)
s = turtle.Screen()
s.bgcolor("orange")
bob = turtle.Turtle()
draw_background(bob)
ts = bob.getscreen()
canvas = ts.getcanvas()
bob.circle(250)
canvas.postscript(file="turtle.eps")
s.exitonclick() # optional
And here's the actual output produced (rendered onscreen via Photoshop):
I haven't found a way to get the canvas background colour on the generated (Encapsulated) PostScript file (I suspect it isn't possible). You can however fill your circle with a colour, and then use Canvas.postscript(colormode='color') as suggested by #mgilson:
import turtle
bob = turtle.Turtle()
bob.fillcolor('orange')
bob.begin_fill()
bob.circle(250)
bob.begin_fill()
ts = bob.getscreen()
ts.getcanvas().postscript(file='turtle.eps', colormode='color')
Improving #martineau's code after a decade
import turtle as t
Screen=t.Screen()
Canvas=Screen.getcanvas()
Width, Height = Canvas.winfo_width(), Canvas.winfo_height()
HalfWidth, HalfHeight = Width//2, Height//2
Background = t.Turtle()
Background.ht()
Background.speed(0)
def BackgroundColour(Colour:str="white"):
Background.clear() # Prevents accumulation of layers
Background.penup()
Background.goto(-HalfWidth,-HalfHeight)
Background.color(Colour)
Background.begin_fill()
Background.goto(HalfWidth,-HalfHeight)
Background.goto(HalfWidth,HalfHeight)
Background.goto(-HalfWidth,HalfHeight)
Background.goto(-HalfWidth,-HalfHeight)
Background.end_fill()
Background.penup()
Background.home()
BackgroundColour("orange")
Bob=t.Turtle()
Bob.circle(250)
Canvas.postscript(file="turtle.eps")
This depends on what a person is trying to accomplish but generally, having the option to select which turtle to use to draw your background to me is unnecessary and can overcomplicate things so what one can do instead is have one specific turtle (which I named Background) to just update the background when desired.
Plus, rather than directing the turtle object via magnitude and direction with setheading() and forward(), its cleaner (and maybe faster) to simply give the direct coordinates of where the turtle should go.
Also for any newcomers: Keeping all of the constants like Canvas, Width, and Height outside the BackgroundColour() function speeds up your code since your computer doesn't have to recalculate or refetch any values every time the function is called.
OK, so iv almost completed my program for my project but I cant get a BUTTON_EVT to work which if i am honest should be the easiest thing todo. I have the buttons on my program which represent the hardware and I have created a def function for them to appear on the OGL canvas.
Problem has been solved... The code associated with the problem is found in the answer below
Edited from your last comment. Use this (using your own images):
def OnClickRouter(self, event):
image=wx.Image('cat.jpg', wx.BITMAP_TYPE_JPEG)
self.frame = bucky(None, image)
self.frame.Show()
If you call bucky() this way you must also fix the class signature:
class bucky(wx.Frame):
# Creating the outer window/frame
def __init__(self, parent, image=None):
wx.Frame.__init__(self, parent, -1,'Karls Network Tool', size=(900,700))
my_image = image if image else wx.Image("myself.bmp", wx.BITMAP_TYPE_BMP)
''''''''''''''''''''''''''''''''
# Button images
buttonOneRouter = my_image.ConvertToBitmap()
self.buttonOneRouter = wx.BitmapButton(panel, -1, buttonOneRouter, pos=(20,340))
self.buttonOneRouter.Bind(wx.EVT_BUTTON, self.OnClickRouter)
''''''''''''''''''''''''''''''''
Then you can see that after clicking the buttonOnerouter what actually you are doing is opening a new frame. The left figure is what I get when I run the program, the right one is after I click and enter again my name (I simplified a bit your code. Thats why you only see one button at the bottom instead of 4):
If you want to put my cat in the canvas instead of in the button there is still some work to do. I recommend to you to give a look at the wxPython demo. In the miscellaneous group of examples you have one called OGL that shows how to do that.
Edit: You can download the wxPython docs and demos package from here
I don't know if this is right or not but I suggest you to take this approach and see if it works or not.
Modify your frame class as:
def __init(self,parent,id,img=None)
def onClickRouter(self,event):
image=wx.Image('router.jpg', wx.BITMAP_TYPE_JPEG)
temp = image.ConvertToBitmap()
self.bmp = wx.StaticBitmap(parent=self, bitmap=temp)
self.frame=bucky(self.bmp)
Please let know the outcome.
I'm working on a window manager written using python's xlib bindings and I'm (initially) attempting to mimic dwm's behavior in a more pythonic way. I've gotten much of what I need, but I'm having trouble using X's built in window border functionality to indicate window focus.
Assuming I've got an instance of Xlib's window class and that I'm reading the documentation correctly, this should do what I want to do (at least for now) - set the window border of a preexisting window to a garish color and set the border width to 2px.
def set_active_border(self, window):
border_color = self.colormap.alloc_named_color(\
"#ff00ff").pixel
window.change_attributes(None,border_pixel=border_color,
border_width = 2 )
self.dpy.sync()
However, I get nothing from this - I can add print statements to prove that my program is indeed running the callback function that I associated with the event, but I get absolutely no color change on the border. Can anyone identify what exactly I'm missing here? I can pastebin a more complete example, if it will help. I'm not exactly sure it will though as this is the only bit that handles the border.
Looks like this was complete PEBKAC. I've found an answer. Basically, I was doing this:
def set_active_border(self, window):
border_color = self.colormap.alloc_named_color(
"#ff00ff"
).pixel
window.configure(border_width=2)
window.change_attributes(
None,
border_pixel=border_color,
border_width=2)
self.dpy.sync()
Apparently this was confusing X enough that it was doing nothing. The solution that I've stumbled upon was to remove the border_width portion from the window.change_attributes() call, like so:
def set_active_border(self, window):
border_color = self.colormap.alloc_named_color(
"#ff00ff"
).pixel
window.configure(border_width=2)
window.change_attributes(
None,
border_pixel=border_color
)
self.dpy.sync()
I hope this helps someone later on down the road!
This is just my second day at pygame, and I am not much aware about many functions. The following is a sample of what I am doing on my surface(window)
for c in nStations:
pygame.time.wait( 20 )
pygame.draw.circle( window, c.tColor, c.tPosition, c.iRadius )
pygame.display.update()
This is looped for n number of circles(in a tuple) to be displayed. Now, this will be repeated a total of 5-6 times. I wish to store/save the surface window as an image(or sub-surface) and put it as a thumbnail/link at the top-right-corner of my window.
Is it possible? Or is pygame not a good library for this? I tried working on cocos2d too, but it's documentation is very poor as compared to pygame.
In case my question didn't make any-sense anywhere, please reply. I'll keep updating it.
P.S. I've seen python.surface library functions. and surface.copy seems to be what I am looking for, but can I store the surface as a thumbnail/image before copying and editing it further?
EDIT->After reading jsbueno's reply, what I did:
tThumbs.append( pygame.transform.smoothscale( window, (32, 32) ) )
# the above statement inside drawing-circle loop
i = 1
for thumb in tThumbs:
window.blit( thumb, (1050, 36*i) )
pygame.display.update()
i += 1
pygame.time.wait( 200 )
The above seems to work perfectly for me. Thanks. :)
Indeed - all drawing and transofmr functions in Pygame accept a "surface" as the first parameter. Actually, in pygame, a "surface" is an image.
The display window is just a surface as well - although a specialized one, whose contents are reflected on screen. But you can use pygame.transform.smoothscale to get a thumbnail copy of your screen surface, and then, the blit method of your screen object itself to paste it on screen.
If this is going to be done continuoulsy, the pasted corner ould also be replicated, like a video camera pointing to its own output - so the "right way" to do it is: keep yoru drawing ina separate, in memory surface, blit it to the screen surface, and then blit the thumbnail above it.