# coding: utf-8
import gi
import SendKeys
from peewee import *
gi.require_version('Gtk', '3.0')
import gi.repository.Gtk as Gtk
file = 'resource.txt'
db = SqliteDatabase('ChaineRs.db')
# Model for database
class Chain(Model):
id = IntegerField(unique=True)
c_item = CharField()
id_root = IntegerField()
id_parent = IntegerField()
id_previous = IntegerField()
id_next = IntegerField()
b_root = BooleanField() # True = root of branch -> c_item = Name of branch
class Meta:
database = db
def next_id(instance):
# find max value of temp_id in model
# increment it by one and assign it to model instance object
try:
e = instance.__name__ + '.select(fn.Max(' + instance.__name__ + '.id))[0].id + 1'
# next_value = Poet.select(fn.Max(Poet.id))[0].id + 1
next_value = eval(e)
except:
next_value = 1
return next_value
class HeaderBarWindow(Gtk.ApplicationWindow):
d_data = dict()
l_data = list()
n_cur = 0 # cursor to 1-st element of l_data -> self.l_data[self.n_cur]
id = 0 # current id
id_level = 0 # level parentage by default
id_root = 0
id_previous = 0
id_next = -1
id_paramount = 0 # id of chosen root for filter
b_to_update = False # define to update or to save item
root = ''
def __init__(self, *args, **kwargs):
super(HeaderBarWindow, self).__init__(*args, **kwargs)
self.set_border_width(10)
self.set_default_size(400, 100) # size of window
hb = Gtk.HeaderBar() # TitleBar
hb.set_show_close_button(True)
#hb.props.title = "ChaineR"
self.set_titlebar(hb)
# number of rows and columns in table
n_row = 9
n_col = 9
# level of buttons
y_level_1 = int(n_row / 2)
y_level_2 = y_level_1 + 1
table = Gtk.Table(n_row, n_col, True)
# ComboBox
self.name_store = Gtk.ListStore(int, str)
self.name_combo = Gtk.ComboBox.new_with_model_and_entry(self.name_store)
# insert roots
l_combo = self.take_roots()
for i in range(len(l_combo)):
self.name_store.append([1, l_combo[i]]) # TODO: change 1 to new id
# TODO: do likewise
# data depends of chosen root
self.name_combo.connect("changed", self.take_data)
self.name_combo.set_entry_text_column(1)
hb.add(self.name_combo)
# button add root
self.b_add_root = Gtk.Button('+')
hb.add(self.b_add_root)
self.b_add_root.connect("clicked", self.insert_root, None)
# filter
self.find_entry = Gtk.Entry()
table.attach(self.find_entry, 0, 3, 0, 1)
self.find_entry.connect('key_press_event', self.find_like, None) # enter event
# button left
self.b_left = Gtk.Button("<")
#self.b_left.add(Gtk.Arrow(Gtk.ArrowType.LEFT, Gtk.ShadowType.NONE))
table.attach(self.b_left, 0, 1, y_level_1, y_level_2)
# editor
self.entry = Gtk.Entry()
# self.entry.connect('key_press_event', self.rewrite, None) # write data
table.attach(self.entry, 1, n_col - 1, y_level_1, y_level_2)
# button right
self.b_right = Gtk.Button(">")
#self.b_right.add(Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.NONE))
table.attach(self.b_right, n_col - 1, n_col, y_level_1, y_level_2)
self.b_right.connect("clicked", self.take_child, None)
# button up
self.b_up = Gtk.Button()
self.b_up.add(Gtk.Arrow(Gtk.ArrowType.UP, Gtk.ShadowType.NONE))
table.attach(self.b_up, int(n_col / 2), int(n_col / 2) + 1, 0, 1)
self.b_up.connect("clicked", self.take_prev, None)
# button down
self.b_down = Gtk.Button()
self.b_down.add(Gtk.Arrow(Gtk.ArrowType.DOWN, Gtk.ShadowType.NONE))
table.attach(self.b_down, int(n_col / 2), int(n_col / 2) + 1, n_row - 1, n_row)
self.b_down.connect("clicked", self.take_next, None)
# button for save data
self.b_save = Gtk.Button("Save")
table.attach(self.b_save, n_col - 1, n_col, n_row - 1, n_row)
self.b_save.connect("clicked", self.rewrite, None)
# button for delete item
self.b_delete = Gtk.Button("Delete")
table.attach(self.b_delete, 0, 1, n_row - 1, n_row)
self.b_delete.connect("clicked", self.delete_item, None)
# label previous
self.l_prev = Gtk.Label()
table.attach(self.l_prev, 1, n_col - 1, y_level_1 - 2, y_level_1 - 1)
# label next
self.l_next = Gtk.Label()
table.attach(self.l_next, 1, n_col - 1, y_level_2 + 1, y_level_2 + 2)
self.add(table)
def take_data(self, combo):
"""
Take data of the root chosen of user
And write to controlls
:return:
"""
if not self.entry.get_text() == '': # если происходит смена root-а то записываем изменения
self.insert_or_update_row()
# hide buttons
self.b_up.hide()
#self.b_down.hide()
self.b_left.hide()
#self.b_right.hide()
# make empty data
d = dict()
self.d_data = d
l = list()
self.l_data = l
# make empty controlls
self.empty_controlls()
# переписываем уровень с предком в качестве рута
# TODO: при смещении вправо предком нужно записывать предыдущий элемент
# take_data используется только в первоначальной загрузке поэтому пишу сюда
self.id_level = self.id_root
# переписываем уровень с предком в качестве предыдущего элемента
self.id_previous = self.id_root
tree_iter = combo.get_active_iter()
if tree_iter != None:
model = combo.get_model()
row_id, self.root = model[tree_iter][:2]
try:
self.take_id_root() # define self.id_root by self.root
#print(self.id_level)
# take data of the root
self.select_data() # select data to self.d_data and self.l_data
except:
pass
#print("Can't find root")
#print("self.n_cur - " + str(self.n_cur))
#if l: # contain objects
# write to controlls
# self.write_to_controlls_default()
def select_data(self):
"""
Function select data and set text to controllers
Take id root and id level of objects of this root
:return:
"""
#self.test_print()
for c in Chain.select().where(Chain.id_root == self.id_root and Chain.id_parent == self.id_root).order_by(
Chain.id.asc()):
self.d_data[c.id] = c.c_item # insert data into dictionary
self.l_data.append(c.id) # write id-s to list for circle
# set text to controllers
#try:
# self.entry.set_text(self.d_data[self.l_data[0]])
#except:
# pass
#try:
# self.l_next.set_label(self.d_data[self.l_data[1]])
#except:
# pass
if self.l_data[0]:
self.rewrite_class_data(self.l_data[0]) # прописываем данные по 1-ому элементу если он есть
self.write_cont_first() # прописываем контроллы
self.b_to_update = True # as it is -> just to update item
#self.test_print()
else:
print("Im here")
self.empty_controlls()
self.id = next_id(Chain)
self.id_previous = 0
self.take_id_root()
self.id_parent = 0
#elf.id_next = 0
#elf.b_root = False
if not self.l_data:
print("and here")
self.empty_controlls()
self.id = next_id(Chain)
self.id_previous = 0
self.take_id_root()
self.id_parent = 0
self.id_next = 0
self.b_root = False
# устанавливаем видимость кнопки если есть следующий
if self.have_next():
self.b_down.set_visible(True)
# устанавливаем видимость кнопки потомка если есть потомок
if self.have_child():
self.b_right.set_visible(True)
else:
self.b_right.set_label("+")
self.b_right.set_visible(True)
if not self.find_entry == '':
self.find_like()
def write_to_controlls_default(self):
"""
Function write data into controlls in default loading
:return:
"""
#self.n_cur = self.l_data[0] # cursor into 1-st element list of data
#self.l_prev.set_label(self.d_data[self.n_cur - 1])
#print("self.d_data - " + str(self.d_data))
#print("self.l_data - " + str(self.l_data))
#print("self.n_cur - " + str(self.n_cur))
self.entry.set_text(self.d_data[self.l_data[self.n_cur]])
try:
self.l_next.set_label(self.d_data[self.l_data[self.n_cur + 1]])
except:
self.l_next.set_label('') # empty if not exist
def write_to_controlls(self):
"""
Function write data into controlls
:return:
"""
self.l_prev.set_label(self.d_data[self.l_data[self.n_cur - 1]])
self.entry.set_text(self.d_data[self.l_data[self.n_cur]])
try:
self.l_next.set_label(self.d_data[self.l_data[self.n_cur + 1]])
except:
self.l_next.set_label('') # empty if not exist
def write_nothing_to_controlls(self):
"""
Function write data into controlls
:return:
"""
#print("self.n_cur - " + str(self.n_cur))
self.l_prev.set_label(self.d_data[self.l_data[self.n_cur - 1]])
self.entry.set_text('') # empty if not exist
# self.l_next.set_label('') # empty if not exist
def rewrite_data(self):
f = open(file, 'w')
l_data = list()
num = 1 # number of line
for i in range(1, len(self.d_data.items())):
l_data.append(self.d_data[str(i)])
for i in range(len(l_data)):
line = str(num) + '--' + l_data[i] + '\n'
f.write(line)
num = num + 1
f.close()
def write_data(self, button, data=None):
f = open(file, 'w')
l_data = list()
num = 1 # number of line
for i in range(1, len(self.d_data.items())):
l_data.append(self.d_data[str(i)])
for i in range(len(l_data)):
line = str(num) + '--' + l_data[i] + '\n'
f.write(line)
num = num + 1
f.close()
# заведение записи через entry
# работает
def rewrite(self, button=0, event=0, data=None):
"""
Function rewrite/write data in controll entry-editor
:param button:
:param event:
:param data:
:return:
"""
# if row exist -> rewrite
# else insert
self.insert_or_update_row()
# make visible button
self.b_down.set_visible(True)
def delete_item(self, button=0, event=0, data=None):
"""
Delete item from system
:param button:
:param event:
:param data:
:return:
"""
# rewrite relationships
# update id_next for previous item
Chain.update(**{'id_next': self.id_next}).where(Chain.id == self.id_previous).execute()
# update id_previous for next item
Chain.update(**{'id_previous': self.id_previous}).where(Chain.id == self.id_next).execute()
if not self.id_next == -1:
t_id_next = self.id_next # temp variable for delete item
# delete item
#Chain.delete().where(Chain.id == self.id).execute()
# move to next
self.id = t_id_next
self.rewrite_class_data()
self.write_cont() # rewrite controlls
else: # if last item
# delete item
#Chain.delete().where(Chain.id == self.id).execute()
self.entry.set_text("")
def select_dict_id(self, id_item):
"""
Function take dictionary of values of taken id
:return:
"""
d = dict()
c = Chain.get(Chain.id == id_item)
d['id'] = c.id
d['c_item'] = c.c_item
d['id_root'] = c.id_root
d['id_parent'] = c.id_parent
d['id_previous'] = c.id_previous
d['id_next'] = c.id_next
d['b_root'] = c.b_root
return d
def rewrite_class_data(self, id_item=None):
"""
Function rewrite data from id data in DB
"""
if id_item == None:
id_item = self.id
d = self.select_dict_id(id_item)
self.id = d['id']
self.id_root = d['id_root']
self.id_level = d['id_parent']
self.id_previous = d['id_previous']
self.id_next = d['id_next']
self.root = d['c_item']
return
def insert_row(self, id_item=None):
"""
Insert row
:return:
"""
if id_item == None:
id_item = next_id(Chain)
Chain.create(id=id_item, c_item=self.entry.get_text(), id_parent=self.id_level, id_previous=self.id_previous, b_root=False)
def insert_or_update_row(self, id_item=None):
"""
Function insert or update row
:return:
"""
#if self.entry.get_text() == '':
# return
if id_item == None:
id_item = next_id(Chain)
if self.b_to_update == False: # if not just update -> insert otherwise -> update
Chain.create(id=id_item, c_item=self.entry.get_text(), id_root=self.id_root, id_parent=self.id_level, id_previous=self.id_previous, id_next=-1, b_root=False)
self.rewrite_class_data(id_item)
else:
Chain.update(**{'c_item': self.entry.get_text()}).where(Chain.id == self.id).execute()
try:
Chain.update(**{'id_next': self.id}).where(Chain.id == self.id_previous).execute()
except:
print("None previous element!")
pass
def write_root(self, combo):
### insert & update text in controls
tree_iter = combo.get_active_iter()
if tree_iter != None:
model = combo.get_model()
name_item = model[tree_iter][:1]
### TODO: update controlls
else:
entry = combo.get_child()
cv_item = entry.get_text() # text from ListStore - ComboBox
idv_parent = 0
idv_previous = 0
# insert data
Chain.create(id=next_id(Chain), c_item=cv_item, id_parent=idv_parent, id_previous=idv_previous, b_root=True)
def on_maximize_toggle(self, action, value):
action.set_state(value)
if value.get_boolean():
self.maximize()
else:
self.unmaximize()
def take_next(self, button, data=None):
"""
Take next item
:param button:
:param data:
:return:
"""
self.b_up.set_visible(True)
self.insert_or_update_row()
text = self.entry.get_text()
self.empty_controlls()
# перезаписываем предыдущий label текущим entry
self.l_prev.set_label(text)
# делаем пустым entry
self.entry.set_text("")
# сохраняем id как id_previous
self.id_previous = self.id
# если есть следующий элемент, то переписываем данные и переписываем контроллы
if self.have_next():
#self.insert_or_update_row()
self.id = self.id_next
self.rewrite_class_data()
self.test_print()
self.write_cont()
self.b_to_update = True # as it is -> just to update item
else:
self.b_to_update = False # as it is -> insert item
# устанавливаем видимость кнопки если есть следующий
if self.have_next():
self.b_down.set_visible(True)
def take_prev(self, button, data=None):
"""
Go to previous items
Change text in items
Work from signal of button
:param button: b_prev
:param data: None
:return:
"""
# перезаписываем entry текущим label previous
# self.entry.set_text(self.l_prev.get_text())
self.insert_or_update_row()
if self.have_previous():
# перезаписываем label next текущим entry
self.l_next.set_label(self.entry.get_text())
self.id = self.id_previous
self.rewrite_class_data()
self.write_cont()
if not self.have_previous():
self.b_up.hide()
# устанавливаем видимость кнопки если есть следующий
# устанавливаем видимость кнопки если есть следующий
if self.have_next():
self.b_down.set_visible(True)
self.b_to_update = True # as it is -> just to update item
else:
self.b_to_update = False # just to insert
def take_child(self, button, data=None):
self.empty_controlls() # clear controlls
# change id_parent
self.id_level = self.id
# show child if it is
if self.have_child(): # if he has a child
self.b_to_update = True # can just update
self.rewrite_class_data(self.select_first_child())
self.write_cont()
# else empty
else:
self.b_to_update = False # can just insert
def take_parent(self, button, data=None):
self.empty_controlls()
# меняем id_parent
self.id = self.id_level
self.rewrite_class_data() # переписываем данные
def on_name_combo_changed(self, combo):
"""
Function for work with combo
:param combo: control ComboBox
:return:
"""
tree_iter = combo.get_active_iter()
if tree_iter != None:
model = combo.get_model()
row_id, name = model[tree_iter][:2]
#print("Selected: ID=%d, name=%s" % (row_id, name))
else:
entry = combo.get_child()
### TODO: insert into database root row
def find_like(self, button=0, event=0, data = None):
"""
Find item which like data in filter
:param button:
:param event:
:param data:
:return:
"""
if not self.entry.get_text() == '': # если происходит фильтрация
self.insert_or_update_row()
entry = self.name_combo.get_child()
if entry.get_text() == '': # make paramount = 0 if it is empty
self.id_paramount = 0
### TODO: find object in hole database -> change parent_id
text_to_find = self.find_entry.get_text().decode("utf-8") # take text from textview
d_all = dict()
l_name = list() # list to find objects
l_name.append(text_to_find)
l_d = list()
# if root is not None -> use in select
entry = self.name_combo.get_child()
paramount_item = entry.get_text()
# print(str(self.id_paramount) + " " + str(paramount_item))
if not self.id_paramount==0 and not paramount_item == "":
for c in Chain.select().where(Chain.id_root == self.id_paramount):
if not c.b_root == 1: # not use root items
d_all[c.id] = c.c_item # take all data in database
else:
for c in Chain.select():
if not c.b_root == True: # not use root items
d_all[c.id] = c.c_item # take all data in database
d_all[c.id] = c.c_item # take all data in database
# print(d_all)
for k, v in d_all.items(): # combine list of values from dictionary
l_d.append(v)
for i in range(len(l_d)):
set_d = set(l_d[i]) # set of each element of dictionary
for j in range(len(l_name)):
set_l = set(l_name[j])
inter = set_d.intersection(set_l)
l_i = len(inter)
if len(inter) == len(set(l_name[j])):
for k, v in d_all.items():
if v == l_d[i]:
res = k # take key
if not self.is_root(int(res)):
self.rewrite_class_data(int(res))
self.write_cont()
self.b_to_update = True # if it is -> just update
if self.have_previous(self.id_previous):
self.b_up.set_visible(True)
else:
self.b_up.hide()
if self.id_previous == 0:
self.b_up.hide()
# self.test_print()
return
#### functions for work with SQLite
def cur_level(self):
"""
Define level of current object
:param n_id: id of current object
:return: id parent
"""
try:
c = Chain.get(Chain.id == self.n_cur)
self.id_level = c.id_parent
except:
pass
def sel_max(self):
"""
Take maximum id for current level
:return: None
"""
# TODO: rewrite for use in multi tree structure
query = Chain.select(fn.max(Chain.id)).where(Chain.id_parent == self.id_level)
return query.count()
def sel_min(self):
"""
Take maximum id for current level
:return: None
"""
# TODO: rewrite for use in multi tree structure
query = Chain.select(fn.min(Chain.id)).where(Chain.id_parent == self.id_level)
for c in Chain.select().where(Chain.id_parent == self.id_level).order_by(Chain.id.asc()):
res = c.id
return res
def insert_root(self, button, data=None):
### insert & update text in controls
tree_iter = self.name_combo.get_active_iter()
try:
entry = self.name_combo.get_child()
cv_item = entry.get_text() # text from ListStore - ComboBox
idv_parent = 0
idv_previous = 0
# insert data
Chain.create(id=next_id(Chain), c_item=cv_item, id_root=self.id_root, id_parent=idv_parent, id_previous=idv_previous, id_next = -1, b_root=True)
self.name_store.append([1, cv_item])
except:
pass
self.empty_controlls()
def insert_row_entry(self, button, data=None):
"""
Function insert row into DB from entry-editor
:param button:
:param data:
:return:
"""
Chain.create(id=next_id(Chain), c_item=self.entry.get_text(), id_root=self.id_root, id_parent=self.id_level, id_previous=self.id_previous, b_root=False)
def insert_row_entry_rewrite(self):
"""
Function insert row into DB from entry-editor
:param button:
:param data:
:return:
"""
Chain.create(id=next_id(Chain), c_item=self.entry.get_text(), id_root=self.id_root, id_parent=self.id_level, id_previous=self.id_previous, b_root=False)
def take_roots(self):
"""
Function take list of roots
:return:
"""
l = list()
try:
for c in Chain.select().where(Chain.b_root==True):
l.append(c.c_item)
except:
pass
return l
# определение id_root
def take_id_root(self):
"""
Function set id of root
:return:
"""
entry = self.name_combo.get_child()
self.root = entry.get_text()
for c in Chain.select().where(Chain.c_item == self.root, Chain.b_root == 1):
self.id_root = c.id
self.id_level = c.id
self.id_previous = 0
self.id_next = -1
self.b_root = False
# set paramount id of chosen root
self.id_paramount = self.id_root
return
# скрываем данные в контроллах
def empty_controlls(self):
"""
Make empty controlls
:return:
"""
text = u""
self.entry.set_text(text)
self.l_prev.set_label(text)
self.l_next.set_label(text)
# Фунции записи контроллов
def write_cont_first(self):
"""
Запись контроллов при первичной загрузке
:return:
"""
self.empty_controlls() # очищаем контроллы
self.entry.set_text(self.get_text_of_id(self.id))
self.l_next.set_label(self.get_text_of_id(self.id_next))
def write_cont(self):
"""
Запись контроллов по текущим параметрам экземпляра
self.id
self.id_next
self.id_previous
:return:
"""
self.empty_controlls() # очищаем контроллы
self.entry.set_text(self.get_text_of_id(self.id))
self.l_next.set_label(self.get_text_of_id(self.id_next))
self.l_prev.set_label(self.get_text_of_id(self.id_previous))
def get_text_of_id(self, id_item):
"""
Определение текста по id
:return:
"""
if id_item == 0 or id_item == -1: # если id пустое то возвращаем пустую запись
return ""
c = Chain.get(Chain.id == id_item)
return c.c_item
def have_next(self):
"""
Определение наличия следующего элемента
:return:
"""
c = Chain.get(Chain.id == self.id)
if c.id_next > 0:
return True
else:
return False
def have_previous(self, id_item=None):
"""
Определение наличия предыдущего элемента
:return:
"""
if id_item==None:
id_item = self.id
for c in Chain.select().where(Chain.id == id_item):
if c.id_previous > 0:
return True
else:
return False
def have_parent(self, id_item=None):
"""
Определение наличия предка
:return:
"""
if id_item==None:
id_item = self.id
c = Chain.select().where(Chain.id == id_item)
if c.id_parent > 0:
return True
else:
return False
def have_child(self, id_parent_item=None):
"""
Определение наличия потомков
:return:
"""
if id_parent_item == None:
id_parent_item = self.id
c = Chain.select().where(Chain.id_root == self.id_root and Chain.id_parent == id_parent_item).order_by(
Chain.id.asc())
if c:
return True
else:
return False
def take_first_child(self, id_root_item=None, id_parent_item=None):
"""
Определение id 1-ого потомка
:return:
"""
if id_root_item == None:
id_root_item = self.id_root
if id_parent_item == None:
id_parent_item = self.id_parent
c = Chain.select().where(Chain.id_root == id_root_item and Chain.id_parent == id_parent_item).order_by(
Chain.id.asc()).limit(1)
return c.id
def is_last(self, id_root_item=None, id_parent_item=None):
"""
Define current item is last in this level
:return:
"""
if id_root_item == None:
id_root_item = self.id_root
if id_parent_item == None:
id_parent_item = self.id_parent
query = self.select(fn.max(Chain.id)).where(Chain.id_root == id_root_item and Chain.id_parent == id_parent_item).order_by(
Chain.id.desc())
#max = query.count()
if self.id == query.count():
return True
else:
return False
def is_root(self, id_item=None):
"""
Define is id_item root
:param id_item:
:return:
"""
if id_item == None:
id_item = self.id
for c in Chain.select().where(Chain.b_root==1):
if c.id == id_item:
return True
return False
def select_first_child(self, id_item=None):
"""
Return id of first child item
:param id_item:
:return:
"""
if id_item == None:
id_item = self.id
for c in Chain.select().where(Chain.id_parent == id_item).order_by(Chain.id.asc()):
print(str(self.id) + " " + str(c.id))
return c.id
def quit(self, button=0, data=None):
"""
Rewrite data and quit from application
:return:
"""
if not self.entry.get_text() == '':
self.insert_or_update_row() # update current row
Gtk.main_quit()
def test_print(self):
"""
Test output data function
:return:
"""
print("self.id - " + str(self.id))
print("self.id_root - " + str(self.id_root))
print("self.id_level - " + str(self.id_level))
print("self.id_previous - " + str(self.id_previous))
print("self.id_next - " + str(self.id_previous))
if __name__ == '__main__':
try:
Chain.create_table() # создаем таблицу
except:
pass
#try:
# test_insert()
#except:
# pass
win = HeaderBarWindow()
#win.connect("delete-event", Gtk.main_quit)
win.connect("delete-event", win.quit)
win.show_all()
Gtk.main()