Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello โ I'm @olemoritz on Twitter.
[SPACE ESCAPE]-Game example to help with Game dev
-
@Karina said:
@stephen I read about *args **kwargs things, but they didn't say what it is, like list or dict. Now you wrote couple words about, not article, and I got what is it๐
im glad i was able to help with
*
and**
.
@Karina said:
I'm reading now your buttons, and you use there ui, but didn't import it. You can access ui through scene?
The
scene
modulewas built off of the
ui` module. both, if im correct, were created for Pythonista specifically. if you go into yourPython Modules
=>Standard Library (3.6)
=>site-packages
=>scene.py
you will see at the top:
#import 'pythonista' #coding: utf-8 from _scene2 import * import _scene2 from scene_drawing import * import math from numbers import Number from io import BytesIO import ui
_scene2
is the core modulescene_drawing
is aditional functionality- then
math
,numbers.Number
,io.BytesIO
are standard - and the
ui
module
when we used
from scene import *
this means everything ๐ so we dont need to importscene_drawing
,math
,numbers.Number
,io.BytesIO
orui
.scene
does this for us.If you want you can still do your own import but it will still be the same instance a that was imported inside
scene
so theres really no need.@Karina said:
Also they not so explain in docs what the sender param is
wow im suprised the docs didnt cover this one.. but its really simple and ill show you an example i made of a
ButtonNode
i made to use withscene
that hould be very close to howui
uses it.When you create a
ui.Button
and set theaction
property and then Tap the button there are a few things that happends..first it changes to the
pressed
state that changes the image to visually how you di tap it. then it calls the function/method that you setaction
to and passesself
as the only argument.class ui.Button(ui.view): ... def _action(self): # some cool stuff self.action(self) # more cool stuff ...
this is why your Custome action needs
sender
now inside your custome action you can use sender to accesd the button you pressed
def button_tapped(sender): sender.superview.background_color = "blue"
this changes the color of the view that contains your button. ๐
here is the example for a button in scene..
import scene class EventHandler: children=[] event_loop=None @classmethod def add_child(cls, node): cls.children.append(node) if cls.event_loop == None: cls.event_loop = node.scene @classmethod def count(cls): return len(cls.children) @classmethod def touch_began(cls, touch): for child in cls.children: if cls.event_loop.point_from_scene(touch.location) in child.frame: child.Button_Tapped() class ButtonNode(scene.ShapeNode): def __init__(self, action, name=f'ButtonNode', bg_color='#d4d2ca', accessoryColor='#e80000', icon=None, accessory='emj:Exclamation_Mark_2', anchor_point=(0.5, 0.5), font_family='Helvetica', font_size=16, parent=None, position=(0, 0), size=(120, 45), corner_radius=8, border_size=20, borderColor='#000000', text='', text_color='#000000', tint='white', enabled=True, *args, **kwargs): self.x, self.y = position self.w, self.h = size super().__init__( path=scene.ui.Path.rounded_rect( self.x, self.y, self.w, self.h, corner_radius), fill_color=bg_color, stroke_color=borderColor, shadow=None, parent=parent, *args, **kwargs) EventHandler.add_child(self) self.icon=self._init_textures(icon) self.accessory=self._init_textures(accessory) self.border_size=border_size self.name=name + ' ' + str(EventHandler.count()) self.enabled=enabled self.text=text if text != '' else self.name self.tint=tint self.text_color=text_color self.borderColor=borderColor self.corner_radius=corner_radius self.showAccesory=False self.accessoryColor=accessoryColor self.parentNode=parent self.position=position self.size=size self.button_action=action self.anchor_point=anchor_point self.font_family=font_family self.font_size=font_size self.components=dict({ 'accessory':None, 'icon':None, 'label':None, 'width':None, 'height':None}) self._setup(self.icon, self.accessory, self.components) def _init_textures(self, img): if type(img) == str or type(img) == scene.ui.Image: return scene.Texture(img) else: return None def _setup(self, i, a, c): if a != None: c['accessory']=scene.SpriteNode( texture=a, size=(self.size[1]/4, self.size[1]/5*1.5), position=scene.Point(-self.size[0]/2+7, self.size[1]/2-10), parent=self, z_position=4, color=self.accessoryColor) if i != None: c['icon']=scene.SpriteNode( texture=i, size=scene.Size(self.size[1]/2, self.size[1]/2), position=scene.Point(self.w/2 - self.size[1]/3 , 0), parent=self, z_position=9) if self.text: c['label']=scene.LabelNode( text=self.text, font=(self.font_family, self.font_size), position=scene.Point(0 , 0), anchor_point=(0.5, 0.5), color=self.text_color, parent=self, z_position=10) def Button_Tapped(self): if self.components['accessory'].alpha > 0: self.components['accessory'].alpha = 0 if self.enabled: self.button_action(self) #--------------------------------------------------------------------------- def my_button_action(sender): print(f'{sender.name}: {sender.text}..') class main(scene.Scene): def setup(self): self.background_color='#f2f2f2' self.my_button_up=ButtonNode( text='', parent=self, position=scene.Point(self.size[0]/2, self.size[1]/2+100), action=my_button_action) self.my_button_right=ButtonNode( text='right', parent=self, position=scene.Point(self.size[0]/2+100, self.size[1]/2), action=my_button_action) self.my_button_down=ButtonNode( text='down', parent=self, position=scene.Point(self.size[0]/2, self.size[1]/2-100), action=my_button_action) self.my_button_left=ButtonNode( text='left', parent=self, position=scene.Point(self.size[0]/2-100, self.size[1]/2), action=my_button_action) def touch_began(self, touch): EventHandler.touch_began(touch) scene.run(main())
-
@Karina said:
@stephen When I launch this it gives to areas of grey and black colors, but it should be blue๐ค Or not?
im assuming your doing Episode 2 of the tutorial. if so your project should look like this at he end
if you are talking about the tutorial we should post questions on the respective thread so anyone in the future can see Q and A wihout having to hunt it down ๐
-
@mikael said:
@stephen, Internet is indeed full of these.
Here is a very basic singleton implementation:
class EventManager: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance # For demonstration only @property def address(self): return id(self) assert EventManager().address == EventManager().address
You might need to be concerned about threads, in which case you would need a lock:
from threading import Lock class EventManager: _instance = None _lock = Lock() def __new__(cls): with cls._lock: if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance
If you think of creating millions of these, a little optimization minimizes the cost of acquiring the lock:
class EventManager: _instance = None _lock = Lock() def __new__(cls): if cls._instance is None: with cls._lock: if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance
Of course, you would also need to be aware of any threads you have when updating the event manager values.
๐คฆ๐ปโโ๏ธ i wasnt calling
super().__new__(cls)
thank you! one more question, im not too familier withthreading
just yet with python.. isnt each Animation orAction
asynchronous and if so since im considering threading here should i make EventManager async aswell? or would the constant calls toupdate
create conflict?the size of this game wont matter too much but my next walkthrough game will be the RPG and the scale of that one will be much greater, limited to resource allocation from IOS. and i want to use the library created from this one on that to help others create thier personal
cookbook
orlibrary
-
@stephen cool design๐
And this is not thread of the tutorial? -
@stephen how did you learn scene and ui? Cause to me docs is not enough
-
@Karina said:
@stephen cool design๐
And this is not thread of the tutorial?this thread for the Example Game ๐. though it is the Topic the code and questions wont be quite the same. thats why im doing the Tutorial in small chunks. that way there is a thread for each and they wont get flooded with conversation from all over the place. everything should stay based on each "Episode". people in the future, or even right now, that are doing the tutorial may never see the questions you have asked and the answer to even one of your questions could of helped many people. but they cant if they are in the wrong Thread.. and thats a shame because you usually have really good questions!
Links to Tutorial:
At the top of each there is a link to the the Files for each.
-
For myself it was about 4 months of trial and error crash course. and every now and then i would find somthing on this forum (ill note that it was tough to find helpfull threads at the time because it was flooded with spam and general conversation Threads.. thanks to @ccc and a couple others they spent the time to filter the forums for everyone. so thats not an issue anymore โบ๏ธ) and somtimes a thread on StackOverflow or GeeksForGeeks.. tdamdouni has a great repository of scripts that helped me alot and still do aswell.
That is why im doing this tutorial and why i made th Example.. To try and help th community Continue to grow and hopfully get some cool games along the way ๐..
-
@stephen I tried to do like an arrow here
class ButtonNode(SpriteNode): def __init__(self, img, action=None, text=None, parent=None, *args, **kwargs): super().__init__(self, img, *args, **kwargs)
But he tells me that in super().init should be 2 numbers. I don't understand what he means at all๐คทโโ๏ธ
-
@Karina said:
@stephen I tried to do like an arrow here
class ButtonNode(SpriteNode): def __init__(self, img, action=None, text=None, parent=None, *args, **kwargs): super().__init__(self, img, *args, **kwargs)
But he tells me that in super().init should be 2 numbers. I don't understand what he means at all๐คทโโ๏ธ
when used in a classbdeffenition, in this case
__init__
, super will gather the needed data from the current environment that it needs to define the instance. so here you dont pass self and super forSpriteNode
is expecting a string fortexture
(string is a sequence of characters) and not self. this automatic defining is why super is used. its called Cooperative Inheritance. as long as there is a super call in all base classes it allows for multiple inheritance without having to bother with explicitly defining who the parent or base class is. for example it would be more practical to made a button a ShapeNode that way you can add a border to the button without adding another sprite in the background by usingpath
. now if we had putsuper().__init__(*args, **kwargs)
and insured the correct args are passed we would just need to change the base class fomButtonNode(SpriteNode):
toButtonNode(ShapeNode):
and super would handle the rest. ๐๐ -
@stephen So here better to put ShapeNode instead of SpriteNode?
-
-
@stephen Yes, I know, I copied it to my pythonista when you showed it. But there a lot of things, don't know from where to begin it. I just used to write more linear, so it's difficult for me to understand. But it goes slowly
-
-
class ButtonNode(SpriteNode): def __init__(self, img, action=None, text=None, parent=None, *args, **kwargs): super().__init__(img, *args, **kwargs) def setup(self): bg = ui.Path.rounded_rect(0, 0, 200, 200, 8) bg.line_width = 7 self.bg = ShapeNode(bg, stroke_color='blue', parent=self) self.add_child(self.bg)
@stephen here he tells me line_width is invalid arg, but Path obj has it, and in example they do the same. Why I can't do that?
-
@Karina said:
class ButtonNode(SpriteNode): def __init__(self, img, action=None, text=None, parent=None, *args, **kwargs): super().__init__(img, *args, **kwargs) def setup(self): bg = ui.Path.rounded_rect(0, 0, 200, 200, 8) bg.line_width = 7 self.bg = ShapeNode(bg, stroke_color='blue', parent=self) self.add_child(self.bg)
@stephen here he tells me line_width is invalid arg, but Path obj has it, and in example they do the same. Why I can't do that?
To start, im not too sure how your executing
setup
you dont make any calls to it. the code insidesetup
Is functional but I would like to point out that you are adding 2 children of this ShapeNode to self.. When you pass parent in any subclass ofNode
it will add that instance to the list of children forself
. i would removeself.add_child(self.bg)
since we know and who the parent will be at construction time ๐.as for you question here i had zero issues with
bg.line_width
when i ran thissetup
method inside myScene
subclass. ill need to see how you cal calling yoursetup
here -
@Karina said:
@stephen said:
@Karina said:
@stephen So here better to put ShapeNode instead of SpriteNode?
i would personally. But there's nothing wrong with using SpriteNode. Check out this one I made not long ago.
Can you explain why, what ShapeNode has that we need?
you defenently do not need
ShapeNode
but there is no reason not to. I probably should of mentioned this earlier lol im sorry..ShapeNode
is a subclass ofSpriteNode
! ๐๐๐ So you have everything aSpriteNode
has includingtexture
as well aspath
. this means you can use a image for the background and use a Path for your Border..# Just to show using Borders with a image Background class ButtonNode(ShapeNode): def __init__(self, rect=(0, 0, 150, 75), corner_radius=6, *args, **kwargs): super().__init__(*args, **kwargs) x, y, width, height = rect if corner_radius > 0: self.path = ui.Path.rounded_rect(x, y, width, height, corner_radius) else: self.path = ui.Path.rect(x, y, width, height) lw=self.line_width=10 self.fill_color=None self.stroke_color='blue' self.bg_texture=SpriteNode( texture=Texture('pzl:Blue8'), size=self.size-(lw, lw), parent=self)
If you were to try and do this with a normal
SpriteNode
you can never have path borders with a image background without covering the edges of your image. Also by using a SpriteNode as base class only the ShapeNode child has corner_radius so your texture images corners will show outside your rounded edges. -
@Karina said:
@stephen Yes, I know, I copied it to my pythonista when you showed it. But there a lot of things, don't know from where to begin it. I just used to write more linear, so it's difficult for me to understand. But it goes slowly
I shortened my
ButtonNode
and removed rhe Event Manage. there is some short comments on this one too.. hopfully it will help you understand better?import scene class ButtonNode(scene.ShapeNode): def __init__(self, action, name=f'ButtonNode', bg_color='lightgray', accessoryColor='red', icon=None, accessory='emj:Exclamation_Mark_2', anchor_point=(0.5, 0.5), font_family='<system>', font_size=16, parent=None, position=(0, 0), size=(120, 45), corner_radius=8, border_size=20, borderColor='black', text='', text_color='black', enabled=True, *args, **kwargs): # these mainly for call to super() self.x, self.y = position self.w, self.h = size super().__init__( path=scene.ui.Path.rounded_rect( self.x, self.y, self.w, self.h, corner_radius), fill_color=bg_color, stroke_color=borderColor, shadow=None, parent=parent, *args, **kwargs) # Normal Properties for Instance() self.enabled=enabled self.button_action=action self.name=name self.position=position self.size=size self.anchor_point=anchor_point self.icon=self._init_textures(icon) # for border self.border_size=border_size self.borderColor=borderColor self.corner_radius=corner_radius # for accessory self.accessory=self._init_textures(accessory) self.showAccesory=False self.accessoryColor=accessoryColor # for Label self.text=text if text != '' else self.name self.text_color=text_color self.font_family=font_family self.font_size=font_size # Container to hold each component. # is just a dict version of self.children but specific. self.components=dict({ 'accessory':None, 'icon':None, 'label':None}) self._setup(self.icon, self.accessory, self.components) # Type Check to make sure img is a string or ui.Image def _init_textures(self, img): if type(img) == str or type(img) == scene.ui.Image: return scene.Texture(img) else: return None # setup our Components def _setup(self, i, a, c): if a != None: # small indicator image in top left corner # if its enabled it will disable and hide when pressed # intended for stuff like new item in inventory... c['accessory']=scene.SpriteNode( texture=a, size=(self.size[1]/4, self.size[1]/5*1.5), position=scene.Point(-self.size[0]/2+7, self.size[1]/2-10), parent=self, z_position=4, color=self.accessoryColor) if i != None: # button image c['icon']=scene.SpriteNode( texture=i, size=scene.Size(self.size[1]/2, self.size[1]/2), position=scene.Point(self.w/2 - self.size[1]/3 , 0), parent=self, z_position=9) if self.text: # button text.. c['label']=scene.LabelNode( text=self.text, font=(self.font_family, self.font_size), position=scene.Point(0 , 0), anchor_point=(0.5, 0.5), color=self.text_color, parent=self, z_position=10) # called when you tap the button def Button_Tapped(self): if self.components['accessory'].alpha > 0: self.components['accessory'].alpha = 0 if self.enabled: self.button_action(self) # custom action def my_button_action(sender): print(f'{sender.name}: {sender.text}..') class main(scene.Scene): def setup(self): self.buttons=list([]) self.background_color='lightgray' self.my_button=ButtonNode( text='My Button', parent=self, action=my_button_action, position=scene.Point(self.size[0]/2, self.size[1]/2+100)) self.buttons.append(self.my_button) def touch_began(self, touch): for btn in self.buttons: if self.point_from_scene(touch.location) in btn.frame: btn.Button_Tapped() scene.run(main())
-
ShapeNode is a subclass of SpriteNode!
Yeah, they say it in docs, I just forgot๐คฆโโ๏ธ. Then it's more clever to make ShapeNode base class
-
lw=self.line_width=10
Here why you're lw and self.line_width? One is not enough? And it seems it should be self.path.line_width cause Path has that attr.
self.bg_texture=SpriteNode( texture=Texture('pzl:Blue8'), size=self.size-(lw, lw), parent=self)
You're trying make here the form of Blue8, but with rounded angles?
-
@stephen in built in examples sometimes there are with pyui files. Do you use them?