Tkinter GUI Not responding when running tasks - python
I am trying to make an antivirus application but I met some issue. After I press the button Scan Now, the GUI become unclickable and Not Responding but the program still running in my IDE. I am thinking to limit the file read per second, is this possible? Or is there any others solution for this issue?
This is my coding:
def md5(self,fname):
hash_md5 = hashlib.md5()
try:
with open(fname, "rb") as f:
for chunk in iter(lambda: f.read(2 ** 20), b""):
hash_md5.update(chunk)
except Exception:
pass
return hash_md5.hexdigest()
def get_all_abs_paths(self,rootdir):
self.progressBar['value'] = self.progressBar['value'] + 5
viruslist = open('C:\FYP\SecuCOM2022\compile.txt','rt')
virusinside = [l.rstrip() for l in viruslist]
paths = list()
virus="detected"
novirus="clear"
for dirpath,_,filenames in os.walk(rootdir):
for f in filenames:
paths.append(os.path.abspath(os.path.join(dirpath, f)))
def getfile(self):
file2=('C:/Windows/System32')
file3=('C:/Program Files')
self.status.set("Scanning...")
self.progressBar['value'] = self.progressBar['value'] + 5
self.get_all_abs_paths(file3)
self.progressBar['value'] = self.progressBar['value'] + 45
self.get_all_abs_paths(file2)
self.status.set("Finished Scan")
self.progressBar['value'] = self.progressBar['value'] + 50
You need to create a thread to run the scan function, see https://pypi.org/project/pythread/
What happens right now is that the Tkinter gui and the scan function run in the same unique thread of your application, so when scan function is running the gui is not, which is why it freeze.
By creating a thread you will have both the gui and the scan function running at the same time in parallel.
Tkinter GUIs have a mainloop and calling any function will cause it to pause until the function is exited.
You can run the function on a seperate thread to allow the rest of the GUI to work while a task is being done.
GUI toolkits like tkinter are event-driven. To work properly, the
mainloop must continuously be able to process keyboard and mouse events.
When it is not processing events, it will start scheduled idle tasks.
So they work quite differently from normal Python scripts that just run from
top to bottom.
A tkinter program runs within the mainloop. So there are only three
things you do before starting the mainloop.
Create a window with some widgets on it.
Create objects (variables) that hold program state.
Define functions that can be run from the mainloop as callbacks or
idle tasks.
A callback is called in response to activating a control (like clicking on
a button).
An idle task is started by the system after a specified number of milliseconds
when the system is not busy processing events. You can schedule idle tasks
with the Tk.after() method.
Basically, the callbacks and idle tasks are your program.
To keep the GUI responsive, callbacks and idle tasks should not take too long; say 50 ms.
So running a complete antivirus scan in a single callback will indeed make the GUI unresponsive.
Basically, there are three possible solutions.
Chop up the scanning process in small pieces. The first piece is executed in a callback, the rest is done in idle tasks. This is probable the most simple solution, because it makes it easy to update the GUI when you're scannning.
Use multiprocessing to start a separate program to do the scanning. Set up a Pipe to enable communication between the GUI and scanning programs. Use an idle task in the GUI to read messages from the pipe and update the GUI accordingly.
Use threading to start a seperate thread to do the scanning. There is some confusion as to tkinter being actually safe to call from multiple threads; see the example below.
Which one works best for you hard for me to say.
Below I give examples for 1 and 3 in the form of a tkinter script to unlock ms-excel files.
First the none-threaded version:
"""Remove passwords from modern excel 2007+ files (xlsx, xlsm)."""
from types import SimpleNamespace
import os
import re
import shutil
import stat
import sys
import zipfile
from tkinter import filedialog
from tkinter import ttk
from tkinter.font import nametofont
import tkinter as tk
__version__ = "2020.04.20"
def create_widgets(root):
"""Create the window and its widgets.
Arguments:
root: the root window.
Returns:
A SimpleNamespace of widgets
"""
# Set the font.
default_font = nametofont("TkDefaultFont")
default_font.configure(size=12)
root.option_add("*Font", default_font)
# General commands and bindings
root.bind_all('q', do_exit)
root.wm_title('Unlock excel files v' + __version__)
root.columnconfigure(3, weight=1)
root.rowconfigure(5, weight=1)
# A SimpleNamespace is used to save widgets that need to be accessed later.
w = SimpleNamespace()
# First row
ttk.Label(root, text='(1)').grid(row=0, column=0, sticky='ew')
fb = ttk.Button(root, text="Select file", command=do_file)
fb.grid(row=0, column=1, columnspan=2, sticky="w")
w.fb = fb
fn = ttk.Label(root)
fn.grid(row=0, column=3, columnspan=2, sticky="ew")
w.fn = fn
# Second row
ttk.Label(root, text='(2)').grid(row=1, column=0, sticky='ew')
backup = tk.IntVar()
backup.set(0)
w.backup = backup
ttk.Checkbutton(root, text='backup', variable=backup,
command=on_backup).grid(row=1, column=1, sticky='ew')
suffixlabel = ttk.Label(root, text='suffix:')
suffixlabel['state'] = 'disabled'
suffixlabel.grid(row=1, column=2, sticky='ew')
w.suffixlabel = suffixlabel
suffix = tk.StringVar()
suffix.set('-orig')
w.suffix = suffix
se = ttk.Entry(root, justify='left', textvariable=suffix)
se.grid(row=1, column=3, columnspan=1, sticky='w')
se['state'] = 'disabled'
w.suffixentry = se
# Third row
ttk.Label(root, text='(3)').grid(row=2, column=0, sticky='ew')
gobtn = ttk.Button(root, text="Go!", command=do_start)
gobtn['state'] = 'disabled'
gobtn.grid(row=2, column=1, sticky='ew')
w.gobtn = gobtn
# Fourth row
ttk.Label(root, text='(4)').grid(row=3, column=0, sticky='ew')
ttk.Label(root, text='Progress:').grid(row=3, column=1, sticky='w')
# Fifth row
sb = tk.Scrollbar(root, orient="vertical")
status = tk.Listbox(root, width=40, yscrollcommand=sb.set)
status.grid(row=4, rowspan=5, column=1, columnspan=3, sticky="nsew")
w.status = status
sb.grid(row=4, rowspan=5, column=5, sticky="ns")
sb.config(command=status.yview)
# Ninth row
ttk.Button(root, text="Quit", command=do_exit).grid(row=9, column=1, sticky='ew')
# Return the widgets that need to be accessed.
return w
def create_state():
"""Create and initialize the global state."""
state = SimpleNamespace()
state.interval = 10
state.path = ''
state.inzf, state.outzf = None, None
state.infos = None
state.currinfo = None
state.worksheets_unlocked = 0
state.workbook_unlocked = False
state.directory = None
state.remove = None
return state
def statusmsg(text):
"""Append a message to the status listbox, and make sure it is visible."""
widgets.status.insert(tk.END, text)
widgets.status.see(tk.END)
# Step functions to call in the after() method.
def step_open_zipfiles():
path = widgets.fn['text']
state.path = path
statusmsg(f'Opening “{path}”...')
first, last = path.rsplit('.', maxsplit=1)
if widgets.backup.get():
backupname = first + widgets.suffix.get() + '.' + last
else:
backupname = first + '-orig' + '.' + last
state.remove = backupname
shutil.move(path, backupname)
state.inzf = zipfile.ZipFile(backupname, mode="r")
state.outzf = zipfile.ZipFile(
path, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=1
)
root.after(state.interval, step_discover_internal_files)
def step_discover_internal_files():
statusmsg(f'Reading “{state.path}”...')
state.infos = [name for name in state.inzf.infolist()]
state.currinfo = 0
statusmsg(f'“{state.path}” contains {len(state.infos)} internal files.')
root.after(state.interval, step_filter_internal_file)
def step_filter_internal_file():
current = state.infos[state.currinfo]
stat = f'Processing “{current.filename}” ({state.currinfo+1}/{len(state.infos)})...'
statusmsg(stat)
# Doing the actual work
regex = None
data = state.inzf.read(current)
if b'sheetProtect' in data:
regex = r'<sheetProtect.*?/>'
statusmsg(f'Worksheet "{current.filename}" is protected.')
elif b'workbookProtect' in data:
regex = r'<workbookProtect.*?/>'
statusmsg('The workbook is protected')
else:
state.outzf.writestr(current, data)
if regex:
text = data.decode('utf-8')
newtext = re.sub(regex, '', text)
if len(newtext) != len(text):
state.outzf.writestr(current, newtext)
state.worksheets_unlocked += 1
statusmsg(f'Removed password from "{current.filename}".')
# Next iteration or next step.
state.currinfo += 1
if state.currinfo >= len(state.infos):
statusmsg('All internal files processed.')
state.currinfo = None
root.after(state.interval, step_close_zipfiles)
else:
root.after(state.interval, step_filter_internal_file)
def step_close_zipfiles():
statusmsg(f'Writing “{state.path}”...')
state.inzf.close()
state.outzf.close()
state.inzf, state.outzf = None, None
root.after(state.interval, step_finished)
def step_finished():
if state.remove:
os.chmod(state.remove, stat.S_IWRITE)
os.remove(state.remove)
state.remove = None
else:
statusmsg('Removing temporary file')
statusmsg(f'Unlocked {state.worksheets_unlocked} worksheets.')
statusmsg('Finished!')
widgets.gobtn['state'] = 'disabled'
widgets.fn['text'] = ''
state.path = ''
# Widget callbacks
def do_file():
"""Callback to open a file"""
if not state.directory:
state.directory = ''
available = [os.environ[k] for k in ('HOME', 'HOMEDRIVE') if k in os.environ]
if available:
state.directory = available[0]
fn = filedialog.askopenfilename(
title='Excel file to open',
parent=root,
defaultextension='.xlsx',
filetypes=(
('excel files', '*.xls*'), ('all files', '*.*')
),
)
if not fn:
return
state.directory = os.path.dirname(fn)
state.worksheets_unlocked = 0
state.workbook_unlocked = False
state.path = fn
widgets.fn['text'] = fn
widgets.gobtn['state'] = 'enabled'
widgets.status.delete(0, tk.END)
def on_backup():
if widgets.backup.get() == 1:
widgets.suffixlabel['state'] = 'enabled'
widgets.suffixentry['state'] = 'enabled'
else:
widgets.suffixlabel['state'] = 'disabled'
widgets.suffixentry['state'] = 'disabled'
def do_start():
root.after(state.interval, step_open_zipfiles)
def do_exit(arg=None):
"""
Callback to handle quitting.
"""
root.destroy()
if __name__ == '__main__':
# Detach from the command line on UNIX systems.
if os.name == 'posix':
if os.fork():
sys.exit() # Create the GUI window.
root = tk.Tk(None)
# Use a dialog window so that it floats even when using a tiling window
# manager.
root.attributes('-type', 'dialog')
# Don't show hidden files in the file dialog
# https://stackoverflow.com/questions/53220711/how-to-avoid-hidden-files-in-file-picker-using-tkinter-filedialog-askopenfilenam
try:
# call a dummy dialog with an impossible option to initialize the file
# dialog without really getting a dialog window; this will throw a
# TclError, so we need a try...except :
try:
root.tk.call('tk_getOpenFile', '-foobarbaz')
except tk.TclError:
pass
# now set the magic variables accordingly
root.tk.call('set', '::tk::dialog::file::showHiddenBtn', '1')
root.tk.call('set', '::tk::dialog::file::showHiddenVar', '0')
except Exception:
pass
# Widgets is a namespace of widgets that needs to be accessed by the callbacks.
# State is a namespace of the global state.
widgets = create_widgets(root)
state = create_state()
root.mainloop()
Then the version that uses threads:
"""Remove passwords from modern excel 2007+ files (xlsx, xlsm).
This is a multithreaded version of unlock-excel.pyw. All the work that was
there done in steps in the mainloop is now done in a single additional thread.
There is some confusion whether tkinter is thread-safe. That is, if one can
call tkinter functions and methods from any but the main thread. The
documentation for Python 3 says “yes”. Comments in the C source code for
tkinter say “its complicated” depending on how tcl is built. *Many* online
sources say “no”, but that could just be an echo chamber effect.
The author has tested this code on FreeBSD 12.1-STABLE amd64 using CPython
3.7.7 combined with a tcl built with threading enabled. There at least it
seems to work without problems.
"""
from types import SimpleNamespace
import os
import re
import shutil
import stat
import sys
import threading
import zipfile
from tkinter import filedialog
from tkinter import ttk
from tkinter.font import nametofont
import tkinter as tk
__version__ = "2020.04.27"
def create_widgets(root):
"""Create the window and its widgets.
Arguments:
root: the root window.
Returns:
A SimpleNamespace of widgets
"""
# Set the font.
default_font = nametofont("TkDefaultFont")
default_font.configure(size=12)
root.option_add("*Font", default_font)
# General commands and bindings
root.bind_all('q', do_exit)
root.wm_title('Unlock excel files v' + __version__)
root.columnconfigure(3, weight=1)
root.rowconfigure(5, weight=1)
# A SimpleNamespace is used to save widgets that need to be accessed later.
w = SimpleNamespace()
# First row
ttk.Label(root, text='(1)').grid(row=0, column=0, sticky='ew')
fb = ttk.Button(root, text="Select file", command=do_file)
fb.grid(row=0, column=1, columnspan=2, sticky="w")
w.fb = fb
fn = ttk.Label(root)
fn.grid(row=0, column=3, columnspan=2, sticky="ew")
w.fn = fn
# Second row
ttk.Label(root, text='(2)').grid(row=1, column=0, sticky='ew')
backup = tk.IntVar()
backup.set(0)
w.backup = backup
ttk.Checkbutton(root, text='backup', variable=backup,
command=on_backup).grid(row=1, column=1, sticky='ew')
suffixlabel = ttk.Label(root, text='suffix:')
suffixlabel['state'] = 'disabled'
suffixlabel.grid(row=1, column=2, sticky='ew')
w.suffixlabel = suffixlabel
suffix = tk.StringVar()
suffix.set('-orig')
w.suffix = suffix
se = ttk.Entry(root, justify='left', textvariable=suffix)
se.grid(row=1, column=3, columnspan=1, sticky='w')
se['state'] = 'disabled'
w.suffixentry = se
# Third row
ttk.Label(root, text='(3)').grid(row=2, column=0, sticky='ew')
gobtn = ttk.Button(root, text="Go!", command=do_start)
gobtn['state'] = 'disabled'
gobtn.grid(row=2, column=1, sticky='ew')
w.gobtn = gobtn
# Fourth row
ttk.Label(root, text='(4)').grid(row=3, column=0, sticky='ew')
ttk.Label(root, text='Progress:').grid(row=3, column=1, sticky='w')
# Fifth row
sb = tk.Scrollbar(root, orient="vertical")
status = tk.Listbox(root, width=60, yscrollcommand=sb.set)
status.grid(row=4, rowspan=5, column=1, columnspan=3, sticky="nsew")
w.status = status
sb.grid(row=4, rowspan=5, column=5, sticky="ns")
sb.config(command=status.yview)
# Ninth row
ttk.Button(root, text="Quit", command=do_exit).grid(row=9, column=1, sticky='ew')
# Return the widgets that need to be accessed.
return w
def create_state():
"""Create and initialize the global state."""
st = SimpleNamespace()
st.directory = None
return st
def statusmsg(text):
"""Append a message to the status listbox, and make sure it is visible."""
widgets.status.insert(tk.END, text)
widgets.status.see(tk.END)
def process_zipfile_thread():
"""Function to process a zip-file. This is to be run in a thread."""
path = widgets.fn['text']
statusmsg(f'Opening “{path}”...')
first, last = path.rsplit('.', maxsplit=1)
if widgets.backup.get():
backupname = first + widgets.suffix.get() + '.' + last
remove = None
else:
backupname = first + '-orig' + '.' + last
remove = backupname
shutil.move(path, backupname)
with zipfile.ZipFile(backupname, mode="r") as inzf, \
zipfile.ZipFile(
path, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=1
) as outzf:
statusmsg(f'Reading “{path}”...')
infos = [name for name in inzf.infolist()]
statusmsg(f'“{path}” contains {len(infos)} internal files.')
worksheets_unlocked = 0
for idx, current in enumerate(infos, start=1):
smsg = f'Processing “{current.filename}” ({idx}/{len(infos)})...'
statusmsg(smsg)
# Doing the actual work
regex = None
data = inzf.read(current)
if b'sheetProtect' in data:
regex = r'<sheetProtect.*?/>'
statusmsg(f'Worksheet "{current.filename}" is protected.')
elif b'workbookProtect' in data:
regex = r'<workbookProtect.*?/>'
statusmsg('The workbook is protected')
else:
outzf.writestr(current, data)
if regex:
text = data.decode('utf-8')
newtext = re.sub(regex, '', text)
if len(newtext) != len(text):
outzf.writestr(current, newtext)
worksheets_unlocked += 1
statusmsg(f'Removed password from "{current.filename}".')
statusmsg('All internal files processed.')
statusmsg(f'Writing “{path}”...')
if remove:
os.chmod(remove, stat.S_IWRITE)
os.remove(remove)
else:
statusmsg('Removing temporary file')
statusmsg(f'Unlocked {state.worksheets_unlocked} worksheets.')
statusmsg('Finished!')
widgets.gobtn['state'] = 'disabled'
widgets.fn['text'] = ''
# Widget callbacks
def do_file():
"""Callback to open a file"""
if not state.directory:
state.directory = ''
available = [os.environ[k] for k in ('HOME', 'HOMEDRIVE') if k in os.environ]
if available:
state.directory = available[0]
fn = filedialog.askopenfilename(
title='Excel file to open',
parent=root,
defaultextension='.xlsx',
filetypes=(('excel files', '*.xls*'), ('all files', '*.*')),
)
if not fn:
return
state.directory = os.path.dirname(fn)
state.worksheets_unlocked = 0
state.workbook_unlocked = False
widgets.fn['text'] = fn
widgets.gobtn['state'] = 'enabled'
widgets.status.delete(0, tk.END)
def on_backup():
if widgets.backup.get() == 1:
widgets.suffixlabel['state'] = 'enabled'
widgets.suffixentry['state'] = 'enabled'
else:
widgets.suffixlabel['state'] = 'disabled'
widgets.suffixentry['state'] = 'disabled'
def do_start():
worker = threading.Thread(target=process_zipfile_thread)
worker.start()
def do_exit(arg=None):
"""
Callback to handle quitting.
"""
root.destroy()
if __name__ == '__main__':
# Detach from the command line on UNIX systems.
if os.name == 'posix':
if os.fork():
sys.exit()
# Create the GUI window.
root = tk.Tk(None)
# Use a dialog window so that it floats even when using a tiling window manager.
if os.name == 'posix':
root.attributes('-type', 'dialog')
# Don't show hidden files in the file dialog
# https://stackoverflow.com/questions/53220711/how-to-avoid-hidden-files-in-file-picker-using-tkinter-filedialog-askopenfilenam
try:
# call a dummy dialog with an impossible option to initialize the file
# dialog without really getting a dialog window; this will throw a
# TclError, so we need a try...except :
try:
root.tk.call('tk_getOpenFile', '-foobarbaz')
except tk.TclError:
pass
# now set the magic variables accordingly
root.tk.call('set', '::tk::dialog::file::showHiddenBtn', '1')
root.tk.call('set', '::tk::dialog::file::showHiddenVar', '0')
except Exception:
pass
# Widgets is a namespace of widgets that needs to be accessed by the callbacks.
# State is a namespace of the global state.
widgets = create_widgets(root)
state = create_state()
root.mainloop()
You can use threading.Thread to run a function.
threading.Thread it's like to open a new cpu thread to run the function
from threading import Thread
# Code....
Thread(target=function()).start
It has some differences between start and start(). start Starts executing the function If a some button or some command says tells the threading to run. But start() executes while the code running.
Related
Tkinter doesn't stop execution even after closing the window
In this code I am displaying a window with a label and two buttons, the user can read the file path currently in use in the label and decide if he wants to change it or continue with the buttons. If he wants to change the path then I change the value inside the textvariable inside the label. The issue is when I do this for example 3 times, then I have to kill the program manually pressing CTRL + C 3 times. If I do it once then I only need to do it once, like there is something preventing the program to terminate. Thanks for the help. def select_file(): #OPEN TKINTER DIALOG LETTING ME TO CHOOSE A FILE AND RETURNS ITS PATH. tkinter.Tk().withdraw() # prevents an empty tkinter window from appearing folder_path = filedialog.askopenfilename() return folder_path def change_file(): file_path = select_file() with open(os.path.expanduser('~/Documents/Squad Generator/excel_file_path.txt'), 'w') as f: f.write(file_path) #WRITES THE FILE PATH IN THE VARIABLE AND CHANGES THE LABEL var.set(file_path) def homepage(): app.geometry("450x200") app.title("Squad Generator") label_1 = tkinter.Label(master=app, justify=tkinter.LEFT,textvariable=var, font = ("JetBrains Mono", 15)) label_1.grid(row=0, column=0, columnspan=2, padx=20, pady=(20, 0), sticky="nsew") button1 = tkinter.Button(master=app, text="Continua", command=main) button1.grid(row=1, column=0, padx=20, pady=20, sticky="ew") #BUTTON CAUSING THE ISSUE. button2 = tkinter.Button(master=app, command=change_file, text="Cambia file") button2.grid(row=1, column=1, padx=0, pady=20, sticky="ew") #CREATING APP MAINLOOP app.mainloop() if __name__ == '__main__': app = tkinter.Tk() var = tkinter.StringVar(None, check_path_file()) homepage()
I've reorganized your application a bit. I've wrapped everything in a basic class named App which serves as the root of your application. This should work as expected and keep everything contained within a single instance of Tk. import tkinter from tkinter.filedialog import askopenfilename class App(tkinter.Tk): # the App class inherits from Tk def __init__(self): super().__init__() # initialize Tk self.geometry("450x200") self.title("Squad Generator") self.label_var=tkinter.StringVar(self, 'Select a file path') self.label_1 = tkinter.Label( master=self, justify=tkinter.LEFT, textvariable=self.label_var, font=("JetBrains Mono", 15) ) self.label_1.grid(row=0, column=0, columnspan=2, padx=20, pady=(20, 0), sticky="nsew") self.button1 = tkinter.Button(master=self, text="Continua", command=main) self.button1.grid(row=1, column=0, padx=20, pady=20, sticky="ew") self.button2 = tkinter.Button( master=self, command=self.change_file, text="Cambia file" ) self.button2.grid(row=1, column=1, padx=0, pady=20, sticky="ew") self.change_file() # call this function when the app starts def change_file(self): file_path = askopenfilename() if file_path: # if a file is selected and not cancelled by the user input_file = '~/Documents/Squad Generator/excel_file_path.txt' with open(os.path.expanduser(input_file), 'w') as f: f.write(file_path) # write out 'file_path' to the 'input_file' #WRITES THE FILE PATH IN THE VARIABLE AND CHANGES THE LABEL self.label_var.set(file_path) else: self.label_var.set('Select a file path') # fallback text if user cancels if __name__ == '__main__': app = App() # instantiate your App class app.mainloop() # run the app If you're not familiar with classes, the word self here can be confusing. All you need to know is that self refers to the App class. So any widgets you define within that class, like button1 have self as their master - i.e., they are children of the App class! Also, any methods you define within the class (like change_file(self)) will be callable within the class via self.change_file, and will have access to any variables in the class, like self.label_var!
From what I see you just did app.destroy() But you actually need to do app.master.destroy() With the first line you're just destroying the current frame, not the the top level container.
The application called an interface that was marshalled for a different thread error
I am writing a code to compare two parts in catia. I used win32com.client to read the document. I am also using threading to run the progress bar. for some reason, I am getting below error even though I have defined document_new as global. Error: Exception in thread Thread-22: Traceback (most recent call last): File "D:\VirtualEnv\env\lib\threading.py", line 926, in _bootstrap_inner self.run() File "D:\VirtualEnv\env\lib\threading.py", line 870, in run self._target(*self._args, **self._kwargs) File "C:\Users\as11114\AppData\Local\Temp\ipykernel_16240\2501566542.py", line 62, in STD_Compare if document_old == document_new: File "D:\VirtualEnv\env\lib\site-packages\win32com\client\dynamic.py", line 290, in __eq__ return self._oleobj_ == other pywintypes.com_error: (-2147417842, 'The application called an interface that was marshalled for a different thread.', None, None) Here is the code. Method STD_Check in class will first read one document and method STD_Compare will then read next document. If both documents are same then user gets a message that both documents are same. import os import win32com.client import numpy as np import pandas as pd import openpyxl import threading from tkinter import * from tkinter import ttk from tkinter import messagebox # import pythoncom to enable 'win32com.client.Dispatch' in Threads import pythoncom # import psutil to check whether any application is open or not, in this case checking Catia import psutil class STD_Check_compare(): def __init__(self): #Form self.root = Tk() self.root.geometry("700x200") self.root.title("STD Check") self.mainframe = ttk.Frame(self.root, padding="3 3 12 12") self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1) #========================================================================================================== def STD_Check(self): self.STD_Check_button["state"] = "disable" global document_new # defined as global pythoncom.CoInitialize() # to enable 'win32com.client.Dispatch' in Threads Cat = win32com.client.Dispatch('catia.application') Cat.Visible = True Cat.DisplayFileAlerts = True document_new = Cat.ActiveDocument #calling the active document self.progressbar_STDCheck.start() #Do something self.progressbar_STDCheck.stop() messagebox.showinfo("", "STD Check done for parameters. Excel created and stored in defined folder") self.STD_Check_button["state"] = "normal" def STD_Compare(self): self.STD_Compare_button["state"] = "disable" pythoncom.CoInitialize() Cat = win32com.client.Dispatch('catia.application') Cat.Visible = True Cat.DisplayFileAlerts = True document_old = Cat.ActiveDocument if document_old == document_new: messagebox.showinfo("", "You have selected same STD file. Select old STD to compare") self.STD_Compare_button["state"] = "normal" return self.progressbar_STDCompare.start() #do something self.progressbar_STDCompare.stop() messagebox.showinfo("", "STD Comparison done in DMU") self.STD_Compare_button["state"] = "normal" def Thread_STD_Check(self): threading.Thread(target=self.STD_Check, daemon=True).start() # Calling STD_Check method def Thread_STD_Compare(self): threading.Thread(target=self.STD_Compare, daemon=True).start() # Calling STD_Compare method #---------------Buttons, labels and Progress bars in Frame-------------------------- def display(self): #Create Label "Enter path" self.Label_path = ttk.Label(self.mainframe, text="Enter path to store Excel files").grid(column=1, row=3, sticky=W) #Create Entrybox for Enter path self.storepath = StringVar() self.path_entry = ttk.Entry(self.mainframe, width=50, textvariable = self.storepath) self.path_entry.grid(column=1, row=4, sticky=W) #Create Label "STD Check" self.Label1 = ttk.Label(self.mainframe, text="Press submit button for STD Check").grid(column=1, row=7, sticky=W) #Create Submit Button for STD Check (Call thread for STD Check) self.STD_Check_button = ttk.Button(self.mainframe, text="Submit", command=self.Thread_STD_Check) #to check STD self.STD_Check_button.grid(column=2, row=7, sticky=W) #Create Label "STD Compare" self.Label2 = ttk.Label(self.mainframe, text="Press submit button to compare STD").grid(column=1, row=10, sticky=W) #Create Submit Button for STD Comparison(Call thread for STD Comparison) self.STD_Compare_button = ttk.Button(self.mainframe, text="Submit", command=self.Thread_STD_Compare) #to compare STD self.STD_Compare_button.grid(column=2, row=10, sticky=W) self.progressbar_STDCheck = ttk.Progressbar(self.mainframe, style='text.Horizontal.TProgressbar', mode='indeterminate', length=150) self.progressbar_STDCheck.grid(column = 3, row = 7) self.progressbar_STDCompare = ttk.Progressbar(self.mainframe, style='text.Horizontal.TProgressbar', mode='indeterminate', length=150) self.progressbar_STDCompare.grid(column = 3, row = 10) self.root.mainloop() start = STD_Check_compare() start.display()
python tkinter infinite loop freezes
im currently making a tkinter app that auto backup files by watching the last mod time of the file. code is as below. from tkinter import * from tkinter import filedialog import shutil import os from datetime import datetime app = Tk() app.title("7dtd auto backup") app.geometry("800x500") stopsign = False def stop(): global stopsign stopsign = True def getSrcFolderPath(): folder_selected = filedialog.askopenfilename() src_text.set(folder_selected) def getDstFolderPath(): folder_selected = filedialog.askdirectory() dst_text.set(folder_selected) def watchpath(file, folder): global stopsign file = file.get() folder = folder.get() backupnum = 1 if file != "": lastmtime = os.path.getmtime(file) while stopsign == False: if os.path.getmtime(file) != lastmtime: lastmtime = os.path.getmtime(file) shutil.copytree(os.path.dirname(file), folder+"/backup"+str(backupnum)) backupnum += 1 src_text = StringVar() #source file selector src_label = Label(app, text="save file") src_label.grid(row=0, column=0) src_entry = Entry(app, textvariable=src_text) src_entry.grid(row=0, column=1) src_btn = Button(app, text="browse", width=12, command=lambda: getSrcFolderPath()) src_btn.grid(row=0, column=2) dst_text = StringVar() dst_label = Label(app, text="destination folder") dst_label.grid(row=0, column=3) dst_entry = Entry(app, textvariable=dst_text) dst_entry.grid(row=0, column=4) dst_btn = Button(app, text="browse", width=12, command=lambda: getDstFolderPath()) dst_btn.grid(row=0, column=5) start_btn = Button(app, text="start", width=12, command=lambda: watchpath(src_text, dst_text)) start_btn.grid(row=2, column=0) end_btn = Button(app, text="end", width=12, command=lambda: stop()) end_btn.grid(row=2, column=1) app.mainloop() the idea is that im setting an infinite loop for the function to keep fetching the last mod time of the file until it has changed, but doing so will make my app freeze during the loop, which means that the app can only be stopped by closing. Is there a way to make it not freeze while still maintaining the function of detecting the change in last mod time? Much appreciated!
The tkinter package offers a widget.after() function that allows you to call a function of your choice after a delay of X milliseconds. It's signature is as follows: w.after(delay_ms, callback=None, *args_for_your_function) To make your code work, you can declare lastmtime as a global variable at the start of your code and then change your watchpatch function to the following: def watchpath(fileVar, folderVar): print("watchpatch firing...") global stopsign global lastmtime if stopsign: stopsign = False print("Stopping watchpath!") return file = fileVar.get() folder = folderVar.get() backupnum = 1 if file != "" and os.path.getmtime(file) != lastmtime: lastmtime = os.path.getmtime(file) while os.path.exists(folder+"/backup"+str(backupnum)): backupnum += 1 print("Copying folder contents to backup folder...") shutil.copytree(os.path.dirname(file), folder+"/backup"+str(backupnum)) backupnum += 1 app.after(1000, watchpath, fileVar, folderVar) Points to note: Your program actually seems to copy the entire contents of the srcfile directory to the specified backup directory, not just the individual file. I've left this logic as-is, but you might want to change it if your goal is to backup just the one file. I added in some basic print statements to help you understand what's happening. Remove these once you're good to go.
Simultaneous use of a checkbutton and a button
I am new to Python and obviously missing something important when working with checkbuttons. Here's the idea behind my program: I select a file manually and then, depending on whether the checkbox is checked or not, trigger either one calculation sequence or another using a button. To achieve this, I wanted to verify the state of the checkbox using the .get() command. What I've found is that only one sequence is triggered all the time independently of the state of the checkbox. .get() is not updated when I click on the checkbox. What am I doing wrong? Any help will be greatly appreciated. from tkinter import * from tkinter import filedialog import tkinter as tk master = Tk() root = tk.Tk() col_sep = "\t" col_h_b = [] # field column for background col_m_b = [] # magnetization column for background def choose_b_file(): root.fileName_b = filedialog.askopenfilename(filetypes=((".dat files", "*.dat"), ("All files", "*.*"))) with open(root.fileName_b, 'r') as f: for line in f: if line[0] != "#": linedata = [float(line.split(col_sep)[i]) for i in range(len(line.split(col_sep)))] col_h_b.append(linedata[4]) col_m_b.append(linedata[5]) print(f.name) offset = BooleanVar() checkbox = tk.Checkbutton(root, text="offset subtraction", variable=offset,onvalue=1, offvalue=0) checkbox.pack() def plot(): if offset.get() == 1: #some mathematical operations and graph plotting else: #other mathematical operations and graph plotting def close_window(): exit() b_data = Button(master, text="Background", width=20, command=choose_b_file) m_minus_b = Button(master, text="Plot", width=5, command=plot) quit = Button(master, text="Quit", width=5, command=close_window) b_data.pack() m_minus_b.pack() quit.pack() root.mainloop()
You are mostly messing parents widgets root and master. Do you need to have a separate window for the checkbox ? Otherwise the quick fix is to replace root by master in the checkbox creation : checkbox = tk.Checkbutton(root, text="offset subtraction" ...) You can also simplify the boolean stuff, the default behavior for the checkbutton is to use 0 and 1, and remove the master or the root, choose only one. from tkinter import * from tkinter import filedialog root = Tk() col_sep = "\t" col_h_b = [] # field column for background col_m_b = [] # magnetization column for background def choose_b_file(): root.fileName_b = filedialog.askopenfilename(filetypes=((".dat files", "*.dat"), ("All files", "*.*"))) with open(root.fileName_b, 'r') as f: for line in f: if line[0] != "#": linedata = [float(line.split(col_sep)[i]) for i in range(len(line.split(col_sep)))] col_h_b.append(linedata[4]) col_m_b.append(linedata[5]) print(f.name) def plot(): if offset.get() == 1: print('True') #some mathematical operations and graph plotting else: print('False') #other mathematical operations and graph plotting def close_window(): exit() offset = IntVar() checkbox = Checkbutton(root, text="offset subtraction", variable=offset) checkbox.pack() b_data = Button(root, text="Background", width=20, command=choose_b_file) m_minus_b = Button(root, text="Plot", width=5, command=plot) quit = Button(root, text="Quit", width=5, command=close_window) b_data.pack() m_minus_b.pack() quit.pack() root.mainloop()
Can't run functions in sequence. Python
So, I'm working on Tkinter and my goal is to when the user presses a button it opens a new window that he can insert data on database and then it populates a table again so it shows the new data inserted. The new window opens fine and data is indeed inserted, but the list is not updated and I don't know why. Button code: self.inserir = Button(self.container2, text="Inserir", command=lambda:self.help(tm.FazTela(bd),self.populate())) Function code that gets the functions as a list and run them: def help(*functions): def func(*args, **kwargs): return_value = None for function in functions: return_value = function(*args, **kwargs) return return_value return func If I call the populate function before the function that generates the window it runs nicely but that's not what I want, I want to update after the user has input data. I don't know if it helps, but here's the code of the window that opens once the button is pressed: from Tkinter import * from database import database as db from database import tratamentos as tr import tkMessageBox class TelaMenor(): def __init__(self): self.root = None self.OPTIONS = [] self.cor1 = '#D32F2F' def CloseWindow(self): self.root.destroy() self.root = None def SendToTR(self,nome,valor,tipo,bd): try: tr.ProdutosRecieve(nome,valor,tipo,bd) except: tkMessageBox.showerror("Erro encontrado", "Digite valores validos!") finally: self.CloseWindow() def FazTela(self,bd): if(self.root!=None): self.CloseWindow() self.FazTela() else: self.root=Tk() # opcoes do droplist self.OPTIONS = [ "Tipo de produto", "Doce", "Salgado", "Massa", "Bebida", "Outro" ] #fim # criacao e posicao dos widgets info = Frame(self.root) info.grid(sticky=N+S+W+E) salto1 = Label(info, text=" ") salto1.grid(row=0, column=0) nome1 = Label(info, text="Nome:") nome1['font']=['bold'] nome1.grid(row=1, column=1, sticky=W) nome2 = Entry(info) nome2["width"]=40 nome2.grid(row=2, column=1) salto2 = Label(info, text="") salto2.grid(row=3, column=0) valor1 = Label(info, text="Valor:") valor1['font']=['bold'] valor1.grid(row=4, column=1, sticky=W) valor2 = Entry(info) valor2["width"]=40 valor2.grid(row=5, column=1) salto3 = Label(info, text="") salto3.grid(row=6, column=0) variable = StringVar(info) variable.set(self.OPTIONS[0]) droplist = apply(OptionMenu, (info, variable) + tuple(self.OPTIONS)) droplist.grid(row=7, column=1) salto4 = Label(info, text="") salto4.grid(row=8, column=0) pronto = Button(info, text="Pronto", bg=self.cor1, bd=3,command=lambda: self.SendToTR(nome2.get(),valor2.get(),variable.get(),bd)) pronto['font']=['bold'] pronto['fg']='white' pronto.grid(row=9, column=1) salto5 = Label(info, text="") salto5.grid(row=10, column=1) espaco1 = Label(info, text=" ") espaco1.grid(row=10, column=2) #fim # barra de "status" status = Label(info, text="Estado: Normal", bg="white", bd=1, relief=SUNKEN, anchor=W) status.grid(row= 11, column=0, sticky=S+W+E, columnspan=3) #fim # formatacao da janela self.root.title('Cadastro do Produto') #root.iconbitmap(r'c:\Python27\DLLs\icon.ico') self.root.resizable(width=False, height=False) self.root.geometry('298x276') self.root.protocol("WM_DELETE_WINDOW",lambda: self.CloseWindow()) self.root.mainloop() #fim Sorry, there are some words in portuguese.
This is a good illustration of why you shouldn't use lambda unless absolutely necessary: it makes debugging difficult. I recommend removing the use of lambda, and instead tie the button to a normal function. Doing so will make it easier to insert debugging code. In this case, your function is running this code: self.help(tm.FazTela(bd),self.populate()) This is the same as doing this: a = tm.FazTela(bd) b = self.populate() self.help(a,b) You also have the problem that you are creating more than one root window. In tkinter you must always have exactly one root window. Instead of creating a second instance of Tk, you need to create an instance of Toplevel. If you want to execute code after the window has been destroyed you can use the function wait_window which will not return until the given window has closed.