Python Forum
[Tkinter] How display matrix on canvas
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] How display matrix on canvas
#1
Hi All,

I am having troubles with Tkinter and tables.
Tkinter has limited possibilities for creating tables, so I create one with this ' program'.

Now I want the output to show on a canvas on a certain place, let's say x-10, y-20.

Can someone please explain how this must be done?
I have some experience with Tkinter but I don't know how to place this matrix.

Thank you.
Thanks in advance,
Best regards,
Jan van Eeden
Noordwijk
The Netherlands.

# Python program to create a table 

from tkinter import *



a=10

class Table: 
      	
	def __init__(self,root): 
		
		# code for creating table 
		for i in range(total_rows): 
			for j in range(total_columns): 
				
				self.e = Entry(root, width=16, fg='blue', 
							font=('Arial',10,'bold')) 
				
				self.e.grid(row=i, column=j) 
				self.e.insert(END, lst[i][j])
				

# take the data 
lst = [('Temp.Strand',a), 
	('Temp. Kamer',18), 
	('Temp. Keuken',20), 
        ('Temp. Zee',20),
       ('Luchtdruk',1020.520),
       ('Luchtdruk Max',1020.520),
       ('Datum','22-01-2020 10:30'),
       ('Luchtdruk Min',1020.520),
       ('Datum','22-01-2020 10:30'),]

# find total number of rows and 
# columns in list 
total_rows = len(lst) 
total_columns = len(lst[0]) 

# create root window S

root = Tk()
#root.config(cursor="none")


##root.attributes("-fullscreen",True)

root.configure(bg='black')


t = Table(root) 
root.mainloop()
Larz60+ write Mar-14-2021, 09:52 AM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.

Fixed for you this time. Please use bbcode tags on future posts.
Reply
#2
Try giving total_rows and total_columns to the Table class as arguments. This code works as expected for me.

# Python program to create a table

from tkinter import *

a=10

class Table:
	def __init__(self,root, total_rows, total_columns):

		# code for creating table
		for i in range(total_rows):
			for j in range(total_columns):

				self.e = Entry(root, width=16, fg='blue',
				font=('Arial',10,'bold'))

				self.e.grid(row=i, column=j)
				self.e.insert(END, lst[i][j])

# take the data
lst = [('Temp.Strand',a),
('Temp. Kamer',18),
('Temp. Keuken',20),
('Temp. Zee',20),
('Luchtdruk',1020.520),
('Luchtdruk Max',1020.520),
('Datum','22-01-2020 10:30'),
('Luchtdruk Min',1020.520),
('Datum','22-01-2020 10:30'),]

# find total number of rows and
# columns in list
total_rows = len(lst)
total_columns = len(lst[0])

# create root window S

root = Tk()

##root.attributes("-fullscreen",True)
root.configure(bg='black')

t = Table(root, total_rows, total_columns)
root.mainloop()
Reply
#3
Your table elements should be children of a frame instead of root. Then you can place the frame where you want in the root window.
Reply
#4
(Mar-13-2021, 02:25 PM)deanhystad Wrote: Your table elements should be children of a frame instead of root. Then you can place the frame where you want in the root window.

Thank you for your advice, can you please show me an example of how to make this?
That would be great and really appreciated
Reply
#5
In this example I made Table a subclass of Frame. The Entry widgets are children of the Table, and the Table is a child of the root window. I used tkinter variables to make it easier to get and set the Entry text to make it easier for the program to modify the table. Use Table.cell to get the entry widgets and Table.value to get the StringVars.

For fun I added __setvalue__() which is called when you execute a command like table['bg'] = 'black'. This lets me catch some setting attributes and applying those attributes to the Entry widgets as well as the frame.

import tkinter as tk
 
class Table(tk.Frame):
    """2D matrix of Entry widgets"""
    def __init__(self, parent, rows=0, columns=0, width=16, data=None):
        super().__init__(parent)
 
        if data is not None:
            rows = len(data)
            columns = len(data[0])
        self.rows = rows
        self.columns = columns
        self.cells = []
        self.values = []
        for row in range(rows): 
            for col in range(columns):
                var = tk.StringVar()
                cell = tk.Entry(self, width=width, textvariable=var)
                cell.grid(row=row, column=col)
                self.cells.append(cell)
                self.values.append(var)
                if data:
                    var.set(data[row][col])
 
    def __setitem__(self, name, value):
        """For setting attributes using instance['attribute'] = value"""
        if name in ('bg'):
            for cell in self.cells:
                cell[name] = value
            super().__setitem__(name, value)
        elif name in ('font', 'fg'):
            for cell in self.cells:
                cell[name] = value
        else:
            super().__setitem__(name, value)

    def value(self, row=None, column=None):
        """Get StringVar or group of StringVars"""
        if row is None:
            return [self.value(row, column) for row in range(self.rows)]
        elif column is None:
            return [self.value(row, column) for column in range(self.columns)]
        return self.values[row * self.columns + column]
 
    def cell(self, row=None, column=None):
        """Get an Entry widget or group of Entry widgets"""
        if row is None:
            return [self.cell(row, column) for row in range(self.rows)]
        elif column is None:
            return [self.cell(row, column) for column in range(self.columns)]
        return self.cells[row * self.columns + column]

table_values = [
    ('Temp.Strand', 10),
    ('Temp. Kamer', 18),
    ('Temp. Keuken', 20),
    ('Temp. Zee', 20),
    ('Luchtdruk', 1020.520),
    ('Luchtdruk Max', 1020.520),
    ('Datum', '22-01-2020 10:30'),
    ('Luchtdruk Min', 1020.520),
    ('Datum', '22-01-2020 10:30')
]

root = tk.Tk()
tk.Label(root, text='This is a table').grid(row=0, column=0)
 
table = Table(root, data=table_values)
table['font'] = ('Arial', 16, 'bold')
table['bg'] = 'black'
table['fg'] = 'white'
for cell in table.cell(column=0):
    cell['fg'] = 'green'

for cell in table.cell(column=1):
     cell['justify'] = tk.RIGHT
 
for cell, value in zip(table.cell(column=1), table_values):
    cell['fg'] = 'red' if isinstance(value[1], str) else 'white'

table.grid(row=1, column=0, padx=10, pady=10)
 
root.mainloop()
BashBedlam likes this post
Reply
#6
WoW, thank you very much!
Reply
#7
After playing with the table a bit I found I do not like using __setitem__() and would rather specify configuration options by providing them to __init__() or using configure(). This Table is easier to work with. I also didn't like having a list of Entry widgets and a list of StringVars, so I made a new class that is a Entry widget with a StringVar.
import tkinter as tk

def popkeys(dictionary, *keys):
    """Pop keys from dictionary.  Return values in list"""
    return [dictionary.pop(key) if key in dictionary else None for key in keys]

class EntryCell(tk.Entry):
    """tk.Entry widget with a StringVar"""
    def __init__(self, *args, **kvargs):
        self.var = kvargs.get('textvariable', False)
        if not self.var:
            self.var = tk.StringVar()
            kvargs['textvariable'] = self.var
        super().__init__(*args, **kvargs)

    def get(self):
        """Return text value"""
        return self.var.get()

    def set(self, value):
        """Set text value"""
        self.var.set(value)

class Table(tk.Frame):
    """2D matrix of Entry widgets"""
    def __init__(self, parent, rows=None, columns=None, data=None, **kwargs):
        super().__init__(parent)
        if data is not None:
            rows = len(data)
            columns = len(data[0])
        elif not rows and columns:
            raise TypeError('__init__() missing required rows and columns or data argument')
        self.rows = rows
        self.columns = columns
        self.cells = []
        for row in range(rows): 
            for col in range(columns):
                self.cells.append(EntryCell(self))
                self.cells[-1].grid(row=row, column=col)
                if data:
                    self.cells[-1].set(data[row][col])
        self.configure(**kwargs)

    def configure(self, **kwargs):
        """Set configure options.  Adds fg, font, justify and columnwidth
        to Frame's config options
        """
        row, col, bg, fg, font, justify, width = \
                popkeys(kwargs, 'row', 'column', 'bg', 'fg', 'font', 'justify', 'columnwidth')

        for cell in self.cell(row, col):
            if bg is not None: cell.configure(bg=bg)
            if fg is not None: cell.configure(fg=fg)
            if font is not None: cell.configure(font=font)
            if justify is not None: cell.configure(justify=justify)
            if width is not None: cell.configure(width=width)
        if bg is not None: kwargs['bg'] = bg
        if kwargs:
            super().configure(**kwargs)

    def cell(self, row=None, column=None):
        """Get cell(s).  To get all cells in a row, leave out column.
        To get all cells in a column, leave out row.  To get all cells
        leave out row and column.
        """
        if row is None and column is None:
            return self.cells
        elif row is None:
            return [self.cell(row, column) for row in range(self.rows)]
        elif column is None:
            return [self.cell(row, column) for column in range(self.columns)]
        return self.cells[row * self.columns + column]

table_values = [
    ('Parameter', 'Value'),
    ('Temp.Strand', 10),
    ('Temp. Kamer', 18),
    ('Temp. Keuken', 20),
    ('Temp. Zee', 20),
    ('Luchtdruk', 1020.520),
    ('Luchtdruk Max', 1020.520),
    ('Datum', '22-01-2020 10:30'),
    ('Luchtdruk Min', 1020.520),
    ('Datum', '22-01-2020 10:30')
]

font = ('Arial', 16, 'bold')

root = tk.Tk()
tk.Label(root, text='This is a table', font=font).grid(row=0, column=0)
 
table = Table(root, data=table_values, font=font, bg='black', fg='white', bd=10, columnwidth=15)
table.configure(column=0, fg='green')
table.configure(column=1, justify=tk.RIGHT)
table.configure(row=0, fg='Blue', justify=tk.CENTER)
table.grid(row=1, column=0, padx=10, pady=10)
 
root.mainloop()
I set up the configure method so you can apply configuration options to a range of cells instead of all cells. Just specify row or column in the configure parameters and it will apply the settings to cells in only that row or column.
Reply
#8
(Mar-15-2021, 03:14 AM)deanhystad Wrote: After playing with the table a bit I found I do not like using __setitem__() and would rather specify configuration options by providing them to __init__() or using configure(). This Table is easier to work with. I also didn't like having a list of Entry widgets and a list of StringVars, so I made a new class that is a Entry widget with a StringVar.
import tkinter as tk

def popkeys(dictionary, *keys):
    """Pop keys from dictionary.  Return values in list"""
    return [dictionary.pop(key) if key in dictionary else None for key in keys]

class EntryCell(tk.Entry):
    """tk.Entry widget with a StringVar"""
    def __init__(self, *args, **kvargs):
        self.var = kvargs.get('textvariable', False)
        if not self.var:
            self.var = tk.StringVar()
            kvargs['textvariable'] = self.var
        super().__init__(*args, **kvargs)

    def get(self):
        """Return text value"""
        return self.var.get()

    def set(self, value):
        """Set text value"""
        self.var.set(value)

class Table(tk.Frame):
    """2D matrix of Entry widgets"""
    def __init__(self, parent, rows=None, columns=None, data=None, **kwargs):
        super().__init__(parent)
        if data is not None:
            rows = len(data)
            columns = len(data[0])
        elif not rows and columns:
            raise TypeError('__init__() missing required rows and columns or data argument')
        self.rows = rows
        self.columns = columns
        self.cells = []
        for row in range(rows): 
            for col in range(columns):
                self.cells.append(EntryCell(self))
                self.cells[-1].grid(row=row, column=col)
                if data:
                    self.cells[-1].set(data[row][col])
        self.configure(**kwargs)

    def configure(self, **kwargs):
        """Set configure options.  Adds fg, font, justify and columnwidth
        to Frame's config options
        """
        row, col, bg, fg, font, justify, width = \
                popkeys(kwargs, 'row', 'column', 'bg', 'fg', 'font', 'justify', 'columnwidth')

        for cell in self.cell(row, col):
            if bg is not None: cell.configure(bg=bg)
            if fg is not None: cell.configure(fg=fg)
            if font is not None: cell.configure(font=font)
            if justify is not None: cell.configure(justify=justify)
            if width is not None: cell.configure(width=width)
        if bg is not None: kwargs['bg'] = bg
        if kwargs:
            super().configure(**kwargs)

    def cell(self, row=None, column=None):
        """Get cell(s).  To get all cells in a row, leave out column.
        To get all cells in a column, leave out row.  To get all cells
        leave out row and column.
        """
        if row is None and column is None:
            return self.cells
        elif row is None:
            return [self.cell(row, column) for row in range(self.rows)]
        elif column is None:
            return [self.cell(row, column) for column in range(self.columns)]
        return self.cells[row * self.columns + column]

table_values = [
    ('Parameter', 'Value'),
    ('Temp.Strand', 10),
    ('Temp. Kamer', 18),
    ('Temp. Keuken', 20),
    ('Temp. Zee', 20),
    ('Luchtdruk', 1020.520),
    ('Luchtdruk Max', 1020.520),
    ('Datum', '22-01-2020 10:30'),
    ('Luchtdruk Min', 1020.520),
    ('Datum', '22-01-2020 10:30')
]

font = ('Arial', 16, 'bold')

root = tk.Tk()
tk.Label(root, text='This is a table', font=font).grid(row=0, column=0)
 
table = Table(root, data=table_values, font=font, bg='black', fg='white', bd=10, columnwidth=15)
table.configure(column=0, fg='green')
table.configure(column=1, justify=tk.RIGHT)
table.configure(row=0, fg='Blue', justify=tk.CENTER)
table.grid(row=1, column=0, padx=10, pady=10)
 
root.mainloop()
I set up the configure method so you can apply configuration options to a range of cells instead of all cells. Just specify row or column in the configure parameters and it will apply the settings to cells in only that row or column.

Thank you very much. You are a great help and learned a bit more on Python.
Unfortunately if have a bit of an issue which I can't figure out.
Now that the table is presented on the screen, I can't figure out how to place an image on, let's say, the richt side from the table. Location 100,100, see the Tk.Label....what am I doing wrong here...
Thanks again for your help!!!!


root = tk.Tk()
root.attributes("-fullscreen",True)

root.configure(bg='black')
tk.Label(root, text='This is a table', font=font).grid(row=0, column=0)

table = Table(root, data=table_values, font=font, bg='black', fg='white', bd=10, columnwidth=15)
table.configure(column=0, fg='green')
table.configure(column=1, justify=tk.RIGHT)
table.configure(row=0, fg='Blue', justify=tk.CENTER)
table.grid(row=1, column=0, padx=10, pady=10)
tk.Label (root,image="image.gif" ). grid(row=100,column=100)

root.mainloop()
Reply
#9
Please enclose code in Python tags to keep indentation. This makes it easier for others to read/test your code. Don't use the reply button unless you are referencing a specific post in the thread. We don't need multiple copies of my monster posts.

I don't think you understand how grid works.

Grid is a layout manager, not an actual grid. If I say a widget is at .grid(row=100,column=100) I know that widget will be below and right of a widget at .grid(row=0, column=0). That is all I know. The widgets may be very far apart or adjacent depending on how many widgets are in rows 1..99 and columns 1..99.

In your code snippet there is a label in grid(0,0), a table in grid(0,1) and an image label in grid(100, 100). These are all in the root window, so I expect to see the table below the label, and the image below and right of the table. Is that what you are seeing? If you want to place the image right of the table it should be in .grid(row=1, column=1). That should center the widget vertically on the right side of the table.

If you are trying to place the image right of a particular row on the table, you need to make the image label a child of table, not root. However, be aware that adding images to the table grid may change the height of a row or width of a column. The grid layout manager adjusts the row height and column width to fit the largest widget.

If you just want a little space between the table and your image, use .grid(padx=5, pady=10) to add 5 pixels of horizontal spacing and 10 pixels of vertical spacing around a widget. The table is already padded out 10 pixels horizontally and vertically. You can increase that for more space or add padding when you .grid() the label.

If you want more control of where widgets appear in your window (Stop calling it a canvas. In tkinter Canvas is a class of widget like Label and Entry), you may want to use .place() instead of .grid(). Place lets you specify the location where a widget appears in a window. When using .place() you need to know the size of each of your widgets and you need to manually set the size of the window, things that are handled by the layout manager when using .grid() or .pack().

I think you would get a lot of benefit from doing a few online tutorials to learn more about tkinter. Look for tutorials that focus on using Frames and the different layout methods (.pack(), .grid(), .place).
Reply
#10
Hi,
Thanks for the explanation. I now much more now, but not enough to get the job done. So your advice to follow some youtube's is a good one. I will do that!!
Thanks again for all the effort.
Best regards
Jan
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] Resizing image inside Canvas (with Canvas' resize) Gupi 2 25,126 Jun-04-2019, 05:05 AM
Last Post: Gupi
  Display and update the label text which display the serial value jenkins43 5 9,107 Feb-04-2019, 04:36 AM
Last Post: Larz60+
  Display more than one button in GUI to display MPU6000 Sensor readings barry76 4 3,906 Jan-05-2019, 01:48 PM
Last Post: wuf

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020