I have a problem i want to put a image as the background for this little converter can someone help me? I was looking online and on stackoverflow but none of the things i found would work with the class.
__author__ = 'apcs'
from tkinter import *
class FarenheitToCelsius(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("Farenheit To Celsius Conversion")
self.grid()
self.farenheitLabel = Label(self, text="Farenheit")
self.farenheitLabel.grid(row=0, column=0)
self.farVar = DoubleVar()
self.farEntry = Entry(self, textvariable=self.farVar)
self.farEntry.grid(row=0, column=1)
self.celsiusLabel = Label(self, text="Celsius")
self.celsiusLabel.grid(row=1, column=0)
self.celVar = DoubleVar()
self.celEntry = Entry(self, textvariable=self.celVar)
self.celEntry.grid(row=1, column=1)
self.button = Button(self,
text="Convert to Celsius",
command=self.convertToFarenheit)
self.button2 = Button(self,
text="Convert to Farenheit",
command=self.convertToCelsius)
self.button.grid(row=2, column=1, columnspan=1)
self.button2.grid(row=2, column=0, columnspan=1)
def convertToFarenheit(self):
fare = self.farVar.get()
cels = (fare - 32) * 5 / 9
self.celVar.set(cels)
def convertToCelsius(self):
cel = self.celVar.get()
far = cel * 9 / 5 + 32
self.farVar.set(far)
def main():
FarenheitToCelsius().mainloop()
main()
I can think of at least three ways to do this:
Create an image in a label, and use place to put it in the frame. Then create all of the other widgets and use pack or grid as you normally would. Make sure you create the frame first, then the label with the image, then the children of the frame, so that the stacking order is correct.
Use a canvas instead of a frame, and use the create_image method to add an image. Then you can pack/place/grid children as normal.
Instead of a frame, use a label as the container, and then pack/place/grid widgets into the label (yes, you can add children to a label widget).
Related
I'm pretty new to Tkinter and I build a little window with different widgets.
My Code looks like this:
import tkinter as tk
from tkinter import ttk
class Application(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.master = master
self.master.geometry("800x600")
self.master.title("Tkinter Sandbox")
self.master.grid_rowconfigure(0, weight=1)
self.master.grid_columnconfigure(1, weight=1)
self._create_left_frame()
self._create_button_bar()
self._create_label_frame()
def _create_left_frame(self):
frame = tk.Frame(self.master, bg="red")
tree_view = ttk.Treeview(frame)
tree_view.column("#0", stretch=tk.NO)
tree_view.heading("#0", text="Treeview")
tree_view.pack(fill=tk.Y, expand=1)
frame.grid(row=0, column=0, rowspan=2, sticky=tk.N + tk.S)
def _create_button_bar(self):
frame = tk.Frame(self.master, bg="blue")
button_run_single = tk.Button(frame, text="Button 1")
button_run_all = tk.Button(frame, text="Button 2")
button_details = tk.Button(frame, text="Button 3")
button_run_single.grid(row=0, column=0)
button_run_all.grid(row=0, column=1, padx=(35, 35))
button_details.grid(row=0, column=2)
frame.grid(row=0, column=1, sticky=tk.N)
def _create_label_frame(self):
frame = tk.Frame(self.master, bg="blue")
name_label = tk.Label(frame, text="Label 1")
performance_label = tk.Label(frame, text="Label 2")
name_entry = tk.Entry(frame)
performance_entry = tk.Entry(frame)
name_label.grid(row=0, column=0)
name_entry.grid(row=0, column=1)
performance_label.grid(row=1, column=0)
performance_entry.grid(row=1, column=1)
frame.grid(row=1, column=1)
if __name__ == '__main__':
root = tk.Tk()
app = Application(root)
app.mainloop()
Between the three buttons and the label + entry frame is a huge space. I want the button and label + entry frame right under each other, without the huge space but the treeview should also expand vertically over the whole application window.
I think the problem might be my row and column configuration but I don't know how to solve this problem.
The way you've structured your code makes it hard to see the problem. As a good general rule of thumb, all calls to grid or pack for widgets within a single parent should be in one place. Otherwise, you create dependencies between functions that are hard to see and understand.
I recommend having each of your helper functions return the frame rather than calling grid on the frame. That way you give control to Application.__init__ for the layout of the main sections of the window.
For example:
left_frame = self._create_left_frame()
button_bar = self._create_button_bar()
label_frame = self._create_label_frame()
left_frame.pack(side="left", fill="y")
button_bar.pack(side="top", fill="x")
label_frame.pack(side="top", fill="both", expand=True)
I used pack here because it requires less code than grid for this type of layout. However, if you choose to switch to grid, or wish to add more widgets to the root window later, you only have to modify this one function rather than modify the grid calls in multiple functions.
Note: this requires that your functions each do return frame to pass the frame back to the __init__ method. You also need to remove frame.grid from each of your helper functions.
With just that simple change you end up with the button bar and label/entry combinations at the top of the section on the right. In the following screenshot I changed the background of the button_bar to green so you can see that it fills the top of the right side of the UI.
You need to change line
self.master.grid_rowconfigure(0, weight=1)
to
self.master.grid_rowconfigure(1, weight=1)
so that the second row takes all the space. Then you need to stick widgets from the label frame to its top by adding sticky parameter to the grid call in _create_label_frame:
frame.grid(row=1, column=1, sticky=tk.N)
I prefer to use the Pack Function since it gives a more open window - its easy to configure. When you use Pack() you can use labels with no text and just spaces to create a spacer, by doing this you won't run into the problem your facing.
i am doing a project to calculate the charges for different weight of the parcel. however, the position of the component(label, button, textbox and scrolltext) did not place it in the manner i want.
i want the length label to be in the center top, and the length textbox next to it. similar to width label and height label.
the following is my code.
from tkinter import *
import tkinter.scrolledtext as st
class Delivery(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("Delivery Charges Calculator(Ong Jia Sheng)")
self.master.geometry("700x350") #width by height dimension
#create the components
#create RadioButtons
self._salute = StringVar() #common variable for Radio buttons
self._rb1 = Radiobutton(self,text="cm",variable=self._salute,value="cm")
self._rb2 = Radiobutton(self,text="inch",variable=self._salute,value="inch")
self._salute.set("cm") #value of radiobutton, this is to set default selection
#create other components
self._lenLb = Label(self, text="Length:")
self._widLb = Label(self, text="Width:")
self._heiLb = Label(self, text="Height:")
self._weiLb = Label(self, text="Weight(kg):")
self._lenTb = Entry(self, width=25)
self._widTb = Entry(self, width=25)
self._heiTb = Entry(self, width=25)
self._weiTb = Entry(self, width=25)
self._charButt = Button(self, width=15, text="Calculate Charge",command=self.calcCharges)
self._cleButt = Button(self, width=15,text="Clear",command=self.Clear)
self._stxt = st.ScrolledText(self,width=700,height=5)
#place onto the window use grid layout
self._lenLb.grid(row=0,column=4)
self._lenTb.grid(row=0,column=5)
self._widLb.grid(row=1,column=4)
self._widTb.grid(row=1,column=5)
self._heiLb.grid(row=2,column=4)
self._heiTb.grid(row=2,column=5)
self._rb1.grid(row=5,column=6)
self._rb2.grid(row=5,column=7)
self._weiLb.grid(row=8,column=4)
self._weiTb.grid(row=8,column=5)
self._charButt.grid(row=9,column=5)
self._cleButt.grid(row=9,column=6)
self._stxt.grid(row=9,column=0)
self.grid()
def calcCharges(self):
self._salute.set("inch")
def Clear(self):
self._salute.set("cm")
def main():
app = Delivery()
app.mainloop()
main()
The width of ScrolledText constructor is expressed in characters (not in pixels).
Since it is very wide (700 char) and alone in the first column, other widgets are moved far to the right and outside the screen.
My objective is to solve the problem of the grid exceeding the window(shown as figure.1)
enter image description here
My program function is creating a grid that number of columns defined by user.
I tried using canvas to solve this problem, but it still doesn't work successfully.
It doesn't show the full grid in the canvas.(shown as figure.2)
enter image description here
Below is my code, could you please help solve the problems or give me some advice.
Thanks a lot.
Code:
import tkinter as tk
import tkinter.messagebox
import tkinter.filedialog
MainWindow = tk.Tk()
MainWindow.title('Helloworld')
MainWindow.geometry('1000x800')
def btn_generate():
global EntryNamelist
global Entrycoordinatelist
global EntryLabellist
con_num = en_condition_num.get()
if con_num != '':
#### Grid Body
for i in range(1,int(con_num) +1 ):
lb_name = tk.Label(fm_grid, text="Condition" + str(i) )
lb_name.grid(row=i, column=0, padx=2, pady=1, ipadx=20, ipady=5)
En_name = tk.Entry(fm_grid, bd = 2,width = 10,font=('Ubuntu', 10))
En_name.grid(row=i, column=1, padx=2, pady=1, ipadx=35, ipady=5)
En_coor = tk.Entry(fm_grid, bd = 2,width = 10,font=('Ubuntu', 10))
En_coor.grid(row=i, column=2, padx=2, pady=1, ipadx=200, ipady=5)
else:
tk.messagebox.showerror("Error", "Please input a num of conditions")
fm_main = tk.Frame()
fm3 = tk.Frame(fm_main)
lb_condition = tk.Label(fm3,text = 'Please input the number of condition')
lb_condition.pack(side="left")
en_condition_num = tk.Entry(fm3, bd = 2,width = 5)
en_condition_num.pack()
fm3.pack()
btn_generate = tk.Button(fm_main,text="Generate Grid",command=btn_generate)
btn_generate.pack()
lb_en = tk.Label(fm_main,text = '')
lb_en.pack()
def myfunction(event):
canvas.configure(scrollregion=canvas.bbox("all"),width=200,height=200)
canvas=tk.Canvas(fm_main)
fm_grid = tk.Frame(canvas)
fm_grid.pack()
myscrollbar=tk.Scrollbar(fm_main,orient="vertical",command=canvas.yview)
canvas.configure(yscrollcommand=myscrollbar.set)
myscrollbar.pack(side="right",fill="y")
canvas.pack(side="left")
canvas.create_window((4,4),window=fm_grid,anchor='nw')
fm_grid.bind("<Configure>",myfunction)
fm_main.pack()
MainWindow.mainloop()
Question: It doesn't show the full grid in the canvas
You have to sync, the width of the Canvas with the width of the Frame inside.
Note: fm_grid = tk.Frame(canvas, bg='blue') is shown in 'blue'.
Dont's:
Remove fm_grid.pack(), you layout with: canvas.create_window(....
Also, i recommend not to use a offset (4, 4), because you have to calculate with this offset on every canvas.configure(..., width=width + 4. Use (0, 0) instead.
# fm_grid.pack()
...
canvas.create_window((4,4),window=fm_grid,anchor='nw')
Useless, to create dynamically window:
Your usage of canvas.bbox is useless, because it's the dimension you want to layout this widget.
Using a fixed width=200, smaller than fm_grid.width will allways cut the fm_grid content, it's not dynamically either.
canvas.configure(scrollregion=canvas.bbox("all"),width=200,height=200)
How to sync the width of the Canvas with the width of the Frame inside?
You bound fm_grid.bind("<Configure>", therefore the event.widget is fm_grid, the Frame inside.
Get the dimensions of the event.widget from there w.winfo_... and build a bbox tuple to set scrollregion.
Use width to set canvas.width, to be in sync with the event.widget.winfo_width().
class ScrollCanvas(tk.Canvas):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
def create_window(self, child):
super().create_window((0, 0), window=child, anchor='nw')
child.bind("<Configure>", self.on_configure)
def on_configure(self, event):
w = event.widget
bbox = x, y, width, height = 0, 0, w.winfo_width(), w.winfo_height()
self.configure(scrollregion=bbox, width=width)
Tested with Python: 3.5 - 'TclVersion': 8.6 'TkVersion': 8.6
I want to create a GUI with Tkinter, such that you are at a grocery store, you enter the item, price, and quantity, and each item will appear on the top part of the screen.
I have a top and bottom frame, and when I place an entry it goes right in the middle of the bottom frame. I have tried justifying the position to the left, anchoring it, sticking it and doing whatever, but it's not moving.
This is my code.
from Tkinter import *
root = Tk()
root.title("project")
root.geometry("700x850+0+0")
textInput = StringVar()
class MenuBoard(object):
def __init__(self,master):
self.master = master
mainFrame = Frame(self.master,bg = "white",width=700,height=400)
mainFrame.grid(row=0,column=0)
labelFrame = Frame(self.master, bg = "red",height=40,width=700)
labelFrame.grid(row=0,column=0,sticky = N)
welcomeLabel = Label(self.master, text = "",fg= "black",bg="red",)
welcomeLabel.config(font=("Courier New",23))
welcomeLabel.grid(row=0,column=0,sticky = N)
actual = MenuBoard(root)
root.mainloop()
-Use
bottomFrame.grid_propagate(False)
to expand the frame and
storeItemEntry.grid(pady=30)
Or whatever value you want for pady. You might have to give row and column numbers to grid() if you're going to place other widgets in bottomFrame.
You saw "a strange dark grey background" as mentioned in the comment because you gave bg = "grey" to bottomFrame. The background wasn't visible initially because the frame shrank to fit the Entry. You can change the color to what you want or remove it entirely.
The following should be close to what you're looking for:
from Tkinter import *
root = Tk()
root.title("project")
root.geometry("700x850+0+0")
textInput = StringVar()
class MenuBoard(object):
def __init__(self,master):
self.master = master
mainFrame = Frame(self.master,bg = "white",width=700,height=400)
mainFrame.grid(row=0,column=0)
labelFrame = Frame(self.master, bg = "red",height=40,width=700)
labelFrame.grid(row=0,column=0,sticky = N)
welcomeLabel = Label(self.master, text = "Main Heading Here",fg= "black",bg="red",)
welcomeLabel.config(font=("Courier New",23))
welcomeLabel.grid(row=0,column=0,sticky = N)
bottomFrame = Frame(self.master, bg = "grey", height=450,width=700) #Change/remove bg
bottomFrame.grid(row=1, column=0)
bottomFrame.grid_propagate(False)
storeItemEntry = Entry(bottomFrame, font=("Courier New",10,"bold"), textvariable=textInput, bd =4)
storeItemEntry.grid(pady=30)
actual = MenuBoard(root)
root.mainloop()
UPDATE:
Based on your comments, here is a rough implementation to work with.
from Tkinter import *
root = Tk()
root.title("project")
root.geometry("700x850+0+0")
class MenuBoard(object):
def __init__(self,master):
self.master = master
mainFrame = Frame(self.master,bg = "white",width=700,height=400)
mainFrame.grid(row=0,column=0)
mainFrame.grid_propagate(False)
mainFrame.grid_columnconfigure(0, weight=1)
heading = " Store Item".ljust(45)[:45] + "Item Price" # Pad the text with white spaces
listHeading = Label(mainFrame, text=heading, anchor="w", font=("Courier New",14,"bold"))
listHeading.grid(row=1, column=0, pady=5, stick="we")
# Use Text widget so you can keep inserting items
self.listItems = Text(mainFrame, font=("Courier New",12))
self.listItems.grid(row=2, column=0, pady=5, stick="we")
self.listItems.config(state="disabled") # Prevents edits on the Text
welcomeLabel = Label(mainFrame, text = "Main Heading Here",fg= "black",bg="red",)
welcomeLabel.config(font=("Courier New",23))
welcomeLabel.grid(row=0,column=0, stick="we")
bottomFrame = Frame(self.master, bg = "grey", height=450,width=700) #Change/remove bg
bottomFrame.grid(row=1, column=0)
bottomFrame.grid_propagate(False)
storeItemLabel = Label(bottomFrame, text="Food Item: ")
storeItemLabel.grid(row=0, column=0)
self.storeItemEntry = Entry(bottomFrame, font=("Courier New",10,"bold"), bd =4)
self.storeItemEntry.grid(row=0, column=1, pady=15)
priceLabel = Label(bottomFrame, text="Price: ")
priceLabel.grid(row=2, column=0)
self.priceEntry = Entry(bottomFrame, font=("Courier New",10,"bold"), bd =4)
self.priceEntry.grid(row=2, column=1,)
btn = Button(bottomFrame, text="Add Item", command=self.add)
btn.grid(row=3, column=1, pady=15)
def add(self):
price = self.storeItemEntry.get() # Get item name from Entry
#Get price, format name and price
groceryItem = price.ljust(50)[:50] + "$%s" %(self.priceEntry.get())
self.listItems.config(state="normal") # Enable edits on the Text
self.listItems.insert("end", "\n "+groceryItem) # Edit Text
self.listItems.config(state="disabled") # Prevents edits on the Text
actual = MenuBoard(root)
root.mainloop()
A couple of things to note:
I removed some of your frames because they seemed redundant, you can add them back if needed.
Since you're working with a class, I added the self keyword to some of the attributes so I can use/call them later in other methods without errors. I left out the attributes that I do not need to call after creation.
StringVar/textvariable is not needed since you're updating the list with a button click.
There are lots of refinements I did not do (i.e. checking to see if a valid input is given before updating the list, ability to delete from the list, etc).
I used methods and features that you may or may not be aware of (.ljust(50)[:50], %s, etc)
I hope this helps :).
I'm trying to put together a window that displays a bunch of labels generated from a dict. I'm having trouble getting the scrollbars to work properly. They won't stick to the sides of the frame when I resize the window, and I can't get the canvas to respond to the scroll command. I need the window to support a large number of labels.
from Tkinter import *
from math import floor
bits = {}
#the dict is then built
class Bitbox(Canvas):
def __init__(self, parent, bitdict, *args, **kwargs):
Canvas.__init__(self, parent, background="black")
self.bitdict = bitdict
self.parent = parent
self.lbllist = []
n=0
for i in bitdict.keys():
label = Label(self, text=i, bg='black', fg='green')
n += 1
label.grid(row = ((n-1)%30), column=int(floor((n-1)/30)))
self.lbllist.append(label)
def main():
root = Tk()
frame = Frame(root)
frame.grid(sticky=N+S+E+W)
bts = Bitbox(frame, bits)
bts.grid(row=0, column=0)
vbar = Scrollbar(frame, orient=VERTICAL)
vbar.grid(row=0, column=1, sticky=N+S)
vbar.config(command=bts.yview)
hbar = Scrollbar(frame, orient=HORIZONTAL)
hbar.grid(row=1, column=0, columnspan=2, sticky=W+E)
bts.config(xscrollcommand=hbar.set)
hbar.config(command=bts.xview)
bts.config(yscrollcommand=vbar.set)
bts.config(scrollregion=(0,0,500,1000))
root.mainloop()
Clearly I'm new at all this. It's entirely possible I have a fundamental misunderstanding of how these widgets interact. Any help is much appreciated.
to get the scrollbar to react to the mouse bind the mouse to the scrollbar like this:
def on_mousewheel(event):
bts.yview_scroll(-1*(event.delta/120), "units")
def main():
global bts
#your code...
root.bind_all("<MouseWheel>",on_mousewheel)