class MultiBoxSelect(Widget): TABLE = "//table[@id={}]{}" def __init__(self, parent, id, number="", move_into=None, move_from=None, available_items="choices_chosen", chosen_items="members_chosen", logger=None): Widget.__init__(self, parent, logger=logger) self.available_options = Select(self, id=available_items) self.chosen_options = Select(self, id=chosen_items) self.id = id if number: self.number = "[{}]".format(number) else: self.number = number if isinstance(move_into, WidgetDescriptor): self._move_into = move_into.klass(self, **move_into.kwargs) else: self._move_into = move_into if isinstance(move_from, WidgetDescriptor): self._move_from = move_from.klass(self, **move_from.kwargs) else: self._move_from = move_from def __locator__(self): return self.TABLE.format(quote(self.id), self.number) def _values_to_remove(self, values): return list((set(values) ^ self.read()) - set(values)) def _values_to_add(self, values): return list((set(values) ^ self.read()) - self.read()) @property def move_into_button(self): """This method is required to avoid 'Element is no longer attached to the DOM' Selenium exception. """ if isinstance(self._move_into, Button): button = self._move_into elif isinstance(self._move_into, basestring): button = self.browser.element(self._move_into, self) return button @property def move_from_button(self): if isinstance(self._move_from, Button): button = self._move_from elif isinstance(self._move_from, basestring): button = self.browser.element(self._move_from, self) return button def fill(self, values): if set(values) == self.read(): return False else: values_to_remove = self._values_to_remove(values) values_to_add = self._values_to_add(values) if values_to_remove: self.chosen_options.fill(values_to_remove) self.move_from_button.click() self.browser.plugin.ensure_page_safe() if values_to_add: self.available_options.fill(values_to_add) self.move_into_button.click() self.browser.plugin.ensure_page_safe() return True def read(self): return {option.text for option in self.chosen_options.all_options}