Displaying an arbitrary number of buttons and automatically arranging the buttons
Don't import tkinter twice
You're doing this:
import tkinter as tk
from tkinter import *

Remove the second import since you are already importing tkinter. That means that everywhere you reference a tkinter class or constant you need to prefix it with tk. (eg: tk.Button). This is much preferable over a wildcard import.
Let tkinter compute the size of the widgets
Since you are creating a grid, there's no reason to explicitly configure the width and the heights of the buttons. Instead, use the sticky attribute to tell tkinter to grow (or shrink) the widget to fit the area given to it.
Because you are properly giving the rows and columns weight, all you need to do is add the sticky attribute when adding the widget to the grid, and then remove the code that explicitly configures the width and height.
For example:
button.grid(row=i,column=j, sticky=&quot;nsew&quot;)

Don't use variables i and j to represent rows and columns
If you are iterating over rows and columns, use variable names to make that more clear. Variables named i and j carry no meaning. Use row and column (or something similar -- r, c, row_number, column_number, etc) instead. Also, there's no reason to save the results of calcRowsAndColumns to a tuple. Save the individual values as scalar variables.
In other words, instead of this:
rowsAndColumnTuple = calcRowsAndColumns(None)
...
For i in range(rowsAndColumnTuple[0]):
    root.grid_rowconfigure(i, weight=1)
    for j in range(rowsAndColumnTuple[1]):
        root.grid_columnconfigure(j, weight=1)

Do this:
rows, columns = calcRowsAndColumns(None)
for row in range(rows):
    root.grid_rowconfigure(row, weight=1)
    for column in range(columns):
        root.grid_columnconfigure(column, weight=1)

Remove the call to update
With the above changes, you don't need to call root.update() toward the end of the script.
Use classes
Your code is hard to understand at a high level. Part of the reason for this is that you have to define some functions before you create the actual GUI. That means I have to scan through the lines of code looking for where you create the window.
While some people find the use of classes daunting, they make writing GUI code much easier. You should consider moving all of this code into a single class, which will allow you to write the code in a manner that makes it more readable.
A description of my recommendation is in this answer on stackoverflow: https://stackoverflow.com/a/17470842/7432. In a nutshell, it suggests starting with this template:
import tkinter as tk

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        # &lt;create the rest of your GUI here&gt;

if __name__ == &quot;__main__&quot;:
    root = tk.Tk()
    MainApplication(root).pack(fill=&quot;both&quot;, expand=True)
    root.mainloop()

You can then move all of your code into MainApplication . This allows the main logic of creating the GUI to be up front rather than buried deep in the code.