desktopmanager.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. # Demonstrates using a taskbar icon to create and navigate between desktops
  2. import win32api, win32con, win32gui, win32service, win32process
  3. import pywintypes
  4. import traceback, _thread, time
  5. import io
  6. ## "Shell_TrayWnd" is class of system tray window, broadcasts "TaskbarCreated" when initialized
  7. def desktop_name_dlgproc(hwnd,msg,wparam,lparam):
  8. """ Handles messages from the desktop name dialog box """
  9. if msg in (win32con.WM_CLOSE,win32con.WM_DESTROY):
  10. win32gui.DestroyWindow(hwnd)
  11. elif msg == win32con.WM_COMMAND:
  12. if wparam == win32con.IDOK:
  13. desktop_name=win32gui.GetDlgItemText(hwnd, 72)
  14. print('new desktop name: ',desktop_name)
  15. win32gui.DestroyWindow(hwnd)
  16. create_desktop(desktop_name)
  17. elif wparam == win32con.IDCANCEL:
  18. win32gui.DestroyWindow(hwnd)
  19. def get_new_desktop_name(parent_hwnd):
  20. """ Create a dialog box to ask the user for name of desktop to be created """
  21. msgs={win32con.WM_COMMAND:desktop_name_dlgproc,
  22. win32con.WM_CLOSE:desktop_name_dlgproc,
  23. win32con.WM_DESTROY:desktop_name_dlgproc}
  24. # dlg item [type, caption, id, (x,y,cx,cy), style, ex style
  25. style=win32con.WS_BORDER|win32con.WS_VISIBLE|win32con.WS_CAPTION|win32con.WS_SYSMENU ## |win32con.DS_SYSMODAL
  26. h=win32gui.CreateDialogIndirect(
  27. win32api.GetModuleHandle(None),
  28. [['One ugly dialog box !',(100,100,200,100),style,0],
  29. ['Button','Create', win32con.IDOK, (10,10,30,20),win32con.WS_VISIBLE|win32con.WS_TABSTOP|win32con.BS_HOLLOW|win32con.BS_DEFPUSHBUTTON],
  30. ['Button','Never mind', win32con.IDCANCEL, (45,10,50,20),win32con.WS_VISIBLE|win32con.WS_TABSTOP|win32con.BS_HOLLOW],
  31. ['Static','Desktop name:',71,(10,40,70,10),win32con.WS_VISIBLE],
  32. ['Edit','',72,(75,40,90,10),win32con.WS_VISIBLE]],
  33. parent_hwnd, msgs) ## parent_hwnd, msgs)
  34. win32gui.EnableWindow(h,True)
  35. hcontrol=win32gui.GetDlgItem(h,72)
  36. win32gui.EnableWindow(hcontrol,True)
  37. win32gui.SetFocus(hcontrol)
  38. def new_icon(hdesk,desktop_name):
  39. """ Runs as a thread on each desktop to create a new tray icon and handle its messages """
  40. global id
  41. id=id+1
  42. hdesk.SetThreadDesktop()
  43. ## apparently the threads can't use same hinst, so each needs its own window class
  44. windowclassname='PythonDesktopManager'+desktop_name
  45. wc = win32gui.WNDCLASS()
  46. wc.hInstance = win32api.GetModuleHandle(None)
  47. wc.lpszClassName = windowclassname
  48. wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW | win32con.CS_GLOBALCLASS
  49. wc.hCursor = win32gui.LoadCursor( 0, win32con.IDC_ARROW )
  50. wc.hbrBackground = win32con.COLOR_WINDOW
  51. wc.lpfnWndProc = icon_wndproc
  52. windowclass = win32gui.RegisterClass(wc)
  53. style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
  54. hwnd = win32gui.CreateWindow(windowclass, 'dm_'+desktop_name, win32con.WS_SYSMENU,
  55. 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
  56. 0, 0, wc.hInstance, None)
  57. win32gui.UpdateWindow(hwnd)
  58. flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP
  59. notify_info = (hwnd, id, flags, win32con.WM_USER+20, hicon, 'Desktop Manager (%s)' %desktop_name)
  60. window_info[hwnd]=notify_info
  61. ## wait for explorer to initialize system tray for new desktop
  62. tray_found=0
  63. while not tray_found:
  64. try:
  65. tray_found=win32gui.FindWindow("Shell_TrayWnd",None)
  66. except win32gui.error:
  67. traceback.print_exc
  68. time.sleep(.5)
  69. win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, notify_info)
  70. win32gui.PumpMessages()
  71. def create_desktop(desktop_name, start_explorer=1):
  72. """ Creates a new desktop and spawns a thread running on it
  73. Will also start a new icon thread on an existing desktop
  74. """
  75. sa=pywintypes.SECURITY_ATTRIBUTES()
  76. sa.bInheritHandle=1
  77. try:
  78. hdesk=win32service.CreateDesktop(desktop_name, 0, win32con.MAXIMUM_ALLOWED, sa)
  79. except win32service.error:
  80. traceback.print_exc()
  81. errbuf=io.StringIO()
  82. traceback.print_exc(None,errbuf)
  83. win32api.MessageBox(0, errbuf.getvalue(), 'Desktop creation failed')
  84. return
  85. if start_explorer:
  86. s=win32process.STARTUPINFO()
  87. s.lpDesktop=desktop_name
  88. prc_info=win32process.CreateProcess(None, "Explorer.exe",None,None,True,win32con.CREATE_NEW_CONSOLE,None,'c:\\',s)
  89. th=_thread.start_new_thread(new_icon,(hdesk,desktop_name))
  90. hdesk.SwitchDesktop()
  91. def icon_wndproc(hwnd, msg, wp, lp):
  92. """ Window proc for the tray icons """
  93. if lp==win32con.WM_LBUTTONDOWN:
  94. ## popup menu won't disappear if you don't do this
  95. win32gui.SetForegroundWindow(hwnd)
  96. curr_desktop=win32service.OpenInputDesktop(0,True,win32con.MAXIMUM_ALLOWED)
  97. curr_desktop_name=win32service.GetUserObjectInformation(curr_desktop,win32con.UOI_NAME)
  98. winsta=win32service.GetProcessWindowStation()
  99. desktops=winsta.EnumDesktops()
  100. m=win32gui.CreatePopupMenu()
  101. desktop_cnt=len(desktops)
  102. ## *don't* create an item 0
  103. for d in range(1, desktop_cnt+1):
  104. mf_flags=win32con.MF_STRING
  105. ## if you switch to winlogon yourself, there's nothing there and you're stuck
  106. if desktops[d-1].lower() in ('winlogon','disconnect'):
  107. mf_flags=mf_flags|win32con.MF_GRAYED|win32con.MF_DISABLED
  108. if desktops[d-1]==curr_desktop_name:
  109. mf_flags=mf_flags|win32con.MF_CHECKED
  110. win32gui.AppendMenu(m, mf_flags, d, desktops[d-1])
  111. win32gui.AppendMenu(m, win32con.MF_STRING, desktop_cnt+1, 'Create new ...')
  112. win32gui.AppendMenu(m, win32con.MF_STRING, desktop_cnt+2, 'Exit')
  113. x,y=win32gui.GetCursorPos()
  114. d=win32gui.TrackPopupMenu(m,win32con.TPM_LEFTBUTTON|win32con.TPM_RETURNCMD|win32con.TPM_NONOTIFY,
  115. x,y, 0, hwnd, None)
  116. win32gui.PumpWaitingMessages()
  117. win32gui.DestroyMenu(m)
  118. if d==desktop_cnt+1: ## Create new
  119. get_new_desktop_name(hwnd)
  120. elif d==desktop_cnt+2: ## Exit
  121. win32gui.PostQuitMessage(0)
  122. win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, window_info[hwnd])
  123. del window_info[hwnd]
  124. origin_desktop.SwitchDesktop()
  125. elif d>0:
  126. hdesk=win32service.OpenDesktop(desktops[d-1],0,0,win32con.MAXIMUM_ALLOWED)
  127. hdesk.SwitchDesktop()
  128. return 0
  129. else:
  130. return win32gui.DefWindowProc(hwnd, msg, wp, lp)
  131. window_info={}
  132. origin_desktop=win32service.OpenInputDesktop(0, True, win32con.MAXIMUM_ALLOWED)
  133. origin_desktop_name=win32service.GetUserObjectInformation(origin_desktop, win32service.UOI_NAME)
  134. hinst=win32api.GetModuleHandle(None)
  135. try:
  136. hicon=win32gui.LoadIcon(hinst, 1) ## python.exe and pythonw.exe
  137. except win32gui.error:
  138. hicon=win32gui.LoadIcon(hinst, 135) ## pythonwin's icon
  139. id=0
  140. create_desktop(str(origin_desktop_name),0)
  141. ## wait for first thread to initialize its icon
  142. while not window_info:
  143. time.sleep(1)
  144. ## exit when last tray icon goes away
  145. while window_info:
  146. win32gui.PumpWaitingMessages()
  147. time.sleep(3)