Мне очень хотелось бы нарисовать изогнутую стрелку с привязкой к ячейке с помощью библиотеки openpyxl, как показано на рисунке ниже;
После того, как я просмотрел официальную документацию, я не могу найти способ сделать это (хотя я нашел подходящее ключевое слово в классе openpyxl.drawing.shapes.PresetGeometry2D)
Как я могу нарисовать такой закрепленный соединитель с помощью Python? Я открыт для других пакетов, если OpenPyxl не поддерживает эту функцию.






Openpyxl обычно не очень хорошо работает с Shapes, поэтому лучшим вариантом обычно будет использование альтернативного модуля, такого как Xlwings или Win32com;
Ниже приведен базовый пример того, как это сделать в Xlwings;
Пример кода
Числовые значения, используемые не для позиционирования, являются значениями перечисления Excel для параметра. Чтобы получить эти значения, используйте необходимый метод Excel Enumerations.
(Обратите внимание, что команды для win32com будут аналогичны этому)
import xlwings as xw
from xlwings.utils import rgb_to_int
with xw.App(visible=True) as app:
wb = xw.Book()
ws = wb.sheets.active
# Add two rectangle shapes
### Excel AddShape command, Values are Type, Left, Top, Width & Height
shape1 = ws.api.Shapes.AddShape(1, 50, 44, 95, 27) # 1 is a rectangle
print(f"Shape1 name '{shape1.Name}'")
shape1.Fill.ForeColor.RGB = rgb_to_int((255, 233, 00)) # Set Fill colour
shape2 = ws.api.Shapes.AddShape(1, 290, 115, 94, 28)
print(f"Shape2 name '{shape2.Name}'")
shape2.Fill.ForeColor.RGB = '&HFF0000' # Alternate method to set fill colour
### AddConnector (line) has same params; Type, Left, Top, Width & Height
connector1 = ws.api.Shapes.AddConnector(3, 1, 1, 1, 1) # 3 is a msoConnectorCurve (Curved connector)
# connector1.Line.ForeColor.RGB = '&H000000' # Set Line colour method 1
# connector1.Line.ForeColor.RGB = rgb_to_int((0, 0, 0)) # Set Line colour method 2
connector1.Line.ForeColor.ObjectThemeColor = 13 # Set line colour method 3
connector1.Line.EndArrowheadStyle = 2 # Set Arrow head style as desired
connector1.Line.EndArrowheadLength = 3 # Set Arrow head length as desired
connector1.Line.EndArrowheadWidth = 3 # Set Arrow head width as desired
### Connecting the curved connector between the two rectangles
connector1.ConnectorFormat.BeginConnect(ConnectedShape=shape1, ConnectionSite=1)
connector1.ConnectorFormat.EndConnect(ConnectedShape=shape2, ConnectionSite=1)
### Re-route the connectors to the shortest route between the two shapes
connector1.RerouteConnections()
wb.save('Shapes.xlsx')
Результирующий лист
Если вы переместите фигуры, вы увидите, что к каждой из них привязан соединитель.
Дополнительные подробности
Для первого параметра команды AddShape «Тип» — это тип добавляемой фигуры. Это типы перечислений Excel, поэтому может быть либо имя, либо число,
Т.е. Перечисление msoShapeRectangle (Rectangle) равно 1, поэтому в качестве типа можно использовать либо «msoShapeRectangle», либо 1.
Остальные параметры — это положение и размер фигуры, как указано в коде.
Для размещения прямоугольников я использовал произвольные значения.
Размер и расположение разъема также можно изменять. Однако, поскольку мы соединяем его между двумя фигурами, мы можем использовать для них произвольные значения и предоставить Excel объединить начальную и конечную точки, поэтому в этом примере я просто установил для них все значения 1.
При соединении изогнутого соединителя между двумя фигурами вам необходимо указать начальную форму и точку (место соединения) на начальной фигуре для соединения, а также конечную форму и место соединения на конечной форме для соединения.
Место соединения, обозначенное цифрой, будет варьироваться в зависимости от формы. Если вы знаете, какое значение требуется для сайта подключения, его можно указать.
Пример в примере, который я использовал, сайт подключения Rectangle 1 — 4, а сайт подключения Rectangle 2 — 2, поэтому команда могла быть такой;
connector1.ConnectorFormat.BeginConnect(ConnectedShape=shape1, ConnectionSite=4)
connector1.ConnectorFormat.EndConnect(ConnectedShape=shape2, ConnectionSite=2)
Однако, если номера мест соединителя неизвестны, вы можете подключиться к двум произвольным точкам, как я сделал здесь, а затем заставить Excel перенаправить соединитель на кратчайший маршрут, используя RerouteConnections()
Следуйте дальше
Можно отказаться от фигур [прямоугольника] и просто нарисовать изогнутый соединитель так, чтобы он проходил между двумя ячейками. Тогда соединитель будет перемещаться вместе с ячейками.
Как уже отмечалось, ячейки не имеют точек соединения, однако мы можем получить данные о левом, верхнем, ширине и высоте ячейки и на их основе выполнить расчеты. Рекомендуется получать сведения о ячейке из листа, для которого установлено масштабирование 100 %, чтобы обеспечить точность. Я включил эту настройку в пример кода ниже.
В приведенном ниже примере вводятся координаты начальной и конечной ячейки, получаются данные слева, сверху, ширины и высоты и используются они для расчета деталей начала и конца соединителя. В коде параметры соединителя:
Тип, BeginX, BeginY, EndX, EndY
BeginX — начало позиции соединителя в строке
BeginY — начало позиции соединителя в столбце
EndX, EndY одинаковы для конечного положения соединителя.
При расчете позиций X я вошел в ячейку на 1/4 ее ширины, чтобы было понятно, где находится разъем.
Пример кода
Добавьте соединитель из ячейки «B3» в «E6».
import xlwings as xw
from xlwings.utils import rgb_to_int
def add_connector(begin_cell, end_cell):
with xw.App(visible=True) as app:
wb = xw.Book()
ws = wb.sheets.active
app.api.ActiveWindow.Zoom = 100 # Ensure the zoom on the sheet is 100%
### Start cell postion values
ws[begin_cell].color = (255, 192, 0)
cell_start = ws[begin_cell]
sleft = cell_start.left
stop = cell_start.top
sheight = cell_start.height
swidth = cell_start.width
print(f"Cell A3 details: Left:{sleft}, Top:{stop}, Height{sheight}, Width{swidth}")
### End cell postion values
ws[end_cell].color = (47, 117, 181)
cell_dest = ws[end_cell]
dleft = cell_dest.left
dtop = cell_dest.top
dheight = cell_dest.height
dwidth = cell_dest.width
print(f"Cell F10 details: Left:{dleft}, Top:{dtop}, Height{dheight}, Width{dwidth}")
### Obtain the connector start and end points from the cells postion
### Add 1/4 of the cell width intrusion in to the cell
### Start cell
begin_x = (sleft + swidth) - swidth/4
begin_y = stop + sheight/2
### End cell
end_x = dleft + swidth/4
end_y = dtop + dheight/2
### AddConnector (line) has params; Type, BeginX, BeginY, EndX, EndY
connector1 = ws.api.Shapes.AddConnector(3, begin_x, begin_y, end_x, end_y)
# connector1.Line.ForeColor.RGB = '&H000000' # Set Line colour method 1
# connector1.Line.ForeColor.RGB = rgb_to_int((0, 0, 0)) # Set Line colour method 2
connector1.Line.ForeColor.ObjectThemeColor = 13 # Set line colour method 3
connector1.Line.EndArrowheadStyle = 2 # Set Arrow head style as desired
connector1.Line.EndArrowheadLength = 3 # Set Arrow head length as desired
connector1.Line.EndArrowheadWidth = 3 # Set Arrow head width as desired
wb.save('connector.xlsx')
add_connector('B3', 'E6')
Пример листа
Кроме того, код, похоже, не работает на моем Mac. Файл "/Users/name/Documents/dev/python/notebooks/venv/lib/python3.12/site-packages/aeosa/appscript/reference.py", строка 596, в типе выбора getattr, код = self.AS_appdata.referencebyname()[имя] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^ KeyError : «Формы»
Я добавил к ответу относительно ваших комментариев, см. раздел «Примечания» внизу.
Это была линия shape1 = ws.api.Shapes.AddShape(1, 50, 44, 95, 27). Действительно, похоже, что Xlwings используют другую операционную систему. Возможно, мне придется попробовать это в Windows или перейти к прямым манипуляциям с XML. Что вы думаете о манипуляциях с XML? Будет ли это осуществимой идеей?
Использование Windows, безусловно, является лучшим вариантом, если она у вас есть. Непосредственное изменение XML является более сложным, поскольку вам необходимо убедиться, что необходимые файлы обновлены правильно, чтобы понимание их взаимодействия и необходимых тегов было абсолютным. Я не нашел сообщений людей, использующих XLwings и AddShape для MAC, поэтому нет подробностей, сталкивались ли другие с той же проблемой.
Я обновил ответ, добавив соединитель между двумя ячейками. Обновление и пример кода см. в следующем разделе. В этом примере кода требуется только AddConnector, поэтому добавить фигуру все равно необходимо.
Чтобы помочь заставить код работать на MACOS, вы можете посмотреть, что возвращается для Shapes, используя пример кода здесь. Итак, создайте лист с требуемыми фигурами и посмотрите, что представляет собой возвращаемый объект.
Ой, спасибо за добрый ответ! Это примерно то, что я хочу, за исключением того, что мне хотелось бы соединять ячейки, а не фигуры. Я проверил документацию, Learn.microsoft.com/en-us/office/vba/api/…, но она принимает форму только в качестве аргумента ConnectedShape. Могу ли я как-нибудь привязать его к определенной точке определенной ячейки???