12

I have a tkinter program:

import urllib.request
from tkinter import *


root = Tk()
root.iconbitmap(default='icon.ico')
root.wm_title('Got Skills\' Skill Tracker')
frame = Frame(width="500",height="500")
frame.pack()


def show():
  name = "zezima"
  page = urllib.request.urlopen('http://hiscore.runescape.com/index_lite.ws?player=' + name)
  page = page.readlines()

  skills = []
  for line in page:
    skills.append([line.decode("utf-8").replace("\n", "").split(",")])

  skills = skills[0:25]

  for item in skills:
    toPrint = item[0][0],"-",item[0][1],"-",item[0][1],"\n"
    w = Message(frame, text=toPrint)
    w.pack()


menu = Menu(root)
root.config(menu=menu)

filemenu = Menu(menu)
menu.add_cascade(label="Commands", menu=filemenu)
filemenu.add_command(label="Show Skills", command=show)


root.mainloop()

When I run the above script, it shows this (which is good):

alt text http://img708.imageshack.us/img708/8821/tkinter1.png

When I click Commands > Show Skills, it turns into this. (Linked because it's tall.) It shows the right thing, but...I can imagine you see the problem.

Two questions:

-How do I add a scrollbar to the frame, and keep the frame a fixed size? (Ideally, keep the size of the first image, add the output of show(), add a scrollbar to the first image of the program.) -With the following code:

  for item in skills:
    toPrint = item[0][0],"-",item[0][1],"-",item[0][2],"\n"
    w = Message(frame, text=toPrint)
    w.pack()

Is that the best way to output what I'm outputting? The list (skills) looks like [[1,2,3],[4,5,6]..], and I want to display 1-2-3 on a line, 4 - 5 - 6 on a line, etc.

But, I don't want that extra line in between them like there is now, and I was wondering if how I did it is the best way to go about doing it.

Andrew
  • 12,172
  • 16
  • 46
  • 61

2 Answers2

12

To add the scroll bars, use tkinter.tix.ScrolledWindow.

To remove extra space drop the extra "\n" and display a string, not a tuple. Here is the complete code:

import urllib.request
from tkinter import *
from tkinter.tix import *

root = Tk()
root.iconbitmap(default='icon.ico')
root.wm_title('Got Skills\' Skill Tracker')
frame = Frame(width="500",height="500")
frame.pack()
swin = ScrolledWindow(frame, width=500, height=500)
swin.pack()
win = swin.window


def show():
  name = "zezima"
  page = urllib.request.urlopen('http://hiscore.runescape.com/index_lite.ws?player=' + name)
  page = page.readlines()

  skills = []
  for line in page:
    skills.append([line.decode("utf-8").replace("\n", "").split(",")])

  skills = skills[0:25]

  for item in skills:
    toPrint = item[0][0],"-",item[0][1],"-",item[0][1]
    w = Message(win, text=' '.join(toPrint), width=500)
    w.pack()


menu = Menu(root)
root.config(menu=menu)

filemenu = Menu(menu)
menu.add_cascade(label="Commands", menu=filemenu)
filemenu.add_command(label="Show Skills", command=show)


root.mainloop()
abbot
  • 27,408
  • 6
  • 54
  • 57
  • I thought I needed that `\n` to keep them from all showing up on the same line. :P. Anyways, thank you very much. It works nicely. :) – Andrew Dec 04 '09 at 12:24
  • Why did you use strings for the width and height for the frame? – Gabe Morris Jan 02 '21 at 20:04
  • @GabeMorris don't remember to be honest :) Most likely I kept the authors original version there. But keep in mind that this is tk bindings for Python, so many parameters are variant and subject to interpretation in context. Dimensions specifically can be ints or strings, optionally with unit suffixes ([ref](https://tkdocs.com/shipman/dimensions.html) and [here](https://tkdocs.com/shipman/frame.html)) – abbot Jan 05 '21 at 13:38
  • Not being used to Tkinter, the additional stuff obscures the exact solution – Denis G. Labrecque Mar 19 '21 at 16:25
  • I get `_tkinter.TclError: invalid command name "tixScrolledWindow"` – TheTechRobo the Nerd Jun 28 '21 at 20:25
  • @TheTechRobo36414519 I got the same error, when implementing in my own code. Have you found any solution for this? – abc Jul 13 '21 at 10:51
  • @abc - I ended up using a module, it seems my system's tkinter build might not have tix (which makes sense, it doesn't have a maintainer, it's been deprecated for a while). So here's the module I used - it's my fork because the original one got archived so issues and PRs can't be submitted. (Edit: I'm dumb, here it is: https://github.com/TheTechRobo/tkScrolledFrame) – TheTechRobo the Nerd Jul 13 '21 at 13:39
1

Here's a class for scrolling frames. Just pass the window object as traditional tkinter style and use obj.frame as window for new widgets.

class ScrollableFrame:
    """
    # How to use class
    from tkinter import *
    obj = ScrollableFrame(master,height=300 # Total required height of canvas,width=400 # Total width of master)
    objframe = obj.frame
    # use objframe as the main window to make widget
    """
    def __init__ (self,master,width,height,mousescroll=0):
        self.mousescroll = mousescroll
        self.master = master
        self.height = height
        self.width = width
        self.main_frame = Frame(self.master)
        self.main_frame.pack(fill=BOTH,expand=1)

        self.scrollbar = Scrollbar(self.main_frame, orient=VERTICAL)
        self.scrollbar.pack(side=RIGHT,fill=Y)

        self.canvas = Canvas(self.main_frame,yscrollcommand=self.scrollbar.set)
        self.canvas.pack(expand=True,fill=BOTH)

        self.scrollbar.config(command=self.canvas.yview)

        self.canvas.bind('<Configure>', lambda e: self.canvas.configure(scrollregion = self.canvas.bbox("all")))

        self.frame = Frame(self.canvas,width=self.width,height=self.height)
        self.frame.pack(expand=True,fill=BOTH)
        self.canvas.create_window((0,0), window=self.frame, anchor="nw")

        self.frame.bind("<Enter>", self.entered)
        self.frame.bind("<Leave>", self.left)

    def _on_mouse_wheel(self,event):
        self.canvas.yview_scroll(-1 * int((event.delta / 120)), "units")

    def entered(self,event):
        if self.mousescroll:
            self.canvas.bind_all("<MouseWheel>", self._on_mouse_wheel)
        
    def left(self,event):
        if self.mousescroll:
            self.canvas.unbind_all("<MouseWheel>")