使用 Python 使用 Uno 访问 LibreOffice Calc

Using Python to access LibreOffice Calc using Uno

我正在尝试使用 Python 3.7 编写脚本来操作 Ubuntu Mint _LibreOffice Calc sheet。出于某种原因,我在尝试导入 Uno 时遇到错误。我可以 运行 从 Calc 内部调用这个宏

    desktop = XSCRIPTCONTEXT.getDesktop()
    model = desktop.getCurrentComponent()
    active_sheet = model.CurrentController.ActiveSheet
    write 'Hello World' in A1
    active_sheet.getCellRangeByName("A1").String = "Hello World!

但不能直接从 VS_code IDE 与 sheet 交互。有没有我需要设置为 Uno_

的路径

使用 LibreOffice 6.4、Python 3.7 和 APSO。我对 LibreOffice 感到非常沮丧,似乎 python 对新手的支持很少。

经过大量研究,我弄清楚了如何使用 Python 3.7、PyCharm 和 MySQL 来操作 Ubuntu LibreOffice Calc sheet .我是所有这一切的新手,所以请原谅我的编码,我还在学习。我提供了用于完成此项目的信息源的链接。

导致我出现问题的主要障碍是不了解 LibreOffice 需要在通过套接字连接之前启动。之后,我能够使用 Uno 和其他模块与 Calc spreadsheet 进行交互。我提供了所有 Py 脚本的副本。希望这会帮助像我一样刚接触这个环境的任何人。

# opens connection to mysql database GVWC
import mysql.connector
import pandas as pd
from gui import validateLogin

def conn_mysql():
    validateLogin #opens input box for username and password
    us, pw=validateLogin() #returns login info
    cnx = mysql.connector.connect(user=us, password=pw ,host='localhost', database= 'GVWC') # connects to DB
    pd.set_option('display.max_columns', None)
    df = pd.read_sql("SELECT * FROM GVWC.2020", cnx) #runs query
    return df, pd #returns data
print("Database connection resolved.")

# connection to libreoffice

import uno
import os
import time

def lo_conn():
    #Open socket to LibraOffice with delay to allow program to wait for connection
    os.popen('/usr/lib/libreoffice/program/soffice --accept="socket,host=localhost,port=2002;urp;StarOffice.ServiceManager"')
    time.sleep(3)  # Sleep for 3 seconds

    #=======================================================================================================================================
    # get the uno component context from the PyUNO runtime
    localContext = uno.getComponentContext()

    # create the UnoUrlResolver
    resolver = localContext.ServiceManager.createInstanceWithContext(
        "com.sun.star.bridge.UnoUrlResolver", localContext)

    # connect to the running office
    ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
    smgr = ctx.ServiceManager

# create input box for login info
from tkinter import *
from functools import partial

def validateLogin(username, password):
    #print("username entered :", username.get())
    #print("password entered :", password.get())
    global pw
    pw = password.get()
    global us
    us = username.get()
    return us, pw

#window
tkWindow = Tk()
tkWindow.geometry("500x100+500+300")
tkWindow.title('Tkinter Login Form - pythonexamples.org')

#username label and text entry box
usernameLabel = Label(tkWindow, text="User Name").grid(row=0, column=0)
username = StringVar()
usernameEntry = Entry(tkWindow, textvariable=username).grid(row=0, column=1)

#password label and password entry box
passwordLabel = Label(tkWindow,text="Password").grid(row=1, column=0)
password = StringVar()
passwordEntry = Entry(tkWindow, textvariable=password, show='*').grid(row=1, column=1)

validateLogin = partial(validateLogin, username, password)

#login button
loginButton = Button(tkWindow, text="Login", command=validateLogin).grid(row=4, column=0)

tkWindow.mainloop()


#main program to create spreedsheet and fill with data from mysql

import os
from workingConn3 import conn_mysql
from lo_conn_3 import lo_conn

def main():
    conn_mysql # call mysql def connection
    df,pd=conn_mysql() # pass table data
    lo_conn #connect to Libreoffice

    # open calc and fill spreedsheet
    #=====================================================================================================================================
    writer = pd.ExcelWriter('Test_DF.xlsx',
                                engine='xlsxwriter',
                                datetime_format='mm/dd/yyyy')

    df.to_excel(writer, index=False, sheet_name='TEST',
                   startrow=5, header = 4)

    #Get the xlsxwriter objects from the dataframe writer object.
    workbook  = writer.book
    worksheet = writer.sheets['TEST']



    #Add a header format.
    header_format = workbook.add_format({
        'font_color': 'white',
        'text_wrap': True,
        'font_name': 'Calibri',
        'font_size': 11,
        'fg_color': '#44546a'})

    # Write the column headers with the defined format.
    for col_num, value in enumerate(df.columns.values):
        worksheet.write(5, col_num, value, header_format)

    colwidths = {}

    # Store the defaults.
    for col in range(14):
        colwidths[col] = 15



    # Then set the column widths.
    for col_num, width in colwidths.items():
        worksheet.set_column(col_num, col_num, width)


    #Center text of column
    cell_format = workbook.add_format()
    cell_format.set_align('center')
    worksheet.set_column('E1:E100',15,cell_format)
    worksheet.set_column('F1:F100',15,cell_format)
    worksheet.set_column('M1:M100',15,cell_format)

    #  Freeze pane on the header row.
    #
    worksheet.freeze_panes(6, 0)
    worksheet.autofilter('A6:O6')

    #Button text
    worksheet.write('A3', 'Press the button to say hello.')

    # Add a button tied to a macro in the VBA project.
    worksheet.insert_button('A1', {'macro': 'start',
                                   'caption': 'Press Me',
                                   'width': 80,
                                   'height': 30})

    # Close the Pandas Excel writer and output the Excel file.
    writer.save()
    writer.close()
    #reopen excel file
    os.popen('/usr/lib/libreoffice/program/soffice Test_DF.xlsx  --accept="socket,host=localhost,port=2002;urp;StarOffice.ServiceManager"')


if __name__== "__main__" :
    main()

https://medium.com/analytics-vidhya/macro-programming-in-openoffice-libreoffice-with-using-python-en-a37465e9bfa5

https://medium.com/analytics-vidhya/starting-libreoffice-with-python-macro-programming-in-openoffice-libreoffice-with-using-10310f9e69f1

https://pjryan126.github.io/jupyter-db-connections/

http://christopher5106.github.io/office/2015/12/06/openoffice-libreoffice-automate-your-office-tasks-with-python-macros.html

https://help.libreoffice.org/6.4/en-US/text/shared/guide/start_parameters.html?&DbPAR=WRITER&System=UNIX

https://www.saltycrane.com/blog/2010/02/using-python-write-excel-openoffice-calc-spreadsheet-ubuntu-linux/

Add dataframe and button to same sheet with XlsxWriter

https://pynative.com/python-mysql-database-connection/

感谢所有帮助过的人。

我遇到了同样的问题,我是这样解决的:

  • 在 Ubuntu 20:
  • 安装 libreoffice
sudo apt update
sudo apt install libreoffice --no-install-recommends
  • 检查 uno 是否可以访问(在我的例子中,我已经完成了所有这些):
python3
Python 3.8.5 (default, Jul 28 2020, 12:59:40)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import uno
>>> print(uno.__file__)
/usr/lib/python3/dist-packages/uno.py
>>> exit()
  • 使用打开的套接字启动 libreoffice 进程(更多信息 here - 第 117 页):
/usr/lib/libreoffice/program/soffice.bin --headless --invisible --nocrashreport --nodefault --nofirststartwizard --nologo --norestore --accept='socket,host=localhost,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext'
  • 打开另一个终端,进入python3交互模式:
import uno
local_ctx = uno.getComponentContext()
smgr_local = local_ctx.ServiceManager
resolver = smgr_local.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", local_ctx)
url = "uno:socket,host=localhost,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext"
  • 现在尝试连接:
uno_ctx = resolver.resolve(url)
  • 如果成功,uno context就是你所需要的
  • 实例化桌面(所有应用程序的根框架 - Calc、Writer、Draw 等)
uno_smgr = uno_ctx.ServiceManager
desktop = uno_smgr.createInstanceWithContext("com.sun.star.frame.Desktop", uno_ctx )
  • 创建新的 Calc 文档 (private:factory/scalc):
PropertyValue = uno.getClass('com.sun.star.beans.PropertyValue')
inProps = PropertyValue( "Hidden" , 0 , True, 0 ), # this is a tuple
document = desktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, inProps )
  • 玩文档:
sheets = document.getSheets()
sheet_1 = sheets[0]
cell_1 = sheet_1.getCellByPosition(1,1)
value = cell_1.setString('Hello World!')
  • 保存并查看结果:
# UNO requires absolute paths
import os
path = os.path.abspath('./testfile.xls')
uno_url = uno.systemPathToFileUrl(path)
# Filters used when saving document.
# https://github.com/LibreOffice/core/tree/330df37c7e2af0564bcd2de1f171bed4befcc074/filter/source/config/fragments/filters
filter = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
filter.Name = 'FilterName'
filter.Value = 'Calc MS Excel 2007 XML'
filters = (filter,)
# use filters = () to save file as *.ods

document.storeToURL(uno_url, filters)
  • 不要忘记终止进程:
desktop.terminate()