При нажатии на кнопку «Отмена» удаленный индекс не возвращается, как эту проблему решить?
Есть программа, которая добавляет проект OrCAD. Головной код программы - main.py В программе есть раздел "Редактирование", который изменяет проект, добавляя или удаляя пользователем индексы. В разделе есть кнопка "Отмена", которая удаляет добавленный или добавляет удаленный пользователем индекс. Функция работает только на добавленный, а на удалённый нет. Все осуществляется через RAWData.py . Вот код.
class EditRawData(QWidget):
signalLogConsole = QtCore.pyqtSignal(QtCore.QVariant)
def __init__(self, parent=None, Database=None):
super().__init__(parent, QtCore.Qt.WindowType.Window)
self.DataBase = Database
self.__buildInterface()
def populateTreeFromDatabase(self):
# Создаем defaultdict для хранения сгруппированных данных
grouped_data = defaultdict(list)
# Проходим через каждую строку результата запроса к базе данных
while self.query.next():
product1 = self.query.value(0)
product2 = self.query.value(1)
product3 = self.query.value(2)
product4 = self.query.value(3)
product5 = self.query.value(4)
product6 = self.query.value(5)
#
# Проверяем, есть ли уже product1 в defaultdict, если нет, то добавляем его
if product1 not in grouped_data:
grouped_data[product1] = []
grouped_data[product1].append((product2, product3, product4, product5, product6))
# Проходим по сгруппированным данным и сортируем продукты в каждой группе с использованием естественной сортировки на основе product2
for product1, products in grouped_data.items():
sorted_products = natsorted(products, key=lambda x: x[0], alg=ns.IGNORECASE)
# Создаем родительский элемент (QStandardItem) для каждого product1 и добавляем его в корень дерева
parent_item = QStandardItem(product1)
self.root_item.appendRow(parent_item)
# Создаем дочерние элементы для каждого значения атрибута продукта и добавляем их в качестве детей родительского элемента
for product in sorted_products:
child_items = [QStandardItem(str(product[i])) for i in range(5)]
parent_item.appendRow(child_items)
def populateTreeFromDatabase(self, key):
self.root_item.removeRows(0, self.root_item.rowCount())
if not self.query.exec(
"SELECT ClassPart, PartReference, ManufacturerPartNumber, Manufacturer, Quantity, Description FROM RAWDATA"):
print("Ошибка выполнения запроса:", self.query.lastError().text())
return
# Создаем defaultdict для хранения сгруппированных данных
grouped_data = defaultdict(list)
# Проходим через каждую строку результата запроса к базе данных
while self.query.next():
product1 = self.query.value(0)
product2 = self.query.value(1)
product3 = self.query.value(2)
product4 = self.query.value(3)
product5 = self.query.value(4)
product6 = self.query.value(5)
# print(key)
if key not in grouped_data:
grouped_data[key] = []
# "SELECT ClassPart, PartReference, ManufacturerPartNumber, Manufacturer, Quantity, Description
if key == 'product1':
j = 0
temp_value = 'product1'
self.tree_model.setHorizontalHeaderLabels(["PartReference", "ManufacturerPartNumber", "Manufacturer", "Quantity", "Description"])
self.root_item = self.tree_model.invisibleRootItem()
grouped_data[product1].append((product2, product3, product4, product5, product6))
elif key == 'product2':
j = 0
temp_value = 'product2'
self.tree_model.setHorizontalHeaderLabels(["ClassPart", "ManufacturerPartNumber", "Manufacturer", "Quantity", "Description"])
self.root_item = self.tree_model.invisibleRootItem()
grouped_data[product2].append((product1, product3, product4, product5, product6))
elif key == 'product4':
j = 1
temp_value = 'product4'
self.tree_model.setHorizontalHeaderLabels(["ClassPart", "PartReference", "ManufacturerPartNumber", "Quantity", "Description"])
self.root_item = self.tree_model.invisibleRootItem()
grouped_data[product4].append((product1, product2, product3, product5, product6))
else:
print("ERROR!!!")
# Проходим по сгруппированным данным и сортируем продукты в каждой группе с использованием естественной сортировки на основе product2
for key, products in grouped_data.items():
sorted_products = natsorted(products, key=lambda x: x[j], alg=ns.IGNORECASE)
# Создаем родительский элемент (QStandardItem) для каждого ключа и добавляем его в корень дерева
parent_item = QStandardItem(key)
self.root_item.appendRow(parent_item)
# Создаем дочерние элементы для каждого значения атрибута продукта и добавляем их в качестве детей родительского элемента
for product in sorted_products:
child_items = [QStandardItem(str(product[i])) for i in range(5)]
parent_item.appendRow(child_items)
self.root_item.removeRow(0)
def update_database(self):
election_model = self.tree_view.selectionModel()
selected_indexes = selection_model.selectedIndexes()
product = []
parent_index = selected_indexes[0].parent()
parent_item = self.tree_model.itemFromIndex(parent_index)
class_part = parent_item.text()
for index in selected_indexes:
item = self.tree_model.itemFromIndex(index)
prod_value = item.text()
print(prod_value)
product.append(prod_value)
if self.query.exec(f"UPDATE RAWDATA SET PartReference = '{product[0]}', ManufacturerPartNumber = '{product[1]}', Manufacturer = '{product[2]}', Quantity = '{product[3]}', Description = '{product[4]}' WHERE ClassPart = '{class_part}' AND PartReference = '{prev_indexes[0]}' AND ManufacturerPartNumber = '{prev_indexes[1]}'"):
print("Data updated successfully")
for i, value in enumerate(product):
item = self.tree_model.itemFromIndex(selected_indexes[i])
item.setText(value)
self.tree_model.setData(selected_indexes[i], value)
# Обновление данных в представлении (TreeView)
self.tree_view.setModel(self.tree_model)
else:
print("Ошибка выполнения запроса:", self.query.lastError().text())
def update_database(self):
selection_model = self.tree_view.selectionModel()
selected_indexes = selection_model.selectedIndexes()
product = []
parent_index = selected_indexes[0].parent()
parent_item = self.tree_model.itemFromIndex(parent_index)
class_part = parent_item.text()
for index in selected_indexes:
item = self.tree_model.itemFromIndex(index)
prod_value = item.text()
print(prod_value)
product.append(prod_value)
if self.query.exec(f"UPDATE RAWDATA SET PartReference = '{product[0]}', ManufacturerPartNumber = '{product[1]}', Manufacturer = '{product[2]}', Quantity = '{product[3]}', Description = '{product[4]}' WHERE ClassPart = '{class_part}' AND PartReference = '{prev_indexes[0]}' AND ManufacturerPartNumber = '{prev_indexes[1]}'"):
print("Data updated successfully")
# Обновление данных в модели
for i, value in enumerate(product):
item = self.tree_model.itemFromIndex(selected_indexes[i])
item.setText(value)
self.tree_model.setData(selected_indexes[i], value)
# Обновление данных в представлении (TreeView)
self.tree_view.setModel(self.tree_model)
else:
print("Ошибка выполнения запроса:", self.query.lastError().text())
def __buildInterface(self):
self.setWindowTitle("Просмотр и редактирование данных")
self.resize(700, 500)
#=====Кнопки управления============
self.btnAddRecord = QPushButton("Добавить запись")
self.btnDelRecord = QPushButton("Удалить запись")
self.btnClassification = QPushButton("Классификация")
self.btnGroup = QPushButton("Группировка")
self.btnCancel = QPushButton("Отмена")
self.hbox1 = QHBoxLayout()
self.hbox1.addWidget(self.btnAddRecord)
self.hbox1.addWidget(self.btnDelRecord)
self.hbox1.addWidget(self.btnClassification)
self.hbox1.addWidget(self.btnGroup)
self.hbox1.addWidget(self.btnCancel)
self.btnDelRecord.clicked.connect(self.delRecord)
self.btnAddRecord.clicked.connect(self.addRecord)
self.btnClassification.clicked.connect(self.Classification)
self.btnCancel.clicked.connect(self.Cancel)
self.btnGroup.clicked.connect(self.Group)
# ====================================
# переключатели для управления группировкой
self.hboxRadio = QHBoxLayout()
self.rbtnNoGroup = QRadioButton("Без группировки")
self.rbtnTypeGroup = QRadioButton("Группировка по типу")
self.rbtnPartNumber = QRadioButton("Группировка по наименованию")
self.rbtnManufacturerGroup = QRadioButton("Группировка по производителю")
self.hboxRadio.addWidget(self.rbtnNoGroup)
self.hboxRadio.addWidget(self.rbtnTypeGroup)
self.hboxRadio.addWidget(self.rbtnPartNumber)
self.hboxRadio.addWidget(self.rbtnManufacturerGroup)
self.rbtnNoGroup.setChecked(True)
self.rbtnNoGroup.toggled.connect(self.Group)
self.rbtnTypeGroup.toggled.connect(self.Group)
self.rbtnPartNumber.toggled.connect(self.Group)
self.rbtnManufacturerGroup.toggled.connect(self.Group)
#===================================
#создаем модель
self.stm = QtSql.QSqlTableModel(self)
self.stm.setTable("RAWDATA")
#self.stm.setSort(2, QtCore.Qt.SortOrder.AscendingOrder)
self.stm.select()
self.tree_view = QtWidgets.QTreeView()
self.table_view = QtWidgets.QTableView()
self.tree_model = QStandardItemModel()
self.tree_model.setHorizontalHeaderLabels(["PartReference", "ManufacturerPartNumber", "Manufacturer", "Quantity" , "Description"])
self.root_item = self.tree_model.invisibleRootItem()
self.upload = QSqlQuery()
self.query = QSqlQuery()
# Получение данных из базы данных
#if not self.query.exec(
# "SELECT ClassPart, PartReference, ManufacturerPartNumber, Manufacturer, Quantity, Description FROM RAWDATA"):
# print("Ошибка выполнения запроса:", self.query.lastError().text())
# return
self.populateTreeFromDatabase('product1')
self.tree_view.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection)
self.tree_view.setModel(self.tree_model)
self.selection_model = self.tree_view.selectionModel()
self.selection_model.selectionChanged.connect(self.on_selection_changed)
column = 0
self.table_view.setIndexWidget(self.tree_view.model().index(0, column), self.tree_view)
self.vbox1 = QVBoxLayout()
self.vbox1.addLayout(self.hbox1)
self.vbox1.addLayout(self.hboxRadio)
self.vbox1.addWidget(self.tree_view)
self.setLayout(self.vbox1)
def on_selection_changed(self, selected, deselected):
# Очистим список при смене выделения
prev_indexes.clear()
# Получим список выбранных индексов и сохраним их
for index in selected.indexes():
text = index.data() # Получаем текстовое значение из ячейки
# print(text)
prev_indexes.append(text) # Добавляем текстовое значение в список
# print(prev_indexes)
def keyPressEvent(self, event):
# попробовать перебиндить на ctrl + s или f5
if event.key() == QtCore.Qt.Key.Key_F5:
self.save_button_event()
# комбинация shift + левый клик
if event.key() == QtCore.Qt.Key.Key_Shift:
self.tree_view.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection)
super().keyPressEvent(event)
def save_button_event(self):
self.update_database()
print("Save!")
def keyReleaseEvent(self, event):
if event.key() == QtCore.Qt.Key.Key_Shift:
self.tree_view.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection)
super().keyReleaseEvent(event)
def delRecord(self):
selection_model = self.tree_view.selectionModel()
selected_indexes = selection_model.selectedIndexes()
rows_to_remove = []
values_to_remove = []
parent_values = []
for index in selected_indexes:
row = index.row()
if row not in rows_to_remove:
rows_to_remove.append(row)
item = self.tree_model.itemFromIndex(index)
value = item.text()
if index.column() < 2:
values_to_remove.append(value)
print(value)
for row in sorted(rows_to_remove, reverse=True):
parent_index = selected_indexes[0].parent()
parent_item = self.tree_model.itemFromIndex(parent_index)
parent_text = parent_item.text()
parent_values.append(parent_text)
self.tree_model.removeRow(row, parent_index)
print(parent_text)
print(row)
x = 0
for i in range((len(values_to_remove))//2):
print(temp_value)
if temp_value == 'product1':
if self.query.exec(f"DELETE FROM RAWDATA WHERE PartReference = '{values_to_remove[x]}' AND ManufacturerPartNumber = '{values_to_remove[x+1]}'"):
print("Data updated successfully")
else:
print("Ошибка выполнения запроса:", self.query.lastError().text())
elif temp_value == 'product2':
if self.query.exec(f"DELETE FROM RAWDATA WHERE ClassPart = '{values_to_remove[x]}' AND ManufacturerPartNumber = '{values_to_remove[x + 1]}' AND PartReference = '{parent_values[(x//2)]}'"):
print("Data updated successfully")
else:
print("Ошибка выполнения запроса:", self.query.lastError().text())
elif temp_value == 'product4':
if self.query.exec(f"DELETE FROM RAWDATA WHERE ClassPart = '{values_to_remove[x]}' AND PartReference = '{values_to_remove[x + 1]}'"):
print("Data updated successfully")
else:
print("Ошибка выполнения запроса:", self.query.lastError().text())
x = x + 2
self.tree_model.submit()
self.tree_view.setModel(self.tree_model)
def addRecord(self):
# Создаем стек отмены, если он еще не был создан
if not hasattr(self, 'undo_stack'):
self.undo_stack = []
selection_model = self.tree_view.selectionModel()
selected_indexes = selection_model.selectedIndexes()
# Определяем родительскую группу выделенной строки
parent_index = selected_indexes[0].parent()
# Создаем пустую строку для отображения в TreeView
empty_row = [QStandardItem('-') for _ in range(5)] # Определение переменной empty_row
parent_item = self.tree_model.itemFromIndex(parent_index)
row_position = selected_indexes[0].row() + 1
# Проверяем, есть ли уже такие элементы в дереве
existing_items = [parent_item.child(i) for i in range(parent_item.rowCount())]
if empty_row not in existing_items:
if row_position <= parent_item.rowCount(): # Проверяем, что позиция вставки превышает количество элементов в родительском элементе
parent_item.insertRow(row_position, empty_row)
# Сохраняем информацию о последней добавленной записи
self.last_added_index = (parent_item, row_position)
# Обновляем представление (TreeView) с новой строкой
self.tree_view.setModel(self.tree_model)
# Если операция добавления прошла успешно, добавляем действие в стек отмены
self.undo_stack.append(('add', parent_item, row_position, empty_row.copy())) # Создаем копию empty_row
else:
print("Ошибка: Позиция вставки превышает количество элементов в родительском элементе.")
else:
print("Ошибка: Пустая строка уже существует в данной группе.")
class_part = parent_item.text()
if self.query.exec(f"INSERT INTO RAWDATA (ClassPart, PartReference, ManufacturerPartNumber, Manufacturer, Quantity, Description) VALUES ('{class_part}', '-', '-', '-', '-', '-')"):
print("Data add successfully")
if self.query.exec(
f"UPDATE RAWDATA SET PartReference = '-', ManufacturerPartNumber = '-', Manufacturer = '-', Quantity = '-', Description = '-' WHERE ClassPart = '{class_part}' AND PartReference = '-' AND ManufacturerPartNumber = '-'"):
print("Data updated successfully")
else:
print("Ошибка выполнения запроса:", self.query.lastError().text())
else:
print("Ошибка выполнения запроса:", self.query.lastError().text())
#процедура проведения классификации компонентов. Вызывается в режиме редактирования сырых импортированных данных.
#Работает исключительно с базой данных
def Classification(self):
if self.DataBase.open():
query = QtSql.QSqlQuery()
query_new = QtSql.QSqlQuery()
query_upd = QtSql.QSqlQuery()
query.clear()
#формируем запрос из таблицы импортированных данных
Result = query.exec("SELECT ClassPart, PartReference FROM RAWDATA")
if Result:
while query.next():
currentClassPart = query.value("ClassPart")
currentPartReference = query.value("PartReference")
#если текущий класс не равен классу из таблицы ALPHABETPOSITION в базе данных
# то обновляем поле
tableClassPart = getRefdesDescription(query_new,getRefdes(currentPartReference))
if tableClassPart != currentClassPart:
query_upd.exec("UPDATE RAWDATA SET ClassPart = " + '"' + tableClassPart + '"' + " WHERE PartReference = " + '"' + currentPartReference + '"')
self.signalLogConsole.emit(
["W: Изменение классификации позиции " + currentPartReference + ": было " + currentClassPart + " стало " + tableClassPart])
self.stm.select()
def Group(self):
global temp_value
if self.rbtnTypeGroup.isChecked():
temp_value = 'product1'
self.populateTreeFromDatabase('product1')
elif self.rbtnNoGroup.isChecked():
self.stm.setFilter("")
self.stm.select()
elif self.rbtnPartNumber.isChecked():
temp_value = 'product2'
self.populateTreeFromDatabase('product2')
elif self.rbtnManufacturerGroup.isChecked():
temp_value = 'product4'
self.populateTreeFromDatabase('product4')
grouped_data = defaultdict(list)
while self.query.next():
product1 = self.query.value(3)
product2 = self.query.value(0)
product3 = self.query.value(1)
product4 = self.query.value(2)
product5 = self.query.value(4)
product6 = self.query.value(5)
if product1 not in grouped_data:
grouped_data[product1] = []
grouped_data[product1].append((product2, product3, product4, product5, product6))
for product1, products in grouped_data.items():
sorted_products = natsorted(products, key=lambda x: x[0], alg=ns.IGNORECASE)
parent_item = QStandardItem(product1)
self.root_item.appendRow(parent_item)
for product in sorted_products:
child_items = [QStandardItem(str(product[i])) for i in range(5)]
parent_item.appendRow(child_items)
self.tree_view.setModel(self.tree_model)
column = 0
self.table_view.setIndexWidget(self.tree_view.model().index(0, column), self.tree_view)
self.table_view.update() # Обновляем представление TableView
self.tree_view.update() # Обновляем представление TreeView
#print("DEBUG70:")
#self.mod = QtSql.QSqlQuery("SELECT id, PartReference, ManufacturerPartNumber, Manufacturer FROM RAWDATA GROUP BY ManufacturerPartNumber")
#self.mod.exec()
#self.model = QtSql.QSqlQueryModel()
#self.model.setQuery(self.mod)
#self.editTab.setModel(self.model)
#print("DEBUG71:")
##self.stm.select()
#print("DEBUG72:")
return 0
def Cancel(self):
try:
if hasattr(self, 'undo_stack') and self.undo_stack:
# Получаем все действия из стека отмены
actions_to_undo = list(reversed(self.undo_stack))
print("Действия для отмены:", actions_to_undo) # Выводим содержимое стека отмены для отладки
for action, parent_item, row_position, removed_row in actions_to_undo:
if action == 'add':
parent_item.removeRow(row_position)
print("Добавление элемента отменено.")
elif action == 'del':
# Получаем родительский индекс и вставляем удаленную строку
self.tree_model.insertRow(row_position, removed_row)
print("Удаление элемента отменено.")
print("Содержимое стека отмены перед очисткой:", self.undo_stack)
self.undo_stack.clear() # Очищаем стек отмены
self.tree_model.layoutChanged.emit()
else:
print("Отмененное действие не найдено в стеке отмены.")
except Exception as e:
print("Ошибка при отмене:", e)
Очень прошу помочь мне с этой проблемой. Нужно, чтобы кнопка "Отмена" отменяла все действия, совершенные пользователем.