|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- # Demonstrates using a taskbar icon to create and navigate between desktops
-
- import _thread
- import io
- import time
- import traceback
-
- import pywintypes
- import win32api
- import win32con
- import win32gui
- import win32process
- import win32service
-
- ## "Shell_TrayWnd" is class of system tray window, broadcasts "TaskbarCreated" when initialized
-
-
- def desktop_name_dlgproc(hwnd, msg, wparam, lparam):
- """Handles messages from the desktop name dialog box"""
- if msg in (win32con.WM_CLOSE, win32con.WM_DESTROY):
- win32gui.DestroyWindow(hwnd)
- elif msg == win32con.WM_COMMAND:
- if wparam == win32con.IDOK:
- desktop_name = win32gui.GetDlgItemText(hwnd, 72)
- print("new desktop name: ", desktop_name)
- win32gui.DestroyWindow(hwnd)
- create_desktop(desktop_name)
-
- elif wparam == win32con.IDCANCEL:
- win32gui.DestroyWindow(hwnd)
-
-
- def get_new_desktop_name(parent_hwnd):
- """Create a dialog box to ask the user for name of desktop to be created"""
- msgs = {
- win32con.WM_COMMAND: desktop_name_dlgproc,
- win32con.WM_CLOSE: desktop_name_dlgproc,
- win32con.WM_DESTROY: desktop_name_dlgproc,
- }
- # dlg item [type, caption, id, (x,y,cx,cy), style, ex style
- style = (
- win32con.WS_BORDER
- | win32con.WS_VISIBLE
- | win32con.WS_CAPTION
- | win32con.WS_SYSMENU
- ) ## |win32con.DS_SYSMODAL
- h = win32gui.CreateDialogIndirect(
- win32api.GetModuleHandle(None),
- [
- ["One ugly dialog box !", (100, 100, 200, 100), style, 0],
- [
- "Button",
- "Create",
- win32con.IDOK,
- (10, 10, 30, 20),
- win32con.WS_VISIBLE
- | win32con.WS_TABSTOP
- | win32con.BS_HOLLOW
- | win32con.BS_DEFPUSHBUTTON,
- ],
- [
- "Button",
- "Never mind",
- win32con.IDCANCEL,
- (45, 10, 50, 20),
- win32con.WS_VISIBLE | win32con.WS_TABSTOP | win32con.BS_HOLLOW,
- ],
- ["Static", "Desktop name:", 71, (10, 40, 70, 10), win32con.WS_VISIBLE],
- ["Edit", "", 72, (75, 40, 90, 10), win32con.WS_VISIBLE],
- ],
- parent_hwnd,
- msgs,
- ) ## parent_hwnd, msgs)
-
- win32gui.EnableWindow(h, True)
- hcontrol = win32gui.GetDlgItem(h, 72)
- win32gui.EnableWindow(hcontrol, True)
- win32gui.SetFocus(hcontrol)
-
-
- def new_icon(hdesk, desktop_name):
- """Runs as a thread on each desktop to create a new tray icon and handle its messages"""
- global id
- id = id + 1
- hdesk.SetThreadDesktop()
- ## apparently the threads can't use same hinst, so each needs its own window class
- windowclassname = "PythonDesktopManager" + desktop_name
- wc = win32gui.WNDCLASS()
- wc.hInstance = win32api.GetModuleHandle(None)
- wc.lpszClassName = windowclassname
- wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW | win32con.CS_GLOBALCLASS
- wc.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
- wc.hbrBackground = win32con.COLOR_WINDOW
- wc.lpfnWndProc = icon_wndproc
- windowclass = win32gui.RegisterClass(wc)
- style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
- hwnd = win32gui.CreateWindow(
- windowclass,
- "dm_" + desktop_name,
- win32con.WS_SYSMENU,
- 0,
- 0,
- win32con.CW_USEDEFAULT,
- win32con.CW_USEDEFAULT,
- 0,
- 0,
- wc.hInstance,
- None,
- )
- win32gui.UpdateWindow(hwnd)
- flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP
- notify_info = (
- hwnd,
- id,
- flags,
- win32con.WM_USER + 20,
- hicon,
- "Desktop Manager (%s)" % desktop_name,
- )
- window_info[hwnd] = notify_info
- ## wait for explorer to initialize system tray for new desktop
- tray_found = 0
- while not tray_found:
- try:
- tray_found = win32gui.FindWindow("Shell_TrayWnd", None)
- except win32gui.error:
- traceback.print_exc
- time.sleep(0.5)
- win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, notify_info)
- win32gui.PumpMessages()
-
-
- def create_desktop(desktop_name, start_explorer=1):
- """Creates a new desktop and spawns a thread running on it
- Will also start a new icon thread on an existing desktop
- """
- sa = pywintypes.SECURITY_ATTRIBUTES()
- sa.bInheritHandle = 1
-
- try:
- hdesk = win32service.CreateDesktop(
- desktop_name, 0, win32con.MAXIMUM_ALLOWED, sa
- )
- except win32service.error:
- traceback.print_exc()
- errbuf = io.StringIO()
- traceback.print_exc(None, errbuf)
- win32api.MessageBox(0, errbuf.getvalue(), "Desktop creation failed")
- return
- if start_explorer:
- s = win32process.STARTUPINFO()
- s.lpDesktop = desktop_name
- prc_info = win32process.CreateProcess(
- None,
- "Explorer.exe",
- None,
- None,
- True,
- win32con.CREATE_NEW_CONSOLE,
- None,
- "c:\\",
- s,
- )
-
- th = _thread.start_new_thread(new_icon, (hdesk, desktop_name))
- hdesk.SwitchDesktop()
-
-
- def icon_wndproc(hwnd, msg, wp, lp):
- """Window proc for the tray icons"""
- if lp == win32con.WM_LBUTTONDOWN:
- ## popup menu won't disappear if you don't do this
- win32gui.SetForegroundWindow(hwnd)
-
- curr_desktop = win32service.OpenInputDesktop(0, True, win32con.MAXIMUM_ALLOWED)
- curr_desktop_name = win32service.GetUserObjectInformation(
- curr_desktop, win32con.UOI_NAME
- )
- winsta = win32service.GetProcessWindowStation()
- desktops = winsta.EnumDesktops()
- m = win32gui.CreatePopupMenu()
- desktop_cnt = len(desktops)
- ## *don't* create an item 0
- for d in range(1, desktop_cnt + 1):
- mf_flags = win32con.MF_STRING
- ## if you switch to winlogon yourself, there's nothing there and you're stuck
- if desktops[d - 1].lower() in ("winlogon", "disconnect"):
- mf_flags = mf_flags | win32con.MF_GRAYED | win32con.MF_DISABLED
- if desktops[d - 1] == curr_desktop_name:
- mf_flags = mf_flags | win32con.MF_CHECKED
- win32gui.AppendMenu(m, mf_flags, d, desktops[d - 1])
- win32gui.AppendMenu(m, win32con.MF_STRING, desktop_cnt + 1, "Create new ...")
- win32gui.AppendMenu(m, win32con.MF_STRING, desktop_cnt + 2, "Exit")
-
- x, y = win32gui.GetCursorPos()
- d = win32gui.TrackPopupMenu(
- m,
- win32con.TPM_LEFTBUTTON | win32con.TPM_RETURNCMD | win32con.TPM_NONOTIFY,
- x,
- y,
- 0,
- hwnd,
- None,
- )
- win32gui.PumpWaitingMessages()
- win32gui.DestroyMenu(m)
- if d == desktop_cnt + 1: ## Create new
- get_new_desktop_name(hwnd)
- elif d == desktop_cnt + 2: ## Exit
- win32gui.PostQuitMessage(0)
- win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, window_info[hwnd])
- del window_info[hwnd]
- origin_desktop.SwitchDesktop()
- elif d > 0:
- hdesk = win32service.OpenDesktop(
- desktops[d - 1], 0, 0, win32con.MAXIMUM_ALLOWED
- )
- hdesk.SwitchDesktop()
- return 0
- else:
- return win32gui.DefWindowProc(hwnd, msg, wp, lp)
-
-
- window_info = {}
- origin_desktop = win32service.OpenInputDesktop(0, True, win32con.MAXIMUM_ALLOWED)
- origin_desktop_name = win32service.GetUserObjectInformation(
- origin_desktop, win32service.UOI_NAME
- )
-
- hinst = win32api.GetModuleHandle(None)
- try:
- hicon = win32gui.LoadIcon(hinst, 1) ## python.exe and pythonw.exe
- except win32gui.error:
- hicon = win32gui.LoadIcon(hinst, 135) ## pythonwin's icon
- id = 0
-
- create_desktop(str(origin_desktop_name), 0)
-
- ## wait for first thread to initialize its icon
- while not window_info:
- time.sleep(1)
-
- ## exit when last tray icon goes away
- while window_info:
- win32gui.PumpWaitingMessages()
- time.sleep(3)
|