[Решено] VBA, Python. Не могу найти Series в Chart.

Автор Massaraksh7, 20 апреля 2024, 23:18

0 Пользователи и 1 гость просматривают эту тему.

Massaraksh7

Создаю chart (делал и в VBA и в Python) - создаётся простой chart. Но не нашёл, как изменить характеристики отдельных серий.
Перерыл весь интернет, ничего про это нет. В Excel в VBA были Series, SeriesCollection - здесь их нет (а может, как-то по-другому называются?).
Ниже пример того, что мне нужно. Как это сделать?

mikekaganski

Цитата: Massaraksh7 от 20 апреля 2024, 23:18Ниже пример того, что мне нужно.

Пожалуйста, приложите пример с готовыми данными и вручную сделанным Chart. Это позволит обсуждать код на конкретном примере.

Цитата: Massaraksh7 от 20 апреля 2024, 23:18делал и в VBA
Вы действительно имеете ввиду VBA? Или Basic в той (неуказанной) программе, которую Вы используете? Пример кода, который у Вас не работает, тоже не помешал бы.
С уважением,
Михаил Каганский

Massaraksh7

#2
Появилась идея, 2-3 дня покопаю, если не получится, то вернусь.
Код - ниже. Не судите стого, третий день, как python изучаю.
Получается chart с одной серией, а мне надо добавить другие серии с range-ями данных, и, главное, настроить цвет, ширину, стиль, наличие линии, а также цвет, стиль и наличие маркера для каждой серии, также название серии, и распределить серии по осям.
import uno
import tempfile
import os
import os.path
import subprocess
import time
from com.sun.star.beans import PropertyValue
from com.sun.star.awt import Rectangle
from com.sun.star.table import CellRangeAddress

subprocess.Popen('soffice --invisible --accept="socket,host=localhost,port=2002;urp;"',shell=True)
#---------------
s=tempfile.gettempdir()
tmpfile=os.path.join(s,"temp.ods")
time.sleep(3)

localContext = uno.getComponentContext()   #---Получаем контекст UNO
resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)  #---Создаем UnoUrlResolver
ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")   #---Подключаемся к запущенному LibreOffice

smgr = ctx.ServiceManager   #---Получаем Сервис-манаджер
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop",ctx)   #---Объект рабочего стола
path = uno.systemPathToFileUrl('c:\\laz-aep\\maket.ods')   #---Путь к пустому документу-макету
doc = desktop.loadComponentFromURL(path, '_default', 0, ())   #---Грузим документ
tmppath = uno.systemPathToFileUrl(tmpfile)          #---Путь к временному документу
props = []       
doc.storeAsURL(tmppath,(tuple(props)));             #---И сохраняем его во временной директории

model = desktop.getCurrentComponent()     #---Получаем доступ к документу
model.CurrentController.Frame.ContainerWindow.setVisible(False) #---Сделать невидимым
sheet = model.CurrentController.ActiveSheet   #---Активный лист

ocell=sheet.getCellRangeByName("A1")
ocell.setString("X")       
sheet.getCellRangeByName("A2").Value = 1
sheet.getCellRangeByName("A3").Value = 2
sheet.getCellRangeByName("A4").Value = 3
sheet.getCellRangeByName("A5").Value = 7
sheet.getCellRangeByName("A6").Value = 8

ocell=sheet.getCellRangeByName("C1")
ocell.setString("Y")
sheet.getCellRangeByName("C2").Value = 1
sheet.getCellRangeByName("C3").Value = 3
sheet.getCellRangeByName("C4").Value = 2       
sheet.getCellRangeByName("C5").Value = 6
sheet.getCellRangeByName("C6").Value = 4

range_address = []
range_address.append(CellRangeAddress())
range_address[0].Sheet = sheet.RangeAddress.Sheet
range_address[0].StartColumn = 0
range_address[0].EndColumn = 0
range_address[0].StartRow = 0
range_address[0].EndRow = 5

range_address.append(CellRangeAddress())
range_address[1].Sheet = sheet.RangeAddress.Sheet
range_address[1].StartColumn = 2
range_address[1].EndColumn = 2
range_address[1].StartRow = 0
range_address[1].EndRow = 5

rec = Rectangle()
rec.X = 6000
rec.Y = 2000
rec.Width = 10000
rec.Height = 10000

charts = sheet.Charts
chart_name = "MyGraph1"
charts.addNewByName(chart_name, rec, tuple(range_address), True, True)

chart = charts[chart_name].EmbeddedObject
#chart = charts.getByName(chart_name).EmbeddedObject
chart.Diagram = chart.createInstance("com.sun.star.chart.XYDiagram")

data = chart.Diagram.getDataRowProperties(0)

model.CurrentController.Frame.ContainerWindow.setVisible(True)  #---Сделать видимым


Massaraksh7

По VBA: На форуме https://forum.openoffice.org нашёл такой блок кода
oSeries1 = CreateDataSeries_XYDiagram(oDataProvider, "Feuille1.A6:A9", "Feuille1.B6:B9", "Feuille1.A3")
oSeries2 = CreateDataSeries_XYDiagram(oDataProvider, "Feuille1.E6:E10", "Feuille1.F6:F10", "Feuille1.E3")
oSeries3 = CreateDataSeries_XYDiagram(oDataProvider, "Feuille1.I6:I10", "Feuille1.J6:J10", "Feuille1.I3")
Автор утверждает, что это создаёт серии. Но при вставке такого кода в макрос говорится: "Подпрограмма или функция не определена". Может, какие-то библиотеки нужны? Искал такую функцию в поиске - всего 4 ссылки.
Точная ссылка: https://forum.openoffice.org/en/forum/viewtopic.php?f=20&t=8991#p46467

sokol92

Цитата: Massaraksh7 от 21 апреля 2024, 12:36нашёл такой блок кода
Новичкам везет!  :)

Это пример от "самого" @Hanya - автора расширений APSO, MRI и многого другого...

Ниже приложен документ. Текст от @Hanya в модуле Module2 библиотеки Standard.

Нажмите кнопку "chart".

Владимир.

Massaraksh7

Да, похоже, это то, что нужно, спасибо! Буду разбираться.
Поясню, для чего всё это нужно. Есть большой проект на Lazarus. Отчётные формы пользователи привыкли видеть в Excel и Word. Сейчас же организация постепенно переходит на AstraLinux, и там не будет, естественно, MSOffice, будет LibreOffice. Поэтому нужен мост для формирования отчётов в LO, чем сейчас и занимаемся. С формированием данных на Scalc разобрался быстро, вроде бы, проблем нет, а вот с графиками - засада: всё по-хитрому сделано.

sokol92

Цитата: Massaraksh7 от 21 апреля 2024, 16:46Буду разбираться
По возможности, расскажите о результате.
Владимир.

economist

#7
По крайней мере обсудите "промышленный способ" вставки визуализаций в отчеты. Это PNG/SVG-графики на Python с помощью либ Altair, Matplotlib, Plotly, Seaborn, Vega-light итд (8-мь было с утра) в среде Pandas в комлектном Python LO. Там все просто, скрипт м.б. либой или внедрен в ODS-файл:

df = pd.read_excel(/home/123/file.ods)
df.loc['2024-04']['сумма', 'колво'].plot(kind='bar', backend='Altair', secondary_y='колво')
plt.savefig('file_сумма_bar.svg')

Просто засеките время сколько у вас уйдет на построение диаграммы в Calc мышью с нуля (два ряда, отбор за период, сумма/колво по осн и вспом оси Y), на Basic и вот этим "3х-строчником". Разброс будет 100 и более крат.

Вставлять графику в отчет можно и вовсе не макросом, а ссылкой (связью), в т.ч. Автотекстом.

Это самый частый паттерн, гибко настраиваемый и не имеющий никаких творческих или типографских ограничений. Либы очень продвинуты - знают палитры для премиум-печати, для дальтоников, для монохромной печати. Объем примеров кода на SO просто огромен.

В Calc на Basic вы столкнетесь с неимоверными трудностями в части подписей данных, комбинированием рядов, а сами диаграммы будут неизбежно уступать кристально чистой SVG-графике (откровенно говоря они буду выглядеть как в эл. таблице, с зубчатыми краями, несуразно расположенными подписями итд). Да, можно сделать красиво везде, но какой ценой!

PS: Декларативная графика (Altair) некоторым аналитикам заходит лучше императивной (остальные либы), это тоже стоит учесть.   
Руб. за сто, что Питоньяк
Любит водку и коньяк!
Потому что мне, без оных, -
Не понять его никак...

Massaraksh7

Цитата: sokol92 от 21 апреля 2024, 17:35
Цитата: Massaraksh7 от 21 апреля 2024, 16:46Буду разбираться
По возможности, расскажите о результате.
Хорошо.
Цитата: economist от 21 апреля 2024, 17:35Да, можно сделать красиво везде, но какой ценой!
Ценой времени. Которого всегда не хватает.

Massaraksh7

Цитата: economist от 21 апреля 2024, 17:35В Calc на Basic
Если разберусь на Basic, попробую по аналогии на Python. Потом дам пинка исполнителю и продолжу заниматься своим Lazarusом. :)

Massaraksh7

Вот как это может быть, может, я чего не понимаю? Я присваиваю значение DASH (или SOLID), а оно в реальности присваивает NONE.
from com.sun.star.drawing.LineStyle import DASH as dash
from com.sun.star.drawing.LineStyle import SOLID as solid
from com.sun.star.drawing.LineStyle import NONE as none
........
charts = sheet.Charts
chart_name = "MyGraph1"
charts.addNewByName(chart_name, rec, tuple(range_address), True, True)
chart = charts[chart_name].EmbeddedObject
chart.Diagram = chart.createInstance("com.sun.star.chart.XYDiagram")
data = chart.Diagram.getDataRowProperties(0)
data.FillColor = 256*256*255
data.LineWidth = 10
print('Place1')
print(data.LineStyle)
data.LineStyle = dash   #SOLID #DASH #NONE
print('Place2')
print(data.LineStyle)
sdfdsfsdf
И в Basic, скорее всего, то же самое.

Massaraksh7

Полный код, если кому интересно.
import uno
import tempfile
import os
import os.path
import time
import subprocess
from com.sun.star.beans import PropertyValue
from com.sun.star.awt import Rectangle
from com.sun.star.table import CellRangeAddress
from com.sun.star.drawing.LineStyle import DASH as dash
from com.sun.star.drawing.LineStyle import SOLID as solid
from com.sun.star.drawing.LineStyle import NONE as none
from com.sun.star.drawing.DashStyle import ROUND as roundpoint

subprocess.Popen('soffice --invisible --accept="socket,host=localhost,port=2002;urp;"',shell=True)
#---------------
s=tempfile.gettempdir()
tmpfile=os.path.join(s,"temp.ods")
time.sleep(3)

localContext = uno.getComponentContext()   #---Получаем контекст UNO
resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)  #---Создаем UnoUrlResolver
ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")   #---Подключаемся к запущенному LibreOffice

smgr = ctx.ServiceManager   #---Получаем Сервис-манаджер
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop",ctx)   #---Объект рабочего стола
path = uno.systemPathToFileUrl('c:\\laz-aep\\maket.ods')   #---Путь к нужному документу
doc = desktop.loadComponentFromURL(path, '_default', 0, ())   #---Грузим документ
tmppath = uno.systemPathToFileUrl(tmpfile)          #---Путь к временному документу
props = []       
doc.storeAsURL(tmppath,(tuple(props)));             #---И сохраняем его во временной директории

model = desktop.getCurrentComponent()     #---Получаем доступ к документу
model.CurrentController.Frame.ContainerWindow.setVisible(False) #---Сделать невидимым
sheet = model.CurrentController.ActiveSheet   #---Активный лист

ocell=sheet.getCellRangeByName("B2")
ocell.setString("X")       
sheet.getCellRangeByName("B3").Value = 1
sheet.getCellRangeByName("B4").Value = 2
sheet.getCellRangeByName("B5").Value = 3
sheet.getCellRangeByName("B6").Value = 7
sheet.getCellRangeByName("B7").Value = 8

ocell=sheet.getCellRangeByName("D2")
ocell.setString("Y")
sheet.getCellRangeByName("D3").Value = 1
sheet.getCellRangeByName("D4").Value = 3
sheet.getCellRangeByName("D5").Value = 2       
sheet.getCellRangeByName("D6").Value = 6
sheet.getCellRangeByName("D7").Value = 4

range_address = []
range_address.append(CellRangeAddress())
range_address[0].Sheet = sheet.RangeAddress.Sheet
range_address[0].StartColumn = 1
range_address[0].EndColumn = 1
range_address[0].StartRow = 0
range_address[0].EndRow = 5

range_address.append(CellRangeAddress())
range_address[1].Sheet = sheet.RangeAddress.Sheet
range_address[1].StartColumn = 3
range_address[1].EndColumn = 3
range_address[1].StartRow = 0
range_address[1].EndRow = 5

rec = Rectangle()
rec.X = 10000
rec.Y = 3000
rec.Width = 10000
rec.Height = 10000

charts = sheet.Charts
chart_name = "MyGraph1"
charts.addNewByName(chart_name, rec, tuple(range_address), True, True)

chart = charts[chart_name].EmbeddedObject
chart.Diagram = chart.createInstance("com.sun.star.chart.XYDiagram")

#data = chart.Diagram.getDataRowProperties(0)
#data.FillBackground = True
#data.FillStyle = 0
#data.FillColor = 255

#chartCollection = sheet.getCharts()
#chart = chartCollection.getByIndex(0)
data = chart.Diagram.getDataRowProperties(0)
data.FillColor = 256*256*255
data.LineWidth = 10
print('Place1')
print(data.LineStyle)
data.LineStyle = dash   #SOLID #DASH #NONE
print('Place2')
print(data.LineStyle)

#sdfdsfsdf
#dashdata = uno.createUnoStruct('com.sun.star.drawing.LineDash')
#dashdata.Style = roundpoint
#dashdata.Dashes = 100
#dashdata.Distance = 100
#data.LineDash = dashdata

model.CurrentController.Frame.ContainerWindow.setVisible(True)  #---Сделать видимым
Из-за этого пока не могу стиль линии настроить.

economist

@Massaraksh7, установите расширение APSO в LO, в нем есть какой-никакой пошаговый отладчик py-скриптов c точками останова, можно в реальном времени видеть значения переменных. Там нет подсветки синтаксиса.

Если хочется полноценной IDE (Visual Studio Code) - то есть докеры с LO+Python+VSCode (можно заменить на Codium) https://github.com/Amourspirit/live-libreoffice-python#live-libreoffice-python Это не единственное подобное решение. Но тема проработана слабо. Вообще Python из LO пока простаивает, ей-богу, часто проще делать что-то снаружи (те же графики), не ныряя в uno, поскольку там моментально вся красота и краткость py-кода летят в тар-тарары. 

Руб. за сто, что Питоньяк
Любит водку и коньяк!
Потому что мне, без оных, -
Не понять его никак...

Massaraksh7

В Бейсике присваивает правильно. Но там свои нюансы: работает только один вид стиля: "Fine Dashed".
На свойство LineDash не реагирует. Ширина и цвет линии отрабатывают нормально.
  oSeries1.Color = RGB(255,0,0)
  oSeries2.Color = RGB(0,255,0)
  oSeries3.Color = RGB(0,0,255)
 
  oSeries2.LineWidth=100
 
'With myTirets
'   .Style = com.sun.star.drawing.DashStyle.ROUND
'   .Dots = 4 ' 4 points
'   .DotLen = 50 ' of 0,5 mm
'   .Dashes = 2 ' with 2 tirets
'   .DashLen = 200 ' of 2 mm
'   .Distance = 150 ' spaced by 1,5 mm
'End With
'myTirets.DashLen=100
'myTirets.Distance=100

oSeries1.LineStyle = com.sun.star.drawing.LineStyle.DASH
oSeries1.LineWidth=50
oSeries1.LineDashName = "Fine Dashed"

Massaraksh7

Цитата: economist от 22 апреля 2024, 09:30Если хочется полноценной IDE (Visual Studio Code)
Нет, пока это временное занятие. Достигну какого-нибудь вменяемого результата - передам исполнителю.