Widget in a second frame in a main class does not appear - python-3.x

I am trying to add a second frame inside my main class and put there a few widgets. I created a frame by using a method and assigned one of the widget to that frame but the problem is it does not appear.
I provided below piece of code with window configuration and 2x Labels which are at the main frame (Both appear correctly) and one in the new frame which appearing problem.
If you have some idea, please help me :)
import tkinter as tk
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
# Adding a background picture
self.background_img = tk.PhotoImage(file="in office.png")
back_ground_img_label = tk.Label(self, image=self.background_img)
back_ground_img_label.pack(fill="both", expand=True)
# Adjusting the window
width_of_window = 1012
height_of_window = 604
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x_coordinate = int((screen_width / 2) - (width_of_window / 2))
y_coordinate = int((screen_height / 2) - (height_of_window / 2) - 30)
self.geometry(
f"{width_of_window}x{height_of_window}+{x_coordinate}+{y_coordinate}"
)
self.bet_frame()
bet_value_label_bg = tk.Label(self)
bet_value_label_bg.place(x=462, y=300)
coin_button_1 = tk.Button(self.frame)
coin_button_1.place(x=233, y=435)
def bet_frame(self):
self.frame = tk.Frame(width=1012, height=604)
self.frame.pack()
if __name__ == "__main__":
MainApplication().mainloop()

The only thing you put in the self.frame is the coin_button_1, but as you place it at (233, 435) is is hidden below the main window self.
Personally I would not use place but rather either pack or even better grid to place the widgets on the screen (see Setting Frame width and height)
So if you change def bet_frame(self) as follows it will be visible
...
bet_value_label_bg = tk.Label(self, text='value')
bet_value_label_bg.place(x=462, y=300)
def bet_frame(self):
self.frame = tk.Frame(master=self, width=1012, height=604)
self.frame.pack()
coin_button_1 = tk.Button(self.frame, text='coin button')
coin_button_1.pack()
...
Note the bet_value_label_bg shows up in the middle of the picture and you may have to expand the main window to make the self.frame visible, depending on the size of the picture.

Related

Tkinter error: bad window path name when deleting frames dynamically

Im trying to recreate a little version of trello in tkinter. Right now im stuck I have a problem when I want to delete frames in a different order. For example: I click on the button and a new frame is generated if I delete that everything works. If I create 3 frames I have to remove them in the same order as I have created them. So I think my problems lies in the pop function but I dont know how to access them manually. When i change the pop function to (1) then I have to delete the second creation first instead of the first.
Here is the code:
from tkinter import *
class Window:
def __init__(self, width, height):
self.root = Tk()
self.width = width
self.height = height
self.root.geometry(width + "x" + height)
class Frames:
def __init__(self):
self.l = Frame(window.root, bg="red", height=300, width=300, relief="sunken")
self.l.place(relwidth=0.3, relheight=0.3)
self.deleteB = Button(self.l, text="X", command=self.delete_frame, bg="blue")
self.deleteB.place(rely=0, relx=0.92)
self.addB = Button(self.l, text="Add", command=self.add_note, bg="blue")
self.addB.place(rely=0, relx=0.65)
def delete_frame(self):
self.l.pack()
self.l.pack_forget()
self.l.destroy()
frames.pop()
def add_note(self):
self.note_Label = Label(self.l, text="Clean the room")
self.note_Label.pack(padx=20, pady=10)
self.delete_Note = Button(self.note_Label, text="X", command=self.del_Note)
self.delete_Note.pack(padx=5, pady=5)
def del_Note(self):
self.note_Label.pack_forget()
self.note_Label.destroy()
class Note:
def __init__(self):
pass
class DragNDrop:
def __init__(self):
pass
def make_draggable(self, widget):
widget.bind("<Button-1>", self.on_drag_start)
widget.bind("<B1-Motion>", self.on_drag_motion)
def on_drag_start(self, event):
widget = event.widget
widget._drag_start_x = event.x
widget._drag_start_y = event.y
def on_drag_motion(self, event):
widget = event.widget
x = widget.winfo_x() - widget._drag_start_x + event.x
y = widget.winfo_y() - widget._drag_start_y + event.y
widget.place(x=x, y=y)
class Buttons:
def __init__(self):
self.button = Button(window.root, width=20, height=20, bg="blue", command=self.add_frames)
self.button.pack()
def add_frames(self):
frames.append(Frames())
print(frames)
window = Window("800", "600")
frames = []
drag = DragNDrop()
button = Buttons()
while True:
for i in frames:
drag.make_draggable(i.l)
window.root.update()
If someone has an Idea or workaround that would be nice to know.
Also I have another Idea instead of destroying them I could just hide them but in the end that makes the programm really slow at some point.
Here is the error: _tkinter.TclError: bad window path name ".!frame2"
Your code needs to remove the frame from the list. Instead, you're calling pop which always removes the last item. That causes you to lose the reference to the last window, and one of the references in frames now points to a window that has been deleted (which is the root cause of the error)
Instead, call remove:
def delete_frame(self):
self.l.destroy()
frames.remove(self)

Problem when trying to add a scrolling bar on a frame

I try do add a scrolling bar in my frame, with no success. I have read posts about this subject on stackoverflow and tried many suggestions, but they don't work for me.
I have tried this.
import tkinter as tk
class Interface(tk.Frame):
def __init__(self, root, **kwargs):
tk.Frame.__init__(self, root, width=768, height=576, **kwargs)
#self.pack(fill=tk.BOTH, expand=True)
self.canvas = tk.Canvas(root, borderwidth=0, background="#ffffff")
self.frame = tk.Frame(self.canvas, background="#ffffff")
self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((4,4), window=self.frame, anchor="nw",
tags="self.frame")
self.frame.bind("<Configure>", self.onFrameConfigure)
self.populate()
def command():
global parameters
temp=[entry.get() for entry in self.entries]
parameters=temp
self.bouton_Executer = tk.Button(self.frame, text="Exécuter le programme", fg="red", command=command)
self.bouton_Executer.place(x=400 ,y= 840)
def populate(self):
self.all_entries= []
label=tk.Label(self.frame, text="a").place(x = 20, y = 60)
entry=tk.Entry(self.frame)
entry.place(x = 60, y = 60)
self.all_entries.append(entry)
label=tk.Label(self.frame, text="b").place(x = 20, y = 80)
entry=tk.Entry(self.frame)
entry.place(x = 60, y = 80)
self.all_entries.append(entry)
def onFrameConfigure(self, event):
#'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all")
root = tk.Tk()
interface = Interface(root)
interface.mainloop()
interface.destroy()
I want to have a window with the two widgets a and b, and a scrolling bar (because in the entire code, I have many widgets and all the widgets don't appear on the window).
By creating the object 'interface', no widgets appear in the window and I see the scrolling bar but I can't scroll the window.
When you use pack or grid, the default behavior is for the containing widget to grow or shrink to fit all of its children. place does not have this behavior, so when you use place to put widgets in a frame, the frame will retain whatever its requested size is. You didn't give the frame a size, so it will default to 1x1 pixels.
It is rarely a good idea to use place. Not only for this reason, but also because you have to do all of the work of making sure the layout fits the window, is responsive, and works on machines that may have a different resolution and different fonts.
If you switch to using grid or pack, your frame will automatically grow to fit all of the widgets contained inside.

expand scrollable ttk.notebook in python3 / gui tkinter

coding a scrollable frame with a ttk.notebook inside works.
But the scrollbar / notebook has a fixed size. How can I change it?
from tkinter import Canvas, Scrollbar, Button, Tk
from tkinter.ttk import Frame, Notebook
class VerticalScrolledFrame(Frame):
"""A pure Tkinter scrollable frame that actually works!
* Use the 'interior' attribute to place widgets inside the scrollable frame
* Construct and pack/place/grid normally
* This frame only allows vertical scrolling
"""
def __init__(self, parent, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = Scrollbar(self, orient='vertical')
vscrollbar.pack(fill='y', side='right', expand='false')
canvas = Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set)
canvas.pack(side='left', fill='both', expand='true')
vscrollbar.config(command=canvas.yview)
# reset the view
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = Frame(canvas)
interior_id = canvas.create_window(0, 0, window=interior,
anchor='nw')
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas's width to fit the inner frame
canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
root = Tk()
class Overview:
def __init__(self):
#mainframe to make a scrollable window
self.mainframe = VerticalScrolledFrame(root)
self.mainframe.grid(row=0, column=0)
# create a notebook
self.TNotebook_Overview = Notebook(self.mainframe.interior)
self.TNotebook_Overview.grid(row=0, column=0)
self.TNotebook_Overview.configure(takefocus="")
self.Frame_Overview = Frame(self.TNotebook_Overview)
self.TNotebook_Overview.add(self.Frame_Overview)
self.TNotebook_Overview.tab(0, text="Table", compound="left",underline="-1", )
buttons = []
for i in range(30):
buttons.append(Button(self.Frame_Overview, text="Button " + str(i)))
buttons[-1].grid(column=0, row=i)
if __name__ == "__main__":
ov = Overview()
root.title('Overview Items Database')
root.geometry('800x800+10+10')
root.configure(background="#4C7274")
root.grab_set()
root.mainloop()
I expect an expanded scrollable notebook/frame filled entire Tk() window.
Because the "ai" of stackoverflow don't allow use code from another thread, here is code https://pastebin.com/ykJGViAz
Inside init, do this instead
self.mainframe.grid(row=0, column=0, sticky="nsew")
self.TNotebook_Overview.grid(row=0, column=0, sticky="nsew")
This tells the frame to fill as much space as it needs
Just add this in the third line of init in the class overview:
self.mainframe.pack(fill = BOTH, expand = 1)

Adding padding query insert to Listbox

I have read a number of threads and other resources to try to find the correct way to handle this but I have not found anything that works with my application.
Here is what I am trying to accomplish.
When a query is completed and the insert of the data to a Listbox is done I cannot seem to get it to margin the data insert by 1 character space.
I am using pack() and I have read the tkinter manual for this and have tried each example available along with others found on various threads here.
The widget:
output = tkinter.Listbox(window_2, height = 20, font='Times 10',
width=42, bd=1, bg = '#FFD599', fg = '#9A0615', selectmode=SINGLE)
output.pack()
output.place(x=210, y=195)
I have tried padx and pady with pack() without success, although this works successfully with the Text widget. I have also attempted to use a few alternatives that I have found here on the site but all without success in margining the Listbox when the data is inserted.
Any advice?
pack's padx/pady and ipadx/ipady options don't affect the data that is inside the listbox. The listbox itself doesn't have any options to add an internal margin.
To get a margin around the inside of the listbox, what I normally do is give it a zero borderwidth and highlightthickness, and then place it in a frame with the same background color and let the frame be the border. You can then add any padding you want between the border and the listbox.
This is also convenient because you can put a scrollbar inside the frame, giving it the appearance that it is inside the listbox without actually being inside the listbox.
Example:
import tkinter as tk
root = tk.Tk()
root.configure(background="gray")
listbox_border = tk.Frame(root, bd=1, relief="sunken", background="white")
listbox_border.pack(padx=10, pady=10, fill=None, expand=False)
listbox = tk.Listbox(listbox_border, width=20, height=10,
borderwidth=0, highlightthickness=0,
background=listbox_border.cget("background"),
)
vsb = tk.Scrollbar(listbox_border, orient="vertical", command=listbox.yview)
listbox.configure(yscrollcommand=vsb)
vsb.pack(side="right", fill="y")
listbox.pack(padx=10, pady=10, fill="both", expand=True)
for i in range(100):
listbox.insert("end", "Item #{}".format(i))
root.mainloop()
here is a variation on the much appreciated answer by Bryan Oakley.
it uses ttk widgets instead of tk widgets
the scrollbar tracks your position in the list box when you scroll with the mouse
uses the oStyle.theme_use("clam") because it may look more modern...this can be commented out
'
import tkinter as tk
from tkinter import ttk
try: # allows the text to be more crisp on a high dpi display
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)
except:
pass
root = tk.Tk()
oStyle = ttk.Style()
oStyle.theme_use("clam")
oStyle.configure('LB.TFrame', bd=1, relief="sunken", background="white")
listbox_border = ttk.Frame(root, style='LB.TFrame')
listbox_border.pack(padx=4, pady=4, fill=None, expand=False)
vsb = ttk.Scrollbar(listbox_border)
vsb.pack(side="right", fill="y")
listbox = tk.Listbox(listbox_border, width=20, height=10, borderwidth=0,
highlightthickness=0, selectmode=tk.SINGLE,
activestyle=tk.NONE)
listbox.pack(padx=6, pady=6, fill="y", expand=True)
listbox.config(yscrollcommand=vsb.set)
vsb.config(command=listbox.yview)
for i in range(100):
listbox.insert("end", "Item #{}".format(i))
root.mainloop()
'
first of all to format chars in a tkinter listbox you need to use a fixed font and .format python funcion....;
So you can do something this
Press Load to load data in the listbox and pay attention to this line code
s = '{0:>8}{1:5}'.format(i[0],i[1])
self.list.insert(tk.END, s)
import tkinter as tk
RS = (('Apple',10),
('Banana',20),
('Peack',8),
('Lemon',6),)
class App(tk.Frame):
def __init__(self,):
super().__init__()
self.master.title("Hello World")
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1,)
f = tk.Frame()
sb = tk.Scrollbar(f,orient=tk.VERTICAL)
self.list = tk.Listbox(f,
relief=tk.GROOVE,
selectmode=tk.BROWSE,
exportselection=0,
background = 'white',
font='TkFixedFont',
yscrollcommand=sb.set,)
sb.config(command=self.list.yview)
self.list.pack(side=tk.LEFT,fill=tk.BOTH, expand =1)
sb.pack(fill=tk.Y, expand=1)
w = tk.Frame()
tk.Button(w, text="Load", command=self.on_callback).pack()
tk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0)
def on_callback(self,):
for i in RS:
s = '{0:>8}{1:5}'.format(i[0],i[1])
self.list.insert(tk.END, s)
def on_close(self):
self.master.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

Python Tkinter fixing text location on scrolling screen

Trying to scroll a graphic on the screen while keeping the text in the same place. The text shows where the mouse is located. I've thought about the idea of scrolling the text in the opposite direction of the screen scroll but I'm not sure if there is an easier way of doing it and if I have to scroll the text the opposite way I'm not sure how to set the initial text pointer so I can come back and recall/reset it. I'm only wanting it to show the position on not as will be doing other things in the near future.
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.keys = dict.fromkeys(('Left', 'Right', 'Up', 'Down'))
self.canvas = tk.Canvas(self, background="bisque", width=700, height=700)
self.canvas.pack(fill="both", expand=True)
self.canvas.configure(scrollregion=(-1000, -1000, 1000, 1000))
self.looper() # start the looping
def keypress(self,event):
if event.keysym in self.keys:
# event type 2 is key down, type 3 is key up
self.keys[event.keysym] = event.type == '2'
def looper(self):
if self.keys['Up']:
self.canvas.yview_scroll(-2,'units')
if self.keys['Down']:
self.canvas.yview_scroll(2,'units')
if self.keys['Left']:
self.canvas.xview_scroll(-2,'units')
if self.keys['Right']:
self.canvas.xview_scroll(2,'units')
self.after(5, self.looper) # set the refresh rate here ... ie 20 milliseconds. Smaller number means faster scrolling
def on_press(self, event):
self.last_x = event.x
self.last_y = event.y
self.startx, self.starty = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y)
def on_motion(self, event):
self.canvas.delete("sx")
self.startx, self.starty = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y)
px = round(-(((1000-self.startx) * .00015) + 69.3),5)
py = round((45.05-((1000+self.starty) * .00015)),5)
self.canvas.create_text(400,-400, text = str(px), fill = "black", tags = "sx")
self.canvas.create_text(475,-400, text = str(py), fill = "black", tags = "sx")
def button_motion(self,event):
delta_x = event.x - self.last_x
delta_y = event.y - self.last_y
self.last_x = event.x
self.last_y = event.y
self.canvas.xview_scroll(-delta_x, "units")
self.canvas.yview_scroll(-delta_y, "units")
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
The simplest solution is to use a widget that is not embedded in the canvas for the text so that it won't scroll when the canvas scrolls. Create a Label with it's parent being the canvas, and then use place to superimpose the label on top of the canvas.

Resources