Albert
Albert

Reputation: 311

Python Tkinter Run in a loop entries x times defined in a variable

I would like to create one Python script using Tkinter that defines the number of the networks and, using the value inserted by the user in entry field, create in the next page x times networks to define. Then the values inserted by the user to create a file which has a pattern like this:

--- #
networks:
- { cloud: cloud1, network:  }
- { cloud: cloud1, network:  }
- { cloud: cloud1, network:  }
- { cloud: cloud1, network:  }
...

Example:

If the user enters 20 on the "Define the number of networks" page, then we should have 20 entry fields in the "Define the networks" page and 20 rows like this in the file: - { cloud: cloud1, network: }

@AXK helped me for accomplishing this, thanks a lot. I would also like to have a scroll when I have more than 10 networks to introduce, for example. I have tried on AXK code to change it as it follows:

import Tkinter as tk
import tkFont as tkfont
import sys
from Tkinter import *

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.title_font = tkfont.Font(
            family="Helvetica", size=18, weight="bold", slant="italic"
        )
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)
        self.container = container
        self.current_frame = None

        self.num_networks = tk.IntVar()  # Var shared between pages
        self.show_frame("StartPage")

    def show_frame(self, page_name):
        """Show a frame for the given page name"""
        if self.current_frame:
            self.current_frame.destroy()
            self.current_frame = None

        frame_class = globals()[page_name]  # Ugly, but works.
        frame = frame_class(parent=self.container, controller=self)
        frame.grid(row=0, column=0, sticky="nsew")
        frame.tkraise()
        self.current_frame = frame


class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self,text="This is the start page",font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)

        button1 = tk.Button(self,text="Define the number of networks",command=lambda: controller.show_frame("PageOne"))
        button1.pack()


class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self,text="Define the number of networks",font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        nwnum = tk.Entry(self, textvariable=controller.num_networks)
        nwnum.pack()

        button1 = tk.Button(self,text="Go to the start page",command=lambda: controller.show_frame("StartPage"),)
        button1.pack()

        button2 = tk.Button(self,text="Define the networks",command=lambda: controller.show_frame("PageTwo"),)
        button2.pack()


class PageTwo(tk.Frame):
    def getent1(self):
      with open("/home/dante/output.txt", "w") as f:
        f.write("--- #" + "\n")
        f.write("networks:" + "\n")
        for entvar in self.entry_vars:
            value = entvar.get()
            if value:
                f.write("- { cloud: cloud1, network: "+ value+ " }"+ "\n")

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.scrollbar = Scrollbar(self, orient='vertical')
        label = tk.Label(self,text="Define the networks",font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        self.entries = []
        self.entry_vars = []

        self.scrollbar.config(command=self.yview)
        self.scrollbar.pack(side='right', fill='y')
        for t in range(1, self.controller.num_networks.get()+1):
            entvar = tk.StringVar()
            ent = tk.Entry(self, textvariable=entvar)
            self.entry_vars.append(entvar)
            self.entries.append(ent)
            ent.pack()
        button3 = tk.Button(self,text="Go to the start page",command=lambda: controller.show_frame("StartPage"),)
        button3.pack()
        button4 = tk.Button(self, text="Create the networks", command=self.getent1)
        button4.pack()

    def yview(self, *args):
        self.entry_vars.yview(*args)

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

While I am running it, I am seeing the scroll in the right side of the tab, but when I am pressing down or up, I am getting the following error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib64/python2.7/lib-tk/Tkinter.py", line 1470, in __call__
    return self.func(*args)
  File "program.py", line 96, in yview
    self.entry_vars.yview(*args)
AttributeError: 'list' object has no attribute 'yview'

How can I accomplish this?

P.S. I am quite new to programming

Upvotes: 0

Views: 73

Answers (1)

AKX
AKX

Reputation: 168996

I refactored things a little:

  • I changed the imports to work with my Python 3 installation. Things may be different for you.
  • The YAML output is written to sys.stdout, not a file, since I don't have /home/dante. ;)
  • The frame objects aren't all initialized immediately now; rather, they're only created when show_frame is being called.
  • The Tk variable holding the number of networks is now owned by the controller; the subframes write/read it from there.
  • The string variables holding the network names for frame 3 are stowed in a list in the third frame, and read from there in the YAML-writing function.

import tkinter as tk
from tkinter import font as tkfont
import sys


class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.title_font = tkfont.Font(
            family="Helvetica", size=18, weight="bold", slant="italic"
        )
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)
        self.container = container
        self.current_frame = None

        self.num_networks = tk.IntVar()  # Var shared between pages
        self.show_frame("StartPage")

    def show_frame(self, page_name):
        """Show a frame for the given page name"""
        if self.current_frame:
            self.current_frame.destroy()
            self.current_frame = None

        frame_class = globals()[page_name]  # Ugly, but works.
        frame = frame_class(parent=self.container, controller=self)
        frame.grid(row=0, column=0, sticky="nsew")
        frame.tkraise()
        self.current_frame = frame


class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(
            self,
            text="This is the start page",
            font=controller.title_font,
        )
        label.pack(side="top", fill="x", pady=10)

        button1 = tk.Button(
            self,
            text="Define the number of networks",
            command=lambda: controller.show_frame("PageOne"),
        )
        button1.pack()


class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(
            self,
            text="Define the number of networks",
            font=controller.title_font,
        )
        label.pack(side="top", fill="x", pady=10)

        nwnum = tk.Entry(self, textvariable=controller.num_networks)
        nwnum.pack()

        button1 = tk.Button(
            self,
            text="Go to the start page",
            command=lambda: controller.show_frame("StartPage"),
        )
        button1.pack()

        button2 = tk.Button(
            self,
            text="Define the networks",
            command=lambda: controller.show_frame("PageTwo"),
        )
        button2.pack()


class PageTwo(tk.Frame):
    def getent1(self):
        f = sys.stdout
        f.write("--- #" + "\n")
        f.write("networks:" + "\n")
        for entvar in self.entry_vars:
            value = entvar.get()
            if value:
                f.write(
                    "- { cloud: cloud1, network: "
                    + value
                    + " }"
                    + "\n"
                )

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        label = tk.Label(
            self,
            text="Define the networks",
            font=controller.title_font,
        )
        label.pack(side="top", fill="x", pady=10)
        self.entries = []
        self.entry_vars = []

        for t in range(1, self.controller.num_networks.get()):
            entvar = tk.StringVar()
            ent = tk.Entry(self, textvariable=entvar)
            self.entry_vars.append(entvar)
            self.entries.append(ent)
            ent.pack()
        button3 = tk.Button(
            self,
            text="Go to the start page",
            command=lambda: controller.show_frame("StartPage"),
        )
        button3.pack()
        button4 = tk.Button(
            self, text="Create the networks", command=self.getent1
        )
        button4.pack()


if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

Upvotes: 1

Related Questions