代码之家  ›  专栏  ›  技术社区  ›  Itzik Kaplan

Python tkinter。filedialog askfolder干扰clr

  •  6
  • Itzik Kaplan  · 技术社区  · 7 年前

    我主要在Spyder中工作,构建需要弹出文件夹或文件浏览窗口的脚本。

    下面的代码在spyder中运行良好。 在Pycharm中 askopenfilename 工作正常 askdirectory 什么也不做(卡住)。 但是,如果在调试模式下运行,脚本工作得很好。 我尝试从SAS jsl运行脚本-同样的问题。

    知道我该怎么办吗? Python 3.6 Pycharm 2017.2

    谢谢

    我使用的代码包括:

    import clr #pythonnet 2.3.0
    import os
    import tkinter as tk
    from tkinter.filedialog import (askdirectory,askopenfilename)
    
    root = tk.Tk()
    root.withdraw()
    PPath=askdirectory(title="Please select your installation folder location", initialdir=r"C:\Program Files\\")
    
    t="Please select jdk file"
    if os.path.exists(os.path.expanduser('~\Documents')):
        FFile = askopenfilename(filetypes=(("jdk file", "*.jdk"),("All Files", "*.*")),title=t, initialdir=os.path.expanduser('~\Documents'))
    else:
        FFile= askopenfilename(filetypes=(("jdk file", "*.jdk"),("All Files", "*.*")),title=t)
    
    sys.path.append(marsDllPath)
    a = clr.AddReference('MatlabFunctions')
    aObj = a.CreateInstance('Example.MatlabFunctions.MatLabFunctions')
    

    编辑:看起来像是与pythonnet“imoprt clr”相关的问题,但我确实需要在代码中使用它。

    这里提出了类似的问题: https://github.com/pythonnet/pythonnet/issues/648

    2 回复  |  直到 7 年前
        1
  •  7
  •   CommonSense    7 年前

    你的问题相当平庸,虽然不太明显。问题不在 tinker pythonnet ,它源于COM线程模型。

    首先,由于您使用 clr ,让我们尝试直接使用对话框(不一定要导入 修补工 模块):

    #   importing pythonnet
    import clr
    
    #   adding reference (if necessary) to WinForms and importing dialogs
    #   clr.AddReference('System.Windows.Forms')
    from System.Windows.Forms import OpenFileDialog, FolderBrowserDialog
    
    #   creating instances of dialogs
    folder_dialog = FolderBrowserDialog()
    file_dialog = OpenFileDialog()
    
    #   try to show any of them
    folder_dialog.ShowDialog()
    file_dialog.ShowDialog()
    

    正如你所看到的,它就像你的情况一样。如上所述,原因来自线程的单元状态( [1] ,则, [2] )。

    因此 clr公司 隐式将此状态设置为MTA(多线程单元),可以通过 CoGetApartmentType 功能:

    #   importing ctypes stuff
    import ctypes
    get_apartment = ctypes.windll.ole32.CoGetApartmentType
    
    #   comment/uncomment this import to see the difference
    #   import clr
    
    apt_type = ctypes.c_uint(0)
    apt_qualifier = ctypes.c_uint(0)
    
    if get_apartment(ctypes.byref(apt_type), ctypes.byref(apt_qualifier)) == 0:
        #   APPTYPE enum: https://msdn.microsoft.com/en-us/library/windows/desktop/ms693793(v=vs.85).aspx
        #   APTTYPEQUALIFIER enum: https://msdn.microsoft.com/en-us/library/windows/desktop/dd542638(v=vs.85).aspx
        print('APTTYPE = %d\tAPTTYPEQUALIFIER = %d' % (apt_type.value, apt_qualifier.value))
    else:
        print('COM model not initialized!')
    

    但是,许多旧的COM对象(如shell对话框)需要STA模式。 关于这两种状态之间的差异,可以找到很好的解释 here there

    最后,解决方案:

    1) 将STA线程用于对话框:

    #   importing tkinter stuff
    import tkinter as tk
    from tkinter.filedialog import askdirectory, askopenfilename
    
    #   importing pythonnet
    import clr
    
    #   adding reference (if necessary) to WinForms and importing dialogs
    #clr.AddReference('System.Windows.Forms')
    from System.Windows.Forms import OpenFileDialog, FolderBrowserDialog
    
    #   adding reference (if necessary) to Threading and importing Thread functionality
    #clr.AddReference('System.Threading')
    from System.Threading import Thread, ThreadStart, ApartmentState
    
    
    #   WinForms thread function example
    def dialog_thread():
        folder_dialog = FolderBrowserDialog()
        file_dialog = OpenFileDialog()
    
        folder_dialog.ShowDialog()
        file_dialog.ShowDialog()
    
    #   Tk thread function example
    def tk_dialog_thread():
        root = tk.Tk()
        root.withdraw()
    
        askdirectory()
        askopenfilename()
    
    #   check again apartment state at start
    current_state = Thread.CurrentThread.GetApartmentState()
    if current_state == ApartmentState.STA:
        print('Current state: STA')
    elif current_state == ApartmentState.MTA:
        print('Current state: MTA')
    
    #   start dialogs via CLR
    thread = Thread(ThreadStart(dialog_thread))
    thread.SetApartmentState(ApartmentState.STA)
    thread.Start()
    thread.Join()
    
    #   start dialogs via Tkinter
    thread = Thread(ThreadStart(tk_dialog_thread))
    thread.SetApartmentState(ApartmentState.STA)
    thread.Start()
    thread.Join()
    

    2) 通过强制STA模式 CoInitialize / CoInitializeEx CLR对MTA执行此操作之前:

    #   importing ctypes stuff
    import ctypes
    co_initialize = ctypes.windll.ole32.CoInitialize
    
    #   importing tkinter stuff
    import tkinter as tk
    from tkinter.filedialog import askdirectory, askopenfilename
    
    #   Force STA mode
    co_initialize(None)
    
    # importing pythonnet
    import clr 
    
    #   dialogs test
    root = tk.Tk()
    root.withdraw()
    
    askdirectory()
    askopenfilename()
    
        2
  •  0
  •   Niranjan    7 年前

    我已经测试了您粘贴在Pycharm 2018.1.3上的代码,其中python-3.6.5安装在win-7 64位机器上。它工作正常,没有任何错误。2017版中几乎没有bug。尝试升级到Pycharm的最新版本