0

My Python code runs without an error but without running the VBA procedure.

The procedure works when run from inside the Excel file. The macro is in a standard module.

My Python (v3.10.6) code running on PyCharm (v221.6008.17) calling the VBA (v7.1.1126) procedure:

    from win32com.client.dynamic import Dispatch
    
    # Get the Excel Application COM object
    xl = Dispatch('Excel.Application')
    
    xl.Application.Run("IDMB.xlsm!PythonModules.EpisodesSort")

My VBA macro EpisodesSort:

Option Explicit

Public Sub EpisodesSort()
    
    Dim sRange$
    
    Call StartUp(Array(CEPISODES))
    
    With Episodes.Sheet2
        .Sort.SortFields.Clear
        sRange = "A1:A" & Episodes.LastUsedRow
        .Sort.SortFields.Add2 Key:=Episodes.Sheet2.Range(sRange), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
        With .Sort
            sRange = "A2:" & Episodes.LastUsedCol.Alphabetic & Episodes.LastUsedRow
            .SetRange Episodes.Sheet2.Range(sRange)
            .Header = xlNo
            .MatchCase = False
            .Orientation = xlTopToBottom
            .SortMethod = xlPinYin
            .Apply
        End With
    End With
        
End Sub

The StartUp procedure called above is in the VBA module CommonModules:

Public Const CEPISODES = "Episodes"

' some public variables omitted

Public varOldValue As Variant
Public wbMain As Workbook
Public Action As cSheet, Actors As cSheet, Artists As cSheet, Build As cSheet, Code As cSheet, Code2 As cSheet, Code3 As cSheet, Delete2 As cSheet, Episodes As cSheet, Incomplete As cSheet, Link As cSheet, Lists2 As cSheet, Login As cSheet, LookUp As cSheet, LostActors As cSheet, Movie As cSheet, MusicTorrentDeletes As cSheet, Ratings2 As cSheet, Reasons2 As cSheet, ShowTitles As cSheet, TorrentTypes As cSheet, Tracks As cSheet, Temp As cSheet, User2 As cSheet, wsTemp As cSheet
Public Sys2 As cSys
Public user As cUser

Public Sub StartUpInitial()
    On Error GoTo Err_Handler
    
    Set Sys2 = New cSys
    Set user = New cUser
    
    Set Temp = New cSheet
    
    Exit Sub
    Exit_Label:
      On Error Resume Next
      Application.Cursor = xlDefault
      Application.ScreenUpdating = True
      Application.CutCopyMode = False
      Application.Calculation = xlCalculationAutomatic
      Exit Sub
    Err_Handler:
      MsgBox Err.Description, vbCritical, "StartUpInitial"
      Resume Exit_Label
End Sub
    
Public Sub StartUp(arrTab As Variant, Optional ExternalWB As Workbook, Optional FindLinks As Boolean)
    On Error GoTo Err_Handler
    
    Dim i%, iTab%
    Dim FindLinks2 As Boolean
    Dim wb As Workbook
        
    'disable excel vba feature to accelerate processing
    Application.Cursor = xlDefault
    Application.ScreenUpdating = False
    Application.EnableEvents = True
    Application.CutCopyMode = False
    Application.Calculation = xlCalculationAutomatic
    
    aCategory = Array("music", "tv", "xxx")
    
    aMonth = Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
    
    aFullMonth = Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December")
    
    aRemoveChar = Array(".", "–", "-", ",", ";", "'", "‘", """", "/", ":")
    
    aPunctuation = Array(" ", "&")
    aPunctuation2 = Array("", "AND")
    
    iToday = Now()
    sWaitFlag = ""
    WaitedAfterPrevBrowser = False
    
    If IsMissing(ExternalWB) Then
        Set wbMain = ThisWorkbook
    ElseIf ExternalWB Is Nothing Then
        Set wbMain = ThisWorkbook
    Else
        Set wbMain = ExternalWB
    End If
    
    If IsMissing(FindLinks) Then
        FindLinks2 = False
    Else
        FindLinks2 = FindLinks
    End If
    
    If Build Is Nothing And Code Is Nothing And Code2 Is Nothing And Code3 Is Nothing And LookUp Is Nothing Then
        Call StartUpInitial
    End If
    
    If Not IsNull(arrTab) Then
        For iTab = 0 To UBound(arrTab)
            Select Case arrTab(iTab)
                Case CEPISODES
                    Set Episodes = New cSheet
                    
                    With Episodes
                        Set .Sheet2 = wbMain.Sheets(CEPISODES)
                        
                        .SearchLine = Array(1)
                        .BuildHeaderDetails
                        
                        .Heading = Array("Key", "Link")
    
                        .BlankLinesAllowed = 1
                        .ColumnNotRow = True
                    
                        'identify columns in source data tab
                        .IdentifyHeading
                    End With
            End Select
        Next iTab
    End If
    
    Exit Sub
    Exit_Label:
      On Error Resume Next
      Application.Cursor = xlDefault
      Application.ScreenUpdating = True
      Application.CutCopyMode = False
      Application.Calculation = xlCalculationAutomatic
      Exit Sub
    Err_Handler:
      MsgBox Err.Description, vbCritical, "StartUp"
      Resume Exit_Label
End Sub
Community
  • 1
  • 1
Cyrus
  • 356
  • 3
  • 14
  • 1
    What is the error you receive with posted code? Also, advise *where* the Excel macro is saved: behind a sheet, workbook, standard module? Depending on error, we may need to see this VBA. – Parfait Aug 14 '22 at 18:47
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. – Community Aug 14 '22 at 18:50
  • No error was produced. The macro is saved in a standard module. My Python code above doesn't execute the macro. I can see this because the sheet I expect to be sorted by column A isn't sorted. – Cyrus Aug 14 '22 at 22:29
  • I have updated my initial question above with some of the VBA code utilised. The requested macro EpisodesSort calls the procedure StartUp, which calculates various dimensions of a worksheet. – Cyrus Aug 14 '22 at 23:01
  • Oh wow! We don't need all this much code, hence the *minimum* of [mcve]. I think you have too much going on here with open workbook, a Python `openpyxl` instance on workbook with `ExcelFile`, and then an attempted Python COM connection to same Excel workbook. Are you properly closing/releasing Python objects (i.e., `wbPanda`, `xl`)? Check Task Manager processes as you may have many background Excel processes never closed, one of which may have ran the sorting. – Parfait Aug 14 '22 at 23:07

1 Answers1

0

Essentially, your question is more a processing one than a programming. Avoid too many interactions on the same open workbook where you segment processes between Python and Excel without conflicting instances.

Specifically, have Python handle two tasks:

  1. Pandas processing: Export your data, then close all objects, particularly ExcelFile. Do note: pandas with underlying engines, openpyxl or xslxwriter, does not yet support saving data to a macro-enabled workbook (i.e., xlsm), so consider following this solution by saving your VBA in a code .bin file and re-importing the code after data export. See other workarounds at bottom of this open pandas ticket.

  2. Excel processing: Launch a COM connection that opens Excel workbook of new data, runs the needed macro, saves changes, and closes workbook. Alternatively, to closing, make background Excel process visible and then release the COM object. Also, consider try/except to ensure processes cleanly release:

    try: 
        xl = Dispatch('Excel.Application') 
        wb = xl.Workbooks.Open("IDMB.xlsm") 
    
        # RUN MACRO
        xl.Application.Run("IDMB.xlsm!PythonModules.EpisodesSort")        
        wb.Save()
    
        # MAKE BACKGROUND PROCESS VISIBLE
        xl.Visible = True
    
        # CLOSE WORKBOOK AND QUIT APP
        # wb.Close(True)
        # xl.Quit()
    
    except Exception as e: 
        print(e) 
    
    finally: 
        # RELEASE OBJECTS (COUNTERPART OF VBA: Set wb = Nothing)
        wb = None
        xl = None
        del wb
        del xl
    
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • Thanks, all. When I run the above, it doesn't show the changes to the Episodes sheet/tab. I close and save the document and then re-open it manually and am then able to see that the macro ran correctly and that the sheet Episodes was sorted as expected, thank you. 1. how do I make the changes to the Excel file completely visible, while the file is still open? – Cyrus Aug 15 '22 at 08:23
  • Again, this solution advises not running your Python COM connection on an open Excel workbook given the challenge of different background instances. Handle your data export and order processing _separately_ in segemented steps. – Parfait Aug 15 '22 at 12:42
  • Apologies for the confusion. Running your code above, as is, leaves the Excel file open. The macro ran correctly but doesn't show the changes to the sheet/tab Episodes, unless I close the document and then re-open it. Can the code be changed, so that I am able to see the changes without closing the document and re-opening it? – Cyrus Aug 15 '22 at 13:54
  • 1
    Got it. I am not sure why that is happening. Possibly, your macro does not fully finish when Excel launches to view or does not update the screen. I see you toggle `ScreenUpdating` on and off. Maybe it remains off? `On Error Resume Next` may be preventing updating turned back on. – Parfait Aug 15 '22 at 15:31
  • I turned those on and off respectively. The macro shows the changes immediately when run from the Excel file but not the Python file. Sometimes solutions present themselves in time. I'm assuming this is the case with this one. Thanks for all your help – Cyrus Aug 15 '22 at 17:19