def _activate_occurrences_block(self, time, occsd): # It's important that the databases are blocked on this thread, and not # on the search thread, otherwise the program would hang if some # occurrences are activated while the user is performing an action core_api.block_databases(block=True) self._activate_occurrences(time, occsd) core_api.release_databases()
def _continue(self): # It's important that the databases are blocked on this thread, and not # on the main thread, otherwise the program would hang if some # occurrences are activated while the user is performing an action core_api.block_databases(block=True) search_old_occurrences_event.signal(filename=self.filename, last_search=self.exclmint) self.state = 0 while self.state < 1: self.search = organism_api.get_occurrences_range( mint=self.exclmint, maxt=self.whileago, filenames=(self.filename, )) self.state = 2 # Make sure to bind *after* self.search is instantiated and # self.state is set to 2, but *before* it's started core_api.bind_to_closing_database(self._handle_closing_database) self.search.start() if self.state == 1: self._abort() else: self._process_results() core_api.release_databases()
def dismiss(self, event): if core_api.block_databases(): organism_alarms_api.dismiss_alarms({self.filename: {self.id_: [self.alarmid, ]}}) # Let the alarm off event close the alarm core_api.release_databases()
def snooze(self, event): if core_api.block_databases(): organism_alarms_api.snooze_alarms({self.filename: {self.id_: [self.alarmid, ]}}, stime=self.awindow.get_snooze_time()) # Let the alarm off event close the alarm core_api.release_databases()
def delete_selected_items(self, no_confirm=False): if core_api.block_databases(): selection = self.get_selections(none=False, descendants=True) if selection: items = [] for item in selection: id_ = self.get_item_id(item) tab = editor.Editor.make_tabid(self.filename, id_) if tab in editor.tabs and not editor.tabs[tab].close( 'quiet' if no_confirm else 'discard'): core_api.release_databases() return False items.append(id_) self.delete_items(items, description='Delete {} items'.format( len(items))) self.dbhistory.refresh() delete_items_event.signal() core_api.release_databases()
def dismiss_all(self, event): if core_api.block_databases(): organism_alarms_api.dismiss_alarms( self._get_shown_alarms_dictionary()) # Let the alarm off event close the alarms core_api.release_databases()
def move_item_to_parent(self): if core_api.block_databases(): selection = self.get_selections(none=False, many=False) if selection: item = selection[0] id_ = self.get_item_id(item) oldpid = core_api.get_item_parent(self.filename, id_) if core_api.move_item_to_parent(self.filename, id_, description='Move item to parent'): newpid = core_api.get_item_parent(self.filename, id_) # oldpid cannot be 0 here because # core_api.move_item_to_parent succeded, which means that # it wasn't the root item oldparent = self.get_tree_item(oldpid) newparent = self.get_tree_item_safe(newpid) self.dvmodel.ItemDeleted(oldparent, item) self.dvmodel.ItemAdded(newparent, item) self._reset_children(id_, item) self._refresh_item_arrow(newparent, oldpid, oldparent) self.select_item(id_) self.dbhistory.refresh() core_api.release_databases()
def cut_items(self, no_confirm=False): if core_api.block_databases(): # get_tree_selections() arguments must be compatible with the # ones used in self.delete_items() selection = wxgui_api.get_tree_selections(self.filename, none=False, descendants=True) if selection: items = [] for item in selection: id_ = wxgui_api.get_tree_item_id(self.filename, item) if not wxgui_api.close_editor( self.filename, id_, ask='quiet' if no_confirm else 'discard'): core_api.release_databases() return False items.append(id_) copypaste_api.copy_items(self.filename, items) wxgui_api.delete_items(self.filename, items, description='Cut {} items'.format( len(items))) wxgui_api.refresh_history(self.filename) cut_items_event.signal() core_api.release_databases()
def create_sibling(self): if core_api.block_databases(): # Do not use none=False in order to allow the creation of the # first item selection = self.get_selections(many=False) # If multiple items are selected, selection will be False if selection is not False: text = 'New item' if len(selection) > 0: previd = self.get_item_id(selection[0]) parid = core_api.get_item_parent(self.filename, previd) id_ = core_api.create_sibling(filename=self.filename, parent=parid, previous=previd, text=text, description='Insert item') else: id_ = core_api.create_child(filename=self.filename, parent=0, text=text, description='Insert item') self.select_item(id_) self.dbhistory.refresh() core_api.release_databases()
def cut_items(self, no_confirm=False): if core_api.block_databases(): # get_tree_selections() arguments must be compatible with the # ones used in self.delete_items() selection = wxgui_api.get_tree_selections(self.filename, none=False, descendants=True) if selection: items = [] for item in selection: id_ = wxgui_api.get_tree_item_id(self.filename, item) if not wxgui_api.close_editor(self.filename, id_, ask='quiet' if no_confirm else 'discard'): core_api.release_databases() return False items.append(id_) copypaste_api.copy_items(self.filename, items) wxgui_api.delete_items(self.filename, items, description='Cut {} items'.format(len(items))) wxgui_api.refresh_history(self.filename) cut_items_event.signal() core_api.release_databases()
def _handle_page_closing(self, event): # Veto the event, page deletion is managed explicitly later event.Veto() if core_api.block_databases(): self.GetCurrentPage().close_tab() core_api.release_databases()
def paste_items_as_siblings(self, no_confirm=False): if core_api.block_databases(): if no_confirm or copypaste_api.can_paste_safely(self.filename) or \ msgboxes.unsafe_paste_confirm().ShowModal() == wx.ID_OK: # Do not use none=False in order to allow pasting in an empty # database selection = wxgui_api.get_tree_selections(self.filename, many=False) # If multiple items are selected, selection will be False if selection is not False: if len(selection) > 0: baseid = wxgui_api.get_tree_item_id( self.filename, selection[0]) roots, ids = copypaste_api.paste_items_as_siblings( self.filename, baseid, description='Paste items') else: roots, ids = copypaste_api.paste_items_as_children( self.filename, 0, description='Paste items') wxgui_api.refresh_history(self.filename) items_pasted_event.signal(filename=self.filename, roots=roots, ids=ids) core_api.release_databases()
def paste_items_as_siblings(self, no_confirm=False): if core_api.block_databases(): if no_confirm or copypaste_api.can_paste_safely(self.filename) or \ msgboxes.unsafe_paste_confirm().ShowModal() == wx.ID_OK: # Do not use none=False in order to allow pasting in an empty # database selection = wxgui_api.get_tree_selections(self.filename, many=False) # If multiple items are selected, selection will be False if selection is not False: if len(selection) > 0: baseid = wxgui_api.get_tree_item_id(self.filename, selection[0]) roots, ids = copypaste_api.paste_items_as_siblings( self.filename, baseid, description='Paste items') else: roots, ids = copypaste_api.paste_items_as_children( self.filename, 0, description='Paste items') wxgui_api.refresh_history(self.filename) items_pasted_event.signal(filename=self.filename, roots=roots, ids=ids) core_api.release_databases()
def snooze_all(self, event): if core_api.block_databases(): organism_alarms_api.snooze_alarms( self._get_shown_alarms_dictionary(), stime=self.get_snooze_time()) # Let the alarm off event close the alarms core_api.release_databases()
def _handle_page_closing(self, event): # Veto the event, page deletion is managed explicitly later event.Veto() if core_api.block_databases(): page = self.GetCurrentPage() databases.close_database(page.get_filename()) core_api.release_databases()
def populate_tree(self, event): if core_api.block_databases(): filename = wxgui_api.get_selected_database_filename() # This method may be launched even if no database is open if filename: group = core_api.get_next_history_group(filename) description = 'Populate tree' i = 0 while i < 10: dbitems = core_api.get_items_ids(filename) try: itemid = random.choice(dbitems) except IndexError: # No items in the database yet itemid = 0 mode = 'child' else: mode = random.choice(('child', 'sibling')) # See the comment in wxgui.tree.expand_item_ancestors # for the reason why calling this method is necessary wxgui_api.expand_item_ancestors(filename, itemid) text = self._populate_tree_text() id_ = self._populate_tree_item(mode, filename, itemid, group, text, description) # It should also be checked if the database supports # organism_basicrules (bug #330) if organism_api and wxscheduler_basicrules_api and \ filename in \ organism_api.get_supported_open_databases(): self._populate_tree_rules(filename, id_, group, description) if links_api and wxlinks_api and len(dbitems) > 0 and \ filename in \ links_api.get_supported_open_databases(): self._populate_tree_link(filename, id_, dbitems, group, description) i += 1 wxgui_api.refresh_history(filename) core_api.release_databases()
def create_child(self): if core_api.block_databases(): selection = self.get_selections(none=False, many=False) if selection: pid = self.get_item_id(selection[0]) id_ = core_api.create_child(filename=self.filename, parent=pid, text='New item', description='Insert sub-item') self.select_item(id_) self.dbhistory.refresh() core_api.release_databases()
def copy_items(self): if core_api.block_databases(): # get_tree_selections() arguments must be compatible with the # ones used in self.delete_items() selection = wxgui_api.get_tree_selections(self.filename, none=False, descendants=True) if selection: items = [] for item in selection: items.append(wxgui_api.get_tree_item_id(self.filename, item)) copypaste_api.copy_items(self.filename, items) core_api.release_databases()
def move_item_down(self): if core_api.block_databases(): selection = self.get_selections(none=False, many=False) if selection: item = selection[0] id_ = self.get_item_id(item) if core_api.move_item_down(self.filename, id_, description='Move item down'): self._move_item(id_, item) self.select_item(id_) self.dbhistory.refresh() core_api.release_databases()
def redo(self, no_confirm=False): if core_api.block_databases(): read = core_api.preview_redo_tree(self.filename) if read: for id_ in read: item = editor.Editor.make_tabid(self.filename, id_) if item in editor.tabs and not editor.tabs[item].close( ask='quiet' if no_confirm else 'discard'): break else: core_api.redo_tree(self.filename) self.dbhistory.refresh() redo_tree_event.signal(filename=self.filename) core_api.release_databases()
def paste_items_as_children(self, no_confirm=False): if core_api.block_databases(): selection = wxgui_api.get_tree_selections(self.filename, none=False, many=False) if selection and ( no_confirm or copypaste_api.can_paste_safely(self.filename) or msgboxes.unsafe_paste_confirm().ShowModal() == wx.ID_OK ): baseid = wxgui_api.get_tree_item_id(self.filename, selection[0]) roots, ids = copypaste_api.paste_items_as_children(self.filename, baseid, description="Paste sub-items") wxgui_api.refresh_history(self.filename) items_pasted_event.signal(filename=self.filename, roots=roots, ids=ids) core_api.release_databases()
def copy_items(self): if core_api.block_databases(): # get_tree_selections() arguments must be compatible with the # ones used in self.delete_items() selection = wxgui_api.get_tree_selections(self.filename, none=False, descendants=True) if selection: items = [] for item in selection: items.append( wxgui_api.get_tree_item_id(self.filename, item)) copypaste_api.copy_items(self.filename, items) core_api.release_databases()
def _finish_search_restart(self): self._set_tab_icon_ongoing() string = self.filters.text.GetValue() self._set_title(string) if not self.filters.option4.GetValue(): string = re.escape(string) self.results.reset() self.search_threaded_action = self._search_threaded_continue self.finish_search_action = self._finish_search_dummy flags = re.MULTILINE if not self.filters.option5.GetValue(): flags |= re.IGNORECASE try: regexp = re.compile(string, flags) except re.error: msgboxes.bad_regular_expression().ShowModal() self.finish_search() else: # Note that the databases are released *before* the threads are # terminated: this is safe as no more calls to the databases are # made after core_api.get_all_items_text in # self._finish_search_restart_database if core_api.block_databases(): if self.filters.option1.GetValue(): filename = wxgui_api.get_selected_database_filename() self._finish_search_restart_database(filename, regexp) else: for filename in core_api.get_open_databases(): self._finish_search_restart_database(filename, regexp) # Note that the databases are released *before* the threads are # terminated: this is safe as no more calls to the databases # are made after core_api.get_all_items_text in # self._finish_search_restart_database core_api.release_databases() else: self.finish_search()
def _do_action(): # [2] Try to block the databases here to avoid hanging the program in case # e.g. modal windows are active: this is not a perfect solution, in fact the # databases must be released just before calling the simulator action, which # has to block them again. This way there's still a (very) short interval # between the release_databases and the block_databases called by the # simulator actions when the user or the alarms timer can still block the # databases, thus hanging the program; note that the simulator is not # designed to be used while interacting manually (the problem with alarms # remains, though, although they should block the databases on their own # thread, and this should prevent hanging the whole application) if core_api.block_databases(quiet=True): if random.choice(simulator_actions.ACTIONS)() == False: # core_api.release_databases must be called by the action, see also # comment [2] _do_action() else: # core_api.release_databases must be called by the action, see also # comment [2] _restart() else: _restart()
def paste_items_as_children(self, no_confirm=False): if core_api.block_databases(): selection = wxgui_api.get_tree_selections(self.filename, none=False, many=False) if selection and (no_confirm or copypaste_api.can_paste_safely(self.filename) or msgboxes.unsafe_paste_confirm().ShowModal() == wx.ID_OK): baseid = wxgui_api.get_tree_item_id(self.filename, selection[0]) roots, ids = copypaste_api.paste_items_as_children( self.filename, baseid, description='Paste sub-items') wxgui_api.refresh_history(self.filename) items_pasted_event.signal(filename=self.filename, roots=roots, ids=ids) core_api.release_databases()
def delete_selected_items(self, no_confirm=False): if core_api.block_databases(): selection = self.get_selections(none=False, descendants=True) if selection: items = [] for item in selection: id_ = self.get_item_id(item) tab = editor.Editor.make_tabid(self.filename, id_) if tab in editor.tabs and not editor.tabs[tab].close( 'quiet' if no_confirm else 'discard'): core_api.release_databases() return False items.append(id_) self.delete_items(items, description='Delete {} items'.format(len(items))) self.dbhistory.refresh() delete_items_event.signal() core_api.release_databases()
def _set_history_limit(self, data, value): if core_api.block_databases(): core_api.update_database_history_soft_limit(self.filename, value) core_api.release_databases()
def print_memory_table(self, table): if core_api.block_databases(): development_api.print_memory_table(table) core_api.release_databases()
def print_all_tables(self, filename): if core_api.block_databases(): development_api.print_all_tables(filename) core_api.release_databases()
def _restart(self): # Note that this function must be kept separate from # NextOccurrencesSearch because the latter can be used without this # (e.g. by wxtasklist); note also that both functions generate their # own events # Blocking here also prevents a second search from running # simultaneously core_api.block_databases(block=True) self.queued = False log.debug('Search next occurrences') # Make sure to use the same set of filenames during the search, because # self.databases itself could change meanwhile due to race conditions filenames = self.databases.keys() base_times = {filename: self.databases[filename].get_last_search() for filename in filenames} # For the moment there seems to be no need to stop the search if a # database is closed, in fact the databases are blocked, and the search # seems to terminate cleanly, and anyway it should take a reasonable # time to complete search = NextOccurrencesSearch(filenames, self.rule_handlers, base_times=base_times) search.start() occs = search.get_results() next_occurrence = occs.get_next_occurrence_time() occsd = occs.get_dict() oldoccsd = occs.get_old_dict() self.cancel() now = int(time_.time()) activate_old_occurrences_event.signal(oldoccsd=oldoccsd) if next_occurrence != None: if next_occurrence <= now: for filename in filenames: self.databases[filename].set_last_search_safe( next_occurrence) self._activate_occurrences(next_occurrence, occsd) else: # Reset last search time in every searched database, so that if # a rule is created with an alarm time between the last search # and now, the alarm won't be activated for filename in filenames: self.databases[filename].set_last_search(now) next_loop = next_occurrence - now self.timer = threading.Timer(next_loop, self._activate_occurrences_block, (next_occurrence, occsd)) self.timer.name = "organism_engine_timer" self.timer.start() log.debug('Next occurrence in {} seconds'.format(next_loop)) else: # Even if no occurrence is found, reset last search time in every # searched database, so that: # 1) this will let the next NextOccurrencesSearch ignore the # occurrences excepted in the previous search # 2) if a rule is created with an alarm time between the last # search and now, the alarm won't be activated for filename in filenames: self.databases[filename].set_last_search(now) core_api.release_databases() # Note that this event is not protected in the databases block search_next_occurrences_event.signal()
def print_all_memory_tables(self, event): if core_api.block_databases(): development_api.print_all_memory_tables() core_api.release_databases()
def set_log_limit(self, data, value): if core_api.block_databases(): organism_alarms_api.update_alarms_log_limit(self.filename, value) core_api.release_databases()
def _change_dependencies(self, data, value): if core_api.block_databases(): ext = data newchoice = value try: ver = self.dependencies[ext] except KeyError: currchoice = 1 else: if ver is None: currchoice = 2 else: currchoice = 0 # This method shouldn't be triggered if the value is not changed, # so there's no need to check that newchoice != currchoice if currchoice == 0: reverse_deps = [] for udep in self.dependencies: dep = str(udep) # Core (None) has been removed from self.dependencies if self.dependencies[dep] is not None: try: ddeps = list( coreaux_api.import_extension_info( dep).dependencies) except AttributeError: ddeps = [] for ddep in ddeps: if ddep[0] == 'extensions.{}'.format(ext): reverse_deps.append(dep) if reverse_deps: self.refresh_dependency(ext) msgboxes.warn_disable_dependency(reverse_deps).ShowModal() else: # The dialog should be completely independent from this # object, because if the operation is confirmed, it needs # to close the database, including the properties tab (so # use CallAfter) wx.CallAfter(DependencyDialogDisable, self.filename, ext, newchoice, self.manager) else: if newchoice == 0: try: deps = list( coreaux_api.import_extension_info( ext).dependencies) except AttributeError: deps = [] missing_deps = [] for dep in deps: sdep = dep[0].split(".") if sdep[0] == 'extensions': try: ver = self.dependencies[sdep[1]] except KeyError: if coreaux_api.import_extension_info( sdep[1]).affects_database: missing_deps.append(sdep[1]) else: if ver is None: missing_deps.append(sdep[1]) if missing_deps: self.refresh_dependency(ext) msgboxes.warn_enable_dependency( missing_deps).ShowModal() else: # The dialog should be completely independent from this # object, because if the operation is confirmed, it # needs to close the database, including the properties # tab (so use CallAfter) wx.CallAfter(DependencyDialogEnable, self.filename, ext, newchoice, self.manager) else: if newchoice == 1: core_api.remove_database_ignored_dependency( self.filename, ext) del self.dependencies[ext] else: core_api.add_database_ignored_dependency( self.filename, ext) self.dependencies[ext] = None self.refresh_dependency(ext) core_api.release_databases()
def _change_dependencies(self, data, value): if core_api.block_databases(): ext = data newchoice = value try: ver = self.dependencies[ext] except KeyError: currchoice = 1 else: if ver is None: currchoice = 2 else: currchoice = 0 # This method shouldn't be triggered if the value is not changed, # so there's no need to check that newchoice != currchoice if currchoice == 0: reverse_deps = [] for udep in self.dependencies: dep = str(udep) # Core (None) has been removed from self.dependencies if self.dependencies[dep] is not None: try: ddeps = list(coreaux_api.import_extension_info( dep).dependencies) except AttributeError: ddeps = [] for ddep in ddeps: if ddep[0] == 'extensions.{}'.format(ext): reverse_deps.append(dep) if reverse_deps: self.refresh_dependency(ext) msgboxes.warn_disable_dependency(reverse_deps).ShowModal() else: # The dialog should be completely independent from this # object, because if the operation is confirmed, it needs # to close the database, including the properties tab (so # use CallAfter) wx.CallAfter(DependencyDialogDisable, self.filename, ext, newchoice, self.manager) else: if newchoice == 0 : try: deps = list(coreaux_api.import_extension_info( ext).dependencies) except AttributeError: deps = [] missing_deps = [] for dep in deps: sdep = dep[0].split(".") if sdep[0] == 'extensions': try: ver = self.dependencies[sdep[1]] except KeyError: if coreaux_api.import_extension_info(sdep[1] ).affects_database: missing_deps.append(sdep[1]) else: if ver is None: missing_deps.append(sdep[1]) if missing_deps: self.refresh_dependency(ext) msgboxes.warn_enable_dependency(missing_deps ).ShowModal() else: # The dialog should be completely independent from this # object, because if the operation is confirmed, it # needs to close the database, including the properties # tab (so use CallAfter) wx.CallAfter(DependencyDialogEnable, self.filename, ext, newchoice, self.manager) else: if newchoice == 1: core_api.remove_database_ignored_dependency( self.filename, ext) del self.dependencies[ext] else: core_api.add_database_ignored_dependency(self.filename, ext) self.dependencies[ext] = None self.refresh_dependency(ext) core_api.release_databases()