Compare commits

..

10 Commits

Author SHA1 Message Date
svkalinin
3937be3cb5 Добавил зависимости. Исправил ошибку при запуске. Добавил описание 2022-10-12 14:39:03 +03:00
svk
77da13fbaa Edit record procedure fixing has began 2019-04-12 16:36:18 +03:00
Sergey
8ca81184a9 Some changes 2018-11-10 14:53:46 +03:00
Sergey Kalinin
7bcca92697 README.md отредактирован онлайн на Bitbucket 2018-03-03 10:59:19 +00:00
Sergey Kalinin
1e0950083e test.py remove 2017-12-11 13:35:38 +03:00
Sergey Kalinin
4efc72e071 Some changes 2017-12-04 13:19:04 +03:00
Sergey Kalinin
9904d5b4e9 Sqlite databases create fixed. Added some template 2017-12-04 13:11:55 +03:00
svk28
9cc5c6cd57 Add "edit records" procedure 2017-06-06 15:35:14 +03:00
svk28
c6a63b972e Fixed "delete record" procedure. Added "Edit records" function 2017-06-02 14:01:14 +03:00
svk28
b2de966586 Fixed "delete record" procedure. Added "Edit records" function 2017-05-12 16:25:32 +03:00
10 changed files with 542 additions and 217 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea/
__pycache__/

View File

@ -1 +1,41 @@
Это зачаток программы управления данными на основе описания структуры в json формате.
# Это зачаток программы управления данными на основе описания структуры в json формате.
Пишется на python3 в качестве графической библиотеки Qt5.
# Использование
По умолчанию используется шаблон и БД 'dm'. Для того чтобы изменить БД, создаем JSON шаблон согласно описания (см. примеры в db_template).
Затем данный файл нужно скопировать в '~/.dm/db_template/', следущим шагом меняем в конфиге 'dm.cfg' имя БД на имя вашего шаблона без расширения json, т.е. к примеру для работы с книжным каталогом (файл db_template/library.json) файл настроек будет выглядеть следующим оюбразом:
```
[DataBase]
db_type = sqlite
db_hostname = localhost
db_name = library
db_user = dm
db_password = password
[Directory]
work_dir = /home/svkalinin/.dm
template_dir = /home/svkalinin/.dm/db_template
```
После этого можно запустить программу, при первом запуске будет создана БД согласно шаблона:
```
python3 gui.py
```
# СУБД
Пока реализована работа с Mysql (или Mariadb) и sqlite (по умолчанию). Для из менения типа БД в конфиге меняем 'dbtype':
```
dbtype = sqlite
или
dbtype = mysql
```

Binary file not shown.

View File

@ -6,8 +6,8 @@
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "int(6)",
"fDescription": "UID",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
@ -60,8 +60,8 @@
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "int(6)",
"fDescription": "UID",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
@ -85,7 +85,7 @@
{
"fName": "users_id",
"fDescription": "Владелец",
"fType": "int(6)",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["users.id", "last_name, name, middle_name"]
@ -93,7 +93,7 @@
{
"fName": "parent_id",
"fDescription": "Родительский документ",
"fType": "int(6)",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["docs.id", "doc_name"]
@ -106,8 +106,8 @@
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "int(6)",
"fDescription": "UID",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
@ -122,7 +122,7 @@
},
{
"fName": "city",
"fDescription": "Населеннй пункт",
"fDescription": "Населенный пункт",
"fType": "varchar(200)",
"index": "no",
"autoIncrement": "no",
@ -147,54 +147,116 @@
]
},
{
"tableName": "test",
"tableDescription": "Шляпа",
"tableName": "items",
"tableDescription": "Оборудование",
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "int(11)",
"fDescription": "UID",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
},
{
"fName": "user",
"fDescription": "Юзер шляпы",
"fType": "int(11)",
"fName": "item_name",
"fDescription": "Наименование",
"fType": "VARCHAR(100)",
"index": "no",
"autoIncrement": "no",
"relation": ["users.id", "last_name, name, middle_name"]
"relation": []
},
{
"fName": "item_address",
"fDescription": "Место расположения",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["address.id", "city, street, house"]
},
{
"fName": "item_type",
"fDescription": "Тип",
"fType": "CHAR(20)",
"index": "no",
"autoIncrement": "no",
"relation": ["itemtype.id", "type_name"]
},
{
"fName": "inventory_number",
"fDescription": "Инвентарный номер",
"fType": "CHAR(10)",
"index": "no",
"autoIncrement": "no",
"relation": []
}
]
},
{
"tableName": "test2",
"tableDescription": "Шляпа2",
"tableName": "itemtype",
"tableDescription": "Тип оборудования",
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "int(11)",
"fDescription": "UID",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
},
{
"fName": "ins_date",
"fDescription": "Дата добавления",
"fName": "type_name",
"fDescription": "Тип",
"fType": "VARCHAR(100)",
"index": "no",
"autoIncrement": "no",
"relation": []
}
]
},
{
"tableName": "itemrelocation",
"tableDescription": "Перемещения",
"fieldList": [
{
"fName": "id",
"fDescription": "UID",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
},
{
"fName": "item_id",
"fDescription": "Оборудование",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["items.id", "item_name, inventory_number"]
},
{
"fName": "source_place",
"fDescription": "Откуда",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["address.id", "city, street, house"]
},
{
"fName": "destination_place",
"fDescription": "Куда",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["address.id", "city, street, house"]
},
{
"fName": "create_datetime",
"fDescription": "Дата перемещения",
"fType": "DATETIME",
"index": "no",
"autoIncrement": "no",
"relation": []
},
{
"fName": "user",
"fDescription": "Юзер шляпы",
"fType": "int(11)",
"index": "no",
"autoIncrement": "no",
"relation": ["users.id", "last_name, name, middle_name"]
}
]
}

141
db_template/library.json Normal file
View File

@ -0,0 +1,141 @@
{
"tables": [
{
"tableName": "publisher",
"tableDescription": "Издательство",
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
},
{
"fName": "publisher_name",
"fDescription": "Наименование",
"fType": "char(20)",
"index": "no",
"autoIncrement": "no",
"relation": []
}
]
},
{
"tableName": "authors",
"tableDescription": "Авторы",
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
},
{
"fName": "last_name",
"fDescription": "Фамилия",
"fType": "char(20)",
"index": "no",
"autoIncrement": "no",
"relation": []
},
{
"fName": "name",
"fDescription": "Имя",
"fType": "char(20)",
"index": "no",
"autoIncrement": "no",
"relation": []
},
{
"fName": "middle_name",
"fDescription": "Отчество",
"fType": "char(20)",
"index": "no",
"autoIncrement": "no",
"relation": []
}
]
},
{
"tableName": "books",
"tableDescription": "Книги",
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
},
{
"fName": "book_name",
"fDescription": "Название",
"fType": "varchar(100)",
"index": "yes",
"autoIncrement": "no",
"relation": []
},
{
"fName": "description",
"fDescription": "Описание",
"fType": "varchar(200)",
"index": "no",
"autoIncrement": "no",
"relation": []
},
{
"fName": "authors_id",
"fDescription": "Автор",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["authors.id", "last_name, name, middle_name"]
},
{
"fName": "publisher_id",
"fDescription": "Издательство",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["publisher.id", "publisher_name"]
},
{
"fName": "category_id",
"fDescription": "Жанр",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["category.id", "category_name"]
}
]
},
{
"tableName": "category",
"tableDescription": "Жанр",
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
},
{
"fName": "category_name",
"fDescription": "Название жанра",
"fType": "varchar(100)",
"index": "yes",
"autoIncrement": "no",
"relation": []
}
]
}
]
}

View File

@ -0,0 +1,72 @@
{
"tables": [
{
"tableName": "type",
"tableDescription": "Тип элемента",
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "INT(6)",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
},
{
"fName": "name",
"fDescription": "Тип элемента",
"fType": "char(20)",
"index": "yes",
"autoIncrement": "no",
"relation": []
}
]
},
{
"tableName": "items",
"tableDescription": "Элементы",
"fieldList": [
{
"fName": "id",
"fDescription": "Номер п.п.",
"fType": "INTEGER",
"index": "PRIMARY KEY",
"autoIncrement": "yes",
"relation": []
},
{
"fName": "item_name",
"fDescription": "Наименование",
"fType": "varchar(100)",
"index": "yes",
"autoIncrement": "no",
"relation": []
},
{
"fName": "description",
"fDescription": "Описание",
"fType": "varchar(200)",
"index": "no",
"autoIncrement": "no",
"relation": []
},
{
"fName": "type_id",
"fDescription": "Тип",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": ["type.id", "name"]
},
{
"fName": "count",
"fDescription": "Количество",
"fType": "INTEGER",
"index": "no",
"autoIncrement": "no",
"relation": []
}
]
}
]
}

127
dm.py
View File

@ -3,8 +3,8 @@ import json, os, configparser, shutil, re
from datetime import datetime, date, time
def firstInit():
# Инициализация переменныхб создание конфигруационного файла, копирование шаблонов
global db_type, db_hostname, db_user, db_password, db_name, template_file, db_type
# Инициализация переменных создание конфигруационного файла, копирование шаблонов
global db_type, db_hostname, db_user, db_password, db_name, template_file, db_type, template_dir
config = configparser.RawConfigParser()
# проверяем тип ОС
if os.name == "nt":
@ -19,7 +19,7 @@ def firstInit():
cfg_file = os.path.join(cfg_dir, 'dm.cfg')
# создадим файл конфигурации
config.add_section('DataBase')
config.set('DataBase', 'db_type', 'mysql')
config.set('DataBase', 'db_type', 'sqlite')
config.set('DataBase', 'db_hostname', 'localhost')
config.set('DataBase', 'db_name', 'dm')
config.set('DataBase', 'db_user', 'dm')
@ -39,6 +39,7 @@ def firstInit():
config.read(cfg_file)
work_dir = config.get('Directory', 'work_dir')
global template_dir
template_dir = config.get('Directory', 'template_dir')
db_type = config.get('DataBase', 'db_type')
db_hostname = config.get('DataBase', 'db_hostname')
@ -59,11 +60,13 @@ def firstInit():
else:
os.mkdir(template_dir)
# копируем и читаем файл шаблон БД
template_file = os.path.join(template_dir, 'tables.json')
template = config.get('DataBase', 'db_name') + '.json'
template_file = os.path.join(template_dir, template)
if os.path.isfile(template_file):
print("Template file already exists")
print("Template file already exists " + template_file)
else:
shutil.copy('tables.json', template_file)
shutil.copy('db_template/dm.json', template_file)
def dbConnect():
@ -167,17 +170,18 @@ def createTables(tbl_list):
i = i + 1
dbTablesDescriptionList.append(one_Table_descr)
dbTablesStructList.append(one_Table_struct)
#print(qwery_create)
print(qwery_create)
c.execute(qwery_create)
return dbTablesNamesList
def initDBstructure():
global dbTablesDescriptionList, template_file, tblNamesList
table_list = open(template_file, "r", encoding="utf-8")
data = json.load(table_list, encoding="utf-8")
tbl_list = data["tables"]
tblNamesList = createTables(tbl_list)
# data = json.load(table_list, encoding="utf-8")
data = json.load(table_list)
#tbl_list = data["tables"]
#tblNamesList = createTables(tbl_list)
tblNamesList = createTables(data["tables"])
return tblNamesList
# выборка данных из заданной таблицы
@ -186,6 +190,7 @@ def selectData(tbl):
# если юольше 1 поля добавить CONCAT
qwery = "SELECT "
subqwery = ""
#print(dbTablesStructList)
for item in dbTablesStructList:
if item[0] == tbl:
for field in item[1]:
@ -204,20 +209,17 @@ def selectData(tbl):
else:
subqwery = table1
# составляем подзапрос и подменяем им поле в запросе
print(subqwery)
if db_type == "mysql":
field_replace = field_replace.replace(",", ",' ',")
#subqwery = "(SELECT CONCAT(" + field_replace + ") FROM " + subqwery + " WHERE " + table1 + "." + field1 + "=" + tbl + "." + field + ") AS " + field
# subqwery = "(SELECT CONCAT({}) FROM {} WHERE {}.{}={}.{}) AS {}"\
# .format(field_replace,subqwery,table1,field1,tbl,field,field)
subqwery = "(SELECT CONCAT('>', {}, '<', {}) FROM {} WHERE {}.{}={}.{}) AS {}"\
.format(field,field_replace,subqwery,table1,field1,tbl,field,field)
subqwery = "(SELECT CONCAT('>', {}.{}, '<', {}) FROM {} WHERE {}.{}={}.{}) AS {}"\
.format(tbl,field,field_replace,subqwery,table1,field1,tbl,field,field)
elif db_type == "sqlite":
field_replace = field_replace.replace(",", " || ' ' ||")
#subqwery = "(SELECT (" + field_replace + ") FROM " + subqwery +" WHERE "+ table1 + "." + field1 +"="+ tbl +"."+ field +") AS " + field
subqwery = "(SELECT ({}) FROM {} WHERE {}.{}={}.{}) AS {}" \
.format(field_replace, subqwery, table1, field1, tbl, field, field)
qwery = qwery.replace(field, subqwery)
#qwery = qwery.rstrip(',') + " FROM " + tbl + " LIMIT 10000"
qwery = '{} FROM {} LIMIT 10000'.format(qwery.rstrip(','), tbl)
print(qwery)
c.execute(qwery)
@ -226,8 +228,6 @@ def selectData(tbl):
# получаем на вход имя таблицы и возвращаем список заголовков полей
def getTableStructure(tbl):
global dbTablesDescriptionList, dbTablesStructList
#print(dbTablesDescriptionList)
#print(dbTablesStructList)
for item in dbTablesDescriptionList:
if item[0] == tbl:
return item[1]
@ -235,8 +235,6 @@ def getTableStructure(tbl):
# Получаем список названий полей и типов для заданной таблицы
def getFields(tbl):
global dbTablesDescriptionList, dbTablesStructList
#print(dbTablesDescriptionList)
#print(dbTablesStructList)
for item in dbTablesStructList:
if item[0] == tbl:
#print(item[1])
@ -265,26 +263,20 @@ def fieldTypeConvert(ftype):
fType = 'character'
else:
fType = ftype
#print(fType)
return fType
def getFieldType(tbl, field):
global dbTablesDescriptionList, dbTablesStructList
#print(dbTablesDescriptionList)
#print(dbTablesStructList)
for item in dbTablesStructList:
if item[0] == tbl:
for i in item[1]:
if i[0] == field:
fType = i[1]
return fType
#print(item[1])
#return item[1]
def getRelationsForField(tblSearch, fieldName):
global dbTablesStructList, c, db_type
#print("Ищем связи для:" +tblSearch +','+ fieldName)
#print(dbTablesStructList)
#searchField = tblSearch + '.' + fieldName
dataList = []
for item in dbTablesStructList:
#print(item)
@ -295,16 +287,10 @@ def getRelationsForField(tblSearch, fieldName):
if tblName == tblSearch:
# выдергиваем из списка название и поле нужной нам таблицы
if rel[0] == fieldName:
#print('table - ' + tblName)
##print('field - ' + fieldName)
#print('relation - ' + str(rel))
l = rel[1][0].split('.')
relTable = l[0]
relField = l[1]
replaceFields = rel[1][1]
# print('reltable - ' + relTable)
#print('relfield - ' + relField)
#print('relrelation - ' + replaceFields)
return [relTable, relField, replaceFields]
return "q"
@ -314,10 +300,6 @@ def insertDataIntoBD(dataList):
global c, conn
dbConnect()
tableName = dataList[0]
#print(dataList)
#datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M")
#qwery = 'INSERT INTO ' + tableName + ' ('
qwery = 'INSERT INTO {} ('.format(tableName)
qweryData = ''
qweryField = ''
@ -326,30 +308,23 @@ def insertDataIntoBD(dataList):
#print(fType)
# проверяем если значение поля пустое то в запрос оно не включается
if item[1] != '':
#qweryField = qweryField + item[0] + ','
qweryField = '{}{},'.format(qweryField,item[0])
if fType == 'integer':
#qweryData = qweryData + '' + item[1] + ','
qweryData = '{}{},'.format(qweryData,item[1])
elif fType == 'datetime':
# преобразуем дату всяко разно
dt = datetime.strptime(item[1], "%d.%m.%y %H:%M")
item[1] = str(dt)
#qweryData = qweryData + '\'' + item[1] + '\','
qweryData = "{}'{}',".format(qweryData,item[1])
elif fType == 'date':
d = item[1].split('.')
#myDate = d[2] + '-' + d[1] + '-' + d[1]
myDate = '{}-{}-{}'.format(d[2], d[1], d[1])
item[1] = str(myDate)
#qweryData = qweryData + '\'' + item[1] + '\','
qweryData = "{}'{}',".format(qweryData, item[1])
else:
#qweryData = qweryData + '\'' + item[1] + '\','
qweryData = "{}'{}',".format(qweryData, item[1])
#qwery = qwery + qweryField.rstrip(',') + ')' + ' VALUES (' + qweryData.rstrip(',') + ');'
qwery = '{}{}) VALUES ({});'.format(qwery, qweryField.rstrip(','), qweryData.rstrip(','))
print(qwery)
@ -385,20 +360,16 @@ def selectDataFromDB(tblName, fieldName, fieldValue):
# составляем подзапрос и подменяем им поле в запросе
if db_type == "mysql":
fieldReplace = fieldReplace.replace(",", ",' ',")
#subqwery = "(SELECT CONCAT(" + fieldReplace + ") FROM " + subqwery + " WHERE " + table1 + "." + field1 + "=" + tblName + "." + field + ") AS " + field
subqwery = '(SELECT CONCAT({}) FROM {} WHERE {}.{}={}.{}) AS {}'\
.format(fieldReplace, subqwery, table1, field1, tblName, field, field)
elif db_type == "sqlite":
fieldReplace = fieldReplace.replace(",", " || ' ' ||")
#subqwery = "(SELECT (" + fieldReplace + ") FROM " + subqwery +" WHERE "+ table1 + "." + field1 +"="+ tblName +"."+ field +") AS " + field
subqwery = '(SELECT ({}) FROM {} WHERE {}.{}={}.{}) AS {}'\
.format(fieldReplace, subqwery, table1, field1, tblName, field, field)
#print("---" + subqwery)
qwery = qwery.replace(field, subqwery)
#qwery = qwery.rstrip(',') + " FROM " + tblName + " LIMIT 10000"
#qwery = qwery.rstrip(',') + " FROM " + tblName + " WHERE " + fieldName + '=' + fieldValue
qwery = '{} FROM {} WHERE {}={}'.format(qwery.rstrip(','), tblName, fieldName, fieldValue)
print(qwery)
c.execute(qwery)
return c.fetchall()
@ -435,11 +406,11 @@ def getTablesDescriptionOfName(tblName):
def deleteRecordsFromDB(tableName, valueList):
global c, db_type, conn
qwery = 'DELETE FROM {} WHERE '.format(tableName)
#print(qwery)
#print(valueList)
subQwery=''
i = 0
for item in getFields(tableName):
print(item[0], item[1])
#print(item[0], item[1])
if item[1] == 'integer':
#subQwery = subQwery + item[0] + '=' + valueList[i] + ' AND '
subQwery = '{}{}={} AND '.format(subQwery, item[0], valueList[i])
@ -449,11 +420,61 @@ def deleteRecordsFromDB(tableName, valueList):
i += 1
qwery += subQwery
qwery = qwery.rstrip('AND ').replace("='None'", " IS NULL")
qwery = qwery.rstrip('AND ').replace("=None", " IS NULL")
qwery = qwery.rstrip('AND ').replace("=NULL", " IS NULL")
print(qwery)
c.execute(qwery)
conn.commit()
return
# Редактирование записи в БД
def editDataIntoBD(dataList):
global c, conn
dbConnect()
tableName = dataList[0]
qwery = 'UPDATE {} SET '.format(tableName)
#qweryData = ''
qweryField = ''
qweryWhere = ''
for item in dataList[1]:
fType = getFieldType(tableName, item[0])
# проверяем если значение поля пустое то в запрос оно не включается
if item[1] != '':
qweryField = '{}{}='.format(qweryField,item[0])
if fType == 'integer':
qweryField = '{}{},'.format(qweryField,item[1])
elif fType == 'datetime':
# преобразуем дату всяко разно
dt = datetime.strptime(item[1], "%d.%m.%y %H:%M")
item[1] = str(dt)
qweryField = "{}'{}',".format(qweryField,item[1])
elif fType == 'date':
d = item[1].split('.')
myDate = '{}-{}-{}'.format(d[2], d[1], d[1])
item[1] = str(myDate)
qweryField = "{}'{}',".format(qweryField, item[1])
else:
qweryField = "{}'{}',".format(qweryField, item[1])
qwery = '{}{} WHERE {};'.format(qwery, qweryField.rstrip(','), qweryWhere)
print(qwery)
#c.execute(qwery)
#conn.commit()
#c.close()
return
# Вывод структуры таблицы из базы данных
def getTableStructureFromDB(tableName):
global c, db_type, conn
if db_type == 'sqlite':
qwery = 'PRAGMA table_info({})'.format(tableName)
fieldsList = c.execute(qwery).fetchall()
#for row in fieldsList:
# print(row)
return(fieldsList)
#initDBstructure()
firstInit()
dbConnect()

148
gui.py Normal file → Executable file
View File

@ -5,6 +5,8 @@ import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
#from PyQt5.QtWidgets import (QMainWindow, QTextEdit, QAction, QFileDialog, QApplication)
#from PyQt5.QtGui import QIcon
#import pymysql
import dm
@ -33,6 +35,7 @@ class MyTable(QTableWidget):
def contextMenuEvent(self, event):
Rmenu = QMenu(self)
addRecord = Rmenu.addAction("Добавить запись")
editRecord = Rmenu.addAction("Редактировать запись")
delRecord = Rmenu.addAction("Удалить запись")
showRelations = Rmenu.addAction("Показать связанные документы")
action = Rmenu.exec_(self.mapToGlobal(event.pos()))
@ -46,6 +49,13 @@ class MyTable(QTableWidget):
elif action == addRecord:
print("Добавить запись ")
addNewRecord()
elif action == editRecord:
row = self.rowAt(event.pos().y())
col = self.columnAt(event.pos().x())
#print("Удаляем row:%d, col:%d" % (row, col))
item = self.item(self.currentIndex().row(), self.currentIndex().column())
print("Редактировать запись ")
self.editCurrentRecord(row)
elif action == showRelations:
item = self.item(self.currentIndex().row(), self.currentIndex().column())
showRelationsRecords(self.currentIndex().column(), item.text())
@ -69,6 +79,24 @@ class MyTable(QTableWidget):
#print(self.item(row, i).text())
i += 1
deleteRecord(self, row, self.dbTableName, valueList)
# редактирование записи
def editCurrentRecord(self, row):
valueList = []
#print(self.dbTableName, row, self.statusTip())
i = 0
#print(self.columnCount())
#print(self.verticalHeaderItem(row).text(), '---')
while i < self.columnCount():
if self.item(row, i):
val = self.item(row, i).text()
print(val)
valueList.append(str(val))
else:
valueList.append('')
i += 1
print(valueList)
editRecord(self, row, self.dbTableName, valueList)
# Диалог выбора связанных данных
class RelationDataView(QMainWindow):
@ -170,9 +198,13 @@ class EditForm(QMainWindow):
lblList = []
editList = []
def __init__(self, tblName, tblDescr):
def __init__(self, tblName, tblDescr, action):
super().__init__()
self.tblName = tblName
self.action = action
if action == 'edit':
self.setWindowTitle("Редактирование записи")
elif action == 'add':
self.setWindowTitle("Добавление записи")
#self.setGeometry(300, 300, 400, 400)
scroll_widget = QWidget()
@ -240,6 +272,7 @@ class EditForm(QMainWindow):
edit.setValidator(integerValidator)
else:
edit = QLineEdit()
# создаём список полей и соответсвующих им QEdit
self.widgetsList.append([fName, edit])
@ -247,15 +280,15 @@ class EditForm(QMainWindow):
hboxEdit.addWidget(lbl2)
hboxEdit.addWidget(edit)
# добавляем к полю кнопку для вызова связанных
print(fName)
#print(fName)
listRelationTableAndField = dm.getRelationsForField(tblName, fName)
print(listRelationTableAndField)
#print(listRelationTableAndField)
if listRelationTableAndField != 'q':
tableRelation = listRelationTableAndField[0]
fieldRelation = listRelationTableAndField[1]
replaceField = listRelationTableAndField[2]
print(tableRelation + '-' + fieldRelation +'-'+replaceField)
#print(tableRelation + '-' + fieldRelation +'-'+replaceField)
btnRelations = QPushButton('...')
btnRelations.setFixedWidth(30)
# добавляем название виджета в список соответсвия кнопок и таблиц БД
@ -284,7 +317,10 @@ class EditForm(QMainWindow):
listFieldData.append([item[0], item[1].text()])
listData.append(listFieldData)
#print(listData)
if self.action == 'add':
dm.insertDataIntoBD(listData)
elif self.action == 'edit':
dm.editDataIntoBD(listData)
self.close()
# выбираем данные для связанной таблицы
def openRelationTable(self):
@ -356,29 +392,38 @@ def addDataIntoTable(dbTableName, tblDataWidget, data='NULL'):
dbFieldRelationValueList = []
for key in data:
n = 0
#print(fieldNames, '\n', key)
print(key)
for item in key:
#print(m, n, item)
listRelationTableAndField = dm.getRelationsForField(dbTableName, fieldNames[n][0])
#print('--->', fieldNames[n][1])
if listRelationTableAndField != 'q':
#print('yahooooo', fieldNames[n][0])
itemString = dm.re.search('>([0-9])+<(.+)', str(item))
# кривой алгоритм, надо придумывать что-то другое
itemString = dm.re.search('>([0-9]+)<(.+)', str(item))
if itemString:
#print('--->',itemString.groups())
newitem = QTableWidgetItem(itemString.groups()[1])
dbFieldRelationValueList.append([newitem, m, n, itemString.groups()[0], itemString.groups()[1]])
else:
newitem = QTableWidgetItem(str(item))
dbFieldRelationValueList.append([newitem, m, n, 'None', 'None'])
else:
# преобразуем все типы в строку
newitem = QTableWidgetItem(str(item))
#print(fieldNames[n][0])
#print(str(item))
# проверяем название поля и если это id то данные вносим в заголовок строки
# таким образом внесем уникальный ID в чётко определенное поле
#if fieldNames[n][0] == "id":
# tblDataWidget.setVerticalHeaderItem(m, QTableWidgetItem(str(item)))
tblDataWidget.setItem(m, n, newitem)
n += 1
m += 1
#print(dbFieldRelationValueList)
# Показ менб и панели инструментов
# Показ меню и панели инструментов
def showMenuToolbar(window):
openFileAction = QAction(QIcon('img/open.gif'), 'Открыть', window)
openFileAction.setShortcut('Ctrl+O')
openFileAction.setStatusTip('Открыть')
openFileAction.triggered.connect(openTemplate)
newAction = QAction(QIcon('img/new.gif'), 'Добавить', window)
newAction.setShortcut('Ins')
newAction.setStatusTip('Добавить')
@ -387,45 +432,46 @@ def showMenuToolbar(window):
deleteAction = QAction(QIcon('img/delete.gif'), 'Удалить', window)
deleteAction.setShortcut('Del')
deleteAction.setStatusTip('Удалить')
# deleteAction.triggered.connect(window.qqqq)
exitAction = QAction(QIcon('img/exit.gif'), 'Выход', window)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Выход')
exitAction.triggered.connect(window.close)
exitAction.setStatusTip('Выйти')
exitAction.triggered.connect(exit)
structureAction = QAction(QIcon('img/table.gif'), 'Показать структуру', window)
#structureAction.setShortcut('Ctrl+Q')
structureAction.setStatusTip('Показать структуру')
structureAction.triggered.connect(showStructure)
cutAction = QAction(QIcon('img/cut.gif'), 'Вырезать', window)
cutAction.setShortcut('Ctrl+X')
cutAction.setStatusTip('Вырезать')
# cutAction.triggered.connect(window.close)
copyAction = QAction(QIcon('img/copy.gif'), 'Копировать', window)
copyAction.setShortcut('Ctrl+С')
copyAction.setStatusTip('Копировать')
# copyAction.triggered.connect(window.close)
pasteAction = QAction(QIcon('img/paste.gif'), 'Вставить', window)
pasteAction.setShortcut('Ctrl+V')
pasteAction.setStatusTip('Вставить')
# pasteAction.triggered.connect(window.close)
findAction = QAction(QIcon('img/find.gif'), 'Копировать', window)
findAction = QAction(QIcon('img/find.gif'), 'Поиск', window)
findAction.setShortcut('Ctrl+F')
findAction.setStatusTip('Искать')
# findAction.triggered.connect(window.close)
printAction = QAction(QIcon('img/print.gif'), 'Печатать', window)
printAction.setShortcut('Ctrl+P')
printAction.setStatusTip('Печатать')
# printAction.triggered.connect(window.close)
window.statusBar()
menubar = window.menuBar()
fileMenu = menubar.addMenu('&Файл')
fileMenu.addAction(openFileAction)
fileMenu.addAction(newAction)
fileMenu.addAction(deleteAction)
fileMenu.addAction(printAction)
fileMenu.addAction(structureAction)
fileMenu.addAction(exitAction)
editMenu = menubar.addMenu('&Редактирование')
@ -439,6 +485,7 @@ def showMenuToolbar(window):
# toolbar = window.addToolBar('Редактирование')
toolbar = window.addToolBar('Панель инструментов')
#toolbar.addAction(openFileAction)
toolbar.addAction(newAction)
toolbar.addAction(deleteAction)
toolbar.addAction(copyAction)
@ -457,7 +504,7 @@ def addNewRecord():
tblName = i[0]
#print(tblName)
editForm = EditForm(tblName, tblDescr)
editForm = EditForm(tblName, tblDescr, 'add')
editForm.show()
#editForm.setParent(listDBTables)
#editForm.show()
@ -497,27 +544,58 @@ def showRelationsRecords(fieldIndex, fieldValue):
addDataIntoTable(tbl, tblRelationsData, qweryResult)
# пишем название активной таблицы БД в строку статуса
tblRelationsData.setStatusTip(tbl)
# Удаление записи
def deleteRecord(tableWidget, row, dbTableName, valueList):
global dbFieldRelationValueList
#print(tableWidget, row, dbTableName, valueList)
#print(self.columnCount())
i = 0
#print(valueList)
for val in valueList:
#print(tableWidget.item(row, i))
#print('Значение из таблицы-->',valueList[i])
for item in dbFieldRelationValueList:
if item[0] == tableWidget.item(row, i):
#print(item[0])
#print('еще значение',tableWidget.item(row, i).text(), item[0])
value = item[3]
valueList.pop(i)
valueList.insert(i, item[3])
i += 1
#print(valueList)
dm.deleteRecordsFromDB(dbTableName, valueList)
# Редактирование записи
def editRecord(tableWidget, row, dbTableName, valueList):
global dbFieldRelationValueList
editForm = EditForm(dbTableName, dm.getTablesDescriptionOfName(dbTableName), 'edit')
editForm.show()
i = 0
for val in valueList:
for item in dbFieldRelationValueList:
if item[0] == tableWidget.item(row, i):
value = item[3]
valueList.pop(i)
valueList.insert(i, item[3])
val = item[3]
#print(val)
editForm.widgetsList[i][1].setText(val)
i += 1
#print(valueList)
#print(editForm.widgetsList)
#dm.deleteRecordsFromDB(dbTableName, valueList)
def showStructure():
global tabRelationsData, listDBTables
tblDescr = listDBTables.model().data(listDBTables.currentIndex())
i = 0
for i in dbTablesList:
if i[1] == tblDescr:
tblName = i[0]
print(tblName)
#print(dm.getTableStructure(tblName))
print(dm.getFields(tblName))
print(dm.getTableStructureFromDB(tblName))
def openTemplate():
global template_file, template_dir
template_file = QFileDialog.getOpenFileName(None, 'Open file', template_dir)[0]
dm.initDBstructure(template_file)
def main():
global dbTablesList, listDBTables, tabRelationsData, dbFieldRelationValueList
@ -555,12 +633,12 @@ def main():
print(dm.getTablesNameOfDescription(listDBTables.model().data(listDBTables.currentIndex())))
mainWin.setGeometry(300, 300, 800, 600)
mainWin.setGeometry(300, 300, 1024, 800)
mainWin.setWindowTitle('Data manipulator')
mainWin.show()
return app.exec_()
if __name__ == '__main__':
main()
print(template_dir)

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
pymysql==1.0.2
PyQt5==5.15.7

93
test.py
View File

@ -1,93 +0,0 @@
#!/usr/bin/env python
#############################################################################
##
## Copyright (C) 2013 Riverbank Computing Limited.
## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
## All rights reserved.
##
## This file is part of the examples of PyQt.
##
## $QT_BEGIN_LICENSE:BSD$
## You may use this file under the terms of the BSD license as follows:
##
## "Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are
## met:
## * Redistributions of source code must retain the above copyright
## notice, this list of conditions and the following disclaimer.
## * Redistributions in binary form must reproduce the above copyright
## notice, this list of conditions and the following disclaimer in
## the documentation and/or other materials provided with the
## distribution.
## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
## the names of its contributors may be used to endorse or promote
## products derived from this software without specific prior written
## permission.
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
## $QT_END_LICENSE$
##
#############################################################################
from PyQt5.QtCore import QModelIndex, Qt
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QApplication, QItemDelegate, QSpinBox, QTableView
class SpinBoxDelegate(QItemDelegate):
def createEditor(self, parent, option, index):
editor = QSpinBox(parent)
editor.setMinimum(0)
editor.setMaximum(100)
return editor
def setEditorData(self, spinBox, index):
value = index.model().data(index, Qt.EditRole)
spinBox.setValue(value)
def setModelData(self, spinBox, model, index):
spinBox.interpretText()
value = spinBox.value()
model.setData(index, value, Qt.EditRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
model = QStandardItemModel(4, 4)
tableView = QTableView()
tableView.setModel(model)
delegate = SpinBoxDelegate()
tableView.setItemDelegate(delegate)
for row in range(4):
for column in range(4):
index = model.index(row, column, QModelIndex())
model.setData(index, (row + 1) * (column + 1))
tableView.setWindowTitle("Spin Box Delegate")
tableView.show()
sys.exit(app.exec_())