Ширина столбца при открытии csv

Автор Reweb, 16 марта 2023, 17:42

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

Reweb

Часто приходиться работать с csv и уже подрастало постоянно настраивать ширину столбцов. 
В 4й версии оптимальная ширина работала как мне нужно - ширина устанавливалась по ширине содержимого в выделенной строке. В 7.4 ширина растягивается по максимальной ширине содержимого столбца и плюяя на выделение.

Отсюда пару вопросов.
1. Как можно настроить оптимальную ширину, чтобы работала как в старом LО? Т.е. выстраивала ширину по выделению.
2. Можно ли настроить ограничение ширины столбца при открытии csv? Скажем, чтобы при открытии макс. ширина была не более 5см, а если содержимое меньше этого значения, то как и  сейчас - по ширине содердимого?




Сведения о версии

Version: 7.4.5.1 (x86) / LibreOffice Community
CPU threads: 4; OS: Windows 6.1 Service Pack 1 Build 7601; UI render: Skia/Raster; VCL: win
Locale: ru-RU (ru_RU); UI: ru-RU
Calc: CL

mikekaganski

С уважением,
Михаил Каганский

Reweb


eeigor

#3
Цитата: Reweb от 16 марта 2023, 22:06Оба вопроса не решаемые?
Напишите для себя маленький макрос, опираясь, к примеру, на этот. Поместите его в библиотеку My Macros & Dialogs (или как там по-русски).
Sub SetColumnWidthsAsInSelRow()
Call SetAppropColumnWidths(bAsInSelRow:=True)
End Sub

Sub SetAppropColumnWidths(Optional bAsInSelRow As Boolean)
''' Устанавливает подходящую ширину столбцов в используемом диапазоне.
''' Ширина столбца с данными будет оптимальной, но не превышающей значения COLUMNWIDTH_MAX,
''' а пустого столбца – по умолчанию (COLUMNWIDTH_DEFAULT).

Const COLUMNWIDTH_DEFAULT = 2260, COLUMNWIDTH_MAX = 5000

Dim oActiveSheet As Object
With ThisComponent.CurrentController
.ZoomValue = 100  'устанавливает масштаб листа в %
oActiveSheet = .ActiveSheet
End With

Dim oCursor As Object, oColumns As Object, oColumn As Object
oCursor = oActiveSheet.createCursor()
oCursor.gotoEndOfUsedArea True
oColumns = oCursor.Columns

If IsMissing(bAsInSelRow) Then bAsInSelRow = False

If bAsInSelRow Then
With ThisComponent.CurrentSelection
If .Rows.Count = 1 And .Columns.Count > 1 Then
Dim oRow As Object
oRow = .Rows(0)
Else
MsgBox "Не выделена строка-образец. Выделите требуемую строку или несколько столбцов в этой строке." _
, , "Настройка ширины столбцов как в выделенной строке"
Exit Sub
End If
End With

Dim j&
For j = 0 To oColumns.Count - 1
oRow.Columns(j).OptimalWidth = True
Next j
Else
For Each oColumn In oColumns
oColumn.OptimalWidth = True
If oColumn.Width > COLUMNWIDTH_MAX Then
oColumn.Width = COLUMNWIDTH_DEFAULT
End If
Next oColumn
End If
End Sub

Цитата: Reweb от 16 марта 2023, 17:42Как можно настроить оптимальную ширину, чтобы работала как в старом LО? Т.е. выстраивала ширину по выделению.
А как это "по выделению"?
Выше добавлена процедура SetColumnWidthsAsInSelRow, вызов которой приведет к настройке ширины столбцов как в выделенной строке-образце.
UPD. Увы, так не работает: столбец расширяется по максимуму... Кто что может предложить?

UPD. Решение приведено в ответе #6.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

mikekaganski

#4
Первая проблема - скорее всего регрессия в диапазоне https://git.libreoffice.org/core/+log/547f4fe%5E..6c5c0302
https://bugs.documentfoundation.org/show_bug.cgi?id=148008
С уважением,
Михаил Каганский

Reweb

Цитата: mikekaganski от 17 марта 2023, 10:13Первая проблема - скорее всего регрессия в диапазоне
Ещё раз прошу прощения, но я видимо слишком туп, чтобы разгадывать Ваши ребусы. Я не пойму как мне может помочь ссылка на коммиты 10летней давности.
Я был бы очень благодарен если бы Вы нашли в себе силы писать на понятном языке. Понятном простым смертным, не сильно знакомым с принципами разработки и ведение соответствующей документации. Смертным, даже не знающих анг язык.

Цитата: eeigor от 17 марта 2023, 09:51Напишите для себя маленький макрос, опираясь, к примеру, на этот.
Ваш макрос попробую позже, а пока опишу мои вчерашние изыскания.

Я обнаружил, что если добавить пустую строку ниже интересующей (для меня это первая, с заголовками столбцов), то оптимальная ширина работает как надо.
Решил записать макрос с костылём: добавление строки -> ширина -> удаление строки. Однако в макросе ни ширина, ни высота, ни форматирование ячеек (нужно вертикальное выравнивание по верху и перенос по словам) не отрабатывают как надо. :(
Сработал только автофильтр и добавление-удаление строки.

Можно ли это как-то поправить?
sub FormatCSV
rem ----------------------------------------------------------------------
rem define variables
dim document   as object
dim dispatcher as object
rem ----------------------------------------------------------------------
rem get access to the document
document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")

rem ----------------------------------------------------------------------
dim args1(0) as new com.sun.star.beans.PropertyValue
args1(0).Name = "ToPoint"
args1(0).Value = "$A$1"

dispatcher.executeDispatch(document, ".uno:GoToCell", "", 0, args1())

rem ----------------------------------------------------------------------
dispatcher.executeDispatch(document, ".uno:DataFilterAutoFilter", "", 0, Array())

rem ----------------------------------------------------------------------
dispatcher.executeDispatch(document, ".uno:InsertRowsAfter", "", 0, Array())

rem ----------------------------------------------------------------------
dim args4(0) as new com.sun.star.beans.PropertyValue
args4(0).Name = "aExtraWidth"
args4(0).Value = 352

dispatcher.executeDispatch(document, ".uno:SetOptimalColumnWidth", "", 0, args4())

rem ----------------------------------------------------------------------
dim args5(0) as new com.sun.star.beans.PropertyValue
args5(0).Name = "ToPoint"
args5(0).Value = "$A$2"

dispatcher.executeDispatch(document, ".uno:GoToCell", "", 0, args5())

rem ----------------------------------------------------------------------
dispatcher.executeDispatch(document, ".uno:DeleteRows", "", 0, Array())

rem ----------------------------------------------------------------------
dim args7(0) as new com.sun.star.beans.PropertyValue
args7(0).Name = "VerticalJustification"
args7(0).Value = 1

dispatcher.executeDispatch(document, ".uno:VerticalJustification", "", 0, args7())

rem ----------------------------------------------------------------------
dim args8(0) as new com.sun.star.beans.PropertyValue
args8(0).Name = "WrapText"
args8(0).Value = true

dispatcher.executeDispatch(document, ".uno:WrapText", "", 0, args8())

rem ----------------------------------------------------------------------
dim args9(0) as new com.sun.star.beans.PropertyValue
args9(0).Name = "RowHeight"
args9(0).Value = 500

dispatcher.executeDispatch(document, ".uno:RowHeight", "", 0, args9())


end sub

ЗЫ. Кстати, дефолтные значения вертикального выравнивание и переноса по словам тоже нигде нельзя настроить?



eeigor

#6
Ну, вот так работает, как надо.

Цитата: Reweb от 16 марта 2023, 17:42Отсюда пару вопросов.
1. Как можно настроить оптимальную ширину, чтобы работала как в старом LО? Т. е. выстраивала ширину по выделению.
2. Можно ли настроить ограничение ширины столбца при открытии csv? Скажем, чтобы при открытии макс. ширина была не более 5 см, а если содержимое меньше этого значения, то как и  сейчас - по ширине содержимого?
1. Вызовите SetColumnWidthsAsInSelRow – установить ширину столбцов как в выделенной строке.
Чтобы идентифицировать строку-образец, достаточно выделить более одного столбца, но только в одной строке. Или выделите строку целиком.
2. Вызовите SetAppropColumnWidths – установить подходящую ширину столбцов. Максимальная ширина задана константой и равна 5 см (COLUMNWIDTH_MAX = 5000).
Масштаб листа устанавливается равным 100%, но Вы можете закомментировать эту строку (.ZoomValue = 100).
Высота строк устанавливается так, чтобы не скрывать многострочные данные.

Sub SetColumnWidthsAsInSelRow()
Call SetAppropColumnWidths(bAsInSelRow:=True)
End Sub

Sub SetAppropColumnWidths(Optional bAsInSelRow As Boolean)
''' Устанавливает подходящую ширину столбцов в используемом диапазоне.
''' Ширина столбца с данными будет оптимальной, но не превышающей значения COLUMNWIDTH_MAX, а пустого столбца – по умолчанию.
''' Если bAsInSelRow равно True, то за образец будет взята выделенная строка.
''' Текст по высоте выравнивается по верхнему краю. Установлен автоперенос слов для диапазона данных.
''' Called by: SetColumnWidthsAsInSelRow
''' Remarks: A word wrap cannot be set in the unused area.
''' При настройке ширины столбцов по строке-образцу значение ячейки копируется в ячейку справа,
''' что за пределами используемого диапазона данных, производится настройка ширины столбца по содержимому,
''' а потом эта ширина устанавливается для требуемого столбца. Временная ячейка очищается.

Const COLUMNWIDTH_MAX = 5000

Dim oActiveSheet As Object
With ThisComponent.CurrentController
Rem .ZoomValue = 100  'sets the zoom of the sheet in %
oActiveSheet = .ActiveSheet
End With

Dim oCursor As Object, oColumns As Object, oColumn As Object
Dim nUnusedCol&  'column index to the right of used area

oCursor = oActiveSheet.createCursor()
oCursor.gotoEndOfUsedArea True
oCursor.VertJustify = com.sun.star.table.CellVertJustify.TOP

oColumns = oCursor.Columns
nUnusedCol = oColumns.Count

If IsMissing(bAsInSelRow) Then bAsInSelRow = False

If bAsInSelRow Then
With ThisComponent.CurrentSelection
If .supportsService("com.sun.star.sheet.SheetCellRange") Then
If .Rows.Count = 1 And .Columns.Count > 1 Then
Dim oRow As Object
oRow = .Rows(0)
Else
MsgBox "Не выделена строка-образец. Выделите требуемую строку или несколько столбцов в этой строке." _
, , "Настройка ширины столбцов как в выделенной строке"
Exit Sub
End If
Else
MsgBox "Не выделена строка-образец. Выделенный объект не является диапазоном." _
, , "Настройка ширины столбцов как в выделенной строке"
Exit Sub
End If
End With

Dim oCell As Object  'empty cell in unused column
Dim j&, s$

For j = 0 To oColumns.Count - 1
s = oRow.getCellByPosition(j, 0).String
oCell = oRow.getCellByPosition(nUnusedCol, 0)
oCell.String = s
oRow.Columns(nUnusedCol).OptimalWidth = True
oRow.Columns(j).Width = oRow.Columns(nUnusedCol).Width
oCell.String = ""
Next j
oRow.Columns(nUnusedCol).OptimalWidth = True  'resets width of empty column to default value
Else
oCursor.IsTextWrapped = False
For Each oColumn In oColumns
oColumn.OptimalWidth = True
If oColumn.Width > COLUMNWIDTH_MAX Then
oColumn.Width = COLUMNWIDTH_MAX
End If
Next oColumn
End If

oCursor.IsTextWrapped = True
oCursor.Rows.OptimalHeight = True
End Sub

Цитата: Reweb от 17 марта 2023, 11:03...чтобы разгадывать Ваши ребусы. Я не пойму как мне может помочь ссылка на коммиты 10летней давности
@Reweb, ответы участников даются не только для Вас, но и для других, и могут носить промежуточный характер (в качестве размышлений по ходу дела).

UPD. В код внесены изменения: добавлена обработка ошибки выделения (например выделена фигура, а не диапазон):
.supportsService("com.sun.star.sheet.SheetCellRange")

UPD. См. альтернативное решение в ответе #13.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

Reweb

Цитата: eeigor от 17 марта 2023, 11:43ответы участников даются не только для Вас, но и для других

Вот именно! Я начинал тоже самое писать, но потом потер :) Если я не понял что этими ссылками хотел сказать коллега, и как они могут помочь в решении проблемы, то и другие могут не понять. (При этом я хоть поверхностно знаю про макросы, программирование/разработку ПО, гуглояндексы и документацию. Но как показывает практика для большинства пользователей это сверхзадачи).

ЗЫ. Про макросы напишу попозже.

eeigor

#8
Цитата: Reweb от 17 марта 2023, 11:03ЗЫ. Кстати, дефолтные значения вертикального выравнивание и переноса по словам тоже нигде нельзя настроить?
В код выше (ответ #6) внесены изменения – добавлены комментарий к процедуре, исправлена ошибка (удалена константа COLUMNWIDTH_DEFAULT) и вставлен требуемый код:
   oCursor.VertJustify = com.sun.star.table.CellVertJustify.TOP
   oCursor.IsTextWrapped = True
   oCursor.Rows.OptimalHeight = True

UPD. Макросы, похоже, работают в точности, как просил автор. Желающие могут добавить их в свою библиотеку макросов.
Подмечено, что для неиспользуемого диапазона нельзя установить автоперенос слов.
Поэтому строка:
   oRow.Columns(nUnusedCol).OptimalWidth = True
работает верно. Иначе при установленном (до выполнения процедуры) автопереносе слов ширина была бы сброшена до ширины по умолчанию.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

sokol92

#9
У меня для подобной цели используется макрос из одной строки, который устанавливает (подбирает) "оптимальную" ширину столбцов для выделенных на листе ячеек.
При этом надо иметь в виду, что ячейки, содержащие текст, с установленным переносом слов при подборе ширины игнорируются. Если в выделении все ячейки содержат тексты и имеют свойство "Переносить по словам", то ширина столбца устанавливается на стандартную величину.

Возможно (как следует из этой темы), в каких-то случаях макрос будет работать не так, как задумано.

' Выполняет автоподбор ширины для выделенных ячеек
Sub SelectionOptimalWidth
 DispCall ThisComponent, "SetOptimalColumnWidth", Array("aExtraWidth", 100)
End Sub

Использованная служебная функция:
' lang:ru
' Вызов Диспетчера команд.
' Параметры:
' oDoc документ.
' cmd  команда Диспетчера. Если в команде отсутствует знак ":", то добавляется префикс ".uno:".
' args массив аргументов команды. Может быть массивом PropertyValues или массивом Variant с четным числом элементов, элементы
'      которого последовательно задают имена и значения свойств (имя1, значение1, ...).
Function DispCall(Byval oDoc, Byval cmd As String, Optional Byval args)
  Static dispatcher as Object
  Dim aProps, arr(0) As New com.sun.star.beans.PropertyValue, i As Long, n As Long
  If dispatcher Is Nothing Then
    dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
  End If  
 
  If Instr(1, cmd, ":")=0 Then cmd=".uno:" & cmd
 
  If IsMissing(args) Then
    aProps=Array()
  Else
    aProps=args
   
    If Lbound(aProps)<=Ubound(aProps) Then
      If Not IsUnoStruct(aProps(Lbound(aProps))) Then
        n=0
        ReDim Preserve arr((Ubound(aProps) - LBound(aProps) + 1) / 2)
        For i=Lbound(aProps) To Ubound(aProps) Step 2
          arr(n).Name = aProps(i)
          arr(n).Value = aProps(i + 1)
          n=n+1
        Next i
        aProps=arr
      End If
    End If
  End If

  dispatcher.executeDispatch oDoc.CurrentController.Frame, cmd, "", 0, aProps
 
  DispCall=True
End Function


LO 7.4.2.3 Win 10.
Владимир.

eeigor

#10
Владимир, и к оптимальной ширине столбца будет добавлен "довесок" (aExtraWidth) равный 100. Зачем, не знаю. Можно обнулить или само свойство удалить. Но реализация упрощения вызова диспетчера понравилась.

Кроме того, метод диспетчера ".uno:SetOptimalColumnWidth" настраивает ширину столбцов в выделенном диапазоне, а у меня в макросе идет ссылка на столбец целиком, так как даже если прежде указать диапазон, то его метод Columns(j) всё равно вернет весь столбец.
Отсюда вопрос: что изменить в моем макросе, чтобы не настраивать ширину во временном столбце, в ячейку которого переносится значение?

UPD. Одно из решений – вызвать метод диспетчера.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

sokol92

Меня это устраивает. Можно значение 100 (0,1 см) изменить на другое.
Владимир.

sokol92

#12
Что касается ячеек со свойством "Переносить по словам", то для них нет иного выхода, как устанавливать ширину столбца вручную. Можно, конечно, вычислить максимум из всех длин отдельных слов (чтобы слова не разрывались), но это уже "высший пилотаж".  :)

Мы широко используем файлы формата csv для транспорта информации из баз данных в LO. В этом формате не предусмотрена возможность указания какой-либо дополнительной информации, например о форматировании поля, так что нужно искать обходные пути. Мы в SQL-запросе к наименованию поля добавляем (в лаконичном формате) указания по форматированию соответствующего столбца.
Владимир.

eeigor

#13
Проанализировал код Владимира и свой, что в ответе #6, ещё раз.
1. По поводу "довеска":
Цитата: sokol92 от 17 марта 2023, 19:06Можно значение 100 (0,1 см) изменить на другое.
Если это свойство удалить совсем, то откроется окно для его задания, что нежелательно.
Если это свойство обнулить, то метод строки OptimalHeight работает неверно, по крайней мере для кириллицы. "Довесок" делает своё дело.

Цитата: eeigor от 17 марта 2023, 19:05...метод диспетчера ".uno:SetOptimalColumnWidth" настраивает ширину столбцов в выделенном диапазоне, а у меня в макросе идет ссылка на столбец целиком, так как даже если прежде указать диапазон, то его метод Columns(j) всё равно вернет весь столбец.
Отсюда вопрос: что изменить в моем макросе, чтобы не настраивать ширину во временном столбце, в ячейку которого переносится значение?
UPD. Одно из решений – вызвать метод диспетчера.
2. Изменил код ниже: теперь настройка ширины столбцов по выделенной строке работает через вызов соответствующего метода диспетчера. Предыдущий код закомментирован. Вы можете удалить его сами.

''''''''''''''''''''''''''''' SetAppropColumnWidths ''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub SetColumnWidthsAsInSelRow()
Call SetAppropColumnWidths(bAsInSelRow:=True)
End Sub

Sub SetAppropColumnWidths(Optional bAsInSelRow As Boolean)
''' Устанавливает подходящую ширину столбцов в используемом диапазоне.
''' Ширина столбца с данными будет оптимальной, но не превышающей значения COLUMNWIDTH_MAX, а пустого столбца – по умолчанию.
''' Если bAsInSelRow равно True, то за образец будет взята выделенная строка.
''' Текст по высоте выравнивается по верхнему краю. Установлен автоперенос слов для диапазона данных.
''' Called by: SetColumnWidthsAsInSelRow
''' Calls: UnoSetOptimalColumnWidth
''' Remarks: A word wrap cannot be set in the unused area.
''' При настройке ширины столбцов по строке-образцу значение ячейки копируется в ячейку справа,
''' что за пределами используемого диапазона данных, производится настройка ширины столбца по содержимому,
''' а потом эта ширина устанавливается для требуемого столбца. Временная ячейка очищается.

Const COLUMNWIDTH_MAX = 5000

Dim oActiveSheet As Object
With ThisComponent.CurrentController
Rem .ZoomValue = 100  'sets the zoom of the sheet in %
oActiveSheet = .ActiveSheet
End With

Dim oCursor As Object, oColumns As Object, oColumn As Object
' Dim nUnusedCol&  'column index to the right of used area

oCursor = oActiveSheet.createCursor()
oCursor.gotoEndOfUsedArea True
oCursor.VertJustify = com.sun.star.table.CellVertJustify.TOP

oColumns = oCursor.Columns
' nUnusedCol = oColumns.Count

If IsMissing(bAsInSelRow) Then bAsInSelRow = False

If bAsInSelRow Then
With ThisComponent.CurrentSelection
If .supportsService("com.sun.star.sheet.SheetCellRange") Then
If .Rows.Count = 1 And .Columns.Count > 1 Then
Dim oRow As Object
oRow = .Rows(0)
Else
MsgBox "Не выделена строка-образец. Выделите требуемую строку или несколько столбцов в этой строке." _
, , "Настройка ширины столбцов как в выделенной строке"
Exit Sub
End If
Else
MsgBox "Не выделена строка-образец. Выделенный объект не является диапазоном." _
, , "Настройка ширины столбцов как в выделенной строке"
Exit Sub
End If
End With

' Dim oCell As Object  'empty cell in unused column
' Dim j&, s$
'
' For j = 0 To oColumns.Count - 1
' s = oRow.getCellByPosition(j, 0).String
' oCell = oRow.getCellByPosition(nUnusedCol, 0)
' oCell.String = s
' oRow.Columns(nUnusedCol).OptimalWidth = True
' oRow.Columns(j).Width = oRow.Columns(nUnusedCol).Width
' oCell.String = ""
' Next j
' oRow.Columns(nUnusedCol).OptimalWidth = True  'resets width of empty column to default value

oCursor.IsTextWrapped = False
ThisComponent.CurrentController.select oRow.queryIntersection(oCursor.RangeAddress)
Call UnoSetOptimalColumnWidth
Else
oCursor.IsTextWrapped = False
For Each oColumn In oColumns
oColumn.OptimalWidth = True
If oColumn.Width > COLUMNWIDTH_MAX Then
oColumn.Width = COLUMNWIDTH_MAX
End If
Next oColumn
End If

oCursor.IsTextWrapped = True
oCursor.Rows.OptimalHeight = True
End Sub

Sub UnoSetOptimalColumnWidth()
Dim document As Object, dispatcher As Object

document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")

Dim args(0) As New com.sun.star.beans.PropertyValue
args(0).Name = "aExtraWidth"
args(0).Value = 100
dispatcher.executeDispatch(document, ".uno:SetOptimalColumnWidth", "", 0, args)
End Sub

UPD. Код не стал короче. Поэтому автор может использовать этот набор макросов или макросы из ответа #6: они идентичны по функциональности.

@Reweb, Вы также можете использовать процедуру UnoSetOptimalColumnWidth независимо, если надо установить ширину столбцов, взяв за образец несколько строк (например строку заголовка тоже). Метод диспетчера не требует, чтобы диапазон был непрерывным: Вы можете выбрать несколько отдельных ячеек из разных строк, удерживая клавишу Ctrl, и вызвать эту процедуру для установки ширины тех столбцов, ячейки в которых были выделены.

Цитата: sokol92 от 17 марта 2023, 18:51У меня для подобной цели используется макрос из одной строки...
Одной строкой не обойтись. Надо по второму вопросу устанавливать ширину столбца не более заданной константой. Кроме того, автору надо настраивать выравнивание текста по высоте и устанавливать автоперенос слов с подгонкой высоты строки. Плюс обработка ошибок выделения.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

sokol92

#14
Цитата: Reweb от 17 марта 2023, 11:03Кстати, дефолтные значения вертикального выравнивание и переноса по словам тоже нигде нельзя настроить?
Можете создать новый документ Calc, изменить свойства стиля ячеек "Базовый" (Меню Стили / Управление стилями) по своему усмотрению и сохранить этот документ как шаблон (Меню Файл / Шаблоны / Сохранить как шаблон). Если Вы при сохранении шаблона укажете свойство "Установить шаблоном по умолчанию", то все новые документы будут создаваться на основе этого шаблона.
Владимир.