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.
Struggling with touch and display of images
-
Hi,
I am pretty new to Python and coding generally, and I have been trying to learn by writing something in Pythonista that might help my daughter with her reading. The idea is that a word appears at the top of the screen (e.g. 'cow') and your score increases if you touch the corresponding picture on a 4 x 4 grid of various images.
I have been 90% there for a while now, but I'm struggling to deal properly with touch values and also to retain the grid of images after the first touch. My most recent efforts seem to have only made it worse (!), so I am turning to the forum in desperation...
I still haven't completely got my head around classes and methods, and I suspect this might be where I am going wrong. If anyone is able to take a look I would be very grateful. My code is as follows:
from scene import * import Image import random import sound import time class MyScene (Scene): def setup(self): # Set up layer1 (where score and word will appear) self.root_layer = Layer(self.bounds) self.layer1 = Layer(Rect(0, 200, self.size.w, self.size.h - 200)) self.root_layer.add_layer(self.layer1) # Create black background background(0, 0, 0) # Setup screen self.w, self.h = self.size.w, self.size.h - 200 self.col_width = self.w / 4 self.row_height = self.h / 4 # Create list of images and the word which will appear with each one. self.images = [('Ant', 'a n t'), ('Bear_Face', 'b e a r'), ('Bug', 'c a t e r p i l l a r'), ('Chicken', 'c h i c k e n'), ('Cow_Face', 'c o w'), ('Dog_Face', 'd o g'), ('Elephant', 'e l e p h a n t'), ('Fish', 'f i s h'), ('Frog_Face', 'f r o g'), ('Honeybee', 'b e e'), ('Pig_Face', 'p i g'),('Snail', 's n a i l'), ('Snake', 's n a k e'), ('Whale', 'w h a l e'), ('Tiger_Face', 't i g e r'), ('Rabbit_Face', 'r a b b i t')] * 2 # Draw grid of images self.co_ords = [] self.a = [] self.rects = [] for i in range(16): x, y = (i % 4) * self.col_width, (i / 4) * self.row_height self.a.append(x) self.a.append(y) self.co_ords.append(self.a) self.a = [] self.rects.append( Rect( x, y, self.col_width, self.row_height )) for i in range(len(self.co_ords)): image(self.images[i][0], self.co_ords[i][0], self.co_ords[i][1], self.col_width, self.row_height) # Pick random image self.random_image() # Set self.score to 0 self.score = 0 # Display word and score self.update_score() self.update_answer() def update_score(self): # display score text("Score: " + str(self.score), font_name='ChalkboardSE-Bold', font_size=24.0, x = self.w / 20, y = self.size.h - 100, alignment = 6) def update_answer(self): # display legible version of answer text(self.images[self.current_image][1], font_name='ChalkboardSE-Bold', font_size=72.0, x = self.w * 0.5, y = self.size.h - 100, alignment=5) def random_image(self): # choose a random image self.current_image = random.randint(0, len(self.images) - 1) def touch_ended(self, touch): # check touch.location against list of self.rects for n, r in enumerate( self.rects ): if touch.location in r: # if touch.location is within correct image: if n == self.current_image: sound.play_effect('Powerup_1') self.score += 1 self.update_score() self.random_image() self.update_answer() return # if touch.location is outside correct image: if n != self.current_image: sound.play_effect('Error') self.score -= 1 self.update_score() return run(MyScene())
-
# converts an image name to a lowercase animal name with spaces between each letter def animal_name(image_name): name = image_name.partition('_')[0].lower() name = { 'bug': 'caterpillar', 'honeybee': 'bee' }.get(name, name) return ' '.join(name) self.images = '''Ant Bear_Face Bug Chicken Cow_Face Dog_Face Elephant Fish Frog_Face Honeybee Pig_Face Snail Snake Whale Tiger_Face Rabbit_Face'''.split()
-
Thank you ccc for addressing the line I was most ashamed of!
-
I think it is ready for our little beta tester...
# See: http://omz-forums.appspot.com/pythonista/post/5883071282806784 import random import scene import sound font_name = 'ChalkboardSE-Bold' # convert an image name to a lowercase # animal name with spaces between each letter def animal_name(image_name): name = image_name.partition('_')[0].lower() name = { 'bug': 'caterpillar', 'honeybee': 'bee' }.get(name, name) return ' '.join(name) class MyScene(scene.Scene): def setup(self): # Setup screen with top 200 pixels reserved for text self.w, self.h = self.size.w, self.size.h - 200 self.col_width = self.w / 4 self.row_height = self.h / 4 # Create a shuffled list of image names self.images = '''Ant Bear_Face Bug Chicken Cow_Face Dog_Face Elephant Fish Frog_Face Honeybee Pig_Face Snail Snake Whale Tiger_Face Rabbit_Face'''.split() random.shuffle(self.images) # Create a grid of image Layers for i in range(16): rect = scene.Rect((i % 4) * self.col_width, (i / 4) * self.row_height, self.col_width, self.row_height) layer = scene.Layer(rect) layer.image = self.images[i] self.add_layer(layer) # Pick random image self.current_image = random.choice(self.images) # Set self.score to 0 self.score = 0 def different_image(self): new_image = random.choice(self.images) if new_image == self.current_image: return self.different_image() return new_image def draw(self): scene.background(0, 0, 0) self.root_layer.update(self.dt) self.root_layer.draw() self.draw_text() def draw_text(self): name = animal_name(self.current_image) # display legible version of answer scene.text(name, font_name=font_name, font_size=72.0, x = self.size.w * 0.5, y = self.size.h - 100, alignment = 5) # display score msg = "Score: %d" % self.score scene.text(msg, font_name=font_name, font_size=24.0, x = self.size.w / 20, y = self.size.h - 100, alignment = 6) def touch_ended(self, touch): # check touch.location against list of self.rects for layer in self.root_layer.sublayers: if touch.location in layer.frame: # if touch.location is within correct image: if layer.image == self.current_image: sound.play_effect('Powerup_1') self.score += 1 self.current_image = self.different_image() # if touch.location is outside correct image: else: sound.play_effect('Error') self.score -= 1 return scene.run(MyScene())
-
suggestion:
self.current_image = random.choice([im for im in self.images if im is not self.current_image])
or possibly
self.current_image = random.choice(set([self.current_image]).symmetric_difference([self.images])
-
DRY... Nice. I added
self.different_image()
to the code above so you Don't Repeat Yourself.I did like the symmetric_difference thing but went for readability (I must be spending too much time with golang!).
-
Fantastic - thank you. While my daughter learns her animal words, I will be studying your code and working out where I went wrong!
-
Guys, is using scene over the top for this app? I thought just using ui would be a smarter choice in this case. I know, it was asked how to solve a problem with scene. I thought the right response would be to use the ui instead. So much easier as a beginner. To extend the functionality will be so much easier in my opinion. I have written many flash card style learning programs to teach myself thai in the past, not in python. But still same concept.
Ok, this is why there is a forum, discussion. If I am wrong , it's ok. But that is what goes through my mind -
@richwoodham, What is the name of this game? What should we call it?
-
@ccc My incredibly unimaginative working title was 'Image_ID', but I'll see if I can think of something better. Although you ought to have naming rights, as it was you who got it working! My daughter has been enjoying using it, and it's definitely helping her recognise letters and words, so thank you again for your help with it.
@Phuket2 I have experimented a little with ui in Editorial recently, and in hindsight I agree that using it for this app would have made sense. I think I originally went with scene because, compared to ui, I was able to make sense more easily of the Pythonista example scripts which used it. I realise now that ui would have done a lot of the work for me. I might have learned a little less, though.
-
@richwoodham, I understand what you mean about the learning curve. I have always leaned towards ui style interface programming. But in the old days on the mac 68000 series processors, I had to get my hands dirty with bitmaps. The computers were too slow! It was hard in the beginning, but it comes to you. Just practice and familiarity. Being so long out of the game, bitmaps scare me i guess, even though they shouldn't. But to be fair, even back in those days the Macintosh toolbox had the best graphic primitives built in.
-
I have been thinking about doing .ui based implementation of
Image_ID
to see if it would be much easier... If I get some hacktime this weekend, I will send you details. Can someone creative please suggest a more compelling name? -
- Where's my Pet?
- Creature Find
- Friend Match
- Hidding Friends
- Zoo Creatures
- Zoo Find
- Animal Match
- Still thinking...
-
scene seems like a perfectly valid implementation.... it is after all meant for things like games. perhaps a customized Layer that has the word as an attribute would make keeping track of the word easier.
another improvement idea... have a larger set of pictures/words, and when you get the correct answer, swap out the picture with a different one. my young kids quickly figured out that they only really had to read the first letter, and could guess the animal.
-
I tightened up the code and posted
AnimalMatchScene.py
.If I get more hacktime, it will take a shot at creating an AnimalMatchView.
a customized Layer that has the word as an attribute would make keeping track of the word easier.
I initially thought the same way and created an AnimalLayer subclass. But then I realized that Layer already gives you that functionality if you can use the image name (Layer.image) as that word or to lookup that word (see:
animal_name()
). So I got removed AnimalLayer. -
OK... We have two working versions:
AnimalMatchScene
in 71 lines vs.AnimalMatchView
in 105 lines. These are not 100% equivalent implementations yet 34 lines of difference shows that ui apps require more code then scene apps (when not using .pyui files). Creating and positioning ui elements requires several lines of code per element. The counter argument is that ui views are more object oriented, easier to maintain, and more pleasing for users.Run both and see which you (and your kids) like best. Send pull requests if you see a better approach. "Star" the repo if the code helps you in your Pythonista adventures
-
I would have thought for a flash card style game ui would be easier than Scene. As pointed out, more code, but simple positioning code. But to make a grid/content class , a Results bar class , maybe a status bar class and then a container class to bring it all together, many games could be derived, ultimately ending up in a lot less code. Example alphabet game, word game, simple puzzle game etc...
But we know what we know. I don't know that much.