How can I get the scrollbar to control the text region? - python

When I try to scroll down the Text object the scroll bar sort of bounces back instead of actually scrolling
memo_content = Text(edit_window,height = 4, width = 40)
memo_content.grid(row = 1, column = 0, sticky = E+W)
scroll_memo_content=Scrollbar(edit_window)
scroll_memo_content.grid(row = 1, column = 2, sticky = N+S)
memo_content.configure(yscrollcommand=scroll_memo_content.set)
The scroll bar has no control over the text field and when I drag it it snaps back to where it was meaning that the only way to scroll is by holding the up/down arrow and the cursor moving

from tkinter import *
root = Tk()
text = Text(root, height=25, width=50)
sb = Scrollbar(root)
text.configure(yscrollcommand=sb.set)
sb.configure(command=text.yview) # <- You missed this
sb.pack(side=RIGHT, fill=Y)
text.pack()
You missed configuring the actual scrollbar.

Configuring a scrollbar requires two steps:
You must configure the scrollbar to know which widget it is controlling. This is typically done by setting the command attribute of the scrollbar to be the yview command of a scrollable widget.
You must configure the window to be scrolled to know scrollbar needs to be updated when its position changes. This is typically done by setting the yscrollcommand or xscrollcomand of the widget to the set method of an appropriate scrollbar.
You are forgetting to do the first step.
Make the following change:
scroll_memo_content=Scrollbar(edit_window, command=memo_content.yview)

Related

How to use tkinter.place()?

I want to have an entry and I want to have a listbox of fixed size under it which is fixed. and I want to have another listbox of dynamic height. That will appear and disappear in time and also change in size. I want the second listbox (which is actually a dropdown) to be shown over the other listbox which I want it to be fixed. My code for changing the size etc is correct and works perfectly with pack() but then it will move the other listbox up and down as it's size changes. And when I change pack() to place(...) it's not shown at all anymore.
Here is my code:
from tkinter import *
root = Tk()
entry = Entry(
root,
width=50
)
frame = Frame(
root,
height=10,
width=50,
background="#caeaa9"
)
dropdown = Listbox(
frame,
background="#11FF11",
height=5,
width=50
)
listbox = Listbox(
frame,
background="#FF1111",
height=10,
width=50
)
entry.pack()
listbox.pack()
dropdown.place()
frame.pack()
mainloop()
But the dropdown does not appear when I run it. What am I missing?
By the way, I want the top border of the dropdown to be exactly on the top border of the listbox and both of them right under the entry.
I highly recommend you don't use the place geometry manager. Ever. If you want to create larger or more complex interfaces, having to place widgets is terrible. I suggest using grid instead:
import tkinter as tk
# Avoid wildcard imports!
root = tk.Tk()
entry = tk.Entry(
root,
width=50
)
frame = tk.Frame(
root,
background="#caeaa9"
)
listbox = tk.Listbox(
frame,
background="#FF1111",
height=10,
width=50
)
dropdown = tk.Listbox(
frame,
background="#11FF11",
height=5,
width=50
)
entry.pack()
listbox.grid(column=0, row=0, sticky=N)
dropdown.grid(column=0, row=0, sticky=N)
frame.pack()
root.mainloop()
You have two problems:
you aren't telling place where to place the widget
the stacking order (z-index) of the dropdown is behind (lower) than the other listbox, so it will appear under the listbox.
give explicit coordinates to place
I'm not entirely clear where you want the dropdown to appear. Since you say "over" the other listbox, I suggest you use the in_ parameter to make the coordinates relative to the other listbox, and then use other place arguments to place it exactly where you want.
Example:
In the following example I make the dropdown exactly the same width, but half the height of the other listbox.
dropdown.place(in_=listbox, x=0, y=0, anchor="nw", relwidth=1.0, relheight=.5)
Fix the stacking order
All widgets have a stacking order. Some people call this a z-index. By default the order is the order in which the widgets are created. Since you create the dropdown before the other listbox, the other listbox has a higher stacking order. That means that it will appear above the dropdown.
A simple solution is to create the dropdown last. If you don't want to do that, you can call the lift method of the widget to raise its stacking order. The argument for lift is the name of a widget you want to be above.
Example:
dropdown.lift(listbox)
Tkinter place is one of the three geometry manager in tkinter. it allows us to specify the position of tkinter widgets in terms of x and y coordinate.
Here is the code example :
from tkinter import *
window = Tk()
Button(window,text = "Click Me").place(x = 50,y = 50)
window.mainloop()
For more info kindly refer this tutorial on tkinter place

Why doesn't my scroll box appear at the top of the scroll bar?

I want my scrollbox to be at the top of the scrollbar. But it shows up in the middle of the scrollbar when I run the program as shown below:
https://i.stack.imgur.com/EfxMJ.png
I want my scrollbar to be at the top so that the first widget in the window is displayed. But for some reason I can't figure out, the scroll box appears in the middle.
Here is my code. What must I do to fix this bug?
from tkinter import *
root = Tk()
root.geometry("640x480")
def scrollFunc(event):
mycanvas.config(scrollregion=mycanvas.bbox('all'))
mycanvas=Canvas(root)
mycanvas.pack(fill=BOTH, expand=True)
myframe = Frame(mycanvas)
mycanvas.create_window((0,0), window=myframe)
myscrollbar = Scrollbar(mycanvas, command=mycanvas.yview)
myscrollbar.pack(side=RIGHT, fill=Y)
mycanvas.config(yscrollcommand=myscrollbar.set)
for x in range(1, 101):
Label(myframe, text="Label: "+str(x)).pack()
mycanvas.bind("<Configure>", scrollFunc)
root.mainloop()
You give a coordinate of 0,0 for the window, but you do not define an anchor point. The default anchor is "c" (or "center"). That means that the middle of your frame will be at 0,0.
The simplest solution is to set the anchor point to be "nw" (which stands for "northwest").
mycanvas.create_window(..., anchor="nw")

Scrollbar in Tkinter inside Text widget

I have problem with set in Scrollbar inside Text widget in Tkinter. I know, that it's preferable to use grid to locate widgets but I want to set my widget in absolute location (x,y - red dot on GUI picture) with specified height and width.
My code:
from Tkinter import *
from ttk import *
class NotebookDemo(Frame):
def __init__(self):
Frame.__init__(self)
self.pack(expand=1, fill=BOTH)
self.master.title('Sample')
self.master.geometry("650x550+100+50")
self._initUI()
def _initUI(self):
self._createPanel()
def _createPanel(self):
# create frame inside top level frame
panel = Frame(self)
panel.pack(side=TOP, fill=BOTH, expand=1)
# create the notebook
nb = Notebook(panel)
nb.pack(fill=BOTH, expand=1, padx=2, pady=3)
self._FirstTab(nb)
def _FirstTab(self, nb):
# frame to hold content
frame = Frame(nb)
#textbox
txtOutput = Text(frame, wrap = NONE, height = 17, width = 70)
txtOutput.place(x=10, y=75)
#button
btnStart = Button(frame, text = 'Start', underline=0)
btnStart.place(x=220, y=380)
#scrollbar
#vscroll = Scrollbar(frame, orient=VERTICAL, command=txtOutput.yview)
#txtOutput['yscroll'] = vscroll.set
#vscroll.pack(side=RIGHT, fill=Y)
#txtOutput.pack(fill=BOTH, expand=Y)
#add to notebook (underline = index for short-cut character)
nb.add(frame, text='TAB 1', underline=0, padding=2)
if __name__ == '__main__':
app = NotebookDemo()
app.mainloop()
If I uncomment this part of code (set Scrollbar):
vscroll = Scrollbar(frame, orient=VERTICAL, command=txtOutput.yview)
txtOutput['yscroll'] = vscroll.set
vscroll.pack(side=RIGHT, fill=Y)
My Scrollbar is located inside all window, not inside Text box:
But of course I want to have the Scrollbar inside the Text box widget (black border).
If I use pack function to textbox:
txtOutput.pack(fill=BOTH, expand=Y)
text widget fill in the whole window...:
I really don't know how fix this problem.
Any help will be appreciated.
Thank you!
EDIT:
Of course I can use place method with Scrollbar too, but I can't change length of them, because it hasn't attribute length.
vscroll.place(x=573, y=75)
While I rarely recommend place, it is quite powerful when you take advantage of the configuration options. For example, you can use in_ to specify a widget that this widget is to be placed relative to. You can use relx to specify a relative x coordinate, and you can use relheight to specify a height.
In your case you can try something like this:
vscroll.place(in_=txtOutput, relx=1.0, relheight=1.0, bordermode="outside")
If you want the illusion that the scrollbar is embedded inside the text widget as is (or used to be) common on some platforms, I recommend placing the text widget and scrollbar in a frame.You can use pack to put the widgets in the frame, and continue to use place to place the combination anywhere you want.
For example:
txtFrame = Frame(frame, borderwidth=1, relief="sunken")
txtOutput = Text(txtFrame, wrap = NONE, height = 17, width = 70, borderwidth=0)
vscroll = Scrollbar(txtFrame, orient=VERTICAL, command=txtOutput.yview)
txtOutput['yscroll'] = vscroll.set
vscroll.pack(side="right", fill="y")
txtOutput.pack(side="left", fill="both", expand=True)
txtFrame.place(x=10, y=75)
Different geometry managers like place and pack don't mix so well. I see four options for you:
Use a parent frame
Create a new Frame that you place at the exact same position as you did with the text box. In this frame, you can use another geometry manager (I'd prefer pack) to make the layout appear as you want.
Use ScrolledText
Use the ScrolledText Tkinter module to have the solution above in a premade form. Note that this widget doesn't use ttk so the scrollbar style does not really adapt to the OS' look. Just use import ScrolledText and replace the Text creation in your code with ScrolledText.ScrolledText(...).
Use place for the scrollbar
If you are using place for the text widget, use place for the scrollbar too. place has options that allow you to place a widget relative to another widget both in location and size (ie: you can place the scrollbar along the right edge of the text widget, and cause it to be exactly as tall as the text widget). See Bryan's answer.
Don't use place
Simple as that. Use grid or pack instead, unless you really need to use place.

Tkinter getting wrong order of widgets

I guess this is a very beginners question. I am using Tkinter to get a GUI in Python. But with the code below the frames a displayed in the wrong order.
# ********************************************
# * Frame for toolbar *
# ********************************************
# Main frame
self.fr_toolbar = Frame(self)
self.fr_toolbar.pack(fill=X)
self.fr_toolbar.grid_columnconfigure(0, weight=1)
# Align toolbars left
self.fr_left = Frame(self.fr_toolbar, bd=1, relief = RAISED)
self.fr_left.grid(row=0, column=0, sticky=W+E)
# Align toolbars right
self.fr_right = Frame(self.fr_toolbar, bd = 1, relief = RAISED)
self.fr_right.grid(row=0, column=1, sticky=E)
# *********************************************
# * Communication toolbar *
# *********************************************
# Create Frame for comm toolbar
self.tb_comm = Frame(self.fr_left)
self.tb_comm.grid(row=0, column=0)
# Several buttons in here...
# ********************************************
# * Scope toolbar *
# ********************************************
# Seperator line
self.fr_split = Frame(self.fr_left, bd=1, relief=SUNKEN, width=2, height=28)
self.fr_split.grid(row=0, column=1)
# Create frame for scope toolbar
self.tb_scope = Frame(self.fr_left)
self.tb_scope.grid(row=0, column=2)
# Several buttons in here
# *********************************************
# * Exit tool bar *
# *********************************************
# Create frame for exit tool bar
self.tb_exit = Frame(self.fr_right)
self.tb_exit.pack()
# Exit button here
The idea was to have the exit toolbar on the right and all other toolbar on the left. So far it works. All toolbar frames inside fr_left are on the lefthand side. tb_exit in fr_right is on the right.
But inside fr_left I get the wrong order to show my toolbars. First I get tb_comm, then tb_scope and as third fr_split even if it is set between the other two toolbars. I tried it with pack() and with grid().
Can anybody explain what I am doing wrong? I thought at least with pack() I will get the widgets in the order I define them. And the same with grid(). I ordered them with column, but Python is ignoring it.
Florian
Ok, you are allowed to hit me! I set the buttons for tb_scope in tb_comm mistakenly. So all toolbars were on the right place but only the buttons were not. As I wrote, beginners fault...
Sorry to steal your time!

Creating Frame inside a Canvas

I am trying to make a frame scrollable, and the only way I found to do this is making a scrollable canvas and adding a frame to it. This would work fine, if it worked for me.
I am able to create a scrollable canvas that works fine, but I can't seem to properly add a frame inside of it:
self.title = Label(root, text="Brnr", font=("Helvetica", 50), anchor = W, pady = 40, padx = 50)
self.title.pack (anchor = NW)
#creates title widget for title
self.frame = Frame(screen, bd =1)
self.frame.pack(fill = BOTH)
#Creates frame widget under which all other widgets will be kept
self.canvas = Canvas(self.frame, bd=1,scrollregion=(0,0, 1000, 1000), height = 600)
#creates canvas so that screen can be scrollable
self.scrollbar = Scrollbar(self.frame, command=self.canvas.yview)
#creates scrollbar
self.canvas.config(yscrollcommand=self.scrollbar.set)
#connects the scrollbar to the canvas
self.scrollbar.pack(side=RIGHT, fill=Y)
self.canvas.pack(expand=YES, fill=BOTH)
#packs the scrollbar and canvas so that they fill the remainder of the screen
self.frameC = Frame(bg = "red")
self.canvas.create_window(0,0, anchor = NW, window = self.frameC, width = 200, height = 200)
#creates window on the scrollable area to add other widgets
self.frameC.pack()
self.groupRec = LabelFrame(self.frameC, text ="Recommendations:", font=("Helvetica", 20))
self.groupRec.pack()
self.signupButton = Button(self.groupRec, text="Sign Up", width=10)
self.signupButton.pack(side=RIGHT)
#creates button to submit login
This gives me a scrollable, but empty, canvas, with none of the labelframe/button appearing.
By default, when you add a window to a canvas, the center of the window will be at the coordinates you give. Thus, the center of your frame will be at 0,0 which is the upper-left corner of the canvas. You can't see the widgets because they are outside the borders of the canvas.
The solution is to include anchor="nw" in the call to create_window, which will place the upper-left corner of your frame in the upper left corner of your canvas.
Don't forget to set the scroll region of the canvas to match the size of your frame. The easiest way to do that is with the command self.canvas.config(scrollregion=self.canvas.bbox("all")). You'll probably also need to add a binding to <Configure> on the canvas so that you can resize the inner frame when the user resizes the window. That's not always necessary, it depends a bit on exactly what you are trying to accomplish.
Here's a pro tip: to debug problems like this it's really helpful to temporarily give your frame and canvas different colors to more easily visualize what is happening.
Don't re-invent the wheel. Install Pmw (Python meta-widgets), assuming you are using Tkinter, http://pmw.sourceforge.net/ and use Pmw.ScrolledFrame.

Categories