{"id":21,"date":"2017-12-18T14:34:00","date_gmt":"2017-12-18T14:34:00","guid":{"rendered":"https:\/\/sandrock.co.za\/carl\/2017\/12\/18\/a-simple-gui-spreadsheet-in-less-than-100-lines-of-python\/"},"modified":"2026-08-11T06:22:40","modified_gmt":"2026-08-11T04:22:40","slug":"a-simple-gui-spreadsheet-in-less-than-100-lines-of-python","status":"publish","type":"post","link":"https:\/\/sandrock.co.za\/carl\/2017\/12\/a-simple-gui-spreadsheet-in-less-than-100-lines-of-python\/","title":{"rendered":"A simple GUI spreadsheet in less than 100 lines of Python"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">\nOriginal challenge: Standard library only<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I set myself a little challenge to write a simple spreadsheet-like program in Python in less than 100 lines, using only the standard library.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The code in <a href=\"https:\/\/github.com\/alchemyst\/minimal_spreadsheet\">this repository<\/a>&nbsp;manages it in 100 non-blank lines, including comments. 88 if you ignore the comments. The full code is also at the end of this post. <i>Note: I have also redone the code in PyQT5. This is discussed after the tkinter code.<\/i><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is what the program looks like when you run it:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/2.bp.blogspot.com\/-bJyBjOCqrho\/WjfPpjmo6WI\/AAAAAAAAkc0\/qTpytlVXQ4MeTiiRDsa5skSwz50L7KEEQCLcBGAs\/s1600\/screenshot.png\"><img decoding=\"async\" src=\"https:\/\/2.bp.blogspot.com\/-bJyBjOCqrho\/WjfPpjmo6WI\/AAAAAAAAkc0\/qTpytlVXQ4MeTiiRDsa5skSwz50L7KEEQCLcBGAs\/s640\/screenshot.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\nYou can click on cells to edit their formulae and move around with the arrow keys. Formulae are evaluated once you press Return or navigate way from a cell. Every cell is evaluated as a Python expression. This means you don&#8217;t do the Excel thing of having formulae start with =. Everything is a fomula. So the formula in A2 in the screenshot above is valid and will evaluate to 1 once focus moves away from the cell. The math module is imported by default, so you can use sin and cos and so on. I use a very cheap and nasty call of eval to make it all work, which you should never do for production code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Other than using eval, I also play a little fast and loose with the way objects are allowed to touch each other in the code. I allow each cell to access all their siblings directly instead of going through an intermediary. I probably also break a couple of other OO rules along the way with the way the cells propagate their calculations when they are changed. But the whole code only took a couple of hours to write and it actually works.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And now, here is the full code:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-Fira-Code\" style=\"font-size:.875rem;font-family:Code-Pro-Fira-Code,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#839496;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>#!\/usr\/bin\/env python\n\nimport tkinter as tk\nimport math\nimport re\nfrom collections import ChainMap\n\nNrows = 5\nNcols = 5\n\ncellre = re.compile(r'b&#91;A-Z&#93;&#91;0-9&#93;b')\n\n\ndef cellname(i, j):\n    return f'{chr(ord(\"A\")+j)}{i+1}'\n\n\nclass Cell():\n    def __init__(self, i, j, siblings, parent):\n        self.row = i\n        self.col = j\n        self.siblings = siblings\n        self.name = cellname(i, j)\n        self.formula = '0'\n        self.value = 0\n        # Dependencies - must be updated if this cell changes\n        self.deps = set()\n        # Requirements - values required for this cell to calculate\n        self.reqs = set()\n\n        self.var = tk.StringVar()\n        entry = self.widget = tk.Entry(parent,\n                                       textvariable=self.var,\n                                       justify='right')\n        entry.bind('&lt;FocusIn>', self.edit)\n        entry.bind('&lt;FocusOut>', self.update)\n        entry.bind('&lt;Return>', self.update)\n        entry.bind('&lt;Up>', self.move(-1, 0))\n        entry.bind('&lt;Down>', self.move(+1, 0))\n        entry.bind('&lt;Left>', self.move(0, -1))\n        entry.bind('&lt;Right>', self.move(0, 1))\n\n        self.var.set(self.value)\n\n    def move(self, rowadvance, coladvance):\n        targetrow = (self.row + rowadvance) % Nrows\n        targetcol = (self.col + coladvance) % Ncols\n\n        def focus(event):\n            targetwidget = self.siblings&#91;cellname(targetrow, targetcol)&#93;.widget\n            targetwidget.focus()\n\n        return focus\n\n    def calculate(self):\n        currentreqs = set(cellre.findall(self.formula))\n\n        # Add this cell to the new requirement's dependents\n        for r in currentreqs - self.reqs:\n            self.siblings&#91;r&#93;.deps.add(self.name)\n        # Add remove this cell from dependents no longer referenced\n        for r in self.reqs - currentreqs:\n            self.siblings&#91;r&#93;.deps.remove(self.name)\n\n        # Look up the values of our required cells\n        reqvalues = {r: self.siblings&#91;r&#93;.value for r in currentreqs}\n        # Build an environment with these values and basic math functions\n        environment = ChainMap(math.__dict__, reqvalues)\n        # Note that eval is DANGEROUS and should not be used in production\n        self.value = eval(self.formula, {}, environment)\n\n        self.reqs = currentreqs\n        self.var.set(self.value)\n\n    def propagate(self):\n        for d in self.deps:\n            self.siblings&#91;d&#93;.calculate()\n            self.siblings&#91;d&#93;.propagate()\n\n    def edit(self, event):\n        self.var.set(self.formula)\n        self.widget.select_range(0, tk.END)\n\n    def update(self, event):\n        self.formula = self.var.get()\n        self.calculate()\n        self.propagate()\n        # If this was after pressing Return, keep showing the formula\n        if hasattr(event, 'keysym') and event.keysym == \"Return\":\n            self.var.set(self.formula)\n\n\nclass SpreadSheet(tk.Frame):\n    def __init__(self, rows, cols, master=None):\n        super().__init__(master)\n        self.rows = rows\n        self.cols = cols\n        self.cells = {}\n\n        self.pack()\n        self.create_widgets()\n\n    def create_widgets(self):\n        # Frame for all the cells\n        self.cellframe = tk.Frame(self)\n        self.cellframe.pack(side='top')\n\n        # Column labels\n        blank = tk.Label(self.cellframe)\n        blank.grid(row=0, column=0)\n        for j in range(self.cols):\n            label = tk.Label(self.cellframe, text=chr(ord('A')+j))\n            label.grid(row=0, column=j+1)\n\n        # Fill in the rows\n        for i in range(self.rows):\n            rowlabel = tk.Label(self.cellframe, text=str(i + 1))\n            rowlabel.grid(row=1+i, column=0)\n            for j in range(self.cols):\n                cell = Cell(i, j, self.cells, self.cellframe)\n                self.cells&#91;cell.name&#93; = cell\n                cell.widget.grid(row=1+i, column=1+j)\n\n\nroot = tk.Tk()\napp = SpreadSheet(Nrows, Ncols, master=root)\napp.mainloop()\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki solarized-dark\" style=\"background-color: #002B36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #586E75; font-style: italic\">#!\/usr\/bin\/env python<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> tkinter <\/span><span style=\"color: #859900\">as<\/span><span style=\"color: #839496\"> tk<\/span><\/span>\n<span class=\"line\"><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> math<\/span><\/span>\n<span class=\"line\"><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> re<\/span><\/span>\n<span class=\"line\"><span style=\"color: #859900\">from<\/span><span style=\"color: #839496\"> collections <\/span><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> ChainMap<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">Nrows <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #D33682\">5<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">Ncols <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #D33682\">5<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">cellre <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> re.compile(<\/span><span style=\"color: #93A1A1; font-weight: bold\">r<\/span><span style=\"color: #DC322F\">&#39;b<\/span><span style=\"color: #CB4B16\">&#91;A-Z&#93;&#91;0-9&#93;<\/span><span style=\"color: #DC322F\">b&#39;<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">cellname<\/span><span style=\"color: #839496\">(i, j):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #859900\">return<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #93A1A1; font-weight: bold\">f<\/span><span style=\"color: #2AA198\">&#39;<\/span><span style=\"color: #CB4B16\">{<\/span><span style=\"color: #268BD2\">chr<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">ord<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #2AA198\">&quot;A&quot;<\/span><span style=\"color: #839496\">)<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\">j)<\/span><span style=\"color: #CB4B16\">}{<\/span><span style=\"color: #839496\">i<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #CB4B16\">}<\/span><span style=\"color: #2AA198\">&#39;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #93A1A1; font-weight: bold\">class<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #CB4B16\">Cell<\/span><span style=\"color: #839496\">():<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">__init__<\/span><span style=\"color: #839496\">(self, i, j, siblings, parent):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.row <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> i<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.col <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> j<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> siblings<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.name <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> cellname(i, j)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.formula <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #2AA198\">&#39;0&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.value <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #D33682\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Dependencies - must be updated if this cell changes<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.deps <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">set<\/span><span style=\"color: #839496\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Requirements - values required for this cell to calculate<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.reqs <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">set<\/span><span style=\"color: #839496\">()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.var <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> tk.StringVar()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        entry <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.widget <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> tk.Entry(parent,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">                                       textvariable<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.var,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">                                       justify<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #2AA198\">&#39;right&#39;<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        entry.bind(<\/span><span style=\"color: #2AA198\">&#39;&lt;FocusIn&gt;&#39;<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.edit)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        entry.bind(<\/span><span style=\"color: #2AA198\">&#39;&lt;FocusOut&gt;&#39;<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.update)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        entry.bind(<\/span><span style=\"color: #2AA198\">&#39;&lt;Return&gt;&#39;<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.update)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        entry.bind(<\/span><span style=\"color: #2AA198\">&#39;&lt;Up&gt;&#39;<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.move(<\/span><span style=\"color: #859900\">-<\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #D33682\">0<\/span><span style=\"color: #839496\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        entry.bind(<\/span><span style=\"color: #2AA198\">&#39;&lt;Down&gt;&#39;<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.move(<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #D33682\">0<\/span><span style=\"color: #839496\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        entry.bind(<\/span><span style=\"color: #2AA198\">&#39;&lt;Left&gt;&#39;<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.move(<\/span><span style=\"color: #D33682\">0<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #859900\">-<\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #839496\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        entry.bind(<\/span><span style=\"color: #2AA198\">&#39;&lt;Right&gt;&#39;<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.move(<\/span><span style=\"color: #D33682\">0<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #839496\">))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.var.set(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.value)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">move<\/span><span style=\"color: #839496\">(self, rowadvance, coladvance):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        targetrow <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> (<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.row <\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\"> rowadvance) <\/span><span style=\"color: #859900\">%<\/span><span style=\"color: #839496\"> Nrows<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        targetcol <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> (<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.col <\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\"> coladvance) <\/span><span style=\"color: #859900\">%<\/span><span style=\"color: #839496\"> Ncols<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">focus<\/span><span style=\"color: #839496\">(event):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            targetwidget <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;cellname(targetrow, targetcol)&#93;.widget<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            targetwidget.focus()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">return<\/span><span style=\"color: #839496\"> focus<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">calculate<\/span><span style=\"color: #839496\">(self):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        currentreqs <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">set<\/span><span style=\"color: #839496\">(cellre.findall(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.formula))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Add this cell to the new requirement&#39;s dependents<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> r <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> currentreqs <\/span><span style=\"color: #859900\">-<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.reqs:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;r&#93;.deps.add(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.name)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Add remove this cell from dependents no longer referenced<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> r <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.reqs <\/span><span style=\"color: #859900\">-<\/span><span style=\"color: #839496\"> currentreqs:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;r&#93;.deps.remove(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.name)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Look up the values of our required cells<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        reqvalues <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> {r: <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;r&#93;.value <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> r <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> currentreqs}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Build an environment with these values and basic math functions<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        environment <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> ChainMap(math.__dict__, reqvalues)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Note that eval is DANGEROUS and should not be used in production<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.value <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">eval<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.formula, {}, environment)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.reqs <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> currentreqs<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.var.set(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.value)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">propagate<\/span><span style=\"color: #839496\">(self):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> d <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.deps:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;d&#93;.calculate()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;d&#93;.propagate()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">edit<\/span><span style=\"color: #839496\">(self, event):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.var.set(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.formula)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.widget.select_range(<\/span><span style=\"color: #D33682\">0<\/span><span style=\"color: #839496\">, tk.<\/span><span style=\"color: #CB4B16\">END<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">update<\/span><span style=\"color: #839496\">(self, event):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.formula <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.var.get()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.calculate()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.propagate()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># If this was after pressing Return, keep showing the formula<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">if<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">hasattr<\/span><span style=\"color: #839496\">(event, <\/span><span style=\"color: #2AA198\">&#39;keysym&#39;<\/span><span style=\"color: #839496\">) <\/span><span style=\"color: #859900\">and<\/span><span style=\"color: #839496\"> event.keysym <\/span><span style=\"color: #859900\">==<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #2AA198\">&quot;Return&quot;<\/span><span style=\"color: #839496\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.var.set(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.formula)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #93A1A1; font-weight: bold\">class<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #CB4B16\">SpreadSheet<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #6C71C4\">tk<\/span><span style=\"color: #839496\">.<\/span><span style=\"color: #6C71C4\">Frame<\/span><span style=\"color: #839496\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">__init__<\/span><span style=\"color: #839496\">(self, rows, cols, master<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #B58900\">None<\/span><span style=\"color: #839496\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">super<\/span><span style=\"color: #839496\">().<\/span><span style=\"color: #268BD2\">__init__<\/span><span style=\"color: #839496\">(master)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.rows <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> rows<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cols <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> cols<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cells <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> {}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.pack()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.create_widgets()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">create_widgets<\/span><span style=\"color: #839496\">(self):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Frame for all the cells<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cellframe <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> tk.Frame(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cellframe.pack(side<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #2AA198\">&#39;top&#39;<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Column labels<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        blank <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> tk.Label(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cellframe)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        blank.grid(row<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #D33682\">0<\/span><span style=\"color: #839496\">, column<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #D33682\">0<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> j <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">range<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cols):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            label <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> tk.Label(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cellframe, text<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #268BD2\">chr<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">ord<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #2AA198\">&#39;A&#39;<\/span><span style=\"color: #839496\">)<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\">j))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            label.grid(row<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #D33682\">0<\/span><span style=\"color: #839496\">, column<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\">j<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Fill in the rows<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> i <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">range<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.rows):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            rowlabel <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> tk.Label(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cellframe, text<\/span><span style=\"color: #859900\">=str<\/span><span style=\"color: #839496\">(i <\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #839496\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            rowlabel.grid(row<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\">i, column<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #D33682\">0<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> j <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">range<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cols):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">                cell <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> Cell(i, j, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cells, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cellframe)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">                <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cells&#91;cell.name&#93; <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> cell<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">                cell.widget.grid(row<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\">i, column<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\">j)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">root <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> tk.Tk()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">app <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> SpreadSheet(Nrows, Ncols, master<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\">root)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">app.mainloop()<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\nPart 2: PyQT5<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\nI posted this blog to Reddit and one of the commenters mentioned that PyQT5 has a nice table widget which is more spreadsheet-like. So I re-did the spreadsheet in PyQT5. The code is functionally similar at the moment and also has approximately the same number of lines (87 non-comment code lines). The main differences are that I could use the QTableWidget to handle all the navigation. I had to do some interesting things with delegates to enable to editing mode I wanted, though, which cost me all the lines I saved from the navigation. I think my next step is going to be to extract the calculation logic into a common class which both of these can use &#8211; there is way too much duplication going on here right now!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So, this is what the PyQT5 one looks like. It&#8217;s a little more compact, but otherwise very similar. The interaction is slightly more spreadsheet-like though, and you get automatic highlighting and selection of multiple cells for free. Very nice.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/4.bp.blogspot.com\/-ZwWrsFP4XI0\/WjvA1kiC_0I\/AAAAAAAAkdw\/OZvxUwAJHKEuuJhphPtZnoliD6QAag-jQCLcBGAs\/s1600\/screenshot_pyqt5.png\"><img decoding=\"async\" src=\"https:\/\/4.bp.blogspot.com\/-ZwWrsFP4XI0\/WjvA1kiC_0I\/AAAAAAAAkdw\/OZvxUwAJHKEuuJhphPtZnoliD6QAag-jQCLcBGAs\/s1600\/screenshot_pyqt5.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\nAnd here is the code. You will notice a great deal of similarity. The key to getting the edit mode thing to work properly is the piece of magic with SpreadSheetDelegate. I didn&#8217;t think of that myself &#8211; I stole it from <a href=\"https:\/\/github.com\/baoboa\/pyqt5\/tree\/master\/examples\/itemviews\/spreadsheet\">this PyQT5 example<\/a>, which has a much nicer spreadsheet (you can change colours, fonts, print etc) with much worse formula logic.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-Fira-Code\" style=\"font-size:.875rem;font-family:Code-Pro-Fira-Code,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#839496;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>#!\/usr\/bin\/env python\n\nimport re\nimport sys\nfrom collections import ChainMap\nimport math\n\nfrom PyQt5.QtCore import Qt\nfrom PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget,\n        QTableWidgetItem, QItemDelegate, QLineEdit)\n\n\ncellre = re.compile(r'b&#91;A-Z&#93;&#91;0-9&#93;b')\n\n\ndef cellname(i, j):\n    return f'{chr(ord(\"A\")+j)}{i+1}'\n\n\nclass SpreadSheetDelegate(QItemDelegate):\n    def __init__(self, parent=None):\n        super(SpreadSheetDelegate, self).__init__(parent)\n\n    def createEditor(self, parent, styleOption, index):\n        editor = QLineEdit(parent)\n        editor.editingFinished.connect(self.commitAndCloseEditor)\n        return editor\n\n    def commitAndCloseEditor(self):\n        editor = self.sender()\n        self.commitData.emit(editor)\n        self.closeEditor.emit(editor, QItemDelegate.NoHint)\n\n    def setEditorData(self, editor, index):\n        editor.setText(index.model().data(index, Qt.EditRole))\n\n    def setModelData(self, editor, model, index):\n        model.setData(index, editor.text())\n\n\nclass SpreadSheetItem(QTableWidgetItem):\n    def __init__(self, siblings):\n        super(SpreadSheetItem, self).__init__()\n        self.siblings = siblings\n        self.value = 0\n        self.deps = set()\n        self.reqs = set()\n\n    def formula(self):\n        return super().data(Qt.DisplayRole)\n\n    def data(self, role):\n        if role == Qt.EditRole:\n            return self.formula()\n        if role == Qt.DisplayRole:\n            return self.display()\n\n        return super(SpreadSheetItem, self).data(role)\n\n    def calculate(self):\n        formula = self.formula()\n\n        if formula is None or formula == '':\n            self.value = 0\n            return\n\n        currentreqs = set(cellre.findall(formula))\n\n        name = cellname(self.row(), self.column())\n\n        # Add this cell to the new requirement's dependents\n        for r in currentreqs - self.reqs:\n            self.siblings&#91;r&#93;.deps.add(name)\n        # Add remove this cell from dependents no longer referenced\n        for r in self.reqs - currentreqs:\n            self.siblings&#91;r&#93;.deps.remove(name)\n\n        # Look up the values of our required cells\n        reqvalues = {r: self.siblings&#91;r&#93;.value for r in currentreqs}\n        # Build an environment with these values and basic math functions\n        environment = ChainMap(math.__dict__, reqvalues)\n        # Note that eval is DANGEROUS and should not be used in production\n        self.value = eval(formula, {}, environment)\n\n        self.reqs = currentreqs\n\n    def propagate(self):\n        for d in self.deps:\n            self.siblings&#91;d&#93;.calculate()\n            self.siblings&#91;d&#93;.propagate()\n\n    def display(self):\n        self.calculate()\n        self.propagate()\n        return str(self.value)\n\n\nclass SpreadSheet(QMainWindow):\n    def __init__(self, rows, cols, parent=None):\n        super(SpreadSheet, self).__init__(parent)\n\n        self.rows = rows\n        self.cols = cols\n\n        self.cells = {}\n\n        self.create_widgets()\n\n    def create_widgets(self):\n        table = self.table = QTableWidget(self.rows, self.cols, self)\n\n        headers = &#91;chr(ord('A') + j) for j in range(self.cols)&#93;\n        table.setHorizontalHeaderLabels(headers)\n\n        table.setItemDelegate(SpreadSheetDelegate(self))\n\n        for i in range(self.rows):\n            for j in range(self.cols):\n                cell = SpreadSheetItem(self.cells)\n                self.cells&#91;cellname(i, j)&#93; = cell\n                self.table.setItem(i, j, cell)\n\n        self.setCentralWidget(table)\n\n\nif __name__ == '__main__':\n    app = QApplication(sys.argv)\n    sheet = SpreadSheet(5, 5)\n    sheet.resize(520, 200)\n    sheet.show()\n    sys.exit(app.exec_())\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki solarized-dark\" style=\"background-color: #002B36\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #586E75; font-style: italic\">#!\/usr\/bin\/env python<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> re<\/span><\/span>\n<span class=\"line\"><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> sys<\/span><\/span>\n<span class=\"line\"><span style=\"color: #859900\">from<\/span><span style=\"color: #839496\"> collections <\/span><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> ChainMap<\/span><\/span>\n<span class=\"line\"><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> math<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #859900\">from<\/span><span style=\"color: #839496\"> PyQt5.QtCore <\/span><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> Qt<\/span><\/span>\n<span class=\"line\"><span style=\"color: #859900\">from<\/span><span style=\"color: #839496\"> PyQt5.QtWidgets <\/span><span style=\"color: #859900\">import<\/span><span style=\"color: #839496\"> (QApplication, QMainWindow, QTableWidget,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        QTableWidgetItem, QItemDelegate, QLineEdit)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">cellre <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> re.compile(<\/span><span style=\"color: #93A1A1; font-weight: bold\">r<\/span><span style=\"color: #DC322F\">&#39;b<\/span><span style=\"color: #CB4B16\">&#91;A-Z&#93;&#91;0-9&#93;<\/span><span style=\"color: #DC322F\">b&#39;<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">cellname<\/span><span style=\"color: #839496\">(i, j):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #859900\">return<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #93A1A1; font-weight: bold\">f<\/span><span style=\"color: #2AA198\">&#39;<\/span><span style=\"color: #CB4B16\">{<\/span><span style=\"color: #268BD2\">chr<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">ord<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #2AA198\">&quot;A&quot;<\/span><span style=\"color: #839496\">)<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\">j)<\/span><span style=\"color: #CB4B16\">}{<\/span><span style=\"color: #839496\">i<\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #D33682\">1<\/span><span style=\"color: #CB4B16\">}<\/span><span style=\"color: #2AA198\">&#39;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #93A1A1; font-weight: bold\">class<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #CB4B16\">SpreadSheetDelegate<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #6C71C4\">QItemDelegate<\/span><span style=\"color: #839496\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">__init__<\/span><span style=\"color: #839496\">(self, parent<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #B58900\">None<\/span><span style=\"color: #839496\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">super<\/span><span style=\"color: #839496\">(SpreadSheetDelegate, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">).<\/span><span style=\"color: #268BD2\">__init__<\/span><span style=\"color: #839496\">(parent)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">createEditor<\/span><span style=\"color: #839496\">(self, parent, styleOption, index):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        editor <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> QLineEdit(parent)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        editor.editingFinished.connect(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.commitAndCloseEditor)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">return<\/span><span style=\"color: #839496\"> editor<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">commitAndCloseEditor<\/span><span style=\"color: #839496\">(self):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        editor <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.sender()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.commitData.emit(editor)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.closeEditor.emit(editor, QItemDelegate.NoHint)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">setEditorData<\/span><span style=\"color: #839496\">(self, editor, index):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        editor.setText(index.model().data(index, Qt.EditRole))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">setModelData<\/span><span style=\"color: #839496\">(self, editor, model, index):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        model.setData(index, editor.text())<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #93A1A1; font-weight: bold\">class<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #CB4B16\">SpreadSheetItem<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #6C71C4\">QTableWidgetItem<\/span><span style=\"color: #839496\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">__init__<\/span><span style=\"color: #839496\">(self, siblings):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">super<\/span><span style=\"color: #839496\">(SpreadSheetItem, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">).<\/span><span style=\"color: #268BD2\">__init__<\/span><span style=\"color: #839496\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> siblings<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.value <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #D33682\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.deps <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">set<\/span><span style=\"color: #839496\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.reqs <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">set<\/span><span style=\"color: #839496\">()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">formula<\/span><span style=\"color: #839496\">(self):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">return<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">super<\/span><span style=\"color: #839496\">().data(Qt.DisplayRole)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">data<\/span><span style=\"color: #839496\">(self, role):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">if<\/span><span style=\"color: #839496\"> role <\/span><span style=\"color: #859900\">==<\/span><span style=\"color: #839496\"> Qt.EditRole:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #859900\">return<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.formula()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">if<\/span><span style=\"color: #839496\"> role <\/span><span style=\"color: #859900\">==<\/span><span style=\"color: #839496\"> Qt.DisplayRole:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #859900\">return<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.display()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">return<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">super<\/span><span style=\"color: #839496\">(SpreadSheetItem, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">).data(role)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">calculate<\/span><span style=\"color: #839496\">(self):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        formula <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.formula()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">if<\/span><span style=\"color: #839496\"> formula <\/span><span style=\"color: #859900\">is<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #B58900\">None<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">or<\/span><span style=\"color: #839496\"> formula <\/span><span style=\"color: #859900\">==<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #2AA198\">&#39;&#39;<\/span><span style=\"color: #839496\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.value <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #D33682\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #859900\">return<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        currentreqs <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">set<\/span><span style=\"color: #839496\">(cellre.findall(formula))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        name <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> cellname(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.row(), <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.column())<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Add this cell to the new requirement&#39;s dependents<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> r <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> currentreqs <\/span><span style=\"color: #859900\">-<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.reqs:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;r&#93;.deps.add(name)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Add remove this cell from dependents no longer referenced<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> r <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.reqs <\/span><span style=\"color: #859900\">-<\/span><span style=\"color: #839496\"> currentreqs:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;r&#93;.deps.remove(name)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Look up the values of our required cells<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        reqvalues <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> {r: <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;r&#93;.value <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> r <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> currentreqs}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Build an environment with these values and basic math functions<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        environment <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> ChainMap(math.__dict__, reqvalues)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #586E75; font-style: italic\"># Note that eval is DANGEROUS and should not be used in production<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.value <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">eval<\/span><span style=\"color: #839496\">(formula, {}, environment)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.reqs <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> currentreqs<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">propagate<\/span><span style=\"color: #839496\">(self):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> d <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.deps:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;d&#93;.calculate()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.siblings&#91;d&#93;.propagate()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">display<\/span><span style=\"color: #839496\">(self):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.calculate()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.propagate()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">return<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #859900\">str<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.value)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #93A1A1; font-weight: bold\">class<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #CB4B16\">SpreadSheet<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #6C71C4\">QMainWindow<\/span><span style=\"color: #839496\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">__init__<\/span><span style=\"color: #839496\">(self, rows, cols, parent<\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #B58900\">None<\/span><span style=\"color: #839496\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">super<\/span><span style=\"color: #839496\">(SpreadSheet, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">).<\/span><span style=\"color: #268BD2\">__init__<\/span><span style=\"color: #839496\">(parent)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.rows <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> rows<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cols <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> cols<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cells <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> {}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.create_widgets()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    <\/span><span style=\"color: #93A1A1; font-weight: bold\">def<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">create_widgets<\/span><span style=\"color: #839496\">(self):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        table <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.table <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> QTableWidget(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.rows, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cols, <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        headers <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> &#91;<\/span><span style=\"color: #268BD2\">chr<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">ord<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #2AA198\">&#39;A&#39;<\/span><span style=\"color: #839496\">) <\/span><span style=\"color: #859900\">+<\/span><span style=\"color: #839496\"> j) <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> j <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">range<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cols)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        table.setHorizontalHeaderLabels(headers)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        table.setItemDelegate(SpreadSheetDelegate(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">))<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> i <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">range<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.rows):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">            <\/span><span style=\"color: #859900\">for<\/span><span style=\"color: #839496\"> j <\/span><span style=\"color: #859900\">in<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #268BD2\">range<\/span><span style=\"color: #839496\">(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cols):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">                cell <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> SpreadSheetItem(<\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cells)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">                <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.cells&#91;cellname(i, j)&#93; <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> cell<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">                <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.table.setItem(i, j, cell)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #839496\">        <\/span><span style=\"color: #268BD2\">self<\/span><span style=\"color: #839496\">.setCentralWidget(table)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #859900\">if<\/span><span style=\"color: #839496\"> __name__ <\/span><span style=\"color: #859900\">==<\/span><span style=\"color: #839496\"> <\/span><span style=\"color: #2AA198\">&#39;__main__&#39;<\/span><span style=\"color: #839496\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    app <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> QApplication(sys.argv)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    sheet <\/span><span style=\"color: #859900\">=<\/span><span style=\"color: #839496\"> SpreadSheet(<\/span><span style=\"color: #D33682\">5<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #D33682\">5<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    sheet.resize(<\/span><span style=\"color: #D33682\">520<\/span><span style=\"color: #839496\">, <\/span><span style=\"color: #D33682\">200<\/span><span style=\"color: #839496\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    sheet.show()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #839496\">    sys.exit(app.exec_())<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Original challenge: Standard library only I set myself a little challenge to write a simple spreadsheet-like program in Python in less than 100 lines, using only the standard library. The code in this repository&nbsp;manages it in 100 non-blank lines, including comments. 88 if you ignore the comments. The full code is also at the end [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1],"tags":[],"class_list":["post-21","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/posts\/21","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/comments?post=21"}],"version-history":[{"count":2,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/posts\/21\/revisions"}],"predecessor-version":[{"id":319,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/posts\/21\/revisions\/319"}],"wp:attachment":[{"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/media?parent=21"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/categories?post=21"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sandrock.co.za\/carl\/wp-json\/wp\/v2\/tags?post=21"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}