#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * #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("Добавить запись") 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 == 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.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(): valueList.append(self.item(row, i).text()) print(self.item(row, i).text()) i += 1 dm.deleteRecordsFromDB(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): super().__init__() self.tblName = tblName 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 item 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) dm.insertDataIntoBD(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(tbl_name, tblDataWidget, data='NULL'): global tblList, 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(listTables.model().data(listTables.currentIndex())), tblDataWidget) # Пишем название активной таблицы БД в строку статуса, эадакая замена глобальной переменной # как оно и где вылезет будем посмотреть. tblDataWidget.setStatusTip(dm.getTablesNameOfDescription(listTables.model().data(listTables.currentIndex()))) tblDataWidget.dbTableName = dm.getTablesNameOfDescription(listTables.model().data(listTables.currentIndex())) # вставка данных в табличный виджет def addDataIntoTable(tbl_name, tblDataWidget, data='NULL'): global tblList, tabRelationsData if data == 'NULL': data = dm.selectData(tbl_name) fieldNames = dm.getTableStructure(tbl_name) #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 item in fieldNames: fName = item[0] fDescr = item[1] #### определяем наличие связей и добавляем дополнительные столбцы ####print(fName, fDescr, dm.getRelationsForField(tbl_name, fName)) # установка заголовков столбцов таблицы tblDataWidget.setHorizontalHeaderItem(n, QTableWidgetItem(fDescr)) n += 1 n = 0 for key in data: m = 0 for item in key: # преобразуем все типы в строку newitem = QTableWidgetItem(str(item)) tblDataWidget.setItem(m, n, newitem) n += 1 m += 1 # Показ менб и панели инструментов def showMenuToolbar(window): 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('Удалить') # deleteAction.triggered.connect(window.qqqq) exitAction = QAction(QIcon('img/exit.gif'), 'Выход', window) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Выход') exitAction.triggered.connect(window.close) 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.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(newAction) fileMenu.addAction(deleteAction) fileMenu.addAction(printAction) 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(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 listTables, tblRelationsData tblDescr = listTables.model().data(listTables.currentIndex()) for i in tblList: if i[1] == tblDescr: tblName = i[0] #print(tblName) editForm = EditForm(tblName, tblDescr) editForm.show() #editForm.setParent(listTables) #editForm.show() #editForm.setParent(tblRelationsData) #editForm = Example() # отображение списка связанных данных def showRelationsRecords(fieldIndex, fieldValue): global tabRelationsData, listTables tblDescr = listTables.model().data(listTables.currentIndex()) i=0 if tabRelationsData.count(): while i <= tabRelationsData.count(): tabRelationsData.removeTab(i) i = i+1 for i in tblList: 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 item 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 main(): global tblList, listTables, tabRelationsData app = QApplication(sys.argv) mainWin = QMainWindow() workArea = QWidget() mainWin.setCentralWidget(workArea) hbox = QHBoxLayout() listTables = QListWidget() listTables.setFrameShape(QFrame.StyledPanel) listTables.setFixedWidth(200) # выводим список таблиц в левом поле tblData = MyTable() tblData.setFrameShape(QFrame.StyledPanel) tabRelationsData = QTabWidget() #tabRelationsData.setFrameShape(QFrame.StyledPanel) splitter1 = QSplitter(Qt.Horizontal) splitter1.addWidget(listTables) splitter2 = QSplitter(Qt.Vertical) splitter2.addWidget(tblData) splitter2.addWidget(tabRelationsData) splitter1.addWidget(splitter2) hbox.addWidget(splitter1) workArea.setLayout(hbox) showMenuToolbar(mainWin) listTables.itemClicked.connect(lambda: clickTablesList(dm.getTablesNameOfDescription(listTables.model().data(listTables.currentIndex())), tblData)) tblList = dm.initDBstructure() for i in tblList: listTables.addItem(i[1]) #tblData = QTableWidget() print(dm.getTablesNameOfDescription(listTables.model().data(listTables.currentIndex()))) mainWin.setGeometry(300, 300, 800, 600) mainWin.setWindowTitle('Data manipulator') mainWin.show() return app.exec_() if __name__ == '__main__': main()