0

I'm currently in the process of creating a database for an inventory system using PySide6 and PyQT6 (QTDesigner). I'm almost finished with it, but I've encountered a bug. Whenever I try to delete or edit a row, only the first row gets selected, which is causing some unexpected behavior.

Here's the code for ButtonWidget:

class ButtonWidget(QWidget):
def __init__(self, parent=None):
    super().__init__(parent)
    # Create two buttons and add them to a horizontal layout
    edit_icon = QtGui.QIcon("Icons/edit.svg")
    self.edit_button = QtWidgets.QPushButton()
    self.edit_button.setIcon(edit_icon)
    delete_icon = QtGui.QIcon("Icons/trash3.svg")
    self.delete_button = QtWidgets.QPushButton()
    self.delete_button.setIcon(delete_icon)
    layout = QHBoxLayout()
    layout.addWidget(self.delete_button)
    layout.addWidget(self.edit_button)
    self.setLayout(layout)

Here's the code for EditDialog:

class EditDialog(QDialog):
def __init__(self, parent=None, row_data=None):
    super(EditDialog, self).__init__(parent)
    self.ui = Ui_Edit_Dialog()
    self.ui.setupUi(self)

    # Set the initial values of the line edits
    self.ui.item_id_lineEdit.setText(str(row_data[0]))
    self.ui.product_name_lineEdit.setText(row_data[1])
    self.ui.category_lineEdit.setText(row_data[2])
    self.ui.quantity_lineEdit.setText(row_data[3])
    self.ui.price_lineEdit.setText(row_data[4])

Here's the code for MainWindow:

class MainWindow(QMainWindow):
    def __init__(self): 
        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # PAGE 1
        self.ui.btn_menu_1.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_1))
        # PAGE 2
        self.ui.btn_menu_2.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_2))
        # PAGE 3
        self.ui.btn_menu_3.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_3))
        # PAGE 4
        self.ui.btn_menu_4.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_4))
        self.show()
        # function of page 4
        self.ui.add_btn.clicked.connect(self.add_btn_function)
        self.loadData()
        self.ui.search_lineEdit.returnPressed.connect(self.search_function)
    def add_btn_function(self):
        product_name = self.ui.product_name_lineEdit.text()
        category = self.ui.category_lineEdit.text()
        quantity = self.ui.quantity_lineEdit.text()
        price = self.ui.price_lineEdit.text()

        if not all([product_name, category, quantity, price]):
            QMessageBox.warning(self, 'Error', 'Please fill in all fields.')
            return
        else:
            cursor.execute(f"INSERT INTO items (productname, category, quantity, price) VALUES (?, ?, ?, ?)", (product_name, category, quantity, price))
            conn.commit()
            print("Successfully created items: ", product_name, category, quantity, price)
            # Clear line edits
            self.ui.product_name_lineEdit.setText('')
            self.ui.category_lineEdit.setText('')
            self.ui.quantity_lineEdit.setText('')
            self.ui.price_lineEdit.setText('')
            # refresh table
            self.loadData()
    def search_function(self):
        search_term = self.ui.search_lineEdit.text()
        if not search_term:
            self.loadData()
            return

        # Clear current contents of the table
        self.ui.tableWidget.setRowCount(0)

        # Query the database for rows matching the search term
        cursor.execute(f"SELECT * FROM items WHERE productname LIKE '%{search_term}%' OR category LIKE '%{search_term}%'")
        rows = cursor.fetchall()

        # Add matching rows to the table
        for row in rows:
            row_position = self.ui.tableWidget.rowCount()
            self.ui.tableWidget.insertRow(row_position)
            for i, item in enumerate(row):
                self.ui.tableWidget.setItem(row_position, i, QTableWidgetItem(str(item)))
    # Load Data
    def loadData(self):
        connection = sqlite3.connect("DBMS.db")
        cursor = connection.cursor()
        query = "SELECT * FROM items"
        result = cursor.execute(query)
        self.ui.tableWidget.setRowCount(0)
        tablerow = 0
        for row_number, row_data in enumerate(result):
            self.ui.tableWidget.insertRow(row_number)
            for column_number, data in enumerate(row_data):
                self.ui.tableWidget.setItem(row_number, column_number, QTableWidgetItem(str(data))) 

            btn_widget = ButtonWidget()
            btn_widget.delete_button.clicked.connect(lambda row=row_number: self.delete_function(row))
            btn_widget.edit_button.clicked.connect(lambda row=row_number: self.edit_function(row))
            self.ui.tableWidget.setCellWidget(row_number, 5, btn_widget)

    # Delete Function
    def delete_function(self, row):
        

        # Show confirmation message box
        confirm = QMessageBox.question(self, 'Confirm Delete', 'Are you sure you want to delete this item?', QMessageBox.Yes | QMessageBox.No)
        if confirm == QMessageBox.Yes:
            # get the ID of the record to delete
            item_id = self.ui.tableWidget.item(row, 0).text()
            # delete the record from the database
            cursor.execute(f"DELETE FROM items WHERE id = ?", (item_id))
            conn.commit()
            print(f"Successfully deleted item with ID: {item_id}")
            # remove the row from the table
            self.ui.tableWidget.removeRow(row)

    def edit_function(self,row):
        # Get the data from the selected row
        row_data = []
        for column in range(self.ui.tableWidget.columnCount()):
            item = self.ui.tableWidget.item(row, column)
            if item is not None:
                row_data.append(item.text())
            else:
                row_data.append('')
        
        # Open the edit dialog for the selected row
        dialog = EditDialog(self, row_data=row_data)
        if dialog.exec() == QDialog.Accepted:
            # Get the modified values from the dialog
            productname = dialog.ui.product_name_lineEdit.text()
            category = dialog.ui.category_lineEdit.text()
            quantity = dialog.ui.quantity_lineEdit.text()
            price = dialog.ui.price_lineEdit.text()
            
            # Update the values in the database
            connection = sqlite3.connect("DBMS.db")
            cursor = connection.cursor()
            query = ("UPDATE items SET productname = ?, category = ?, quantity = ?, price = ? WHERE id = ?")
            values = (productname, category, quantity, price, item_id) # row+1 because ids start from 1 in the database
            cursor.execute(query, values)
            connection.commit()
            
            # Refresh the table widget
            self.loadData()
musicamante
  • 41,230
  • 6
  • 33
  • 58
Hydroxy21
  • 17
  • 6
  • 1
    You got yourself a nice SQL injection here `cursor.execute(f"SELECT * FROM items WHERE productname LIKE '%{search_term}%' OR category LIKE '%{search_term}%'")` – ניר Feb 19 '23 at 05:56
  • what do you mean? – Hydroxy21 Feb 19 '23 at 06:06
  • 1. Read more about "SQL injection"; 2. the [`clicked`](//doc.qt.io/qt-6/qabstractbutton.html#clicked) signal *always* has a default argument based on the `checked` state, so you *must* ignore it if you want to provide a valid argument for the scope of the loop; 3. please always check the post preview before submitting, and ensure that when you post code it has the proper syntax; 4. always try to provide examples in single code blocks (instead of forcing us to "fix" your code in order to test it, while risking to make it invalid in the process); 5. don't call `self.show()` in the `__init__()`; – musicamante Feb 19 '23 at 06:23
  • 6. unless you are forced to use extremely old Python 2 versions (or you **really** know what you're doing) just use `super().()` (without the `self`; please do some research on the matter); 7. Qt provides a pretty well designed [QtSql](https://doc.qt.io/qt-6/qtsql-index.html) sub-module, which is usually reliable for generic usage; unless you *really* need specialized, custom behavior, you should normally prefer that, instead of trying to use higher level classes such as QTableWidget, since they would only make things more complicated and prone to errors and issues. – musicamante Feb 19 '23 at 06:48

0 Answers0