Reputation: 250
I am trying to put a labelframe next to a few other widgets but I cannot figure it out at all. Currently it displays underneath all the other widgets when I use the .pack() method. When I try to configure the position using .grid(column=x, row=y) then the program runs but hangs and no GUI comes up.
Here is the relevent code
def createWidgets(self):
# Create entrybox and align to grid
self.send_entry = tk.Entry(self)
self.send_entry.grid(row=0,column=0)
# Create button,allign to grid, get xml
self.change_sub = tk.Button(self,text='Change Subreddit',padx=5, pady=5, command=lambda :self.getXML(self.send_entry.get())).grid(row=0 , column=3)
# Create scrollbar on Y-Axis
self.lb_scrollY = tk.Scrollbar(self,orient=tk.VERTICAL)
# On grid next to Listbox(sticky means fill whole row
self.lb_scrollY.grid(row=1,column=4,sticky=tk.NS,rowspan=6)
# Create Listbox and get Y from scrollbar
self.thread_lb = tk.Listbox(self,yscrollcommand=self.lb_scrollY.set)
# Calls function whenever a new item is selected
self.thread_lb.bind('<<ListboxSelect>>',self.updateSelected)
# scrolly will change the view of listbox
self.lb_scrollY['command']=self.thread_lb.yview
self.thread_lb.grid(row=1,column=0,sticky=tk.NS+tk.EW,columnspan=4,rowspan=3)
self.threadFrame = tk.LabelFrame(main,text='Reddit')
self.threadLabelTitle = tk.Label(self.threadFrame,textvariable=self.threadTitle,wraplength=200).grid(row=1,column=2,sticky= tk.EW)
self.threadLabelAuth = tk.Label(self.threadFrame, textvariable=self.threadAuth,wraplength=200).grid(row=2,column=2,sticky = tk.EW)
self.threadLabelPub = tk.Label(self.threadFrame, textvariable=self.threadPub,wraplength=200).grid(row=3,column=2,sticky = tk.EW)
self.threadLabelArtLink = tk.Label(self.threadFrame, textvariable=self.threadArtLink,wraplength=200).grid(row=4,column=2,sticky = tk.EW)
self.threadLabelThreadLink = tk.Label(self.threadFrame, textvariable=self.threadLink,wraplength=200).grid(row=5,column=2,sticky = tk.EW)
self.threadImgLink = tk.Label(self.threadFrame, textvariable=self.threadImg,wraplength=200).grid(row=6,column=2,sticky = tk.EW)
# self.columnconfigure(2,minsize=200)
# self.rowconfigure(1,minsize=175)
self.threadFrame.pack()
self.QUIT = tk.Button(self, text="QUIT", fg="red", command=main.destroy,padx=5, pady=5).grid(row=7)
And here is the full program so you can run it if you like.
import xml.etree.ElementTree as ET
import webbrowser,time,urllib.request,re
import tkinter as tk
import urllib
main = tk.Tk()
class Application(tk.Frame):
def __init__(self, master=None):
self.threadTitle = tk.StringVar()
self.threadAuth = tk.StringVar()
self.threadPub = tk.StringVar()
self.threadArtLink = tk.StringVar()
self.threadLink = tk.StringVar()
self.threadImg = tk.StringVar()
self.threadArtLink.set('Click something to display thread info')
# Intializes tkinter gui framework
tk.Frame.__init__(self, master)
# Packs widgets needed
self.pack()
# Creates the widgets functions
self.createWidgets()
# Intializes the man rss.xml
self.initial()
# self.threadLabelArtLink = None
# self.threadLabelTitle = None
# self.threadLabelThreadLink = None
# self.threadLabelArtLink = None
# self.threadImgLink = None
def createWidgets(self):
# Create entrybox and align to grid
self.send_entry = tk.Entry(self)
self.send_entry.grid(row=0,column=0)
# Create button,allign to grid, get xml
self.change_sub = tk.Button(self,text='Change Subreddit',padx=5, pady=5, command=lambda :self.getXML(self.send_entry.get())).grid(row=0 , column=3)
# Create scrollbar on Y-Axis
self.lb_scrollY = tk.Scrollbar(self,orient=tk.VERTICAL)
# On grid next to Listbox(sticky means fill whole row
self.lb_scrollY.grid(row=1,column=4,sticky=tk.NS,rowspan=6)
# Create Listbox and get Y from scrollbar
self.thread_lb = tk.Listbox(self,yscrollcommand=self.lb_scrollY.set)
# Calls function whenever a new item is selected
self.thread_lb.bind('<<ListboxSelect>>',self.updateSelected)
# scrolly will change the view of listbox
self.lb_scrollY['command']=self.thread_lb.yview
self.thread_lb.grid(row=1,column=0,sticky=tk.NS+tk.EW,columnspan=4,rowspan=3)
self.threadFrame = tk.LabelFrame(main,text='Reddit')
self.threadLabelTitle = tk.Label(self.threadFrame,textvariable=self.threadTitle,wraplength=200).grid(row=1,column=2,sticky= tk.EW)
self.threadLabelAuth = tk.Label(self.threadFrame, textvariable=self.threadAuth,wraplength=200).grid(row=2,column=2,sticky = tk.EW)
self.threadLabelPub = tk.Label(self.threadFrame, textvariable=self.threadPub,wraplength=200).grid(row=3,column=2,sticky = tk.EW)
self.threadLabelArtLink = tk.Label(self.threadFrame, textvariable=self.threadArtLink,wraplength=200).grid(row=4,column=2,sticky = tk.EW)
self.threadLabelThreadLink = tk.Label(self.threadFrame, textvariable=self.threadLink,wraplength=200).grid(row=5,column=2,sticky = tk.EW)
self.threadImgLink = tk.Label(self.threadFrame, textvariable=self.threadImg,wraplength=200).grid(row=6,column=2,sticky = tk.EW)
# self.columnconfigure(2,minsize=200)
# self.rowconfigure(1,minsize=175)
self.threadFrame.pack()
self.QUIT = tk.Button(self, text="QUIT", fg="red", command=main.destroy,padx=5, pady=5).grid(row=7)
def updateSelected(self, event):
# getting selected listbox item
i=self.thread_lb.curselection()
# Returns tuple that must be split
x,y,z = re.split("\D+",str(i))
y=int(y)
print(self.threadTitleList[y])
print(self.threadPubDateList[y])
print(self.threadLinkList[y])
print(self.threadDescList[y])
self.threadTitle.set(self.threadTitleList[y])
self.threadAuth.set('Will have poster here')
self.threadPub.set(self.threadPubDateList[y])
self.threadArtLink.set(self.threadLinkList[y])
self.threadLink.set(self.threadDescList[y])
self.threadImg.set('Will put image here')
# # threadTitle = self.threadTitleList[y]
# print(self.threadLabelTitle["text"])
# # self.threadLabelTitle['text']=threadTitle
# self.threadLabelAutPub['text']=self.threadPubDateList[y]
# self.threadImgLink['text']=self.threadLinkList[y]
# self.threadLabelThreadLink['text']=self.threadDescList[y]
# main.update()
def descStripper(self,desc):
# Intialize values
l1,l2,l2Start = 0,0,0
t1,t2,t2start = 0,0,0
link = ""
thread = ""
# Where to start looking for each in description element
l1=int(desc.find('<br/> <a href="'))
t1=int(desc.find('</a> <a href="'))
# If both of the tags are found then continue
if l1 != -1 and t1 != -1:
# Start looking for end of quotes 16 characters from beginning of tag
l2Start = l1+16
l2=int(desc.find('"',l2Start))
# Link is created from what is in the quotes
link = desc[l1+15:l2]
# Same as above but to find thread link
t2start = t1+15
t2=int(desc.find('"',t2start))
thread = desc[t1+14:t2]
return link,thread
else:
# If it can't find one it will return an error
link = "Couldn't find the stuff :("
thread = "Couldn't find the thread link :("
return link, thread
def lbPopulator(self,title,pub,link):
# Delete old entries from listbox
self.thread_lb.delete(0,tk.END)
# Iterate through all the items and append them to the listbox
for item in title:
self.thread_lb.insert(tk.END,item)
def getXmlData(self):
# Intialize lists
self.threadPubDateList = []
self.threadTitleList = []
self.threadLinkList = []
self.threadDescList = []
self.threadThumbNailList = []
# Use the downloaded rss.xml for XML parsing
tree=ET.parse('rss.xml')
# define root as the base of the XML parsing tree
root=tree.getroot()
for channel in root:
# Iterate through all the channels
for SubChannel in channel:
# Iterate through all the items in the channel
if SubChannel.tag == 'item':
# If the SubChannel is called item then search for the items below
for threadInfo in SubChannel:
# iterate through all the items in the 'item'
if threadInfo.tag == 'title':
# append the tag from the title to the list
self.threadTitleList.append(threadInfo.text)
if threadInfo.tag == 'pubDate':
# Append the pubdate info to the list but remove excess characters
self.threadPubDateList.append(threadInfo.text[:-6])
if threadInfo.tag == 'description':
# Pass all the information from the description to the stripper to get the useful
# information and links
link,thread = self.descStripper(threadInfo.text)
self.threadLinkList.append(link)
self.threadDescList.append(thread)
# Populate the listbox with the newly generated lists
self.lbPopulator(self.threadTitleList,self.threadPubDateList,self.threadLinkList)
def getXML(self,subreddit):
try:
# Try to download the xml file using the user input subreddit
url = 'http://www.reddit.com'+subreddit+'.rss'
source = urllib.request.urlretrieve(url,'rss.xml')
self.getXmlData()
except urllib.error.HTTPError as err:
# Error caused by reddit API limiting connections
print('Too many requests-Try again')
def initial(self):
try:
# Same as above but downloads the front page
source = urllib.request.urlretrieve('http://www.reddit.com/.rss','rss.xml')
self.getXmlData()
except urllib.error.HTTPError as err:
print('Too many requests-Trying again 3')
# If error occurs program waits 3 seconds and then restarts
time.sleep(3)
self.__init__()
# main.geometry("350x400")
app = Application(master=main)
# Begins the applications GUI loop
app.mainloop()
Upvotes: 1
Views: 5533
Reputation: 386362
There isn't enough information in your question to know exactly what you want, so I'm guessing you want the labelframe with the text "Reddit" to be to the right of all of the other widgets. is that right?
If so, the problem is because of these two lines:
self.pack()
...
self.threadFrame.pack()
Without any options, pack places things at the top of any empty space in the containing widget. So, self
fills the top part of the window, leaving empty space below it. self.threadFrame
is placed at the top of the empty space, which is why these two widgets appear stacked.
If you tried switching just the second statement to use grid, the GUI will hang just like you report, because you can't use both grid and pack when the two widgets have the same parent.
To get them side-by-side, a simple fix is to be explicit when you are packing:
self.pack(side="left", fill="both", expand=False)
...
self.threadFrame.pack(side="right", fill="both", expand=True)
My use of expand will cause the threadFrame
to grow and shrink as you resize the window. I don't know if that's the behavior you want or not. You can play around with the expand options to see how they affect the window when you resize it.
You could just as easily use grid to get the exact same result, as long as you use grid for both:
self.grid(row=0, column=0, sticky="nsew")
...
self.threadFrame.grid(row=0, column=1, stick="nsew")
...
main.grid_columnconfigure(1, weight=1)
main.grid_rowconfigure(0, weight=1)
As you can see, with grid you have to do a little extra work to get the same resize behavior. This is why it's good to use both grid and pack in different parts of your application: pack is great when you want to put a couple of widgets side-by-side or in a vertical stack. grid is great when you have a more complex layout that can be specified in rows and columns. As I said earlier, however, just be sure you don't mix grid and pack for two or more widgets with the same parent.
Upvotes: 0
Reputation: 4695
Try this code and use place
method instead of pack
:
from tkinter import *
root=Tk()
mylabel = Label(root,text="This is my lablel.")
mylabel.place(x=30,y=30,height=20,width=100)
Upvotes: 3