Copy from and to

This commit is contained in:
Mauricio Baeza 2019-10-10 00:07:45 -05:00
parent 3813ba9d87
commit aa905e19f3
1 changed files with 516 additions and 40 deletions

View File

@ -60,11 +60,14 @@ import mailbox
import uno import uno
import unohelper import unohelper
from com.sun.star.util import Time, Date, DateTime from com.sun.star.util import Time, Date, DateTime
from com.sun.star.beans import PropertyValue from com.sun.star.beans import PropertyValue, NamedValue
from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
from com.sun.star.awt.MessageBoxResults import YES from com.sun.star.awt.MessageBoxResults import YES
from com.sun.star.awt.PosSize import POSSIZE, SIZE from com.sun.star.awt.PosSize import POSSIZE, SIZE
from com.sun.star.awt import Size, Point from com.sun.star.awt import Size, Point
from com.sun.star.awt import Rectangle
from com.sun.star.awt import KeyEvent
from com.sun.star.awt.KeyFunction import QUIT
from com.sun.star.datatransfer import XTransferable, DataFlavor from com.sun.star.datatransfer import XTransferable, DataFlavor
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
@ -73,7 +76,12 @@ from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
from com.sun.star.lang import XEventListener from com.sun.star.lang import XEventListener
from com.sun.star.awt import XActionListener from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMouseListener from com.sun.star.awt import XMouseListener
from com.sun.star.awt import XMouseMotionListener
from com.sun.star.util import XModifyListener from com.sun.star.util import XModifyListener
from com.sun.star.awt import XTopWindowListener
from com.sun.star.awt import XWindowListener
from com.sun.star.awt import XMenuListener
from com.sun.star.awt import XKeyListener
try: try:
from fernet import Fernet, InvalidToken from fernet import Fernet, InvalidToken
@ -696,6 +704,12 @@ class LOCalc(LODocument):
index = [s.Name for s in self._sheets if s.CodeName == index][0] or index index = [s.Name for s in self._sheets if s.CodeName == index][0] or index
return LOCalcSheet(self._sheets[index], self) return LOCalcSheet(self._sheets[index], self)
def __setitem__(self, key, value):
self._sheets[key] = value
def __contains__(self, item):
return item in self.obj.Sheets
@property @property
def active(self): def active(self):
return LOCalcSheet(self._cc.getActiveSheet(), self) return LOCalcSheet(self._cc.getActiveSheet(), self)
@ -729,7 +743,12 @@ class LOCalc(LODocument):
obj = self.obj.getStyleFamilies()['CellStyles'] obj = self.obj.getStyleFamilies()['CellStyles']
return LOCellStyles(obj) return LOCellStyles(obj)
def insert(self, name, pos): def create(self):
return self.obj.createInstance('com.sun.star.sheet.Spreadsheet')
def insert(self, name, pos=-1):
# ~ sheet = obj.createInstance('com.sun.star.sheet.Spreadsheet')
# ~ obj.Sheets['New'] = sheet
index = pos index = pos
if pos < 0: if pos < 0:
index = self._sheets.Count + pos + 1 index = self._sheets.Count + pos + 1
@ -741,6 +760,12 @@ class LOCalc(LODocument):
name = n name = n
return LOCalcSheet(self._sheets[name], self) return LOCalcSheet(self._sheets[name], self)
def move(self, name, pos=-1):
return self.sheets.move(name, pos)
def remove(self, name):
return self.sheets.remove(name)
def copy(self, source='', target='', pos=-1): def copy(self, source='', target='', pos=-1):
index = pos index = pos
if pos < 0: if pos < 0:
@ -763,7 +788,7 @@ class LOCalc(LODocument):
return LOCalcSheet(self._sheets[index], self) return LOCalcSheet(self._sheets[index], self)
def copy_from(self, doc, source, target, pos): def copy_from(self, doc, source='', target='', pos=-1):
index = pos index = pos
if pos < 0: if pos < 0:
index = self._sheets.Count + pos + 1 index = self._sheets.Count + pos + 1
@ -876,6 +901,12 @@ class LOCalcSheet(object):
def __getitem__(self, index): def __getitem__(self, index):
return LOCellRange(self.obj[index], self.doc) return LOCellRange(self.obj[index], self.doc)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def _init_values(self): def _init_values(self):
self._events = None self._events = None
@ -905,6 +936,31 @@ class LOCalcSheet(object):
self.doc.activate(self.obj) self.doc.activate(self.obj)
return return
@property
def visible(self):
return self.obj.IsVisible
@visible.setter
def visible(self, value):
self.obj.IsVisible = value
@property
def password(self):
return ''
@visible.setter
def password(self, value):
self.obj.protect(value)
def unprotect(self, value):
try:
self.obj.unprotect(value)
return True
except:
pass
return False
def get_cursor(self, cell):
return self.obj.createCursorByRange(cell)
@property @property
def events(self): def events(self):
return self._events return self._events
@ -1171,7 +1227,7 @@ class LOCellRange(object):
def __enter__(self): def __enter__(self):
return self return self
def __exit__(self, *args): def __exit__(self, exc_type, exc_value, traceback):
pass pass
def __getitem__(self, index): def __getitem__(self, index):
@ -1240,19 +1296,72 @@ class LOCellRange(object):
values = tuple(values) values = tuple(values)
self.obj.setDataArray(values) self.obj.setDataArray(values)
@property
def formula(self):
return self.obj.getFormulaArray()
@formula.setter
def formula(self, values):
if isinstance(values, list):
values = tuple(values)
self.obj.setFormulaArray(values)
@property
def columns(self):
return self._obj.Columns.Count
@property
def rows(self):
return self._obj.Rows.Count
def to_size(self, rows, cols):
cursor = self.sheet.get_cursor(self.obj[0,0])
cursor.collapseToSize(cols, rows)
return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc)
def copy_from(self, rango):
data = rango
if isinstance(rango, LOCellRange):
data = rango.data
rows = len(data)
cols = len(data[0])
self.to_size(rows, cols).data = data
return
def copy_to(self, cell, formula=False):
rango = cell.to_size(self.rows, self.columns)
if formula:
rango.formula = self.data
else:
rango.data = self.data
return
def offset(self, row=1, col=0): def offset(self, row=1, col=0):
ra = self.obj.getRangeAddress() ra = self.obj.getRangeAddress()
col = ra.EndColumn + col col = ra.EndColumn + col
row = ra.EndRow + row row = ra.EndRow + row
return LOCellRange(self.sheet[row, col], self.doc) return LOCellRange(self.sheet[row, col].obj, self.doc)
@property
def next_cell(self):
a = self.current_region.address
if hasattr(a, 'StartColumn'):
col = a.StartColumn
else:
col = a.Column
if hasattr(a, 'EndRow'):
row = a.EndRow + 1
else:
row = a.Row + 1
return LOCellRange(self.sheet[row, col].obj, self.doc)
@property @property
def sheet(self): def sheet(self):
return self.obj.Spreadsheet return LOCalcSheet(self.obj.Spreadsheet, self.doc)
@property @property
def draw_page(self): def draw_page(self):
return self.sheet.getDrawPage() return self.sheet.obj.getDrawPage()
@property @property
def name(self): def name(self):
@ -1270,9 +1379,23 @@ class LOCellRange(object):
@property @property
def current_region(self): def current_region(self):
cursor = self.sheet.createCursorByRange(self.obj[0,0]) cursor = self.sheet.get_cursor(self.obj[0,0])
cursor.collapseToCurrentRegion() cursor.collapseToCurrentRegion()
return LOCellRange(self.sheet[cursor.AbsoluteName], self.doc) return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc)
@property
def visible(self):
cursor = self.sheet.get_cursor(self.obj)
rangos = [LOCellRange(self.sheet[r.AbsoluteName].obj, self.doc)
for r in cursor.queryVisibleCells()]
return tuple(rangos)
@property
def empty(self):
cursor = self.sheet.get_cursor(self.obj)
rangos = [LOCellRange(self.sheet[r.AbsoluteName].obj, self.doc)
for r in cursor.queryEmptyCells()]
return tuple(rangos)
@property @property
def cell_style(self): def cell_style(self):
@ -1308,7 +1431,7 @@ class LOCellRange(object):
address = rango.address address = rango.address
else: else:
address = rango.getRangeAddress() address = rango.getRangeAddress()
cursor = self.sheet.createCursorByRange(self.obj) cursor = self.sheet.get_cursor(self.obj)
result = cursor.queryIntersection(address) result = cursor.queryIntersection(address)
return bool(result.Count) return bool(result.Count)
@ -1316,11 +1439,16 @@ class LOCellRange(object):
self.obj.fillAuto(0, source) self.obj.fillAuto(0, source)
return return
def clear(self, what=31):
self.obj.clearContents(what)
return
class EventsListenerBase(unohelper.Base, XEventListener): class EventsListenerBase(unohelper.Base, XEventListener):
def __init__(self, controller, window=None): def __init__(self, controller, name, window=None):
self._controller = controller self._controller = controller
self._name = name
self._window = window self._window = window
def disposing(self, event): def disposing(self, event):
@ -1331,25 +1459,23 @@ class EventsListenerBase(unohelper.Base, XEventListener):
class EventsButton(EventsListenerBase, XActionListener): class EventsButton(EventsListenerBase, XActionListener):
def __init__(self, controller): def __init__(self, controller, name):
super().__init__(controller) super().__init__(controller, name)
def actionPerformed(self, event): def actionPerformed(self, event):
name = event.Source.Model.Name event_name = '{}_action'.format(self._name)
event_name = '{}_action'.format(name)
if hasattr(self._controller, event_name): if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event) getattr(self._controller, event_name)(event)
return return
class EventsMouse(EventsListenerBase, XMouseListener): class EventsMouse(EventsListenerBase, XMouseListener, XMouseMotionListener):
def __init__(self, controller): def __init__(self, controller, name):
super().__init__(controller) super().__init__(controller, name)
def mousePressed(self, event): def mousePressed(self, event):
name = event.Source.Model.Name event_name = '{}_click'.format(self._name)
event_name = '{}_click'.format(name)
if event.ClickCount == 2: if event.ClickCount == 2:
event_name = '{}_double_click'.format(name) event_name = '{}_double_click'.format(name)
if hasattr(self._controller, event_name): if hasattr(self._controller, event_name):
@ -1365,6 +1491,13 @@ class EventsMouse(EventsListenerBase, XMouseListener):
def mouseExited(self, event): def mouseExited(self, event):
pass pass
# ~ XMouseMotionListener
def mouseMoved(self, event):
pass
def mouseDragged(self, event):
pass
class EventsMouseGrid(EventsMouse): class EventsMouseGrid(EventsMouse):
selected = False selected = False
@ -1406,6 +1539,125 @@ class EventsModify(EventsListenerBase, XModifyListener):
return return
class EventsKey(EventsListenerBase, XKeyListener):
"""
event.KeyChar
event.KeyCode
event.KeyFunc
event.Modifiers
"""
def __init__(self, cls):
super().__init__(cls.events, cls.name)
self._cls = cls
def keyPressed(self, event):
pass
def keyReleased(self, event):
event_name = '{}_key_released'.format(self._cls.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
else:
if event.KeyFunc == QUIT and hasattr(self._cls, 'close'):
self._cls.close()
return
class EventsWindow(EventsListenerBase, XTopWindowListener, XWindowListener):
def __init__(self, cls):
self._cls = cls
super().__init__(cls.events, cls.name, cls._window)
def windowOpened(self, event):
event_name = '{}_opened'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def windowActivated(self, event):
control_name = '{}_activated'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
def windowDeactivated(self, event):
control_name = '{}_deactivated'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
def windowMinimized(self, event):
pass
def windowNormalized(self, event):
pass
def windowClosing(self, event):
if self._window:
control_name = 'window_closing'
else:
control_name = '{}_closing'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
# ~ else:
# ~ if not self._modal and not self._block:
# ~ event.Source.Visible = False
return
def windowClosed(self, event):
control_name = '{}_closed'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
# ~ XWindowListener
def windowResized(self, event):
# ~ sb = self._container.getControl('subcontainer')
# ~ sb.setPosSize(0, 0, event.Width, event.Height, SIZE)
event_name = '{}_resized'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def windowMoved(self, event):
pass
def windowShown(self, event):
pass
def windowHidden(self, event):
pass
class EventsMenu(EventsListenerBase, XMenuListener):
def __init__(self, controller):
super().__init__(controller, '')
def itemHighlighted(self, event):
pass
@catch_exception
def itemSelected(self, event):
name = event.Source.getCommand(event.MenuId)
if name.startswith('menu'):
event_name = '{}_selected'.format(name)
else:
event_name = 'menu_{}_selected'.format(name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def itemActivated(self, event):
return
def itemDeactivated(self, event):
return
class UnoBaseObject(object): class UnoBaseObject(object):
def __init__(self, obj): def __init__(self, obj):
@ -1429,19 +1681,39 @@ class UnoBaseObject(object):
def parent(self): def parent(self):
return self.obj.getContext() return self.obj.getContext()
def _get_possize(self, name):
ps = self.obj.getPosSize()
return getattr(ps, name)
def _set_possize(self, name, value):
ps = self.obj.getPosSize()
setattr(ps, name, value)
self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
return
@property @property
def x(self): def x(self):
return self.model.PositionX if hasattr(self.model, 'PositionX'):
return self.model.PositionX
return self._get_possize('X')
@x.setter @x.setter
def x(self, value): def x(self, value):
self.model.PositionX = value if hasattr(self.model, 'PositionX'):
self.model.PositionX = value
else:
self._set_possize('X', value)
@property @property
def y(self): def y(self):
return self.model.PositionY if hasattr(self.model, 'PositionY'):
return self.model.PositionY
return self._get_possize('Y')
@y.setter @y.setter
def y(self, value): def y(self, value):
self.model.PositionY = value if hasattr(self.model, 'PositionY'):
self.model.PositionY = value
else:
self._set_possize('Y', value)
@property @property
def width(self): def width(self):
@ -1452,7 +1724,10 @@ class UnoBaseObject(object):
@property @property
def height(self): def height(self):
return self._model.Height if hasattr(self._model, 'Height'):
return self._model.Height
ps = self.obj.getPosSize()
return ps.Height
@height.setter @height.setter
def height(self, value): def height(self, value):
self._model.Height = value self._model.Height = value
@ -1699,9 +1974,45 @@ class UnoGrid(UnoBaseObject):
return return
def get_custom_class(tipo, obj):
classes = {
'label': UnoLabel,
'button': UnoButton,
'text': UnoText,
'listbox': UnoListBox,
'grid': UnoGrid,
# ~ 'link': UnoLink,
# ~ 'tab': UnoTab,
# ~ 'roadmap': UnoRoadmap,
# ~ 'image': UnoImage,
# ~ 'radio': UnoRadio,
# ~ 'groupbox': UnoGroupBox,
# ~ 'tree': UnoTree,
}
return classes[tipo](obj)
def add_listeners(events, control, name=''):
listeners = {
'addActionListener': EventsButton,
'addMouseListener': EventsMouse,
}
if hasattr(control, 'obj'):
control = contro.obj
is_grid = control.ImplementationName == 'stardiv.Toolkit.GridControl'
for key, value in listeners.items():
if hasattr(control, key):
if is_grid and key == 'addMouseListener':
control.addMouseListener(EventsMouseGrid(events))
continue
getattr(control, key)(listeners[key](events, name))
return
class LODialog(object): class LODialog(object):
def __init__(self, properties): def __init__(self, **properties):
self._obj = self._create(properties) self._obj = self._create(properties)
self._init_values() self._init_values()
@ -1799,13 +2110,13 @@ class LODialog(object):
} }
return services[control] return services[control]
def _get_custom_class(self, tipo, obj): # ~ def _get_custom_class(self, tipo, obj):
classes = { # ~ classes = {
'label': UnoLabel, # ~ 'label': UnoLabel,
'button': UnoButton, # ~ 'button': UnoButton,
'text': UnoText, # ~ 'text': UnoText,
'listbox': UnoListBox, # ~ 'listbox': UnoListBox,
'grid': UnoGrid, # ~ 'grid': UnoGrid,
# ~ 'link': UnoLink, # ~ 'link': UnoLink,
# ~ 'tab': UnoTab, # ~ 'tab': UnoTab,
# ~ 'roadmap': UnoRoadmap, # ~ 'roadmap': UnoRoadmap,
@ -1813,8 +2124,8 @@ class LODialog(object):
# ~ 'radio': UnoRadio, # ~ 'radio': UnoRadio,
# ~ 'groupbox': UnoGroupBox, # ~ 'groupbox': UnoGroupBox,
# ~ 'tree': UnoTree, # ~ 'tree': UnoTree,
} # ~ }
return classes[tipo](obj) # ~ return classes[tipo](obj)
def _set_column_model(self, columns): def _set_column_model(self, columns):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html #~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html
@ -1846,12 +2157,174 @@ class LODialog(object):
set_properties(model, properties) set_properties(model, properties)
name = properties['Name'] name = properties['Name']
self.model.insertByName(name, model) self.model.insertByName(name, model)
control = self._get_custom_class(tipo, self.obj.getControl(name)) control = self.obj.getControl(name)
self._add_listeners(control) add_listeners(self.events, control, name)
control = get_custom_class(tipo, control)
setattr(self, name, control) setattr(self, name, control)
return return
class LOWindow(object):
def __init__(self, **kwargs):
self._events = None
self._menu = None
self._container = None
self._obj = self._create(kwargs)
def _create(self, properties):
ps = (
properties.get('X', 0),
properties.get('Y', 0),
properties.get('Width', 500),
properties.get('Height', 500),
)
self._title = properties.get('Title', TITLE)
self._create_frame(ps)
self._create_container(ps)
# ~ self._create_splitter(ps)
return
def _create_frame(self, ps):
service = 'com.sun.star.frame.TaskCreator'
tc = create_instance(service, True)
self._frame = tc.createInstanceWithArguments((
NamedValue('FrameName', 'EasyMacroWin'),
NamedValue('PosSize', Rectangle(*ps)),
))
self._window = self._frame.getContainerWindow()
self._toolkit = self._window.getToolkit()
desktop = get_desktop()
self._frame.setCreator(desktop)
desktop.getFrames().append(self._frame)
self._frame.Title = self._title
return
def _create_container(self, ps):
# ~ toolkit = self._window.getToolkit()
service = 'com.sun.star.awt.UnoControlContainer'
self._container = create_instance(service, True)
service = 'com.sun.star.awt.UnoControlContainerModel'
model = create_instance(service, True)
model.BackgroundColor = get_color(225, 225, 225)
self._container.setModel(model)
self._container.createPeer(self._toolkit, self._window)
self._container.setPosSize(*ps, POSSIZE)
self._frame.setComponent(self._container, None)
return
def _get_base_control(self, tipo):
services = {
'label': 'com.sun.star.awt.UnoControlFixedText',
'button': 'com.sun.star.awt.UnoControlButton',
'text': 'com.sun.star.awt.UnoControlEdit',
'listbox': 'com.sun.star.awt.UnoControlListBox',
'link': 'com.sun.star.awt.UnoControlFixedHyperlink',
'roadmap': 'com.sun.star.awt.UnoControlRoadmap',
'image': 'com.sun.star.awt.UnoControlImageControl',
'groupbox': 'com.sun.star.awt.UnoControlGroupBox',
'radio': 'com.sun.star.awt.UnoControlRadioButton',
'tree': 'com.sun.star.awt.tree.TreeControl',
'grid': 'com.sun.star.awt.grid.UnoControlGrid',
}
return services[tipo]
def add_control(self, properties):
tipo = properties.pop('Type').lower()
base = self._get_base_control(tipo)
obj = create_instance(base, True)
model = create_instance('{}Model'.format(base), True)
set_properties(model, properties)
obj.setModel(model)
x = properties.get('X', 5)
y = properties.get('Y', 5)
w = properties.get('Width', 200)
h = properties.get('Height', 25)
obj.setPosSize(x, y, w, h, POSSIZE)
name = properties['Name']
self._container.addControl(name, obj)
add_listeners(self.events, obj, name)
control = get_custom_class(tipo, obj)
setattr(self, name, control)
return
def _create_popupmenu(self, menus):
menu = create_instance('com.sun.star.awt.PopupMenu', True)
for i, m in enumerate(menus):
label = m['label']
cmd = m.get('event', '')
if not cmd:
cmd = label.lower().replace(' ', '_')
if label == '-':
menu.insertSeparator(i)
else:
menu.insertItem(i, label, m.get('style', 0), i)
menu.setCommand(i, cmd)
# ~ menu.setItemImage(i, path?, True)
menu.addMenuListener(EventsMenu(self.events))
return menu
def _create_menu(self, menus):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMenu.html
#~ nItemId specifies the ID of the menu item to be inserted.
#~ aText specifies the label of the menu item.
#~ nItemStyle 0 = Standard, CHECKABLE = 1, RADIOCHECK = 2, AUTOCHECK = 4
#~ nItemPos specifies the position where the menu item will be inserted.
self._menu = create_instance('com.sun.star.awt.MenuBar', True)
for i, m in enumerate(menus):
self._menu.insertItem(i, m['label'], m.get('style', 0), i)
cmd = m['label'].lower().replace(' ', '_')
self._menu.setCommand(i, cmd)
submenu = self._create_popupmenu(m['submenu'])
self._menu.setPopupMenu(i, submenu)
self._window.setMenuBar(self._menu)
return
def add_menu(self, menus):
self._create_menu(menus)
return
def _add_listeners(self, control=None):
if self.events is None:
return
controller = EventsWindow(self)
self._window.addTopWindowListener(controller)
self._window.addWindowListener(controller)
self._container.addKeyListener(EventsKey(self))
return
@property
def name(self):
return self._title.lower().replace(' ', '_')
@property
def events(self):
return self._events
@events.setter
def events(self, value):
self._events = value
self._add_listeners()
@property
def width(self):
return self._container.Size.Width
@property
def height(self):
return self._container.Size.Height
def open(self):
self._window.setVisible(True)
return
def close(self):
self._window.setMenuBar(None)
self._window.dispose()
self._frame.close(True)
return
# ~ Python >= 3.7 # ~ Python >= 3.7
# ~ def __getattr__(name): # ~ def __getattr__(name):
@ -1935,7 +2408,7 @@ def active_cell():
def create_dialog(properties): def create_dialog(properties):
return LODialog(properties) return LODialog(**properties)
# ~ Export ok # ~ Export ok
@ -2095,7 +2568,7 @@ def inputbox(message, default='', title=TITLE, echochar=''):
'Width': 200, 'Width': 200,
'Height': 80, 'Height': 80,
} }
dlg = LODialog(args) dlg = LODialog(**args)
dlg.events = ControllersInput(dlg) dlg.events = ControllersInput(dlg)
args = { args = {
@ -2974,7 +3447,7 @@ class SmtpServer(object):
def __enter__(self): def __enter__(self):
return self return self
def __exit__(self, *args): def __exit__(self, exc_type, exc_value, traceback):
self.close() self.close()
@property @property
@ -3109,6 +3582,9 @@ def server_smtp_test(config):
return server.error return server.error
def create_window(kwargs):
return LOWindow(**kwargs)
class LIBOServer(object): class LIBOServer(object):
HOST = 'localhost' HOST = 'localhost'