I realize that the first suggestion will be to "stop using Tix", but I like some of the widgets even though they haven't been maintained since '08. One thing I've noticed is that some of the dialog boxes won't stay open. For instance I'm using a FileEntry Widget inside of a LabelFrame Widget inside of the notebook widget. The file dialog looks as follows.
And when you click on the file dialog button you get:
The red arrow shows the drop down to files in that filter, but nothing happens when I click it. You can see a brief flash (like in the milliseconds) like an event loop was checked or something but then nothing. The same with the other buttons on FileEntry.
For the full code to this, you can see here
I think the relevant parts are:
import os, os.path, sys, Tix
from Tkconstants import *
import tkFileDialog
import traceback, tkMessageBox
from Tkinter import *
class pyigblast_gui():
def __init__(self,root):
#Initialization
self.root = root
self.exit = -1
self.dir = None
self.argument_dict = {'query':''}
#local
_program_name = sys.argv[0]
_directory_name = os.path.dirname(_program_name)
def MainMenu(self):
main_menu = Tix.Frame(self.root,bd=2,relief=RAISED)
return main_menu
def TabNotebook(self):
notebook_frame = self.root
notebook = Tix.NoteBook(notebook_frame, ipadx=5, ipady=5, bg='black')
notebook.add('f_and_d', label="Files and Databases", underline=0,
createcmd=lambda self=self,nb=notebook,name='f_and_d': self.files_and_directories(nb,name))
notebook.add('readme', label="Usage", underline=0,
createcmd=lambda self=self,nb=notebook,name='readme': self.readme(nb,name) )
return notebook
def files_and_directories(self,nb,name):
f_and_d_page = nb.page(name)
options = "label.padX4"
self.input_frame = Tix.LabelFrame(f_and_d_page,options=options)
self.input_frame.pack(side=TOP,expand=0,fill=BOTH)
self.make_fasta_entry()
#self.input_frame.grid(in_=f_and_d_page,row=0,column=0,columnspan=2)
def make_fasta_entry(self):
message = Tix.Message(self.input_frame,relief=Tix.FLAT, width=500, anchor=W,
text='Enter the entry FASTA file here',font=('Arial',16))
self.fasta_entry = Tix.FileEntry(self.input_frame, label="Select a FASTA file:",selectmode="normal")
message.pack(side=TOP,expand=1,fill=BOTH,padx=3,pady=3)
self.fasta_entry.pack(side=TOP,fill=X,padx=3,pady=3)
def build(self):
window_info = self.root.winfo_toplevel()
window_info.wm_title('PyIgBLAST - GUI')
#if window_info <= 800:
window_info.geometry('1500x900+10+10')
frame1 = self.MainMenu()
frame1.pack(side=BOTTOM, fill=X)
frame2 = self.TabNotebook()
frame2.pack(side=TOP,expand=1,fill=BOTH,padx=5,pady=5)
window_info.wm_protocol("WM_DELETE_WINDOW", lambda self=self:self.quitcmd())
def loop(self):
while self.exit < 0:
# There are 2 whiles here. The outer one lets you continue
# after a ^C interrupt.
try:
# This is the replacement for _tkinter mainloop()
# It blocks waiting for the next Tcl event using select.
while self.exit < 0:
self.root.tk.dooneevent(0)
except SystemExit:
# Tkinter uses SystemExit to exit
self.exit = 1
return
except KeyboardInterrupt:
if tkMessageBox.askquestion ('Interrupt', 'Really Quit?') == 'yes':
# self.tk.eval('exit')
self.exit = 1
return
continue
except:
# Otherwise it's some other error - be nice and say why
t, v, tb = sys.exc_info()
text = ""
for line in traceback.format_exception(t,v,tb):
text += line + '\n'
try: tkMessageBox.showerror ('Error', text)
except: pass
self.exit = 1
raise SystemExit, 1
def destroy(self):
self.root.destroy()
if __name__ == '__main__':
root = Tix.Tk()
pyigblast_class = pyigblast_gui(root)
pyigblast_class.build()
pyigblast_class.loop()
pyigblast_class.destroy()
Also in a seperate but unrelated warning given by Tix, I get this output to the terminal.
(TixForm) Error:Trying to use more than one geometry
manager for the same master window.
Giving up after 50 iterations.
If anyone can tell me about what I need to change with Tix to keep the dialog open and/or why it says I'm using two geometry managers I would appreciate it!
Thanks,
J
Ok, shine that noise.
The ttk solution is so much cleaner and more customizable. Here is a very elegant solution recapitulating this exact file dialog, but is much cleaner using ttk.
http://www.pyinmyeye.com/2012/08/tkinter-filedialog-demo.html
Related
I've made a Python program which reads the file and then sends/receives the data towards/from the microcontroller and everything worked well until I added a menu to display short instructions.
Since the UART communication has to run in a separate thread I used threading and StringVar() to access the data from the main thread.
To demonstrate the problem I've made a short example which has nothing to do with microcontrollers.
The steps to reproduce the problem:
Click the Next screen radio button to open the second screen (at the initial screen the menu works well)
Click Help > Instructions
After (or sometimes even before) closing the message box the program will crash with:
TclStackFree: incorrect freePtr. Call out of sequence?
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Note:
In the original program where there are more UI elements the program always crashes before showing the message box and after removing even more UI elements the program doesn't crash every time - that's why I've left some "redundant" labels. When some more labels are added the program will crash every time.
I have narrowed the cause of the crash to:
displayVal.set()
in checkForData() function. By removing that instruction everything works well.
Moreover, after removing displayVal.trace("w", displayVal_trace) the program will not crash anymore but opening the menu will still temporarily freeze the working thread.
I thought after displayVal.set() Tkinter is trying to update the label but can't because of showing the menu - however, the problem remained even after I removed label_data = Label(up2, textvariable = displayVal).
Here is the stripped down code tested with Python 2.7:
from Tkinter import *
import tkMessageBox
import threading
import time
threadRun = True
checkDelay = 0.5
def checkForData():
global threadRun, checkDelay
print "Simulating thread for receiving messages from UART"
while threadRun == True:
print time.time()
displayVal.set(time.time()) # <-- if removed the menu works OK (no crash)
time.sleep(checkDelay)
print "No more receiving of messages"
def listenForData():
t = threading.Thread(target=checkForData)
t.daemon = False
t.start()
def stopListening():
global threadRun, checkDelay
threadRun = False
time.sleep(checkDelay + 0.1)
def exit_handler():
print "Exiting..."
stopListening()
root.destroy()
root = Tk()
right = int((root.winfo_screenwidth() - 600) / 2)
down = int(root.winfo_screenheight() / 3 - 400 / 2)
root.geometry("600x400+%d+%d" % (right, down))
root.resizable(width = False, height = False)
root.protocol("WM_DELETE_WINDOW", exit_handler)
displayVal = StringVar()
displayVal.set("nothing")
def setupView():
global masterframe
masterframe = Frame()
masterframe.pack(fill=BOTH, expand=True)
selectPort() # the 1st screen for selecting COM ports
def selectPort():
global masterframe, radioVar
# remove everything from the frame
for child in masterframe.winfo_children():
child.destroy()
radioVar = StringVar()
l1 = Label(masterframe, text = "Select...")
l1.pack(pady=(50, 20))
# this would be a list of detected COM ports
lst = ["Next screen"]
if len(lst) > 0:
for n in lst:
r1 = Radiobutton(masterframe, text=n, variable=radioVar, value=n)
r1.config(command = next_screen)
r1.pack()
def mainScreen():
global masterframe, term, status
# remove previous screen from the frame
for child in masterframe.winfo_children():
child.destroy()
up1 = Frame(masterframe)
up1.pack(side=TOP)
up2 = Frame(masterframe)
up2.pack()
terminal = Frame(masterframe)
terminal.pack()
down = Frame(masterframe)
down.pack(side=BOTTOM, fill=BOTH)
label_top = Label(up1, text="Something")
label_top.pack(pady=5)
label_data = Label(up2, textvariable = displayVal)
label_data.pack(pady=(10, 0))
term = Text(terminal, height=10, width=35, bg="white")
term.pack()
term.tag_config("red", foreground="red")
term.tag_config("blue", foreground="blue")
term.insert(END, "The file has been read\n", "red")
term.insert(END, "File contents:\n")
term.insert(END, data)
status = Label(down, text="Status...", bd=1, relief=SUNKEN, anchor=W, bg="green")
status.pack(fill=X)
displayVal.trace("w", displayVal_trace) # <-- if removed only temporary freeze but no crash
def displayVal_trace(name, index, mode):
global term
if(displayVal.get() != "NOTHING"):
term.insert(END, "\nReceived: ", "blue")
term.insert(END, displayVal.get())
term.see(END)
def next_screen():
listenForData()
mainScreen()
def stop():
stopListening()
def instructions():
tkMessageBox.showinfo("Help", "This is help")
main_menu = Menu(root)
root.config(menu = main_menu)
help_menu = Menu(main_menu)
main_menu.add_cascade(label="Help", menu=help_menu)
help_menu.add_command(label="Instructions", command=instructions)
data = [[1], [2], [3]] # this would be the data read from the file
b1 = data[0][0]
b2 = data[1][0]
b3 = data[2][0]
print "Read from file:", b1, b2, b3
setupView()
root.mainloop()
I've cloned a class called ListBoxChoice found on the web found (adding some needed features) below:
from Tkinter import *
class ListBoxChoice(object):
def __init__(self, master=None, title=None, message=None,\
list=[]):
self.master = master
self.value = None
self.list = list[:]
self.modalPane = Toplevel(self.master)
self.modalPane.transient(self.master)
self.modalPane.grab_set()
self.modalPane.bind("<Return>", self._choose)
self.modalPane.bind("<Escape>", self._cancel)
if title:
self.modalPane.title(title)
if message:
Label(self.modalPane, text=message).pack(padx=5, pady=5)
listFrame = Frame(self.modalPane)
listFrame.pack(side=TOP, padx=5, pady=5)
scrollBar = Scrollbar(listFrame)
scrollBar.pack(side=RIGHT, fill=Y)
# get the largest value of the 'list' to set the width
widthOfList = 0
for k in list:
if len(str(k)) > widthOfList:
widthOfList = len(str(k))
# now pad some space to back of the widthOfList
widthOfList = widthOfList + 2
self.listBox = Listbox(listFrame, selectmode=SINGLE,\
width=widthOfList)
self.listBox.pack(side=LEFT, fill=Y)
scrollBar.config(command=self.listBox.yview)
self.listBox.config(yscrollcommand=scrollBar.set)
self.list.sort()
for item in self.list:
self.listBox.insert(END, item)
buttonFrame = Frame(self.modalPane)
buttonFrame.pack(side=BOTTOM)
chooseButton = Button(buttonFrame, text="Choose",\
command=self._choose)
chooseButton.pack()
cancelButton = Button(buttonFrame, text="Cancel",\
command=self._cancel)
cancelButton.pack(side=RIGHT)
def _choose(self, event=None):
try:
firstIndex = self.listBox.curselection()[0]
self.value = self.list[int(firstIndex)]
except IndexError:
self.value = None
self.modalPane.destroy()
def _cancel(self, event=None):
self.modalPane.destroy()
def returnValue(self):
self.master.wait_window(self.modalPane)
return self.value
if __name__ == '__main__':
import random
root = Tk()
returnValue = True
list = [random.randint(1,100) for x in range(50)]
while returnValue:
returnValue = ListBoxChoice(root, "Number Picking",\
"Pick one of these crazy random numbers",\
list).returnValue()
print returnValue
Now this example says to do something like this:
results = ListBoxChoice(root, list=listOfItems).returnValue().
What I'm trying to do is provide a list of values from which the user selects a single value. The window should close before I use the results from the selected value. Here is that code:
from tkinter import Tk, Label
form ListBoxChoice import ListBoxChoice
...
eventList = ["20190120","20190127","20190203"]
root = Tk()
root.withdraw() # This causes the ListBoxChoice object not to appear
selectValue = ListBoxChoice(root, title="Event",\
message="Pick Event", list=eventList).returnValue()
root.wait_window() # Modal Pane/window closes but not the root
print("selectValue:", selectValue)
A root window is placed behind the modalPane (Toplevel). I have to close that window before the calling process continues. So there is a block in place.
I've tried to put a sleep(1.01) command above but had no impact.
How do I get the ListBoxChoice to close once the selection has been made
before my print statement of the selectValue? For it is at that point I want to use the results to plot data.
If I don't use root.wait_winow(), it is only when the plot is closed (end of the process) that the ListBoxChoice box close as well.
Suggestions?
Slightly updated
Here's a version of the ListBoxChoice class which I think works the way you desire. I've updated my previous answer slightly so the class is now defined in a separate module named listboxchoice.py. This didn't change anything I could see when I tested—it other words it still seems to work—but I wanted to more closely simulate the way you said you're using it the comments.
It still uses wait_window() because doing so is required to give tkinter's mandatory event-processing-loop the opportunity to run (since mainloop() isn't called anywhere). There's some good background material in the article Dialog Windows about programming tkiner dialogs you might find useful. The added root.withdraw() call eliminates the issue of not being able to close it because it's not there. This is fine since there's no need to have the empty window being displayed anyway.
test_lbc.py
import random
try:
import Tkinter as tk # Python 2
except ModuleNotFoundError:
import tkinter as tk # Python 3
from listboxchoice import ListBoxChoice
root = tk.Tk()
root.withdraw() # Hide root window.
values = [random.randint(1, 100) for _ in range(50)]
choice = None
while choice is None:
choice = ListBoxChoice(root, "Number Picking",
"Pick one of these crazy random numbers",
values).returnValue()
print('choice: {}'.format(choice))
listboxchoice.py
""" ListBoxChoice widget to display a list of values and allow user to
choose one of them.
"""
try:
import Tkinter as tk # Python 2
except ModuleNotFoundError:
import tkinter as tk # Python 3
class ListBoxChoice(object):
def __init__(self, master=None, title=None, message=None, values=None):
self.master = master
self.value = None
if values is None: # Avoid use of mutable default argument value.
raise RuntimeError('No values argument provided.')
self.values = values[:] # Create copy.
self.modalPane = tk.Toplevel(self.master, takefocus=True)
self.modalPane.bind("<Return>", self._choose)
self.modalPane.bind("<Escape>", self._cancel)
if title:
self.modalPane.title(title)
if message:
tk.Label(self.modalPane, text=message).pack(padx=5, pady=5)
listFrame = tk.Frame(self.modalPane)
listFrame.pack(side=tk.TOP, padx=5, pady=5)
scrollBar = tk.Scrollbar(listFrame)
scrollBar.pack(side=tk.RIGHT, fill=tk.Y)
# Get length the largest value in 'values'.
widthOfList = max(len(str(value)) for value in values)
widthOfList += 2 # Add some padding.
self.listBox = tk.Listbox(listFrame, selectmode=tk.SINGLE, width=widthOfList)
self.listBox.pack(side=tk.LEFT, fill=tk.Y)
scrollBar.config(command=self.listBox.yview)
self.listBox.config(yscrollcommand=scrollBar.set)
self.values.sort()
for item in self.values:
self.listBox.insert(tk.END, item)
buttonFrame = tk.Frame(self.modalPane)
buttonFrame.pack(side=tk.BOTTOM)
chooseButton = tk.Button(buttonFrame, text="Choose", command=self._choose)
chooseButton.pack()
cancelButton = tk.Button(buttonFrame, text="Cancel", command=self._cancel)
cancelButton.pack(side=tk.RIGHT)
def _choose(self, event=None):
try:
firstIndex = self.listBox.curselection()[0]
self.value = self.values[int(firstIndex)]
except IndexError:
self.value = None
self.modalPane.destroy()
def _cancel(self, event=None):
self.modalPane.destroy()
def returnValue(self):
self.master.wait_window(self.modalPane)
return self.value
everyone. I'm new to Python and trying to learn it as my future jobs will require me knowing it. I'm playing around with Tkinter, trying to get a pinging script to work. The result of this script will show a list of servers in column 0 and a list of whether it is up or down in column 1. I have it working except for one thing: the widgets overlap, causing this script to be a memory hog. For example, if the site "google.com" responds with "UP" and I take down my internet, it will show as "DOWN". However, as soon as a plug my internet back in, it will show as "UP" but I can see the remnants of the word "DOWN" behind the label. I've tried different ways to destroy the widget before every utilization but can not get it to work. I understand if my code is a little messy so I'm definitely open to criticism. Below is the code I have with a few example sites listed in the "host" variable:
import pyping
import Tkinter as tk
from Tkinter import *
import time
host = ["google.com", "yahoo.com", "espn.com"]
root = tk.Tk()
class PingTest:
result = []
resultfc = []
def __init__(self, hostname, inc):
self.hostname = hostname
self.inc = inc
self.ping(hostname)
def results(self, result1, resultfc1):
self.result = result1
self.resultfc = resultfc1
def ping(self, y):
self.y = y
q = ""
try:
x = pyping.ping(self.y, count=1)
q = x.ret_code
except Exception:
pass
finally:
if q == 0:
self.results("UP", "green")
else:
self.results("DOWN", "red")
self.window()
def window(self):
self.label1 = Label(root, text=self.hostname)
self.label2 = Label(root, text=self.result, fg=self.resultfc, bg="black")
a = Label(root, text=self.hostname)
b = Label(root, text=self.result, fg=self.resultfc, bg="black")
b.update()
b.update_idletasks()
if b == TRUE:
b.grid_forget() # These two lines don't seem to help my cause
b.destroy()
a.grid(row=self.inc, column=0)
b.grid(row=self.inc, column=1)
while TRUE:
i = 0
for h in host:
PingTest(h, i)
i += 1
time.sleep(1)
I would update labels instead of destroying them.
We can use threading to check each site without having to block the mainloop().
By creating a list of labels you can use the index of the list to set up the labels on your GUI and at the same time we can start a thread per object in list to check on the site status and return if site is up or down. I chose to use urllib and threading to make this work.
import tkinter as tk
import urllib.request
import threading
import time
host = ["google.com", "yahoo.com", "espn.com"]
class CheckURL:
def __init__(self, host, widget):
self.host = host
self.widget = widget
self.update_labels()
def update_labels(self):
if urllib.request.urlopen("http://www." + self.host).getcode() == 200:
self.widget.config( text='UP', fg='green')
else:
self.widget.config(text='DOWN', fg='red')
time.sleep(5)
self.update_labels()
root = tk.Tk()
labels = []
for ndex, x in enumerate(host):
tk.Label(root, text=x).grid(row=ndex, column=0)
labels.append(tk.Label(root, text='DOWN', fg='red'))
labels[-1].grid(row=ndex, column=1)
threading._start_new_thread(CheckURL, (x, labels[-1]))
root.mainloop()
I've been building an app to track stock prices. The user should see a window with an entry widget and a button that creates a new frame with a label and a button. The label is the stock price and symbol, the button is a delete button, and should hide that frame if clicked.
I've re-written this program 4 times now, and it's been a great learning experience, but what I've learned is that I can't have the "mini-frames" being called from methods part of the main GUI class - this funks up the delete buttons, and updates the value behind frame.pack_forget() so it only deletes the last item ever.
I've moved my mini-frame widgets down into the class for the actual stock values. I've packed them (what I assume to be correct) but they don't show up. They also don't error out, which isn't very helpful. Here's my code, although I've omitted a lot of the functional parts to show what is happening with my frames. Keep in mind I need to keep it so that I can call my updater (self.update_stock_value) with a .after method against myapp.myContainer.
Is there a better way to do this?? Thanks in advance, my head hurts.
import re
import time
import urllib
from Tkinter import *
import threading
from thread import *
runningThreads = 0
# each object will be added to the gui parent frame
class MyApp(object):
def __init__(self, parent):
self.myParent = parent
self.myContainer = Canvas(parent)
self.myContainer.pack()
self.create_widgets()
# METHOD initiates basic GUI widgets
def create_widgets(self):
root.title("Stocker")
self.widgetFrame = Frame(self.myContainer)
self.widgetFrame.pack()
self.input = Entry(self.widgetFrame)
self.input.focus_set()
self.input.pack()
self.submitButton = Button(self.widgetFrame, command = self.onButtonClick)
self.submitButton.configure(text = "Add new stock")
self.submitButton.pack(fill = "x")
# METHOD called by each stock object
# returns the "symbol" in the entry widget
# clears the entry widget
def get_input_value(self):
var = self.input.get()
self.input.delete(0, END)
return var
# METHOD called when button is clicked
# starts new thread with instance of "Stock" class
def onButtonClick(self):
global runningThreads # shhhhhh im sorry just let it happen
runningThreads += 1 # count the threads open
threading.Thread(target = self.init_stock,).start() # force a tuple
if runningThreads == 1:
print runningThreads, "thread alive"
else:
print runningThreads, "threads alive"
def init_stock(self):
new = Stock()
class Stock(object):
def __init__(self):
# variable for the stock symbol
symb = self.stock_symbol()
# lets make a GUI
self.frame = Frame(myapp.myContainer)
self.frame.pack
# give the frame a label to update
self.testLabel = Label(self.frame)
self.testLabel.configure(text = self.update_stock_label(symb))
self.testLabel.pack(side = LEFT)
# create delete button to kill entire thread
self.killButton = Button(self.frame, command = self.kill_thread)
self.killButton.configure(text = "Delete")
self.killButton.pack(side = RIGHT)
# create stock label
# call updater
def kill_thread(self):
global runningThreads
runningThreads -= 1
self.stockFrame.pack_forget() # hide the frame
self.thread.exit() # kill the thread
def update_stock_label(self, symb):
self.testLabel.configure(text = str(symb) + str(get_quote(symb)))
myapp.myContainer.after(10000, self.update_stock_label(symb))
def stock_symbol(self):
symb = myapp.get_input_value()
print symb
# The most important part!
def get_quote(symbol):
try:
# go to google
base_url = "http://finance.google.com/finance?q="
# read the source code
content = urllib.urlopen(base_url + str(symbol)).read()
# set regex target
target = re.search('id="ref_\d*_l".*?>(.*?)<', content)
# if found, return.
if target:
print "found target"
quote = target.group(1)
print quote
else:
quote = "Not Found: "
return quote
# handling if no network connection
except IOError:
print "no network detected"
root = Tk()
root.geometry("280x200")
myapp = MyApp(root)
root.mainloop()
Your code won't run because of numerous errors, but this line is definitely not doing what you think it is doing:
self.frame.pack
For you to call the pack function you must include (), eg:
self.frame.pack()
You ask if your code is the best way to do this. I think you're on the right track, but I would change a few things. Here's how I would structure the code. This just creates the "miniframes", it doesn't do anything else:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self)
self.submit = tk.Button(self, text="Submit", command=self.on_submit)
self.entry.pack(side="top", fill="x")
self.submit.pack(side="top")
def on_submit(self):
symbol = self.entry.get()
stock = Stock(self, symbol)
stock.pack(side="top", fill="x")
class Stock(tk.Frame):
def __init__(self, parent, symbol):
tk.Frame.__init__(self, parent)
self.symbol = tk.Label(self, text=symbol + ":")
self.value = tk.Label(self, text="123.45")
self.symbol.pack(side="left", fill="both")
self.value.pack(side="left", fill="both")
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
How would I programmatically activate a window in Windows using Python? I'm sending keystrokes to it and at the moment I'm just making sure it's the last application used then sending the keystroke Alt+Tab to switch over to it from the DOS console. Is there a better way (since I've learned by experience that this way is by no means foolproof)?
You can use the win32gui module to do that. First you need to get a valid handle on your window. You can use the win32gui.FindWindow if you know the window class name or the exact title. If not, you can enumerate the windows with the win32gui.EnumWindows and try to find the right one.
Once you have the handle, you can call the win32gui.SetForegroundWindow with the handle. It will activate the window and will be ready for getting your keystrokes.
See an example below. I hope it helps
import win32gui
import re
class WindowMgr:
"""Encapsulates some calls to the winapi for window management"""
def __init__ (self):
"""Constructor"""
self._handle = None
def find_window(self, class_name, window_name=None):
"""find a window by its class_name"""
self._handle = win32gui.FindWindow(class_name, window_name)
def _window_enum_callback(self, hwnd, wildcard):
"""Pass to win32gui.EnumWindows() to check all the opened windows"""
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) is not None:
self._handle = hwnd
def find_window_wildcard(self, wildcard):
"""find a window whose title matches the wildcard regex"""
self._handle = None
win32gui.EnumWindows(self._window_enum_callback, wildcard)
def set_foreground(self):
"""put the window in the foreground"""
win32gui.SetForegroundWindow(self._handle)
w = WindowMgr()
w.find_window_wildcard(".*Hello.*")
w.set_foreground()
Pywinauto and SWAPY will probably require the least effort to set the focus of a window.
Use SWAPY to auto-generate the python code necessary to retrieve the window object, e.g.:
import pywinauto
# SWAPY will record the title and class of the window you want activated
app = pywinauto.application.Application()
t, c = u'WINDOW SWAPY RECORDS', u'CLASS SWAPY RECORDS'
handle = pywinauto.findwindows.find_windows(title=t, class_name=c)[0]
# SWAPY will also get the window
window = app.window_(handle=handle)
# this here is the only line of code you actually write (SWAPY recorded the rest)
window.SetFocus()
If by chance other windows are in front of the window of interest, not a problem. This additional code or this will make sure it is shown before running the above code:
# minimize then maximize to bring this window in front of all others
window.Minimize()
window.Maximize()
# now you can set its focus
window.SetFocus()
import ctypes, platform
if platform.system() == 'Windows':
Active_W = ctypes.windll.user32.GetActiveWindow()
ctypes.windll.user32.SetWindowPos(Active_W,0,0,0,0,0,0x0002|0x0001)
Here we go. you just need to store the value of the active window.
Pip install keyboard.
Before you set foreground window, simulate a keyboard to esc that is keyboard.send('esc')
You may want to do it three times for either of the following:
Sidebar
Windows key overlay
Task manager which is always on top
Using SetWindowPos or SetForegroundWindow might NOT be enough if the window was minified aka IsIconic! We can use ShowWindow with SW_RESTORE (9):
import ctypes
def activate_window(hwnd):
user32 = ctypes.windll.user32
user32.SetForegroundWindow(hwnd)
if user32.IsIconic(hwnd):
user32.ShowWindow(hwnd, 9)
Depending on how you identify the desired window there are some ways to get the hwnd aka window handle.
You could loop through all the windows to find the right handle according to the pid via user32.GetWindowThreadProcessId or by window name with user32.GetWindowTextW
To get ProcessIds you could use Windows built-in wmic. There are loads of other nifty things you can do with it. (all attributes: wmic process get /?) (get handle is broken tho) For example:
def get_pids(proc_name):
out = subprocess.check_output('wmic process where Name="%s" get ProcessId' % proc_name)
pids = out.decode().strip().split()[1:]
if not pids:
raise WindowsError('Could not find pids for process')
return [int(pid) for pid in pids]
GUI Application to keep windows active
Python3
install library
pip install pywin32
save below code as alive.pyw file
from ctypes import windll, wintypes, byref, c_uint, sizeof, Structure
import tkinter as tk
import ctypes
import sys
import threading
import time
import win32api
import win32con
stop_threads = True
SET_IDLE_TIME = 40 #in seconds
tm1 = time.time()
value = 0
class LASTINPUTINFO(Structure):
_fields_ = [
('cbSize', c_uint),
('dwTime', c_uint),
]
def get_idle_duration():
global value, tm1
lastInputInfo = LASTINPUTINFO()
lastInputInfo.cbSize = sizeof(lastInputInfo)
windll.user32.GetLastInputInfo(byref(lastInputInfo))
# millis = 4294967 - lastInputInfo.dwTime - windll.kernel32.GetTickCount()
# print(windll.kernel32.GetTickCount(), lastInputInfo.dwTime, sizeof(lastInputInfo), millis)
tm2 = time.time() - tm1
last_idel_time = lastInputInfo.dwTime
# print()
if value != last_idel_time:
value = last_idel_time
tm2 = time.time() - time.time()
tm1 = time.time()
# print("time:", tm1)
return tm2
def press_key_2():
global stop_threads, tm1
while True:
if not stop_threads:
break
idle_time = get_idle_duration() #seconds
# print(idle_time)
g = float("{:.2f}".format(idle_time))
st = str(g) + " / " + str(SET_IDLE_TIME)
var.set(st)
time.sleep(0.1)
if idle_time < SET_IDLE_TIME:
continue
print("in ideal state pressing cltr")
win32api.keybd_event(ord('x'), 0, win32con.KEYEVENTF_EXTENDEDKEY, 0)
tm1 = time.time()
#---------------- Monitor threads ------------------------------
t1 = threading.Thread(target=press_key_2, name='t1')
t1.daemon = True
#----------------- TK functions ----------------------
def display_on():
global tk, t1, stop_threads
stop_threads = True
print("Always On")
ctypes.windll.kernel32.SetThreadExecutionState(0x80000002)
root.iconify()
t1.start()
# t2.start()
def display_reset():
print("quit pressed")
global stop_threads
stop_threads = False
ctypes.windll.kernel32.SetThreadExecutionState(0x80000000)
sys.exit(0)
root = tk.Tk()
root.geometry("200x110")
root.title("Devil")
frame = tk.Frame(root)
frame.pack()
var = tk.StringVar()
var_idle = tk.StringVar()
label = tk.Label(frame, textvariable = var)#, bd = 5, justify = tk.RIGHT, padx = 10, pady = 10)
label_idle = tk.Label(frame,textvariable = var_idle)
var_idle.set("Idle Time")
var.set("-")
button = tk.Button(frame,
text="Quit",
fg="red",
command=display_reset)
slogan = tk.Button(frame,
text="Always ON",
command=display_on)
label_idle.pack(side=tk.BOTTOM,padx=15, pady=13)
label.pack(side=tk.BOTTOM,padx=15, pady=5)
slogan.pack(side=tk.LEFT,padx=15, pady=5)
button.pack(side=tk.LEFT,padx=15, pady=5)
root.mainloop()
ctypes.windll.kernel32.SetThreadExecutionState(0x80000000)
To add on #luc's answer, following is how the code would be more verbose about the handle it selected when multiple windows exist:
After pip install pywin32, run
import win32gui
import re
class WindowMgr:
"""Encapsulates some calls to the winapi for window management"""
def __init__ (self):
"""Constructor"""
self._handle = None
self._handles = []
def find_window(self, class_name, window_name=None):
"""find a window by its class_name"""
self._handle = win32gui.FindWindow(class_name, window_name)
def _window_enum_callback(self, hwnd, wildcard):
"""Pass to win32gui.EnumWindows() to check all the opened windows"""
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) is not None:
self._handles.append(hwnd)
self._handle = hwnd
def find_window_wildcard(self, wildcard):
"""find a window whose title matches the wildcard regex"""
self._handle = None
self._handles = []
win32gui.EnumWindows(self._window_enum_callback, wildcard)
self.set_handle()
def set_foreground(self):
"""put the window in the foreground"""
if self._handle != None:
win32gui.SetForegroundWindow(self._handle)
else:
print("No handle is selected, couldn't set focus")
def set_handle(self):
"""get one handle to operate on from all the matched handles"""
if len(self._handles) < 1:
print("Matched no window")
return False
if len(self._handles) > 1:
print("Selecting the first handle of multiple windows:")
else: # len(self._handles) == 1:
print("Matched a single window:")
self.print_matches()
self._handle = self._handles[0]
return True
def print_matches(self):
"""print the title of each matched handle"""
for hwnd in self._handles:
print("- " + str(win32gui.GetWindowText(hwnd)))
w = WindowMgr()
w.find_window_wildcard(".*Hello.*")
w.set_foreground()
Note: I couldn't make the addition by editing #luc's answer as its suggested edit queue is full.