问题
I'm trying to write a script to manipulate a Ubuntu Mint _LibreOffice Calc sheet using Python 3.7,. For some reason I'm getting an error when I try to import Uno. I can run a macro from inside Calc calling this
desktop = XSCRIPTCONTEXT.getDesktop()
model = desktop.getCurrentComponent()
active_sheet = model.CurrentController.ActiveSheet
write 'Hello World' in A1
active_sheet.getCellRangeByName("A1").String = "Hello World!
"
but can't directly from the VS_code IDE, interact with the sheet. Is there a path that I need to set to Uno_
Using LibreOffice 6.4, Python 3.7 and APSO. I'm so frustrated with LibreOffice there seems to be very little python support for newbies.
回答1:
After a lot of research, I have figured out how to manipulate a Ubuntu LibreOffice Calc sheet using Python 3.7 and PyCharm and MySQL. I'm a Newbie at all of this, so please excuse my coding, I'm still learning. I have supplied links to information sources I used to complete this project.
The main stumbling block that was causing me problems, was not understanding that LibreOffice needed to be initiated prior to connecting through the socket. After that I was able to use Uno and other modules to interact with the Calc spreadsheet. I have supplied copies of all my Py scripts. Hopefully this will help anyone who like me is new to this environment.
# 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/
Thanks to everyone who helped.
回答2:
I had the same problem and I solved it like this:
- install libreoffice in Ubuntu 20:
sudo apt update
sudo apt install libreoffice --no-install-recommends
- check if uno is accessible (in my case this is all I've done):
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()
- start libreoffice process with opened socket (more info here - page 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'
- open another terminal and enter python3 interactive mode:
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,tcpNoDalay=1;urp;StarOffice.ComponentContext"
- now try to connect:
uno_ctx = resolver.resolve(url)
- if succeeded,
uno context
is all you need - instantiate Desktop (the root frame for all applications - Calc, Writer, Draw, ...)
uno_smgr = uno_ctx.ServiceManager
desktop = uno_smgr.createInstanceWithContext("com.sun.star.frame.Desktop", uno_ctx )
- create a new Calc document (
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 )
- play with the document:
sheets = document.getSheets()
sheet_1 = sheets[0]
cell_1 = sheet_1.getCellByPosition(1,1)
value = cell_1.setString('Hello World!')
- save it and check the results:
# 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)
- don't forget to terminate the process:
desktop.terminate()
- you can find my implementation here: https://github.com/CosminEugenDinu/libreoffice-python-library
- Good Luck !
来源:https://stackoverflow.com/questions/60663664/using-python-to-access-libreoffice-calc-using-uno