Multiple listboxs bound to same function - python

Honestly, I do not feel like this should be happening, but it is.
self.marketList.bind('<<ListboxSelect>>', self.market_selected)
self.jobsList.bind('<<ListboxSelect>>', self.job_selected)
There really isn't any more interaction between these two functions. When you click on an item in the marketList box, it's supposed to bring up the jobs in the jobsList box. Currently, it is applying the binding two both boxes. When I click on a job entry in the jobsBox, it clears the jobs and my troubleshooting is showing that it's calling market_selected. I'm not sure why this is happening, but it's really messing with what I'm trying to do with it.
How can I ensure that my binding is on only one widget, and won't be applied to multiple widgets?
edit:
I'm told that this isn't enough code to reproduce the error.
This is all the relevant code.
As I said previously, self.market_selected is called when I click on anything in the jobsList
edit #2
I uploaded the entire script.
import MarketWizard
import JobWizard
import SpanWalkerDocuments as swd
import tkinter as tk
from tkinter import *
from tkinter import ttk
import SpanWalkerDocuments as SpanWalker
class SpanWalker:
def __init__(self):
self.root = tk.Tk()
self.root.title('Luke Spanwalker')
self.root.resizable(True, True)
self.MainFrame = Frame(self.root, bg='red')
self.sidebarFrame = Frame(self.root, bg='blue')
self.tabControl = ttk.Notebook(self.sidebarFrame)
self.marketFrame = Frame(self.tabControl)
self.clientFrame = Frame(self.tabControl)
self.jobsFrame = Frame(self.tabControl)
self.polesFrame = Frame(self.MainFrame, height=100, width=50)
self.tabControl.add(self.marketFrame, text="Markets")
self.tabControl.add(self.jobsFrame, text="Jobs")
self.tabControl.add(self.clientFrame, text="Clients")
#self.tabControl.add(self.polesFrame, text="Poles")
self.tabControl.pack(expand=1, fill="both")
self.MainFrame.grid(row=0, column=1)
self.sidebarFrame.grid(row=0, column=0)
#Awesome, our tabbed control is ready.
#Now, we need to make the listBox widgets that will actually display our data.
self.marketList = Listbox(self.marketFrame)
self.jobsList = Listbox(self.jobsFrame)
self.polesList = Listbox(self.polesFrame)
#Binding functions! Yay for binding functions!
self.marketList.bind('<<ListboxSelect>>', self.market_selected)
self.jobsList.bind('<<ListboxSelect>>', self.job_selected)
self.curMarket = ""
self.markets = []
self.jobs = []
self.poles = []
def UpdateMarkets (self):
if len(self.markets) > 0:
self.markets.clear()
self.marketList.delete(0, END)
for mark in swd.Market.objects:
self.markets.append(mark)
for m in range(0, len(self.markets)):
self.marketList.insert(m, self.markets[m].title)
def OpenMarketWizard(self):
mw = MarketWizard.MarketWizard()
mw.RunMarketWizard(self.root)
def market_selected(self, event):
print("Market Selected")
selection = self.marketList.curselection()
selectedMarket = ",".join([self.marketList.get(i) for i in selection])
self.PopulateJobs(selectedMarket)
def PopulateJobs(self, market):
self.jobs.clear()
self.jobsList.delete(0, END)
self.GetJobs(market)
for j in range(0, len(self.jobs)):
self.jobsList.insert(j, self.jobs[j].jobName)
def GetJobs(self, market):
marketJobs = []
jobs = []
if market =="":
return
for j in swd.Job.objects:
jobs.append(j)
print("Market = {0}".format(market))
for i in jobs:
if i.market == market:
self.jobs.append(i)
def job_selected(self, event):
print("Job Selected")
selection = self.jobsList.curselection()
selectedJob = ",".join([self.jobsList.get(i) for i in selection])
print("The selected job is: {0}".format(selectedJob))
def PopulateMarkets(self):
self.marketList.destroy()
for m in range(0, len(self.markets)):
marketList.insert(m, self.markets[m].title)
def OpenJobWizard(self):
jw = JobWizard.JobWizard()
jw.RunJobWizard(self.root)
#We need to pack everything that belongs in our tabbed function.
def marketListDisplay(self, show):
if show == True:
self.marketList.pack(fill="both")
self.UpdateMarkets()
self.newButton=Button(self.marketFrame, text="Open Market Wizard", command=lambda:self.OpenMarketWizard())
self.newButton.pack()
self.refreshButton=Button(self.marketFrame, text="Refresh Markets", command=lambda:self.UpdateMarkets())
self.refreshButton.pack()
else:
self.marketList.forget_pack()
def jobsListDisplay(self, show):
if show==True:
self.jobsList.pack()
self.newButton = Button(self.jobsFrame, text="Create New Job / Open Job Wizard", command=lambda:self.OpenJobWizard())
self.newButton.pack()
else:
self.jobsList.forget_pack()
def polesListDisplay(self, show):
if show==True:
self.polesList.pack()
else:
self.polesList.forget_pack()
#Query, why not put them all in a function?
sp = SpanWalker()
sp.marketListDisplay(True)
sp.polesListDisplay(True)
sp.jobsListDisplay(True)

The answer was given by acw1668. I just added exportselection=0 during the creation process for the listboxes, and it worked perfectly.
Sorry for the confusion, y'all, I've never really asked for help on here before.
Thanks for the help!

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}

How to speed up scrolling responsiveness when displaying lots of text

I am trying to create a Python script to highlight specific patterns in a .txt file. To do this, I have altered a script which used Tkinter to highlight a given set of data. However, the files I tend to get it to process are around 10000 lines, which results in slow scrolling as I think it renders everything - whether it is on the screen or not (correct me if I'm wrong). Is it possible to alter my code such that it renders the output in a more efficient way? I have tried searching for a means to do this, but have not found anything myself.
My code is as follows:
from Tkinter import *
class FullScreenApp(object):
def __init__(self, master, **kwargs):
self.master=master
pad=3
self._geom='200x200+0+0'
master.geometry("{0}x{1}+0+0".format(
master.winfo_screenwidth()-pad, master.winfo_screenheight()-pad))
master.bind('<Escape>',self.toggle_geom)
def toggle_geom(self,event):
geom=self.master.winfo_geometry()
print(geom,self._geom)
self.master.geometry(self._geom)
self._geom=geom
root = Tk()
app = FullScreenApp(root)
t = Text(root)
t.pack()
#Import file
with open('data.txt') as f:
for line in f:
t.insert(END, line)
#Search terms - Leave blank if not required
search_term0 = '0xCAFE'
search_term1 = '0x0011'
search_term2 = '0x961E'
search_term3 = '0x0000'
search_term4 = ''
#Assigns highlighted colours for terms not blank
t.tag_config(search_term0, background='red')
if search_term1 != '':
t.tag_config(search_term1, background='red')
if search_term2 != '':
t.tag_config(search_term2, background='red')
if search_term3 != '':
t.tag_config(search_term3, background='red')
if search_term4 != '':
t.tag_config(search_term4, background='red')
#Define search
#Requires text widget, the keyword, and a tag
def search(text_widget, keyword, tag):
pos = '1.0'
while True:
idx = text_widget.search(keyword, pos, END)
if not idx:
break
pos = '{}+{}c'.format(idx, len(keyword))
text_widget.tag_add(tag, idx, pos)
#Search for terms that are not blank
search(t, search_term0, search_term0)
if search_term1 != '':
search(t, search_term1, search_term1)
if search_term2 != '':
search(t, search_term2, search_term2)
if search_term3 != '':
search(t, search_term3, search_term3)
if search_term4 != '':
search(t, search_term4, search_term3)
root.mainloop()
An example of the data in a file is given in the following link: here
Many thanks for your time, it is really appreciated.
Assuming MCVE is the following:
import tkinter as tk
def create_text(text_len):
_text = list()
for _ in range(text_len):
_text.append("{}\n".format(_))
_text = "".join(_text)
return _text
if __name__ == '__main__':
root = tk.Tk()
txt = tk.Text(root)
txt.text = create_text(10000)
txt.insert('end', txt.text)
txt.pack()
root.mainloop()
Analysis
Based on this I don't think it is a rendering issue. I think it's an issue with having a fixed rate of registering <KeyPress> events. Meaning that the number of events registered per second is fixed, even though the hardware may be capable of registering at a faster rate. A similar regulation should be true also for the mouse-scroll event.
Solutions for rendering
Perhaps slicing text for a buffer proportion of txt['height'] would help. But isn't that how Tk supposed to be rendering anyway?
Solutions for rendering unrelated issue
If a step would be defined as the cursor's movement to the previous or the next line, for every registered event of Up or Down; then scrolling_speed = step * event_register_frequency.
By increasing the step size
An easy workaround would be to simply increase the step size, as in increasing the number of lines to jump, for each registration of the key bind.
But there's already such default behavior, assuming the page length > 1 line, Page Up or Page Down has a step size of a page. Which makes the scrolling speed increase, even though the event registration rate remains the same.
Alternatively, a new event handler with a greater step size may be defined to call multiple cursor movements for each registration of Up and Down, such as:
import tkinter as tk
def create_text(text_len):
_text = list()
for _ in range(text_len):
_text.append("{}\n".format(_))
_text = "".join(_text)
return _text
def step(event):
if txt._step_size != 1:
_no_of_lines_to_jump = txt._step_size
if event.keysym == 'Up':
_no_of_lines_to_jump *= -1
_position = root.tk.call('tk::TextUpDownLine', txt, _no_of_lines_to_jump)
root.tk.call('tk::TextSetCursor', txt, _position)
return "break"
if __name__ == '__main__':
root = tk.Tk()
txt = tk.Text(root)
txt.text = create_text(10000)
txt.insert('end', txt.text)
txt._step_size = 12
txt.bind("<Up>", step)
txt.bind("<Down>", step)
txt.pack()
root.mainloop()
By mimicking keypress event registry rate increase:
As mentioned in here actually modifying keypress registry rate is out of scope of Tk. Instead, it can be mimicked:
import tkinter as tk
def create_text(text_len):
_text = list()
for _ in range(text_len):
_text.append("{}\n".format(_))
_text = "".join(_text)
return _text
def step_up(*event):
_position = root.tk.call('tk::TextUpDownLine', txt, -1)
root.tk.call('tk::TextSetCursor', txt, _position)
if txt._repeat_on:
root.after(txt._repeat_freq, step_up)
return "break"
def step_down(*event):
_position = root.tk.call('tk::TextUpDownLine', txt, 1)
root.tk.call('tk::TextSetCursor', txt, _position)
if txt._repeat_on:
root.after(txt._repeat_freq, step_down)
return "break"
def stop(*event):
if txt._repeat_on:
txt._repeat_on = False
root.after(txt._repeat_freq + 1, stop)
else:
txt._repeat_on = True
if __name__ == '__main__':
root = tk.Tk()
txt = tk.Text(root)
txt.text = create_text(10000)
txt.insert('end', txt.text)
txt._repeat_freq = 100
txt._repeat_on = True
txt.bind("<KeyPress-Up>", step_up)
txt.bind("<KeyRelease-Up>", stop)
txt.bind("<KeyPress-Down>", step_down)
txt.bind("<KeyRelease-Down>", stop)
txt.pack()
root.mainloop()
By both increasing step-size and mimicking registry rate increase
import tkinter as tk
def create_text(text_len):
_text = list()
for _ in range(text_len):
_text.append("{}\n".format(_))
_text = "".join(_text)
return _text
def step_up(*event):
_no_of_lines_to_jump = -txt._step_size
_position = root.tk.call('tk::TextUpDownLine', txt, _no_of_lines_to_jump)
root.tk.call('tk::TextSetCursor', txt, _position)
if txt._repeat_on:
root.after(txt._repeat_freq, step_up)
return "break"
def step_down(*event):
_no_of_lines_to_jump = txt._step_size
_position = root.tk.call('tk::TextUpDownLine', txt, _no_of_lines_to_jump)
root.tk.call('tk::TextSetCursor', txt, _position)
if txt._repeat_on:
root.after(txt._repeat_freq, step_down)
return "break"
def stop(*event):
if txt._repeat_on:
txt._repeat_on = False
root.after(txt._repeat_freq + 1, stop)
else:
txt._repeat_on = True
if __name__ == '__main__':
root = tk.Tk()
txt = tk.Text(root)
txt.text = create_text(10000)
txt.insert('end', txt.text)
txt._step_size = 1
txt._repeat_freq = 100
txt._repeat_on = True
txt.bind("<KeyPress-Up>", step_up)
txt.bind("<KeyRelease-Up>", stop)
txt.bind("<KeyPress-Down>", step_down)
txt.bind("<KeyRelease-Down>", stop)
txt.pack()
root.mainloop()
So this is solved by something called multi threading. A computer can do multiple tasks at once, otherwise, your web experience wouldn't be the same. Here is a simple function that demonstrates mulit-threading
from threading import Thread
def execOnDifferentThread(funct=print, params=("hello world",)):
t = Thread(target=funct, args=params)
t.start()
Now note, this might not be the best example, but all you have to do to run a function in parallel now, is execOnDifferentThread(funct=A, params=B) where A is a function name, and B is a tuple of arguments that will be passed on to your function. Now, I don't want to write your code for you, but using this function, you can multi-thread certain parts of your code to make it faster. If you are truly stuck, just comment where and ill help. But please try on your own first, now that you have the power of multi-threading on your hands

Tkinter Async Error...without multithreading?

I've recently needed to do some GUI work with Python and stumbled on Tkinter. For the most part, I like it; it's clean and mostly intuitive and brief. There is, however, one little sticking point for me: sometimes it crashes out of nowhere. The program will run normally five times in a row and then the sixth time, halfway through it will freeze and I will get the error
Tcl_AsyncDelete: async handler deleted by the wrong thread
My efforts to find a solution to this problem, both on this website and others, all have to do with multithreading, but I don't use multiple threads. Not explicitly, anyway. I suspect the fact that I have a timer in the GUI is to blame, but I have not been able to figure out why the error pops up, or indeed why it is so infrequent.
What follows is the shortest tkinter program I've written. This happens in all of them, so I suspect the problem will be easiest to see here. Any and all help is appreciated, just don't point me toward another solution without telling me how it applies to my code, because I assure you, I have looked at it, and it either doesn't apply or I didn't understand how it did. I don't mind having missed the answer, but I'm new to tkinter (and to multithreading in general) so I might need it told more explicitly.
This code is for a simulation of a game show I saw online. Most of it can probably be safely ignored, but I'm pasting it all here because I don't know where the error is from.
import re
from tkinter import Tk, Frame, DISABLED, Button, Label, font, NORMAL, ttk
from random import choice
import winsound
class Question:
def __init__(self, t, a, o):
self.text = t.strip().capitalize() + "?"
self.answer = a.strip().title()
self.options = o
self.firstIsRight = self.answer.lower() == self.options[0].lower()
assert self.firstIsRight or self.answer.lower() == self.options[1].lower(), self
def __eq__(self, other):
return self.text == other.text
def __repr__(self):
return "{1} or {2}, {0}".format(self.text, self.options[0], self.options[1])
class Application(Frame):
def __init__(self, master=None):
self.setup()
Frame.__init__(self, master)
self.grid()
self.customFont = font.Font(family="Times New Roman", size=30)
self.createWidgets()
def setup(self):
self.questions = []
with open("twentyone.txt",'r') as file:
for line in file:
groups = re.split("[,\.\?]",line)
answers = re.split(" or ",groups[0])
self.questions.append(Question(groups[1], groups[2], answers))
def createWidgets(self):
self.gamePanel = Frame(self)
self.gamePanel.grid(column=0,row=0)
self.displayPanel = Frame(self)
self.displayPanel.grid(column=0,row=1)
self.buttonPanel = Frame(self)
self.buttonPanel.grid(column=0,row=2)
self.QUIT = Button(self.buttonPanel,text="QUIT",font=self.customFont,command=self.quit)
self.QUIT.grid(row=0,column=2)
self.BEGIN = Button(self.buttonPanel, text="BEGIN",font=self.customFont, command = self.begin)
self.BEGIN.grid(row=0,column=0)
self.STOP = Button(self.buttonPanel, text="STOP",font=self.customFont, command = self.stop)
self.STOP.grid(row=0,column=1)
self.STOP["state"] = DISABLED
self.TITLE = Label(self.gamePanel,text="21 Questions Wrong",font=self.customFont,bg="Black",fg="White")
self.TITLE.grid(columnspan=2)
self.questionText = Label(self.gamePanel,text="Questions go here",font=self.customFont,wraplength=400)
self.questionText.grid(row=1,columnspan=2)
self.leftChoice = Button(self.gamePanel,text="Option 1",font=self.customFont)
self.leftChoice.grid(row=2,column=0)
self.rightChoice = Button(self.gamePanel,text="Option 2",font=self.customFont)
self.rightChoice.grid(row=2,column=1)
self.timerText = Label(self.displayPanel, text="150",font=self.customFont)
self.timerText.grid(row=0)
self.progress = ttk.Progressbar(self.displayPanel, length=100,maximum=22)
self.progress.grid(row=0,column=1,padx=10)
def begin(self):
self.timer(250)
self.asked = []
self.STOP["state"] = NORMAL
self.leftChoice["state"] = NORMAL
self.rightChoice["state"] = NORMAL
self.restart = False
self.askNewQuestion()
def askNewQuestion(self):
if self.restart:
self.currentQuestion = self.asked[int(self.progress["value"])]
else:
self.currentQuestion = choice([i for i in self.questions if i not in self.asked])
self.asked.append(self.currentQuestion)
self.questionDisplay()
def questionDisplay(self):
self.questionText["text"] = self.currentQuestion.text
self.leftChoice["text"] = self.currentQuestion.options[0]
self.rightChoice["text"] = self.currentQuestion.options[1]
if self.currentQuestion.firstIsRight:
self.leftChoice["command"] = self.correct
self.rightChoice["command"] = self.wrong
else:
self.leftChoice["command"] = self.wrong
self.rightChoice["command"] = self.correct
def correct(self):
self.progress.step()
if self.progress["value"] >= 21:
self.gameOver(True, 21)
else:
if self.progress["value"] >= len(self.asked):
self.restart = False
self.askNewQuestion()
def wrong(self):
self.restart = True
self.progress["value"] = 0
winsound.Beep(750,700)
self.askNewQuestion()
def stop(self):
self.after_cancel(self.timerAfter)
self.BEGIN["state"] = NORMAL
self.STOP["state"] = DISABLED
def gameOver(self, success, longest):
self.after_cancel(self.timerAfter)
self.BEGIN["state"] = NORMAL
self.STOP["state"] = DISABLED
self.questionText["text"] = "Congratulations!" if success else "Too Bad!"
self.leftChoice["text"] = "Game"
self.leftChoice["state"] = DISABLED
self.rightChoice["text"] = "Over"
self.rightChoice["state"] = DISABLED
self.showPoints(success, longest)
def showPoints(self, s, l):
if s:
timeTaken = max(0, 100-int(self.timerText["text"]))
print("You scored {0} points".format(1000-10*timeTaken))
else:
print("You scored no points")
def timer(self, time):
self.BEGIN["state"] = DISABLED
self.STOP["state"] = NORMAL
self.runTimer(time)
def runTimer(self, current=None, resume=False):
if current is not None:
self.current = current
self.timerText["text"] = self.current
if self.current == 0:
self.gameOver(False, len(self.asked)-1)
else:
self.current -= 1
self.timerAfter = self.after(1000,self.runTimer)
root = Tk()
app = Application(master=root)
app.master.title("Score Calculator!")
app.anchor("center")
root.mainloop()
root.destroy()

Python GUI - entry values are not strings but methods?

I have a small problem with my program :
I can't to understand how to reuse the entered values in the program. It says that I cannot use float on a method, but I thought that the s.get gave me a string. It also seems that it doesn't actually use the values entered but just the default values.
Does anyone know what is wrong with the code?
from tkinter import *
from math import *
import numpy as np
import matplotlib.pyplot as plt
class GUI:
def __init__(self):
self.root = Tk()
self.labelVariable = StringVar()
self.root.title('Projet informatique')
self.initialize()
self.root.mainloop()
def initialize(self):
self.main = Frame(self.root)
self.main.pack()
label = Label(self.main, textvariable=self.labelVariable, font=('courier',10,'bold'), anchor="w", fg="red", bg="white")
label.pack()
self.labelVariable.set(u"Modélisation de populations atteintes d'un virus")
v=Listbox(self.main)
v.insert("end","Modèle SIR")
v.insert("end", "Modèle de Witowski")
v.insert("end", "Modèle de Munz")
v.insert("end", "Modèle avec traitement")
v.bind("<Double-Button-1>", self.Double)
v.pack(expand=1,fill=BOTH)
def Double(self,event):
widget = event.widget
selection = widget.curselection()
value = widget.get(selection[0])
self.newWindow(value)
def ModifyTextarea(self,elem,msg,clear=None):
elem.config(state=NORMAL)
if clear:
elem.delete(1.0, END)
else:
elem.insert(END,msg)
elem.config(state=DISABLED)
def newWindow(self,msg):
top = Toplevel(self.root)
q1 = Frame(top)
q1.pack()
top.grab_set()
text = Text(q1,state=DISABLED,exportselection=True)
text.pack()
lbl = Label(q1,text="")
lbl.pack()
self.ModifyTextarea(text,msg)
v2=StringVar()
v3=StringVar()
v4=StringVar()
v5=StringVar()
v1=StringVar()
v6=StringVar()
e1=Entry(q1,textvariable=v1)
e2=Entry(q1,textvariable=v2)
e3=Entry(q1,textvariable=v3)
e4=Entry(q1,textvariable=v4)
e5=Entry(q1,textvariable=v5)
e6=Entry(q1,textvariable=v6)
e1.pack()
e2.pack()
e3.pack()
e4.pack()
e5.pack()
e6.pack()
v1.set("Taux de mortalité des zombies")
v2.set("Coefficient de propagation du virus")
v3.set("Taux de rémission")
v4.set("Taux d'infectés devenant malades")
v5.set("Taux de guérison")
v6.set("Pas de temps")
a=v1.get
b=v2.get
ze=v3.get
T=v4.get
N=v5.get
dt=v6.get
def callback():
print (a,b,ze,T,N,dt)
btnquit = Button(q1,width = 1,text = "Ok",command =callback())
btnquit.pack()
def zombies(a,b,ze,T,N,dt) :
a=flot(a)
b=float(b)
ze=float(ze)
T=float(T)
N=float(N)
dt=float(dt)
n = T/dt
n=int(n)
t=np.zeros((n+1))
for i in range (0,n):
t[0]=0
t[i+1]=t[i]+dt
s = np.zeros((n+1))
z = np.zeros((n+1))
r = np.zeros((n+1))
s[0] = N-1
z[0] =1
r[0] = 0
for i in range (n-1):
s[i+1] = s[i] + dt*(-b*s[i]*z[i])
z[i+1] = z[i] + dt*(b*s[i]*z[i]-a*s[i]*z[i]+ze*r[i])
r[i+1] = r[i] + dt*(a*s[i]*z[i]- ze*r[i])
if s[i+1]<0 or s[i+1] >N:
s[i+1]=0
break
if z[i+1] > N or z[i+1] < 0:
z[i+1]=0
break
if r[i+1] <0 or r[i+1] >N:
r[i+1]=0
break
return (t,s,r,z,i+2)
t,s,r,z,i=zombies(a,b,ze,T,N,dt)
plt.plot(t[:i],s[:i],'b')
plt.plot(t[:i],z[:i],'r')
plt.plot(t[:i],r[:i],'y')
plt.show()
if __name__ == "__main__":
app = GUI()
These are all incorrect:
a=v1.get
b=v2.get
ze=v3.get
T=v4.get
N=v5.get
dt=v6.get
You aren't calling the get function. To call it, add parenthesis:
a=v1.get()
...
FWIW, you don't need to use StringVars for each entry widget. You can just as easily get the value directly from the widget. Using StringVar often just adds extra code and extra objects to manage with no real benefit.
The problem is that you are just referencing the get methods, but not calling them. Also, you should make sure to call those functions inside your callback function, otherwise you will always print the values those variables had when you initialized your UI. Finally, note that instead of binding the callback to the button, you are executing the function once and binding its result to the button.
def callback():
# here, you have to ADD () to get the result of get
print(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get())
# here, you have to REMOVE () to get the callback function itself
btnquit = Button(q1, width=1, text="Ok", command=callback)
Similarly in your zombies function:
def zombies(): # no need for input parameters
a = float(v1.get())
b = float(v2.get())
etc.
And finally, you have to bind that function to a button (maybe instead of that callback function?), otherwise zombies will be executed just once at the end of the newWindow method, i.e. before inputting any actual data. The plotting should probably also be part of that function.

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