Excel показывает #VALUE!
, когда мой UDF возвращает строку, содержащую более 255 символов.
Xlwings - 0.7.1, а excel - 2007, который, согласно Microsoft, может содержать до 32767 символов в ячейке.
В чем может быть проблема?
2 ответа
Насколько я могу судить, Py.CallUDF (используемый xlwings udfs) возвращает массив 2D Variant.
Также кажется, что по какой-то причине возврат массива Variant с длиной строки больше 255 из чистого UDF VBA приводит к ошибке #VALUE при вызове в excel. Размещение часов в массиве в редакторе VBA показывает, что данные не повреждены, просто они не передаются правильно. Небольшой поиск вернул несколько вопросов о максимальной длине строк в VBA, но ничего, что конкретно касалось этой проблемы.
Однако возврат массивов String или отдельных строк с> 255 символами работает нормально.
Вот несколько чистых примеров VBA, показывающих проблему:
Вернуть массив вариантов:
Function variant_long_string(n)
Dim temp(0 To 0, 0 To 0) As Variant
temp(0, 0) = String(n, "a")
variant_long_string = temp
End Function
Вызов из Excel возвращает (не выполняется при N> 255):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 #VALUE!
Вернуть элемент массива вариантов:
Function variant_long_string_element(n)
Dim temp(0 To 0, 0 To 0) As Variant
temp(0, 0) = String(n, "a")
variant_long_string_element = temp(0, 0)
End Function
Вызов из Excel возвращает (успешно для N> 255):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
Возвращенный массив строк:
Function string_long_string(n)
Dim temp(0 To 0, 0 To 0) As String
temp(0, 0) = String(n, "a")
string_long_string = temp
End Function
Вызов из Excel возвращает (успешно для N> 255):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
Временное решение
Если ваш Python UDF возвращает только одно строковое значение, например:
@xw.func
def build_long_string(n):
res = 'a'*int(n)
return res
Xlwings автоматически сгенерирует следующий макрос VBA в модуле xlwings_udfs:
Function build_long_string(n)
If TypeOf Application.Caller Is Range Then On Error GoTo failed
build_long_string = Py.CallUDF(PyScriptPath, "build_long_string", Array(n), ThisWorkbook)
Exit Function
failed:
build_long_string = Err.Description
End Function
В качестве быстрого патча, чтобы заставить ваш UDF работать, немного измените этот макрос на это:
Function build_long_string(n)
If TypeOf Application.Caller Is Range Then On Error GoTo failed
temp = Py.CallUDF(PyScriptPath, "build_long_string", Array(n), ThisWorkbook)
build_long_string = temp(0, 0)
Exit Function
failed:
build_long_string = Err.Description
End Function
Позволяет строке> 255 для успешного преобразования в Excel. Вы можете сделать что-то подобное для результата массива, вам просто нужно будет преобразовать массив Variant в массив String путем цикла / переназначения всех значений из temp на результат.
Основываясь на предложении @schoolie выше о преобразовании массива 2D Variant в массив 2D String, я изменил источник логики генерации функции VBA в моем локальном xlwings:
В udfs.generate_vba_wrapper ()
Заменить:
vba.write('{fname} = Py.CallUDF("{module_name}", "{fname}", {args_vba}, ThisWorkbook)\n',
module_name=module_name,
fname=fname,
args_vba=args_vba,
)
С участием:
vba.write('r = Py.CallUDF("{module_name}", "{fname}", {args_vba}, ThisWorkbook)\n',
module_name=module_name,
fname=fname,
args_vba=args_vba,
)
vba.write('ReDim strarray(UBound(r, 1), UBound(r, 2)) As String\n')
vba.write('For i = 0 To UBound(r, 1)\n')
vba.write(' For j = 0 To UBound(r, 2)\n')
vba.write(' strarray(i, j) = CStr(r(i, j))\n')
vba.write(' Next\n')
vba.write('Next\n')
vba.write('{fname} = strarray\n', fname=fname)
Другой вариант - исправить сгенерированный макрос VB в редакторе VB после выполнения «Импортировать Python UDF». Однако это изменение будет потеряно при повторном импорте. Код уже предоставлен @schoolie выше
Похожие вопросы
Новые вопросы
xlwings
Xlwings - это модуль, позволяющий автоматизировать Excel с помощью Python вместо VBA.