2012-05-31 15 views
15

Próbuję dodać kilka rozszerzeń powłoki przy użyciu Pythona z ikonami i podmenu, ale staram się uzyskać znacznie więcej niż wersja demonstracyjna w pywin32. Nie mogę niczego wymyślić, wyszukując google.Menu kontekstowe Eksploratora Windows z podmenu za pomocą pywin32

wierzę muszę się zarejestrować serwera COM, aby móc zmienić opcje w menu w zależności od tego, gdzie prawo kliknięciu plik/folder jest i typ pliku itp

# 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

Witaj GP89. Napisz tutaj, jeśli znajdziesz rozwiązanie. Próbuję dodać proste menu kontekstowe, które pojawia się tylko dla określonego folderu, ale nie mam pojęcia o programowaniu Win32. –

+0

Hej, zajęło to dużo czasu, ale jestem już prawie z tym teraz. Nie mogłem znaleźć żadnego sposobu debugowania lub naprawdę żadnych przykładów w sieci. Opublikuję go następnego dnia lub dwóch :) – GP89

+0

Umieszczam odpowiedź poniżej, jeśli wyjmiesz bit pliku w rejestrze i wyrejestrujesz metody oraz 'return 0' z metody Query, jeśli nie jesteś w folder, który Cię interesuje, powinien być w stanie dość łatwo uzyskać to, czego potrzebujesz. – GP89

Odpowiedz

14

I okazało się, jak w tym celu po wielu próbach i błędach oraz w googlowaniu.

Poniższy przykład przedstawia menu z podmenu i ikonami.

# 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

Czy to działa w systemie Windows 10? Czy nie mam z tym szczęścia? – Dynite

+0

@Dynite Nie jestem pewna, przepraszam, nie użyłem systemu Windows w ciągu kilku lat. Pracowałem z Windows 7, gdy robiłem to – GP89

+0

Znalazłem kilka alternatywnych https://msdn.microsoft.com/en-gb/library/windows/desktop/cc144171(v=vs.85).aspx#cascade_subcommands i kilka komentarzy na temat ta strona (zawierająca błędy) tutaj, https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/3ee4702a-f75a-4790-9332-e50f241efd6f/unable-to-create-extended-cascaded- menu-using-extendedsubcommandskey? forum = windowsuidevelopment – Dynite

Powiązane problemy