How to close two windows of the same application using python? - python

I'm working on an automation program in python, I've two windows of cmd open on my screen. I want my code to only close the first window, and leave the second window as it is. How will I make my code determine which window to close?
To close the window I'm using the keyboard package in python, the code writes exit in the cmd window and generates an enter key press to exit the window.
keyboard.write("exit") # closes main cmd window
keyboard.press_and_release("enter") # window closes

If you use Windows OS, you can use the pywin32 and win32gui libraries which contain the modules win32gui and win32con which will help search through windows matching a certain name and getting the window handle number (hwnd). Given a hwnd, you can then close the first window that was opened.
You will first need to install pywin32 and wind32gui:
pip install win32gui
pip install pywin32
Here's the code to close the first command prompt window:
import win32gui
import win32con
def windowEnumerationHandler(hwnd, top_windows):
top_windows.append((hwnd, win32gui.GetWindowText(hwnd)))
def return_window_hwnd(window_name):
'''Return list window handles of that match a given window name'''
windows = []
win32gui.EnumWindows(windowEnumerationHandler, windows)
print(windows) #[(67084, 'Command Prompt'), (65868, ''), (722426, 'Command Prompt'), ...]
hwnds = []
for wind in windows:
if window_name in wind[1] or window_name == wind[1]:
hwnds.append(wind[0])
return hwnds
# search for Command Prompt windows and return the list of hwnd
command_hwnd_arr = return_window_hwnd('Command Prompt')
# if more than 1 is window open, close the first opened window
if len(command_hwnd_arr) > 1:
window_handle_to_close = command_hwnd_arr[1]
win32gui.PostMessage(window_handle_to_close, win32con.WM_CLOSE, 0, 0)

Related

How to update PyQT5 application for both Windows and macOS?

I have an application built with PyQT5 for both Windows and macOS. Currently, the user checks for updates by clicking the button and when there is a new update available I am redirecting them to the browser to my server to download the latest .exe (Windows) or .pkg (macOS). The issue is for say if the user downloads and installs the latest version in a different location than the previous one which will result in two instances of the same application.
I want to improve the user experience and make an auto-updater like all the established applications. When the user clicks the updates the application should download the new updates without making any hassles for the users and update the application for both the OS.
For Windows, I am using Pyinstaller to make the .exe file and then Inno Setup to make it executable. Moreover, for macOS I am using setuptools to make the .app and macOS packages app to make it executable.
It would be really great if someone could help me to implement an update feature for my PyQT5 application.
Hi there I have made a program where it can update itself, I was using tkinter, but it should work for PyQT5 if the same widgets are there. We are using GitHub for the program to download from there. Also I am on windows so I don't know if this works on mac. Here is my code:
import tkinter as tk #for you it is pyqt5
from tkinter import * #MessageBox and Button
import requests #pip install requests
import os #part of standard library
import sys #part of standard library
VERSION = 4
b1 = Button(frame, text = "Back", command = homepage)
b1.pack(ipadx= 10, ipady = 10, fill = X, expand = False, side = TOP)
checkupdate = Label(frame, text = "Looking for updates", font = ("Arial", 14))
checkupdate.pack()
try:
link = "https://raw.githubusercontent.com/SomeUser/SomeRepo/main/SomeFolder/version.txt"
check = requests.get(link)
if float(VERSION) < float(check.text):
mb1 = messagebox.askyesno('Update Available', 'There is an update available. Click yes to update.')
if mb1 is True:
filename = os.path.basename(sys.argv[0])
for file in os.listdir():
if file == filename:
pass
else:
os.remove(file)
exename = f'NameOfYourApp{float(check.text)}.exe'
code = requests.get("https://raw.githubusercontent.com/SomeUser/SomeRepo/main/SomeFolder/NewUpdate.exe", allow_redirects = True)
open(exename, 'wb').write(code.content)
root.destroy()
os.remove(sys.argv[0])
sys.exit()
elif mb1 == 'No':
pass
else:
messagebox.showinfo('Updates Not Available', 'No updates are available')
except Exception as e:
pass
Make sure you convert all the tkinter code to PyQt5 widgets. Also this is windows change the "https://raw.githubusercontent.com/SomeUser/SomeRepo/main/SomeFolder/NewUpdate.exe" to "https://raw.githubusercontent.com/SomeUser/SomeRepo/main/SomeFolder/NewUpdate.app" for mac. If this does not work comment and I will try my best to convert this to PyQt5.

Python activate windows with the same name

So what I need do to is periodically change the active window, my issue is that they all have the same name and using their HWND only works for the first window. Besides that i'd like to not have to insert it's HWND everytime
import win32gui, time
def main():
while(1):
win32gui.SetForegroundWindow(788574)#win2
side()
time.sleep(5)
def side():
while(1):
win32gui.SetForegroundWindow(3147934)#win1
main()
time.sleep(5)
if __name__ == '__main__':
main()
To cycle through selected windows, there's a few steps:
Use win32gui.EnumWindows to go through all open windows
Use win32gui.GetWindowText to get the title bar text from a window
Use win32com.client.Dispatch and SendKeys to activate the switch process
Use win32gui.SetForegroundWindow to select the window to activate
Here's the code:
import win32com.client as win32
import win32gui
import time
title = "Untitled - Notepad2" # cycle all windows with this title
def windowEnumerationHandler(hwnd, top_windows):
top_windows.append((hwnd, win32gui.GetWindowText(hwnd)))
top_windows = [] # all open windows
win32gui.EnumWindows(windowEnumerationHandler, top_windows)
winlst = [] # windows to cycle through
for i in top_windows: # all open windows
if i[1] == title:
winlst.append(i)
for x in range(5): # cycle 5 times
for w in winlst: # each window with selected title
shell = win32.Dispatch("WScript.Shell") # set focus on desktop
shell.SendKeys('%') # Alt key
win32gui.SetForegroundWindow(w[0]) # bring to front, activate
time.sleep(2) # 2 seconds

Bring window to focus with python periodically

import win32gui
import time
def windowEnumerationHandler(hwnd, top_windows):
top_windows.append((hwnd, win32gui.GetWindowText(hwnd)))
if __name__ == "__main__":
top_windows = []
win32gui.EnumWindows(windowEnumerationHandler, top_windows)
for i in top_windows:
print(i)
if "zoom" in i[1].lower():
print(i, 'is found')
while True:
win32gui.ShowWindow(i[0],5)
win32gui.SetForegroundWindow(i[0])
time.sleep(1)
I've heard that zoom monitors whether the window is not in focus for more than 30 seconds, so I've been working on a way to repetitively throw it to the front while I work on other projects. The problem is the code raises an exception
0, 'SetForegroundWindow', 'No error message is available'
and the window just flashes yellow. Same problem with chrome as well. Would appreciate some help here :)
I had the same problem while I was trying to SetForegroundWindow(hwnd). The icon on the taskbar was just flashing, but the program stayed in the background. As you can read here:
https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setforegroundwindow?redirectedfrom=MSDN
"An application cannot force a window to the foreground while the user is working with another window. Instead, Windows flashes the taskbar button of the window to notify the user."
For me helped:
import win32gui, win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys('%')
win32gui.SetForegroundWindow(hwnd)

Python win32api get "stack" of windows

I'm looking for a way to find out what order windows are open on my desktop in order to tell what parts of what windows are visible to the user.
Say, in order, I open up a maximized chrome window, a maximized notepad++ window, and then a command prompt that only covers a small portion of the screen. Is there a way using the win32api (or possibly other library) that can tell me the stack of windows open so I can take the window dimensions and find out what is visible? I already know how to get which window has focus and the top-level window, but I'm looking for more info than that.
In the example I mentioned above, I'd return that the full command prompt is visible but in the places it isn't, the notepad++ window is visible for example. No part of the chrome window would be visible.
import win32gui
import win32con
def get_windows():
def sort_windows(windows):
sorted_windows = []
# Find the first entry
for window in windows:
if window["hwnd_above"] == 0:
sorted_windows.append(window)
break
else:
raise(IndexError("Could not find first entry"))
# Follow the trail
while True:
for window in windows:
if sorted_windows[-1]["hwnd"] == window["hwnd_above"]:
sorted_windows.append(window)
break
else:
break
# Remove hwnd_above
for window in windows:
del(window["hwnd_above"])
return sorted_windows
def enum_handler(hwnd, results):
window_placement = win32gui.GetWindowPlacement(hwnd)
results.append({
"hwnd":hwnd,
"hwnd_above":win32gui.GetWindow(hwnd, win32con.GW_HWNDPREV), # Window handle to above window
"title":win32gui.GetWindowText(hwnd),
"visible":win32gui.IsWindowVisible(hwnd) == 1,
"minimized":window_placement[1] == win32con.SW_SHOWMINIMIZED,
"maximized":window_placement[1] == win32con.SW_SHOWMAXIMIZED,
"rectangle":win32gui.GetWindowRect(hwnd) #(left, top, right, bottom)
})
enumerated_windows = []
win32gui.EnumWindows(enum_handler, enumerated_windows)
return sort_windows(enumerated_windows)
if __name__ == "__main__":
windows = get_windows()
for window in windows:
print(window)
print()
# Pretty print
for window in windows:
if window["title"] == "" or not window["visible"]:
continue
print(window)
Microsoft MSDN has good artice on zorder info with GetWindow() and GW_HWNDNEXT
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx

How do I use Tkinter and allow my application to keep the focus?

I have a small application that the user can interact with as a command line. I want the user to be able to copy to the Windows clipboard information that the application has just displayed on-screen. Obviously, the user can do this manually, but it takes several steps: right-click on the window, select "Mark", select the rectangle of text, and press Enter to copy it. I want to allow the user to do this automatically by typing a short command like "cb" or "copy".
Per this answer, an easy way to get clipboard functionality is using the tkinter library. This does indeed work well. However, I find that when my application starts up, it loses the focus. It seems that a hidden window (opened by Tk() and then hidden by withdraw()) has it. The act of hiding the window with withdraw() did not give focus back to my application. This is inconvenient, because having opened the application, the user has to manually switch back to it rather than being able to just begin typing.
I want to create a tkinter object and either give the focus back to my application after I hide the new window, or have my application not lose focus in the first place. How can I do this?
There are various questions already relating to tkinter and focus, but they seem generally to relate to giving focus to the windows that tkinter itself opens, whereas I want to keep focus on the original window of my application, and deny it to the tkinter window.
I'm working at a Windows 8 machine.
Pastebin http://pastebin.com/6jsasiNE
On Windows NT, Windows Server 2003, and Windows 7+
You don't need to use Tkinter at all to achieve your goal.
clip.py:
import subprocess
def write_to_clipboard(string):
p = subprocess.Popen(['clip'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p.communicate(input=string)
This code just calls the standard windows clip.exe utility, pasting whatever passed in the string variable.
Usage:
from clip import write_to_clipboard
try:
while True:
write_to_clipboard(raw_input())
except KeyboardInterrupt:
pass
On Windows 95, 98, ME, and XP
Those versions of windows don't come with clip.exe, so here's a python only version:
clip.py:
import subprocess
def write_to_clipboard(string):
p = subprocess.Popen(['python', __file__], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p.communicate(input=string)
if __name__ == "__main__":
import sys
from Tkinter import Tk
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append(sys.stdin.read())
r.update()
r.destroy()
This will work on all version of windows and in face any OS supporting TK.
Note that you must run the Tk code in a separate process like this, because even though we call r.destroy(), Tk seems to "lock" the clipboard (no other process can access the clipboard until this process has exited).
Reading and Writing Clipboard
If you want to be able to read from the clipboard as well as write to it, this is the solution.
clip.py:
import subprocess
def write(string):
p = subprocess.Popen(['python', __file__, 'write'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p.communicate(input=string)
def read():
p = subprocess.Popen(['python', __file__, 'read'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return p.communicate()[0]
if __name__ == "__main__":
import sys
from Tkinter import Tk
if len(sys.argv) != 2:
sys.exit(1)
r = Tk()
try:
r.withdraw()
if sys.argv[1] == "write":
r.clipboard_clear()
r.clipboard_append(sys.stdin.read())
r.update()
elif sys.argv[1] == "read":
sys.stdout.write(r.clipboard_get()),
else:
sys.exit(1)
finally:
r.destroy()
Usage:
import clip
print "clipboard contains: %s" % clip.read()

Categories

Resources