2012-05-31 95 views
15

我想添加一些使用圖標和子菜單的Python的外殼擴展,但我努力得到遠遠比pywin32中的演示。我似乎也無法通過搜索谷歌提出任何事情。Windows資源管理器上下文菜單與子菜單使用pywin32

我相信我需要註冊一個COM服務器能夠根據在右側點擊文件,以改變在子菜單中的選項/文件夾等

# A sample context menu handler. 
# Adds a 'Hello from Python' menu entry to .py files. When clicked, a 
# simple message box is displayed. 
# 
# To demostrate: 
# * Execute this script to register the context menu. 
# * Open Windows Explorer, and browse to a directory with a .py file. 
# * Right-Click on a .py file - locate and click on 'Hello from Python' on 
# the context menu. 

import pythoncom 
from win32com.shell import shell, shellcon 
import win32gui 
import win32con 

class ShellExtension: 
    _reg_progid_ = "Python.ShellExtension.ContextMenu" 
    _reg_desc_ = "Python Sample Shell Extension (context menu)" 
    _reg_clsid_ = "{CED0336C-C9EE-4a7f-8D7F-C660393C381F}" 
    _com_interfaces_ = [shell.IID_IShellExtInit, shell.IID_IContextMenu] 
    _public_methods_ = shellcon.IContextMenu_Methods + shellcon.IShellExtInit_Methods 

    def Initialize(self, folder, dataobj, hkey): 
     print "Init", folder, dataobj, hkey 
     self.dataobj = dataobj 

    def QueryContextMenu(self, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags): 
     print "QCM", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags 
     # Query the items clicked on 
     format_etc = win32con.CF_HDROP, None, 1, -1, pythoncom.TYMED_HGLOBAL 
     sm = self.dataobj.GetData(format_etc) 
     num_files = shell.DragQueryFile(sm.data_handle, -1) 
     if num_files>1: 
      msg = "&Hello from Python (with %d files selected)" % num_files 
     else: 
      fname = shell.DragQueryFile(sm.data_handle, 0) 
      msg = "&Hello from Python (with '%s' selected)" % fname 
     idCmd = idCmdFirst 
     items = ['First Python content menu item!'] 
     if (uFlags & 0x000F) == shellcon.CMF_NORMAL: # Check == here, since CMF_NORMAL=0 
      print "CMF_NORMAL..." 
      items.append(msg) 
     elif uFlags & shellcon.CMF_VERBSONLY: 
      print "CMF_VERBSONLY..." 
      items.append(msg + " - shortcut") 
     elif uFlags & shellcon.CMF_EXPLORE: 
      print "CMF_EXPLORE..." 
      items.append(msg + " - normal file, right-click in Explorer") 
     elif uFlags & CMF_DEFAULTONLY: 
      print "CMF_DEFAULTONLY...\r\n" 
     else: 
      print "** unknown flags", uFlags 
     win32gui.InsertMenu(hMenu, indexMenu, 
          win32con.MF_SEPARATOR|win32con.MF_BYPOSITION, 
          0, None) 
     indexMenu += 1 
     for item in items: 
      win32gui.InsertMenu(hMenu, indexMenu, 
           win32con.MF_STRING|win32con.MF_BYPOSITION, 
           idCmd, item) 
      indexMenu += 1 
      idCmd += 1 

     win32gui.InsertMenu(hMenu, indexMenu, 
          win32con.MF_SEPARATOR|win32con.MF_BYPOSITION, 
          0, None) 
     indexMenu += 1 
     return idCmd-idCmdFirst # Must return number of menu items we added. 

    def InvokeCommand(self, ci): 
     mask, hwnd, verb, params, dir, nShow, hotkey, hicon = ci 
     win32gui.MessageBox(hwnd, "Hello", "Wow", win32con.MB_OK) 

    def GetCommandString(self, cmd, typ): 
     # If GetCommandString returns the same string for all items then 
     # the shell seems to ignore all but one. This is even true in 
     # Win7 etc where there is no status bar (and hence this string seems 
     # ignored) 
     return "Hello from Python (cmd=%d)!!" % (cmd,) 

def DllRegisterServer(): 
    import _winreg 
    folder_key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT, 
    "Folder\\shellex") 
    folder_subkey = _winreg.CreateKey(folder_key, "ContextMenuHandlers") 
    folder_subkey2 = _winreg.CreateKey(folder_subkey, "PythonSample") 
    _winreg.SetValueEx(folder_subkey2, None, 0, _winreg.REG_SZ, 
    ShellExtension._reg_clsid_) 

    file_key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT, 
    "*\\shellex") 
    file_subkey = _winreg.CreateKey(file_key, "ContextMenuHandlers") 
    file_subkey2 = _winreg.CreateKey(file_subkey, "PythonSample") 
    _winreg.SetValueEx(file_subkey2, None, 0, _winreg.REG_SZ, 
    ShellExtension._reg_clsid_) 

    print ShellExtension._reg_desc_, "registration complete." 

def DllUnregisterServer(): 
    import _winreg 
    try: 
     folder_key = _winreg.DeleteKey(_winreg.HKEY_CLASSES_ROOT, 

     "Folder\\shellex\\ContextMenuHandlers\\PythonSample") 
     file_key = _winreg.DeleteKey(_winreg.HKEY_CLASSES_ROOT, 

     "*\\shellex\\ContextMenuHandlers\\PythonSample ") 
    except WindowsError, details: 
     import errno 
     if details.errno != errno.ENOENT: 
      raise 
    print ShellExtension._reg_desc_, "unregistration complete." 

if __name__=='__main__': 
    from win32com.server import register 
    register.UseCommandLine(ShellExtension, 
        finalize_register = DllRegisterServer, 
        finalize_unregister = DllUnregisterServer) 
+0

嗨GP89。如果你找到解決方案,請回到這裏。我試圖添加一個簡單的上下文菜單,只顯示一個特定的文件夾,但我不知道Win32編程。 –

+0

嘿,它花了很長時間,但我現在幾乎在那裏。我找不到任何方式來進行調試或者網上的任何實例。我會在第二天或第二天發佈它:) – GP89

+0

我把答案放在下面,如果你取出寄存器中的文件位並取消註冊方法,並且如果你不在你對你感興趣的文件夾應該能夠很容易地得到你想要的文件 – GP89

回答

14

我發現了文件的類型如何經過大量的反覆試驗和谷歌搜索後才能做到這一點。

下面的例子顯示了一個帶有子菜單和圖標的菜單。

# A sample context menu handler. 
# Adds a menu item with sub menu to all files and folders, different options inside specified folder. 
# When clicked a list of selected items is displayed. 
# 
# To demostrate: 
# * Execute this script to register the context menu. `python context_menu.py --register` 
# * Restart explorer.exe- in the task manager end process on explorer.exe. Then file > new task, then type explorer.exe 
# * Open Windows Explorer, and browse to a file/directory. 
# * Right-Click file/folder - locate and click on an option under 'Menu options'. 

import os 
import pythoncom 
from win32com.shell import shell, shellcon 
import win32gui 
import win32con 
import win32api 

class ShellExtension: 
    _reg_progid_ = "Python.ShellExtension.ContextMenu" 
    _reg_desc_ = "Python Sample Shell Extension (context menu)" 
    _reg_clsid_ = "{CED0336C-C9EE-4a7f-8D7F-C660393C381F}" 
    _com_interfaces_ = [shell.IID_IShellExtInit, shell.IID_IContextMenu] 
    _public_methods_ = shellcon.IContextMenu_Methods + shellcon.IShellExtInit_Methods 

    def Initialize(self, folder, dataobj, hkey): 
     print "Init", folder, dataobj, hkey 
     win32gui.InitCommonControls() 
     self.brand= "Menu options" 
     self.folder= "C:\\Users\\Paul\\" 
     self.dataobj = dataobj 
     self.hicon= self.prep_menu_icon(r"C:\path\to\icon.ico") 


    def QueryContextMenu(self, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags): 
     print "QCM", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags 

     # Query the items clicked on 
     files= self.getFilesSelected() 

     fname = files[0] 
     idCmd = idCmdFirst 

     isdir= os.path.isdir(fname) 
     in_folder= all([f_path.startswith(self.folder) for f_path in files]) 

     win32gui.InsertMenu(hMenu, indexMenu, 
      win32con.MF_SEPARATOR|win32con.MF_BYPOSITION, 
      0, None) 
     indexMenu += 1 

     menu= win32gui.CreatePopupMenu() 
     win32gui.InsertMenu(hMenu,indexMenu,win32con.MF_STRING|win32con.MF_BYPOSITION|win32con.MF_POPUP,menu,self.brand) 
     win32gui.SetMenuItemBitmaps(hMenu,menu,0,self.hicon,self.hicon) 
#  idCmd+=1 
     indexMenu+=1 

     if in_folder: 
      if len(files) == 1: 
       if isdir: 
        win32gui.InsertMenu(menu,0,win32con.MF_STRING,idCmd,"Item 1"); idCmd+=1 
       else: 
        win32gui.InsertMenu(menu,0,win32con.MF_STRING,idCmd,"Item 2") 
        win32gui.SetMenuItemBitmaps(menu,idCmd,0,self.hicon,self.hicon) 
        idCmd+=1 
     else: 
      win32gui.InsertMenu(menu,0,win32con.MF_STRING,idCmd,"Item 3") 
      win32gui.SetMenuItemBitmaps(menu,idCmd,0,self.hicon,self.hicon) 
      idCmd+=1 

     if idCmd > idCmdFirst: 
      win32gui.InsertMenu(menu,1,win32con.MF_SEPARATOR,0,None) 

     win32gui.InsertMenu(menu,2,win32con.MF_STRING,idCmd,"Item 4") 
     win32gui.SetMenuItemBitmaps(menu,idCmd,0,self.hicon,self.hicon) 
     idCmd+=1 
     win32gui.InsertMenu(menu,3,win32con.MF_STRING,idCmd,"Item 5") 
     win32gui.SetMenuItemBitmaps(menu,idCmd,0,self.hicon,self.hicon) 
     idCmd+=1 

     win32gui.InsertMenu(menu,4,win32con.MF_SEPARATOR,0,None) 

     win32gui.InsertMenu(menu,5,win32con.MF_STRING|win32con.MF_DISABLED,idCmd,"Item 6") 
     win32gui.SetMenuItemBitmaps(menu,idCmd,0,self.hicon,self.hicon) 
     idCmd+=1 

     win32gui.InsertMenu(hMenu, indexMenu, 
          win32con.MF_SEPARATOR|win32con.MF_BYPOSITION, 
          0, None) 
     indexMenu += 1 
     return idCmd-idCmdFirst # Must return number of menu items we added. 

    def getFilesSelected(self): 
     format_etc = win32con.CF_HDROP, None, 1, -1, pythoncom.TYMED_HGLOBAL 
     sm = self.dataobj.GetData(format_etc) 
     num_files = shell.DragQueryFile(sm.data_handle, -1) 
     files= [] 
     for i in xrange(num_files): 
      fpath= shell.DragQueryFile(sm.data_handle,i) 
      files.append(fpath) 
     return files 

    def prep_menu_icon(self, icon): #Couldn't get this to work with pngs, only ico 
     # First load the icon. 
     ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON) 
     ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON) 
     hicon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, ico_x, ico_y, win32con.LR_LOADFROMFILE) 

     hdcBitmap = win32gui.CreateCompatibleDC(0) 
     hdcScreen = win32gui.GetDC(0) 
     hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y) 
     hbmOld = win32gui.SelectObject(hdcBitmap, hbm) 
     # Fill the background. 
     brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU) 
     win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush) 
     # unclear if brush needs to be feed. Best clue I can find is: 
     # "GetSysColorBrush returns a cached brush instead of allocating a new 
     # one." - implies no DeleteObject 
     # draw the icon 
     win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x, ico_y, 0, 0, win32con.DI_NORMAL) 
     win32gui.SelectObject(hdcBitmap, hbmOld) 
     win32gui.DeleteDC(hdcBitmap) 

     return hbm 

    def InvokeCommand(self, ci): 
     mask, hwnd, verb, params, dir, nShow, hotkey, hicon = ci 
     win32gui.MessageBox(hwnd, str(self.getFilesSelected()), "Wow", win32con.MB_OK) 

    def GetCommandString(self, cmd, typ): 
     # If GetCommandString returns the same string for all items then 
     # the shell seems to ignore all but one. This is even true in 
     # Win7 etc where there is no status bar (and hence this string seems 
     # ignored) 
     return "Hello from Python (cmd=%d)!!" % (cmd,) 

def DllRegisterServer(): 
    import _winreg 
    folder_key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT, 
    "Folder\\shellex") 
    folder_subkey = _winreg.CreateKey(folder_key, "ContextMenuHandlers") 
    folder_subkey2 = _winreg.CreateKey(folder_subkey, "PythonSample") 
    _winreg.SetValueEx(folder_subkey2, None, 0, _winreg.REG_SZ, 
    ShellExtension._reg_clsid_) 

    file_key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT, 
    "*\\shellex") 
    file_subkey = _winreg.CreateKey(file_key, "ContextMenuHandlers") 
    file_subkey2 = _winreg.CreateKey(file_subkey, "PythonSample") 
    _winreg.SetValueEx(file_subkey2, None, 0, _winreg.REG_SZ, 
    ShellExtension._reg_clsid_) 

    print ShellExtension._reg_desc_, "registration complete." 

def DllUnregisterServer(): 
    import _winreg 
    try: 
     folder_key = _winreg.DeleteKey(_winreg.HKEY_CLASSES_ROOT, 

     "Folder\\shellex\\ContextMenuHandlers\\PythonSample") 
     file_key = _winreg.DeleteKey(_winreg.HKEY_CLASSES_ROOT, 

     "*\\shellex\\ContextMenuHandlers\\PythonSample") 
    except WindowsError, details: 
     import errno 
     if details.errno != errno.ENOENT: 
      raise 
    print ShellExtension._reg_desc_, "unregistration complete." 

if __name__=='__main__': 
    from win32com.server import register 
    register.UseCommandLine(ShellExtension, 
        finalize_register = DllRegisterServer, 
        finalize_unregister = DllUnregisterServer) 
+0

這是否可以在Windows 10上運行?沒有太多的運氣呢? – Dynite

+0

@Dynite我不太確定,現在幾年來我還沒有使用Windows系統。正在使用Windows 7時,當我這樣做 – GP89

+0

找到一些替代方法https://msdn.microsoft.com/en-gb/library/windows/desktop/cc144171(v=vs.85).aspx#cascade_subcommands和一些意見該頁面(因爲它包含錯誤)在這裏,https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/3ee4702a-f75a-4790-9332-e50f241efd6f/unable-to-create-extended-cascaded-菜單採用-extendedsubcommandskey?論壇= windowsuidevelopment – Dynite

相關問題