0

I am writing a code to compare two parts in catia. I used win32com.client to read the document. I am also using threading to run the progress bar. for some reason, I am getting below error even though I have defined document_new as global.
Error:

Exception in thread Thread-22:
Traceback (most recent call last):
  File "D:\VirtualEnv\env\lib\threading.py", line 926, in _bootstrap_inner
    self.run()
  File "D:\VirtualEnv\env\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\as11114\AppData\Local\Temp\ipykernel_16240\2501566542.py", line 62, in STD_Compare
    if document_old == document_new:
  File "D:\VirtualEnv\env\lib\site-packages\win32com\client\dynamic.py", line 290, in __eq__
    return self._oleobj_ == other
pywintypes.com_error: (-2147417842, 'The application called an interface that was marshalled for a different thread.', None, None)

Here is the code. Method STD_Check in class will first read one document and method STD_Compare will then read next document. If both documents are same then user gets a message that both documents are same.

import os
import win32com.client
import numpy as np
import pandas as pd
import openpyxl
import threading
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
# import pythoncom to enable 'win32com.client.Dispatch' in Threads
import pythoncom
# import psutil to check whether any application is open or not, in this case checking Catia
import psutil

class STD_Check_compare():
    
    def __init__(self):
        #Form    
        self.root = Tk()
        self.root.geometry("700x200")
        self.root.title("STD Check")
        self.mainframe = ttk.Frame(self.root, padding="3 3 12 12")
        self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
#==========================================================================================================
    def STD_Check(self):
        
        self.STD_Check_button["state"] = "disable"
        
        global document_new # defined as global
        
        pythoncom.CoInitialize() # to enable 'win32com.client.Dispatch' in Threads
        Cat = win32com.client.Dispatch('catia.application')
        Cat.Visible = True
        Cat.DisplayFileAlerts = True
        document_new = Cat.ActiveDocument  #calling the active document

        self.progressbar_STDCheck.start()
        #Do something

        self.progressbar_STDCheck.stop()
        messagebox.showinfo("", "STD Check done for parameters. Excel created and stored in defined folder")
        self.STD_Check_button["state"] = "normal"


    def STD_Compare(self):
        
        self.STD_Compare_button["state"] = "disable"
        
        pythoncom.CoInitialize()
        Cat = win32com.client.Dispatch('catia.application')
        Cat.Visible = True
        Cat.DisplayFileAlerts = True

        document_old = Cat.ActiveDocument

        if document_old == document_new:
            messagebox.showinfo("", "You have selected same STD file. Select old STD to compare")
            self.STD_Compare_button["state"] = "normal"
            return

        self.progressbar_STDCompare.start()
        #do something

        self.progressbar_STDCompare.stop()
        messagebox.showinfo("", "STD Comparison done in DMU")
        self.STD_Compare_button["state"] = "normal"
         

    def Thread_STD_Check(self):
        threading.Thread(target=self.STD_Check, daemon=True).start() # Calling STD_Check method
        
    def Thread_STD_Compare(self):
        threading.Thread(target=self.STD_Compare, daemon=True).start() # Calling STD_Compare method
    
    #---------------Buttons, labels and Progress bars in Frame--------------------------
    def display(self):
        #Create Label "Enter path"
        self.Label_path = ttk.Label(self.mainframe, text="Enter path to store Excel files").grid(column=1, row=3, sticky=W)
        #Create Entrybox for Enter path
        self.storepath = StringVar()
        self.path_entry = ttk.Entry(self.mainframe, width=50, textvariable = self.storepath)
        self.path_entry.grid(column=1, row=4, sticky=W)

        #Create Label "STD Check"
        self.Label1 = ttk.Label(self.mainframe, text="Press submit button for STD Check").grid(column=1, row=7, sticky=W)
        #Create Submit Button for STD Check (Call thread for STD Check)
        self.STD_Check_button = ttk.Button(self.mainframe, text="Submit", command=self.Thread_STD_Check) #to check STD
        self.STD_Check_button.grid(column=2, row=7, sticky=W)

        #Create Label "STD Compare"
        self.Label2 = ttk.Label(self.mainframe, text="Press submit button to compare STD").grid(column=1, row=10, sticky=W)
        #Create Submit Button for STD Comparison(Call thread for STD Comparison)
        self.STD_Compare_button = ttk.Button(self.mainframe, text="Submit", command=self.Thread_STD_Compare) #to compare STD
        self.STD_Compare_button.grid(column=2, row=10, sticky=W)
        
        self.progressbar_STDCheck = ttk.Progressbar(self.mainframe, style='text.Horizontal.TProgressbar', mode='indeterminate', length=150)
        self.progressbar_STDCheck.grid(column = 3, row = 7)

        self.progressbar_STDCompare = ttk.Progressbar(self.mainframe, style='text.Horizontal.TProgressbar', mode='indeterminate', length=150)
        self.progressbar_STDCompare.grid(column = 3, row = 10)
        
        
        self.root.mainloop()
    
start = STD_Check_compare()
start.display()
Ashish
  • 99
  • 6
  • 1
    Is `catia.application` running as an out-of-process server? COM uses RPC under the covers to communicate with OOP COM servers. On each function call the parameters have to be packed up (or "marshalled") then sent over RPC, and the response has to be un-packed. Passing one COM interface to a different thread can cause issues as usually the marshalling is set up for each thread individually (the "Apartment Model"). I am assuming this is where your error is coming from. You may need to read up on the different models. – DS_London Sep 28 '22 at 17:21
  • I looked into it as suggested by you. As Catia runs as exe, it should be an OOP server. I have gone through this Marshal thing however couldn't understand much as there is so much info. Do you have any example to show exactly how to pack and unpack instances in such cases? – Ashish Sep 29 '22 at 06:54
  • I'm afraid I don't have examples. I try to keep all my dispatch interfaces in the same thread to avoid these questions! – DS_London Sep 29 '22 at 08:52
  • You can't use CATIA object across threads. In C#/Forms you have to use Delegates and Invokes and magic spells to be safe. Don't know about how that works in python. – C R Johnson Sep 29 '22 at 16:57

0 Answers0