ogeretal
ogeretal

Reputation: 521

Rows in tkinter grid

I want to use the grid method in tkinter to show some data I have in a database. When I define the __init__ method I have to set up a specific number of rows, which are similar to each other. But, as the value of the rows will be taken from a database, I can not define precisely the number of rows I will need.

Until now, my only solution is too set a maximum number of rows in the __init__ method, which I can control. But this solution does not show all the information on the screen. Any help will be welcome.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------------------------------

__title__= 'Control'
__date__ = '02/07/2017'
__version__ = '0.0.1'

from tkinter import *
from tkinter import ttk

class Panel():
    def __init__(self):

        self.root = Tk()
        self.root. title('Control V' + __version__)
        self.root.geometry('500x400')
        self.root.configure(bg='beige')
        self.root.minsize(400,300)

        self.window = ttk.Frame(self.root, borderwidth=2, relief="raised",
                                padding=(10,10) )
        self.window.grid(column=0, row=0)
        self.window.option_add("*Font", "Helvetica 12")

        # Row 0
        self.r0c0 = ttk.Label(self.window, text="Header", padding=(1,1))
        self.r0c0.grid(row=0, column=0, columnspan=3)

        # Row 1
        counter = 1
        # From db : o = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
        o = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}

        self.r1c0 = ttk.Label(self.window, text=o['Name'], padding=(1,1))
        self.r1c0.grid(row=1, column=0)
        self.r1c1 = ttk.Label(self.window, text=o['Age'], padding=(1,1))
        self.r1c1.grid(row=1, column=1)
        self.r1c2 = ttk.Label(self.window, text=o['Class'], padding=(1,1))
        self.r1c2.grid(row=1, column=2)

        # Row 2, Row 3, ... All are similar to Row 1
        counter = 2
        # From db : o = {'Name': 'Ezra', 'Age': 5, 'Class': 'First'}
        o = {'Name': 'Ezra', 'Age': 5, 'Class': 'First'}

        self.r2c0 = ttk.Label(self.window, text=o['Name'], padding=(1,1))
        self.r2c0.grid(row=2, column=0)
        self.r2c1 = ttk.Label(self.window, text=o['Age'], padding=(1,1))
        self.r2c1.grid(row=2, column=1)
        self.r2c2 = ttk.Label(self.window, text=o['Class'], padding=(1,1))
        self.r2c2.grid(row=2, column=2)

        # Row 3, Row 4, ... All are similar to Row 1 & 2.
        # Can I make a loop where n is unknown at definition

        # All set
        self.root.mainloop()

def main():
    my_panel = Panel()
    return 0

if __name__ == '__main__':
    main()

Upvotes: 2

Views: 2307

Answers (2)

martineau
martineau

Reputation: 123393

Displaying a variable number of things in a tkinter GUI is usually handled with Scrollbar which can be attached to Listbox, Canvas, Text, or Entry widgets. In this case a Listbox seems appropriate so here's an example of using it:

__title__= 'Control'
__date__ = '02/07/2017'
__version__ = '0.0.1'

from itertools import cycle
from tkinter import *
from tkinter import ttk

# Some test data
db = [
    {'Name': 'Zara', 'Age': 7, 'Class': 'First'},
    {'Name': 'Ezra', 'Age': 5, 'Class': 'First'},
    {'Name': 'Bob', 'Age': 8, 'Class': 'Third'},
    {'Name': 'Mary', 'Age': 6, 'Class': 'Second'},
]

class Panel():
    def __init__(self):
        self.root = Tk()
        self.root. title('Control V' + __version__)
        self.root.geometry('500x400')
        self.root.configure(bg='beige')
        self.root.minsize(400,300)

        self.window = ttk.Frame(self.root, borderwidth=2, relief="raised",
                                padding=(10,10))
        self.window.grid(column=0, row=0)
        self.window.option_add("*Font", "Helvetica 12")

        self.listbox = Listbox(self.window)
        self.listbox.grid(row=0, column=0, sticky=N+E+S+W)

        self.scrollbar = Scrollbar(self.window)
        self.scrollbar.grid(row=0, column=1, sticky=N+S)

        self.listbox.config(yscrollcommand=self.scrollbar.set)
        self.scrollbar.config(command=self.listbox.yview)

        # Row 0
        self.listbox.insert(END, "Header")

        # Simulate getting many rows from database by repeating them
        endless_data = cycle(db)
        for i in range(20):
            row = next(endless_data)
            line = '{Name:5} {Age:2} {Class}'.format(**row)
            self.listbox.insert(END, line)

        # All set
        self.root.mainloop()

def main():
    my_panel = Panel()

if __name__ == '__main__':
    main()

This is what it looks like:

screenshot of listbox

Upvotes: 1

PM 2Ring
PM 2Ring

Reputation: 55469

The built-in enumerate function lets you loop over a sequence and get both the sequence item and a sequential index number. By default, the indices start at zero, but you can supply an alternate starting index.

To make the code easier to read, I've put the Label creation and packing code into a separate method.

import tkinter as tk
from tkinter import ttk 

class Panel:
    def __init__(self, field_names, database):
        self.root = tk.Tk()

        self.window = ttk.Frame(self.root, borderwidth=2, 
            relief="raised", padding=(10,10))
        self.window.grid()

        # Create the header row
        for col, field_name in enumerate(field_names):
            self.add_field(field_name, 0, col, background='#ccf')

        # Create the data rows
        for row, record in enumerate(database, 1):
            for col, field_name in enumerate(field_names):
                self.add_field(record[field_name], row, col)

        self.root.mainloop()

    def add_field(self, value, row, col, background=''):
        label = ttk.Label(self.window, text=value, background=background,
            borderwidth=1, relief="raised", padding=(1,1))
        label.grid(row=row, column=col, sticky='ew')


def main():
    field_names = ('Name', 'Age', 'Class')

    database = [
        {'Name': 'Zara', 'Age': 7, 'Class': 'First'},
        {'Name': 'Ezra', 'Age': 5, 'Class': 'First'},
        {'Name': 'Amy', 'Age': 6, 'Class': 'Second'},
        {'Name': 'David', 'Age': 10, 'Class': 'Third'},
    ]

    my_panel = Panel(field_names, database)

if __name__ == '__main__':
    main()

Upvotes: 1

Related Questions