Python - Functions & Execution order - python

I want to use the bot_create function with a button but I keep getting (on line 20) the problem "bots not defined" so I moved the function down below the button but got the problem "bot_create not defined".
I didn't get this problem using C++ and I'm new to Python. How should I arrange the functions?
import tkinter as tk
import numpy as np
import multiprocessing as mp
bots_max = 1000 # Maximum number of bots
bot = []
bot_count = 0
# Menu functions
def save_field():
pass
# Field functions
def field_clear():
pass
# Bots functions
def bots_create():
bot[bot_count] = bots
bot_count += 1
main = tk.Tk()
field_sides = 600
ctrls_width = 200
main.geometry("800x600")
main.resizable(0, 0)
main.title("Swarm Simulator v1.0")
# Controls menu on left side
button1 = tk.Button(main, text = "Button 1").pack(side = "left", command = bots_create())
class environment:
def __init__():
pass
class wall:
def __init__():
pass
# Bots
class bots:
alive = True
def __init__():
alive = True
# Field where bots live
field = tk.Canvas(main, width = field_sides, height = field_sides, bg = "white").pack(side = "right")
for particle in bots:
print("|")
main.mainloop()

Here's a version of your code that fixes all the syntactic problems, and so compiles (what I really mean is that my IDE now thinks its ok). It also runs, but I don't know if it does what you intended. See my comments in the code:
import tkinter as tk
import numpy as np
import multiprocessing as mp
# moved your class defs up to fix problems with accessing them before they are defined
class environment:
def __init__(self): # need a self param here
pass
class wall:
def __init__(self): # need a self param here
pass
# Bots
class bots:
alive = True
def __init__(self): # need a self param here
alive = True
bots_max = 1000 # Maximum number of bots
bot = []
# bot_count = 0 # this no longer does anything. use `len(bot)` to get the number of objects in the 'bot' list
# Menu functions
def save_field():
pass
# Field functions
def field_clear():
pass
# Bots functions
def bots_create():
# bot[bot_count] = bots # this will crash as it is referring to a non-existent location in the list
# also, your use of "bots" here makes no sense
# bot_count += 1 # this makes 'bot_count' a local variable, which is not what you want
bot.append(bots()) # not sure this is what you want, but this creates a new 'bots' object and adds it to the 'bot' list
main = tk.Tk()
field_sides = 600
ctrls_width = 200
main.geometry("800x600")
main.resizable(0, 0)
main.title("Swarm Simulator v1.0")
# Controls menu on left side
button1 = tk.Button(main, text = "Button 1").pack(side = "left", command = bots_create())
# Field where bots live
field = tk.Canvas(main, width = field_sides, height = field_sides, bg = "white").pack(side = "right")
for particle in bot: # maybe you want to iterate over the 'bot' list instead of the 'bots' type?
print("|")
main.mainloop()
As #khelwood says, it seems that you should swap the use of the names bot and bots per the way you are using them

Related

Python self.attribute cannot be seen when calling class method

I have a problem related to a TKinter GUI I am creating, but the problem is not necessarily specific to this library.
Background
I am currently in the advanced stage of a python self-learning course. The learning module I am on is covering TKinter for creating interactive GUI's. I am making a game whereby randomly generated numbered buttons must be clicked in succession in the quickest time possible.
Brief: https://edube.org/learn/pcpp1-4-gui-programming/lab-the-clicker
Problem
Under my class, game_grid, I have created an instance variable; 'self.holder', a 25 entry dictionary of {Key : TkinterButtonObject} form
When calling this instance variable for use in a class method, I get the following error:
AttributeError: 'game_grid' object has no attribute 'holder'
I have a print statement under class init which proves this attribute has been successfully created. I have made sure my spacing and tabs are all OK, and have tried every location for this variable, including using as a class variable, and a global variable to no avail - as it is an semi-complex object. I don't see what difference it should make, but any ideas would be much appreciated. I am also aware this could be done without classes, but I am trying to adopt DRY principles and orthogonality in all of my programs.
Thanks in advance.
Full Code:
import tkinter as tk
from tkinter import*
import random
from tkinter import messagebox
import time
win = tk.Tk()
class game_grid:
def __init__(self, win):
self.last_number = 0
self.number_buttons = {}
self.row_count = 0
self.column_count = 0
#Generate a list of 25 random numbers
self.number_list = random.sample(range(0, 999), 25)
#Puts the numbers in a dictionary (number : buttonobject)
self.holder = {i: tk.Button(win, text = str(i), command = game_grid.select_button(self, i)) for i in self.number_list}
#pack each object into window by iterating rows and columns
for key in self.holder:
self.holder[key].grid(column = self.column_count, row = self.row_count)
if self.column_count < 4:
self.column_count += 1
elif self.column_count == 4:
self.column_count = 0
self.row_count += 1
print(self.holder)
def select_button(self, number):
if number > self.last_number:
self.holder[number].config(state=tk.DISABLED)
self.last_number = number
else:
pass
class stopclock():
def __init__(self):
#Stopclock variable initialisation
self.time_begin = 0
self.time_end = 0
self.time_elapsed= 0
def start(self):
if self.time_begin == 0:
self.time_begin = time.time()
return("Timer started\nStart time: ", self.time_begin)
else:
return("Timer already active")
def stop(self):
self.time_end = time.time()
self.time_elapsed = time_end - time_begin
return("Timer finished\nEnd time: ", time_begin,"\nTime Elapsed: ", time_elapsed)
play1 = game_grid(win)
win.mainloop()
Perhaps you meant:
command = self.select_button(self, i)
Update:
Though from research:How to pass arguments to a Button command in Tkinter?
It should be:
command = lambda i=i: self.select_button(i)
You call select_button from inside the dict comprehension of holder. select_button then tries to use holder, but it is not yet defined. You don't want to actually call select_button, but assign a function to the button, like that:
self.holder = {i: tk.Button(window, text=str(i), command=lambda i=i: self.select_button(i)) for i in self.number_list}

GUI status in Python Tkinter

EDIT: Made some changes based on feedback in comments.
I am trying to use a statusTextF function to show a WAIT/READY message on python GUI.
I have defined, at the beginning, when the GUI is drawn.
statusText = True
and then call the following when i want it to wait:
statusText = False
I call statusText as a global variable everywhere I use it, and I have the statusTextF function as shown below:
def statusTextF():
if statusText == True:
statusTitle = tk.Label(root,text="READY")
statusTitle.config(font=statusFont,bg="light green")
statusX = 500
statusY = 450
statusTitle.place(x=statusX,y=statusY)
separation = 45
else:
statusTitle = tk.Label(root,text="WAIT")
statusTitle.config(font=statusFont,bg="light red")
statusX = 500
statusY = 450
statusTitle.place(x=statusX,y=statusY)
separation = 45
I am seeing 'READY' all the time though.
What could be wrong?
Now, it doesn't display anything, and also I get a light red is unknown colour error.
Here's a demonstration of how to do something like you want. It doesn't use a Queue for communication between the main GUI thread and the status updating thread since the amount of information being exchanged between them is so minimal. It instead uses a threading.Lock to control access to a global variable shared between them. Note that it's also implicitly being used to protect updates to the separation global.
Using a queue.Queue to exchange the information ought be fairly easy to implement — should you need one for some reason — since they don't need a separate Lock because they implement "all the required locking semantics" interally.
Note: I've tried to (mostly) follow the PEP 8 - Style Guide for Python Code to make it fairly readable — which I strongly suggest you read (and also follow).
import random
import time
import tkinter as tk
import tkinter.font as tkFont
import threading
class StatusUpdater(threading.Thread):
# # Not really needed since it doesn't do anything except call superclass ctor here.
# def __init__(self, *args, **kwargs):
# super().__init__(*args, **kwargs) # Initialize base class constructor.
def run(self):
global status_flag_lock, status_flag # Must declare these to change their values.
while True:
# Randomly update status_flag.
value = random.randint(0, 100)
with status_flag_lock:
status_flag = bool(value % 2) # True if odd number.
time.sleep(.5) # Pause updating for a little while.
def statusTextF():
global status_flag_lock, separation # Must declare these to change their values.
with status_flag_lock:
if status_flag:
statusTitle.config(text="READY", bg="light green")
separation = 45
else:
statusTitle.config(text="WAIT", bg="pink")
separation = 55
root.after(250, statusTextF) # Continue the polling.
status_flag_lock = threading.Lock() # To control concurrent access.
root = tk.Tk()
root.geometry('600x600')
STATUS_FONT = tkFont.Font(family='Courier', size=8)
STATUS_X, STATUS_Y = 500, 450
status_flag = True
separation = 45
statusTitle = tk.Label(root, text="UNKNOWN", font=STATUS_FONT, bg="gray50")
statusTitle.place(x=STATUS_X, y=STATUS_Y)
status_updater = StatusUpdater(daemon=True)
status_updater.start() # Start updating of status flag.
root.after(250, statusTextF) # Start polling status every 250 millisecs.
root.mainloop()

Tkinter window event <Visibility>

I tried to get an event if the windows visibility is changed.
I found out that there is an event called "Visibility".
The Operating System is Windows 64bit.
So I implemented in the following way:
root.bind('<Visibility>', visibilityChanged)
But I always got the state "VisibilityUnobscured" no matter if there is a window over it or not. What is the normal behaviour of this event? How can I implement a feature like that?
Example Prog:
import tkinter as tk
class GUI:
def __init__(self, master):
self.master = master
master.title("Test GUI")
self.master.bind('<Visibility>', self.visibilityChanged)
self.label = tk.Label(master, text="GUI")
self.label.pack()
self.close_button = tk.Button(master, text="Close", command=master.quit)
self.close_button.pack()
def visibilityChanged(self, event):
if (str(event.type) == "Visibility"):
print(event.state)
root = tk.Tk()
my_gui = GUI(root)
root.mainloop()
What is the normal behaviour of this event?
It's well described in the docs:The X server generates VisibilityNotify event whenever the visibility changes state and for any window.
How can I implement a feature like that?
It depends on how far you are going to go in your wishes, since this isn't a trivial task. Thus, don't treat that answer as a complete solution, but as a problem overview and a set of suggestions.
The event problem
Windows OS uses a message-passing model - the system communicates with your application window via messages, where each message is a numeric code that designates a particular event. Application window has an associated window procedure — a function that processes (responds or ignores) all messages sent.
The most generic solution is to set a hook to catch certain events/messages and it's possible either via SetWindowsHookEx or pyHook.
The main problem is to get event, because the Windows WM has no such message as VisibilityNotify. As I said in comment section - one option, on which we can rely, is the z-order
(there's possibility to check Visibility of the window, whenever this window changes it's position in z-order).Therefore our target message is either WM_WINDOWPOSCHANGING or WM_WINDOWPOSCHANGED.
A naive implementation:
import ctypes
import ctypes.wintypes as wintypes
import tkinter as tk
class CWPRETSTRUCT(ctypes.Structure):
''' a class to represent CWPRETSTRUCT structure
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644963(v=vs.85).aspx '''
_fields_ = [('lResult', wintypes.LPARAM),
('lParam', wintypes.LPARAM),
('wParam', wintypes.WPARAM),
('message', wintypes.UINT),
('hwnd', wintypes.HWND)]
class WINDOWPOS(ctypes.Structure):
''' a class to represent WINDOWPOS structure
https://msdn.microsoft.com/en-gb/library/windows/desktop/ms632612(v=vs.85).aspx '''
_fields_ = [('hwnd', wintypes.HWND),
('hwndInsertAfter', wintypes.HWND),
('x', wintypes.INT),
('y', wintypes.INT),
('cx', wintypes.INT),
('cy', wintypes.INT),
('flags', wintypes.UINT)]
class App(tk.Tk):
''' generic tk app with win api interaction '''
wm_windowposschanged = 71
wh_callwndprocret = 12
swp_noownerzorder = 512
set_hook = ctypes.windll.user32.SetWindowsHookExW
call_next_hook = ctypes.windll.user32.CallNextHookEx
un_hook = ctypes.windll.user32.UnhookWindowsHookEx
get_thread = ctypes.windll.kernel32.GetCurrentThreadId
get_error = ctypes.windll.kernel32.GetLastError
get_parent = ctypes.windll.user32.GetParent
wnd_ret_proc = ctypes.WINFUNCTYPE(ctypes.c_long, wintypes.INT, wintypes.WPARAM, wintypes.LPARAM)
def __init__(self):
''' generic __init__ '''
super().__init__()
self.minsize(350, 200)
self.hook = self.setup_hook()
self.protocol('WM_DELETE_WINDOW', self.on_closing)
def setup_hook(self):
''' setting up the hook '''
thread = self.get_thread()
hook = self.set_hook(self.wh_callwndprocret, self.call_wnd_ret_proc, wintypes.HINSTANCE(0), thread)
if not hook:
raise ctypes.WinError(self.get_error())
return hook
def on_closing(self):
''' releasing the hook '''
if self.hook:
self.un_hook(self.hook)
self.destroy()
#staticmethod
#wnd_ret_proc
def call_wnd_ret_proc(nCode, wParam, lParam):
''' an implementation of the CallWndRetProc callback
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644976(v=vs.85).aspx'''
# get a message
msg = ctypes.cast(lParam, ctypes.POINTER(CWPRETSTRUCT)).contents
if msg.message == App.wm_windowposschanged and msg.hwnd == App.get_parent(app.winfo_id()):
# if message, which belongs to owner hwnd, is signaling that windows position is changed - check z-order
wnd_pos = ctypes.cast(msg.lParam, ctypes.POINTER(WINDOWPOS)).contents
print('z-order changed: %r' % ((wnd_pos.flags & App.swp_noownerzorder) != App.swp_noownerzorder))
return App.call_next_hook(None, nCode, wParam, lParam)
app = App()
app.mainloop()
As you can see, this implementation has a similar behavior as a "broken" Visibility event.
This problem stems from the fact, that you can catch only thread-specified messages, hence application doesn't know about changes in the stack. It's just my assumptions, but I think that the cause of the broken Visibility is same.
Of course, we can setup a global hook for all messages, regardless a thread, but this approach requires a DLL injection, which is an another story for sure.
The visibility problem
It's not a problem to determine obscuration of the window, since we can rely on Graphical Device Interface.
The logic is simple:
Represent window (and each visible window, which is higher in the z-order) as a rectangle.
Subtract from main rectangle each rectangle and store result.
If final geometrical subtraction is:
... an empty rectangle — return 'VisibilityFullyObscured'
... a set of rectangles — return 'VisibilityPartiallyObscured'
... a single rectangle:
if geometrical difference between result and original rectangle is:
... an empty rectangle — return 'VisibilityUnobscured'
... a single rectangle — return 'VisibilityPartiallyObscured'
A naive implementation (with self-scheduled loop):
import ctypes
import ctypes.wintypes as wintypes
import tkinter as tk
class App(tk.Tk):
''' generic tk app with win api interaction '''
enum_windows = ctypes.windll.user32.EnumWindows
is_window_visible = ctypes.windll.user32.IsWindowVisible
get_window_rect = ctypes.windll.user32.GetWindowRect
create_rect_rgn = ctypes.windll.gdi32.CreateRectRgn
combine_rgn = ctypes.windll.gdi32.CombineRgn
del_rgn = ctypes.windll.gdi32.DeleteObject
get_parent = ctypes.windll.user32.GetParent
enum_windows_proc = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
def __init__(self):
''' generic __init__ '''
super().__init__()
self.minsize(350, 200)
self.status_label = tk.Label(self)
self.status_label.pack()
self.after(100, self.continuous_check)
self.state = ''
def continuous_check(self):
''' continuous (self-scheduled) check '''
state = self.determine_obscuration()
if self.state != state:
# mimic the event - fire only when state changes
print(state)
self.status_label.config(text=state)
self.state = state
self.after(100, self.continuous_check)
def enumerate_higher_windows(self, self_hwnd):
''' enumerate window, which has a higher position in z-order '''
#self.enum_windows_proc
def enum_func(hwnd, lParam):
''' clojure-callback for enumeration '''
rect = wintypes.RECT()
if hwnd == lParam:
# stop enumeration if hwnd is equal to lParam (self_hwnd)
return False
else:
# continue enumeration
if self.is_window_visible(hwnd):
self.get_window_rect(hwnd, ctypes.byref(rect))
rgn = self.create_rect_rgn(rect.left, rect.top, rect.right, rect.bottom)
# append region
rgns.append(rgn)
return True
rgns = []
self.enum_windows(enum_func, self_hwnd)
return rgns
def determine_obscuration(self):
''' determine obscuration via CombineRgn '''
hwnd = self.get_parent(self.winfo_id())
results = {1: 'VisibilityFullyObscured', 2: 'VisibilityUnobscured', 3: 'VisibilityPartiallyObscured'}
rgns = self.enumerate_higher_windows(hwnd)
result = 2
if len(rgns):
rect = wintypes.RECT()
self.get_window_rect(hwnd, ctypes.byref(rect))
# region of tk-window
reference_rgn = self.create_rect_rgn(rect.left, rect.top, rect.right, rect.bottom)
# temp region for storing diff and xor rgn-results
rgn = self.create_rect_rgn(0, 0, 0, 0)
# iterate over stored results
for _ in range(len(rgns)):
_rgn = rgn if _ != 0 else reference_rgn
result = self.combine_rgn(rgn, _rgn, rgns[_], 4)
self.del_rgn(rgns[_])
if result != 2:
# if result isn't a single rectangle
# (NULLREGION - 'VisibilityFullyObscured' or COMPLEXREGION - 'VisibilityPartiallyObscured')
pass
elif self.combine_rgn(rgn, reference_rgn, rgn, 3) == 1:
# if result of XOR is NULLREGION - 'VisibilityUnobscured'
result = 2
else:
# 'VisibilityPartiallyObscured'
result = 3
# clear up regions to prevent memory leaking
self.del_rgn(rgn)
self.del_rgn(reference_rgn)
return results[result]
app = App()
app.mainloop()
Unfortunately, this approach is far from a working solution, but it's tweakable in a perspective.

random TKinter Dice Roller

import Tkinter
import random
win = Tkinter.Tk()
win.title('Dice Roller')
def mainloop():
class Die:
def __init__(self,ivalue, parent):
self.value = ivalue
self.display = Tkinter.Label(parent,relief='ridge',borderwidth=4, text=str(self.value))
def roll(self):
self.value = random.randint(1,6)
self.display.config(text=str(self.value))
class diceRoller:
def rolldice():
d1.roll()
d2.roll()
d3.roll()
def __init__(self):
self.diceList = []
self.win = Tkinter.Tk("Dice Roller")
for i in range(3):
di = Die(self.win)
self.dieList.append(di)
rolldice()
row1 = Tkinter.Frame(win)
row2 = Tkinter.Frame(win)
d1.roll.display.pack(side="left")
d2.roll.display.pack(side="left")
d3.roll.display.pack(side="left")
row1.pack()
rolldice = Tkinter.Button(row2, command=rolldice(), text = "Roll")
rolldice.pack()
row2.pack()
win.mainloop()
I'm having trouble with my python code with Tkinter. I'm trying to get it to produce a window with three buttons that present the numbers rolled on the dice and another that lets me re roll the dice.
The code you have inside class diceRoller under the method rolldice has to be in some method. It cannot just stay in a class. Create a method using def and add that code to it.
If a name of a method is __init__, it is called a constructor. A constructor is a method that will be called when the object is created (i.e. when you do diceRoller()). You perhaps should put the code, that's now just laying inside diceRoller into such a method/constructor, I don't know (you should know if that's the case).
Note: In Python we usually write first characters of class names uppercase.

How do you wait for an user to click a tkinter button with threads

I am currently working on a projet similar to tic tac toe (with an AI)
And I did a GUI to allow the user to play with the bot
But the program doesn't wait for the player to choose and instead it crash because it doesn't have value
So I searcher about threads, tried for a long time can't achieve to figure out how it works
I did some tests (bellow) that my archly resemble what I need to do with my code
But it doesn't work neither
Do someone have an answer ?
import threading
import tkinter as tk
windo = tk.Tk()
windo.title("Morpion")
windo.resizable(width=tk.FALSE, height=tk.FALSE)
class Player:
def __init__(self,name):
self.name = name
self.choice = None
def Change_Var(x):
print(P.choice)
P.choice = x
print(P.choice)
play_event.set()
def boucle():
i = 0
while not play_event.isSet() and i < 3000:
print(i)
i += 1
P = Player("Deltix")
start = tk.Button(height=2,width=8, text = "Start",command = lambda x = 0 : Change_Var(x))
start.grid(column = 2, row = 3,pady = 5)
play_event = threading.Event()
threading.Thread(target = windo.mainloop()).start
threading.Thread(target = boucle()).start```
You don't need to use threads, just use events.
Say you wanted to bind a function to the enter key.
This would be your code:
canvas = Canvas(master)
def clicked(event):
print("Enter was pressed")
canvas.bind("<Return>", clicked)
You can read more about tkinter events here.
Hopefully this helps!

Categories

Resources