import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GObject, Gdk from mods.db import db, List, ListRecord, ListRecordIndex from mods.files import open_list_win_file, listrecord_row_ui_str, editable_row_ui_str, list_row_ui_str from mods.utils import show_msg, disable_widget, enable_widget, ConditionalFilter import re import peewee ANAMNEZ_LIST = 'anamnez' OBSERV_LIST = 'observ' lists_map = { ANAMNEZ_LIST: 'Анамнез', OBSERV_LIST: 'Осмотр', } for s_id in lists_map: with db.atomic(): q = List.select().where(List.system_id == s_id) if not len(q): List.create(name=lists_map[s_id], system_id=s_id) class ListRecFilter(ConditionalFilter): def filter(self, list_id, query): if query != self.fstr: self.fstr = query self.ids = list(map(lambda x: x.id, self.search_func(list_id, query))) return self.ids class ListRow(Gtk.ListBoxRow): @GObject.Property def db_id(self): return self._db_id @db_id.setter def db_id_setter(self, value): self._db_id = value @GObject.Property def name(self): return self._name @name.setter def name_setter(self, value): self._name = value class ListRecordRow(Gtk.ListBoxRow): @GObject.Property def db_id(self): return self._db_id @db_id.setter def db_id_setter(self, value): self._db_id = value @GObject.Property def text(self): return self._text @text.setter def text_setter(self, value): self._text = value def get_list(list_id): with db.atomic(): return List.get_by_id(list_id) def get_listrecord(listrec_id): with db.atomic(): return ListRecord.get_by_id(listrec_id) def get_all_list_records(list_id): with db.atomic(): list_o = List.get_by_id(list_id) return ListRecord.select().where(ListRecord.list == list_o) def add_list_record(list_id, text): with db.atomic(): list_o = List.get_by_id(list_id) rec = ListRecord.create(list=list_o, text=text) ListRecordIndex.insert( { ListRecordIndex.rowid: rec.id, ListRecordIndex.text: text } ).execute() return rec def delete_list_record(listrec_id): with db.atomic(): ListRecord.delete().where(ListRecord.id == listrec_id).execute() ListRecordIndex.delete().where(ListRecordIndex.rowid == listrec_id).execute() def change_list_record(listrec_id, new_text): listrec_o = get_listrecord(listrec_id) if listrec_o.text == new_text: return listrec_o with db.atomic(): ListRecord.update(text=new_text).where(ListRecord.id == listrec_id).execute() ListRecordIndex.update(text=new_text).where(ListRecordIndex.rowid == listrec_id).execute() return ListRecord.get_by_id(listrec_id) def search_list_record(list_id, q): q = re.sub(r'\s+', ' ', q).strip() q = ' '.join([ f'{x}*' for x in q.split(' ')]) list_o = get_list(list_id) with db.atomic(): return (ListRecord.select() .join( ListRecordIndex, on=(ListRecord.id == ListRecordIndex.rowid)) .where((ListRecord.list == list_o) & (ListRecordIndex.match(q))) .order_by(ListRecordIndex.bm25())) ##################################################################################################################### def build_list_row(list_o): b = Gtk.Builder() b.add_from_string(list_row_ui_str) win = b.get_object('win') box = b.get_object('list_box') b.get_object('name').set_text(list_o.name) row = ListRow() row.props.db_id = list_o.id row.name = list_o.name win.remove(win.get_children()[0]) row.add(box) return row def build_listrecord_row(listrec_o): b = Gtk.Builder() b.add_from_string(listrecord_row_ui_str) win = b.get_object('win') box = b.get_object('listrecord_box') b.get_object('text').set_text(listrec_o.text) row = ListRecordRow() row.props.db_id = listrec_o.id row.text = listrec_o.text win.remove(win.get_children()[0]) row.add(box) return row def build_listrec_editable_row(listrec_o=None): b = Gtk.Builder() b.add_from_string(editable_row_ui_str) #b.connect_signals({'editing_done': listrec_editing_done}) win = b.get_object('win') box = b.get_object('editable_box') text_input = b.get_object('text_input') text_input.set_text(listrec_o.text if listrec_o else '') row = ListRecordRow() row.props.db_id = listrec_o.id if listrec_o else -1 row.text = listrec_o.text if listrec_o else '' win.remove(win.get_children()[0]) row.add(box) return (row, text_input) def create_open_list_win(list_id): list_o = get_list(list_id) b = Gtk.Builder() listrecord_filter = ListRecFilter(search_list_record) class OpenListHandler: def row_add_cancel(self, entry, ev, row): if ev.keyval == Gdk.KEY_Escape: lr_list.remove(row) row.destroy() lr_list.unselect_all() self.listrec_row_unselected() def row_edit_cancel(self, entry, ev, edit_row): if ev.keyval == Gdk.KEY_Escape: rec = get_listrecord(edit_row.props.db_id) row = build_listrecord_row(rec) lr_list.remove(edit_row) edit_row.destroy() lr_list.add(row) lr_list.show_all() def enable_buttons_on_add_edit(self, *a): enable_widget([add_button, edit_button, remove_button, lr_filter]) def remove_row(self, button): row = lr_list.get_selected_row() delete_list_record(row.props.db_id) lr_list.remove(row) row.destroy() lr_list.show_all() self.listrec_row_unselected() def edit_row(self, button): row = lr_list.get_selected_row() rec = get_listrecord(row.props.db_id) (edit_row, inp) = build_listrec_editable_row(rec) lr_list.remove(row) row.destroy() lr_list.add(edit_row) inp.grab_focus() inp.connect('key-release-event', self.row_edit_cancel, edit_row) inp.connect('activate', self.update_row, edit_row) inp.connect('destroy', self.enable_buttons_on_add_edit) disable_widget([add_button, edit_button, remove_button, lr_filter]) lr_list.show_all() def add_new_row(self, button): (row, text_input) = build_listrec_editable_row() lr_list.add(row) text_input.grab_focus() text_input.connect('key-release-event', self.row_add_cancel, row) text_input.connect('activate', self.save_new_row, row) text_input.connect('destroy', self.enable_buttons_on_add_edit) disable_widget([add_button, edit_button, remove_button, lr_filter]) lr_list.show_all() def update_row(self, inp, edit_row): new_text = re.sub(r'\s+', ' ', inp.get_text()).strip() if new_text: try: rec = change_list_record(edit_row.props.db_id, new_text) except peewee.IntegrityError: return show_msg('Такая же запись уже существует', 'В данном списке уже существует\nдругая запись с данным текстом', level='warn') row = build_listrecord_row(rec) else: row = build_listrecord_row(get_listrecord(edit_row.props.db_id)) lr_list.add(row) lr_list.select_row(row) lr_list.show_all() lr_list.remove(edit_row) edit_row.destroy() def save_new_row(self, inp, row): row_text = re.sub(r'\s+', ' ', inp.get_text()).strip() if row_text: try: rec = add_list_record(list_id, row_text) except peewee.IntegrityError: return show_msg('Данная запись уже существует', 'В данном списке уже существует\nзапись с данным текстом', level='warn') new_row = build_listrecord_row(rec) lr_list.add(new_row) lr_list.select_row(new_row) lr_list.show_all() lr_list.remove(row) row.destroy() def listrec_row_selected(self, *a): enable_widget([edit_button, remove_button]) def listrec_row_unselected(self, *a): disable_widget([edit_button, remove_button]) def listrec_filter_changed(self, filter_widget): lr_list.unselect_all() self.listrec_row_unselected() lr_list.invalidate_filter() b.add_from_file(open_list_win_file) b.connect_signals(OpenListHandler()) # Gtk objects w = b.get_object('open_list_window') lr_list = b.get_object('listrecord_list') lr_filter = b.get_object('listrecord_filter') add_button = b.get_object('add_button') edit_button = b.get_object('edit_button') remove_button = b.get_object('remove_button') list_header = b.get_object('list_win_header') ############# ###### def listrecord_sort_func(row1, row2, *a): text1 = row1.props.text text2 = row2.props.text return (text1 > text2) - (text1 < text2) def listrecord_filter_func(row): fstr = lr_filter.get_text().strip() if not fstr: listrecord_filter.reset() return True return row.props.db_id in listrecord_filter.filter(list_id, fstr) ###### lr_list.set_sort_func(listrecord_sort_func) lr_list.set_filter_func(listrecord_filter_func) list_header.props.title = list_o.name for lr in get_all_list_records(list_id): lr_list.add(build_listrecord_row(lr)) return w