data-manipulation/gui.py

645 lines
26 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/python3
# -*- coding: utf-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
class MyTable(QTableWidget):
def __init__(self, dbTableName = 'NULL', *args):
self.dbTableName = dbTableName
QTableWidget.__init__(self, *args)
self.resizeColumnsToContents()
self.horizontalHeader().setSortIndicatorShown(True)
self.horizontalHeader().setStretchLastSection(True)
self.horizontalHeader().setCascadingSectionResizes(True)
# равномерное изменение ширины столбцов
self.horizontalHeader().setSectionResizeMode(1)
# изменение ширины столбцов по размеру текста
#self.horizontalHeader().setSectionResizeMode(3)
self.horizontalHeader().setStyleSheet("color: black; font-weight:bold; background-color: lightblue;")
self.setWordWrap(True)
self.setSortingEnabled(True)
# обработка нажатия мышой на таблице
self.doubleClicked.connect(self.on_click)
# self.doubleClicked.connect(self.contextMenuEvent(self.cursor()))
# показ контекстного меню
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()))
if action == delRecord:
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("Запись" + item.text())
self.delRecord(row)
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())
def on_click(self):
#print(self.cursor().pos().x())
#self.contextMenuEvent(self.cursor())
for currentQTableWidgetItem in self.selectedItems():
print(currentQTableWidgetItem, currentQTableWidgetItem.row(), currentQTableWidgetItem.column(), currentQTableWidgetItem.text())
# удаление записи
def delRecord(self, row):
valueList = []
#print(self.dbTableName, row, self.statusTip())
i = 0
#print(self.columnCount())
while i < self.columnCount():
if self.item(row, i):
valueList.append(self.item(row, i).text())
else:
valueList.append('')
#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):
lblList = []
editList = []
def __init__(self, tblName, editWidget, field, lbl, rel):
super().__init__()
self.tblName = tblName
self.editWidget = editWidget
self.lbl = lbl
self.tblDescr = dm.getTablesDescriptionOfName(tblName)
self.field = field
self.rel = rel
self.setWindowTitle(self.tblDescr)
self.setGeometry(300, 300, 640, 480)
scroll_widget = QWidget()
self.general_layout = QVBoxLayout()
#scroll_area = QScrollArea()
l = QVBoxLayout()
headBox = QHBoxLayout()
lblHeader = QLabel("<B>Таблица: " + self.tblDescr)
l.addLayout(headBox)
headBox.addWidget(lblHeader)
hboxData = QHBoxLayout()
self.tableViewData = MyTable(tblName)
#print(self.widgetsList)
hboxData.addWidget(self.tableViewData)
hboxBtn = QHBoxLayout()
btnOk = QPushButton('Ок')
btnOk.clicked.connect(lambda: self.ok())
btnCancel = QPushButton('Закрыть')
btnCancel.clicked.connect(lambda: self.close())
l.addLayout(hboxData)
hboxBtn.addWidget(btnOk)
hboxBtn.addWidget(btnCancel)
l.addLayout(hboxBtn)
self.general_layout.addLayout(l)
scroll_widget.setLayout(self.general_layout)
#scroll_area.setWidget(scroll_widget)
self.setCentralWidget(scroll_widget)
addDataIntoTable(self.tblName, self.tableViewData)
def ok(self):
#listData = [self.tblName]
#listFieldData = []
# выбираем из выделенной строки значение нужной ячейки
# соответсвующей полю связанной таблицы
for currentQTableWidgetItem in self.tableViewData.selectedItems():
lstFields = dm.getTableStructure(self.tblName)
i = 0
for fieldItem in lstFields:
if fieldItem[0] == self.field:
#print(fieldItem, i)
self.editWidget.setText(self.tableViewData.item(currentQTableWidgetItem.row(), i).text())
# пррячем воле ввода
self.editWidget.hide()
print(self.lbl.text())
# в метку добавляем значения для подставновки соответсвенно связи
txt = '<b>'+str(self.getDataFromTableWidget(currentQTableWidgetItem.row()))
# устанавливаем текст на метку и включаем её
self.lbl.setText(txt)
self.lbl.setVisible(True)
break
i = i+1
self.close()
# получаем данные из таблицы для соответсвующих полей
# для подставновки в форму редактирования(добавления) для связей
def getDataFromTableWidget(self, row):
fields = self.rel.split(',')
lstFields = dm.getTableStructure(self.tblName)
print(fields)
print(lstFields)
txt = ''
for item in fields:
item = item.rstrip(' ').lstrip(' ')
print(item)
i = 0
for fieldItem in lstFields:
if fieldItem[0] == item:
print(item, fieldItem)
print(self.tableViewData.item(row, i).text())
txt = txt + ' ' +self.tableViewData.item(row, i).text()
i = i + 1
print(txt)
return txt
# Форма добавления записи в БД
class EditForm(QMainWindow):
lblList = []
editList = []
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()
self.general_layout = QVBoxLayout()
scroll_area = QScrollArea()
l = QVBoxLayout()
headBox = QHBoxLayout()
lblHeader = QLabel("<B>Таблица: " + tblDescr)
l.addLayout(headBox)
#palet = QPalette(lblHeader.palette())
#lblHeader.setPalette(palet)
headBox.addWidget(lblHeader)
hboxBtn = QHBoxLayout()
btnOk = QPushButton('Записать')
btnOk.clicked.connect(lambda: self.save())
btnCancel = QPushButton('Закрыть')
btnCancel.clicked.connect(lambda: self.close())
# цифровой валидатор, для проверки ввода
integerRegex = QRegExp("^[0-9]+$")
integerValidator = QRegExpValidator(integerRegex, self)
# список соответствия полей и QEdit
self.widgetsList = []
self.btnRelationsList = []
# добавляем динамически поля на форму в зависимости от типов данных
for fName, fType, fAutoIncr in dm.getFields(tblName):
#print(item)
#fName = item[0]
#fType = item[1]
#fAutoIncr = item[2]
fDescr = dm.getFieldDescription(tblName, fName)
hboxEdit = QHBoxLayout()
lbl = QLabel(fDescr)
lbl2 = QLabel(fDescr)
lbl2.hide()
#listRelationTableAndField = []
btnRelations = ''
tableRelation = ''
# создаем поле ввода соответсвующее типу данных
if fType == 'datetime':
edit = QDateTimeEdit()
edit.setCalendarPopup(True)
edit.setFrame(False)
now = QDateTime.currentDateTime()
edit.setDateTime(now)
elif fType == 'date':
edit = QDateEdit()
edit.setCalendarPopup(True)
edit.setFrame(False)
now = QDate.currentDate()
edit.setDate(now)
elif fType == 'time':
edit = QTimeEdit()
edit.setCalendarPopup(True)
edit.setFrame(False)
now = QTime.currentTime()
edit.setTime(now)
elif fType == 'integer':
edit = QLineEdit()
# отключаем поле если оно автоинкрементное
if fAutoIncr == 'AUTOINCREMENT' or fAutoIncr == 'AUTO_INCREMENT':
edit.setDisabled(True)
# проверка на ввод цифр
edit.setValidator(integerValidator)
else:
edit = QLineEdit()
# создаём список полей и соответсвующих им QEdit
self.widgetsList.append([fName, edit])
hboxEdit.addWidget(lbl)
hboxEdit.addWidget(lbl2)
hboxEdit.addWidget(edit)
# добавляем к полю кнопку для вызова связанных
#print(fName)
listRelationTableAndField = dm.getRelationsForField(tblName, fName)
#print(listRelationTableAndField)
if listRelationTableAndField != 'q':
tableRelation = listRelationTableAndField[0]
fieldRelation = listRelationTableAndField[1]
replaceField = listRelationTableAndField[2]
#print(tableRelation + '-' + fieldRelation +'-'+replaceField)
btnRelations = QPushButton('...')
btnRelations.setFixedWidth(30)
# добавляем название виджета в список соответсвия кнопок и таблиц БД
# пиздец извращения =(
self.btnRelationsList.append([btnRelations, tableRelation, fieldRelation, replaceField, edit, lbl2, replaceField])
btnRelations.clicked.connect(lambda: self.openRelationTable())
hboxEdit.addWidget(btnRelations)
l.addLayout(hboxEdit)
#print(self.widgetsList)
hboxBtn.addWidget(btnOk)
hboxBtn.addWidget(btnCancel)
l.addLayout(hboxBtn)
self.general_layout.addLayout(l)
scroll_widget.setLayout(self.general_layout)
scroll_area.setWidget(scroll_widget)
self.setCentralWidget(scroll_area)
def save(self):
listData = [self.tblName]
listFieldData = []
for item in self.widgetsList:
#print(item[0] + ' ' + item[1].text())
if item[1].isEnabled:
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):
sender = self.sender()
# ищем в списке соответсвующую кнопоку и связанные с ней данные
for item in self.btnRelationsList:
if sender == item[0]:
btn = item[0]
tbl = item[1]
fld = item[2]
lbl = item[5]
rel = item[6]
editWidget = item[4]
# выбираем данные из БД
#data = dm.selectData(tbl)
#print(data)
relForm = RelationDataView(tbl, editWidget, fld, lbl, rel)
relForm.show()
#relForm.setParent(self)
# обработка нажатия на списке таблиц БД
def clickTablesList(dbTableName, tblDataWidget, data='NULL'):
global dbTablesList, tabRelationsData
# удаляем все открытые табы
i=0
if tabRelationsData.count():
tabRelationsData.clear()
#print(tabRelationsData.count())
while i <= tabRelationsData.count():
#print(i)
tabRelationsData.removeTab(i)
i += 1
addDataIntoTable(dm.getTablesNameOfDescription(listDBTables.model().data(listDBTables.currentIndex())), tblDataWidget)
# Пишем название активной таблицы БД в строку статуса, эадакая замена глобальной переменной
# как оно и где вылезет будем посмотреть.
tblDataWidget.setStatusTip(dm.getTablesNameOfDescription(listDBTables.model().data(listDBTables.currentIndex())))
tblDataWidget.dbTableName = dm.getTablesNameOfDescription(listDBTables.model().data(listDBTables.currentIndex()))
# вставка данных в табличный виджет
def addDataIntoTable(dbTableName, tblDataWidget, data='NULL'):
global dbTablesList, tabRelationsData, dbFieldRelationValueList
if data == 'NULL':
data = dm.selectData(dbTableName)
fieldNames = dm.getTableStructure(dbTableName)
#print(fieldNames)
# проверка на наличие записей в таблице
if data:
# количество строк
rows = len(data)
# КОЛИЧЕСТВО КОЛОНОК
cols = len(data[0])
else:
rows = 0
cols = len(fieldNames)
n = 0
tblDataWidget.setRowCount(rows)
tblDataWidget.setColumnCount(cols)
for fname, fDescr in fieldNames:
#fName = item[0]
#fDescr = item[1]
#### определяем наличие связей и добавляем дополнительные столбцы
####print(fName, fDescr, dm.getRelationsForField(dbTableName, fName))
# установка заголовков столбцов таблицы
tblDataWidget.setHorizontalHeaderItem(n, QTableWidgetItem(fDescr))
n += 1
m = 0
dbFieldRelationValueList = []
for key in data:
n = 0
print(key)
for item in key:
listRelationTableAndField = dm.getRelationsForField(dbTableName, fieldNames[n][0])
if listRelationTableAndField != 'q':
# кривой алгоритм, надо придумывать что-то другое
itemString = dm.re.search('>([0-9]+)<(.+)', str(item))
if itemString:
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
# Показ меню и панели инструментов
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('Добавить')
newAction.triggered.connect(addNewRecord)
deleteAction = QAction(QIcon('img/delete.gif'), 'Удалить', window)
deleteAction.setShortcut('Del')
deleteAction.setStatusTip('Удалить')
exitAction = QAction(QIcon('img/exit.gif'), 'Выход', window)
exitAction.setShortcut('Ctrl+Q')
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('Вырезать')
copyAction = QAction(QIcon('img/copy.gif'), 'Копировать', window)
copyAction.setShortcut('Ctrl+С')
copyAction.setStatusTip('Копировать')
pasteAction = QAction(QIcon('img/paste.gif'), 'Вставить', window)
pasteAction.setShortcut('Ctrl+V')
pasteAction.setStatusTip('Вставить')
findAction = QAction(QIcon('img/find.gif'), 'Поиск', window)
findAction.setShortcut('Ctrl+F')
findAction.setStatusTip('Искать')
printAction = QAction(QIcon('img/print.gif'), 'Печатать', window)
printAction.setShortcut('Ctrl+P')
printAction.setStatusTip('Печатать')
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('&Редактирование')
editMenu.addAction(copyAction)
editMenu.addAction(cutAction)
editMenu.addAction(pasteAction)
editMenu.addAction(printAction)
helpMenu = menubar.addMenu('&Помощь')
# toolbar = window.addToolBar('Редактирование')
toolbar = window.addToolBar('Панель инструментов')
#toolbar.addAction(openFileAction)
toolbar.addAction(newAction)
toolbar.addAction(deleteAction)
toolbar.addAction(copyAction)
toolbar.addAction(cutAction)
toolbar.addAction(pasteAction)
toolbar.addAction(findAction)
toolbar.addAction(printAction)
# toolbar.addAction(exitAction)
# форма для добавления новой записи
def addNewRecord():
global listDBTables, tblRelationsData
tblDescr = listDBTables.model().data(listDBTables.currentIndex())
for i in dbTablesList:
if i[1] == tblDescr:
tblName = i[0]
#print(tblName)
editForm = EditForm(tblName, tblDescr, 'add')
editForm.show()
#editForm.setParent(listDBTables)
#editForm.show()
#editForm.setParent(tblRelationsData)
#editForm = Example()
# отображение списка связанных данных
def showRelationsRecords(fieldIndex, fieldValue):
global tabRelationsData, listDBTables
tblDescr = listDBTables.model().data(listDBTables.currentIndex())
i=0
if tabRelationsData.count():
while i <= tabRelationsData.count():
tabRelationsData.removeTab(i)
i = i+1
for i in dbTablesList:
if i[1] == tblDescr:
tblName = i[0]
fieldNamesList = dm.getTableStructure(tblName)
fieldName = fieldNamesList[fieldIndex][0]
# print(fieldNamesList)
# print(fieldName)
# print("showRelations")
# print(tblName)
# print(fieldValue)
data = dm.selectRelationsDataFromDB(tblName, fieldName, fieldValue)
#print(data)
for tbl, qweryResult in data:
#tbl = item[0]
#qweryResult = item[1]
if qweryResult:
#print('report' + str(qweryResult) + 'report')
tblRelationsData = MyTable(tbl)
tblRelationsData.setParent(tabRelationsData)
tabRelationsData.addTab(tblRelationsData, dm.getTablesDescriptionOfName(tbl))
addDataIntoTable(tbl, tblRelationsData, qweryResult)
# пишем название активной таблицы БД в строку статуса
tblRelationsData.setStatusTip(tbl)
# Удаление записи
def deleteRecord(tableWidget, row, dbTableName, valueList):
global dbFieldRelationValueList
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])
i += 1
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
dbFieldRelationValueList = []
app = QApplication(sys.argv)
mainWin = QMainWindow()
workArea = QWidget()
mainWin.setCentralWidget(workArea)
hbox = QHBoxLayout()
listDBTables = QListWidget()
listDBTables.setFrameShape(QFrame.StyledPanel)
listDBTables.setFixedWidth(200)
# выводим список таблиц в левом поле
tblData = MyTable()
tblData.setFrameShape(QFrame.StyledPanel)
tabRelationsData = QTabWidget()
#tabRelationsData.setFrameShape(QFrame.StyledPanel)
splitter1 = QSplitter(Qt.Horizontal)
splitter1.addWidget(listDBTables)
splitter2 = QSplitter(Qt.Vertical)
splitter2.addWidget(tblData)
splitter2.addWidget(tabRelationsData)
splitter1.addWidget(splitter2)
hbox.addWidget(splitter1)
workArea.setLayout(hbox)
showMenuToolbar(mainWin)
listDBTables.itemClicked.connect(lambda: clickTablesList(dm.getTablesNameOfDescription(listDBTables.model().data(listDBTables.currentIndex())), tblData))
dbTablesList = dm.initDBstructure()
for i in dbTablesList:
listDBTables.addItem(i[1])
#tblData = QTableWidget()
print(dm.getTablesNameOfDescription(listDBTables.model().data(listDBTables.currentIndex())))
mainWin.setGeometry(300, 300, 1024, 800)
mainWin.setWindowTitle('Data manipulator')
mainWin.show()
return app.exec_()
if __name__ == '__main__':
main()
print(template_dir)