I am having difficulty getting a simple python Arcade app to work on my Win10 machine. I think the issue is with the "start_render" function not taking effect. I have the following issues:
the screen does not "redraw", i.e., all existing sprites remain on the screen and are never removed. if I run some of the builtin code samples I get the character redrawn on the screen 100s of times as it moves
the background color is never drawn on; it always remains black
Using the "finish_render()" method in the on_draw() method does not work either; it creates a constant flickering and not a stable view
The sipmle code I'm running is below.
import arcade
class TestUI(arcade.Window):
def __init__(self, width, height, title):
super().__init__( width, height, title)
def setup(self):
arcade.set_background_color(arcade.csscolor.BLUE)
def on_draw(self):
arcade.start_render()
arcade.draw_circle_filled(100,100,50,arcade.csscolor.TEAL)
def main():
app = TestUI(1200,800,"Test game")
app.setup()
arcade.run()
if __name__ == '__main__':
main()
This gives me a Teal circle on a full black background. If I run one of the code examples
python -m arcade.examples.sprite_moving_platforms
I get the constant redrawing (see screenshot)
I am running the following versions:
Python 3.7.4
Arcade 2.5.7
On a Windows 10 machine
Any pointers on what could be going wrong?
Thanks!
Related
Problem statement
I have written a code in Python on which a user can draw on a tkinter GUI canvas. The program is capable of taking the app's screenshot but saving the canvas content to a viewable image file (such as .jpg or .png) is not properly done yet.
Screenshots
What I draw on the tkinter app's canvas.
What the code (given below) saved.
What I expect.
I have tried and will expect
A number of possible solutions from the internet (including Stack Overflow) have been tried but somehow the process cannot be completed successfully. After drawing whatever on the canvas, the saved image will have nothing but a dot whether it is in *.jpg or *.png. I need the image of the full canvas with all the colorful drawings on it in that saved image file.
For any reference, here is my code
from tkinter import Tk, Canvas, Button
from PIL import Image, ImageDraw
X = 0
Y = 0
app = Tk()
def set_xy(click):
global X, Y
X, Y = click.x, click.y
def draw_line(drag):
global X, Y
draw_panel.create_line((X, Y, drag.x, drag.y), fill="black", capstyle="round", smooth=True)
draw.line([(X, Y), (drag.x, drag.y)], fill="black", joint="curve")
X, Y = drag.x, drag.y
def save(event=None):
# ATTENTION: This is not working properly
image.save("image.png", "png")
# App's drawing panel
draw_panel = Canvas(app, background="white", cursor="dot")
draw_panel.place(x=0, relwidth=1.0, y=0, relheight=1.0)
# Invisible canvas for saving as an image later on
image = Image.new("RGB", (draw_panel.winfo_width(), draw_panel.winfo_height()), "white")
draw = ImageDraw.Draw(image)
# Mouse and Key bindings
draw_panel.bind('<Button-1>', set_xy)
draw_panel.bind('<B1-Motion>', draw_line)
app.bind('<s>', save) # press 's' to save the drawing as .png
app.mainloop()
Addition Informations:
HOST: Windows 10 Version 20H2 (OS Build 19042.2130)
IDE: PyCharm 2022.2 (Community Edition) (Build #PC-222.3345.131)
Language: Python 3.10.6
Framework: tkinter Tk/Tcl 8.6
Library: pillow 9.2.0
Any solution shall be highly appreciated, and I shall be thankful.
That's a nice idea and there is just one piece missing. You set the size of the image to the size of the canvas before you call mainloop() for the first time. The canvas does not have a size at this time because the window has not been initiated, yet.
You need to force an update before getting the width and height. Then, it works fine:
# Invisible canvas for saving as an image later on
draw_panel.update()
image = Image.new("RGB", (draw_panel.winfo_width(), draw_panel.winfo_height()), "white")
draw = ImageDraw.Draw(image)
If you print the draw_panel.winfo_width() before and after calling the update, you see what I mean. Before update(), width and height are both 1. Afterwards, they are 200, as they should be.
Recently i have been trying to get gifs to work with tkinter and after some research i figured out how i was going to do it and wrote a class to make playing them easier
class GifWrapper():
def __init__(self, parent, file=None):
self.file=file
self.parent=parent
if file:
self.info=parse(file=file)
else:
raise GifError("expected file to be passed")
print(str(self.info.frames)+" "+str(len(self.info.delays)))
self.frames=[]
for i in range(0, self.info.frames):
self.frames.append(PhotoImage(file=self.file, format="gif -index {}".format(i)))
print("added frame {}".format(i))
async def play(self):
curFrame=0
for i in self.info.delays:
await asyncio.sleep(i/100)
self.parent["image"]=self.frames[curFrame]
curFrame+=1
However when i tested this out i noticed that my gif was pretty strange as seen here, the first frame seems to be only slightly affected, the second frame seems perfectly ok and the rest are pretty badly affected by the issue, i remember reading somewhere that pixels with an alpha of 0 were treated as transparent in PhotoImage files so i assume that this might be the problem however i think it's weird that the first and second images are affected differently, is there a way to change this?
import pyglet
window = pyglet.window.Window()
label = pyglet.text.Label("Hello World!",
font_name="Times New Roman",
color=(255,255,255,255),
font_size=36,
x=window.width//2, y=window.height//2,
anchor_x="center", anchor_y="center")
#window.event
def on_draw():
window.clear()
label.draw()
pyglet.app.run()
This code is taken from the pyglet tutorial at https://pyglet.readthedocs.io/en/pyglet-1.2-maintenance/programming_guide/quickstart.html but when run it doesn't draw the label until any key is pressed. I added the color as I thought the text may have defaulted to black.
Am I missing something really obvious as to why this behaviour is happening?
OK, having had my memory jogged by the comments, I installed the MS Fonts and it now works in python 2.x but I still need to press a key to see the text in python 3. Maybe the font thing is a red-herring and there is some incompatibility with python 3.
As mentioned in the comments.
Most examples found on the internet (even most guides) assume a Windows platform.
If there's a font= declaration with a Windows font but you're running Linux, make sure you got the proper fonts installed or revert to a font you've got installed.
$ fc-list
Also not declaring a font will work too:
label = pyglet.text.Label("Hello World!",
color=(255,255,255,255),
font_size=36,
x=window.width//2, y=window.height//2,
anchor_x="center", anchor_y="center")
Because Pyglet will default to sans-serif:
If you do not particularly care which font is used, and just need to display some readable text, you can specify None as the family name, which will load a default sans-serif font (Helvetica on Mac OS X, Arial on Windows XP)
Your issue is probably that you don't manually update the screen after you drew something. Normally when you press a key, it forces a window.flip(), which basically means update the screen content.
window.clear() also triggers this behavior, but x.draw() does not. Why? Well the thing that takes time in computer graphics is not really the calculations, it's the updating them to the screen that takes time.. There for .draw() doesn't update the screen, it just puts the stuff in the graphical buffer ("page"), you decide when to flip the page and show the new buffer on the screen.
Try this out:
#window.event
def on_draw():
window.clear()
label.draw()
window.flip()
This might be a overkill solution, but it will probably solve the problem.
This overrides the default loop of pyglet and the default draw behavior, it's also one of the classes i use the most in my pyglet projects since it gives me the option to create my own framework.
import pyglet
class Window(pyglet.window.Window):
def __init__(self):
super(Window, self).__init__(vsync = False)
self.sprites = {}
self.sprites['testlabel'] = label = pyglet.text.Label("Hello World!",
color=(255,255,255,255),
font_size=36,
x=self.width//2, y=self.height//2, #self here, being pyglet.window.Window that we've inherited and instanciated with super().
anchor_x="center", anchor_y="center")
self.alive = 1
def on_draw(self):
self.render()
def render(self):
self.clear()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj.draw()
self.flip()
def on_close(self):
self.alive = 0
def run(self):
while self.alive:
self.render()
# This is very important, this queries (and empties)
# the pyglet event queue, if this queue isn't cleared
# pyglet will hang because it can't input more events,
# and a full buffer is a bad buffer, so we **NEED** this!
event = self.dispatch_events()
win = Window()
win.run()
It's extremely basic, but you can add more sprites, objects and render them in def render(self):.
I have a wxPython application developed on Linux that I wish to deploy on Windows machines. While most of the things work well on porting there are small (but catastrophic) problems, none of which seem related. Rather than list them all, I'll start with one.
I have an array of images that I read in and place in a GridBagSizer in a Class. Unfortunately, the 'Bind' statement doesn't seem to take effect, and when I double-click on an image 'onSuitDClick' is not called.
suitSizer=SuitDisplay(self, suit
def onSuitDClick(self, event):
print 'sc'
class SuitDisplay(wx.Panel):
def __init__(self, frame, suit):
wx.Panel.__init__(self, frame, id=wx.ID_ANY)
suitSizer=wx.GridBagSizer(1, 13)
suitList=[]
for ii in range(0, len(g.CARDS)):
if g.CARDS[ii]+g.SUITS[suit] in frame.availableCards:
card=g.CARDS[ii]+g.SUITS[suit]
suitList.append(card)
for ii in range(0, len(suitList)):
card= suitList[ii]
image = wx.Bitmap(frame.imageFolder+card+'.png',wx.BITMAP_TYPE_PNG).ConvertToImage()
img = wx.StaticBitmap(self, -1, image.ConvertToBitmap(), name=card)
img.Bind(wx.EVT_LEFT_DCLICK, frame.onSuitDClick)
suitSizer.Add(img, pos=(0,cardPos), flag=wx.LEFT, border=border)
self.SetSizer(suitSizer)
Needless to say, this works perfectly in Linux.
Can anyone suggest where I might look for a solution?
I'm trying to set the minimum size of the buttons in this GtkButtonBox. Currently they seem to be fixed - approx 85 pixels I think.
Is this possible?
If not, is there another way in Gtk to get two small sized buttons to snuggle together like in the above picture rather than having them appear to be two separate buttons? For example GtkStackSwitcher may be something I could use but there doesn't appear to be a way to respond to click events for a button.
I've used this test program to create the above (Ubuntu 14.04, Gtk+3.10 and Python3):
from gi.repository import Gtk
import sys
class MyWindow(Gtk.ApplicationWindow):
def __init__(self, app):
Gtk.Window.__init__(self, title="example", application=app)
self.set_default_size(350, 200)
self.set_border_width(10)
hbox = Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL)
hbox.set_layout(Gtk.ButtonBoxStyle.EXPAND)
button = Gtk.Button(label="a")
hbox.add(button)
button2 = Gtk.Button(label="b")
hbox.add(button2)
self.add(hbox)
class MyApplication(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self)
def do_activate(self):
win = MyWindow(self)
win.show_all()
def do_startup(self):
Gtk.Application.do_startup(self)
app = MyApplication()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
With regards to a question about the desktop environment I'm using.
I've tried Mate, Unity and Gnome-Shell. All work the same way. I've removed the title and those controls. Still the same thing happens. To me this looks more like a GTK issue.
I believe that GtkButtonBox imposes some layout constraints on its buttons that you may not want here. Try using buttons in just a regular GtkGrid, but give them the GTK_STYLE_CLASS_LINKED CSS class.
For each button, do:
button.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED)