Pass element to multiple function in Python 3 - python

So I'm working on a program that shuffles cards in a deck and returns a picture of the card when the user hits the "deal" button. But I am trying to pass fileName from the deal function to the refreshImages function and I can't seem to get it to work. Wondering if someone can show me how and explain why. Thanks.
def deal(self):
card = self.deck.deal()
self.stateLabel["text"] = str(card)
fileName = "DECK/" + str(card.rank) + card.suit[0] + ".gif"
self.refreshImages(fileName)
if len(self.deck) == 0:
self.dealBtn["state"] = "disabled"
def shuffle(self):
card = self.deck.shuffle()
fileName = "DECK/" + str(card.rank) + card.suit[0] + "gif"
self.refreshImages(fileName)
if len(seld.deck) == 0:
self.dealBtn["state"] = "disabled"
def refreshImages(self):
"""Updates the images in the window."""
self.image = PhotoImage(file = fileName)
self.cardLabel1["image"] = self.image

Since your deal method calls the refreshImages method with a file name as an argument, the refreshImages method should be declared with such a parameter; otherwise the caller's local variable fileName won't get magically passed to the method being called:
def refreshImages(self, fileName):
"""Updates the images in the window."""
self.image = PhotoImage(file = fileName)
self.cardLabel1["image"] = self.image

Related

Nested Class - How can I use outer class variables for inner class

I created a super simple Character and Buildable class for a miniature game I am developing and I have created a nested class where Character is for the outer class and Buildable is the inner class. I am trying to loop through resources from Character inside my upgradeBuildable function within Buildable.
class Character:
# Constructor -- Also why do comments use # and not // -_-
def __init__(self):
self.name = "Pickle"
self.health = 100
self.resources = ["Fire", "Coal"]
self.buildObj = self.Buildable() # Object for nested 2nd class
# Function to display character info
def characterInfo(self):
print("CharacterInfo --> " + "Name:", self.name, "| Health:", self.health, "| Resources:", self.resources)
def printRes(self):
print(self.resources)
# Function for collecting resources
def collectResource(self, newResource):
self.resources.append(newResource)
class Buildable:
def __init__(self):
self.buildings = ["Fire 1"]
self.upgradeStatus = 0
self.outer = Character()
def displayBuildable(self):
print("Buildables -->", self.buildings)
def createBuildable(self, newBuilding):
self.buildings.append(newBuilding)
def upgradeBuildable(self, replaceBuilding):
if self.upgradeStatus == 0:
# Update selected building
for i in range(len(self.buildings)):
if self.buildings[i] == replaceBuilding:
self.buildings[i] = "2x"
break
print(Character.resources)
self.upgradeStatus = self.upgradeStatus + 1
print(self.upgradeStatus)
When I try to access print the resource attribute from Character in upgradeBuildable I get a recursion error "RecursionError: maximum recursion depth exceeded" which is confusing to me.
All I am trying to do is try and print the resources the character has inside my inner Buildable class. Any help is much appreciated !!
My main is followed below:
from BW_Classes import Character
pick = Character()
pick.characterInfo()
# Object created for inner class -- Buildable()
build = pick.buildObj
build.displayBuildable()
print()
print("Building......")
build.createBuildable("Fire 2")
build.createBuildable("Coal Mine")
build.createBuildable("Coal Supreme")
build.createBuildable("Ocean")
build.displayBuildable()
print()
print("Upgrading....")
build.upgradeBuildable("Fire 2")
build.displayBuildable()
I believe what you are look for is Inheritance. Inheritance basically means that a class inherits the properties and methods of another class, which I am assuming you are looking for when you say nested classes.
The error is being thrown because when you initialize the character, the function initializes the buildable. When the buildable is initialized, it initializes the character. See the loop?
Your code will probably look something like this:
class Character(Buildable):
def __init__(self):
self.name = "Pickle"
self.health = 100
self.resources = ["Fire", "Coal"]
#initialize the buildable class
super().__init__()
# Function to display character info
def characterInfo(self):
print("CharacterInfo --> " + "Name:", self.name, "| Health:", self.health, "| Resources:", self.resources)
def printRes(self):
print(self.resources)
# Function for collecting resources
def collectResource(self, newResource):
self.resources.append(newResource)
class Buildable():
def __init__(self):
self.buildings = ["Fire 1"]
self.upgradeStatus = 0
def displayBuildable(self):
print("Buildables -->", self.buildings)
def createBuildable(self, newBuilding):
self.buildings.append(newBuilding)
def upgradeBuildable(self, replaceBuilding):
if self.upgradeStatus == 0:
# Update selected building
for i in range(len(self.buildings)):
if self.buildings[i] == replaceBuilding:
self.buildings[i] = "2x"
break
print(Character.resources)
self.upgradeStatus = self.upgradeStatus + 1
print(self.upgradeStatus)

Python set class variable after calling

I'm making a game using the pygame module and I have a player class:
class Player(pygame.sprite.Sprite):
def __init__(self, name, position, axsis, movment, idle, walk = None, jump = None):
pygame.sprite.Sprite.__init__(self)
self.name = name
self.idle = idle
self.walk = walk
self.jump = jump
self.image = self.idle[0]
self.movment = movment
self.left, self.right = axsis
self.pos = vec(position[0],position[1])
I am adding my characters using json data type and trying to add animations after calling the class but i can't do it
Sample code
class Game():
def __init__(self,json_file):
self.player_attribute = json_file
def character(self):
self.npc = []
for i in self.player_attribute:
self.npc.append(Player(i['name'],
i['position'],
i['axsis'],
i['movment']))
self.animation()
return self.npc
def add_animation(self):
for i in self.npc:
i.idle = "images\ghost.png"
def main_loop()
self.character
when i try this i get an error
self.image = self.idle[0]
TypeError: init() missing 1 required positional argument: 'idle'
how can i add the variables of the class after calling the class
It is not clear what the 'idle' parameter is supposed to represent in your code. However, the reason for the exception is that you are not passing any argument for 'idle' when constructing the Player object. You need something like:
self.npc.append(Player(i['name'],
i['position'],
i['axsis'],
i['movment'],
i['idle']))
You can either do that or alternatively you can pass a default argument to the Player constructor so that, when initializing you do not need to explicitly pass idle:
class Player(pygame.sprite.Sprite):
def __init__(self, name, position, axsis, movment, idle=[1], walk = None, jump = None):
You can modify its content at a later time, however I suggest you are careful what you instantiate that object attribute as, because it might come bite you back later (type error or value error).
If it were me, I would move this out of init, or not build the object until all values and their types are known (see Builder Design Pattern).
Hope this helped :) Cheers!

Issue using images in Python Tkinter

I am creating a Blackjack GUI game with Tkinter and I'm running into an issue where the deal button clears the image of the old card from the screen when a new one is added. My educated guess is the card_image inside the deal() function is overwriting itself when I use the function again. If this is the case why is this and what's the best fix? Thanks.
import random
from tkinter import *
from PIL import Image, ImageTk
root =Tk()
root.title('21 Blackjack')
root.iconbitmap('images/21_cards.ico')
root.geometry('1280x750')
root.configure(bg='green')
cards = []
suits = ['hearts', 'clubs', 'diamonds', 'spades']
face_cards = ['ace', 'jack', 'queen', 'king']
extension = 'png'
for y in suits:
for x in range(2, 11):
name = 'images/{}-{}.{}'.format(str(x), y, extension)
cards.append(name)
for x in face_cards:
name = 'images/{}-{}.{}'.format(str(x), y, extension)
cards.append(name)
print(cards)
print(len(cards))
random.shuffle(cards)
print(cards[0])
hand = []
def deal():
global card_image, card_label, hand
card_image = ImageTk.PhotoImage(Image.open(cards[0]).resize((180, 245), Image.ANTIALIAS))
card_label = Label(root, image=card_image, relief="raised").pack(side="left")
hand += cards[:1]
cards.pop(0)
print(hand)
deal_button = Button(root, text="deal", command=deal).pack()
root.mainloop()
Instead of
card_image = ImageTk.PhotoImage(Image.open(cards[0]).resize((180, 245), Image.ANTIALIAS))
card_label = Label(root, image=card_image, relief="raised").pack(side="left")
Do:
card_label = Label(root, image=card_image, relief="raised").pack(side="left")
card_image = ImageTk.PhotoImage(Image.open(cards[0]).resize((180, 245), Image.ANTIALIAS))
card_label.image = card_image #Keeps reference to the image so it's not garbage collected
Do note when you want use the photo use the variable card_image
As people have pointed out I needed to add card_label.image = card_image to the function and remove card_image & card_label global to stop the image being removed. But for some reason Python didn't like having the image being packed before I did this.
The function now looks like this.
global hand
card_image = ImageTk.PhotoImage(Image.open(cards[0]).resize((180, 245), Image.ANTIALIAS))
card_label = Label(root, image=card_image, relief="raised")
card_label.image = card_image
card_label.pack(side="left")
hand += cards[:1]
cards.pop(0)
print(hand)
You should use an image pool. Lucky for you, I have one right here
import os
from glob import glob
from PIL import Image, ImageTk
from typing import List, Tuple, Union, Dict, Type
from dataclasses import dataclass
#dataclass
class Image_dc:
image :Image.Image
rotate :int = 0
photo :ImageTk.PhotoImage = None
def size(self) -> Tuple[int]:
if not self.photo is None:
return self.photo.width(), self.photo.height()
return self.image.width, self.image.height
class ImagePool(object):
##__> PRIVATE INTERFACE <__##
__PATHS = dict()
__IMAGES = dict()
#staticmethod
def __name(path) -> str:
return os.path.basename(os.path.splitext(path)[0])
#staticmethod
def __unique(path:str, prefix:str='') -> str:
name = f'{prefix}{ImagePool.__name(path)}'
if name in ImagePool.names():
sol = 'using a prefix' if not prefix else f'changing your prefix ({prefix})'
msg = ("WARNING:\n"
f"{name} was not loaded due to a same-name conflict.\n"
f"You may want to consider {sol}.\n\n")
print(msg)
return None
return name
#staticmethod
def __request(name:str) -> Image_dc:
if name in ImagePool.__PATHS:
path = ImagePool.paths(name)
if os.path.isfile(path):
if name not in ImagePool.__IMAGES:
ImagePool.__IMAGES[name] = Image_dc(Image.open(path))
return ImagePool.__IMAGES[name]
else:
raise ValueError(f'ImagePool::__request - Path Error:\n\tpath is not a valid file\n\t{path}')
raise NameError(f'ImagePool::__request - Name Error:\n\t"{name}" does not exist')
return None
#staticmethod
def __size(iw:int, ih:int, w:int=None, h:int=None, scale:float=1.0) -> Tuple[int]:
if not w is None and not h is None:
if iw>ih:
ih = ih*(w/iw)
r = h/ih if (ih/h) > 1 else 1
iw, ih = w*r, ih*r
else:
iw = iw*(h/ih)
r = w/iw if (iw/w) > 1 else 1
iw, ih = iw*r, h*r
return int(iw*scale), int(ih*scale)
##__> PUBLIC INTERFACE <__##
#staticmethod
def names(prefix:str='') -> List[str]:
names = [*ImagePool.__PATHS]
return names if not prefix else list(filter(lambda name, pre=prefix: name.startswith(pre), names))
#staticmethod
def paths(name:str=None) -> Union[Dict, str]:
if name is None:
return ImagePool.__PATHS
if name in ImagePool.__PATHS:
return ImagePool.__PATHS[name]
raise NameError(f'ImagePool::paths - Name Error:\n\tname "{name}" does not exist')
#staticmethod
def images(name:str=None, prefix:str='') -> Union[Dict, Image.Image]:
if name is None:
return {name:ImagePool.__request(name).image for name in self.names(prefix)}
return ImagePool.__request(name).image
#staticmethod
def photos(name:str=None, prefix:str='') -> Union[Dict, ImageTk.PhotoImage]:
if name is None:
return {name:ImagePool.__request(name).photo for name in self.names(prefix)}
return ImagePool.__request(name).photo
#staticmethod
def append_file(path:str, prefix:str='') -> Type:
if not os.path.isfile(path):
raise ValueError(f'ImagePool::append_file - Value Error:\n\tpath is not valid\n\t{path}')
name = ImagePool.__unique(path, prefix)
if name:
ImagePool.__PATHS[name] = path
return ImagePool
#staticmethod
def append_directory(directory:str, filters=['*.png', '*.jpg'], prefix:str='') -> Type:
if not os.path.isdir(directory):
raise ValueError(f'ImagePool::append_directory - Value Error:\n\tdirectory is not valid\n\t{directory}')
filters = filters if isinstance(filters, (List, Tuple)) else [filters]
for filter in filters:
for path in glob(f'{directory}/{filter}'):
ImagePool.append_file(path, prefix)
return ImagePool
#staticmethod
def photo(name:str, width:int=None, height:int=None, scale:float=1.00, rotate:int=None) -> ImageTk.PhotoImage:
image_t = ImagePool.__request(name)
size = ImagePool.__size(*image_t.size(), width, height, scale)
rotate = image_t.rotate if rotate is None else rotate
#only resize if the new size or rotation is different than the current photo size or rotation
#however, a small margin for error must be considered for the size
diff = tuple(map(lambda i, j: i-j, image_t.size(), size))
if (diff > (1, 1)) or (diff < (-1, -1)) or (image_t.rotate != rotate):
image_t.rotate = rotate
image_t.photo = ImageTk.PhotoImage(image_t.image.resize(size, Image.LANCZOS).rotate(rotate))
return image_t.photo
Using that file you can do a lot of things very easily. You can put all your card images in a folder and get them all with:
ImagePool.append_directory(path_to_folder)
You can then get any card and force it to fit in it's parent (no-matter-what) with:
somelabel['image'] = ImagePool.photo(image_name, allotted_width, allotted_height)
or just:
somelabel['image'] = ImagePool.photo(image_name)
you can get a list of every name in the pool with
deck = ImagePool.names()
or grab it while you also append a directory
deck = ImagePool.append_directory(path_to_folder).names()
That is very helpful to you, because you can simply shuffle and pop that list as the official "deck" in your game. Like this:
somecard['image'] = ImagePool.photo(deck.pop(0))
Assuming you will have more than just card graphics, but do not want to get all the images when creating a deck, there is a solution for that, as well.
first supply a prefix when appending the cards image directory, and then use that prefix when calling names(). As long as only card images were in the cards image directory, only card names will be returned.
deck = ImagePool.append_directory(path_to_folder, prefix='cards_').names('cards_')
notes:
allotted area is only allotted and in no way should be assumed to represent the final width and/or height of the actual image. The image will do whatever it has to to fit in the allotted space without losing it's original height and width ratio. If you don't supply allotted width and height the image will be it's full size.
All image and photo references are automatically maintained in ImagePool. There is no reason to store your own references
The entire class is static so references to all the images and photos can be accessed anywhere without an ImagePool instance. In other words, you never need to do this: images = ImagePool() and therefore never need to figure out how to get images into some class or other document.
No images actually load until you start requesting them, and then it happens automatically. This is a good thing. You have 52 cards, but if you only use ex: 6 in the first game ~ only 6 will fully load. Eventually all the cards will get played and be fully loaded, and you didn't have some huge burp in your game where you tried to fully create 52 cards all at once.
The ImagePool class has more features, but the ones explained here are all you should need for your game.

Python CodeSkulptor Pause Drawing from Inside For Loop

I want to build some visualizations for searching algorithms (BFS, A* etc.) within a grid.
My solution should show each step of the algorithm using CodeSkulptor simplegui (or the offline version using SimpleGUICS2Pygame.)
I've made a version which highlights all the cells visited by changing their color, but I've run into trouble trying to make the path display step-by-step with a time delay between each step.
I've extracted the essence of the problem and created a minimal example representing it in the code below, also run-able online here: http://www.codeskulptor.org/#user47_jB2CYfNrH2_2.py
What I want is during the change_colors() function, for there to be a delay between each iteration.
CodeSkulptor doesn't have time.sleep() available, and I don't think it would help anyway.
CodeSkulptor does have timers available, which might be one solution, although I can't see how to use one in this instance.
Code below:
import time
try:
import simplegui
except ImportError:
import SimpleGUICS2Pygame.simpleguics2pygame as simplegui
simplegui.Frame._hide_status = True
TITLE = "TEST"
FRAME_WIDTH = 400
FRAME_HEIGHT = 400
DELAY = 10
class Square:
"""This class represents a simple Square object."""
def __init__(self, size, pos, pen_size=2, pen_color="red", fill_color="blue"):
"""Constructor - create an instance of Square."""
self._size = size
self._pos = pos
self._pen_size = pen_size
self._pen_color = pen_color
self._fill_color = fill_color
def set_color(self, color):
self._fill_color = color
def get_color(self):
return self._fill_color
def is_in(self, pos):
"""
Determine whether coordinates are within the area of this Square.
"""
return self._pos[0] < pos[0] < self._pos[0] + self._size and self._pos[1] < pos[1] < self._pos[1] + self._size
def draw(self, canvas):
"""
calls canvas.draw_image() to display self on canvas.
"""
points = [(self._pos[0], self._pos[1]), (self._pos[0] + self._size, self._pos[1]),
(self._pos[0] + self._size, self._pos[1] + self._size), (self._pos[0], self._pos[1] + self._size)]
canvas.draw_polygon(points, self._pen_size, self._pen_color, self._fill_color)
def __str__(self):
return "Square: {}".format(self._pos)
def draw(canvas):
for square in squares:
square.draw(canvas)
def change_colors():
for square in squares:
# time.sleep(1) # Not implemented in CodeSkulptor and would'nt work anyway
square.set_color("green")
frame = simplegui.create_frame(TITLE, FRAME_WIDTH, FRAME_HEIGHT)
frame.set_draw_handler(draw)
width = 20
squares = []
for i in range(10):
squares.append(Square(width, (i * width, 0)))
change_colors()
frame.start()
Any help appreciated.
Yes, you need to use a timer. Something like this:
I = 0
def change_next_color():
if I < len(squares):
squares[I].set_color("green")
global I
I += 1
timer = simplegui.create_timer(1000, change_next_color)
timer.start()
http://www.codeskulptor.org/#user47_udyXzppCdw2OqdI.py
I also replaced
simplegui.Frame._hide_status = True
by simplegui.Frame._hide_controlpanel = True
https://simpleguics2pygame.readthedocs.io/en/latest/simpleguics2pygame/frame.html#SimpleGUICS2Pygame.simpleguics2pygame.frame.Frame._hide_controlpanel
See also _keep_timers option of SimpleGUICS2Pygame to help you:
https://simpleguics2pygame.readthedocs.io/en/latest/simpleguics2pygame/frame.html#SimpleGUICS2Pygame.simpleguics2pygame.frame.Frame._keep_timers
Possible improvements:
Find a better solution that don't use a global counter.
Stop timer when all work is finished.

How to create and keep a set of variables in python?

I'm developing an application that reads a message input from telegram with a set of variables, and then starts a game with the user. So I created a class that represents an instance of the game, making one game per chat possible:
class Battle:
def __init__(self, mainchat):
self.mainchat = mainchat
print('Instance of battle started on chat %s' % self.mainchat)
pcount = 0
team1 = []
team2 = []
p1 = ()
p2 = ()
p1score = 0
p2score = 0
battlechoicep1 = -1
battlechoicep2 = -1
so, as soon as I get a message, I start an instance of a battle based on user inputes, e.g.
battle = Battle(chat_id)
battle.p1 = 'Paul'
battle.battlechoicep1 = 4
...
this way has been working fine right now, but every time I want to reset the battle, I go through a function that does this:
battle.pcount = 0
battle.team1 = []
battle.team2 = []
battle.p1 = ()
battle.p2 = ()
battle.p1score = 0
battle.p2score = 0
battle.battlechoicep1 = -1
battle.battlechoicep2 = -1
save() # outside function that saves the scores into a pickle file
return
So, I would like to make it so this is a function inside my class, so everytime I call battle.reset it would call something like this
def reset():
battle.pcount = 0
battle.team1 = []
battle.team2 = []
battle.p1 = ()
battle.p2 = ()
battle.p1score = 0
battle.p2score = 0
battle.battlechoicep1 = -1
battle.battlechoicep2 = -1
save() # outside function that saves the scores into a pickle file
return
I don't know how is the right approach to this problem, I don't even know if what I've been doing up to now is 'correct' (it is working at least).
Creating the function inside the class (like def reset(self):) seems to have no effect.
You're on the right track with def reset(self). You just need to change the instances of battle to self in the method itself. NOTE: This needs to be a method of the Battle class.
def reset(self):
self.pcount = 0
... # etc
save() # outside function that saves the scores into a pickle file
When you pass in self as the first parameter of a class method, it allows the method to work on the instance of the class that you've called it on. If you just do def reset(self) without changing the battle to self, it will try to modify a variable in the current scope called battle, which in this case probably doesn't exist.
The other thing you could do if you just want reset to create a completely new object without preserving any of the attributes, you can just do:
def reset(self):
return Battle()
You're almost there!
class Battle:
def __init__(self, mainchat):
self.mainchat = mainchat
print('Instance of battle started on chat %s' % self.mainchat)
self.reset()
def reset(self):
self.team1, self.team2 = [], []
self.p1 = self.p2 = () #New tuples will be assigned and overwritten
self.pcount = self.p1score = self.p2score = 0
self.battlechoicep1 = self.battlechoicep2 = -1
save() # outside function that saves the scores into a pickle file
So when you need to reset, just call battle.reset()! Maybe the save function can also be a class method as well, just follow the same format.

Categories

Resources