How to prevent Tkinter multithread from running the function twice? - python

I made a Tkinter tool which may help me keep copying the text and search it on google without pasting.
In the beginning, when I pressed "Enable Copy", the Tkinter freezed.
So, I used threading module to separate the copy function. Otherwise, I cannot press "Stop" while the copy function is working.
However, now when I pressed "Enable Copy", I can only search the text from my clipboard for the first time. After that, I copied the text once but searched it twice no matter what.
I guess the reason might be the "threading" module, but the Tkinter would freeze without it...
That's where I got stucked.
If anyone has better idea, please kindly advise.
My Code:
import tkinter as tk
import keyboard
import webbrowser
import threading
class myGUI:
def __init__(self):
self.thread_running = bool
self.win = tk.Tk()
self.win.title("Copy Tool")
self.win.geometry('300x50')
tk.Button(self.win, text="Enable Copy", command=self.start_thread).pack()
tk.Button(self.win, text="Stop", command=self.close_thread).pack()
def copy_(self):
while self.thread_running:
if keyboard.read_key()== "ctrl" and keyboard.read_key()== "c":
text_fromcopy=self.win.clipboard_get()
print(text_fromcopy)
if "&" in text_fromcopy:
text_fromcopy = text_fromcopy.replace("&", "%26")
url = "https://www.google.com/search?q={}".format(text_fromcopy)
webbrowser.open(url,new=0)
def close_thread(self):
self.thread_running = False
def start_thread(self):
self.thread_running = True
threading.Thread(target=self.copy_).start()
if __name__ == '__main__':
app = myGUI()
app.win.mainloop()

Related

Not able to get Tkinter Values outside of class

I have created a simple tKinter Gui with PAGE builder and I am able to click a button and execute the corresponding command function within it. But when I try to get a value of a specific text box within the function I get various errors mostly no such property found. I have tried adding self and the class name into the property and even passing the property from the class as well as making it a function within that class but I still can't seem to access the values of the textbox 'Username'. I would really appreciate any help on how to get those text box values within the function as I have been researching for hours but still cannot make it work. Also if anyone knows of any good tutorial on this topic would help tremendously. Thank you.
The project has 2 files: (I've tried to remove the non essential code)
MacUpdaterPageDesign.py
import sys
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.constants import *
import os.path
_script = sys.argv[0]
_location = os.path.dirname(_script)
import MacUpdaterPageDesign_support
class Toplevel1:
def __init__(self, top=None):
top.title("Mac Updater")
top.configure(background="#d9d9d9")
self.top = top
self.MainFrame = tk.Frame(self.top)
self.MainFrame.place(relx=0.0, rely=0.18, relheight=0.811
, relwidth=1.099)
self.Username = tk.Text(self.MainFrame)
self.Username.place(relx=0.15, rely=0.081, relheight=0.048
, relwidth=0.279)
#this button calls the CopyMACfunc on the support page
self.CopyMAC = tk.Button(self.MainFrame)
self.CopyMAC.place(relx=0.143, rely=0.846, height=34, width=117)
self.CopyMAC.configure(command=MacUpdaterPageDesign_support.CopyMACfunc)
self.CopyMAC.configure(text='Copy MAC')
MacUpdaterPageDesign_support.py
import sys
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.constants import *
import MacUpdaterPageDesign
def main(*args):
'''Main entry point for the application.'''
global root
root = tk.Tk()
root.protocol( 'WM_DELETE_WINDOW' , root.destroy)
# Creates a toplevel widget.
global _top1, _w1
_top1 = root
_w1 = MacUpdaterPageDesign.Toplevel1(_top1)
root.mainloop()
def CopyMACfunc(*args):
#this part must retrieve the value in from Username
#tried many variations of below but throws error
username = MacUpdaterPageDesign.Username.get("1.0",END)
print(username)
if __name__ == '__main__':
MacUpdaterPageDesign.start_up()
Actually while writing this question I finally got it to work:
username = _w1.Username.get("1.0",END)
it did work with the following but not sure if this would be the right way to do it per say. Maybe their are better ways if anyone knows. Also would appreciate any recommendation for a good tutorial or where to learn all of this type of info. Thanks

How do I remove an idlelib "Hovertip" once it's been created?

I have a dialog window with some entries on it, and as part of validating those entries I will occasionally disable my 'Okay' button and place an idlelib Hovertip on it letting the user know that there was an issue.
The trouble is that once I re-enable the 'Okay' button, I can't figure out how to remove the Hovertip...so I just set the hover_delay to an arbitrarily long time and hope the user clicks 'Okay' before the tip shows up.
Is there a way to remove the Hovertip once it's been attached to my button, and can this be done in such a way that the Hovertip can be added again when needed?
Setting the message to an empty string just results in an empty Hovertip, and deleting the Hovertip makes it go away forever (even when I want it back).
import tkinter as tk
from tkinter import ttk
from idlelib.tooltip import Hovertip
class App(tk.Tk):
"""Example App for Helpful Folks"""
def __init__(self):
super().__init__()
self.toggle_state = False
self.toggle_btn = ttk.Button(
self,
text='Toggle',
command=self.toggle
)
self.toggle_btn.pack()
self.okay_btn = ttk.Button(
self,
text='Okay',
command=self.bell, # ding
)
self.okay_btn.pack()
def toggle(self):
if self.toggle_state:
self.okay_btn.configure(state=tk.NORMAL)
self.tooltip.__del__() # gone forever, never to return
else:
self.okay_btn.configure(state=tk.DISABLED)
self.tooltip = Hovertip(
self.okay_btn,
'No dice!',
hover_delay=300
)
self.toggle_state = not self.toggle_state
if __name__ == '__main__':
app = App()
app.mainloop()

Can't get tkinter tabs to show

I decided I want to learn how to make GUIs with something more than entry fields and buttons, so I'm starting off with tabs. After a little bit of research I made myself a program, but don't know why it doesn't work.
# --IMPORTS--
from tkinter import *
import tkinter.ttk as ttk
import time
# --CLASSES--
class Gui:
def __init__(self):
self.root = Tk()
self.root.title("tab test")
def setup(self):
# tabs
tabc = ttk.Notebook(self.root)
tab1 = ttk.Frame(tabc)
tabc.add(tab1, text="test 1")
tabc.grid()
def run(self):
self.root.mainloop()
# --MAIN--
if __name__ == "__main__":
gui = Gui()
gui.run()
When I run the program I just get a blank screen (see screenshot) and there is no way to see if there is a tab, let alone which one is selected.
Like I said, I don't see the reason why it isn't working. There are no error messages to point me in the right direction and I'm not 100% sure on how tabs work to begin with, so I thought I'd ask here. I've tried changing .grid() to .pack() but I think it's more of an error on my end than a bug with tkinter. Thanks in advance!
you have to run your setup method.
# --MAIN--
if __name__ == "__main__":
gui = Gui()
gui.setup()
gui.run()

Testing tkinter application

I wrote a small application using python 3 and tkinter. Testing every widget, even though there are not many of them feels daunting so I wanted to write a couple of automated tests to simplify the process. I read some other question that seemed relevant to this problem but none fit my needs. Right now I'm doing the testing in a very simple manner - I invoke the command for every widget and manually click through it to see if it works. It does make things a bit faster, but I constantly run into some problems - i.e. I can't automatically close popup windows (like showinfo) even with using libraries to simulate keyboard clicks (namely pynput). Is there an efficient approach for testing applications using tkinter?
Here is the code I use right now:
import tkinter as tkinter
import unittest
from mygui import MyGUI
class TKinterTestCase(unittest.TestCase):
def setUp(self):
self.root = tkinter.Tk()
def tearDown(self):
if self.root:
self.root.destroy()
def test_enter(self):
v = MyGUI(self.root)
v.info_button.invoke()
v.close_button.invoke()
v.btnOut.invoke()
if __name__ == "__main__":
unittest.main()
I don't know much about unittest but I found a workaround to close popup dialogs like showinfo during the tests. The idea is to use keyboard event to invoke the button of the dialog. But since the app is waiting for the user to close the popup dialog, we need to schedule in advance the keyboard event using after:
self.root.after(100, self.root.event_generate('<Return>'))
v.button.invoke()
Full example
import tkinter
from tkinter import messagebox
import unittest
class MyGUI(tkinter.Frame):
def __init__(self, master, **kw):
tkinter.Frame.__init__(self, master, **kw)
self.info_button = tkinter.Button(self, command=self.info_cmd, text='Info')
self.info_button.pack()
self.quit_button = tkinter.Button(self, command=self.quit_cmd, text='Quit')
self.quit_button.pack()
def info_cmd(self):
messagebox.showinfo('Info', master=self)
def quit_cmd(self):
confirm = messagebox.askokcancel('Quit?', master=self)
if confirm:
self.destroy()
class TKinterTestCase(unittest.TestCase):
def setUp(self):
self.root = tkinter.Tk()
self.root.bind('<Key>', lambda e: print(self.root, e.keysym))
def tearDown(self):
if self.root:
self.root.destroy()
def test_enter(self):
v = MyGUI(self.root)
v.pack()
self.root.update_idletasks()
# info
v.after(100, lambda: self.root.event_generate('<Return>'))
v.info_button.invoke()
# quit
def cancel():
self.root.event_generate('<Tab>')
self.root.event_generate('<Return>')
v.after(100, cancel)
v.quit_button.invoke()
self.assertTrue(v.winfo_ismapped())
v.after(100, lambda: self.root.event_generate('<Return>'))
v.quit_button.invoke()
with self.assertRaises(tkinter.TclError):
v.winfo_ismapped()
if __name__ == "__main__":
unittest.main()

problems with the update of a label Tkinter

I'm really stuck on a basilary things: I have this code
from tkinter import *
import sys
import subprocess
import tkinter as tk
def cd():
f=(subprocess.check_output("net view"))
e=(f.decode(sys.stdout.encoding))
label1=Label(text=e).pack()
def mainscreen():
mainscreen=Tk()
mainscreen.title("Terfysgol's kit V 2.0")
frame1=Frame(mainscreen)
frame1.pack()
puls1=Button(frame1,text="List of device", borderwidth= "2",command= cd).pack()
mainscreen()
When I run it all the time that I press the button it create a new label but I only want to update the text of the label1.
This is what you are after:
def cd():
f=(subprocess.check_output("net view"))
e=(f.decode(sys.stdout.encoding))
label1.config(text = e)
and then at the top of your program after your imports you need to put:
label1 = Label()
label1.pack()
Please note that I'm not suggesting this is good program structure, but that is up to you to sort out. This answer is just a quick fix to provide you with enough information to work out the rest of what you need.
Also you can remove the import tkinter as tk line already imports tkinter.

Categories

Resources