#!/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("Таблица: " + 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 = ''+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("Таблица: " + 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)