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.
Help building a touch menu
-
I wanted to build a menu to execute a script, but I keep getting this error. Problem being, I don't have a line 570 in the program I am trying to launch. The code for the file I am trying to launch can be found here. I didn't modify Pythonista either.
from scene import * import sys import console class MyScene (Scene): def setup(self): self.x = self.size.w * 0 self.y = self.size.h * 0 def draw(self): background(0.40, 0.80, 1.00) # light blue background color fill(0.50, 1.00, 0.00) # play button fill color self.playpx, self.playpy = (320, 350) self.playsx, self.playsy = (358, 100) rect(self.playpx, self.playpy, self.playsx, self.playsy) # play button rectangle fill(1.00, 0.00, 0.00) # quit game button fill color self.quitpx, self.quitpy = (320, 200) self.quitsx, self.quitsy = (358, 100) rect(320, 200, 358, 100) # quit game button rectangle tint(1.00, 1.00, 1.00) # white text color text('Play Game', font_name='AvenirNext-Heavy', font_size=60.0, x=500.0, y=400.0) text('Quit Game', font_name='AvenirNext-Heavy', font_size=60.0, x=500.0, y=252.0) text('Hockey', font_name='Avenir-Medium', font_size=160.0, x=510.0, y=600) fill(1.00, 0.80, 0.40) # stick color rect(70, 10, 40, 400) # left stick rect(70, 10, 100, 40) # left stick rect(900, 10, 40, 400) # right stick rect(840, 10, 70, 40) # right stick fill(1.00, 0.00, 0.00) # fill color for red stripe details rect(70, 360, 40, 10) # left stick red 1 rect(70, 320, 40, 10) # left stick red 2 rect(900, 360, 40, 10) # right stick red 1 rect(900, 320, 40, 10) # right stick red 2 fill(1.00, 1.00, 1.00) # fill color for white tripe details rect(70, 340, 40, 10) # left stick white rect(900, 340, 40, 10)# right stick white def check(self, x, y, posx, posy, sizex, sizey): if x >= posx and x <= posx + sizex: if y >= posy and y <= posy + sizey: return True return False def check_quit(self, x, y): return self.check(x, y, self.quitpx, self.quitpy, self.quitsx ,self.quitsy) def check_play(self, x, y): return self.check(x, y, self.playpx, self.playpy, self.playsx ,self.playsy) def touch_ended(self, touch): x, y = touch.location if self.check_quit(x, y): raise SystemExit() if self.check_play(x, y): execfile('air-hockey.py') run(MyScene())
-
[Errno 2] No such file or directory: 'air-hockey.py'
That is the error that I get.
-
@ccc, He probably want's to execute air-hockey when user presses on "Play Game", but he doesn't know how to setup the menu.
-
@tjferry, I am not sure, what was your error, as you didn't post the traceback. But anyway, I've made your menu work if you need it: http://www.randomgalaxy.com/programming/python/forums/omz-pythonista/air-hockey-menu.txt
-
I get the error that AttributeError: 'HockeyScene' object has no attribute 't', you can tell that by changing air-hockey.py to any other file that uses the scene module.
-
@tjferry14, can you please put full traceback, like on what line, etc. And btw, I don't see class HockeyScene same as variable t usage somewhere...
-
@shadowslayer Sorry. The HockeyScene is in the program I am trying to launch. Here is the full traceback.
https://www.flickr.com/photos/99736631@N02/13412392113/
Problem being, I don't have a line 570 in the program I am trying to launch. I didn't modify Pythonista either. Thanks for your menu as well. Updated the main question script with your code.
-
@tjferry14, Oh, I see... Btw, can I take a look at the source code for HockeyScene? Maybe you modified something, but didn't noticed. At least, I've never ran into such a traceback before...
-
You might want to look at the undocumented scene.Button class for detecting clicks in text blocks: http://omz-forums.appspot.com/pythonista/post/6403240845377536
There has been a lot written in this Forum about the inability to run a second scene once you have called scene.run() a first scene. If you look again at error above, it is complaining about line 570 in the file scene.py, not your app. You might search this forum for "multiscene" to get a sense of one workaround to the scene in a scene issue.
In particular, http://omz-forums.appspot.com/pythonista/post/4657033039052800 is close to your issue.
-
@ShadowSlayer the code for the Hockey Scene can be found here: https://gist.github.com/gillibrand/3271073
@ccc I had a feeling I would have to use a multi scene, but I thought that just launching the file might be simpler/faster. Thanks for your help.
-
Do you think I might be able to get some help with Multi scene? Or perhaps a more efficient way of doing this? I've tried but cannot seem to get anything from the examples.
-
@tjferry14, I can say nothing but what @ccc said. You should probably use multiscene. But I've never heard of it before, so I can't help you with this.
Good luck with it!
-
So, I implemented the air hockey code into the multi scene examples, and I am still presented with the error from my first post.
Quite frustrating.
-
You need to have the menu app and the hockey app use the SAME multiscene. If you are calling scene.run() twice, it will not work.
-
from scene import *
from sound import *
from copy import copy
from math import modf, floor
import timescreen_size = Size() class Player (object): # Player object... def __init__(self, name, color): # define the player color self.color = color # set color as the color of player self.score = 0 # set score as zero self.name = name # set name as the name as the player def __str__(self): return self.name class Puck (object): # Puck object def __init__(self, pos, scene): # define the puck self.vector = Vector3(0, 0, 0) # set the puck as a vector self.pos = pos # set the puck position def reverse_vector(self): v = self.vector self.vector = Vector3(-v.x, -v.y, 0) class Game (Scene): def setup(self): self.left_player = Player("Green Player", Color(0.50, 1.00, 0.00)) # sets green player color self.right_player = Player("Gold Player", Color(1.00, 0.80, 0.40)) # sets gold player color self.players = (self.left_player, self.right_player) # sets players in left corner and right corner self.start_time = 0.0 self.stop_time = 0.0 self.last_seconds = 0 self.running = False self.start_time = time.time() self.running = True for s in ('Drums_06', 'Woosh_1', 'Powerup_2'): # for these sound effects... load_effect(s) # load the sound effects in center = self.size.w / 2 # sets center as center of the screen middle = self.size.h / 2 # sets middle as middle of the screen gh = self.size.h / 4 # sets goal height self.puck_radius = gh / 2.5 # sets puck size/radius self.puck = self.centered_puck() # makes puck start out centered # the last time the puck was hit; Used to animate its slow down self.puck_start_t = 0 # sets puck start to the dead center of the screen pr = self.puck_radius # set pr to puck radius self.line_width = self.puck_radius / 6 # sets puck radius inside the center line lw = self.line_width # sets lw to line width self.red_line = Rect(center - lw / 2, 0, lw, self.size.h) # sets red center line self.red_circle = Rect(w=gh, h=gh) # sets circle in the red center line self.red_circle.center(self.bounds.center()) # set the size of the circle in the center line self.left_goal = Rect(w=gh, h=gh) # sets left goal as a rectangle self.right_goal = Rect(w=gh, h=gh) # sets right goal as a rectangle self.left_goal.center(0, middle) # sets the left goal to be centered self.right_goal.center(self.size.w, middle) # sets the right goal to be centered self.winner = None # when someone reaches seven goals, save them to show a message def score_goal(self, player): player.score += 1 # sets player score to plus one for backend purposes self.puck = self.centered_puck() # sets up variable for the puck being centered # following block of code sets the score so when a player reaches 7 they win for p in self.players: if p.score > 6: self.winner = p break overlay = Layer(self.bounds) # sets the cover as an overlay, so its not behind the ice def hide_overlay(): overlay.animate('alpha', 0.0, 1, completion=lambda: overlay.remove_layer()) if self.winner: message = "%s Wins" % (self.winner) # shows a message for whatever player wins on_completion = None self.hide_overlay = hide_overlay else: message = "Goal!" # shows a message for when a player scores a goal on_completion = hide_overlay self.stop_time = time.time() self.running = True # restart the puck on the other player's side if player == self.left_player: self.puck.pos.x += self.size.w / 4 + self.puck_radius else: self.puck.pos.x -= self.size.w / 4 + self.puck_radius size = self.puck_radius * 1.0 # sets puck radius text_layer = TextLayer(message, 'AvenirNext-Heavy', size) # sets text font to Futura text_layer.frame.center(self.bounds.center()) # sets the text to display from the center text_layer.frame.y += self.size.h / 4 # sets the text to autosize itself text_layer.animate('scale_x', 1.3, 0.3, autoreverse=True) # sets the x animation display time/size text_layer.animate('scale_y', 1.3, 0.3, autoreverse=True) # sets the y animation display time/size overlay.add_layer(text_layer) # sets the text layer to overlay #following code sets color, etc. for when a player scores a goal start_color, end_color = copy(player.color), copy(player.color) start_color.a = 0 end_color.a = .5 overlay.background = start_color overlay.animate('background', end_color, .10, completion=on_completion) self.add_layer(overlay) play_effect('Woosh_1') # sets effect of puck scoring to Woosh def centered_puck(self): # set center pos = Rect(w=self.puck_radius, h=self.puck_radius) pos.center(self.bounds.center()) return Puck(pos, self) def move_puck(self): # this lets the puck move by touching it once, like a real air hockey game puck = self.puck ease_x = (self.t - self.puck_start_t) / 4.0 ease = max(0, 1 - curve_ease_out(ease_x)) puck.pos.x += puck.vector.x * ease puck.pos.y += puck.vector.y * ease if puck.pos.right() > self.size.w and not puck.pos.intersects(self.right_goal): # if puck hits the side, play the drum sound effect puck.vector.x *= -1 play_effect('Drums_06') puck.pos.x = min(self.size.w - puck.pos.w, puck.pos.x) if puck.pos.left() > self.size.w: # sets the left goal self.score_goal(self.left_player) return if puck.pos.left() < 0 and not puck.pos.intersects(self.left_goal): puck.vector.x *= -1 play_effect('Drums_06') puck.pos.x = max(0, puck.pos.x) if puck.pos.right() < 0: # sets the right goal self.score_goal(self.right_player) return if puck.pos.top() > self.size.h or puck.pos.bottom() < 0: # if puck hits the side, play the drum sound effect puck.vector.y *= -1 play_effect('Drums_06') puck.pos.y = max(0, puck.pos.y) puck.pos.y = min(self.size.h - puck.pos.h, puck.pos.y) def draw(self): stroke_weight(self.line_width) background(1, 1, 1) # red lines on ice in middle stroke(1.00, 0.40, 0.40) fill(1.00, 0.40, 0.40) rect(*self.red_line.as_tuple()) fill(1, 1, 1) ellipse(*self.red_circle.as_tuple()) # red goal markers for goal in (self.left_goal, self.right_goal): rect(*goal.as_tuple()) for touch in self.touches.values(): self.handle_touch(touch) # black puck no_stroke() fill(0, 0, 0) ellipse(*self.puck.pos.as_tuple()) self.draw_scores() self.move_puck() if self.root_layer: self.root_layer.update(self.dt) self.root_layer.draw() if self.winner is not None: # shows message/allows player to tap to play again c = Rect() c.center(self.bounds.center()) tint(1, 1, 1) message = "Tap to Play Again" y_offset = self.size.h / 4 size = self.puck_radius text(message, x=c.x, y=c.y-y_offset, font_size=size, font_name='AvenirNext-Heavy') def draw_scores(self): # sets score font/size for p, x in zip(self.players, (50, self.size.w - 50)): tint(*p.color.as_tuple()) text(str(p.score), x=x, y=self.size.h- 50, font_size=32, font_name='AvenirNext-Heavy') tint(0.00, 0.50, 1.00) #Format the elapsed time (dt): dt = 0.0 if self.running: dt = (time.time() - self.start_time) else: dt = (self.stop_time - self.start_time) minutes = abs(dt) / 60 # absolute value of dt divided by 60 seconds = abs(dt) % 60 # absolute value times percentage of 60 centiseconds = modf(abs(dt))[0] * 100 s = '%02d:%02d.%02d' % (minutes, seconds, centiseconds) text(s, x=510, y=700, font_size=34, font_name='AvenirNext-Heavy') def handle_touch(self, touch): if self.winner: # check for winner overlay and clear. # Overlays to restart game to play again self.winner = None self.hide_overlay() del self.hide_overlay self.left_player.score = 0 self.right_player.score = 0 # stops the clock self.stop_time = time.time() self.running = False # resets the clock self.start_time = 0.0 self.last_seconds = 0 self.stop_time = 0.0 # starts the clock self.start_time = time.time() self.running = True return tx = touch.location.x # sets x touch location... ty = touch.location.y # sets y touch location... finger = Rect(tx - 20, ty - 20, 40, 40) # sets touch location for finger size if finger.intersects(self.puck.pos): dx = tx - touch.prev_location.x dy = ty - touch.prev_location.y # Richochet for unmoving finger, to make it like a real air hockey game if abs(dx) < 5 and abs(dy) < 5: self.puck.reverse_vector() # Slide puck out from the finger. No puck holding. if dx == 0: dx = self.puck.vector.x if dy == 0: dy = self.puck.vector.y if not any([dx, dy]): return while finger.intersects(self.puck.pos): self.puck.pos.x += dx self.puck.pos.y += dy else: self.step = 0 self.puck_start_t = self.t self.puck.vector = Vector3(dx * .8, dy * .8, 0) self.puck.pos.x += dx self.puck.pos.y += dy class Start (Scene): def draw(self): background(0.40, 0.80, 1.00) # light blue background color fill(0.50, 1.00, 0.00) # play button fill color rect(320, 350, 358, 100) # play button rectangle tint(1.00, 1.00, 1.00) # white text color text('Play Game', font_name='AvenirNext-Heavy', font_size=60.0, x=500.0, y=400.0) def touch_ended(self, touch): main_scene.switch_scene(Game) class GameOver (Scene): def setup(self): self.button = Button(Rect((screen_size.w/2)-100, (screen_size.h/2)-50, 200, 100), 'RESTART') self.button.action = self.restart self.add_layer(self.button) def restart(self): main_scene.switch_scene(Game) def draw(self): background(0,0,0) self.button.draw() no_tint() text('Your score was: {0}'.format(score), x=screen_size.w/2, y=screen_size.h-50, font_size=24) class MultiScene (Scene): def __init__(self, start_scene): self.active_scene = start_scene() def switch_scene(self, new_scene): self.active_scene = new_scene() self.setup() def setup(self): global screen_size screen_size = self.size self.active_scene.add_layer = self.add_layer self.active_scene.size = self.size self.active_scene.setup() def draw(self): self.active_scene.touches = self.touches self.active_scene.draw() def touch_began(self, touch): self.active_scene.touch_began(touch) def touch_moved(self, touch): self.active_scene.touch_moved(touch) def touch_ended(self, touch): self.active_scene.touch_ended(touch) main_scene = MultiScene(Start) run(main_scene)
-
Not sure why this isn't working, as I'm not calling the run twice.
-
It should work if you change your MultiScene class to this:
class MultiScene (Scene): def __init__(self, start_scene): self.active_scene = start_scene() def switch_scene(self, new_scene): self.active_scene = new_scene() self.setup() def setup(self): global screen_size screen_size = self.size self.active_scene.add_layer = self.add_layer self.active_scene.size = self.size self.active_scene.bounds = self.bounds self.active_scene.root_layer = self.root_layer self.active_scene.setup() def draw(self): self.active_scene.touches = self.touches self.active_scene.t = self.t self.active_scene.draw() def touch_began(self, touch): self.active_scene.touch_began(touch) def touch_moved(self, touch): self.active_scene.touch_moved(touch) def touch_ended(self, touch): self.active_scene.touch_ended(touch)
-
@Sebastian, your solution works fine, but the overlays don't show their color for goals. They show up, but with a white background. Same with the game over layer.
-
This post is deleted! -
Two things to try...
In the draw() method of "sub-scenes", instead of calling background(r, g, b) try calling main_scene.background(r, g, b)... I am not sure if it will work but it is worth a try.
If that does not work, you could try putting your overlays directly onto main_scene instead of onto sub-scenes.