Архив рубрики: OpenOffice

Печать твердых копий из OOBase

ООфис  имеет встроенные средства для создания отчетов и они наверное подойдут многим. Но откровенно говоря я склоняюсь к печати документов через программно генерируемые таблицы ООКалк.

Кто еще хочет попробовать вот много кода из которого можно и для себя взять какие-то моменты. Главное собственно вызвать ООКалк с пустым документом, а по прочим вопросам документации достаточно.

Sub PrintNZP()

MsgBox "Закройте текущее сообщение и дождитесь сообщение <<Обработка документа окончена>>"

Dim oNDoc, oNSheets, oNSheet

oNDoc = StarDesktop.loadComponentFromURL( _
"private:factory/scalc", "_blank", 0, Array() )
oNSheets = oNDoc.getSheets()
oNSheet = oNSheets.getByIndex(0)


Dim sCurrentKod As String
Dim Connection As Variant
Dim Statement As Variant
Dim ResultSet As Variant
Dim UpdateStatement As Variant
Dim sKod As String
Dim dPlatmin As Double
Dim dCts As Double
Dim dPlatkop As Double
Dim iCountErr As Integer

If FormDoc_det.isModified Then
FormDoc_det().UpdateRow()
End If

GlobalScope.BasicLibraries.loadLibrary("aovc")
sCurrentKod = FormDoc_h().Columns.getByName("Kod").String
Connection = aovc.GetConnection()
Statement = Connection.CreateStatement()
ResultSet = Statement.ExecuteQuery("SELECT doc_det.*, doc_det.kol * doc_det.nv AS min, doc_det.kol * doc_det.rc / 100 AS grn, (SELECT DISTINCT name FROM cennic AS c WHERE c.kod = doc_det.koddet) AS namedet, (SELECT DISTINCT i.name FROM izdel AS i WHERE i.kod = doc_det.kodiz) AS nameiz FROM dbo.doc_det AS doc_det where doc_det.kod=" & sCurrentKod & " order by kod")

Dim iCounter as Integer
Dim I As Integer
oNSheet.GetCellByPosition(7,0).string = "А К Т"
oNSheet.GetCellByPosition(3,1).string = "инвентаризации незавершенного производства по цеху 16 на 1-е"
oNSheet.GetCellByPosition(7,0).CharHeight = 14
oNSheet.GetCellByPosition(3,1).CharHeight = 14
oNSheet.GetCellByPosition(3,1).string = FormDoc_h.Columns.GetByName("prim").String
oNSheet.GetCellByPosition(0,2).string = "№ пп"
oNSheet.GetCellRangeByPosition(0,2,0,3).Merge(True)
oNSheet.GetCellByPosition(0,2).Columns.GetByIndex(0).Width = "700"
oNSheet.GetCellByPosition(0,2).isTextWrapped = True
oNSheet.GetCellByPosition(0,2).HoriJustify = 2
oNSheet.GetCellByPosition(0,2).VertJustify = 2

oNSheet.GetCellByPosition(1,2).string = "Наименование изделий, деталей и узлов"
oNSheet.GetCellByPosition(1,2).Columns.GetByIndex(0).Width = "4200"
oNSheet.GetCellRangeByPosition(1,2,1,3).Merge(True)
oNSheet.GetCellByPosition(1,2).isTextWrapped = True
oNSheet.GetCellByPosition(1,2).HoriJustify = 2
oNSheet.GetCellByPosition(1,2).VertJustify = 2

oNSheet.GetCellByPosition(2, 2).string = "№№ деталей или узлов"
oNSheet.GetCellByPosition(2,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(2,2,2,3).Merge(True)
oNSheet.GetCellByPosition(2,2).HoriJustify = 2
oNSheet.GetCellByPosition(2,2).VertJustify = 2
oNSheet.GetCellByPosition(2,2).Columns.GetByIndex(0).Width = "1800"

oNSheet.GetCellByPosition(3, 2).string = "Ед. изм."
oNSheet.GetCellByPosition(3,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(3,2,3,3).Merge(True)
oNSheet.GetCellByPosition(3,2).HoriJustify = 2
oNSheet.GetCellByPosition(3,2).VertJustify = 2
oNSheet.GetCellByPosition(3,2).Columns.GetByIndex(0).Width = "1000"

oNSheet.GetCellByPosition(4, 2).string = "№ послед-ней закончен-ной операции"
'oNSheet.GetCellByPosition(4,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(4,2,4,3).isTextWrapped = True
oNSheet.GetCellRangeByPosition(4,2,4,3).Merge(True)
oNSheet.GetCellByPosition(4,2).HoriJustify = 2
oNSheet.GetCellByPosition(4,2).VertJustify = 2
oNSheet.GetCellByPosition(4,2).Columns.GetByIndex(0).Width = "2000"

oNSheet.GetCellByPosition(5, 2).string = "Книж-ный оста-ток"
oNSheet.GetCellByPosition(5,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(5,2,5,3).Merge(True)
oNSheet.GetCellByPosition(5,2).HoriJustify = 2
oNSheet.GetCellByPosition(5,2).VertJustify = 2
oNSheet.GetCellByPosition(5,2).Columns.GetByIndex(0).Width = "1800"


oNSheet.GetCellByPosition(6, 2).string = "Факти-ческое нали-чие"
oNSheet.GetCellByPosition(6,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(6,2,6,3).Merge(True)
oNSheet.GetCellByPosition(6,2).HoriJustify = 2
oNSheet.GetCellByPosition(6,2).VertJustify = 2
oNSheet.GetCellByPosition(6,2).Columns.GetByIndex(0).Width = "1400"

oNSheet.GetCellByPosition(7, 2).string = "Результат"
oNSheet.GetCellByPosition(7,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(7,2,8,2).Merge(True)
oNSheet.GetCellByPosition(7,2).HoriJustify = 2
oNSheet.GetCellByPosition(7,2).VertJustify = 2
'oNSheet.GetCellByPosition(7,2).Columns.GetByIndex(0).Width = "1800"


oNSheet.GetCellByPosition(7, 3).string = "Из-лиш-ки"
oNSheet.GetCellByPosition(7,3).isTextWrapped = True
oNSheet.GetCellByPosition(7,3).HoriJustify = 2
oNSheet.GetCellByPosition(7,3).VertJustify = 2
oNSheet.GetCellByPosition(7,3).Columns.GetByIndex(0).Width = "1100"

oNSheet.GetCellByPosition(8, 3).string = "Недо-стача"
oNSheet.GetCellByPosition(8,3).isTextWrapped = True
oNSheet.GetCellByPosition(8,3).HoriJustify = 2
oNSheet.GetCellByPosition(8,3).VertJustify = 2
oNSheet.GetCellByPosition(8,3).Columns.GetByIndex(0).Width = "1100"


oNSheet.GetCellByPosition(9, 2).string = "Стоимость материала"
oNSheet.GetCellByPosition(9,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(9,2,10,2).Merge(True)
oNSheet.GetCellByPosition(9,2).HoriJustify = 2
oNSheet.GetCellByPosition(9,2).VertJustify = 2
'oNSheet.GetCellByPosition(9,2).Columns.GetByIndex(0).Width = "1800"


oNSheet.GetCellByPosition(9, 3).string = "На единицу"
oNSheet.GetCellByPosition(9,3).isTextWrapped = True
oNSheet.GetCellByPosition(9,3).HoriJustify = 2
oNSheet.GetCellByPosition(9,3).VertJustify = 2
oNSheet.GetCellByPosition(9,3).Columns.GetByIndex(0).Width = "2400"

oNSheet.GetCellByPosition(10, 3).string = "На все количе-ство"
oNSheet.GetCellByPosition(10,3).isTextWrapped = True
oNSheet.GetCellByPosition(10,3).HoriJustify = 2
oNSheet.GetCellByPosition(10,3).VertJustify = 2
oNSheet.GetCellByPosition(10,3).Columns.GetByIndex(0).Width = "1900"


oNSheet.GetCellByPosition(11, 2).string = "Стоимость зарплаты"
oNSheet.GetCellByPosition(11,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(11,2,12,2).Merge(True)
oNSheet.GetCellByPosition(11,2).HoriJustify = 2
oNSheet.GetCellByPosition(11,2).VertJustify = 2

oNSheet.GetCellByPosition(11, 3).string = "На единицу"
oNSheet.GetCellByPosition(11,3).isTextWrapped = True
oNSheet.GetCellByPosition(11,3).HoriJustify = 2
oNSheet.GetCellByPosition(11,3).VertJustify = 2
oNSheet.GetCellByPosition(11,3).Columns.GetByIndex(0).Width = "2400"

oNSheet.GetCellByPosition(12, 3).string = "На все количе-ство"
oNSheet.GetCellByPosition(12,3).isTextWrapped = True
oNSheet.GetCellByPosition(12,3).HoriJustify = 2
oNSheet.GetCellByPosition(12,3).VertJustify = 2
oNSheet.GetCellByPosition(12,3).Columns.GetByIndex(0).Width = "1600"

oNSheet.GetCellByPosition(13, 2).string = "Цехо-вые рас-ходы _____%"
oNSheet.GetCellByPosition(13,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(13,2,13,3).Merge(True)
oNSheet.GetCellByPosition(13,2).HoriJustify = 2
oNSheet.GetCellByPosition(13,2).VertJustify = 2
oNSheet.GetCellByPosition(13,2).Columns.GetByIndex(0).Width = "1600"

oNSheet.GetCellByPosition(14, 2).string = "Обще-завод-ские рас-ходы _____%"
oNSheet.GetCellByPosition(14,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(14,2,14,3).Merge(True)
oNSheet.GetCellByPosition(14,2).HoriJustify = 2
oNSheet.GetCellByPosition(14,2).VertJustify = 2
oNSheet.GetCellByPosition(14,2).Columns.GetByIndex(0).Width = "1600"

oNSheet.GetCellByPosition(15, 2).string = "Всего завод-ская себе-стои-мость"
oNSheet.GetCellByPosition(15,2).isTextWrapped = True
oNSheet.GetCellRangeByPosition(15,2,15,3).Merge(True)
oNSheet.GetCellByPosition(15,2).HoriJustify = 2
oNSheet.GetCellByPosition(15,2).VertJustify = 2
oNSheet.GetCellByPosition(15,2).Columns.GetByIndex(0).Width = "1800"



Dim BasicBorder as New com.sun.star.table.BorderLine
Dim BasicBorder0 as New com.sun.star.table.BorderLine
Dim oBorder As Object
oBorder = oNSheet.getCellRangeByPosition(0,2,15,4).TableBorder
rem Werte für einen einfachen Rand
BasicBorder.Color = RGB(0, 0, 0)
BasicBorder.InnerLineWidth = 20
BasicBorder.OuterLineWidth = 20
BasicBorder.LineDistance =0

BasicBorder0.Color = RGB(0, 0, 0)
BasicBorder0.InnerLineWidth = 100
BasicBorder0.OuterLineWidth = 0
BasicBorder0.LineDistance = 0

oBorder.LeftLine = BasicBorder
oBorder.TopLine = BasicBorder
oBorder.RightLine = BasicBorder
oBorder.BottomLine = BasicBorder
oBorder.HorizontalLine = BasicBorder
oBorder.VerticalLine = BasicBorder
oNSheet.getCellRangeByPosition(0,2,15,4).TableBorder = oBorder

Dim currentKodDet As Long
Dim currentKodIz As Long
Dim Npp As Long
Dim Total As Long
Dim Count As Long

currentKodDet = -9999
currentKodIz = -9999
Npp=0
Total = 0
Count = 0

iCounter = 3

While ResultSet.Next

If ResultSet.Columns.getByName("kol").Long = 0 Then
GOTO lNext
End If

oBorder = oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder
oBorder.LeftLine = BasicBorder
oBorder.TopLine = BasicBorder
oBorder.RightLine = BasicBorder
oBorder.BottomLine = BasicBorder
oBorder.HorizontalLine = BasicBorder
oBorder.VerticalLine = BasicBorder
oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder = oBorder

If currentKodDet <> ResultSet.Columns.getByName("koddet").Long OR currentKodIz <> ResultSet.Columns.getByName("kodiz").Long Then

If Count > 1 Then
oNSheet.GetCellByPosition(5, iCounter).value = Total
oNSheet.GetCellByPosition(6, iCounter).value = Total
oBorder = oNSheet.getCellRangeByPosition(4, iCounter,6,iCounter).TableBorder
oBorder.TopLine = BasicBorder0
oNSheet.getCellRangeByPosition(4, iCounter,6,iCounter).TableBorder = oBorder
iCounter = iCounter + 1
End If

oBorder = oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder
oBorder.LeftLine = BasicBorder
oBorder.TopLine = BasicBorder
oBorder.RightLine = BasicBorder
oBorder.BottomLine = BasicBorder
oBorder.HorizontalLine = BasicBorder
oBorder.VerticalLine = BasicBorder
oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder = oBorder

currentKodDet = ResultSet.Columns.getByName("koddet").Long
Npp = Npp + 1
Total = 0
Count = 0
iCounter = iCounter + 1
End If

If currentKodIz <> ResultSet.Columns.getByName("kodiz").Long Then
oBorder = oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder
oBorder.LeftLine = BasicBorder
oBorder.TopLine = BasicBorder0
oBorder.RightLine = BasicBorder
oBorder.BottomLine = BasicBorder
oBorder.HorizontalLine = BasicBorder
oBorder.VerticalLine = BasicBorder
oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder = oBorder
currentKodIz = ResultSet.Columns.getByName("kodiz").Long
Npp = 1
oNSheet.GetCellByPosition(1, iCounter).string = Trim(ResultSet.Columns.getByName("nameiz").String)
oNSheet.GetCellByPosition(1, iCounter).CharHeight = 20
iCounter = iCounter + 1
EndIf

oBorder = oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder
oBorder.LeftLine = BasicBorder
oBorder.TopLine = BasicBorder
oBorder.RightLine = BasicBorder
oBorder.BottomLine = BasicBorder
oBorder.HorizontalLine = BasicBorder
oBorder.VerticalLine = BasicBorder
oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder = oBorder


Count = Count + 1

If Count = 1 Then
oNSheet.GetCellByPosition(1, iCounter).string = Trim(ResultSet.Columns.getByName("namedet").String) & Rem97(ResultSet.Columns.getByName("naz").Long)
oNSheet.GetCellByPosition(0, iCounter).value = Npp
End If

oNSheet.GetCellByPosition(4, iCounter).String = sNop(ResultSet.Columns.getByName("nop").Long)
oNSheet.GetCellByPosition(5, iCounter).value = ResultSet.Columns.getByName("kol").Long
oNSheet.GetCellByPosition(6, iCounter).value = ResultSet.Columns.getByName("kol").Long

If ResultSet.Columns.getByName("nv").Double > 0 Then
oNSheet.GetCellByPosition(2, iCounter).value = ResultSet.Columns.getByName("nv").Double
oNSheet.GetCellByPosition(11, iCounter).value = ResultSet.Columns.getByName("rc").Double
Else
oNSheet.GetCellByPosition(11, iCounter).string = " - "
End If

Total = Total + ResultSet.Columns.getByName("kol").Long

iCounter = iCounter + 1
lNext: REM
Wend
If Count > 1 Then
oBorder = oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder
oBorder.LeftLine = BasicBorder
oBorder.TopLine = BasicBorder
oBorder.RightLine = BasicBorder
oBorder.BottomLine = BasicBorder
oBorder.HorizontalLine = BasicBorder
oBorder.VerticalLine = BasicBorder
oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder = oBorder
oNSheet.GetCellByPosition(5, iCounter).value = Total
oNSheet.GetCellByPosition(6, iCounter).value = Total
oBorder = oNSheet.getCellRangeByPosition(4, iCounter,6,iCounter).TableBorder
oBorder.TopLine = BasicBorder0
oNSheet.getCellRangeByPosition(4, iCounter,6,iCounter).TableBorder = oBorder
iCounter = iCounter + 1
End If
oBorder = oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder
oBorder.LeftLine = BasicBorder
oBorder.TopLine = BasicBorder
oBorder.RightLine = BasicBorder
oBorder.BottomLine = BasicBorder0
oBorder.HorizontalLine = BasicBorder
oBorder.VerticalLine = BasicBorder
oNSheet.getCellRangeByPosition(0,iCounter,15,iCounter).TableBorder = oBorder


oNSheet.GetCellByPosition(1, iCounter + 1).string = "==========================================="
MsgBox "Обработка документа окончена"

End Sub


OOBase Basic

Спасибо за включение ссылки на мои заметки в Вики русскоязычного форума ООо http://wiki.forumooo.ru/wiki/%D0%9F%D0%BE%D0%BB%D0%B5%D0%B7%D0%BD%D1%8B%D0%B5_%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8 (Сразу замечу, что не имел к этому никакого отношения - просто пытался старательно выполнять свою работу). Понял, что не хватает собственно файла с моими наработками. Вот он с Виртуального Диска Гуугла:
 
https://drive.google.com/file/d/0B1CrvL0JhNFeeFBJWWJ4TGZra1U/view?usp=sharing

OpenOfficeBase + Basic — обещанное продолжение.

После почти двухлетнего перерыва, связанного со сменой профессональных интересов и поиском красивых решений - продолжаю повествование. Что же поменялось?

Во-первых, я стал более ориентироваться на использование в качестве движка OpenOfficrBase (not OpenOfficeWriter). Это произошло в связи с тем, что 3-я версия офиса поддерживает библиотеки уроня файла базы данных, которые могут удобно распрастраняться внутри файла .odb.

Во-вторых, я перешел на использование соединения JDBC, так как "родные" офисные соединения, к сожалению, не всегда поддерживают возможности JDBC 3...4.

В-третьих, я перешел на использование вода через форму (а не прямо в таблице). Это обусловлено и большей наработкой кода, который позволяет мастерить формы без проблем. И, в большей степени, из-за обнаружившегося ограничения ввода связанных данных через список (или комбобокс) примерно 30 тыс. строк (связано с разядностью целого числа).

Теперь немного кода.

Довольно накладно заполнять форму из базы данных и сохранять значения формы в базе данных. Соблюдая некоторые соглашения об именовании, можно это сделать просто (имя поля равно имени элемента)

Sub FromBaseToDialog(oForm, oDialog)
If oForm.IsNew Or oForm.IsAfterLast() Or oForm.IsBeforeFirst() Then
Exit Sub
End If
Dim I
Dim sName As String
For I = 0 To Ubound(oDialog.Model.ElementNames)
sName = oDialog.Model.ElementNames(I)
If Mid(sName, 1, 1) <> "_" And Mid(sName, 1, 5) <> "Label" Then
ODialog.GetControl(sName).SetText(oForm.Columns.GetByName(sName).String)
End If
If Mid(sName, 1, 6) = "_date_" Then
DateFromBaseToControl(oForm.Columns.GetByName(Mid(sName, 7)), ODialog.GetControl(sName))
End If
If Mid(sName, 1, 7) = "_combo_" Then
'xRay oForm.Columns.GetByName(Mid(sName, 8))
If oForm.Columns.GetByName(Mid(sName, 8)).String <> "" Then
InitComboBox(oDialog, sName, oForm.Columns.GetByName(Mid(sName, 8)).String)
End If
End If


Next I
End Sub

Sub FromDialogToBase(oDialog, oForm)
Dim I
Dim sName As String
For I = 0 To Ubound(oDialog.Model.ElementNames)
sName = oDialog.Model.ElementNames(I)
If Mid(sName, 1, 1) <> "_" And Mid(sName, 1, 5) <> "Label" Then
If Mid(sName,1,4) = "date" Then
oForm.Columns.GetByName(sName).UpdateString(ctl.util.Replace(Trim(oDialog.GetControl(sName).GetText()), ",", "."))
Else
oForm.Columns.GetByName(sName).UpdateString(Trim(oDialog.GetControl(sName).GetText()))
End If
End If
If Mid(sName, 1, 6) = "_date_" Then
DateFromControlToBase(ODialog.GetControl(sName), oForm.Columns.GetByName(Mid(sName, 7)))
' DateFromBaseToControl(oForm.Columns.GetByName(Mid(sName, 7)), ODialog.GetControl(sName))
End If
If Mid(sName, 1, 7) = "_combo_" Then
oForm.Columns.GetByName(Mid(sName, 8)).UpdateString(ODialog.GetControl(sName).GetModel.Tag)
' DateFromBaseToControl(oForm.Columns.GetByName(Mid(sName, 7)), ODialog.GetControl(sName))
End If
Next I
End Sub

Как видно, есть специальные имена, начинающиеся с знака подчеркивания. Они подойдут для всяких меток, которые не должны обрабатываться. И еще пока что два обрабатываемых имени _date_ и _combo_, которые обрабатываются специальным образом. О списках  (комбобоксах) ма поговорим в следующий раз, а о датах можно поговорить сейчас. OpenOffice удобно обрабатывает даты вводом через календарь, но что касается перевода БД в строку, присваивания строки элементу и обратно - то тут царит полнй хаос. Для PosgreSQL я использую вспомогательные функции:

Function DateToControl(dt) As Long
DateToControl = dt.YEAR*10000 + dt.MONTH*100 + dt.DAY
End Function

Sub DateFromBaseToControl(field, ctl)
If DateToControl(field.DATE) > 0 Then
ctl.SetDate(DateToControl(field.DATE)
Else
ctl.SetText("")
End If
End Sub

Function DateFromControl(dt As Long) As Object
Dim oDt As New com.sun.star.util.Date
Dim NewDate As String
NewDate = dt
If dt > 0 Then
oDt.YEAR = Mid(NewDate,1,4)
oDt.MONTH = Mid(NewDate,5,2)
oDt.DAY = Mid(NewDate,7,2)
End If
DateFromControl = oDt
End Function

Sub DateFromControlToBase(ctl, field)
if ctl.GetDate() <> 0 Then
field.UpdateDate(DateFromControl(ctl.GetDate()))
else
field.UpdateNull()
End If
End Sub

Ну вот собственно и все.

Теперь по кнопке вызываем диалог:

Sub Dogovor_Edit(Event)
Dim oDialog As Object
Dim f_dogovor As Object
f_dogovor = Thiscomponent.DrawPage.Forms.GetByName("f_dogovor")
oDialog = GetDialog("ctl", "dogovor")
If oDialog.Execute() = 1 Then
If Event.Source.Model.Tag = "new" Then
f_dogovor.MoveToInsertRow()
ctl.util.FromDialogToBase(oDialog, f_dogovor)
f_dogovor.InsertRow()
Else
ctl.util.FromDialogToBase(oDialog, f_dogovor)
f_dogovor.UpdateRow()
EndIf
Else
End If
End Sub



Пока.

OpenOffice Base — что же дальше

Я понимаю, что читатель истосковался по более сложному коду. И он скорo будет опубликован. Мое направление сейчас - обернуть функционалом стандартный котнрол Диалогов - ComboBox.

Напомню, что Combobox Диалогов это не совсем тот же элемент, что и Combobox Формы. Но он нам очень пригодился бы для реализации ввода во "всплывающие" и модальное диалоговое окно, где загружать форму было бы слишком неюзабельно. Пока.

Объекты Connection, Statement, ResultSet в OpenOffice

Для решения простых задач достаточно декларативно задать объекты данных в Мастерах-построителях или в Палитрах свойств объектов Формы. Если бы на этом возможности OpenOffice заканчивались, то кроме игр с данными ему сложно было бы найти применение. Вспоминаю сколько раз я раскрывал Star/OpenOffice, создавал подключение к серверу баз данных и с грустью закрывал, полистав весьма скудный (и до сих пор это так) Help.

Но время не стоит на месте. В Интернете начала накапливаться полезная информация о макросах OpenOffice и вот мы здесь.

Для начала работы следует получить объект Connection. Его можно получить несколькими способами. В частности из свойства ActiveConnection объекта Form или по символическому имени DataSource, которое можно задавать из Главного меню|OpenOffice Tools|Options|OpenOffice.org Base|Databases. Если вынести функцию GetConnection в скрипт из раздела My Macros, всегда будет достаточно изменить код ровно в одном месте чтобы измененить подключение.

Function GetConnection() As Variant

  Dim DataSource As Variant
  Dim Connection As Variant
  DatabaseContext = CreateUnoService("com.sun.star.sdb.DatabaseContext")
  DataSource = DatabaseContext.GetByName("sourcename")
  GetConnection = DataSource.GetConnection()

End Function

Получив Connection надо создать Satement или PreparedStatement.

oStatement = GetConnection().CreateStatement
oResultSet = Statement.ExecuteQuery("select * from ...")
lCount = Statement.ExecuteUpdate("update ...")

Теперь особое предупреждение для любителей строить запросы конкатенацией строк. Не забудем, что OpenOffice является офисным приложением. Поэтому при переводе чисел с точкой в строку десятичная точка превращается... превращается ... правильно - в десятичную запятую, что скорее всего откажется воспринять ваш сервер баз данных. Для такого случая удобно воспользоваться параметрическим запросом.

pStatement = GetConnection().PrepareStatement("update ... set ... = ? where ... = ?")
pStatement.SetDouble(1, 123.456)
pStatement.SetString(2, "789" )
                                                    ' pStatement.ExecuteQuery
pStatement.ExecuteUpdate


Объект ResultSet, полученный в результате запроса поддерживает удобную (привычную) навигацию и может быть изменяемым. То есть все плюшки, известные по ODBC/JDBC/DAO/ADO  и иже с ними налицо. Впрочем это все уже можно прочитать в Интернете. Я же хотел дать зацепки для поиска нужных моментов. Пока.

Библиотеки, диалоги и документы Writer при работе с OpenOffice Base

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

Написав первые строчки на OpenOffice Basic опытный программист начинает задумываться а как же рационально организовать программный код. Продукт, который от редактора текста эволюционирует в сторону среды для работы клиентской части приложения, работающего с сервером баз данных, тянет за собой неизбежно длинный "хвост", надеюсь, что дальше будет лучше, но уже и сейчас неплохо.

Начнем собственно с OpenOffice Base. Мы уже отмечали раннее, что Форма открывается как документ Writer  и собственно им и является. Отсюда очевидное решение. Зачем "грузить" многофункциональное IDE Base, если можно сразу разрабатывать Форму в IDE Writer. Результат совершенно аналогичный.

Идем дальше. Программный код хранится во внешних библиотеках, которые представляют собой папку/ каталог на диске и макросах/скриптах, которые представляют собой файл .vba. Внешние скрипты отображаются в разделе  My Macros. Существуют еще и внутренние приватные скрипты, которые хранятся вместе с Формой во внутреннем формате документа Writer. Несмотря на то, что такие скрипты удобно распространять (вместе с файлом), их повторная применяемость исключена,  так как они существуют только внутри Формы/документа Writer.

Без внутренних скриптов не обойтись. Они загружаются вместе со своим документом, в то время как внешние библиотеки должны быть явно загружены таким вот оператором:

GlobalScope.BasicLibraries.LoadLibrary("LibName")

Теперь процедуры и функции библиотеки становятся доступны в текущем контексте. К ним можно обращаться как SomeFunction() или Libname.SomeFunction(). В последнем случае мы застрахованы от переопределения функций в разных библиотеках.

Все что сказано о макросах верно и для Диалогов (диалоговых окон). Каждый внешний Диалог хранится в библиотеках/папках/каталогах в отдельном файле. Внутренний - во внутреннем формате документа Writer. Диалог это достаточно мощный инструмент. Он может содержать стандартные элементы управления (кнопки, текстовые поля, радиоклавиши, списки, комбинированные поля), Но все эти элементы управления (в отличие от подобных в Формах) не могут иметь декларативной связи с базами данных и должны заполняться данными программно. Зато эти элементы легковесны и модальны. Загружать внешний Диалог нужно явно как показано в тексте функции

Funcion OpenDialog(sLibName, sDialogName)

  GlobalScope.DialogLibraries.LoadLibrary(sLibName)
  oLib = GlobalScope.DialogLibraries.GetByName(sLibName)
  oDialog = oLib.GetByName(sDialogName)
  oDlg = CreateUnoDialog(oDialog)
  oDlg.Execute()
  OpenDialog = oDlg

End Function

Это все, что сегодня я хотел сказать. Пока.

Подчиненные объекты form и списки в OpenOffice как реляцинные таблицы баз данных

Как мы уже уточнили в прошлом сообщении объект form может непосредственно входить в коллекцию forms,  а может быть подчиненным другому объекту form. Для этого открыв Навигатор форм надо выделить существующую form и правая кнопка мыши|new form.

Вспомним, что объект  form является объектом типа ResultSet. Поэтому с каждой form нужно связать существующую таблицу данных, запрос или SQL-запрос. В подчиненной form отражаются данные отфильтрованные по значению связанных полей в основной form по типу Master/Slave. Для этого в редакторе свойств подчиненной формы нужно заполнить свойства link master fields и link slave fields.

ОК. Теперь в каждую из form добавим по элементу Table Control, выбрав соответствующую form в Навигаторе форм. При этом запустится Мастер, с предложением выбрать таблицу данных или запрос, если Вы еще явно не указали его в свойствах объекта form. После этого Мастер предложить выбрать отображаемые поля. Впрочем, этот процесс можно прервать и заполнить данные в визуальном Конструкторе.

Второй тип связи, который мы рассмотрим - связь типа Справочник. Это распространенный случай. Мы храним в таблице данных ключ объекта (GUID, autoincrement и т.п.) а в отображаемой таблице выводим его полное наименование. Для этого есть удобный механизм. В визуальном Конструкторе таблицы выделяем колонку правая кнопка мыши|Replace With|List Box. Далее редактируем список свойств колонки. На закладке data свойству Тype of List Content присваиваем значение SQL. Запрос в свойстве List Content должен в первой колонке содержать отображаемое значение, а свойство Bound Field индекс колонки с ключевым полем (если считать, что первая колонка имеет индекс 0). То есть в подавляющем большинстве запросов это будет 1 для запросов типа

SELECT USERNAME, USERID FROM USERS ORDER BY USERNAME

Еще одна плюшка - накладывать фильтр на таблицу данных можно по значению введенному в текстовое поле. Для этого напишем на языке OO Basic процедуру обрабатывающую событие от клавиатуры.


Sub TextBox_onKeyUp(oEvent)
 
  oDocument = ThisComponent
  oDocument.LockControllers
  sText = oEvent.Source.text

  if Len(sText) > 3 Then
    oForm =  ThisComponent.Drawpage.Forms.getByName("MyForm")
    oForm.Filter = "USERNAME LIKE '" + sText  + "%'"
    oForm.reload()
  End If

  oDocument.UnlockControllers 

End Sub

Свяжем его с событием Key released текстового поля (на закладке event списка свойств). Заметим, что это поле должно быть расположено в другой form, чтобы перезагрузка form не "обнуляло" его текущее значение, введенное с клавиатуры. Пока.

Формы, Forms и Form в OpenOffice Base

С формами в OpenOffice Base произошла некоторая путаница. Я не ошибся в названии. Дело в том, что в процессе эволюции программного обеспечения оказалось, что Формы, Forms и Form - это немного не одно и то же. И Form - это не только Form. Поясню подробнее.

Когда Вы открываете базу данных OpenOffice Base в режиме редактирования, то видите в левой части экрана четыре закладки. В русифицированной версии это будет звучать как
  • Таблицы
  • Запросы
  • Формы
  • Отчеты
В этом смысле Форма является некоторым самостоятельным приложением, которое запускается в отдельном окне Windows. Замечу еще, что на самом деле это окно в котором запускается экземпляр OpenOffice Writer (ну и ну!)


Каждая Форма имеет коллекцию Forms объектов Form. Вначале эта коллекция пустая. Несмотря на это вы можете открывать и закрывать Форму. И даже выводить в ней текстовую информацию. И только при добавлении нового элемента управления (например кнопки Button) система создаст объект Form с именем Standard, если Вы до этого времени не создали такой объект сами.

Создавать новые формы удобнее всего из Навигатора форм, который вызывается из Главного меню View|Noolbar|Form Navigation. Объекты Form создаются или на верхнем уровне в коллекции Forms или подчиненные другому объекту Form. Это никак не отражается на внешнем виде Формы. Подчинение касается данных отображаемых в форме.

Тут надо отметить одну аномалию объект Form. Этот объект является в первую очередь объектом, связанным с таблицей базы данных или запросом SQL. Можно думать о Form как об объекте ResultSet (так оно и есть на самом деле). С другой стороны, Form является контейнером для элементов управления подобно FORM в документе HTML (и это тоже правда).

Теперь займемся программированием на OO Basic.

Откройте только что созданную Форму в режиме редактирования (правая кнопка мыши|Edit). Создайте в ней новый объект Form с именем Standatd или другим Вам понравившимся. Добавьте в этот объект элемент управления Button.

Для придания функциональности напишем макрос (скрипт) на языке OO Basic. Для этого не закрывая редактируемую Форму откроем из Главного меню Tools|Macros|Organize macros|OpenOffice.com Basic.

В раскрывшемся окне нужно выбрать место положения Вашего макроса/скрипта. В данном случае выберем расположение внутри текущей Формы (ее название можно увидеть в раскрывшемся окне).

Наконец, выбрав имя библиотеки и модуля (можно оставить по умолчанию) введем текст процедуры

Sub Button_Click(oEvent)

  oForm = ThisComponent.Drawpage.Forms.getByName("Standard")
  Xray oForm
  oForm1 = oEvent.Source.getModel().getParent()
  Xray oForm1
  
  If oForm.Name = oForm1.Name Then
    MsgBox "Is equel"
  End If

End Sub

В данной процедуре получен один и тот же объект Form двумя способами - из глобального контекста и из объекта события для кнопки Button. Код слишком очевиден, чтобы дополнительно разбирать его. Следует хорошо запомнить эти способы, они нам еще пригодятся.

Обратите внимание на вызов процедуры Xray. Для вызова этой процедуры требуется плагин/расширение, установку которого мы рассмотрели раннее. После успешной установки в окне редактора макросов/скриптов появится новая неактивная панель Xray Storm. Кликнув по панели мышью запустим процедуру окончательной установки (автозагрузки) плагина/расширения  систему. После чего строка

  Xray oForm

вызовет отладочное окно,  котором можно подробно рассматривать свойства методы и интерфейсы исследуемого объекта. Может случится, что Вам сразу не удалось установить Xray. Тогда нужно удалить/закомментировать вызовы этой процедуры и воспользоваться средствами встроенного отладчика (breakpoint, step, watch)

Теперь осталось связать процедуру с событием нажатия кнопки. Никакие соглашения об именованиях тут не действуют. В дизайнере Формы над кнопкой Button правая кнопка мыши|Control в появившемся окне выбираем закладку Events и событие When initiating, где даем привязку к процедуре Button_Click. В результате соформирутся ссылка похожая на

vnd.sun.star.script:Standard.Module1.Button_Click?language=Basic&location=document

Если скрипт не запускается, следует проверить не отключен ли он в результате слишком высокого уровня безопасности  
Главное меню|Tools||Options|OpenOffice.org|Security|Mcro Security

В данном сообщении объект Form был рассмотрен прежде всего как контейнер для элементов управления. Как объект данных ResultSet рассмотрим в следующем сообщении. Пока.



OpenOffice Base — подключение к серверу баз данных

В предыдущем сообщении описан процесс установки среды для работы с базами данных - сервер PostgreSQL и пакет OpenOffice версии 2.4.1 или выше. Кроме того описано как установить некоторые плагины/расширения для OpenOffice. Сейчас для нас важно иметь плагин/расширение postgresql-sdbc для прямого доступа к серверу PostgreSQL. Не все релизы плагинов/расширений совместимы с конкретным релизом OpenOffice. О чем могут выдаваться предупреждающе сообщения, а могут и не выдаваться.

Если плагин/расширение установлен правильно, он должен иметь статус Enabled в окне Tolls|Extension Manager, вызываемого из Главного меню OpenOffice. Если это так - подключимся к существующей базе данных PostgreSQL. (Создание баз данных в PostgreSQL выходит за рамки этой серии сообщений, поскольку уже хорошо описано в документации PostgreSQL).

Вызываем из Главного меню OpenOffice File|New|Database.
В появившемся диалоге выбираем радиокнопку Connect to an existing database.
В активном выпадающем списке выбираем postgresql. Если этот тип появился там. Если нет - пробуем загрузить другую версию плагина/расширения  postgresql-sdbc.

Далее вводим строку параметров подключения

host=localhost dbname=somedatabase user=someuser password=somepassword

Если Вы еще не корректировали Ваш файл data/pg_hba.conf, в котором заданы параметры подключения как

host all all 127.0.0.1/32 trust

ваша строка подключения будет еще проще 

host=localhost dbname=somedatabase

Напоминаю, что при такой конфигурации Вы можете подключаться к серверу только с локального компьютера, то есть на котором работает сервер.

Далее Вам предложат сохранить новую базу данных OpenOffice в файловой системе и зарегистрировать ее  в списке источников данных. Сохранив новую базу данных OpenOffice, откройте Tools|Options|OpenOffice.com Base|Databases, где вы можете просмотреть и изменить символическое имя базы данных и полный путь к файлу базы данных в файловой системе. Эти сведения мы будем использовать в своих программах для создания объектов из языка OO Basic. По умолчанию символическое имя равно имени файла базы данных OpenOffice, но это всегда можно изменить.

И в заключении о "родном" формате Баз Данных OpenOffice - HSQL. В отличие от MS Access - это чисто монопольная база данных. Поэтому не стоит углубляться в тонкости работы с этим форматом. Тем более, что все базы данных доступны в OpenOffice через интерфейс sdbc (аналог odbc и jdbc). Так что разница в работе будет минимальной. Практически ее не будет. Пока.

Начинаем работу с OpenOffice Base

Кому предназначен этот пост - прежде всего мне. Если Вы ищете средство для быстрой разработки интерфейса к серверу баз данных, например PostgreSQL - значит, возможно, и Вам.

Для 99% случаев, данные стоит хранить на SQL-сервере, не обязательно MS. Не ввязываясь в полемику какой сервер круче, замечу, что бесплатных и легко администрируемых серверов в настоящее время предостаточно. К известным всем решениям я могу добавить только свеженький Cubrid-сервер.

Сервер баз данных - это, конечно, хорошо. Но на чем писать клиентскую часть? Практически все средства быстрой разработки коммерческие. И даже если это стоит недорого - никто не застрахован от наличия в руководстве зациклившихся на своем Эго субъектов, от которых зависит решение. Мне приходилось слышать разные типы отказов от "то же самое можно сделать и в Excel" до "я Вам напишу это за день на Делфях и оба будем счастливы". И вот по крайней мере с версии 2.0 OpenOffice уже содержит хороший набор средств для быстрой разработки клиентской части баз данных. Эти средства пока мало востребованы. Основная причина, на мой взгляд - отсутствие документации. Прошло 5 лет, вышел очередной релиз - OpenOffice 3, наконец, фрагменты кода стали гулять в Интернете. Вышла подробная книга на английском языке и первая книга на русском о программировании на языке OO Basic для OpenOffice и OpenOffice Base.


Дальнейшая часть сообщения посвящена подготовке рабочей среды.

При выборе сервера баз данных, я остановился на PostgreSQL. Его очень легко установить и администрировать. Кроме того, есть хороший плагин для OpenOffice, позволяющий работать без технологий ODBC/JDBC напрямую с сервером. Итак, нам потребуется архив для установки без инсталляции с сайта www.postgresql.org. На сегодня это postgresql-9.1.3-2-windows-binaries.

Этот архив распаковываем  в любую папку/каталог. Далее открываем папку/каталог bin и выполняем команду

initdb.exe -D ../data --locale=Russian_Russia.1251 --encoding=utf-8

Теперь в папке/каталоге data (можно выбрать любое другое имя) будут находиться ваши/наши базы данных. Далее стартуем сервер.

pg_ctl.exe -D ../data start

Не забываем культурно останавливать сервер.

pg_ctl.exe -D ../data stop
 
Для создания баз данных, таблиц и других объектов удобнее всего воспользоваться программой pgAdmin3.exe, которая расположена в папке/каталоге bin. Программа имеет удобный графический интерфейс.

Для начала работы необходимо разобраться с файлом data/pg_hba.conf, в котором содержаться правила доступа к серверу баз данных с различных хостов. По умолчанию возможен только неограниченный доступ с локального компьютера, что определяется строкой в файле

host     all     all     127.0.0.1/32     trust

Впрочем, защита данных это особый разговор.

Теперь загружаем OpenOffice. Я выбрал portable версию 2.4.1. Она сравнительно легковесна и в ней работают все нужные плагины/расширения. Далее Вам потребуется Java. Я поставил себе jre1.4.2, как вполне достаточную и легковесную. Эту программу надо зарегистрировать в OpenOffice из главного меню Tools|Option|OpenOffice.org|Java. Теперь можно загружать плагины/расширения. Их нам потребуется два.

Первый - postgresql-sdbc для прямого доступа к серверу PostgreSQL. Второй- OceanBasicTool, который включает Xray - средство для просмотра свойств и методов объектов. При дефиците документации это средство дает ощутимую помощь.

Плагины/расширения инсталлируют из главного меню OpenOffice Tools|Extension Manager|Add...

Все готово для начала работы. Я не буду описывать как создавать базы данных и таблицы в PostgreSQL. Для этого хватает специализированных ресурсов. Пока.