I am trying to make a button interface for my program which is imported as rlg. rlg has a live graphing system in which two variables can be measured and updated as the generations of the simulation go on. I want to be able to make these two variables selective so i made a dictionary 'graphLines' in the main() method of rlg in which each string selection on the drop down menu acts as a key. However i dont seem to be able to access it and get the error message: AttributeError: 'function' object has no attribute 'graphLines'. Can anyone see what i am doing wrong.
from Tkinter import *
import runlivegraph3 as rlg
def run():
rlg.main()
def setLine1(name):
rlg.main.Line1data = rlg.main.graphLines[name] #graphlines is a dictionary in runlivegraph3 main method
def setLine2(name):
rlg.main.Line2data = rlg.main.graphLines[name]
root = Tk()
var1 = StringVar()
var1.set("select graph line 1 data") #initial variable in drop down menu, each string is a key in the graphLines dictionary
op1 = OptionMenu(root, var1, 'Political attacks in turn',
'Ethnic attacks in turn',
'Total attacks in turn',
'Ethnic attacks as a percentage of total attacks',
'Political attacks as a percentage of total attacks',
'Group 1 ethnic antagonism',
'Group 2 ethnic antagonism',
command = setLine1).pack()
var2 = StringVar()
var2.set("select graph line 2 data") #initial variable in drop down menu
op2 = OptionMenu(root, var2, 'Political attacks in turn',
'Ethnic attacks in turn',
'Total attacks in turn',
'Ethnic attacks as a percentage of total attacks',
'Political attacks as a percentage of total attacks',
'Group 1 ethnic antagonism',
'Group 2 ethnic antagonism',
command = setLine2).pack()
butn = Button(root, text = 'run', command = run)
butn.pack()
root.mainloop()
this is the main() function of the program i am importing in the Tkinter button program
from matplotlib.pylab import *
import sys, random, time, csv
def main():
IDs = {}
boardDims = (20,20)
Line1data = None
Line2data = None
turnLimit = 40
pause = 0.0001
ethnicPred = []
politicalPred = []
totalAttacks = []
generation = []
data1 = []
data2 = []
data3 = []
ethAnt1 = []
ethAnt2 = []
polAnt1 = []
polAnt2 = []
EthnicAttacksInTurn = []
PoliticalAttacksInTurn = []
TotalAttacksInTurn = []
ProportionEth = []
ProportionPol = []
board = make_board(boardDims)
finallyAddAgents(IDs, board, boardDims)
splitAgents(IDs)
setRemainingPolitics(IDs)
setPoliticalAntagonism(IDs)
turn = 0
line1, = plot(turn, 0, 'b') #initialise lines
line2, = plot(turn, 0, 'r')
running = 1
while running:
ion() #sets up graph base and axes
axes()
xlim(0,turnLimit)
ylim(0,30)
if turn == turnLimit: running = 0
print_board3(IDs, board, boardDims)
print 'turn ', str(turn)
polAttackTurn = []
ethAttackTurn = []
AllAgentsPerformActions(IDs, board,turn,ethnicPred, politicalPred,
totalAttacks,polAttackTurn,ethAttackTurn)
totalAttackTurn = sum(ethAttackTurn) + sum(polAttackTurn)
if totalAttackTurn != 0:
propEth = (sum(ethAttackTurn)*100)/totalAttackTurn
propPol = (sum(polAttackTurn)*100)/totalAttackTurn
if totalAttackTurn == 0:
propEth = 0
propPol = 0
TotalAttacksInTurn.append(totalAttackTurn)
EthnicAttacksInTurn.append(sum(ethAttackTurn))
PoliticalAttacksInTurn.append(sum(polAttackTurn))
ProportionEth.append(propEth)
ProportionPol.append(propPol)
k = sum(politicalPred)
j = sum(ethnicPred)
#f = sum(totalAttacks)
#print k, j, f
data1.append(j)
data2.append(k)
#data3.append(f)
generation.append(turn)
for agent in IDs.values():
if agent.group == '1':
ethAnt1.append(agent.antagonism['2'])
break
for agent in IDs.values():
if agent.group == '2':
ethAnt2.append(agent.antagonism['1'])
break
for agent in IDs.values():
if agent.politics == 'A':
polAnt1.append(agent.polAntagonism['B'])
break
for agent in IDs.values():
if agent.politics == 'B':
polAnt2.append(agent.polAntagonism['A'])
break
#this is the dictionary i am trying to access from the Tkinter button program
graphLines = {'Political attacks in turn':sum(polAttackTurn),
'Ethnic attacks in turn':sum(ethAttackTurn),
'Total attacks in turn':totalAttackTurn,
'Ethnic attacks as a percentage of total attacks': propEth,
'Political attacks as a percentage of total attacks': propPol,
'Group 1 ethnic antagonism': ethAnt1[-1],
'Group 2 ethnic antagonism': ethAnt2[-1]}
line1.set_ydata(append(line1.get_ydata(), Line1data))
line1.set_xdata(append(line1.get_xdata(), turn))
line2.set_ydata(append(line2.get_ydata(), Line2data))
line2.set_xdata(append(line2.get_xdata(), turn))
draw()
turn += 1
I figured I'd better turn my comment into an answer, so here I go.
You are getting confused between the difference between variables and attributes, so I'll explain the difference with some examples. Your question is not a matter of importing actually, but more about scope and object oriented programming (OOP).
(e.g. 1) To set a local variable within a function, you can do:
def spam():
eggs = 5
(e.g. 2) To set an attribute on a function object (which is usually not so logical), you can do:
def spam():
pass
spam.eggs = 5
While these may appear to be similar, their effect is very different. In the first example, eggs is a local variable within the function spam. Local variables are created, accessed, and modified only within their defining function.
def spam():
eggs = 5
print spam.eggs
will result in an error, however
def spam():
pass
spam.eggs = 5
print spam.eggs
will not. In the second example, eggs is an attribute of the function (object) spam. It can be created, accessed, and modified both within a method of the object or outside the object, but not within the function itself as a local variable (also because the function does not know of its existence until it is fully defined). Therefore, the following would raise an error:
def spam():
print eggs
spam.eggs = 5
spam()
because eggs is an attribute, not a local variable.
If you are familiar with OOP, here is some expansion:
The first example would be equivalent to:
class Spam(object):
def __init__(self):
eggs = 5
while the second example would be equivalent to:
class Spam(object):
def __init__(self):
self.eggs = 5
In terms of OOP, the difference is simply that the first sets a local variable, while the second sets an instance variable. Trying to do Spam().eggs on the first class would not make sense, while on the second it could.
Finally,
To solve your problem, either define the variables you need outside of a function, or use the global keyword to show that they are global. Example usage:
def spam():
global eggs
eggs = 5
spam()
print eggs # Prints 5
Related
I've recently been refactoring some of my code to use OOP, and I've run into a problem where I can't quite get either global vars, exec(), or a combination of the two to work. The relevant code is below:
# invObject class. Has all properties of an inventory object.
class invObject:
def __init__(self, name, info, output, effect):
self.name = name # Used in menus.
self.info = info # Describes effect.
self.output = output # Printed on use.
self.effect = effect # Executed on use.
def printInfo(self): # Function for name and description.
return "{} - {}".format(self.name, self.info)
def use(self): # Function to use items. It's that easy.
global dodgeChance
global maxHp
global hp
global atk
exec(self.effect)
print(self.output) # Prints output. Also very simple.
print("{} {} {} {}".format(dodgeChance, maxHp, hp, atk)) # debugging
...
inventory[slot].use() # does not update values
Basic rundown: inventory[slot].use() should call the use() function of the object. use() should execute the code stored in inventory[slot].effect.
The output from the debugging line doesn't change anything, even inside the function. I've already tried making it return exec(self.effect) to no avail. print(self.output) does work.
EDIT: Here's a minimal reproducible example that includes everything it needs to run, not just the most important things.
# Assign base stats
dodgeChance = 0
maxHp = int(input("Input maximum HP. > "))
hp = int(input("Input current HP. > "))
# invObject class. Has all properties of an inventory object.
class invObject:
def __init__(self, name, info, output, effect):
self.name = name # Used in menus.
self.info = info # Describes effect.
self.output = output # Printed on use.
self.effect = effect # Executed on use.
def printInfo(self): # Function for name and description.
return "{} - {}".format(self.name, self.info)
def use(self): # Function to use items. It's that easy.
global dodgeChance
global maxHp
global hp
global atk
exec(self.effect)
print(self.output) # Prints output. Also very simple.
print("{} {} {} {}".format(dodgeChance, maxHp, hp, atk)) # debugging
empty = invObject("None", "Vacant slot.", "There's nothing in that slot!", "")
apple = invObject("Apple", "Gives 20 health.", "Ate the apple. Gained 20 health.", "hp = hp + 20\nif hp > maxHp: hp = maxHp")
drink = invObject("Drink", "Some kind of energy drink. Raises dodge chance to 75%.", "Drank the drink. Dodge chance is now 75%!", "dodgeChance = 75")
# Assign base inventory
inventory = [apple, drink, empty]
slot = int(input("Input slot number to use. ")) - 1
inventory[slot].use() # does not update values
# Show final stats
print("New HP value: " + str(hp))
print("Dodge chance: " + str(dodgeChance) + "%")
print()
print("Inventory contents:")
print("Slot 1: " + str(inventory[0].name))
print("Slot 2: " + str(inventory[1].name))
print("Slot 3: " + str(inventory[2].name))
EDIT 2: Another thing: the code works if I don't use exec() (e.g. change it out for hp += 20).
exec() has optional arguments for you to provide the global and local variable contexts.
But you didn't provide them.
I was trying to make a quiz with tkinter and python classes(I have never used classes). Does anyone have a explanation to why I am getting this error "PS C:\Users\user_name> & python "c:/python projects/tkinter_w_classes.py"
File "c:/python projects/tkinter_w_classes.py", line 17
global selected = 'a'
^
SyntaxError: invalid syntax"
class Question():
def __init__(self,question, op_a,op_b,op_c,op_d, answer):
self.question = question
self.op_a = op_a
self.op_b = op_b
self.op_c = op_c
self.op_d = op_d
self.answer = answer
def makewin(self):
root = tk.Tk()
root.geometry('500x500')
def bexea():
global selected = 'a'
if selected == answer:
global score += 1
def bexeb():
selected = 'b'
if selected == answer:
score += 1
def bexec():
selected = 'c'
if selected == answer:
score += 1
def bexed():
selected = 'd'
if selected == answer:
score += 1
ql = tk.Label(text=question).pack()
ba = tk.Button(text=op_a,command=bexea).pack()
bb = tk.Button(text=op_b,command=bexeb).pack()
bc = tk.Button(text=op_c,command=bexec).pack()
bd = tk.Button(text=op_d,command=bexed).pack()
q1 = Question()
q1('What is 2 + 2', '1', '2', '3', '4', '4')
q1.makewin()
A global statement cannot perform assignment; it just marks the name as global, rather than local. All global statements must appear at the start of the function.
def bexea():
global selected, score
selected = 'a'
if answer == selected:
score += 1
There is little reason to make selected global, though. No function looks at what value it may have received previously; they all simply set a new value and use that. If you define selected at all, it can simply be a local variable.
def bexea():
global score
selected = 'a'
if selected == answer:
score += 1
There are other issues with your class design (including the use of any global variables), but that's beyond the scope of this question.
class Question():
selectecd = ''
def markSelected(self):
self.selected = 'a'
def printSelected(self):
print(self.selected)
This should be a proper class based approach. Don't use global ... it should be enough to have it in a instance based scope.
(For those who saw this question the last time I asked it, I sincerely apologize, I used the term "module" when I meant "function", but thank you for your very helpful advice nontheless! I'll make sure to keep it in mind when I begin to add other files into the equation.)
I'm trying to make a text based adventure game using python, and as a result it requires a lot of variables, and as backtracking is a must, I need to use global variables for the essential ones. I have run into speed bumps when trying to get these to be read by other functions. This is the line of code used to define the universal variables, and their starting value
def reset():
global gold, exp, etnl, maxHP, curHP, maxmana, curmana, attack, defence, helm, armtop, armbot, boots, gloves, weapons
gold = 0
exp = 0
etnl = 100 #exp to next level
maxHP = 50
curHP = 50
maxmana = 10
curmana = 10
attack = 5
defence = 5
helm = "none"
armtop = "none"
armbot = "none"
boots = "none"
gloves = "none"
weapon = "fists"
And for example, when I try to display one of the global variables, it shows up as the variable being undefined, as shown here:
def gamestart():
clear() #this command is fine, simply to make it look neater when it is run again
print("you wake up in a clearing in the forest, you can't remember what happened.")
print("you feel numb, you realize you're lying flat on your back.")
print
print("HP " + str(curHP) + "/" + str(maxHP))
Can someone help me out with this?
Is there an easier way to do this?
All help is appreciated!
(yes, I make sure to run the reset function before the newgame function)
A much simpler version if this, at least according to me is:
def variable():
global foo
foo = 7
def trigger():
variable():
output():
def output():
print(foo)
You could store those things into a class used as storage-container. If you declare them classvariables and any accessors as #classmethods you do not need an instance.
class GameState:
gold = 0
exp = 0
etnl = 100 #exp to next level
maxHP = 50
curHP = 50
maxmana = 10
curmana = 10
helm = "none"
armtop = "none"
armbot = "none"
boots = "none"
gloves = "none"
weapon = "fists"
weapons = {"fists":(5,5),"sword":(15,12),"mace":(30,3),"cushion":(2,20)}
#classmethod
def reset(cls):
cls.gold = 0
cls.exp = 0
cls.etnl = 100 #exp to next level
cls.maxHP = 50
cls.curHP = 50
cls.maxmana = 10
cls.curmana = 10
cls.helm = "none"
cls.armtop = "none"
cls.armbot = "none"
cls.boots = "none"
cls.gloves = "none"
cls.weapon = "fists"
#classmethod
def attack(cls):
return cls.weapons.get(cls.weapon,(0,0))[0]
#classmethod
def defense(cls):
return cls.weapons.get(cls.weapon,(0,0))[1]
for w in State.weapons:
State.weapon = w
print("{} has attack {} and defense {}.".format(w, State.attack(),State.defense()))
Output:
fists has attack 5 and defense 5.
sword has attack 15 and defense 12.
mace has attack 30 and defense 3.
cushion has attack 2 and defense 20.
You might want to seperate some things out - f.e. an extra class for the weapon/damage/defense related stuff ...
More reading:
What is the difference between #staticmethod and #classmethod?
https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables
Instead of global variables have you considered storing all the stats in a class/struct? Create an instance of the class at the start of the game, with its default values being specified in the constructor.
G = StartClass()
def gamestart():
print("you wake up in a clearing in the forest, you can't remember what happened.")
print("you feel numb, you realize you're lying flat on your back.")
print("HP " + str(G.curHP) + "/" + str(G.maxHP))
Alternatively, declaring G globally and passing it into gamestart(G) and/or re-instantiating in the reset() function might be options.
Here is a simple example of what I think you are trying to accomplish. If you are using global variables, then you need to be sure you are not inadvertently creating local variables with the same names in your functions (when you mean to be modifying the global variable).
You should look at using classes which I think would help you with some of the semantic confusion here.
value = 10
def reset():
global value
value = 10
def start():
print(f'initial value: {value}')
global value
value += 1
print(f'updated value: {value}')
reset()
print(f'reset value: {value}')
start()
# OUTPUT
# initial value: 10
# updated value: 11
# reset value: 10
I am writing a function to load a previously saved game in chess using Pygame. I have six classes: main2, GUI2, navigation2, calc, board and pieces. I store the attribute that stores where each chess piece is on the board class like so:
class board:
def __init__(self,main):
self.main = main
self.pieces = self.main.pieces
self.prv_pos = []
self.dict = self.pieces.dict
A = ["bRook","bKnight","bBishop","bQueen","bKing","bBishop","bKnight","bRook"]
B = ["bPawn","bPawn","bPawn","bPawn","bPawn","bPawn","bPawn","bPawn"]
C = [0,0,0,0,0,0,0,"wKing"]
D = [0,0,0,0,0,0,0,0]
E = [0,0,0,0,"wQueen",0,0,0]
F = [0,0,0,0,0,0,0,0]
G = ["wPawn","wPawn","wPawn","wPawn","wPawn","wPawn","wPawn","wPawn"]
H = ["wRook","wKnight","wBishop","wQueen",0,"wBishop","wKnight","wRook"]
self.piece_pos= [A,B,C,D,E,F,G,H]
I also have a class called main that is passed an instance on each class possible thus all my objects interact with each other through the main class. Thus in my navigation class that hosts the functions to save and load the game I wrote this:
class navigation:
def __init__(self,GUI):
self.GUI = GUI
self.main = self.GUI.main
self.piece_pos = self.GUI.main.board.piece_pos
self.GUI.draw_button("save",self.GUI.SQ_DIM*8,self.GUI.SQ_DIM)
self.GUI.draw_button("load",self.GUI.SQ_DIM*8,self.GUI.SQ_DIM*3)
self.GUI.draw_button("exit",self.GUI.SQ_DIM*8,self.GUI.SQ_DIM*5)
def game_save(self):
file = open("Save file.txt","w")
for line in self.piece_pos:
for item in line:
file.write(str(item)+",")
file.write("\n")
file.close()
def game_load(self): #WIP
self.piece_pos = []
self.GUI.draw_board()
As you may notice that the game_load function is pretty empty; that is because I wanted to check that in the self.piece_pos = [] line that the attribute is actually cleared throughout the classes. But when I call the self.GUI.draw_board() function which just draws the current board positions which are stored in piece_pos in the class board the board's appearance in the GUI is the same. I expect an error message in the python shell telling me there is no self.piece_pos[i][j] but it seems to me that the attribute hasn't changed whatsoever.
The function for draw_board() is stored in the class 'GUI'.
def draw_board(self):
X = 0
Y = 0
Ycounter = False
sqcounter = False
#Draw the board
print("GUI update")
for i in range(0,8):
for j in range(0,8):
print()
print("Piece at:")
print(i,j)
print(self.main.piece_at(i,j))
pg.draw.rect(self.window, self.sq_colour[i][j],(X,Y,self.SQ_DIM,self.SQ_DIM),0)
if self.main.piece_at(i,j) == 0:
print("square empty")
pass
else:
print("square occupied")
self.window.blit(self.dict[self.main.piece_at(i,j)],(X,Y))
#Change of X coord
if X >(self.SQ_DIM*6):
X = 0
Ycounter = True
sqcounter = True
else:
X +=(self.SQ_DIM)
#Change of Y coord
if Ycounter == True:
Y +=self.SQ_DIM
Ycounter = False
else:
pass
So I have come to the conclusions that I am not understanding something about how to globalize the piece_pos attribute. But I don't know how to solve this conundrum? Any ideas why?
This is because in game_load(), self.piece_pos refers to navigation.piece_pos, while draw_board() uses the object referenced by GUI.main.board.piece_pos.
You're effectively doing this
a = [5]
b = a
print(a, b) # prints [5] [5]
# By reassigning `b` you only change what it refers to,
# it doesn't affect what it used to refer to before.
b = []
print(a, b) # prints [5] []
# Instead, if you make an *in-place* modification, then the underlying
# object will indeed change, as both names refer to the same object.
a = b = [5]
b.append(3)
print(a, b) # prints [5, 3] [5, 3]
If you really want to make a change, it should be
def game_load(self): #WIP
self.main.board.piece_pos = []
self.GUI.draw_board()
Since the navigation class is a wrapper of GUI, it might be best to get rid of self.piece_pos and use function calls to get/set the piece positions, e.g.,
def get_piece_pos(self):
return self.main.board.piece_pos
def set_piece_pos(self, new_pos):
self.main.board.piece_pos = new_pos
Overall though, I don't see the point of this class. The save/load/exit buttons belong to the GUI, as should the respective save/load functions. I would just integrate this functionality in the GUI class, as this is the object the user interacts with.
Hi I'm making a program on python and I'm having trouble adding a global variable to my program so I'm just going to post my code and show you how I tried to do it.
So this is my class:
import globalvariables
class Bus :
def __init__(self, Number, Capacity, Destination, Seats):
self.Bus_Number = Number
self.Bus_Capacity = Capacity
self.Bus_Destination = Destination
self.Seats_taken = Seats
def Book(self):
self.Seats_taken = Seats + 1
def ShowBus(self):
return (str(self.Bus_Number) + ", " + str(self.Bus_Capacity) + ", " + str(self.Bus_Destination) + ", " + str(self.Seats_taken))
and this is my module for global variables
Seats = 0
and this is what I'm trying to run:
import Transport
import globalvariables
Big_Red = Transport.Bus(1, 50, "NYC", 0)
Big_Red.Book()
print(Big_Red.ShowBus())
I'm getting this error:
Traceback (most recent call last):
File "D:\Python\Assignment 3\Tester.py", line 5, in <module>
Big_Red.Book()
File "D:\Python\Assignment 3\Transport.py", line 14, in Book
self.Seats_taken = Seats + 1
NameError: global name 'Seats' is not defined
The variable Seats is local to __init__ function and can't be accessed outside of it.
So,
self.Seats_taken = Seats + 1
Should be :
self.Seats_taken = self.Seats_taken + 1
or :
self.Seats_taken += 1
Instead of using global variables inside class you should use class attributes:
class Bus :
seats = 50 #shared across all instances
def __init__(self):
#code
def Book(self):
self.Seats_taken = self.seats + 1
Globals should be avoided. In case you still want it to be :
def Book(self):
self.Seats_taken = globalvariables.Seats + 1
When you import globalvariables, you gain access to names qualified by the module name: globalvariables.Seats. To import Seats into the namespace of another module, use from globalvariables import Seats. (In desperate cases, you can import all names from a module: from globalvariables import *, but usually you don't want this.)
When you define a function, it has its own local namespace. It includes all function's arguments.
Seats = 100
def foo(Seats):
# returns the value of variable named Seats
# defined within "def foo", *not* defined by "Seats = 100"
return Seats
print foo(200) # prints 200
print foo() # fails because Seats are not set
To initialize a function parameter, use default value:
def foo(seats=0):
print foo() # prints 0
print foo(55) # prints 55
Also, global variables are evil. Global constants are good.
You want to use a global variable to track seats taken. You'll be much better off if you encapsulate it into a class that only allows reasonable access, does not allow to set the value arbitrarily, log access if needed, etc:
class SeatsDispenser(object):
def __init__(self, initial_count):
self.count = initial_count
def allocate(self, number_of_seats):
self.count -= number_of_seats
if self.count < 0:
raise ValueError("Overcommitted!")
def seats_left(self):
return self.number_of_seats
Naming your variables, classes, constants, and functions with the same Title Case is impractical. Usually variables are lower_case, functions are lowerCamelCase, classes are TitleCamelCase and constants are ALL_CAPS.
A reasonable piece of code would look like this:
import constants # modules are usually lower case
import transport
def Bus(object):
def __init__(self, number, capacity, seats=constants.SEATS):
self.number = number
self.capacity = capacity
self.seats = seats
big_red = Bus(constants.NYC_BUS_NUMBER, 50, 25)
default_blue = Bus(1, 20) # seats not passed, the default value is used
seats_dispenser = SeatsDispenser(100)
seats_dispenser.allocate(big_red.count)
seats_dispenser.allocate(default_blue.count)
print seats_dispenser.seats.left()