I am making a personal project using Tkintr to create an interactive Periodic Table of Elements. Instead of making each Button for each element individually, I used the following code to create a bunch of buttons (beginner coder so sorry for formatting, etc):
from tkinter import *
from tkinter import messagebox
import pandas as pd
raw_csv = pd.read_csv('data/elements.csv')
el_symbols = {row.Number: row.Symbol for (index, row) in raw_csv.iterrows()}
img_ref_dict = {f'{sym}_img': f'images/{num}.png' for num, sym in el_symbols.items()}
window = Tk()
window.title("Periodic Table of Elements")
window.config(width=1202, height=676)
# All da buttons
# Creating population for button assignment
for name, path in img_ref_dict.items():
exec(f'{name} = PhotoImage(file="{path}")')
exec(f'{name}_button = Button(image={name}, highlightthickness=0, command=lambda: popup())')
window.mainloop()
To my understanding, the code contained within .mainloop() is continuously looping so is my for loop creating the buttons repeatedly? Would it be better to just create each button one by one? This method is just so much cleaner to me.
As per #acw1668's advice, you're on the right track but are better off doing something like:
for path in img_ref_dict.values(): # you don't really need 'name'
img = PhotoImage(file=path)
btn = Button(image=img, highlightthickness=0, command=lambda: popup())
btn.pack() # add the button to the UI (you'll probably want to tweak this a bit)
Minor Edit to say that unless you really need the name for each button image, you could tweak img_ref_dict to create a list of image paths instead, like so:
img_refs = [f'images/{num}.png' for num in el_symbols.keys()]
...and then modify the for loop slightly:
for path in img_refs:
# yadda yadda
Related
So I have the following code that creates the GUI in the picture below:
import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
# Some code
# Creating Master TreeView
treeView = ttk.Treeview(root)
treeView.heading("#0", text="Variables", anchor=tk.W)
treeView.place(relx=0, rely=0.00,relwidth=0.1,relheight=1)
# Some Code
# Creating Folders/Sub Folders
var = treeView.insert("", 0, text=name)
treeView.insert(var, "end", text="Type: "+type)
treeView.insert(var, "end", text="Value: "+str(value))
This is what It looks like without being pressed and then pressed
Is there anyway to decrease the tabspace of the sub folders? Like bring it back to where the black point is?
For context, this is what the whole gui looks like:
I have to reserve so much space for the Treeview just to make sure the subfolders appear on the screean, and it takes up way to much space. I tend to find that the treeview uses a lot of unnecessary space when adding subfolders
You can try and constain the tree column:
treeView.column('#0', width=your_width, stretch=False)
I have a general problem, which I sketch here as the details will be too involved to post here. I know the problem statement, is a bit fuzzy and I may need to go back and forth with an expert in this site. (unfortunately, it is difficult to put everything up here based on the type of the problem. I will really appreciate any help).
I am trying to create a GUI application using Tkinter. The way I am doing is as follows. I have a background script (say back.py) which has the data loaded, calculations done and graph plotted.
Now the way I want to do is that I have a GUI script which uses Tkinter and calls the back.py ( using import). Now I have the window created, with a button. This is where I am stuck. I want to click the button to trigger the background script and generate the plots ( which the background script generates).
After this I want to close the plot and want my GUI to pop up some buttons to input me some parameters. These parameters will be input to the next part of the back.py code ( I chose the parameters based on the plot). When I again click the button ( with the parameters selected), I want to start running the background code again which will output me a file.
How can I do this?. A general rough idea will be helpful.
Let me put an example as much as I can:( at least the skeleton of the code)
I have a background file say (a.py) and gui file ( say g.py)
a.py
import ...
def progA():
# reading a file
# doing something and generating a plot from the file
# Once the GUI's first part is done generating the plot, I need to close that
# plot (or button) and then click the button 2 to run the next function
def progB(y1, y2,y3):
#run a code... and generate an output file
g.py
from tkinter import *
from tkinter.ttk import *
class GUI ():
def create widgets(self):
#....
def create panel(self):
#create buttons
panel1 = ...
btn1 = Button(panel1, text="yyyyy", command=progA)
btn1.pack()
def create_panel1(self):
#create buttons
panel1 = ...
btn1 = Button(panel1, text="yyyyy", command=progA)
btn1.pack()
def create_panel2(self):
#create buttons
panel2 = ...
btn2 = Button(panel1, text="yyyyy", command=progB)
btn2.pack()
All_Entries = []
window = Tk()
D=GUI(window)
window.mainloop()
import a
runprogram1 = a.progA()
runprogram2 = a.probB(x, y, z)
My question is now, does the above makes sense? So I have a couple of questions:
How will I ensure that when I close the plots (from the output of progA), that the second button will show up?
Where can I input the there values of the parameters in the second button?
I am a new python user. I'm used to programing on matlab.
I've trying to make a simple GUI with Tkinter pack, but I'm having some problems with that. I had already read and searched what i want but I couldn't develop it.
What I'm trying to do is to make a listbox and when I choose one (or more) options the index be returned (and stored) as a variable (array or vector) that could be used to indexing another array.
The best result I got was a listbox where the index were printed, but not stored as a variable (at least it hasn't been shows in the variables list)
I'm using spyder (anaconda).
I tryied a lot of codes and I don't have this anymore.
Sorry for the dumb question. I guess I still thinking in a Matlab way to write
To keep this application simple, your best option is to get the listbox selection when you want to do something with it:
from tkinter import Tk, Listbox, MULTIPLE, END, Button
def doStuff():
selected = lb.curselection()
if selected: # only do stuff if user made a selection
print(selected)
for index in selected:
print(lb.get(index)) # how you get the value of the selection from a listbox
def clear(lb):
lb.select_clear(0, END) # unselect all
root = Tk()
lb = Listbox(root, selectmode=MULTIPLE) # create Listbox
for n in range(5): lb.insert(END, n) # put nums 0-4 in listbox
lb.pack() # put listbox on window
# notice no parentheses on the function name doStuff
doStuffBtn = Button(root, text='Do Stuff', command=doStuff)
doStuffBtn.pack()
# if you need to add parameters to a function call in the button, use lambda like this
clearBtn = Button(root, text='Clear', command=lambda: clear(lb))
clearBtn.pack()
root.mainloop()
I've also added a button to clear the listbox selection because you cannot unselect items by default.
First, import tkinter, then, create the listbox. Then, you can use curselection to get the contents of the listbox.
import tkinter as tk
root = tk.Tk() #creates the window
myListbox = tk.Listbox(root, select=multiple) #allows you to select multiple things
contentsOfMyListbox = myListbox.curselection(myListbox) #stores selected stuff in tuple
See the documentation here.
I am wondering if it is possible to redirect the output of a function to a tab in the ttk notebook widget.
I assumed it would be similar to the listbox widget where you just used listbox.insert but I can not get this to work.
I apologize if this is a simple question but its really stumped me and I am unable to find any helpful material online to help me.
thanks in advance
Im using python 3.3
The Insert function works almost the same, the only difference is instead of taking a string it takes a frame. Add is a lot simpler though, you don't need to specify an index it just adds it to the end. All you need to do is create a frame, pack a Text element into it, and then pack the whole thing into the notebook. It would look something like this
noteb = ttk.Notebook( root, width=500, height=300 )
frame1 = tkinter.Frame( noteb )
textbox = tkinter.Text( frame1, put whatever you want to put here )
frame1.pack( expand=1, fill='both' )
noteb.add( frame1, whatever parameters you want )
noteb.pack( expand=1, fill='both' )
You should then be able to change the text in textbox directly.
Depends somewhat on what type of widgets you use as tabs, but basically it shouldn't be very different. Just keep track of your tab widgets and call the appropriate method.
Example with two Text tabs:
from Tkinter import *
from ttk import Notebook
def addText(tab):
tab.insert(END, "foo! ")
root = Tk()
nb = Notebook(root, height=240, width=480)
tabs = {"foo": [], "bar": []}
for tabname in tabs:
tab = Text(nb)
tabs[tabname] = tab
nb.add(tab, text= tabname)
nb.pack()
Button(root, text= "Add text!", command = lambda: addText(tabs["foo"])).pack()
root.mainloop()
Clicking the "Add text!" button appends some text to the first tab.
How do I randomly add buttons to a Tkinter GUI? I need it to be able to create a button, then put it anywhere on the window, is this possible? I am using Python 2.6 on Windows.
If you want random button placement (or anything not aligned along a grid, etc.), you can use the place geometry manager. Depending on platform, overlapped buttons may not behave as you expect, though, so you may want to avoid them.
Here's a simple example:
from Tkinter import *
from random import random
root = Tk()
frame = Frame(root, height=200, width=200)
for i in range(10):
Button(frame, text=str(i)).place(x=random() * 150, y=random() * 180)
frame.pack()
root.mainloop()
There are several options to choose from. For example, you could design on a grid where you have six buttons per row. Then it's just a matter of starting at row 0, incrementing the column for each button. When you get to the last column, reset the column to 0 and increment the row by one.
Another option is to use a text widget as the container, and embed your buttons in the text widget with wrapping enabled. With this trick the buttons will fill a row automatically and wrap if the user grows or shrinks the main windows. It's a tiny bit more work, but it works well if that's the behavior you want.