hummeldn
hummeldn

Reputation: 13

Replacing input() portions of a Python program in a GUI

I am pretty new to python and have been working with a program that was originally meant for the command line. As such, it uses the input() function quite a bit, especially in the middle of loops. Mostly, the original code would do something like this:

for item in list:
    # some logic here
    user_input - input("prompt")
    # uses user_input

I've decided I want to make a GUI for it, but I also want to make it optional. I have a class called Viewer that decorates the original program, and I am struggling to figure out how to best to handle the calls to input().

My first thought was just to inject a new input function altogether so that way it looks for input in my GUI text box instead the sys.stdout. I found many Stack Overflow answers on how to print sys.stdout to a GUI element (like this), but not how to take input from one. All of my attempts either ended in freezing the GUI by creating a loop, or the program not pausing for input and simply grabbing what was in the box when the prompt was put forward.

Secondly, I tried to break apart my functions to better match the code examples for buttons. So, button 1 would trigger a function_part_1, which would go until it required an input. If the GUI flag was turned off, it would automatically go to an input function, otherwise it would return and a button press would trigger function_part_2:

def function_part_1(list):
   item = list[0]
   # do some logic on item 1
   if GUI:
      print("prompt")
      return
      # After this, the GUI waits for a button press, and then calls function_part_2
   else:
      function_input(list)


def function_input(list):
   user_input = input("prompt")
   function_part_2(user_input, list)


def function_part_2(user_input, list):
   # uses user_input on item
   list.remove(list[0])
   if list:
      function_part_1(list)
   else:
       return

However, this turned ugly really quickly. First, it broke apart many loops which would require a lot of refactoring to keep the logic in tact (Especially for the nested loops). It also requires that I pass all my local variables from function-part to function-part, which exploded the function headers. In order to have only a single input box for the user, the GUI has to have logic to know which function is executing and what function-part comes next. It made my functions harder to follow and hurt readability.

Is there a nicer way to do this?

I am using appJar for the GUI, which is based around Tkinter. I am willing to switch to pure Tkinter if that would make things easier.

Upvotes: 0

Views: 1288

Answers (1)

Aran-Fey
Aran-Fey

Reputation: 43146

The easiest way is to overwrite the input function. Here's a working example using tkinter:

import tkinter as tk

def input(prompt=''):
    win= tk.Tk()

    label= tk.Label(win, text=prompt)
    label.pack()

    userinput= tk.StringVar(win)
    entry= tk.Entry(win, textvariable=userinput)
    entry.pack()

    # pressing the button should stop the mainloop
    button= tk.Button(win, text="ok", command=win.quit)
    button.pack()

    # block execution until the user presses the OK button
    win.mainloop()

    # mainloop has ended. Read the value of the Entry, then destroy the GUI.
    userinput= userinput.get()
    win.destroy()

    return userinput

print(input('foobar'))

Upvotes: 1

Related Questions