2 Module for generating dynamic dialogs
3 i.e. dialogs that auto-adjust their layout based on contents and
6 Copyright 2020 by Massimo Del Fedele
10 from datetime
import date
14 from com.sun.star.style.VerticalAlignment
import MIDDLE
as VA_MIDDLE
16 from com.sun.star.awt
import Size
17 from com.sun.star.awt
import XActionListener, XTextListener
18 from com.sun.star.task
import XJobExecutor
20 from com.sun.star.awt
import XTopWindowListener
22 from com.sun.star.util
import MeasureUnit
24 from LeenoConfig
import Config
29 ''' get current script's path '''
30 return os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
35 Get Size of parent window in order to
36 be able to create a dialog on center of it
39 serviceManager = ctx.ServiceManager
40 toolkit = serviceManager.createInstanceWithContext(
41 "com.sun.star.awt.Toolkit", ctx)
43 oWindow = toolkit.getActiveTopWindow()
45 oDesktop = ctx.ServiceManager.createInstanceWithContext(
46 "com.sun.star.frame.Desktop", ctx)
47 oDoc = oDesktop.getCurrentComponent()
49 oView = oDoc.getCurrentController()
50 oWindow = oView.getFrame().getComponentWindow()
51 rect = oWindow.getPosSize()
52 return rect.Width, rect.Height
60 oToolkit = ctx.ServiceManager.createInstanceWithContext(
61 "com.sun.star.awt.Toolkit", ctx)
62 aWorkArea = oToolkit.WorkArea
63 nWidht = aWorkArea.Width
64 nHeight = aWorkArea.Height
66 return nWidht, nHeight
71 Dialog positions are scaled by weird factors
72 so we need to figure them out before proceeding
78 docframe = doc.getCurrentController().getFrame()
79 docwindow = docframe.getContainerWindow()
81 sc = docwindow.convertSizeToPixel(Size(1000, 1000), MeasureUnit.APPFONT)
83 return 1000.0 / float(sc.Width), 1000.0 / float(sc.Height)
87 gets the size of a given image
88 BEWARE : SIZE IN PIXEL !
91 serviceManager = ctx.ServiceManager
93 imageModel = serviceManager.createInstance(
94 "com.sun.star.awt.UnoControlImageControlModel")
95 image = serviceManager.createInstance(
96 "com.sun.star.awt.UnoControlImageControl")
97 image.setModel(imageModel)
98 imageModel.ImageURL = uno.systemPathToFileUrl(os.path.join(
getCurrentPath(), Image))
99 size = imageModel.Graphic.SizePixel
101 return size.Width, size.Height
105 Get 'best' size for a big dialog icon
106 (like the one of alert and ok dialogs)
109 siz = min(scWidth, scHeight)
117 Get the size needed to display a multiline text box
118 BEWARE : SIZE IN PIXEL !
121 serviceManager = ctx.ServiceManager
123 textModel = serviceManager.createInstance(
124 "com.sun.star.awt.UnoControlFixedTextModel")
125 text = serviceManager.createInstance(
126 "com.sun.star.awt.UnoControlFixedText")
127 text.setModel(textModel)
129 size = text.getMinimumSize()
131 return size.Width, size.Height
136 Get the size needed to display a multiline edit field
137 BEWARE : SIZE IN PIXEL !
140 serviceManager = ctx.ServiceManager
142 editModel = serviceManager.createInstance(
143 "com.sun.star.awt.UnoControlEditModel")
144 edit = serviceManager.createInstance(
145 "com.sun.star.awt.UnoControlEdit")
146 edit.setModel(editModel)
148 size = edit.getMinimumSize()
150 return size.Width, size.Height
155 Get the size needed to display a list box
156 BEWARE : SIZE IN PIXEL !
159 serviceManager = ctx.ServiceManager
161 textModel = serviceManager.createInstance(
162 "com.sun.star.awt.UnoControlFixedTextModel")
163 text = serviceManager.createInstance(
164 "com.sun.star.awt.UnoControlFixedText")
165 text.setModel(textModel)
170 size = text.getMinimumSize()
171 maxW = max(maxW, size.Width)
172 maxH = max(maxH, size.Height)
175 maxH = text.getMinimumSize().Height
182 Get the size needed to display a radio button
183 BEWARE : SIZE IN PIXEL !
186 serviceManager = ctx.ServiceManager
188 rbModel = serviceManager.createInstance(
189 "com.sun.star.awt.UnoControlRadioButtonModel")
190 rb = serviceManager.createInstance(
191 "com.sun.star.awt.UnoControlRadioButton")
194 size = rb.getMinimumSize()
196 return size.Width, size.Height
201 Get the height needed to display a radio button
202 BEWARE : SIZE IN PIXEL !
209 Get the size needed to display a checkbox
210 BEWARE : SIZE IN PIXEL !
213 serviceManager = ctx.ServiceManager
215 cbModel = serviceManager.createInstance(
216 "com.sun.star.awt.UnoControlCheckBoxModel")
217 cb = serviceManager.createInstance(
218 "com.sun.star.awt.UnoControlCheckBox")
221 size = cb.getMinimumSize()
223 return size.Width, size.Height
228 Get the height needed to display a checkbox
229 BEWARE : SIZE IN PIXEL !
236 Get 'best' button size in a dialog
242 height = max(height, 24)
243 return width + 15, height + 15
248 returns stored last used path, if any
249 otherwise returns calling document base path
251 oPath =
Config().read(
'Generale',
'ultimo_percorso')
254 oPath = oDoc.getURL()
255 if oPath
is not None and oPath !=
'':
256 oPath = uno.fileUrlToSystemPath(oPath)
259 return os.path.join(oPath,
'')
264 store the folder of given path item to config
265 if a file path is given, we strip the file part
267 oPath = os.path.dirname(oPath)
268 oPath = os.path.join(oPath,
'')
269 Config().write(
'Generale',
'ultimo_percorso', oPath)
274 short a path adding ... in front
275 baset on a maximum allowed field width
301 Base class for all dialog exceptions
306 class AbstractError(DialogException):
308 Try to instantiate base class DialogItem
312 self.
message =
"Can't instantiate abstract class"
316 ''' Error during layout calculation in dialogs '''
324 Dialog items alignment when size is bigger than mininum one
327 HORZ_ALIGN_CENTER = 2
331 VERT_ALIGN_CENTER = 16
332 VERT_ALIGN_BOTTOM = 32
338 GROUPBOX_TOP_BORDER = 25
339 GROUPBOX_BOTTOM_BORDER = 10
340 GROUPBOX_LEFT_BORDER = 10
341 GROUPBOX_RIGHT_BORDER = 10
344 class DialogItem(unohelper.Base, XTextListener, XActionListener):
346 Base class for every dialog item
350 MinWidth=None, MinHeight=None,
351 MaxWidth=None, MaxHeight=None,
352 FixedWidth=None, FixedHeight=None,
372 self.
align = HORZ_ALIGN_LEFT | VERT_ALIGN_TOP
393 to be redefined if widget needs to adapt to size changes
397 def _adjustSize(self):
398 ''' calculate min size and adjust considering minimum, maximum and fixed ones '''
401 self._width, self._height = self.calcMinSize()
406 if self._fixedWidth
is not None:
407 self._width = self._fixedWidth
409 if self._minWidth
is not None:
410 self._width = max(self._width, self._minWidth)
411 if self._maxWidth
is not None:
412 self._width = min(self._width, self._maxWidth)
414 if self._fixedHeight
is not None:
415 self._height = self._fixedHeight
417 if self._minHeight
is not None:
418 self._height = max(self._height, self._minHeight)
419 if self._maxHeight
is not None:
420 self._height = min(self._height, self._maxHeight)
422 return self._width, self._height
426 gets minimum control size (from content / type)
427 MUST be defined in derived classes
431 def _equalizeElements(self):
433 This one is meant for grouping controls
434 like Sizers or GroupBox
438 def _adjustLayout(self):
440 This one is meant for grouping controls
441 like Sizers or GroupBox
447 bring a string representation of object
449 res = 4 * indent *
' ' + type(self).__name__ +
': {'
450 res += f
'Id:{self._id}'
451 res += f
', X:{self._x}, Y:{self._y}'
452 res += f
', Width:{self._width}, Height:{self._height}'
453 if self._fixedWidth
is not None:
454 res += f
', fixedWidth:{self._fixedWidth}'
456 if self._minWidth
is not None:
457 res += f
', minWidth:{self._minWidth}'
458 if self._maxWidth
is not None:
459 res += f
', maxWidth:{self._maxWidth}'
461 if self._fixedHeight
is not None:
462 res += f
', fixedHeight:{self._fixedHeight}'
464 if self._minHeight
is not None:
465 res += f
', minHeight:{self._minHeight}'
466 if self._maxHeight
is not None:
467 res += f
', maxHeight:{self._maxHeight}'
473 convert object to string
479 Get control's properties (name+value)
481 MUST be redefined on each visible control
485 def _initControl(self, oControl):
487 do some special initialization
488 (needed, for example, for droplists...)
492 def _getModelName(self):
494 this MUST be redefined for classes that don't use
495 the standard model naming
497 clsName = type(self).__name__
498 return "com.sun.star.awt.UnoControl" + clsName +
"Model"
500 def _addUnoItems(self, owner):
502 Add uno item(s) to owning UNO dialog
504 dialogModel = owner._dialogModel
505 dialogContainer = owner._dialogContainer
508 modelName = self._getModelName()
509 oControlModel = dialogModel.createInstance(modelName)
516 uno.invoke(oControlModel,
"setPropertyValue", (
"PositionX", int(self._x * xScale)))
517 uno.invoke(oControlModel,
"setPropertyValue", (
"PositionY", int(self._y * yScale)))
518 uno.invoke(oControlModel,
"setPropertyValue", (
"Width", int(self._width * xScale)))
519 uno.invoke(oControlModel,
"setPropertyValue", (
"Height", int(self._height * yScale)))
522 props = self.getProps()
523 for key, val
in props.items():
525 uno.invoke(oControlModel,
"setPropertyValue", (key, val))
528 self._id = owner._getNextId()
529 oControlModel.Name = self._id
532 dialogModel.insertByName(self._id, oControlModel)
535 self._UNOControlModel = oControlModel
536 self._UNOControl = dialogContainer.getControl(self._id)
539 self._initControl(self._UNOControl)
547 self._UNOControl.addActionListener(self)
548 except AttributeError:
552 self._UNOControl.addTextListener(self)
553 except AttributeError:
558 removes all reference to owner and UNO widget
559 so we know that dialog is not in running state
561 self._UNOControlModel =
None
562 self._UNOControl =
None
568 an action on underlying widget happened
585 an action on underlying widget happened
590 def _presetWidgets(self):
592 NEEDED ONLY FOR LISTBOX/DROPDOWN
593 used to pre-set the dropdown content
594 must be done with dialog visible
601 shall do widget-dependent process for each control
602 and return a 'parameter' to send to dialog event handler
603 MUST BE REDEFINED FOR WIDGETS HANDLING EVENTS
616 class Spacer(DialogItem):
617 ''' A virtual widget used to leave space among other widgets'''
622 MinSize = MIN_SPACER_SIZE
623 super().
__init__(MinWidth=MinSize, MinHeight=MinSize)
627 Calculate widget's minimum size
631 def _addUnoItems(self, owner):
636 class Sizer(DialogItem):
638 Base class for horizontal and vertical sizers
639 Every dialog MUST contain exactly one sizer
654 bring a string representation of object
656 res = super().
dump(indent) +
'\n'
658 res += item.dump(indent + 1) +
'\n'
659 res += 4 * indent *
' ' +
'}'
669 def _addUnoItems(self, owner):
671 fill UNO dialog with items
674 item._addUnoItems(owner)
678 removes all reference to owner and UNO widget
679 so we know that dialog is not in running state
684 def _presetWidgets(self):
686 item._presetWidgets()
689 ''' get widget by ID'''
693 if hasattr(item,
'getWidget'):
694 widget = item.getWidget(wId)
695 if widget
is not None:
709 Used to arrange controls horizontally
713 super().
__init__(Id=Id, Items=Items)
717 Calculate widget's minimum size
723 itemMinWidth, itemMinHeight = item._adjustSize()
726 item._width, item._height = itemMinWidth, itemMinHeight
730 h = max(h, itemMinHeight)
733 def _equalizeElements(self):
735 equalize elements inside container
736 for hSizer, that means to put all of them at max height
740 maxH = max(maxH, item._height)
743 item._equalizeElements()
746 def _adjustLayout(self):
748 based on requested size and (previously calculated) minimum size
749 layout contained items
750 WARNING : we need a PREVIOUS call to calcMinSize and _equalizeElements ¯on widget three
752 xOrg, yOrg = self.
_x, self.
_y
755 curXOrg, curYOrg = xOrg, yOrg
771 if isinstance(item, Spacer):
778 dW += nSpacers * MIN_SPACER_SIZE
780 space = int(dW / nSpacers)
781 lastSpace = dW - space * (nSpacers - 1)
784 if isinstance(item, Spacer):
792 item._x, item._y = curXOrg, curYOrg
797 item._x, item._y = curXOrg, curYOrg
799 curXOrg += item._width
808 if item._fixedWidth
is None:
810 widths.append(item._width)
813 ratios.append(item / totw)
820 if item._fixedWidth
is None:
821 itemSpace = int(dW * ratios[iItem])
822 if iItem < len(ratios) - 1:
823 item._width += itemSpace
824 spaceRemainder -= itemSpace
826 item._width += spaceRemainder
830 item._x, item._y = curXOrg, curYOrg
832 curXOrg += item._width
837 item._x, item._y = curXOrg, curYOrg
839 curXOrg += item._width
851 Used to arrange controls vertically
855 super().
__init__(Id=Id, Items=Items)
859 Calculate widget's minimum size
865 itemMinWidth, itemMinHeight = item._adjustSize()
868 item._width, item._height = itemMinWidth, itemMinHeight
871 w = max(w, itemMinWidth)
875 def _equalizeElements(self):
877 equalize elements inside container
878 for hSizer, that means to put all of them at max height
882 maxW = max(maxW, item._width)
885 item._equalizeElements()
888 def _adjustLayout(self):
890 based on requested size and (previously calculated) minimum size
891 layout contained items
892 WARNING : we need a PREVIOUS call to calcMinSize and _equalizeElements ¯on widget three
894 xOrg, yOrg = self.
_x, self.
_y
897 curXOrg, curYOrg = xOrg, yOrg
913 if isinstance(item, Spacer):
920 dH += nSpacers * MIN_SPACER_SIZE
922 space = int(dH / nSpacers)
923 lastSpace = dH - space * (nSpacers - 1)
926 if isinstance(item, Spacer):
934 item._x, item._y = curXOrg, curYOrg
939 item._x, item._y = curXOrg, curYOrg
941 curYOrg += item._height
950 if item._fixedHeight
is None:
952 heights.append(item._height)
955 ratios.append(item / toth)
962 if item._fixedHeight
is None:
963 itemSpace = int(dH * ratios[iItem])
964 if iItem < len(ratios) - 1:
965 item._height += itemSpace
966 spaceRemainder -= itemSpace
968 item._height += spaceRemainder
972 item._x, item._y = curXOrg, curYOrg
974 curYOrg += item._height
979 item._x, item._y = curXOrg, curYOrg
981 curYOrg += item._height
992 Display a box of text
995 MinWidth=None, MinHeight=None,
996 MaxWidth=None, MaxHeight=None,
997 FixedWidth=None, FixedHeight=None,
999 TextColor=None, BackgroundColor=None,
1000 Border=0, BorderColor=None):
1003 MinWidth=MinWidth, MinHeight=MinHeight,
1004 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1005 FixedWidth=FixedWidth, FixedHeight=FixedHeight)
1015 Calculate widget's minimum size
1021 Get control's properties (name+value)
1023 MUST be redefined on each visible control
1026 'Label': self.
_text,
1028 'VerticalAlign': VA_MIDDLE,
1037 convert object to string
1039 txt = self.
_text.replace(
"\n",
"\\n")
1040 return super().
dump(indent) + f
", Text: '{txt}'" +
'}'
1072 MinWidth=None, MinHeight=None,
1073 MaxWidth=None, MaxHeight=None,
1074 FixedWidth=None, FixedHeight=None):
1077 MinWidth=MinWidth, MinHeight=MinHeight,
1078 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1079 FixedWidth=FixedWidth, FixedHeight=FixedHeight)
1086 Calculate widget's minimum size
1088 if self.
_text !=
'':
1095 Get control's properties (name+value)
1097 MUST be redefined on each visible control
1102 'VerticalAlign': VA_MIDDLE,
1112 convert object to string
1114 txt = self.
_text.replace(
"\n",
"\\n")
1115 return super().
dump(indent) + f
", Text: '{txt}'" +
'}'
1136 MinWidth=
None, MinHeight=
None,
1137 MaxWidth=
None, MaxHeight=
None,
1138 FixedWidth=
None, FixedHeight=
None):
1141 MinWidth=MinWidth, MinHeight=MinHeight,
1142 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1143 FixedWidth=FixedWidth, FixedHeight=FixedHeight)
1148 Calculate widget's minimum size
1154 Get control's properties (name+value)
1156 MUST be redefined on each visible control
1159 'Date': LeenoUtils.date2UnoDate(self.
_date),
1161 'VerticalAlign': VA_MIDDLE,
1166 convert object to string
1168 return super().
dump(indent) + f
", Date: '{self._date}'" +
'}'
1173 d = LeenoUtils.date2UnoDate(self.
_date)
1188 A text field with a button
1189 used to select a file
1192 MinWidth=None, MinHeight=None,
1193 MaxWidth=None, MaxHeight=None,
1194 FixedWidth=None, FixedHeight=None,
1195 InternalHandler=None):
1198 btnIcon =
'Icons-16x16/file.png'
1200 if Path
is None or Path ==
'':
1203 self.
add(
Edit(Text=
'', ReadOnly=
True))
1205 self.
add(
Button(Id=
'select', Icon=btnIcon, FixedWidth=btnWidth, FixedHeight=btnHeight, InternalHandler=self.
pathHandler))
1216 Get control's properties (name+value)
1218 MUST be redefined on each visible control
1220 return {
'Text': self.
_path}
1224 convert object to string
1227 return super().
dump(indent) + f
", Path: '{pth}'" +
'}'
1232 if file
is not None:
1258 A text field with a button
1259 used to select a path
1262 MinWidth=None, MinHeight=None,
1263 MaxWidth=None, MaxHeight=None,
1264 FixedWidth=None, FixedHeight=None,
1265 InternalHandler=None):
1268 btnIcon =
'Icons-16x16/folder.png'
1270 if Path
is None or Path ==
'':
1272 self.
add(
Edit(Text=
'', ReadOnly=
True))
1274 self.
add(
Button(Id=
'select', Icon=btnIcon, FixedWidth=btnWidth, FixedHeight=btnHeight, InternalHandler=self.
pathHandler))
1284 Get control's properties (name+value)
1286 MUST be redefined on each visible control
1288 return {
'Text': self.
_path}
1292 convert object to string
1295 return super().
dump(indent) + f
", Path: '{pth}'" +
'}'
1300 if folder
is not None:
1301 folder = os.path.join(folder,
'')
1324 A text field with a button
1325 used to select a date
1328 MinWidth=None, MinHeight=None,
1329 MaxWidth=None, MaxHeight=None,
1330 FixedWidth=None, FixedHeight=None,
1331 InternalHandler=None):
1334 btnIcon =
'Icons-24x24/calendar.png'
1336 dateWidth, dummy =
getTextBox(
'88 SETTEMBRE 9999XX')
1339 self.
add(
Edit(Text=
'', ReadOnly=
True, FixedWidth=dateWidth))
1350 Get control's properties (name+value)
1352 MUST be redefined on each visible control
1358 convert object to string
1361 return super().
dump(indent) + f
", Date: '{LeenoUtils.date2String(dat)}'" +
'}'
1366 if nDate
is not None:
1393 MinWidth=None, MinHeight=None,
1394 MaxWidth=None, MaxHeight=None,
1395 FixedWidth=None, FixedHeight=None,
1396 InternalHandler=None):
1404 if MinWidth
is not None:
1405 minH = int(MinWidth * ratio)
1406 if MinHeight
is None or MinHeight < minH:
1408 elif MinHeight
is not None:
1409 MinWidth = int(MinHeight / ratio)
1412 if MaxWidth
is not None:
1413 maxH = int(MaxWidth * ratio)
1414 if MaxHeight
is None or MaxHeight > maxH:
1416 elif MaxHeight
is not None:
1417 MaxWidth = int(MaxHeight / ratio)
1420 MinWidth=MinWidth, MinHeight=MinHeight,
1421 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1422 FixedWidth=FixedWidth, FixedHeight=FixedHeight,
1423 InternalHandler = InternalHandler)
1429 Calculate widget's minimum size
1435 Get control's properties (name+value)
1437 MUST be redefined on each visible control
1448 convert object to string
1450 return super().
dump(indent) + f
", Image: '{self._image}'" +
'}'
1456 Display a ProgressBar
1458 def __init__(self, *, Id=None, MinVal=0, MaxVal=100, Value=0,
1459 MinWidth=None, MinHeight=None,
1460 MaxWidth=None, MaxHeight=None,
1461 FixedWidth=None, FixedHeight=None,
1462 InternalHandler=None):
1465 MinWidth=MinWidth, MinHeight=MinHeight,
1466 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1467 FixedWidth=FixedWidth, FixedHeight=FixedHeight,
1468 InternalHandler = InternalHandler)
1475 Calculate widget's minimum size
1479 Width = int(2 * pW / 3)
1485 elif Width > ws / 4:
1492 Get control's properties (name+value)
1494 MUST be redefined on each visible control
1497 "ProgressValueMin": self.
_minVal,
1498 "ProgressValueMax": self.
_maxVal,
1499 "ProgressValue": self.
_value}
1504 convert object to string
1506 return super().
dump(indent) +
'}'
1530 Display a push button which reacts to presses
1531 If 'RetVal' is given, button closes the dialog and returns this value
1532 If 'RetVal' is None, button DOESN'T close the dialog, but external handler is called
1534 def __init__(self, *, Id=None, Label='', RetVal=None, Icon=None,
1535 MinWidth=None, MinHeight=None,
1536 MaxWidth=None, MaxHeight=None,
1537 FixedWidth=None, FixedHeight=None,
1539 TextColor=None, BackgroundColor=None,
1540 InternalHandler=None):
1545 MinWidth=MinWidth, MinHeight=MinHeight,
1546 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1547 FixedWidth=FixedWidth, FixedHeight=FixedHeight,
1548 InternalHandler = InternalHandler)
1557 Calculate widget's minimum size
1563 Get control's properties (name+value)
1565 MUST be redefined on each visible control
1567 res = {
'Label': self.
_label}
1568 if self.
_icon is not None:
1569 res[
'ImageAlign'] = 0
1583 convert object to string
1585 res = super().
dump(indent)
1586 res += f
", Label: '{self._label}'"
1587 res += f
", Icon: '{self._icon}'" +
'}'
1608 Display a check box (aka option)
1610 def __init__(self, *, Id=None, Label='aLabel', State=False,
1611 MinWidth=None, MinHeight=None,
1612 MaxWidth=None, MaxHeight=None,
1613 FixedWidth=None, FixedHeight=None,
1614 InternalHandler=None):
1617 MinWidth=MinWidth, MinHeight=MinHeight,
1618 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1619 FixedWidth=FixedWidth, FixedHeight=FixedHeight,
1620 InternalHandler = InternalHandler)
1626 Calculate widget's minimum size
1632 Get control's properties (name+value)
1634 MUST be redefined on each visible control
1638 'State': int(self.
_state),
1639 'VerticalAlign': VA_MIDDLE,
1656 convert object to string
1658 res = super().
dump(indent)
1659 res += f
", Label: '{self._label}'"
1660 res += f
", State: '{self._state}'"
1673 Display a list of strings
1675 def __init__(self, *, Id=None, List=None, Current=None,
1676 MinWidth=None, MinHeight=None,
1677 MaxWidth=None, MaxHeight=None,
1678 FixedWidth=None, FixedHeight=None,
1679 InternalHandler=None):
1682 MinWidth=MinWidth, MinHeight=MinHeight,
1683 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1684 FixedWidth=FixedWidth, FixedHeight=FixedHeight,
1685 InternalHandler = InternalHandler)
1691 Calculate widget's minimum size
1697 Get control's properties (name+value)
1699 MUST be redefined on each visible control
1706 def _initControl(self, oControl):
1708 do some special initialization
1709 (needed, for example, for droplists...)
1711 oControl.DropDownLineCount = 10
1712 oControl.MultipleMode =
False
1715 def _presetWidgets(self):
1717 NEEDED ONLY FOR LISTBOX/DROPDOWN
1718 used to pre-set the dropdown content
1719 must be done with dialog visible
1725 ''' an action on underlying widget happened '''
1739 convert object to string
1741 res = super().
dump(indent)
1742 res += f
", Items: '{self._list}'"
1743 res += f
", Current: '{self._current}'"
1749 if type(list) == set:
1750 self.
_list = tuple(list)
1759 itemIndex = list.index(self.
_current)
1767 for item
in self.
_list:
1786 Display a list of strings
1788 def __init__(self, *, Id=None, List=None, Current=None,
1789 MinWidth=None, MinHeight=None,
1790 MaxWidth=None, MaxHeight=None,
1791 FixedWidth=None, FixedHeight=None,
1792 InternalHandler=None):
1795 MinWidth=MinWidth, MinHeight=MinHeight,
1796 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1797 FixedWidth=FixedWidth, FixedHeight=FixedHeight,
1798 InternalHandler = InternalHandler)
1804 Calculate widget's minimum size
1810 Get control's properties (name+value)
1812 MUST be redefined on each visible control
1819 def _initControl(self, oControl):
1821 do some special initialization
1822 (needed, for example, for droplists...)
1824 oControl.DropDownLineCount = 10
1828 ''' an action on underlying widget happened '''
1842 convert object to string
1844 res = super().
dump(indent)
1845 res += f
", Items: '{self._list}'"
1846 res += f
", Current: '{self._current}'"
1852 if type(list) == set:
1853 self.
_list = tuple(list)
1861 for item
in self.
_list:
1879 Display a radio button connected with others
1882 MinWidth=None, MinHeight=None,
1883 MaxWidth=None, MaxHeight=None,
1884 FixedWidth=None, FixedHeight=None,
1885 InternalHandler=None):
1888 MinWidth=MinWidth, MinHeight=MinHeight,
1889 MaxWidth=MaxWidth, MaxHeight=MaxHeight,
1890 FixedWidth=FixedWidth, FixedHeight=FixedHeight,
1891 InternalHandler = InternalHandler)
1896 Calculate widget's minimum size
1902 Get control's properties (name+value)
1904 MUST be redefined on each visible control
1911 ''' an action on underlying widget happened '''
1914 dest = self.
_id.split(
'_')
1915 if len(dest) != 3
or dest[0] !=
'RadioGroup':
1918 radioItem = int(dest[2])
1919 group = self.
_owner.getWidget(groupId)
1920 return group.widgetEvent(radioItem)
1924 convert object to string
1926 res = super().
dump(indent)
1927 res += f
", Label: '{self._label}'"
1934 Groups a sequence of radio buttons
1935 It has no label not border, if those are required
1936 you shall insert it into a GroupBox item
1938 def __init__(self, *, Id=None, Horz=False, Items=None, Default=0):
1946 elif Default >= len(Items):
1947 Default = len(Items) - 1
1964 self.
_x, self.
_y = 0, 0
1969 Calculate widget's minimum size
1973 def _equalizeElements(self):
1975 Equalize all elements sizes in Sizer
1977 self.
_sizer._equalizeElements()
1979 def _adjustLayout(self):
1981 Adjust layout of contained Sizer
1985 self.
_sizer._adjustLayout()
1988 ''' add elements to group '''
1992 def _addUnoItems(self, owner):
1994 fill UNO dialog with items
2000 if self.
_id is None:
2001 self.
_id = owner._getNextId()
2006 for item
in self.
_sizer._items:
2007 item._id =
'RadioGroup_' + self.
_id +
'_' + str(curId)
2011 self.
_sizer._addUnoItems(owner)
2016 def _destruct(self):
2018 removes all reference to owner and UNO widget
2019 so we know that dialog is not in running state
2032 ''' gets widget by Id '''
2042 Get control's properties (name+value)
2044 MUST be redefined on each visible control
2049 ''' get number of radio buttons '''
2050 return len(self.
_sizer._items)
2054 gets the currently active radio button index
2061 sets the currently active radio button index
2062 if dialog is displaying, set also the widget on it
2065 if self.
_owner is not None:
2067 for item
in self.
_sizer._items:
2068 item._UNOControlModel.State = idx == current
2073 bring a string representation of object
2075 res = super().
dump(indent) +
'\n'
2076 for item
in self.
_sizer._items:
2077 res += item.dump(indent + 1) +
'\n'
2078 res += 4 * indent *
' ' +
'}'
2091 Display a box with optional border and label
2092 Used mostly to group items
2093 In behaviour is similar to Dialog
2095 def __init__(self, *, Id=None, Label='aLabel', Horz=False, Items=None):
2103 self.
_sizer._x, self.
_sizer._y = GROUPBOX_LEFT_BORDER, GROUPBOX_TOP_BORDER
2104 self.
_x, self.
_y = 0, 0
2109 Calculate widget's minimum size
2114 max(wl, ws + GROUPBOX_LEFT_BORDER + GROUPBOX_RIGHT_BORDER),
2115 max(hl, hs + GROUPBOX_TOP_BORDER + GROUPBOX_BOTTOM_BORDER))
2117 def _equalizeElements(self):
2119 Equalize all elements sizes in Sizer
2121 self.
_sizer._equalizeElements()
2123 def _adjustLayout(self):
2125 Adjust layout of contained Sizer
2127 self.
_sizer._x = self.
_x + GROUPBOX_LEFT_BORDER
2128 self.
_sizer._y = self.
_y + GROUPBOX_TOP_BORDER
2129 self.
_sizer._adjustLayout()
2132 ''' add elements to group '''
2134 self.
_sizer._items.append(item)
2136 def _addUnoItems(self, owner):
2138 fill UNO dialog with items
2140 super()._addUnoItems(owner)
2141 self.
_sizer._addUnoItems(owner)
2143 def _destruct(self):
2145 removes all reference to owner and UNO widget
2146 so we know that dialog is not in running state
2152 ''' gets widget by Id '''
2162 Get control's properties (name+value)
2164 MUST be redefined on each visible control
2172 Gets a string representing the action on the control
2173 This string will be sent to event handler along with control name
2174 If the value returned is None or an empty string, no action will be performed
2180 bring a string representation of object
2182 res = super().
dump(indent) +
'\n'
2183 for item
in self.
_sizer._items:
2184 res += item.dump(indent + 1) +
'\n'
2185 res += 4 * indent *
' ' +
'}'
2189 class Dialog(unohelper.Base, XJobExecutor, XTopWindowListener):
2193 def __init__(self, *, Title='', Horz=False, Handler=None, CanClose=True, Items=None):
2195 unohelper.Base.__init__(self)
2201 self.
_sizer._x, self.
_sizer._y = DIALOG_BORDERS, DIALOG_BORDERS
2202 self.
_x, self.
_y = 0, 0
2213 Optimize widget's placement inside dialog based on
2214 constraints, sizers and spacers
2218 self.
_sizer._adjustSize()
2221 self.
_sizer._equalizeElements()
2223 self.
_sizer._adjustLayout()
2228 ''' add elements to dialog '''
2230 self.
_sizer._items.append(item)
2234 convert object to string
2236 res = f
'Dialog: {{X:{self._x}, Y:{self._y}'
2237 res += f
' , Width:{self._width}, Height:{self._height}\n'
2244 convert object to string
2248 def _getNextId(self):
2249 ''' gets next free Id '''
2253 def _construct(self):
2255 build internal dialog's UNO structures
2265 self.
_x = int((pW - self.
_width) / 2)
2276 "com.sun.star.awt.UnoControlDialogModel")
2304 self.
_sizer._addUnoItems(self)
2309 def _destruct(self):
2311 Resets internal pointers to UNO objects
2312 so we'll be able to know if dialog is running or not
2320 def _presetWidgets(self):
2321 self.
_sizer._presetWidgets()
2325 We didn't find a way to stop closing here, so we use the
2326 Closeable property of DialogModel to disallow closing
2327 Here we just catch the closing op to set return value
2328 Even if Closeable is set to False, this hook gets called
2337 internal event handler
2338 will call a provided external event handler
2344 if self.
_handler(self, widgetId, widget, param):
2350 if hasattr(widget,
'_retVal')
and widget._retVal
is not None:
2351 self.
stop(widget._retVal)
2355 Runs the dialog and wait for completion
2383 Shall be called from inside an handler
2385 if RetVal
is not None:
2392 Shows the dialog without waiting for completion
2410 ''' get widget by ID'''
2423 ''' use a dictionary to fillup dialog data '''
2424 for key, val
in data.items():
2426 if widget
is not None:
2429 print(f
"Widget with Id='{key}' not found")
2433 retrieve all data fields with names in 'fields'
2439 val = widget.getData()
2448 def FileSelect(titolo='Scegli il file...', est='*.*', mode=0, startPath=None):
2450 titolo { string } : titolo del FilePicker
2451 est { string } : filtro di visualizzazione file
2452 mode { integer } : modalità di gestione del file
2454 Apri file: `mode in(0, 6, 7, 8, 9)`
2455 Salva file: `mode in(1, 2, 3, 4, 5, 10)`
2456 see:('''http://api.libreoffice.org/docs/idl/ref/
2457 namespacecom_1_1sun_1_1star_1_1ui_1_1
2458 dialogs_1_1TemplateDescription.html''' )
2459 see:('''http://stackoverflow.com/questions/30840736/
2460 libreoffice-how-to-create-a-file-dialog-via-python-macro''')
2462 estensioni = {
'*.*':
'Tutti i file(*.*)',
2463 '*.odt':
'Writer(*.odt)',
2464 '*.ods':
'Calc(*.ods)',
2465 '*.odb':
'Base(*.odb)',
2466 '*.odg':
'Draw(*.odg)',
2467 '*.odp':
'Impress(*.odp)',
2468 '*.odf':
'Math(*.odf)',
2469 '*.xpwe':
'Primus(*.xpwe)',
2470 '*.xml':
'XML(*.xml)',
2471 '*.dat':
'dat(*.dat)', }
2473 oFilePicker.initialize((mode, ))
2477 if startPath
is None:
2481 oPath = os.path.join(oPath,
'')
2482 oPath = uno.systemPathToFileUrl(oPath)
2483 oFilePicker.setDisplayDirectory(oPath)
2485 oFilePicker.Title = titolo
2486 app = estensioni.get(est)
2487 oFilePicker.appendFilter(app, est)
2488 if oFilePicker.execute():
2489 oPath = uno.fileUrlToSystemPath(oFilePicker.getFiles()[0])
2497 titolo { string } : titolo del FolderPicker
2503 if startPath
is None:
2507 oPath = os.path.join(oPath,
'')
2508 oPath = uno.systemPathToFileUrl(oPath)
2509 oFolderPicker.setDisplayDirectory(oPath)
2511 oFolderPicker.Title = titolo
2512 if oFolderPicker.execute():
2513 oPath = uno.fileUrlToSystemPath(oFolderPicker.getDirectory())
2514 oPath = os.path.join(oPath,
'')
2521 dlg =
Dialog(Title=Title, Horz=
False, CanClose=
True, Items=[
2530 Button(Label=
'Ok', Icon=
'Icons-24x24/ok.png', MinWidth=MINBTNWIDTH, RetVal=1),
2537 return NotifyDialog(Image=
'Icons-Big/exclamation.png', Title=Title, Text=Text)
2540 return NotifyDialog(Image=
'Icons-Big/info.png', Title=Title, Text=Text)
2542 def Ok(*, Title='', Text=''):
2543 return NotifyDialog(Image=
'Icons-Big/ok.png', Title=Title, Text=Text)
2546 dlg =
Dialog(Title=Title, Horz=
False, CanClose=
False, Items=[
2555 Button(Label=
'Si', Icon=
'Icons-24x24/ok.png', MinWidth=MINBTNWIDTH, RetVal=1),
2557 Button(Label=
'No', MinWidth=MINBTNWIDTH, RetVal=0),
2564 dlg =
Dialog(Title=Title, Horz=
False, CanClose=
True, Items=[
2573 Button(Label=
'Si', Icon=
'Icons-24x24/ok.png', MinWidth=MINBTNWIDTH, RetVal=1),
2575 Button(Label=
'No', MinWidth=MINBTNWIDTH, RetVal=0),
2577 Button(Label=
'Annulla', Icon=
'Icons-24x24/cancel.png', MinWidth=MINBTNWIDTH, RetVal=-1),
2585 Display a progress bar with some options
2587 def __init__(self, *, Title='', Closeable=False, MinVal=0, MaxVal=100, Value=0, Text=''):
2590 Title=Title, Horz=
False, CanClose=Closeable,
2591 Handler=
lambda dialog, widgetId, widget, cmdStr :
2592 self.
_dlgHandler(dialog, widgetId, widget, cmdStr)
2597 if Text
is not None:
2606 Button(Label=
'Annulla', MinWidth=MINBTNWIDTH, RetVal=-1),
2611 def _dlgHandler(self, dialog, widgetId, widget, cmdStr):
2633 percent =
'{:.0f}%'.format(100 * (val - minVal) / (maxVal - minVal))
2634 txt = self.
_text +
' (' + percent +
')'
2645 percent =
'{:.0f}%'.format(100 * (val - minVal) / (maxVal - minVal))
2646 txt = self.
_text +
' (' + percent +
')'
2653 if Icon
is not None:
2659 for label, value
in Buttons.items():
2660 bottom.add(
Button(Label=label, MinWidth=MINBTNWIDTH, RetVal=value))
2661 if idx < len(Buttons) - 1:
2664 dlg =
Dialog(Title=Title, Items=[top,
Spacer(), bottom])
2668 def YesNo(*, Title='', Text='', CanClose=True):
2671 by default (CanClose=True) dialog may be dismissed
2672 closing it on topbar or pressing escape, with result 'No'
2675 Icon=
"Icons-Big/question.png",
2676 Title=Title, Text=Text, CanClose=CanClose,
2677 Buttons={
'Si':
'si',
'No':
'no'}
2685 Yes/No/Cancel dialog
2686 by default (CanClose=True) dialog may be dismissed
2687 closing it on topbar or pressing escape, with result 'No'
2690 Icon=
"Icons-Big/question.png",
2691 Title=Title, Text=Text, CanClose=
True,
2692 Buttons={
'Si':
'si',
'No':
'no',
'Annulla':-1}
2701 Allow to pick a date from a calendar
2704 curDate = date.today()
2707 return 256*256*r + 256*g + b
2711 dateWidth, dummy =
getTextBox(
'88 SETTEMBRE 8888XX')
2713 workdaysBkColor = rgb(38, 153, 153)
2714 workdaysFgColor = rgb(255, 255, 255)
2715 holydaysBkColor = rgb(27, 248, 250)
2716 holydaysFgColor = rgb(0, 0, 0)
2720 Text=LeenoUtils.DAYNAMES[0], Align=1,
2721 BackgroundColor=rgb(38, 153, 153),
2722 TextColor=rgb(255, 255, 255),
2723 FixedWidth=btnWidth, FixedHeight=btnHeight
2725 for day
in LeenoUtils.DAYNAMES[1:]:
2726 dayNamesLabels.append(
Spacer())
2727 dayNamesLabels.append(
2729 BackgroundColor=workdaysBkColor
if day
not in (
'Sab',
'Dom')
else holydaysBkColor,
2730 TextColor=workdaysFgColor
if day
not in (
'Sab',
'Dom')
else holydaysFgColor,
2731 FixedWidth=btnWidth, FixedHeight=btnHeight
2738 for week
in range(0, 5):
2740 id = str(week) +
'.0'
2742 Id=id, Label=str(monthDay),
2743 BackgroundColor=workdaysBkColor,
2744 TextColor=workdaysFgColor,
2745 FixedWidth=btnWidth, FixedHeight=btnHeight
2748 for day
in range(1, 7):
2750 id = str(week) +
'.' + str(day)
2752 Id=id, Label=str(monthDay),
2753 BackgroundColor=workdaysBkColor
if day
not in (5, 6)
else holydaysBkColor,
2754 TextColor=workdaysFgColor
if day
not in (5, 6)
else holydaysFgColor,
2755 FixedWidth=btnWidth, FixedHeight=btnHeight
2758 weeks.append(
HSizer(Items=items))
2762 def loadDate(dlg, dat):
2765 for week
in range(0, 5):
2766 for wDay
in range(0, 7):
2767 id = str(week) +
'.' + str(wDay)
2768 if day <= 0
or day > days:
2769 dlg[id].setLabel(
'')
2771 dlg[id].setLabel(str(day))
2776 def handler(dlg, widgetId, widget, cmdStr):
2778 if widgetId ==
'prevYear':
2779 curDate = date(year=curDate.year-1, month=curDate.month, day=curDate.day)
2780 elif widgetId ==
'prevMonth':
2781 month = curDate.month - 1
2784 year = curDate.year - 1
2787 curDate = date(year=year, month=month, day=curDate.day)
2788 elif widgetId ==
'nextMonth':
2789 month = curDate.month + 1
2792 year = curDate.year + 1
2795 curDate = date(year=year, month=month, day=curDate.day)
2796 elif widgetId ==
'nextYear':
2797 curDate = date(year=curDate.year+1, month=curDate.month, day=curDate.day)
2798 elif widgetId ==
'today':
2799 curDate = date.today()
2800 elif '.' in widgetId:
2801 txt = widget.getLabel()
2805 curDate = date(year=curDate.year, month=curDate.month, day=day)
2808 loadDate(dlg, curDate)
2810 dlg =
Dialog(Title=
'Selezionare la data', Horz=
False, CanClose=
True, Handler=handler, Items=[
2812 Button(Id=
'prevYear', Icon=
'Icons-24x24/leftdbl.png'),
2814 Button(Id=
'prevMonth', Icon=
'Icons-24x24/leftsng.png'),
2816 FixedText(Id=
'date', Text=
'99 Settembre 9999', Align=1, FixedWidth=dateWidth),
2818 Button(Id=
'nextMonth', Icon=
'Icons-24x24/rightsng.png'),
2820 Button(Id=
'nextYear', Icon=
'Icons-24x24/rightdbl.png'),
2823 HSizer(Items=dayNamesLabels),
2825 ] + mkDayLabels() + [
2829 Button(Label=
'Ok', MinWidth=MINBTNWIDTH, Icon=
'Icons-24x24/ok.png', RetVal=1),
2831 Button(Id=
'today', Label=
'Oggi', MinWidth=MINBTNWIDTH),
2833 Button(Label=
'Annulla', MinWidth=MINBTNWIDTH, Icon=
'Icons-24x24/cancel.png', RetVal=-1),
2838 loadDate(dlg, curDate)