This question already has answers here:
How can I create a text input box with Pygame?
(5 answers)
Closed 3 years ago.
All I need right now is basic text fields and buttons for input in pygames. A text field as some simple structure that I can read into variables, and a button to call a function.
First, I browsed around and found it was not a straight-forward process to create a text field in pygames. Eventually, by cobbling together this wall of code, I became the proud father of one, somewhat broken text field. Then I looked into buttons, and found to some horror that implementing them is even more complicated.
Surely PyGame has some sort of buttons module built in right? No.
Excuse me? I'm not trying to write a whole program just for one button, I just need the interface for a deeper simulation. That's all.
So then I looked into Tkinter, which has very easy-to-understand commands for GUI input. But no, that was also not meant to be.
I don't believe you can embed tkinter in to pygame.
So then I tried PGU, but found a stunning lack of any straight-forward examples of how to actually use it for what I need (simple text fields and buttons). When I tried looking for one, I found this piece of wisdom.
Are there any good, modern widget toolkits for Pygame? No. Every year someone makes a new Pygame UI library and then abandons it after a few versions
So if that was true, how is anyone supposed to get anything done with this language? What exactly is the best practice for a simple textfield and simple button in a pygame environment?
yes - pygame is barebones "we control the pixels" - and has been around a lot of time, and itself looked like abandoned for a lot of time (it was just in late 2016 that it would install clean on Python 3, for example). So, in the meantime pygame looked abandoned people moved away.
For the time being, you have to try your luck mining a usable toolkit from here:
https://www.pygame.org/wiki/gui
The PGU toolkit listed there, as you noted, seems quite complete - bar examples on how to build simple widgets to interact with an existing Pygame codebase. All the examples assume one wants to just have an stand alne gui application for form filling, which, I agree, is quite inapropriate: if one wants this kind of application, just use Tkiner, Qt or create a web app.
It turns out, however, that using a standalone widget in an existing pygame window is quite simple. Despite all the library examples calling the .run method, which is the spoiler part, since nce you call run the PGU app takes over and there is no way to run your code back, save for callbacks. However, the run method itself consist of a very simple infinite loop, calling the app's loop method. You just have to put a call to app.loop in all frames of your own pygame loop and have the widgets working.
It will swallow pygame events, however - if you want to treat event while you are displaying widgets, you should dispatch all events to app.event and call app.update instead of calling app.loop. It is trivial once you look at the source code for the app class at https://github.com/parogers/pgu/blob/master/pgu/gui/app.py
So, a complete example for using a text-input can be:
import pygame
from pgu import gui
SIZE = W, H = 800, 600
def bouncing_rect(screen):
surf = pygame.Surface((50, 50))
rect = surf.get_rect()
surf.fill((255, 0, 0))
vx, vy = 10, 10
while True:
screen.blit(surf, rect)
new_surface = yield rect
if new_surface:
surf = new_surface
rect.width = surf.get_width()
rect.height = surf.get_height()
rect.x += vx
rect.y += vy
if rect.right > W or rect.left < 0:
vx = -vx
rect.x += vx * 2
if rect.bottom > H or rect.top < 0:
vy = -vy
rect.y += vy * 2
def main():
screen = pygame.display.set_mode((SIZE))
app = gui.Desktop()
txt = gui.Input()
app.init(widget=txt, screen=screen, area=pygame.Rect(50, 50, 200,25))
bouncing = bouncing_rect(screen)
previous_value = ""
font = pygame.font.SysFont(name="Sans", size=30)
while True:
screen.fill((0,0,0))
rect = next(bouncing)
app.loop()
if txt.value != previous_value:
previous_value = txt.value
rect = bouncing.send(font.render(txt.value, True, (255, 255, 0)))
pygame.display.update([rect])
app.repaint()
pygame.display.flip()
pygame.time.delay(30)
try:
main()
finally:
pygame.quit()
(I just made the bouncing object as generator function using the "send" method, since there are very few examples of this pattern, but it could be a class just the same - if it were a common pygame.Sprite child class, I'd just call "update" instead of using next/send. )
Related
This question already has answers here:
Making the background move sideways in pygame
(2 answers)
How to scroll the background surface in PyGame?
(1 answer)
How to move the background image with keys in pygame?
(1 answer)
Pygame : Two layered scrolling background, can you help me?
(1 answer)
Closed 2 years ago.
I'm trying to convert a simple game written in pygame in pyglet.
I almost figure out everything, but now I'm facing this situation,
I have a background sprite like this example:
In my pygame code I use to write this to let it scrolls smoothly in the background:
base_shift = self.graphic.IMAGES['base'].get_width() - self.COMMON_CONST['screenWidth']
while True:
base_x = -((-base_x + 3) % base_shift)
self.screen.blit(self.graphic.IMAGES['base'], (base_x, y))
Basically the image is longer than the actual screen and once it passes the delta value between image X and screen X, the image is set to 0 again.
I'm trying to do the same in pyglet inside the def update(self, dt): function but I have no idea exactly how to do it. I mean I understand the meaning of dt but still.. To move the image now I do something like this:
def update(self, dt):
self.base.x -= 15 * dt
Of course this will continue to move and it does not reset his position.
How can I replicate the behavior that I have done in pygame?
thanks
UPDATE
I try this code:
def update(dt):
self.base.x = -((-(self.base.x - 15 * dt)) % base_shift)
It seems to work but it's not smooth when is back to 0 is not perfect aligned.
UPDATE/2 TEMPORARY SOLUTION
I finally went to another completely different solution. I split the image in a single pattern something like this:
and then I repeat it many times to fill the screen width plus 2. Then I move all of them and when the first goes completely out of the screen size, I remove from the array and add a new one.
I don't know if there is a better solution so far is ok.
I've created a simple application to display a scatterplot of data using Tkinter's Canvas widget (see the simple example below). After plotting 10,000 data points, the application becomes very laggy, which can be seen by trying to change the size of the window.
I realize that each item added to the Canvas is an object, so there may be some performance issues at some point, however, I expected that level to be much higher than 10,000 simple oval objects. Further, I could accept some delays when drawing the points or interacting with them, but after they are drawn, why would just resizing the window be so slow?
After reading effbot's performance issues with the Canvas widget it seems there may be some unneeded continuous idle tasks during resizing that need to be ignored:
The Canvas widget implements a straight-forward damage/repair display
model. Changes to the canvas, and external events such as Expose, are
all treated as “damage” to the screen. The widget maintains a dirty
rectangle to keep track of the damaged area.
When the first damage event arrives, the canvas registers an idle task
(using after_idle) which is used to “repair” the canvas when the
program gets back to the Tkinter main loop. You can force updates by
calling the update_idletasks method.
So, the question is whether there is any way to use update_idletasks to make the application more responsive once the data has been plotted? If so, how?
Below is the simplest working example. Try resizing the window after it loads to see how laggy the application becomes.
Update
I originally observed this problem in Mac OS X (Mavericks), where I get a substantial spike in CPU usage when just resizing the window. Prompted by Ramchandra's comments I've tested this in Ubuntu and this doesn't seem to occur. Perhaps this is a Mac Python/Tk problem? Wouldn't be the first I've run into, see my other question:
PNG display in PIL broken on OS X Mavericks?
Could someone also try in Windows (I don't have access to a Windows box)?
I may try running on the Mac with my own compiled version of Python and see if the problem persists.
Minimal working example:
import Tkinter
import random
LABEL_FONT = ('Arial', 16)
class Application(Tkinter.Frame):
def __init__(self, master, width, height):
Tkinter.Frame.__init__(self, master)
self.master.minsize(width=width, height=height)
self.master.config()
self.pack(
anchor=Tkinter.NW,
fill=Tkinter.NONE,
expand=Tkinter.FALSE
)
self.main_frame = Tkinter.Frame(self.master)
self.main_frame.pack(
anchor=Tkinter.NW,
fill=Tkinter.NONE,
expand=Tkinter.FALSE
)
self.plot = Tkinter.Canvas(
self.main_frame,
relief=Tkinter.RAISED,
width=512,
height=512,
borderwidth=1
)
self.plot.pack(
anchor=Tkinter.NW,
fill=Tkinter.NONE,
expand=Tkinter.FALSE
)
self.radius = 2
self._draw_plot()
def _draw_plot(self):
# Axes lines
self.plot.create_line(75, 425, 425, 425, width=2)
self.plot.create_line(75, 425, 75, 75, width=2)
# Axes labels
for i in range(11):
x = 75 + i*35
y = x
self.plot.create_line(x, 425, x, 430, width=2)
self.plot.create_line(75, y, 70, y, width=2)
self.plot.create_text(
x, 430,
text='{}'.format((10*i)),
anchor=Tkinter.N,
font=LABEL_FONT
)
self.plot.create_text(
65, y,
text='{}'.format((10*(10-i))),
anchor=Tkinter.E,
font=LABEL_FONT
)
# Plot lots of points
for i in range(0, 10000):
x = round(random.random()*100.0, 1)
y = round(random.random()*100.0, 1)
# use floats to prevent flooring
px = 75 + (x * (350.0/100.0))
py = 425 - (y * (350.0/100.0))
self.plot.create_oval(
px - self.radius,
py - self.radius,
px + self.radius,
py + self.radius,
width=1,
outline='DarkSlateBlue',
fill='SteelBlue'
)
root = Tkinter.Tk()
root.title('Simple Plot')
w = 512 + 12
h = 512 + 12
app = Application(root, width=w, height=h)
app.mainloop()
There is actually a problem with some distributions of TKinter and OS Mavericks. Apparently you need to install ActiveTcl 8.5.15.1. There is a bug with TKinter and OS Mavericks. If it still isn't fast eneough, there are some more tricks below.
You could still save the multiple dots into one image. If you don't change it very often, it should still be faster. If you are changing them more often, here are some other ways to speed up a python program. This other stack overflow thread talks about using cython to make a faster class. Because most of the slowing down is probably due to the graphics this probably won't make it a lot faster but it could help.
Suggestions on how to speed up a distance calculation
you could also speed up the for loop by defining an iterator ( ex: iterator = (s.upper() for s in list_to_iterate_through) ) beforehand, but this is called to draw the window, not constantly as the window is maintained, so this shouldn't matter very much. Also, a another way to speed things up, taken from python docs, is to lower the frequency of python's background checks:
"The Python interpreter performs some periodic checks. In particular, it decides whether or not to let another thread run and whether or not to run a pending call (typically a call established by a signal handler). Most of the time there's nothing to do, so performing these checks each pass around the interpreter loop can slow things down. There is a function in the sys module, setcheckinterval, which you can call to tell the interpreter how often to perform these periodic checks. Prior to the release of Python 2.3 it defaulted to 10. In 2.3 this was raised to 100. If you aren't running with threads and you don't expect to be catching many signals, setting this to a larger value can improve the interpreter's performance, sometimes substantially."
Another thing I found online is that for some reason setting the time by changing os.environ['TZ'] will speed up the program a small amount.
If this still doesn't work, than it is likely that TKinter is not the best program to do this in. Pygame could be faster, or a program that uses the graphics card like open GL (I don't think that is available for python, however)
Tk must be getting bogged down looping over all of those ovals. I'm not
sure that the canvas was ever intended to hold so many items at once.
One solution is to draw your plot into an image object, then place the image
into your canvas.
This question already has answers here:
Pygame how to fix 'trailing pixels'?
(4 answers)
Closed 5 years ago.
I'm trying to develop a simple game in python using pygame and IDLE. I have, since yesterday, looked through a variety of sources in order to learn about the language (and programming in general), even so I have encountered some problems and misunderstandings of how it all works. So, if anyone could please advise me on how to proceed (or point me in the direction of some good learning material) then I would appreciate it greatly.
So far, I've got a small bit of code that forms the basis of my game idea, so I will post it here and list some of my problems.
import pygame
def main():
pygame.init()
logo = pygame.image.load("coolblack.jpg")
pygame.display.set_icon(logo)
pygame.display.set_caption("Battleship")
screenWidth = 800
screenHeight = 600
screen = pygame.display.set_mode((screenWidth, screenHeight))
bgd_image = pygame.image.load("grid.png")
#--------------------------------------------------------------------
#the image named 'image' should be above 'bgd_image' but below 'cv9'
#in fact, everything should be above bgd_image, especially 'cv9'
#--------------------------------------------------------------------
image = pygame.image.load("coolblack.jpg")
cv9 = pygame.image.load("ussessexcv9.gif").convert_alpha()
xposCv9 = 400
yposCv9 = 510
step_xCv9 = 1
step_yCv9 = 1
screen.blit(bgd_image, (0,0))
screen.blit(image, (400,300))
screen.blit(cv9, (xposCv9, yposCv9))
pygame.display.flip()
clock = pygame.time.Clock()
running = True
#---------------------------------------------
#I've got a pretty good idea (sort of) about
#what is happening in the section
#below this point, however it seems that
#the image 'cv9' creates a trail of itself
#every time it moves, so how could I make it
#so that it doesn't do so?
#---------------------------------------------
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
if xposCv9>screenWidth-64 or xposCv9<0:
step_xCv9 = -step_xCv9
if yposCv9>screenHeight-64 or yposCv9<0:
step_yCv9 = -step_yCv9
xposCv9 += step_xCv9
yposCv9 += step_yCv9
screen.blit(cv9, (xposCv9, yposCv9))
pygame.display.flip()
clock.tick(60)
if __name__=="__main__":
main()
The way that pygame works is that it has internally a representation of the screen which you are updating. So, it starts entirely black, then you do your first "blit". This will update the internal representation. Then when you call "pygame.display.flip" it shows that representation on the screen. However, this will not automatically "clear" the representation for you back to all black for your next frame. So, on the next frame, you blit again (slightly to the left, say), and the first blit remains, creating your "trail".
Therefore, for what you're doing, the best thing would be to in your loop, clear the internal representation of the screen before you start drawing the next frame. You can "clear" the screen by filling it with a single color, like so...
BLACK = (0,0,0)
....
screen.blit(cv9, (xposCv9, yposCv9))
pygame.display.flip()
clock.tick(60)
screen.fill(BLACK) # Add this to "clear" the screen.
Note that if you chose to go this route, this means you will need to redraw ALL of the elements every single frame (not just the ones that changed since the last frame).
BTW, in case you are wondering, there is a good reason for why the frame is not automatically cleared at the end. In some cases, it might be faster to only update the parts of the screen that update. This can cause performance speedups in some applications. However, it's probably best to start with clearing the screen as the example shows above.
I am making a battleship game for a project. While I have completed the logic and the game works with text input. I was hoping make a (very basic) GUI while still use the methods created for the text.
The two options I've been looking at are PyGame and Tkinter. PyGame does not seem to have a text output/label function. Tkinter does, but it doesn't seem as easy (i feel) as PyGame is.
Though I would love to give either of these frameworks the time they deserve, I have just over 60hrs before this is due.
I wanted to know if anyone one has any experience or insights and if it is a realistic option.
Thanks as always!
This is a fairly trivial thing to do in Tkinter. A battleship game shows an array of coordinates which you can display as a grid of checkbuttons.
You could check Kivy, it's working in top of OpenGL, provide severals basics widgets (label, button, slider, textinput, layouts, ...), and you can create your own / display graphics etc. Works as a python framework, almost all platforms.
You can also check the recent game contest to see what you can do with it :)
Why don't you try Cocos2D?
It's higher level, and it supports rich labeling (Class cocos.text.RichLabel)
http://cocos2d.org/
I suggest you use pyqt/pyside for this task. This lets you access the powerful Qt framework which has great documentation. If you design a game that does not need fast graphics you can simply use QGraphicsView/QGraphicsScene and related classes to display icons/numbers/whatever. Of course you can use OpenGL with Python and Qt as well.
You can render text on pygame, just use:
class pygame.font.Font
#create a new Font object from a file
pygame.font.Font(filename, size): return Font
pygame.font.Font(object, size): return Font
method Font.render
#method of Font() to draw text on a new Surface
Font.render(text, antialias, color, background=None): return Surface
Simple example how to use text on pygame:
from pygame import font as pgfont, sprite as pgspr
import pygame as pg
class FontSprite(pgspr.DirtySprite):
def __init__(self, text, x, y):
'''self.image = surface'''
pgspr.DirtySprite.__init__(self)
self.text = text
self.color = [0,0,0]
self.image = self.get_image()
self.rect = pg.Rect((x, y), self.image.get_size())
def get_image(self):
self.dirty = 1
return pgfont.Font('fonts\\BRLNSR.TTF', self.size).render(self.text,
True,
self.color)
Got this question from a professor, a physicist.
I am a beginner in Python programming. I am not a computer professional I am a physicist. I was trying to write a code in python for my own research which involves a little image processing.
All I need to do is to display an image and then select a region of interest using my mouse and finally crop out the selected region. I can do this in Matlab using the ginput() function.
I tried using PIL. But I find that after I issue the command Image.show(), the image is displayed but then the program halts there unless I exit from the image window. Is there any way to implement what I was planning. Do I need to download any other module? Please advise.
While I agree with David that you should probably just use GIMP or some other image manipulation program, here is a script (as I took it to be an exercise to the reader) using pygame that does what you want. You will need to install pygame as well as the PIL, usage would be:
scriptname.py <input_path> <output_path>
Actual script:
import pygame, sys
from PIL import Image
pygame.init()
def displayImage( screen, px, topleft):
screen.blit(px, px.get_rect())
if topleft:
pygame.draw.rect( screen, (128,128,128), pygame.Rect(topleft[0], topleft[1], pygame.mouse.get_pos()[0] - topleft[0], pygame.mouse.get_pos()[1] - topleft[1]))
pygame.display.flip()
def setup(path):
px = pygame.image.load(path)
screen = pygame.display.set_mode( px.get_rect()[2:] )
screen.blit(px, px.get_rect())
pygame.display.flip()
return screen, px
def mainLoop(screen, px):
topleft = None
bottomright = None
runProgram = True
while runProgram:
for event in pygame.event.get():
if event.type == pygame.QUIT:
runProgram = False
elif event.type == pygame.MOUSEBUTTONUP:
if not topleft:
topleft = event.pos
else:
bottomright = event.pos
runProgram = False
displayImage(screen, px, topleft)
return ( topleft + bottomright )
if __name__ == "__main__":
screen, px = setup(sys.argv[1])
left, upper, right, lower = mainLoop(screen, px)
im = Image.open(sys.argv[1])
im = im.crop(( left, upper, right, lower))
im.save(sys.argv[2])
Hope this helps :)
For what it's worth (coming from another physicist), I would just do this in an image processing program like the GIMP. The main benefit of doing this task in Python (or any language) would be to save time by automating the process, but unless you - well, the professor - can somehow develop an algorithm to automatically figure out what part of the image to crop, there doesn't seem to be much time to be saved by automation.
If I remember correctly, GIMP is actually scriptable, possibly with Python, so it might be possible to write a time-saving GIMP script to do what your professor friend wants.
Image.show() just calls whatever simple picture viewer it can find on the current platform, one that may or may not have a crop-and-save facility.
If you are on a Windows box and you just need to make it work on your machine, set the ‘Open with...’ association to make it so running an image loads it into an editor of your choice. On OS X and *nix you'd want to hack the _showxv() method at the bottom of Image.py to change the command used to open the image.
If you do actually need to provide a portable solution, you'll need to use a UI framework to power your cropping application. The choices boil down to Tkinter (ImageTk.py gives you a wrapper for displaying PIL images in Tk), PyQT4 (ImageQt in PIL 1.1.6 gives you a wrapper for displaying images in QT4) or wxPython (a higher-level application authoring toolkit using wxWidgets). It'll be quite a bit of work to get the hang of a full UI kit, but you'll be able to completely customise how your application's interface will work.
Is there a script in python like a library to auto crop images :
Automatically crop image
What you are looking for is the module: matplotlib, it emulates Matlab. See the ginput() function. That allow you to find the bounding box, then you can use crop from PIL.
http://matplotlib.sourceforge.net/api/figure_api.html