简体   繁体   中英

Tkinter event widgets

I am currently making a GUI using Tkinter within a class. I want the GUI to ask the user for various inputs, based on their previous selection. Also, I will note that I am relatively new to python, and definitely Tkinter. However, I have made GUIs in MATLAB before.

So, what I want to do is the following:

  1. Ask user for speeds to process, separated by a comma. The user will then input speeds such as the following: [90, 100, 102p5].
    • To get the input from the entry, you must press the Enter key, which will call a function to get the string of speeds. I then take the string that is inputted and separate it based on the commas to form a list.
  2. Then, once the speeds are inputted, I want to update the GUI and show an entry section for each speed to input various pressures at that specific speed, separated by a comma. Pressures will be in the form [100kpa, 200kpa, 300kpa, 400kpa].
    • To get the values inputted for each pressure, you must press enter in each entry box, like before. I again form the string into a list of pressures.

The method that I have works, however I do not think this is the best practice. This is the link to see what the GUI looks like when you input various speeds, as described in step 1: GUI Image

Ultimately, by the end of this GUI I want to have a list of speeds, and for each speed a list of pressures. I am then going to take those lists and give them to another function to run a program I previously created.

One thing to note is I do not know how many speeds the user will want to process. I also do not know how many pressures will be inputted for each speed.

So, these are my questions: Would this be the "pythonic" way of handling events with Tkinter? I did not find anything online about how to show a new widget based on the completion (by pressing the Enter key) of the previous one.

I am also not 100% sure if the best method to pass variables in between the different methods is by making the variables an attribute of self. For example, how I pass the "speeds" variable and the dictionary "bp". Should I use global variables instead? It also gets complicated because I have to pass variables with an event, which does not allow the extra parameters.

Thank you in advance for your help! If you know a good website that explains Tkinter in a more intermediate way that would be helpful as well!

from Tkinter import *
import tkMessageBox


class MyWindow(Frame):
    def __init__(self, master):
            self.speeds = []
            Frame.__init__(self, master)
            self.speed_widgets()

    def speed_widgets(self):
            label = Label(self.master, text="Enter the speeds you want to process, seperated by a comma:")
            label = label.grid(row=0)
            self.speeds_text = Entry(self.master)
            self.speeds_text.grid(row=0, column=1)
            self.speeds_text.bind('<Return>', self.get_speeds)


    def get_speeds(self, event):
            #global speeds
            self.speeds = self.speeds_text.get()
            if (not self.speeds):
                    tkMessageBox.showerror('Invalid Entry','Please enter at least one speed.')
            else:
                    self.speeds = ''.join(self.speeds.split())
                    self.speeds = self.speeds.split(',')
                    num_speeds = len(self.speeds)
                    #print speeds
            self.bp_widget()


    def bp_widget(self):
            keys = []
            for speed in self.speeds:
                    keys.append(speed)
            self.bp = dict.fromkeys(keys)

            label = Label(self.master, text='Enter the back pressures for each speed, seperated by a comma:')
            label = label.grid(row=1)

            i = 2
            self.bp_text_widgets = []
            for speed in self.speeds:
                    label = Label(self.master, text=speed+'=')
                    label.grid(row=i, column=0)

                    self.bp_text_widgets.append(Entry(self.master))
                    self.bp_text_widgets[i-2].grid(row=i,column=1)
                    self.bp_text_widgets[i-2].bind('<Return>', lambda event, arg=i: self.get_bp(event, arg))
                    i += 1

    def get_bp(self, event, i):
            self.bp[self.speeds[i-2]] = self.bp_text_widgets[i-2].get()
            print self.bp[self.speeds[i-2]]


root = Tk()
MyWindow(root)
root.mainloop()

The question about global variables is easy to answer. As a rule, it's better to avoid them. Saving the values as attributes is the way the great majority of people would do it, judging from what I have seen.

As to what you are doing with the entry widgets, I see a couple of problems. First if I enter fives speeds in the first widget, and press Enter, five new entry widgets come up. So far, so good. However, if I now realize that I made a mistake and go back and delete a speed, I still have 5 entry widgets. I've played around with this, adding and deleting speeds, and I can get the GUI all messed up.

Second, what happens if I forget to press Enter after adding the back pressures, or if I correct a mistake in the back pressures and then forget to press Enter? The problem is that I only see the input, and there is no confirmation of what the app has actually received.

This is a matter of taste, perhaps, but what I like to do is to pop up a dialog for data entry and echo the input data somewhere in the GUI, if possible. So, I would have a button labeled "Input Speeds" that would pop up a dialog where the user would enter the speeds. When he clicked ok, a label would be created with text like "Speeds 70 80 90". There would be an "Edit" button next to the Label, in case the use changed his mind, or wanted to enter a new problem.

For each speed, I would pop up a dialog with instructions like: "Enter the Pressures for Speed 80." When the user clicked OK, a label would appear with the relevant data, and an Edit button.

If I understand you right, the reason that the unused entries are sticking around is that you don't know how to make them disappear. Look up the grid_forget method. For example:

http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/grid-methods.html

I hope I've addressed your questions. Let me know if I've missed something.

EDIT: As to what is the "best way" to do the control flow, of course, I don't have the answer. Personally, I like to have the methods do one thing and return. I would have another function responsible for calling the data-entry methods in the correct sequence. That way, the data-entry methods are separable. I can call them independently if the user realizes that he forgot something, and wants to go back. This is probably possible with your setup also, but interaction is not so clear to me. No doubt, that's largely a matter of what I'm used to. Still, I find that making things independent of one another, as far as possible, simplifies making changes in the future. Also, I don't know how much more there is to the GUI. If what you've shown is pretty much all of the user interaction, then I think what you have will work perfectly fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM