0

I'm trying to use Tkinter to start a while loop when pressing the "on" button from an option menu and stop that while loop when pressing the "off" from the option menu. The while loop is infinite but I was hoping I could press "off" and it would exit the program. I've looked up another way to do this using try: and keyboardInterruption but I'm really trying to have it stop using the "off" button. Not sure how to code to make the off button work, any suggestions/solutions ? (I'm new to coding, thanks)

import psutil, os, random, webbrowser, sys
import tkinter import*
import tkinter import tkk
import tkinter
import ttkbootstrap as tk
import keyboard


window=tk.Window()

window.title("Channel App")
window.geometry('400x300')

def Selected(event):
 if clicked.get()== 'On':
  Start_Channel()
 else:
  clicked.get() == 'Off'
 Stop_Channel()

options = [
  'On',
  'Off'
]

clicked = StringVar()
clicked.set(options[0])

drop= OptionMenu(window, clicked, *options, command= Selected)
drop.grid(row=3,column=0) 

def Stop_Channel():
 print("Stop Channel")
 sys.exit(0)

def Start_Channel():
  while True:
    vlc_found = any('vlc.exe' in proc.name() for proc in psutil.process_iter())
    print(vlc_found)

    if (vlc_found == False):
      
      print("Did not find")
      basedir=("C:\\Users\\..")

      file = random.choice([x for x in os.listdir(basedir) if os.path.isfile(os.path.join(basedir, x))])

      print("Playing file {}...".format(file))

      webbrowser.open(os.path.join(basedir, file))

     elif(Selected(event)==clicked.get()=="Off"):
         Stop_Channel()

window.mainloop()
4
  • Once you enter that while loop, the body of that loop is the only thing your program is doing. Your GUI is completely dead at that point, because there's nothing inside the loop that is handling mouse or keyboard events. So this is simply not a workable approach in a GUI program. The general solution in Tkinter is to do one step of the work in your function, then instead of looping, use .after() to schedule another call to the function in the near future (perhaps 100-500 milliseconds later). Since the function isn't running continuously, it doesn't block event handling. Commented Jan 8, 2024 at 4:10
  • Thanks for the information @jasonharper, I'm going to look more into .after(). So should I break down the steps in Start_Channel() function to various functions so that .after() can be utilized or should I just plug in the .after() somewhere into the current code? Commented Jan 8, 2024 at 5:05
  • 1
    What is the purpose of the while loop actually? Also you cannot terminate the process created by webbrowser.open() by justing terminate the current tkinter application. Commented Jan 8, 2024 at 5:58
  • @acw1668 The purpose of the while loop is to constantly check if VLC is playing, If it is playing then don't do anything but if it's not playing then go to a specific folder and randomly open/play a file (which opens VLC). I changed the setting on VLC so once a video is done playing it automatically closes VLC. So if VLC isn't playing then the video is done playing and it will then go back to the folder and randomly choose a video file and start playing again. I'm not concerned about terminating the process for the webbrowser its more about the overall loop to stop opening VLC when its not on Commented Jan 8, 2024 at 6:19

2 Answers 2

1

As said in the comment, it is better to avoid using while loop in a tkinter application as it will block the tkinter.mainloop() from handling pending events and updates. Use .after() to replace the while loop.

def Start_Channel():
     vlc_found = any('vlc.exe' in proc.name() for proc in psutil.process_iter())
     print(vlc_found)

     if (vlc_found == False):

          print("Did not find")
          basedir = "..."

          file = random.choice([x for x in os.listdir(basedir) if os.path.isfile(os.path.join(basedir, x))])

          print("Playing file {}...".format(file))

          webbrowser.open(os.path.join(basedir, file))
     # run Start_Channel() one second later
     window.after(1000, Start_Channel)

Also the Selected() should be modified as below:

def Selected(value):
     if value == 'On':
          Start_Channel()
     else:
          Stop_Channel()
Sign up to request clarification or add additional context in comments.

1 Comment

Appreciate you it works properly now with the code you provided, also thank you for fixing Selected() for me :)
1

There are two solutions.

You could actually place break to exit the while loop,or you could use a condition in the while loop.because sys.exit will stop the entire program.From your question I think you need to close the while loop only, not the window. So,

1.Break Approach

def Start_Channel():
    while True:
        vlc_found = any('vlc.exe' in proc.name() for proc in psutil.process_iter())
        print(vlc_found)

        if not vlc_found:
            print("Did not find")
            basedir = "C:\\Users\\.."
            file = random.choice([x for x in os.listdir(basedir) if os.path.isfile(os.path.join(basedir, x))])
            print("Playing file {}...".format(file))
            webbrowser.open(os.path.join(basedir, file))
        elif Selected(event) == clicked.get() == "Off":
            break

2.Condition approach:

def Start_Channel():
    stop_channel = False
    while not stop_channel:
        ... #your other code
        elif Selected(event) == clicked.get() == "Off":
            stop_channel = True

WARNING

I am only answering the question you asked, but to be honest the approach you are using can make your app completely non responsive, as jasonharper said when you run a while loop before tkinter.mainloop() it will prevent event triggering and updates(non responsive window,and will surely crash) so the best approach is to make your code use threading(ref : threading Gfg) module or use .after()(ref : after method) method, else your app is not going to work properly.Its because tkinter was made to work like that.Also you can use subprocess module to open the VLC instead of using webbrowser module(or that could also cause errors).

2 Comments

Note that Selected() returns None, so Selected(event) == clicked.get() == "Off" will be always False. Note also that event is undefined in Start_Channel().
Thank you so much for pointing that out I was a bit confused why it wasn't working for that part :). I do appreciate both approaches

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.