I am Python 3 noob creating a checkers game, I have a bunch of functions... and my game's loop is as follows:
while EndGame==0:
PrintBoard()
PrintBoardGui()
print("################","\n","Player 1","\n","################","\n")
Player=1
PlayerSelectPiece()
MovePiece()
PrintBoard()
PrintBoardGui()
print("################","\n","Player 2","\n","################","\n")
Player=2
if NbrPlayer==2:
PlayerSelectPiece()
else:
AiSelectPiece()
MovePiece()
PrintBoardGui() that I run at the beggining of each turn and creates a Tkinter window and draws the board on a new Canvas in a Tkinter Frame.
After that, I have to close the window in order for the program to continue.
I know this is a sub-optimal solution.
I have looked around trying to understand Tkinter's loops and read a bit about the after() function but i really don't know how I could implement it in my code.
Ultimatly I would like my Tkinter window to stay open (mabye disabled or something) while I input stuff in the console to move the pieces. Can you help me?
At first, how do you want to interact with your game ?
With text in the console ?
With buttons ?
With keyboard/mouse event ?
The "after" method is used to refresh your screen after an amount of time.
It will call the method that you pass through the parameters.
You shouldn't have to put an infinite loop in it. But you will have to check with a simple condition the game ending, to display another screen.
If you have to use the console entry, it can be a bit hard for a beginner to manage the GUI update and the console.
I am not sure what ur question was but, If you would want to run the code forever (until you stop it). you would have to use the while loop like this:
while "thing" == True:
PrintBoard()
PrintBoardGui()
print("################","\n","Player 1","\n","################","\n")
Player=1
PlayerSelectPiece()
MovePiece()
PrintBoard()
PrintBoardGui()
print("################","\n","Player 2","\n","################","\n")
Player=2
if NbrPlayer==2:
PlayerSelectPiece()
else:
AiSelectPiece()
MovePiece()
thing = False
at the end you change thing to False otherwise it would be infinite and it would bug.
Related
I want Python to kind of ignore a statement that is unlikely to be called in a function that is often called.
I do not have a formal education in programming, so please excuse my lackluster ability to desribe things. I will try to explain the concept by example.
Say I am writing a video game, first-person shooter, drawing 60 frames per second.
In the settings menu, the user can select whether or not to display the name of other players above their head. If they disable this, I store this value as showplayernames = False.
Then in my drawing function that outputs the graphics I have:
def draw():
#code that draws the graphics on screen
if showplayernames:
#code that draws the name of players on screen
I call this function 60 times a second, but there is absolutely no point for checking if showplayernames is True 60 times a second. It will not change that often, in fact I could make this a kind of "constant" during play by preventing it to change. If showplayernames is False, then the third and fourth lines of the code are completely redundant, but they are executed nevertheless. The computer isn't smart enough to know it can ignore it, and there is a performance difference: reading a value and then checking if it is false takes time.
I could write two copies of the game (or at least the draw() function), one with only the first two lines when the user selects not to show names in the settings, and another without the if statement, and then run the appropriate one.
def draw_with_names():
#code that draws the graphics on screen
#code that draws the name of players on screen
def draw_without_names():
#code that draws the graphics on screen
Although looping through either of these 60 times a second is more efficient than running draw() ,this is obviously not the way to go. There are dozens of settings like this.
So how do software and game designers implement these kind of "software-wide" settings efficiently?
I'm not a game developer, but here's one option. Create a list of function pointers and add or remove from the list based on settings. Example:
def draw_player_names():
# Code to overlay names
def draw_fps():
# Code to overlay fps
def draw():
# Main code to draw a frame of the game
# Hold all the functions to call
optional_funcs = []
if showplayernames: optional_funcs.append(draw_player_names)
if show_fps: optional_funcs.append(draw_fps)
# Main game loop
while True:
draw()
for f in optional_funcs: f()
This can be extended for any number of functions/options.
not an game designer, but here is my voice.
You could store settings inside json file next to you python, but then you need to cover reading, getting right values etc.
You could use Environment variables to store value but that would end up using still "if" in the code.
Game designers use triggers and events to get things done, and on the lowest level I would assume those things also use if's.
system-wide-settings will in the end be used with if's
You could use overwrites based on event/trigger and use "same" draw function in both times but that only complicates code, and we all know to "keep it simple".
Sorry if this is not the answer you were looking for.
As Matthias said, you shouldn't really bother about this kind of optimization. Look at these two very silly functions:
def lot_of_ifs(settings):
#do something
a=2**10
b=2**10
c=2**10
for setting in settings:
if setting:
#do something more
a=2**10
b=2**10
c=2**10
def no_ifs():
#do something
a=2**10
b=2**10
c=2**10
timeit("lot_of_ifs([0,0,0,0,0])", globals=globals())
0.2630380000000514
timeit("no_ifs()", globals=globals())
0.10232830000040849
The cost of creating a list, looping over it, and executing five ifs is about 0.16 seconds for one million iterations, or 160 nanoseconds per iteration, while at 60 fps you have 16.6 million nanoseconds to execute your code.
To understand my question kindly follow the paragraphs written below:
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
consider the code:
from tkinter import *
window = Tk()
print("lol")
print("Hello World")
window.mainloop()
the output didn't print "Hello World" or "lol" infinite number of times, so the mainloop() doesn't loop the code of the current module.
Now consider this code:
from tkinter import *
print("lol")
window = Tk()
print("Hello World")
while True:
window.update()
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
Now the first question arises what does the window.update() function do to update the values in the GUI, does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
The second question is :
I read this article
"Mainloop in Python Tkinter is an infinite loop of the application window which runs forever so that we can see the still screen.
The application window is like a frame that keeps on destroying every microsecond but the main loop keeps on creating a new updated window.
This process of destroying old window screens and creating a new one happens so fast that human eyes don’t even realize it.
Since the process runs infinite time that is why we are able to see the application in front of us and when we close the window then the loop terminates or exits."
Now if this is true then to recreate an updated window the root.mainloop() must read the entire root GUI code again and again entirely or is there another explanation to it.
I have been trying to understand this for the past 6hrs and I have visited every site and I cannot find the solution for the life of me.
Regards,
Rashik
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
No.
Via this function, it calls this C code which has the embedded Tcl interpreter process one event, or wait for Tkinter_busywaitinterval before trying to process another event
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
window.update() calls TCL update, which is described to
[...] bring the application “up to date” by entering the event loop repeatedly until all pending events (including idle callbacks) have been processed.
Your infinite loop doesn't have a sleep, so it's spinning your CPU as hard as possible to do practically nothing.
[...] Does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
It certainly doesn't re-read your code. It processes any pending widget updates, which may have happened by running e.g. window.text("...") in e.g. a click callback or an .after() timeout, etc.
I read this article [...]
That article seems wrong and/or at least over-simplifies things.
This simple example clock should clarify how things work:
import time
import tkinter as tk
root = tk.Tk()
text = tk.Label(root)
text.pack()
def tick():
text["text"] = time.ctime() # update `text` widget's content
root.after(1000, tick) # schedule for this function to be called after 1 second
if __name__ == '__main__':
tick() # call the `tick` function once before entering main loop
root.mainloop()
I am a beginner in Python.
I use to code with Tkinter. I want to improve my programs with the extension "threading".
However, in some of my programs, if i leave the Tkinter window, the program is still running.
I would like to know how stop it. I guess it's the thread which is still running. I tried different ways to close the thread, but the only issue i found is to create an error (and it is not a nice solution according to me) :).
I would like to know if an instruction like the following one exist to stop the thread.
Thread(target=FinTemps).stop()
(I know that 'stop' is not working)
and if it is possible to stop the Thread with a loop 'while', I tried this and i don't understand why it doesn't work :
import tkinter as tk
from threading import Thread as Th
from time import *
global end
end=False
begining=time()
def screen():
global end
while end==False:
chrono=time()-beginning
if chrono<5:
sentence.set("Hey !")
elif chrono<8:
sentence.set("What's up ?")
window=tk.Tk()
window.geometry("{}x{}".format(fenêtre.winfo_screenwidth(), window.winfo_screenheight()))
window.title("Test Tkinter & Thread")
sentence=tk.StringVar()
sentence.set("none")
GreatTitle=tk.Label(window, textvariable=sentence, width=500)
Th(target=screen).start()
GreatTitle.pack(expand=1)
window.mainloop()
end=True
Thank you ;)
(And sorry if my english is not wonderful (I'm french) or if my code or explainations weren't very understandable or without following traditional presentation).
Have a good day :)
What you are doing is not safe. tkinter is not thread-safe, and running any tkinter API cross-thread risks corruption of various kinds.
Bright side is, you don't need threads here. In real code, you wouldn't use a thread at all, you'd just schedule the changes to occur on tkinter's event loop with the after method, simplifying your code to just:
import tkinter as tk
window=tk.Tk()
window.geometry("{}x{}".format(window.winfo_screenwidth(), window.winfo_screenheight()))
window.title("Test Tkinter & Thread")
sentence=tk.StringVar()
sentence.set("none")
GreatTitle=tk.Label(window, textvariable=sentence, width=500)
GreatTitle.pack(expand=1)
window.after(0, sentence.set, "Hey !") # Happens almost immediately, as in thread case
window.after(5000, sentence.set, "What's up ?") # Delays five seconds, as in thread case
window.mainloop()
This is also far more efficient than what you had; as written, your thread would spin forever, constantly looping without pause for as long as it was allowed to do so, keeping a core constantly running at 100%; the GUI itself would run slower because it couldn't do much of its work while the other thread held the GIL. Using .after, no meaningful resources are consumed at any time aside from the moment the event fires and the sentence is updated.
It may be that your while loop checks end too frequent, it may cause race condition when you try to set end to True after the root window is destroyed.
You can try adding a short sleep inside the while loop to reduce the chance of race condition:
while end == False:
...
sleep(0.1)
I am pretty new to python and have been working with a program that was originally meant for the command line. As such, it uses the input() function quite a bit, especially in the middle of loops. Mostly, the original code would do something like this:
for item in list:
# some logic here
user_input - input("prompt")
# uses user_input
I've decided I want to make a GUI for it, but I also want to make it optional. I have a class called Viewer that decorates the original program, and I am struggling to figure out how to best to handle the calls to input().
My first thought was just to inject a new input function altogether so that way it looks for input in my GUI text box instead the sys.stdout. I found many Stack Overflow answers on how to print sys.stdout to a GUI element (like this), but not how to take input from one. All of my attempts either ended in freezing the GUI by creating a loop, or the program not pausing for input and simply grabbing what was in the box when the prompt was put forward.
Secondly, I tried to break apart my functions to better match the code examples for buttons. So, button 1 would trigger a function_part_1, which would go until it required an input. If the GUI flag was turned off, it would automatically go to an input function, otherwise it would return and a button press would trigger function_part_2:
def function_part_1(list):
item = list[0]
# do some logic on item 1
if GUI:
print("prompt")
return
# After this, the GUI waits for a button press, and then calls function_part_2
else:
function_input(list)
def function_input(list):
user_input = input("prompt")
function_part_2(user_input, list)
def function_part_2(user_input, list):
# uses user_input on item
list.remove(list[0])
if list:
function_part_1(list)
else:
return
However, this turned ugly really quickly. First, it broke apart many loops which would require a lot of refactoring to keep the logic in tact (Especially for the nested loops). It also requires that I pass all my local variables from function-part to function-part, which exploded the function headers. In order to have only a single input box for the user, the GUI has to have logic to know which function is executing and what function-part comes next. It made my functions harder to follow and hurt readability.
Is there a nicer way to do this?
I am using appJar for the GUI, which is based around Tkinter. I am willing to switch to pure Tkinter if that would make things easier.
The easiest way is to overwrite the input function. Here's a working example using tkinter:
import tkinter as tk
def input(prompt=''):
win= tk.Tk()
label= tk.Label(win, text=prompt)
label.pack()
userinput= tk.StringVar(win)
entry= tk.Entry(win, textvariable=userinput)
entry.pack()
# pressing the button should stop the mainloop
button= tk.Button(win, text="ok", command=win.quit)
button.pack()
# block execution until the user presses the OK button
win.mainloop()
# mainloop has ended. Read the value of the Entry, then destroy the GUI.
userinput= userinput.get()
win.destroy()
return userinput
print(input('foobar'))
I'm making a simple game using Pygame where you have to get (by moving the cursor over them) all the circles that will appear on the screen (more will appear every second). The code is quite long so I made an example code. This code works fine, the Pygame window doesn't become unresponsive at all:
import pygame, random, sys
pygame.init()
window=pygame.display.set_mode((480,360))
end_program=False
while not end_program:
for event in pygame.event.get():
if event.type==pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
end_program=True
pass
pygame.quit()
sys.exit()
However, in my game, in order to give the user the choice to play again, I need to wrap everything inside end_program in another loop. In the example shown, this is break_from_second_loop:
import pygame, random, sys
pygame.init()
window=pygame.display.set_mode((480,360))
end_program=False
while not end_program:
for event in pygame.event.get():
if event.type==pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
end_program=True
break_from_second_loop=False
while not break_from_second_loop:
pass
pygame.quit()
sys.exit()
Now if this is run, the window becomes unresponsive! Anyone know why something as simple as wrapping the code in another loop (without altering the code at all) does this?
The problem is that the game can't respond, or do anything at all, if you're not running the event loop. And in the other loop, you're not running the event loop.
This is a general problem with event-loop-based programming. You can't do anything that takes a long time, and you can't do anything that has to run across multiple events.
So, you have to break your loop apart into steps, and do just one step (or a few of them) each time through the event loop.
In this particular case, it's actually pretty simple: just change that while to an if (and move the has_got_all_circles=False outside the main loop), and your logic now runs exactly once each time through the event loop.
Alternatively, change it to an if and also move it inside the for, so now it runs exactly once per event, instead of once per event loop iteration.
A third alternative is to factor out the whole thing into a function and set it as an idle or timer function, that runs whenever the event loop is idle, or once every frame, or once every 20ms, or whatever.
It's hard to know which of the three is appropriate in your case, but the basic idea is the same in all of them, so I'll just show the second one:
end_program=False
break_from_second_loop=False
while not end_program:
for event in pygame.event.get():
if event.type==pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
end_program=True
if not break_from_second_loop:
pass
This blog post explains the general problem in more detail—although most of it isn't really appropriate to this specific problem.
The issue you're having is that you're not nesting your event loop code within the while loop that does your game logic. Here's the general structure of what you want:
while not end_program:
while not end_game:
handle_events()
do_one_frame_of_game_logic()
offer_another_game()
It's possible that offer_another_game will also need to be run in its own loop, with its own event handling code.
Indeed, you might want to encapsulate the logic you want to use into a state machine system. You'd have states like PlayingGame, GameOver, and DoYouWantToPlayAgain, each of which would run for a while, then hand off to another state. Your main loop would then be something like:
state = StartState()
while state:
state.handle_events()
state.update()
state.draw()
state = state.next_state() # most of the time, the state will return itself