Adding Tkinter Buttons to a list without .append - python

I'm currently having a play around with tkinter trying to make my code efficient. I want to be able to index my buttons at a later date so I'm trying to put then into a list without first creating a blank list and appending them to it as I loop (The only way I currently know how to do it). I've got the below currently going on but at the moment every time the loop runs it overwrites my button at index 0 rather than appending to the list, how would I go about using something like this or use list comprehension to create my buttons?
for btn in range (6):
self.Preset_Lbl = [tk.Button(self.window, width = 5, height = 1, text = mylist[0]
[btn], relief = "groove")]
self.Preset_Lbl[btn].grid(row = btn, column = 1)
thanks in advance

trying to make my code efficient
In general, you should only bother to optimize code after it has been proven (by measurements, i.e. through profiling) that it is a major bottleneck. In general for a GUI the creation of a window and filling it with widgets is a one-time event. So it is unlikely to be the most inefficient code in the program.
First, optimizations can often make code more complicated and difficult to read and understand. For example, for the programmer stopbutton is pretty much self explanatory. If you referred to the same object as buttons[0] it would not be clear what that button is supposed to do.
Second, you don't know what the bottlenecks in your code are before you measure them.
For example, in my stl2pov program, it turned out that string formatting was actually consuming most of the time. And using the appropriate type specifier to the format string halved the necessary time.
Another example is where replacing statistics.mean with statistics.fmean reduced the runtime by about a third.

In contrast to the comments, you can indeed create a list variable and initialize all its values in the same line as such without needing to loop through it (It does use a for loop inside the implementation, but it's reduced to one line and without using the function append() directly):
self.Preset_lbl = [tk.Button(self.window, width=5, height=1, text=mylist[0][btn], relief="groove") for btn in range(6)]
You need to decide for yourself, if you care about the readability of this approach or you can also check if it would impact performance in any way by profiling/benchmarking it like stated by Roland Smith.

Related

How to know which check box has just been toggled within a mass created checkbox from tkinter or related libraries

let's say I've this piece of code where it give me a hundred check boxes created by a for loop(I'm working with a switch in customtkinter but it has the same logic as checkbox in tkinter), those check box could either be off or on initially however, I just need to know what is the latest check box that has been toggled, its position or at least what it called so that I could work on it. I've done some research but I couldn't figured out how to work on this, here is the part of that code for the check box, I've not assigned the variable to it since I'm trying to figure out the logic first but any help would be much appreciated, thank you!
with open(r"D:\Largecodefile\TkinterApply\List1.txt",'r') as f:
Lines=f.read().splitlines()
for i,val in enumerate(Lines):
switch = ct.CTkSwitch(master=self.scrollable_framel, text=val,command=self.changestate)
switch.grid(row=i, column=0, padx=10, pady=(0, 20))
self.scrollable_frame_switches.append(switch)
The command=self.changestate doesn't seem to return anything as of right now and it's just mainly used to run the function and that's it so I don't think it'd be of great help either :(
Edit: I'm thinking of cross comparing between 2 list of off or on state after a certain event occured for those 100 boxes but that doesn't seem to be a good idea, and perhaps it's the same thing for creating 100 function for this purpose as well since it'd take a lot of time to process as the list get longer
You're on the right track, but you have to set the value of i to a variable the lambda can pass: command=lambda button_index=i: changestate(button_index) - that way the value is updated for each button!
Here's an example of how to pass the index value i to the event handler function changestate (I've removed self here since this isn't in a class that I can see, but if you're using a class, just add self.)
def changestate(button_index):
print(button_index) # do whatever you want here!
for i, val in enumerate(Lines):
switch = ct.CTkSwitch(
master=self.scrollable_framel,
text=val,
# pass the latest index value to the lambda for each button
command=lambda button_index=i: changestate(button_index)
)
switch.grid(row=i, column=0, padx=10, pady=(0, 20))
self.scrollable_frame_switches.append(switch)

Python: implement a "software-wide" setting that does not change often without running an if statement in every loop

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.

Simplifying this Tkinter For Loop to Update Entry Values between two columns

This script works fine for doing what I want it to, but I think there's likely a far more direct alternative that I'm missing.
All I have is a tkinter checkbox that when pressed, runs this function that maps all the info from column 0's rows of entry boxes into column 1's entry boxes. Currently, this is the script:
def update_company_new():
company = str()
for child in frame.winfo_children():
if child.grid_info()["column"] == 0:
company = child.get()
if child.grid_info()["column"] == 1:
child.insert(0, company)
Is there a more direct way to do this? This seems something that could normally be done with a simple list comprehension but I can't find enough details on additional options for winfo_children() and grid_info() that make it adaptable to tkinter grid references.
It can be done using list comprehension:
[frame.grid_slaves(row=w.grid_info()['row'], column=1)[0].insert(0, w.get()) for w in frame.grid_slaves(column=0)]
But it is hard to understand for beginner and it created an useless list of None.
It is better to use a simple for loop instead:
for w in frame.grid_slaves(column=0):
row = w.grid_info['row']
peer = frame.grid_slaves(row=row, column=1)
# check if there is Entry in column 1
if peer:
peer[0].insert(0, w.get())

PySide/PyQt: PointingHandCursor recipes?

I have a pyside application with a numerous buttons, toolbuttons, tabs etc... I would like all of them to have a 'pointing hand' cursor when hovering/clicking. This means my code is full of statements like this:
someWidget.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
It is a relatively long, ugly line.
I can encapsulate the above in a function:
def hand_cursor(widget):
widget.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
This makes the rest of the code somewhat neater:
hand_cursor(someWidget)
But I still have to write that line all over the place.
Does anyone know of a recipe/trick to be able to get all my buttons, tab bars etc to have the pointing hand cursor without repeating myself all over?
I would maintain a list of all the widgets for which you want this behaviour, and loop:
widgetsToChange = [someWidget, anotherWidget, ...]
for w in widgetsToChange:
hand_cursor(w)
#or w.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
You can also use QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) to change for all the application.

Beginner python ( Tkinter ) - How to ask for a rate to make a currency conversion?

I am a beginner in programming so I'm sorry if the code is confusing or too long.
My question is: I am stuck with the rate input and how to put everything together to make the conversion works?
Thank you.
from tkinter import *
def convertDtoE():
fromDtoE=fromDtoEVar
fromDtoEVar.get()
fromEtoD=fromEtoDVar
fromEtoDVar.get()
fromDtoEVar.set(fromEtoD)
def convertEtoD():
fromDtoE=fromDtoEVar
fromDtoEVar.get()
fromEtoD=fromEtoDVar
fromEtoDVar.get()
fromEtoDVar.set(fromDtoE)
def main():
window=Tk()
global fromDtoEVar
fromDtoEVar=DoubleVar()
fromDtoEVar.set(0.0)
global fromEtoDVar
fromEtoDVar=DoubleVar()
fromEtoDVar.set(0.0)
aa=Label(window,text="Dollars")
aa.pack()
a=Entry(window,textvariable=fromDtoEVar)
a.pack()
rr=Label(window,text="Rate")
rr.pack()
rate=Entry(window)
rate.pack()
bb=Label(window,text="Euros")
bb.pack()
b=Entry(window,textvariable=fromEtoDVar)
b.pack()
c=Button(window, text="Convert Euros", command=convertEtoD)
c.pack()
d=Button(window, text="Convert Dollars", command=convertDtoE)
d.pack()
window.mainloop()
main()
I'm not sure exactly what you're asking, but there's one very big problem with your code that you repeat multiple times, so I'll assume that's what you want fixed:
fromDtoE=fromDtoEVar
fromDtoEVar.get()
This does nothing useful. It returns the current value of the DoubleVar, which you then ignore. Meanwhile, you've bound another name to the DoubleVar itself, but then you use the original name. I think what you wanted is:
fromDtoE=fromDtoEVar.get()
Now, at least, you'll be copying actually numbers around, instead of the string representations of Tk variables like PY_VAR0.
You're still not actually doing any conversions, just copying the numbers back and forth. To fix that, first you're going to need to actually access the value in the Rate widget. You can attack a DoubleVar to that the same way you do the other two widgets, access its value in the same way, and then just do the math.

Categories