I am building a rudimentary python program for tracking bullet fire in my local Rogue Trader campaign. I hate writing - erasing - rewriting on my sheet leaving it smudged and gross. This gives me an excuse to practice my coding skills. Eventually going to have it save values to a file and then read them upon start up, but that's in the future.
I am letting it ask for what guns I have, setting a clipSize for said gun, and then create a button that references each gun. Upon pressing the button, fireGun is supposed to take the value of the gun shot corresponding to which button is pressed. However, the way it runs currently, all guns fire from the same ammo amount which is the last 'clipSize' entered.
I need each button to track its own variable to update the correct dictionary reference upon fireGun.
from tkinter import *
addGuns = 'true'
gunList = {}
while (addGuns == 'true'):
newGun = input("What is the name of your gun? ")
clipSize = int(input("What is its clip size? "))
gunList[newGun] = clipSize
gunCheck = input("Done adding guns? ")
if (gunCheck == 'yes'):
addGuns = 'false'
root = Tk()
root.title("Pew Pew")
def fireGun(x):
startingAmmo = gunList[x]
endingAmmo = startingAmmo - 1
gunList[x] = endingAmmo
print(gunList[x])
return
for gun in gunList:
button = Button(root, text = gun, command = lambda name = gun:fireGun(gun))
button.pack()
root.mainloop()
Use partial to send parameters to a function with a command= call. You could also use an Entry to get the gun info, and one label for each gun with the name and updated ammo amount on each label. A very sloppy example (I have to go to work).
from tkinter import *
from functools import partial
gunCheck="no"
gunList = {}
while gunCheck != 'yes':
newGun = input("What is the name of your gun? ")
clipSize = int(input("What is its clip size? "))
gunList[newGun] = int(clipSize)
gunCheck = input("----->Done adding guns? ")
## if (gunCheck == 'yes'):
## addGuns = 'false'
root = Tk()
root.title("Pew Pew")
def fireGun(x):
startingAmmo = gunList[x]
endingAmmo = startingAmmo - 1
gunList[x] = endingAmmo
print(gunList[x])
label_list[x].config(text=x + "-->" + str(endingAmmo))
return
label_list={}
fr=Frame(root)
fr.pack(side="top")
for gun in gunList:
lab=Label(fr, text="%s --> %d" %(gun, gunList[gun]))
lab.pack(side=TOP)
label_list[gun]=lab
button = Button(root, text = gun, command = partial(fireGun, gun))
button.pack()
root.mainloop()
Related
I participated in a local programming competition yesterday, where I was put on a team of four with members of varying skill levels. We were trying to make a text-based Zork style game, but since UI was one of the categories I was put on creating a GUI which I have no experience with in python. I used Tkinter and got everything mostly working inside the GUI itself, but can't figure out how to get the GUI to interact with the rest of the code.
I'm just including the relevant bits here, partially because the rest of the code is quite a mess, with no comments and lots of syntax errors, but I can post it if needed. Here's the code:
def pinput():
global e
player_input = e.get()
print(player_input)
def progress():
hallway_1()
def hallway_1():
global player_input
global prin
global e
prin.set("You are in a hallway. Yellow lockers line the sides of all walls and you see three doors. One leads into a Library, one to a History classroom, and one to a Math room. You can 1.) Search the hallway or 2.) Procede to one of the rooms.")
while player_input != "1" and player_input != "2": #Best guess at a way to get the code to wait for input, actually just causes code to freeze
pass
if player_input == "1": #Even if 1 is in the entry box beforehand, it never gets here
prin.set("You search through the lockers and find 1 health potion.\n Would you like to add it to your inventory? 1.) Yes 2.) No")
player_input = "999"
def player_attack():
global enemy_hp
enemy_hp -= player_strength
prin.set("You just attacked the " + enemy_name + "!")
def player_block():
global block
prin.set("You predict that your enemy is going to attack and decide to block")
block = 1
def player_use():
global player_item_use
global item_name
player_item_use = 100
prin.set("You decide to use one of your items. What do you use?")
while player_turn_input != 0 or 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 or 9 or 10 or 11 or 12 or 13 or 14 or 15 or 16 or 17 or 18 or 19 or 20 or 21 or 22 or 23 or 24 or 25 or 26:
player_item_use = e.get()
time.sleep(1)
item_name = Inventory[player_item_use]
item(item_name)
def run_away():
global player_hp
global lost_item_1
global lost_item_2
prin.set("You decide to run away from the " + enemy_name + "!\nThis causes you to lose 20 health points and two inventory items.")
player_hp -= 20
if backpack == True:
lost_item_1 = random.randint(1,27)
lost_item_2 = random.randint(1,27)
else:
lost_item_1 = random.randint(1,9)
lost_item_2 = random.randint(1,9)
RemoveInventory(lost_item_1)
RemoveInventory(lost_item_2)
library()
top = tk.Tk()
prin = tk.StringVar()
#path = "smalllibrary.png"
#tkimage = ImageTk.PhotoImage(Image.open(path))
#displayimage = tk.Label(top, image=tkimage).grid(row=0)
inv = tk.Text(top, width="24")
inv.grid(row=0, column=1)
for x in Inventory:
inv.insert("end", str(x) + str(invnum) + '\n')
invnum += 1
healthbar = tk.Label(top, text=("Health: " + str(player_hp))).grid(row=1, column=1)
text = tk.Label(top, bg="black", fg="white", textvariable=prin, justify="left", cursor="box_spiral").grid(row=1, sticky="w")
e = tk.Entry(top)
e.grid(row=2)
e.focus_set()
Attack = tk.Button(top, text ="Attack", activebackground="red", width="10", command = player_attack).grid(row=4, sticky="e")
Block = tk.Button(top, text ="Block", activebackground="red", width="10", command = player_block).grid(row=5, sticky="e")
Use = tk.Button(top, text ="Use Item", activebackground="red", width="10", command = player_use).grid(row=4, column=1, sticky="w")
Run = tk.Button(top, text ="Run", activebackground="red", width="10", command = run_away).grid(row=5, column=1, sticky="w")
Enter = tk.Button(top,text='Enter',command=pinput).grid(row=3)
Progress = tk.Button(top, text="Progress", activebackground="green",
prin.set("REDACTED")#This had my team's full names
top.mainloop()
So, there are a few problems. I added the "progress" button for testing purposes, ideally the game would start in Hallway 1. However, I couldn't figure out where to put the hallway_1() function for this to work. Before the top.mainloop() and the GUI wouldn't open. After it, and e.get() threw TCL errors.
So, I bound it to a button. However, now the code keeps getting stuck in the "while" loop even if 1 is inputted into the entry box before the code runs. I'm perplexed by this because Visual Studio reports player_input as being '1' in the Autos box, but it keeps running through the loop anyway.
You can't do a while loop like that in a GUI event handler. Until you return from the handler function, the GUI doesn't get to update its display, accept user input, or do anything else, which means player_input will never change once you get there. A handler function needs to do one thing, set up anything needed for future handlers, and return immediately.
It's hard to get your head around thinking in terms of the event loop and handler callbacks the first time you write a GUI, but it's absolutely essential; until you do, things seem bizarre and arbitrary.
So, the answer isn't to "wait for input", but to set up a handler that fires when that input is ready. (You can instead set up a handler on an after function so it fires every N milliseconds instead of firing on input, but that usually just makes things more complicated.)
One way to do this is to have the "Progress" button trigger "the next step", and keep track of some state that lets you determine what "the next step" is. An in fact, you already have that state: it's whether the player has already given correct input or not. So:
def pinput():
global player_input
player_input = e.get()
print(player_input)
def progress():
pinput()
if player_input != "1" and player_input != "2":
hallway_1_enter()
else:
hallway_1_doit()
def hallway_1_enter():
global player_input
global prin
global e
prin.set("You are in a hallway. Yellow lockers line the sides of all walls and you see three doors. One leads into a Library, one to a History classroom, and one to a Math room. You can 1.) Search the hallway or 2.) Procede to one of the rooms.")
def hallway_1_doit():
if player_input == "1": #Even if 1 is in the entry box beforehand, it never gets here
# etc.
This is obviously a bit of a clunky design, but it's the smallest change to your existing design that gets you past the first step. Once you get the hang of it, you should be able to improve it from there.
Also notice the global player_input I put in pinput. Your existing function didn't have that, so it was just creating a local variable with the same name, which then goes away immediately; the global never gets changed. Any function that wants to assign to a global needs the global statement.
So I have a currency which is increasing (that system is working fine). The first part updates the label every 100 ms. I have another button which triggers the second function which is supposed to clear the labels from the first. It sets home_status equal to 0 which should in theory run Money.place_forget() to clear the code. I have tested each part individually and it works but when I put the clears inside the elif statement it doesn't. It does not give me any errors, it just simply doesn't do anything (it does print END OF UPDATE HOME so the elif is triggered).
Any suggestions?
def updatehome(self):
print("UPDATE HOME")
global buy_button, home_status, currency
MoneyLabel = Label(self, text = "Money: ")
MoneyLabel.place(x = 5, y = 70)
Money = Label(self, text=currency)
Money.place(x = 50, y = 70)
if (home_status == 1):
self.after(100, self.updatehome)
elif (home_status == 0):
print("END OF UPDATE HOME")
Money.place_forget()
MoneyLabel.place_forget()
def clearhome(self):
print("CLEAR HOME")
global home_status
home_status = 0
you are creating ten labels every second, all stacked on top of each other, but you are only deleting the very last label you create.
I have a script that continually takes in text and outputs text (its a text based game)
I would like to run it through a tkinter GUI as opposed to the console
Python : Converting CLI to GUI
This question perfectly answers how to convert "print" into a GUI insert.
The problem is that my game obviously runs through a ton of loops, and that screws up the "app.mainloop()" because it either never runs (and then the GUI never shows up) or you run it first, and it doesn't let anything else run.
I suppose I could try and and stagger these loops somehow, but that seems very hackish. I could also try to modify my entire codebase to run inside the app.mainloop(), but what I really think I need is multiple threads. Problem is, I have no idea how to make that work.
There are a few other questions, but they either don't work or don't make much sense:
Tkinter with multiple threads
Run process with realtime output to a Tkinter GUI
Thanks.
Edit: extremely simplified code:
def moveNorth():
print('You have moved north')
def interpreter(command):
if command == 'go north':
moveNorth()
else:
print('invalid command')
def listener():
playerchoice = sys.stdin.readline().strip()
return playerchoice
if __name__ == '__main__':
print('Welcome')
while playing:
interpreter(listener())
I think you might be making it more complicated than it needs to be.
For Tkinter at least it is very simple change console interactions into a GUI interaction instead.
The simplest example I can give is to use an Entry field for user input and a Text widget for the output.
Here is a simple example of a console based game being moved to a GUI using Tkinter.
Console number guessing game:
import random
print("simple game")
print("-----------")
random_num = random.randint(1, 5)
print(random_num)
x = True
while x == True:
#Input for user guesses.
guess = input("Guess a number between 1 and 5: ")
if guess == str(random_num):
#Print answer to console.
print("You win!")
x = False
else:
print("Try again!")
Here is the Tkinter GUI example of the same game:
import tkinter as tk
import random
root = tk.Tk()
entry_label = tk.Label(root, text = "Guess a number between 1 and 5: ")
entry_label.grid(row = 0, column = 0)
#Entry field for user guesses.
user_entry = tk.Entry(root)
user_entry.grid(row = 0, column = 1)
text_box = tk.Text(root, width = 25, height = 2)
text_box.grid(row = 1, column = 0, columnspan = 2)
text_box.insert("end-1c", "simple guessing game!")
random_num = random.randint(1, 5)
def guess_number(event = None):
#Get the string of the user_entry widget
guess = user_entry.get()
if guess == str(random_num):
text_box.delete(1.0, "end-1c") # Clears the text box of data
text_box.insert("end-1c", "You win!") # adds text to text box
else:
text_box.delete(1.0, "end-1c")
text_box.insert("end-1c", "Try again!")
user_entry.delete(0, "end")
# binds the enter widget to the guess_number function
# while the focus/cursor is on the user_entry widget
user_entry.bind("<Return>", guess_number)
root.mainloop()
As you can see there is a bit more code for the GUI but most of that is the GUI design.
The main part that you need to change is the use of entry vs input for your answers and the use of insert vs print for your response. The rest is really just design stuff.
If you want to keep the questions on a continuous nature you can update the label with a new question or you could even use tkinters askstring function for each new question. There are many options.
the main thing is getting the value of the user answer, using that answer to test with the question, then printing the results to the text box.
I'm in the middle of making a small game involving hacking into people's computers, and stealing files and money in order to complete missions. Here is the code as of now:
#SICCr4k2: Broke
#
#
#
#Remember whenever you are printing a random ip address to add the "." in between each part of the ip (each random number)
## LAST LEFT ON HERE: MAKE BUTTONS FOR NODES
## MAKE FILES FOR NULL'S NODE
## SET THE CORRECT PLACEMENTS FOR ALL THE BUTTONS
## nullMain referenced before assignment
## make it so that you send a message through the prompt to get their ip, then it automatically puts the ip in the nodes
## window. Like you send the person a message, and then it gets the ip and puts it in the nodes window
## take away the buttons in the nodes window, just at labels where it points to the host's ip address.
import random
import time
import sys
import os
import tkinter as tk
from tkinter import *
#def nodes():
# nodeWindow = tk.Tk()
# frame = tk.Frame(nodeWindow, width=700, height=400)
# frame.grid_propagate(0)
# frame.grid()
# nodeWindow.title("||| Nodes |||")
# nullIp = tk.Label(nodeWindow, text="Ip: 221.153.52.216")
# nullIp.grid(row=0, column=0)
# nullMain = tk.Button(nodeWindow, text="Null", function=nullMainCallback())
# nullMain.config(height=1, width=100)
# nullMain.grid(row=0, column=0)
# def nullMainCallback():
# nullMain.destroy()
# nullIp = tk.Label(nodeWindow, text="Ip: 221.153.52.216")
# nullIp.grid(row=0, column=0)
#def commands():
def numbers():
number1 = random.randint(1, 99)
number2 = random.randint(1, 99)
print(number1)
if number1 != number2:
numbers()
if number1 == number2:
os.system('cls')
def ips():
nullIp = ('18.279.332')
def getIp():
x = random.randint(1, 222)
if x == 127:
x += 1
return '{}.{}.{}.{}'.format(
x,
random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255))
def commandInput():
CommandInput = input(">>> ")
if CommandInput == ("myNodes()"):
nodes()
else:
commandInput()
commandInput()
def usernameCreation():
username = input(">>> ")
print("'" + username + "' is that correct?")
usernameInput = input(">>> ")
if usernameInput == ("yes"):
print("Okay...")
if usernameInput ==("no"):
usernameCreation()
def game():
def tutorial():
print('Hello.')
time.sleep(3)
print('Welcome back.')
time.sleep(3)
print('How was it?')
time.sleep(3)
print('Being hacked for the first time?')
time.sleep(3)
print("You're probably wondering who I am.")
time.sleep(5)
print("Well, my name is Null.")
time.sleep(3)
print("Only because I am well known for nothing.")
time.sleep(3)
print("Other than not being alive.")
time.sleep(3)
os.system('cls')
print("First thing's first, what shall I call you?")
usernameCreation()
print("Let's give you a bit of movement.")
time.sleep(3)
print("""The first thing you will want to do would be to connect to my computer, but
to do that, you have to find my ip address. Here. I just uploaded new software to your computer.""")
time.sleep(3)
print("""You will now be able to access my ip, nad many other's with a simple command. The command is
getIp(). Input that command below, but inside the parenthesis, you type in the screen name. For instance: getIp(Null).
type that command in to get my ip.""")
input(">>> ")
if ("getIp(Null)"):
numbers()
print("""My ip was just added to your nodes, which you can access by typing myNodes().""")
game()
I just want to note that when I run the program, it doesn't list any errors or anything, it just doesn't execute at all... Any ideas????
You define the function tutorial inside game (which you shouldn't really do – there's no point in defining it that way) but never call tutorial.
Inside of game you want to call tutorial:
def game():
def tutorial():
# code for tutorial
tutorial()
A better way to structure your code, however, is to use a main method (which is the standard way to start the execution of a program` and keep every other function separate. There's no need to nest functions as you've done.
So, for example:
def main():
tutorial()
# all other function definitions
def tutorial():
# code for tutorial
if __name__ == "__main__":
main()
You never call tutorial() although you shouldn't nest functions like this.
I'm working on a GUI Python program using Tkinter.
I have a function that is called when a button is pressed (and when the program is loaded). The program is currently unfinished and only checks data validation at this current point. As the default entry is current invalid, it throws an error.
However, after this point, the entry box is disabled and will not let me enter any data. I cannot figure out why this is happening and I was wondering if someone could tell me the reason so I can work on a solution.
Thanks
import sys
import random
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
root = Tk()
root.title("COSC110 - Guessing Game")
hint = StringVar()
guesses = []
guess_input = ''
def loadWordList(filename): #Load the words from a file into a list given a filename.
file = open(filename, 'r')
line = file.read().lower()
wordlist = line.split()
return wordlist
word = random.choice(loadWordList('words.txt'))
def getHint(word, guesses): #Get hint function, calculates and returns the current hint.
hint = ' '
for letter in word:
if letter not in guesses:
hint += '_ '
else:
hint += letter
return hint
def guessButton(guess, word, guesses):
guess = str(guess_input)
guess = guess.lower()
if not guess.isalpha():
is_valid = False
elif len(guess) !=1:
is_valid = False
else:
is_valid = True
while is_valid == False:
messagebox.showinfo("Error:","Invalid input. Please enter a letter from a-z.")
break
hint.set(getHint(word, guesses))
return hint
label_instruct = Label(root, text="Please enter your guess: ")
label_instruct.grid(row=1,column=1,padx=5,pady=10)
guess_input = Entry(root,textvariable=guess_input)
guess_input.grid(row=1, column=2)
guess_button = Button(root, text="Guess", width=15, command=guessButton(guess_input,word,guesses))
guess_button.grid(row=1, column=3,padx=15)
current_hint = Label(root, textvariable=hint)
current_hint.grid(column=2,row=2)
label_hint = Label(root, text="Current hint:")
label_hint.grid(column=1,row=2)
label_remaining = Label(root, text="Remaining guesses: ")
label_remaining.grid(column=1,row=3)
root.mainloop() # the window is now displayed
Any tips are appreciated.
There are two apparent problems.
Firstly, you shouldn't use
guess_button = Button(root, text="Guess", width=15, command=guessButton(guess_input,word,guesses))
because you can't call a function with arguments on the command config.
My suggestion would be to take a look here and use one of the proposed methods, I particularly like the one using functools and partial:
from functools import partial
#(...)
button = Tk.Button(master=frame, text='press', command=partial(action, arg))
with action being the function you want to call and arg the parameters you want to call separated by a comma.
Secondly, you are using
guess = str(guess_input)
which doesn't return the Entry typed text, use instead
guess = guess_input.get()
PS: Albeit not directly related to your question, you should use
if var is False:
instead of
if var == False: