0

I am creating a UI for another python program and it essentially just makes an interactive component to the project. The goal is to have a specific label that updates (print statements) from the python program the button runs. This is what I have programmed in my UI...


import tkinter as ttk
import subprocess
import sys
import time
import os
import tkinter.font as font
from tkinter.ttk import *

app = ttk.Tk()

app.geometry("400x400")
app.configure(bg='gray')

photo = ttk.PhotoImage(file=r"C:\Users\ex\ex_button_active.png")
myFont = font.Font(family='Helvetica', size=20, weight='normal')

ttk.Label(app, text='Ex', bg='gray', font=(
    'Verdana', 15)).pack(side=ttk.TOP, pady=10)
app.iconbitmap(r'C:\Users\ex\ex_icon.ico')


def ex_activation():
    global pro
    print("Running program!")
    pro = subprocess.Popen("python programex.py", shell=True)


def ex_stop():
    global pro
    print("Stopping Program... Please Wait!")
    os.kill(pro.pid, 0)


ex_activation_button = ttk.Button(app, bg='black', image=photo, width=120, height=120, command=ex_activation)

ex_stop_button = ttk.Button(app, bg='Gray', text='Stop Program', width=12, command=ex_stop, height=3)

ex_stop_button['font'] = myFont

app.title("Ex")
ex_activation_button.pack(side=ttk.TOP)
ex_stop_button.pack(side=ttk.LEFT)

# app.mainloop()
while True:
    try:
        app.update()
        app.update_idletasks()
    except KeyboardInterrupt:
        pass

If anyone has an idea of how I can implement This StackOverflow Post into my code, I would greatly appreciate the help and support.

EDIT for acw1668 These are some tests I had run and got some strange numbers in the Pycharm run window instead of the UI.

Running program!
3528
Stopping Program 3528 ... Please Wait!
monitor done
Running program!
144
Stopping Program 144 ... Please Wait!
monitor done
Running program!
14008
Stopping Program 14008 ... Please Wait!
monitor done
Running program!
21748
Stopping Program 21748 ... Please Wait!
monitor done
Redgar Pro
  • 51
  • 7
  • Lol, I recognise this code, at least part of it, from my answer here: https://stackoverflow.com/questions/63388925/writing-a-python-ui-for-a-seperate-program-with-tkinter-the-stop-button-for-thi/63390206 Anyway, that's a cool feature to implement, so let me try something :) – Eric Roy Aug 14 '20 at 21:03
  • @EricRoy Thanks :) I am still very new to tkinter. In case you we're curious, the UI is for a Virtual assistant project. – Redgar Pro Aug 14 '20 at 21:21
  • And the virtual assistant prints data depending on the questions asked. It also speaks but printing data is an important part. – Redgar Pro Aug 14 '20 at 21:41
  • That's a nice thing to work on. I hope my answer will help you to get more into tkinter :) – Eric Roy Aug 14 '20 at 21:47
  • @EricRoy I forgot to at you when I responded lol – Redgar Pro Aug 15 '20 at 00:43
  • The answer to this [question](https://stackoverflow.com/questions/62163329/redirecting-stdout-to-tkinter-immediately-without-waiting-for-the-process-to-co) may help. – acw1668 Aug 15 '20 at 02:17
  • @acw1668 do you think this could apply to the program EricRoy and I wrote? Could you maybe right it in an answer format? I saw the post and it seemed to utilize the sys.stdout method. That would be really great if you could. – Redgar Pro Aug 15 '20 at 03:18

1 Answers1

0

I used a simplified version of your code. To get the print status inside the tkinter app, the easiest way I have thinked of is using a third file: in my case, log.txt.

The daemon.py will log() some output (custom function) to the text file instead of printing it. And in the tkinter mainloop, the app will be constantly checking changes to that file.

Note that with file.readline(), I am just reading one line, as tkinter Labels prefer that. If you plan using more lines, you could change the label to a textarea.

Anyway: here is the code. I hope it'll work inside your project!

# FILE 1: daemon.py

from time import sleep
from sys import exit

def log(s): # This will be the equivalent to the print function
    with open("log.txt", "w") as fileout:
        fileout.write(s)

x = 0
while True:
    try:
        log("seconds elapsed: " + str(x))
        x+=1
        sleep(1)
    except KeyboardInterrupt:
        log("Stopping with x =" + str(x))
        exit()
# FILE 2: log.txt

# You can leave this file empty, but you must create a file named 'log.txt'.
# FILE 3: mainapp.py

import tkinter as tk
import subprocess
import os

def ex_activation():
    print("Running")
    global pro
    pro = subprocess.Popen("python daemon.py", shell=True)

def ex_stop():
    print("Stopped") # I just dont know why, but this print can't be removed.
    global pro
    os.kill(pro.pid, 0)

app = tk.Tk()

ex_activation_button = tk.Button(app, text="run", command=ex_activation)
ex_stop_button = tk.Button(app, text="stop", command=ex_stop)
lab = tk.Label(app, text="If you see this, an error occurred")

with open("log.txt", "w+") as fileinout:
    fileinout.write("Click run to start the execution!")
    lab.config(text=fileinout.readline())

ex_activation_button.pack()
ex_stop_button.pack()
lab.pack()

# app mainloop
while True:
    try:
        app.update()
        app.update_idletasks()
        with open( "log.txt", "r" ) as filein:
            lab.config(text=filein.readline())
    except KeyboardInterrupt:
        pass
Eric Roy
  • 334
  • 5
  • 15
  • Hey Eric Roy :) The program above makes sense to me but my virtual assistant runs infinitely until it hits an error or I press stop. This means that it does have a `whileTrue:` as well making it a little difficult to incorporate into the program. While this does make 100% sense and I do understand how you got there :) I just don't know how to work with 2 whileTrue's? Do you have any ideas? – Redgar Pro Aug 14 '20 at 23:36
  • I mean, I guess I could do a nested while loop but the while loop in my program has an else statement. Would it be possible to make a nested while loop with an else statement. – Redgar Pro Aug 15 '20 at 02:10
  • You just have to put your `while True:` content inside the `try:` statement. Don't forget to replace all `print()` to `log()`. And if your program stops also due to errors, you could note which error is it (ValueError, AttributeError, ...) and add it to the except statement, so you dont get the ugly exception on screen. – Eric Roy Aug 15 '20 at 07:06
  • Hi Eric, thanks for the response. I just wanted to mention that it measures the seconds elapsed while running the program and puts and updates it in log.txt However, the idea was to get any print statement from the daemon.py to show up on the gui as a label. Was I using the program wrong? I deeply apologize for using your time up with this post, I'm learning more about tkinter everyday :) – Redgar Pro Aug 15 '20 at 07:25
  • Yes that's why I created the function log(), and if you replace it in all the print() in your daemon, it will show up every line on the label. – Eric Roy Aug 15 '20 at 11:08
  • Hi @EricRoy this didn't work with the label and the label stayed the same. – Redgar Pro Aug 15 '20 at 16:37
  • do you think it would work if both programs were one file and I just put the program under the `def ex_activation` function created earlier? – Redgar Pro Aug 15 '20 at 17:18
  • You could do it with threads, but I don't have enough knowdelage to show you an example... And I tried again the 3 files i put on the answer and it's working for me: what issue you are guetting? – Eric Roy Aug 15 '20 at 17:47
  • The program runs and all but the python ide grays out most imports when I put your example inside the virtual assistant py file. I would have to put it at the top as a default for all prints --> log to go to the txt file. The problem is the exit at the end and the overall method used to redirect the prints. It basically grays put all imports and claims them to be unused. If you need more info, I can try again to get the exact results. I'm happy to provide – Redgar Pro Aug 15 '20 at 18:23
  • If I understand correctly, you culd remove the `while True:` from the daemon.py and put all your code (except imports) inside the try statement. Then just replace prints to logs (or maybe look for how to overwrite print function), and I can't see where it would fail. If this isn't what you are asking, i still don't get it then :S – Eric Roy Aug 15 '20 at 21:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219887/discussion-between-eric-roy-and-redgar-pro). – Eric Roy Aug 15 '20 at 21:33