如何使用Win32COM在Python中检索Excel Iribbonui实例?

发布于 2025-02-13 16:50:32 字数 4552 浏览 0 评论 0原文

我正在尝试使用Python的win32com软件包将Excel加载项色带上的特定控件无效,以便重新呼叫。

阅读Microsoft的对象引用 iribbonui 我所看到的所有建议是保存一个在其onload回调中引用功能区。这也是我过去在VBA编码时完成此操作的方式。不幸的是,我不确定如何使用Python的win32com软件包到达这一点。

我正在使用 pyxll 作为使用Python代码代码的手段而不是Xlam。通过使用此功能,我能够访问功能区 onload 回调中的功能区com对象,但在onclick on Callback 中不在功能区中的回调,因为唯一的传递到这些函数的参数是控件的com对象。

将一些恢复调试信息

  • 参数传递到功能区on Lota回调的实例是win32com.gen_py.gen_py.microsoft Office 16.0对象库库库
  • 参数的_OLEOBJ _OLEOBJ _OLEOBJ _OLEOBJ _OLEOBJ _ _ _ 值是PyidisPatch实例,
  • 错误似乎是围绕dispatchbaseclass in win32com.client
class DispatchBaseClass:
    def __init__(self, oobj=None):
        if oobj is None:
            oobj = pythoncom.new(self.CLSID)
        elif isinstance(oobj, DispatchBaseClass):
            try:
                oobj = oobj._oleobj_.QueryInterface(
                    self.CLSID, pythoncom.IID_IDispatch
                )  # Must be a valid COM instance
            except pythoncom.com_error as details:
                import winerror

                # Some stupid objects fail here, even tho it is _already_ IDispatch!!??
                # Eg, Lotus notes.
                # So just let it use the existing object if E_NOINTERFACE
                if details.hresult != winerror.E_NOINTERFACE:
                    raise
                oobj = oobj._oleobj_
        self.__dict__["_oleobj_"] = oobj  # so we dont call __setattr__
  • 我的想法是保存对实际类的参考,重新创建它,设置clsid类变量,然后实例化新课。
def ribbon_loaded(ribbon):
    r_class = ribbon.__class__

    # was thinking I could pickle this class for use elsewhere
    class RibbonUI(r_class):  # also tried type(r_class)
        CLSID = ribbon.CLSID
    
    # just checking below that this would work

    # if CLSID is not set on the new class above __init__ fails
    # but, even when provided __new__ fails with the next line
    new_ribbon = RibbonUI()

    new_ribbon.InvalidateControl("ribbon_control_1")

如果不设置CLSID在新类上,错误是

2022-07-06 20:48:09,294 - INFO : ribbon.CLSID: IID('{000C03A7-0000-0000-C000-000000000046}')
2022-07-06 20:48:09,314 - ERROR : Error calling ribbon function 'components.ribbon.ribbon_loaded'
2022-07-06 20:48:15,364 - ERROR : Traceback (most recent call last):
2022-07-06 20:48:15,364 - ERROR :   File "C:\Users\mhill\PycharmProjects\excel-addin\components\ribbon.py", line 46, in ribbon_loaded
2022-07-06 20:48:15,365 - ERROR :     new_ribbon = RibbonUI()
2022-07-06 20:48:15,366 - ERROR :   File "c:\users\mhill\PyCharmProjects\excel-addin\.venv\lib\site-packages\win32com\client\__init__.py", line 514, in __init__
2022-07-06 20:48:15,366 - ERROR :     oobj = pythoncom.new(self.CLSID)
2022-07-06 20:48:15,366 - ERROR : pywintypes.com_error: (-2147221164, 'Class not registered', None, None)

在创建新类并指定CLSID类变量时,错误变为

2022-07-06 20:48:22,429 - INFO : ribbon.CLSID: IID('{000C03A7-0000-0000-C000-000000000046}')
2022-07-06 20:48:22,429 - ERROR : Error calling ribbon function 'components.ribbon.ribbon_loaded'
2022-07-06 20:48:28,949 - ERROR : Traceback (most recent call last):
2022-07-06 20:48:28,950 - ERROR :   File "C:\Users\mhill\PycharmProjects\excel-addin\components\ribbon.py", line 46, in ribbon_loaded
2022-07-06 20:48:28,951 - ERROR :     new_ribbon = RibbonUI()
2022-07-06 20:48:28,951 - ERROR : TypeError: type.__new__() takes exactly 3 arguments (0 given)

相关问题提到了创建IDTextensibility2对象的一些问题,但不确定该如何处理。

除了模仿从功能区的onload回调的情况

下,在Microsoft文档所建议的那样,除了模仿将全局引用对功能区的全局引用外,我如何在功能区onload Callback中的Iribbonui com对象和能够在控件的onclick回调中访问它吗?

I'm trying to invalidate specific controls on an Excel add-in ribbon using Python's win32com package in order to have their callbacks re-fire.

Reading over Microsoft's Object References and the IRibbonUI documentation all I've seen recommended is to save a reference to the ribbon in its onLoad callback. This is also how I've accomplished this in the past when coding in VBA. Unfortunately, I'm not sure how to get to that point using python's win32com package.

I'm using PyXLL as a means to code the add-in using Python and for future distribution as an xll instead of xlam. By using this I'm able to access the ribbon com object in the ribbon onLoad callback, but not within the onClick callbacks of the controls included in the ribbon, since the only parameter passed into those functions is the control's com object.

Some relavent debugging info

  • parameter passed into the ribbon onLoad callback is an instance of win32com.gen_py.Microsoft Office 16.0 Object Library.IRibbonUI
  • the parameter's _oleobj_ value is a PyIDispatch instance
  • The errors seems to center around the DispatchBaseClass in win32com.client
class DispatchBaseClass:
    def __init__(self, oobj=None):
        if oobj is None:
            oobj = pythoncom.new(self.CLSID)
        elif isinstance(oobj, DispatchBaseClass):
            try:
                oobj = oobj._oleobj_.QueryInterface(
                    self.CLSID, pythoncom.IID_IDispatch
                )  # Must be a valid COM instance
            except pythoncom.com_error as details:
                import winerror

                # Some stupid objects fail here, even tho it is _already_ IDispatch!!??
                # Eg, Lotus notes.
                # So just let it use the existing object if E_NOINTERFACE
                if details.hresult != winerror.E_NOINTERFACE:
                    raise
                oobj = oobj._oleobj_
        self.__dict__["_oleobj_"] = oobj  # so we dont call __setattr__
  • My thinking is to save a reference to the actual class, recreate it, set the CLSID class variable, then instantiate the new class.
def ribbon_loaded(ribbon):
    r_class = ribbon.__class__

    # was thinking I could pickle this class for use elsewhere
    class RibbonUI(r_class):  # also tried type(r_class)
        CLSID = ribbon.CLSID
    
    # just checking below that this would work

    # if CLSID is not set on the new class above __init__ fails
    # but, even when provided __new__ fails with the next line
    new_ribbon = RibbonUI()

    new_ribbon.InvalidateControl("ribbon_control_1")

Without setting CLSID on the new class the error is

2022-07-06 20:48:09,294 - INFO : ribbon.CLSID: IID('{000C03A7-0000-0000-C000-000000000046}')
2022-07-06 20:48:09,314 - ERROR : Error calling ribbon function 'components.ribbon.ribbon_loaded'
2022-07-06 20:48:15,364 - ERROR : Traceback (most recent call last):
2022-07-06 20:48:15,364 - ERROR :   File "C:\Users\mhill\PycharmProjects\excel-addin\components\ribbon.py", line 46, in ribbon_loaded
2022-07-06 20:48:15,365 - ERROR :     new_ribbon = RibbonUI()
2022-07-06 20:48:15,366 - ERROR :   File "c:\users\mhill\PyCharmProjects\excel-addin\.venv\lib\site-packages\win32com\client\__init__.py", line 514, in __init__
2022-07-06 20:48:15,366 - ERROR :     oobj = pythoncom.new(self.CLSID)
2022-07-06 20:48:15,366 - ERROR : pywintypes.com_error: (-2147221164, 'Class not registered', None, None)

When creating the new class and specifying the the CLSID class variable the error becomes

2022-07-06 20:48:22,429 - INFO : ribbon.CLSID: IID('{000C03A7-0000-0000-C000-000000000046}')
2022-07-06 20:48:22,429 - ERROR : Error calling ribbon function 'components.ribbon.ribbon_loaded'
2022-07-06 20:48:28,949 - ERROR : Traceback (most recent call last):
2022-07-06 20:48:28,950 - ERROR :   File "C:\Users\mhill\PycharmProjects\excel-addin\components\ribbon.py", line 46, in ribbon_loaded
2022-07-06 20:48:28,951 - ERROR :     new_ribbon = RibbonUI()
2022-07-06 20:48:28,951 - ERROR : TypeError: type.__new__() takes exactly 3 arguments (0 given)

Related question mentions something about creating an IDTExtensibility2 object, but not quite sure what to do with that.

The question

Apart from mimicking saving a global reference to the ribbon object from within the ribbon's onLoad callback as Microsoft's documentation suggests, how can I properly save a reference to the IRibbonUI com object when in the ribbon onLoad callback and be able to access it in the controls' onClick callbacks?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

安静被遗忘 2025-02-20 16:50:34

叹气,看起来全局变量选项是推荐路线

摘录

...
例如,在您的功能区XML中,您将Onload操作添加到
customui元素如下(假设您的代码位于名为的模块中
your_module.py):

 < customui
 xmlns =“ http://schemas.microsoft.com/office/2009/07/customui”
 onload =“ your_module.on_load”
>
 <! - 您的色带定义在这里 - >
</customui>
 

然后在您的模块中(上面使用了“ your_module.py”),您将添加操作
函数“ on_load”,以iribbonui对象。然后您可以保存
它是一个全局变量,因此您可以调用“无效”方法
后来。

  _ribbon = none

def on_load(控制):
    全局_ribbon
    _ribbon =控制

#稍后,当您想使控件无效时,您会致电
_ribbon.invalidatecontrol(contry_id)
 

Sigh, looks like the global variable option is the recommended route

Excerpt

...
For example, in your ribbon XML you would add the onLoad action to the
customUI element as follows (assuming your code is in a module named
your_module.py):

<customUI
 xmlns="http://schemas.microsoft.com/office/2009/07/customui"
 onLoad="your_module.on_load"
>
 <!-- your ribbon definition here -->
</customUI>

Then in your module ("your_module.py" is used above) you would add the action
function "on_load" that takes the IRibbonUI object. You can then save
it as a global variable so that you can call the "Invalidate" method
later.

_ribbon = None

def on_load(control):
    global _ribbon
    _ribbon = control

# Later when you want to invalidate a control you would call
_ribbon.InvalidateControl(control_id)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文