Text widgets inline in scrollable frame - python

I would like to align text widgets horizontally and be able to scroll left and right the frame where are placed these widgets. The code below is almost what I want except the fact that my scrollbar doesn't work.
I found some example where it is said not to use pack or grid in Canvas. However, if I use place layout, my widgets simply disapear.
from tkinter import *
class MainView(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
self.canvas = Canvas(self)
self.sensorsStatsFrame = Frame(self.canvas)
myscrollbar = Scrollbar(self,orient=HORIZONTAL,command=self.canvas.xview)
myscrollbar.pack(side=BOTTOM,fill=X)
self.canvas.configure(xscrollcommand=myscrollbar.set)
self.canvas.pack(side=TOP, fill=BOTH)
test0 = Text(self.sensorsStatsFrame, bg="red", state=DISABLED, width=150)
test1 = Text(self.sensorsStatsFrame, bg="green")
test0.pack(side=LEFT)
test1.pack(side=LEFT)
self.canvas.create_window((0,0),window=self.sensorsStatsFrame,anchor='nw')
self.canvas.config(scrollregion=self.canvas.bbox("all"))
if __name__ == "__main__":
root = Tk()
main = MainView(root)
main.pack(fill="both", expand=1)
root.wm_geometry("1100x500")
root.wm_title("MongoDB Timed Sample Generator")
root.mainloop()

I would like to align text widgets horizontally and be able to scroll left and right the frame where are placed these widgets.
If i didn't misunderstand you, you should add an event function to your codes.
from tkinter import *
class MainView(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
self.canvas = Canvas(self)
self.sensorsStatsFrame = Frame(self.canvas)
myscrollbar = Scrollbar(self,orient=HORIZONTAL,command=self.canvas.xview)
myscrollbar.pack(side=BOTTOM,fill=X)
self.canvas.configure(xscrollcommand=myscrollbar.set)
self.canvas.pack(side=TOP, fill=BOTH)
test0 = Text(self.sensorsStatsFrame, bg="red", state=DISABLED, width=150)
test1 = Text(self.sensorsStatsFrame, bg="green")
test0.pack(side=LEFT)
test1.pack(side=LEFT)
self.canvas.create_window((0,0),window=self.sensorsStatsFrame,anchor='nw')
# Call the function like the below.
self.sensorsStatsFrame.bind("<Configure>", self.onFrameConfigure)
# Add below function to your codes.
def onFrameConfigure(self, event):
self.canvas.config(scrollregion=self.canvas.bbox("all"))
if __name__ == "__main__":
root = Tk()
main = MainView(root)
main.pack(fill="both", expand=1)
root.wm_geometry("1100x500")
root.wm_title("MongoDB Timed Sample Generator")
root.mainloop()

Related

Python tkinter Frame with multiple widgets within another Frame

I'd like to position one of my python tkinter Frames (ButtonWindow) within my other Frame (MainWindow), so that when I run the app the widgets in ButtonWindow are present in MainWindow along with the MainWindow widget.
In the code below the Buttons from ButtonWindow are present along with the MainWindow Label, but the ButtonWindow Label is missing.
I looked at the answers in Frame inside another frame In python Tkinter and tried to set the background to purple to understand where the borders of ButtonWindow actually are, but I can't see any purple?
Thanks for any help!
import tkinter as tk
class ButtonWindow(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.bd = 5
self.bg = "purple"
self.label = tk.Label(text="Button window", font=12)
for i in range(3):
self.button = ttk.Button(text="button", command= lambda: button_fun())
self.button.pack(side = tk.LEFT)
def button_fun(self):
pass
class MainWindow(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.label = tk.Label(text="Main Window", font=12)
self.label.pack(pady=10,padx=10)
self.button_window = ButtonWindow()
self.button_window.pack()
app = MainWindow()
app.mainloop()
It is better to specify the parent of widgets when creating them, otherwise they will be children of root window.
Also you have never called any layout function on the "Button window" label so it is not visible.
self.bd = 5 and self.bg = "purple" will not change the border width and the background color. Use self.config(bd=5, bg="purple") instead.
import tkinter as tk
from tkinter import ttk
class ButtonWindow(tk.Frame):
def __init__(self, master=None, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.config(bd=5, bg="purple") # replace self.bd = 5 and self.bg = "purple"
self.label = tk.Label(self, text="Button window", font=12, fg='white', bg='purple') # specify parent
self.label.pack() # pack the label, otherwise it is not visible
for i in range(3):
self.button = ttk.Button(self, text="button", command=self.button_fun) # specify parent
self.button.pack(side=tk.LEFT)
def button_fun(self):
pass
class MainWindow(tk.Frame):
def __init__(self, master=None, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.label = tk.Label(self, text="Main Window", font=12) # specify parent
self.label.pack(pady=10, padx=10)
self.button_window = ButtonWindow(self) # specify parent
self.button_window.pack()
root = tk.Tk() # create root window explicitly
MainWindow(root).pack()
root.mainloop()
Also I have changed command=lambda: button_fun() to command=self.button_fun. The former one will raise exception when any of the buttons is clicked.
If I understood correctly what you want is create a Main Frame with another frame inside it which include 3 buttons.
In this case, I changed a little bit your code to do that. One of the changes was replace the tk.Label for the tk.LabelFrame (This corrects the Label that was missing in the ButtonWindow as you stated).
The second change that I suggest is to pass the MainFrame to the ButtonWindow as a parent frame. To do this I created the myCoreFrame inside the MainWindowclass. Also, for all widgets I've set a parent frame.
import tkinter as tk
class ButtonWindow():
def __init__(self, Frame, *args, **kwargs):
self.label = tk.LabelFrame(Frame, text="Button window", font=12, bg = "purple")
self.label.pack()
for i in range(3):
self.button = tk.Button(self.label, text="button", command= lambda: button_fun())
self.button.pack(side = tk.LEFT)
def button_fun(self):
pass
class MainWindow():
def __init__(self, window, *args, **kwargs):
myCoreFrame = tk.Frame(window)
myCoreFrame.pack()
self.label = tk.LabelFrame(myCoreFrame, text="Main Window", font=12, bg = "red")
self.label.pack(pady=10,padx=10)
self.button_window = ButtonWindow(self.label)
root = tk.Tk()
app = MainWindow(root)
root.mainloop()
The problem is that you aren't putting the labels and buttons inside frames. You need to explicitly set the frame as the parent of the button and label. If you don't, the widgets become children of the root window.
class ButtonWindow(tk.Frame):
def __init__(self, *args, **kwargs):
...
self.label = tk.Label(self, text="Button window", font=12)
# ^^^^^^
for i in range(3):
self.button = ttk.Button(self, text="button", command= lambda: button_fun())
# ^^^^^^
...
class MainWindow(tk.Frame):
def __init__(self, *args, **kwargs):
...
self.label = tk.Label(self, text="Main Window", font=12)
# ^^^^^
self.label.pack(pady=10,padx=10)
self.button_window = ButtonWindow(self)
# ^^^^
...

Cannot set a background color for my tkinter application?

When I try to set a background color for my main Frame, that has all widgets as childs, it only change the very bottom of the background. If I set a background for all Frame widgets, it still does not color some empty space. How can I set a background color for it ? Here's the result with Frames colored.
The runnable code:
import tkinter as tk
class ToolbarButton(tk.Button):
def __init__(self, master, text, pixelref, *args, **kw):
super().__init__(master)
self.configure(text=text, image=pixelref, height=20, width=20, compound='center')
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs, bg="red")
self.parent = parent
# Textframe
self.text_frame = tk.Frame(root, width=600, height=790, bg="green") #doesn't show: has text_widget over
self.text_frame.pack_propagate(False)
self.text_widget = tk.Text(self.text_frame, width=1, height=1)
self.text_widget.pack(expand=True, fill='both')
# Toolbar
self.toolbar = tk.Frame(root,bg="blue")
self.pixel = tk.PhotoImage(width=1, height=1)
self.bold_button = ToolbarButton(self.toolbar, 'B', self.pixel)
self.bold_button.pack(side='left', padx=4)
self.italic_button = ToolbarButton(self.toolbar, 'I', self.pixel)
self.italic_button.pack(side='left', padx=4)
self.underline_button = ToolbarButton(self.toolbar, 'U', self.pixel)
self.underline_button.pack(side='left', padx=4)
# Packing
self.toolbar.pack(side='top', pady=60)
self.text_frame.pack(expand=True)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
You have placed your text_frame widget on the root, instead of on the application frame. I think you want self there instead of root. That makes it behave as I expect.
self.text_frame = tk.Frame(self, width=600, height=790, bg="green") #doesn't show: has text_widget over

How to center a Label in a scrollable Frame in tkinter?

I am trying to create a label (for now and different widgets in the future) inside a scrollable tkinter Frame but it always shows in the upper left corner of the window. My code is shown below and any help would be appreciated. Thank you.
import tkinter as tk
from tkinter import *
from tkinter import ttk
class ScrollableFrame(ttk.Frame):
def __init__(self, container, *args, **kwargs):
super().__init__(container, *args, **kwargs)
self.canvas = tk.Canvas(self)
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
self.scrollable_frame = ttk.Frame(self.canvas)
self.scrollable_frame.bind(
"<Configure>",
lambda e: self.canvas.configure(
scrollregion=self.canvas.bbox("all")
)
)
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor=NW)
self.canvas.pack(fill="both")
def _on_mousewheel(self, event):
self.canvas.yview_scroll(-1*int(event.delta/120), "units")
if __name__ == "__main__":
root = tk.Tk()
root.geometry("600x150")
frame = ScrollableFrame(root)
for i in range(20):
a = tk.Label(frame.scrollable_frame, text="Number: "+str(i))
a.pack(side=TOP, anchor=W, fill=X, expand=YES)
frame.pack(fill="both")
root.mainloop()

Widgets are not showing up in simple tkinter app

I've looked at other question with a similar title and have read the answers, however nothing has worked for me. I am trying to make a simple app with a listbox + scroll bar with two buttons below it all within a group box. I've used pyqt but this is my first time using tkinter:
import tkinter as tk
class InputWindow(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.initialize()
def initialize(self):
# Group box to contain the widgets
self.input = tk.LabelFrame(self, text="Input Files")
# Listbox with scrollbar to the side
self.listbox = tk.Listbox(self.input)
self.scrollbar = tk.Scrollbar(self.listbox, orient=tk.VERTICAL)
self.listbox.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.listbox.yview)
self.listbox.grid(row=0, column=0, columnspan=2)
self.add_btn = tk.Button(self.input, text="Add...")
self.add_btn.grid(row=1, column=0)
self.remove_btn = tk.Button(self.input, text="Remove")
self.remove_btn.grid(row=1, column=1)
if __name__ == "__main__":
root = tk.Tk()
app = InputWindow(root)
root.mainloop()
This is more or less what I want but in tkinter:
What am I doing wrong/how can this be done?
You're forgetting two things:
To pack (or grid or place) app
To pack (or grid or place) input
You're program with the required statements:
import tkinter as tk
class InputWindow(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.initialize()
def initialize(self):
# Group box to contain the widgets
self.input = tk.LabelFrame(self, text="Input Files")
# Listbox with scrollbar to the side
self.listbox = tk.Listbox(self.input)
self.scrollbar = tk.Scrollbar(self.listbox, orient=tk.VERTICAL)
self.listbox.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.listbox.yview)
self.listbox.grid(row=0, column=0, columnspan=2)
self.add_btn = tk.Button(self.input, text="Add...")
self.add_btn.grid(row=1, column=0)
self.remove_btn = tk.Button(self.input, text="Remove")
self.remove_btn.grid(row=1, column=1)
self.input.pack(expand=1, fill="both") # Do not forget to pack!
if __name__ == "__main__":
root = tk.Tk()
app = InputWindow(root)
app.pack(expand=1, fill="both") # packing!
root.mainloop()

Tkinter canvas not scrolling

Tkinter experts, I'm having trouble getting a Canvas to scroll. This is my second GUI, and I've done something similar before, so I don't know what I'm doing wrong. I'd appreciate any help you can offer.
Here's a minimal version of what I'm trying to do. I'm using python 3.4.3 on Windows 10.
import tkinter as tk
import tkinter.font as tk_font
import tkinter.ttk as ttk
import random
def get_string_var(parent, value=''):
var = tk.StringVar(parent)
var.set(value)
return var
class SummaryFrame(ttk.Frame):
def __init__(self, parent, **kwargs):
ttk.Frame.__init__(self, parent, **kwargs)
var_names = ['label_'+str(num) for num in range(1, 20)]
self.vars = {}
for name in var_names:
self.vars[name] = get_string_var(self)
self._add_summary_labels(self, self.vars, 1)
#staticmethod
def _add_summary_labels(frame, vars, start_row):
current_row = start_row
for name in vars:
tk.Label(frame, text=name, anchor=tk.N+tk.W).grid(row=current_row, column=0, sticky=tk.N+tk.S+tk.W+tk.E)
text_label = tk.Label(frame, wraplength=200, textvariable=vars[name], anchor=tk.N+tk.W, justify=tk.LEFT)
text_label.grid(row=current_row, column=1, sticky=tk.W)
current_row += 1
def set_summary_fields(self, info):
for name in info:
if name in self.vars:
self.vars[name].set(info[name])
class OuterFrame(ttk.Frame):
def __init__(self, parent, **kwargs):
ttk.Frame.__init__(self, parent, **kwargs)
self.canvas = tk.Canvas(self)
scrollbar = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self.canvas.yview)
self.canvas.configure(yscrollcommand=scrollbar.set)
self.summary = SummaryFrame(self.canvas)
self.summary.pack(fill=tk.BOTH, expand=1)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
scrollbar.pack(side=tk.LEFT, fill=tk.Y, expand=1)
label_text = {}
for i in range(1, 20):
label_text['label_'+str(i)] = "information " * random.randint(1, 20)
self.set_fields(label_text)
def set_fields(self, info):
self.summary.set_summary_fields(info)
self.canvas.configure(scrollregion=(1, 1, self.summary.winfo_width(), self.summary.winfo_height()))
if __name__ == "__main__":
root = tk.Tk()
frame = OuterFrame(root)
frame.pack(fill=tk.BOTH, expand=1)
root.mainloop()
The scrollbar should change when the contents of the inner SummaryFrame expands, but doesn't. It remains grayed out and inoperable. What am I doing wrong?
Short answer: you are configuring the scrollregion to be one pixel by one pixel.
You are configuring the scrollregion based on the width and height of the self.summary, but you are doing this before the window has a chance to be mapped to the screen. The width and height, therefore, are both one.
You need to wait for the window to be drawn before computing the width and height of the window.

Categories

Resources