I'm designing a Character Sheet from Dungeons & Dragons in Python, using Tkinter to take care of the graphical interface. However, I wanted to add an element (in this case a "proficiency" in a skill) to a list, if the checkbox corresponding to the same skill is active.
The checkbox buttons are working for some commands like exit ("root.destroy") but this in particulary doesn't seem to do anything.
I created a class Character, this Character has an empty Proficencies list and to add a proficiency to it, I created a function that did it whenever the matching checkbox value was set to True ("1")
import tkinter as tk
def modifier(score):
score = int(score)
return (score - 10) // 2
class Character:
proficiencies = []
abilities = {"Strength": 12, "Dexterity": 16, "Constitution": 14, "Intelligence": 10, "Wisdom": 16, "Charisma": 9}
skills = {"Athletics": 0, "Acrobatics": 0, "Sleight of Hand": 0, "Stealth": 0, "Arcana": 0, "History": 0,
"Investigation": 0, "Nature": 0, "Religion": 0,"Animal Handling": 0, "Insight": 0, "Medicine": 0,
"Perception": 0, "Survival": 0, "Deception": 0, "Intimidation": 0, "Performance": 0, "Persuasion": 0}
counter = 0
variables = []
skills = []
root = tk.Tk()
def addSkill():
if exec("var" + str(counter) + ".get() == 1"):
Character.proficiencies.append(skills[counter].replace('_', ' '))
elif exec("var" + str(counter) + ".get() == 0"):
pass
for skill in sorted(Character.skills.keys()):
skills.append(skill.replace(" ", "_"))
exec("var" + str(counter) + " = tk.IntVar()")
exec(skill.replace(" ", "") + "= tk.Checkbutton(root, text=skill, variable = var" + str(counter) + ", command = addSkill)")
exec(skill.replace(" ", "") + ".pack()")
variables.append("var" + str(counter))
counter += 1
counter = 0
root.mainloop()
index = 0
for skill in Character.skills:
if index == 0:
ability = "Strength"
elif index >= 1 and index <= 3:
ability = "Dexterity"
elif index >= 4 and index <= 8:
ability = "Intelligence"
elif index >= 9 and index <= 13:
ability = "Wisdom"
elif index >= 14:
ability = "Charisma"
if skill in Character.proficiencies:
Character.skills[skill] = 10 + (modifier(Character.abilities[ability]) + 2) * 2
else:
Character.skills[skill] = 10 + modifier(Character.abilities[ability]) * 2
index += 1
Here's an example of following Bryan Oakley's suggestion to avoid using exec() and not dynamically create named variables, I think you'll agree it's quite a bit easier to read and understand than the code you where using.
import tkinter as tk
SKILL_NAMES = ('Athletics', 'Acrobatics', 'Sleight of Hand', 'Stealth', 'Arcana',
'History', 'Investigation', 'Nature', 'Religion', 'Animal Handling',
'Insight', 'Medicine', 'Perception', 'Survival', 'Deception',
'Intimidation', 'Performance', 'Persuasion')
class Character:
proficiencies = []
abilities = {"Strength": 12, "Dexterity": 16, "Constitution": 14,
"Intelligence": 10, "Wisdom": 16, "Charisma": 9}
skills = dict.fromkeys(SKILL_NAMES, 0)
def modifier(score):
return (int(score) - 10) // 2
root = tk.Tk()
# Create tk.IntVars for holding value of each skill.
skill_vars = {skill: tk.IntVar() for skill in Character.skills}
# tkinter Checkbutton callback.
def addSkill(skill, var):
""" Add skill to proficiencies if Checkbutton is now checked. """
if var.get() == 1:
Character.proficiencies.append(skill)
# Create a Checkbutton corresponding to each skill.
for skill in Character.skills:
btn = tk.Checkbutton(root, text=skill, variable=skill_vars[skill],
command=lambda name=skill, var=skill_vars[skill]: addSkill(name, var))
btn.pack()
root.mainloop()
for index, skill in enumerate(Character.skills):
if index == 0:
ability = "Strength"
elif 1 <= index <= 3:
ability = "Dexterity"
elif 4 <= index <= 8:
ability = "Intelligence"
elif 9 <= index <= 13:
ability = "Wisdom"
elif index >= 14:
ability = "Charisma"
if skill in Character.proficiencies:
Character.skills[skill] = 10 + (modifier(Character.abilities[ability]) + 2) * 2
else:
Character.skills[skill] = 10 + modifier(Character.abilities[ability]) * 2
To whoever is having troubles with the same mistake, I think I've found the error.
Instead of:
def addSkill():
if exec("var" + str(counter) + ".get() == 1"):
Character.proficiencies.append(skills[counter].replace('_', ' '))
elif exec("var" + str(counter) + ".get() == 0"):
pass
I wrote:
def addSkill():
if var0.get() == 1:
Character.proficiencies.append(skills[0].replace('_', ' '))
if var1.get() == 1:
Character.proficiencies.append(skills[1].replace('_', ' '))
...
if var17.get() == 1:
Character.proficiencies.append(skills[17].replace('_', ' '))
And now it works :)
Related
I'm new to python and I am currently learning for loops in Tkinter and trying to build pin code GUI.
What I want to happen is when the back space button is pressed "<" the last item added to the list "Entered Pin" is removed and 1 Asterix is removed from TxtWindow? I'm wondering if there is a particular way in python to remove the last item added to a list when I am unaware how many items there will be in the list.
Thanks in advance my code is below
import tkinter as tk
window = tk.Tk()
def PinEntry(x):
global u
global counter
if u == 1:
TxtWindow.delete("1.0", "end")
u = u+1
if EntertedPin == DefaultPin:
window.destroy()
if x in range (len(EntryKeyList)):
EntertedPin.append (EntryKeyList [x])
TxtWindow.insert (tk.END, "*")
counter = counter +1
if x == 2:
EntertedPin.clear()
TxtWindow.delete("1.0", "end")
TxtWindow.insert (tk.END, "Enter Pin")
u = 1
coutner = 0
if x == 0:
EntertedPin.clear()
TxtWindow.delete("1.0", "end")
TxtWindow.insert (tk.END, "Enter Pin")
u = 1
counter = 0
if x == 12:
# Delete last number in the list and remove one asterix from the TxtWindow
EntertedPin = []
DefaultPin = [1,2,3,0]
u = 1
counter = -1
TxtWindow = tk.Text(window, relief = "sunken", width = 10, height = 1)
TxtWindow.insert (tk.END, "Enter Pin")
TxtWindow.grid (row = 1, column = 2)
x = 1
y = 1
a = 5
b = 4
c = 1
d = 3
e = 1
f = 2
g = 1
EntryKeyList = ["CLR", 0, "ENT", 1, 2, 3, 4, 5, 6, 7, 8, 9, "<" ]
for i in range (13):
KeypadBtn = tk.Button(window, width = 5, height = 3, text = EntryKeyList[i], command = lambda x=i: PinEntry(x))
if y <=3:
KeypadBtn.grid (row = a , column = x )
x = x +1
y=y +1
elif y <= 6:
KeypadBtn.grid (row = b , column = c )
c = c +1
y=y+1
elif y <= 9:
KeypadBtn.grid (row = d , column = e )
e = e +1
y=y+1
elif y <=12:
KeypadBtn.grid (row = f , column = g )
g = g +1
y=y+1
else:
KeypadBtn.grid (row = 1, column = 3)
window.mainloop()
The commands what you are looking for
TxtWindow.delete("end-2c", "end")
mylist=mylist[:-1] or mylist.pop()
But the code is wrong. The backspace cannot add "x" to widget.
def PinEntry(x):
global u
global counter
global EntertedPin
global DefaultPin
if u == 1:
TxtWindow.delete("1.0", "end")
u = u+1
if x in range (len(EntryKeyList)):
if x == 12:
# Delete last number in the list and remove one asterix from the TxtWindow
EntertedPin=EntertedPin[:-1]
TxtWindow.delete("end-2c", "end")
# If the button is not number:
else:
if x == 2:
EntertedPin.clear()
TxtWindow.delete("1.0", "end")
TxtWindow.insert (tk.END, "Enter Pin")
u = 1
coutner = 0
elif x == 0:
EntertedPin.clear()
TxtWindow.delete("1.0", "end")
TxtWindow.insert (tk.END, "Enter Pin")
u = 1
counter = 0
EntertedPin.append (EntryKeyList [x])
TxtWindow.insert (tk.END, "*")
counter = counter +1
if EntertedPin == DefaultPin:
window.destroy()
Hello ! I have a question guys.
I was writing my first serious python training project because I'm beginner but I encountered a problem that stops me from further development of my program.
I don't have idea how can I write function/module in my class that check if player X or player Y has won. I tried on so many different ways but it seems to not work. I know that the my code is looking awful. Thank you for your time spent.
import sys
class Tic_tac_toe():
def __init__(self):
self.board = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
self.move_X = None
self.move_0 = None
self.WINNING_MOVE = None
self.loop = None
self.nameX = None
self.nameO = None
self.choice = None
def welcome(self):
try:
print("Welcome ! :)\n\nWho is PLAYER X ? :")
self.nameX = input()
print("\nWho is PLAYER O ? :")
self.nameO = input()
print("\nHello {} and {}, ready to play? (Y/N) :".format(self.nameX.title(), self.nameO.title()))
self.choice = input()
if self.choice.title() == 'N' or '\n':
sys.exit()
print('\n{} is PLAYER X.\n{} is PLAYER Y.'.format(self.nameX.title(),self.nameO.title()))
except ValueError:
print('\nTry again:\n')
def printBoard(self):
print()
print(self.board['top-L'] + '|' + self.board['top-M'] + '|' + self.board['top-R'])
print('-+-+-')
print(self.board['mid-L'] + '|' + self.board['mid-M'] + '|' + self.board['mid-R'])
print('-+-+-')
print(self.board['low-L'] + '|' + self.board['low-M'] + '|' + self.board['low-R'])
print()
def moves_X(self):
try:
self.move_X = int(input("Player X :\nChoose field (1,9) : "))
self.write_on_boardX()
self.printBoard()
except ValueError:
print("\nYOU DIDN'T ENTER NUMBER !\n")
def moves_O(self):
try:
self.move_O = int(input("Player O :\nChoose field (1,9) : "))
self.write_on_boardO()
self.printBoard()
except ValueError:
print("\nYOU DIDN'T ENTER NUMBER!\n")
def mix_XO(self):
self.loop = 0
for x in range(1,10):
if self.loop % 2 == 0:
self.moves_X()
self.loop += 1
elif self.loop % 2 == 1:
self.moves_O()
self.loop += 1
def write_on_boardX(self):
if self.move_X == 1:
self.board['top-L'] = 'X'
elif self.move_X == 2:
self.board['top-M'] = 'X'
elif self.move_X == 3:
self.board['top-R'] = 'X'
elif self.move_X == 4:
self.board['mid-L'] = 'X'
elif self.move_X == 5:
self.board['mid-M'] = 'X'
elif self.move_X == 6:
self.board['mid-R'] = 'X'
elif self.move_X == 7:
self.board['low-L'] = 'X'
elif self.move_X == 8:
self.board['low-M'] = 'X'
elif self.move_X == 9:
self.board['low-R'] = 'X'
def write_on_boardO(self):
if self.move_O == 1:
self.board['top-L'] = 'O'
elif self.move_O == 2:
self.board['top-M'] = 'O'
elif self.move_O == 3:
self.board['top-R'] = 'O'
elif self.move_O == 4:
self.board['mid-L'] = 'O'
elif self.move_O == 5:
self.board['mid-M'] = 'O'
elif self.move_O == 6:
self.board['mid-R'] = 'O'
elif self.move_O == 7:
self.board['low-L'] = 'O'
elif self.move_O == 8:
self.board['low-M'] = 'O'
elif self.move_O == 9:
self.board['low-R'] = '0'
def winning_movesX(self):
pass
def main():
app = Tic_tac_toe()
app.welcome()
app.printBoard()
app.mix_XO()
main()
One way to achieve this is to first create a map of all the possible winning 'lines' and then see if the elements on that line are all the same, by creating a set and checking it contains only 1 item (X or O). For example:
lines = [
['top-L','top-M','top-R'],
['top-L', 'mid-L', 'low-L']
... etc ...
]
for line in lines:
if len(set(self.board[x] for x in line])) == 1:
print("Winner:", self.board[line[0]])
Different Approach might be easier...
A simpler way would be to use a 3x3 matrix, e.g. with:
0 as a default value
-1 for player 1
+1 for player 2
You can then calculate the sum of the rows/columns/diagonals. With -3 as a player 1 win, +3 as a player 2 win. For example by using numpy:
>>> import numpy as np
>>> np.zeros((3, 3), dtype=int)
array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
=> self.board = np.zeros((3,3), dtype=int)
and a win check would roughly look like:
d1,d2 = 0,0 #used to calculate sum of diagonal
for i in range(3):
#check row
if sum(self.board[i, :]) in (3,-3):
self.win = True
#check column
elif sum(self.board[:, i]) in (3,-3):
self.win = True
#check diagonal
d1 += self.board[i, i] #diagonal from top left corner to bottom right corner
d2 += self.board[i, 2-i] #diagonal from top right corner to bottom left corner
elif d1 in (3,-3) or d2 in (-3,3):
self.win = True
Note: You know which player has won since you know who's turn it was last.
Well, and since you seem to be rather new to python here a quick overview on how to access elements of an array:
>>> board = np.zeros((3,3), dtype=int)
>>> board[0][1] = 3
>>> board
array([[0, 3, 0],
[0, 0, 0],
[0, 0, 0]])
And in case you keep this board:
"""
[1] | [2] | [3]
-----------------
[4] | [5] | [6]
-----------------
[7] | [8] | [9]
"""
you could use the integer division // and the modulo function % instead of all the elif statements to determine what field/position on the board the user meant:
position = int(input("Choose position:"))
row = (position-1)//3
column = (position-1)%3
self.board[row][column] = 1 #or -1
P.S.:
If you want to implement a "player vs. computer" later on, you might change the value -1 to -3 to get unique sum values. (-1 +1 +0 = 0 +0 +0)
Ok I have a feeling that this is a simple simple issue but I have been staring at this code for about 10 hours now.
The issue I am having is in mastermind is that once I get it to recognize that I have the correct colors in the right spot I can get it to display the right spots with X and the wrong spots with O. I need to be able to convert that so instead of X and O I need it to tell the user that he/she has 2 blacks and one white
For example: The secret code is RGYB The user enters RGOY so then Python relays "You have 2 blacks(The R and G spots) and one 1 White (The Y because it's the right color just in the wrong index) As of right now I got it to display X for the right color in the right spot and anything else it is an O
I will post what I have been working with now but today I am at my wit's end
https://pastebin.com/HKK0T7bQ
if correctColor != "XXXX":
for i in range(4):
if guess[i] == tempCode[i]:
correctColor += "X"
if guess[i] != tempCode[i] in tempCode:
correctColor += "O"
print (correctColor + "\n")
if correctColor == "XXXX":
if attempts == 1:
print ("You think you are sweet because you got it right on the first try? Play me again!")
else:
print ("Well done... You needed " + str(attempts) + " attempts to guess.")
game = False
A few comments
X and O
you use X and 0 to denote the success, it will be easier and faster to use a list or tuple or booleans for this, that way you can use sum() to count how many colors and locations were correct. Then whether you represent that with X and O or red and white pins is a matter for later
compartmentalization
Your game logic (guess input, input validation, do you want to continue, etc) is mixed with the comparison logic, so it would be best to separate the different functions of your program into different methods.
This is an fineexample to introduce object oriented programming, but is so simple it doesn't need OO, but it can help. What you need is a method which takes a series of colours and compares it to another series of colours
Standard library
Python has a very extended standard library, so a lot of stuff you want to do probably already exists
Correct colours
to count the number of letters which occur in 2 strings, you can use collections.Counter
guess = "RGOY "
solution = "RGYB"
a = collections.Counter(guess)
b = collections.Counter(solution)
a & b
Counter({'G': 1, 'R': 1, 'Y': 1})
correct_colours = sum((a & b).values())
3
So the user guessed 3 colours correctly
Correct locations
can be solved with an easy list comprehension
[g == s for g, s in zip(guess, solution)]
[True, True, False, False]
sum(g == s for g, s in zip(guess, solution))
2
so the used put 2 colours on the correct location
This is a MasterMind I made in Python. Hope you like it and it helped you! :)
import random
import time
from tkinter import *
def select_level():
global level
level = level_selector.get()
root.destroy()
root = Tk()
level_selector = Scale(root, from_=1, to=3, tickinterval=1)
level_selector.set(0)
level_selector.pack()
Button(root, text="Select a difficulty level", command=select_level).pack()
mainloop()
cpc_1_digit = 0
cpc_2_digit = 0
cpc_3_digit = 0
cpc_4_digit = 0
p_1_digit = 0
p_2_digit = 0
p_3_digit = 0
p_4_digit = 0
correct_correct = 0
correct_wrong = 0
chances = 0
if level == 1:
chances = 15
elif level == 2:
chances = 10
else:
chances = 7
cpc_1_digit = random.randint(0, 9)
while cpc_2_digit == cpc_1_digit or cpc_2_digit == cpc_3_digit or cpc_2_digit ==
cpc_4_digit:
cpc_2_digit = random.randint(0, 9)
while cpc_3_digit == cpc_1_digit or cpc_3_digit == cpc_2_digit or cpc_3_digit ==
cpc_4_digit:
cpc_3_digit = random.randint(0, 9)
while cpc_4_digit == cpc_1_digit or cpc_4_digit == cpc_2_digit or cpc_4_digit ==
cpc_3_digit:
cpc_4_digit = random.randint(0, 9)
while chances > 0:
correct_correct = 0
correct_wrong = 0
answer = input("Enter a four-digit number with different digits (e.g 1476): ")
p_1_digit = int(answer[0])
p_2_digit = int(answer[1])
p_3_digit = int(answer[2])
p_4_digit = int(answer[3])
if p_1_digit == cpc_1_digit:
correct_correct = int(correct_correct) + 1
elif p_1_digit == cpc_2_digit or p_1_digit == cpc_3_digit or p_1_digit ==
cpc_4_digit:
correct_wrong = int(correct_wrong) + 1
else:
pass
if p_2_digit == cpc_2_digit:
correct_correct = correct_correct + 1
elif p_2_digit == cpc_1_digit or p_2_digit == cpc_3_digit or p_2_digit ==
cpc_4_digit:
correct_wrong = int(correct_wrong) + 1
else:
pass
if p_3_digit == cpc_3_digit:
correct_correct = int(correct_correct) + 1
elif p_3_digit == cpc_1_digit or p_3_digit == cpc_2_digit or p_3_digit ==
cpc_4_digit:
correct_wrong = int(correct_wrong) + 1
else:
pass
if p_4_digit == cpc_4_digit:
correct_correct = int(correct_correct) + 1
elif p_4_digit == cpc_1_digit or p_4_digit == cpc_3_digit or p_4_digit ==
cpc_2_digit:
correct_wrong = int(correct_wrong) + 1
else:
pass
print("")
if int(correct_correct) == 4:
print("Congratsulations! You found the computer's number!")
break
elif int(correct_wrong) > 0 or int(correct_correct) >= 1 and int(correct_correct)
< 4:
print("You got " + str(correct_correct) + " correct digit(s) in the correct
place, and " + str(correct_wrong) + " correct digit(s) but in wrong place.")
elif int(correct_correct) == 0 and int(correct_wrong) == 0:
print("You didn't guess any number, try again!")
else:
raise Exception("CheckError: line 69, something went wrong with the
comparings.")
exit()
print("")
chances = chances - 1
if chances == 0:
print("You lost... The secret number was " + str(cpc_1_digit) + str(cpc_2_digit)
+ str(cpc_3_digit) + str(cpc_4_digit) + ". Try again by rerunning the program.")
time.sleep(4)
I'm trying to make a constant updater that adds 2 of a resource each second. The problem is, when I do time.sleep(1), it puts the whole program to sleep and if I don't put it, the program crashes.
def coins(self):
""" Creates the resource coins """
self.coins = 0
self.goldlabel = Label(self,text = str(self.coins) + " gold")
self.goldlabel.grid(row = 6, column = 0, columnspan = 1, sticky = W)
def residences(self):
self.residence = 0
def passivegold(self):
amount = self.residence
if self.residence > 0:
add = amount * 2
self.coins += add
# time.sleep(1)
self.goldlabel.configure(text = str(self.coins) + " gold")
I need help understanding how I can use the checkbox I've made turn a part of the program off when checked on and turn another part off when the checkbox is off. My idea is that when the checkbox is on, I want the addPercTip(self) section to be turned on and the addRateTip to be turned off, and vice-versa when the checkbox is off. PercTip off and RateTip on. My problem right now is that in my calculations, it is trying to take info from both parts, so one of them needs to be off. Any help would be enormously appreciated!
from Tkinter import *
class App(Tk):
def __init__(self):
Tk.__init__(self)
self.headerFont = ("Times", "16", "italic")
self.title("Restaurant Tipper")
self.addOrigBill()
self.addChooseOne()
self.addPercTip()
self.addRateTip()
self.addOutput()
def addChooseOne(self):
Label(self, text = "Check ON for % check OFF for rating!",
font = self.headerFont).grid(row = 2, column = 1)
self.checkVar = IntVar()
self.chkCheck = Checkbutton(self, variable = self.checkVar)
self.chkCheck.grid(row = 3, column = 1)
def calculate(self):
bill = float(self.txtBillAmount.get())
percTip = self.percVar
rateTip = int(self.scrScale.get())
tip = bill * percTip
self.lblTip["text"] = "%.2f" % tip
totalBill = tip + bill
self.lblTotalBill["text"] = "%.2f" % totalBill
if rateTip <= 2:
percTip = .10
elif 3 <= rateTip <= 4:
percTip = .12
elif 5 <= rateTip <= 6:
percTip = .15
elif 7 <= rateTip <= 8:
percTip = .17
elif 9 <= rateTip <= 10:
percTip = .20
else:
self.lblTotalBill["text"] = "Something is wrong"
def main():
app = App()
app.mainloop()
if __name__ == "__main__":
main()
When you instantiate a checkbutton you can set a command attribute. This is a function that will be called whenever the button is checked.
self.chkCheck(self, variable = self.checkVar, command = doStuff)
def doStuff(self)
print 'doing stuff'
EDIT:
As to the comment below:
def doStuff(self):
if self.checkVar.get() == 1: <<<1 is the 'checked value'
percTip = True
rateTip = False
However, you don't actually need to do that. In your calculate() function you could simply call self.checkVar.get() and if it is 1 then evaluate and if 0 (unchecked) then evaluate differently etc.