diff --git a/GUI/GUI_main.py b/GUI/GUI_main.py new file mode 100644 index 0000000..9886664 --- /dev/null +++ b/GUI/GUI_main.py @@ -0,0 +1,262 @@ +from PyQt5 import uic +import time +import student +import file_operations +from PyQt5.QtCore import QTimer,Qt +from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox,QProgressBar, QTextEdit, QTableWidget, QTableWidgetItem, QHeaderView,QLineEdit,QPushButton + +class Mygui(QMainWindow): + + def __init__(self): + super(Mygui, self).__init__() + + # Load the .ui file + uic.loadUi("GUI\\pages.ui", self) + + self.tableWidget = self.findChild(QTableWidget, "tableWidget") + self.pushButton_6.clicked.connect(self.display_records) + + header = self.tableWidget.horizontalHeader() + header.setStretchLastSection(True) + header.setSectionResizeMode(QHeaderView.Fixed) + # Connect button signals + self.setup_buttons() + self.progressBar.setValue(3) + self.show() + + def setup_buttons(self): + """Connect buttons to their specific actions.""" + # to switch between pages in a QStackedWidget + + self.pushButton_1.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(0)) #first page + self.pushButton_5.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(2)) #second page + self.pushButton_7.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(1)) #third page + # To add details: + self.pushButton.clicked.connect(self.add_record) + # To Show the records: + self.pushButton_6.clicked.connect(self.display_records) + # To check the roll number: + self.pushButton_2.clicked.connect(self.check_records) + # To update the records: + self.pushButton_3.clicked.connect(self.update_student_record) + # To delete all Records: + self.pushButton_8.clicked.connect(self.delete_all_records) + # For buttons to work via enter key: + # Update-1 + self.setTabOrder(self.lineEdit_4,self.pushButton_2) + # Update-2 + self.setTabOrder(self.lineEdit_6,self.lineEdit_5) + self.setTabOrder(self.lineEdit_5,self.pushButton_3) + # Add + self.setTabOrder(self.lineEdit,self.lineEdit_2) + self.setTabOrder(self.lineEdit_3,self.pushButton) + + def mousePressEvent(self, event): + """Reset the progress bar to zero when any part of the window is clicked.""" + self.progressBar.setValue(0) + super(Mygui, self).mousePressEvent(event) + def update_progress_bar(self): + """fill the progress bar with a small delay.""" + for i in range(101): + time.sleep(0.001) + self.progressBar.setValue(i) + + def add_record(self): + roll = self.lineEdit.text() + name = self.lineEdit_2.text() + percentage = self.lineEdit_3.text() + + #check if fields are not empty and percentage is numeric + if roll.isdigit() and percentage.replace('.', '', 1).isdigit(): + student_obj = student.Student() + student_obj.add_record(int(roll), name, float(percentage)) + + self.textEdit.append("Details have been successfully added!") + # Write the student record to the binary file using file_operations + result = file_operations.write_record(student_obj) + self.statusBar().showMessage(result) + # Progress bar: + QTimer.singleShot(0, self.update_progress_bar) + # Clear the input fields + self.lineEdit.clear() + self.lineEdit_2.clear() + self.lineEdit_3.clear() + else: + self.statusBar().showMessage("Invalid input. Please check the fields.") + + def check_records(self): + """Check if the roll number exists and display the student details.""" + roll = self.lineEdit_4.text() + + if roll.isdigit(): + roll = int(roll) + student_record = file_operations.get_record(roll) + + if student_record: + # If student_record is a string, split it into components + record_fields = student_record.split(",") + if len(record_fields) == 3: # Ensure there are exactly 3 fields + roll, name, percentage = record_fields + # Format the student data and display it in the textEdit widget + self.textBrowser.setText(f"Roll Number: {roll.strip()}\n" + f"Name: {name.strip()}\n" + f"Percentage: {percentage.strip()}") + self.statusBar().showMessage("Record found!") + else: + self.textBrowser.setText("Error: Record format is incorrect.") + self.statusBar().showMessage("Record format error.") + else: + # If the roll number is not found, show an error message + self.textBrowser.setText("No record found for this roll number.") + self.statusBar().showMessage("Record not found.") + else: + self.statusBar().showMessage("Invalid roll number.") + + def update_student_record(self): + # Get roll number, new name, and new percentage from the input fields + roll = self.lineEdit_4.text() + new_name = self.lineEdit_6.text() + new_percentage = self.lineEdit_5.text() + + if roll.isdigit(): + roll = int(roll) + if not new_name or not new_percentage: + self.statusBar().showMessage("Please provide new details to update.") + return + + # If percentage is provided, ensure it's valid + if new_percentage and not new_percentage.replace('.', '', 1).isdigit(): + self.statusBar().showMessage("Invalid percentage value.") + return + + new_percentage = float(new_percentage) if new_percentage else None + + # Call the update_record function + result = file_operations.update_record(roll, new_name, new_percentage) + print(f"Update result: {result}") + + # For a success pop-up dialog box + msg = QMessageBox() + msg.setIcon(QMessageBox.Information) + msg.setText(f"Record for Roll Number {roll} has been updated successfully.") + msg.setWindowTitle("Success") + msg.exec_() + + # Display the result of the update + if result == "Record updated successfully.": + self.display_records() + else: + self.statusBar().showMessage(result) + + # Clear the input fields after the update + self.lineEdit_4.clear() + self.lineEdit_6.clear() + self.lineEdit_5.clear() + self.textBrowser.clear() + else: + self.statusBar().showMessage("Invalid roll number.") + + def keyPressEvent(self, event): + if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: + # Get the currently focused widget + focused_widget = self.focusWidget() + + # Check if the focused widget is a QLineEdit + if isinstance(focused_widget, QLineEdit): + # Move to the next input field or button if focus is on QLineEdit + self.focusNextChild() + elif isinstance(focused_widget, QPushButton): + # Click the QPushButton if it is focused + focused_widget.animateClick() + else: + buttons = self.findChildren(QPushButton) + for button in buttons: + if button.hasFocus(): + button.animateClick() + break + + super(Mygui, self).keyPressEvent(event) + + def display_records(self): + records = file_operations.display_all_records() # Get all records from the file + + # Clear the table before displaying new records + self.tableWidget.clearContents() + + # Reset row count to match the number of records + self.tableWidget.setRowCount(0) + + # Set the number of columns and headers + self.tableWidget.setColumnCount(3) + self.tableWidget.setHorizontalHeaderLabels(["Roll Number", "Name", "Percentage"]) + + # Enable the grid and set grid style (if needed) + self.tableWidget.setShowGrid(True) + self.tableWidget.setGridStyle(1) + + self.tableWidget.horizontalHeader().setStretchLastSection(True) + self.tableWidget.setAlternatingRowColors(True) + + # Set column width to ensure text fits properly + self.tableWidget.setColumnWidth(0, 150) # Adjust the width for Roll Number + self.tableWidget.setColumnWidth(1, 200) # Adjust the width for Name + self.tableWidget.setColumnWidth(2, 150) # Adjust the width for Percentage + + if records and records[0] != "No records found.": + # Set row count to the number of records + self.tableWidget.setRowCount(len(records)) + for row, record in enumerate(records): + fields = record.strip().split(",") + if len(fields) == 3: + roll, name, percentage = fields + self.tableWidget.setItem(row, 0, QTableWidgetItem(roll.strip())) + self.tableWidget.setItem(row, 1, QTableWidgetItem(name.strip())) + self.tableWidget.setItem(row, 2, QTableWidgetItem(percentage.strip())) + else: + # If no records, ensure the table is empty + self.tableWidget.setRowCount(1) + self.tableWidget.setItem(0, 0, QTableWidgetItem("No records found")) + + # Delete all records with confirmation dialog + def delete_all_records(self): + """Show a confirmation dialog before deleting all records.""" + msg = QMessageBox() + msg.setIcon(QMessageBox.Warning) + msg.setWindowTitle("Delete All Records") + msg.setText("Are you sure you want to delete all records?") + msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + + # Show the confirmation dialog and check the user's choice + result = msg.exec_() + + if result == QMessageBox.Yes: + # Call the file operation to clear all records + result = file_operations.clear_all_records() # Make sure to implement this function + if result == "All records deleted.": + self.display_records() # Refresh the table after deletion + self.statusBar().showMessage("All records have been deleted.") + else: + self.statusBar().showMessage("Error deleting records.") + else: + self.statusBar().showMessage("Delete action canceled.") + + +def load_stylesheet(app, stylesheet_file): + """Load the external QSS file.""" + try: + with open(stylesheet_file, "r") as file: + app.setStyleSheet(file.read()) + except Exception as e: + print(f"Error loading stylesheet: {e}") + +def main(): + app = QApplication([]) + + # Load the external stylesheet + load_stylesheet(app, "GUI/stylesheet.qss") + window = Mygui() + app.exec_() + +if __name__ == '__main__': + main() + diff --git a/GUI/GUI_student.py b/GUI/GUI_student.py index 7bc47cc..1a76276 100644 --- a/GUI/GUI_student.py +++ b/GUI/GUI_student.py @@ -9,7 +9,7 @@ class Mygui(QMainWindow): def __init__(self): super(Mygui, self).__init__() - uic.loadUi("first_gui.ui", self) + uic.loadUi("Student-Management-System\\GUI\\first_gui.ui", self) self.show() diff --git a/GUI/file_operations.py b/GUI/file_operations.py index fb2435d..166aa7a 100644 --- a/GUI/file_operations.py +++ b/GUI/file_operations.py @@ -6,12 +6,12 @@ def write_record(student): try: with open("stud.dat", "ab") as file: - pickle.dump(student, file) # Ensure student is a Student instance - return "Record added in file!" + pickle.dump(student, file) # Add the student object to the file + print("New record added successfully.") + return "Record added successfully." except Exception as e: - return f"Error: {str(e)}" - - + return f"Error writing record: {e}" + def display_all_records(): records = [] try: @@ -19,7 +19,7 @@ def display_all_records(): while True: try: student = pickle.load(file) # Load the Student object - records.append(student.display_record()) # Append the student's record + records.append(student.display_record()) except EOFError: break return records if records else ["No records found."] @@ -29,6 +29,70 @@ def display_all_records(): return ["Error in unpickling data! The file might be corrupted."] except IOError: return ["File could not be opened!"] + +def update_record(roll_number, new_name=None, new_percentage=None): + updated = False + records = [] + + try: + # Open the file in read-binary mode + with open("stud.dat", "rb") as file: + while True: + try: + student_obj = pickle.load(file) # Load each student record + if student_obj.roll == roll_number: + # Update the student details + if new_name: + student_obj.name = new_name.upper() + if new_percentage is not None: + student_obj.per = new_percentage + updated = True + records.append(student_obj) + except EOFError: + break + + # Rewrite the updated records + with open("stud.dat", "wb") as file: + for student_obj in records: + pickle.dump(student_obj, file) + + if updated: + return f"Record for Roll Number {roll_number} updated successfully." + else: + return f"Record with Roll Number {roll_number} not found." + + except FileNotFoundError: + return "File not found! Please add a record first." + except pickle.UnpicklingError: + return "Error in unpickling data! The file might be corrupted." + except Exception as e: + return f"An error occurred: {e}" + +def get_record(roll_number): + try: + with open("stud.dat", "rb") as file: + while True: + try: + student_obj = pickle.load(file) # Load each student object + if student_obj.roll == roll_number: + return student_obj.display_record() # Return the record if roll number matches + except EOFError: + break + return f"Record with Roll Number {roll_number} not found." + except FileNotFoundError: + return "File not found! Please add a record first." + except pickle.UnpicklingError: + return "Error in unpickling data! The file might be corrupted." + except Exception as e: + return f"An error occurred: {e}" + +def clear_all_records(): + try: + with open("stud.dat", "wb") as file: + pass # This will clear the file + return "All records deleted." + except Exception as e: + return f"Error deleting records: {str(e)}" def delete_file(self): try: diff --git a/GUI/pages.ui b/GUI/pages.ui new file mode 100644 index 0000000..da6b0c6 --- /dev/null +++ b/GUI/pages.ui @@ -0,0 +1,684 @@ + + + MainWindow + + + + 0 + 0 + 861 + 681 + + + + MainWindow + + + + + + + + + 20 + 10 + 821 + 631 + + + + + 14 + + + + + + + + + 80 + 50 + 671 + 101 + + + + + + + + + 0 + 10 + 161 + 61 + + + + + 14 + + + + Add + + + + ../icons/graduation.png../icons/graduation.png + + + + 28 + 28 + + + + false + + + false + + + false + + + false + + + + + + + + + + 10 + 10 + 151 + 61 + + + + + 14 + + + + Update + + + + ../../../../../Downloads/pen.png../../../../../Downloads/pen.png + + + + 28 + 28 + + + + false + + + false + + + false + + + + + + + + + + 10 + 10 + 161 + 61 + + + + + 14 + + + + Records + + + + ../icons/students.png../icons/students.png + + + + 28 + 28 + + + + false + + + false + + + false + + + + + + + + + + 30 + 150 + 761 + 451 + + + + + 75 + false + true + + + + false + + + 0 + + + + + + 330 + 10 + 421 + 431 + + + + + + 10 + 210 + 401 + 181 + + + + + + + 10 + 150 + 401 + 51 + + + + Add Record + + + + + + 10 + 10 + 401 + 131 + + + + + QLayout::SetDefaultConstraint + + + 10 + + + 10 + + + + + + 75 + true + + + + Roll Number + + + + + + + + 12 + + + + + + + + + 75 + true + + + + Name + + + + + + + + 12 + + + + + + + + + 75 + true + + + + Percentage + + + + + + + + 12 + + + + + + + + + true + + + + 10 + 400 + 401 + 10 + + + + false + + + 7 + + + false + + + false + + + + + + + -10 + 0 + 771 + 461 + + + + + + + + + + ../icons/11.jpg + + + true + + + image_label + widget + + + + + + 310 + 150 + 141 + 46 + + + + + 12 + + + + Check Data + + + + + + 200 + 220 + 141 + 31 + + + + Update Data + + + + + + 310 + 360 + 141 + 48 + + + + + 12 + + + + Update Details + + + false + + + + + + 200 + 20 + 381 + 126 + + + + + + + + + + Roll Number + + + + + + + + 12 + + + + + + + + + + + + + 210 + 260 + 371 + 91 + + + + + + + + 14 + + + + New Name + + + + + + + + 14 + + + + + + + + + 14 + + + + New Percentage + + + + + + + + 14 + + + + + + + + + + + + 50 + 20 + 281 + 21 + + + + Details of the Students + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 20 + 50 + 711 + 391 + + + + + + 10 + 10 + 691 + 391 + + + + + 0 + 0 + + + + + Arial,sans-serif + 50 + false + false + + + + QFrame::StyledPanel + + + 1 + + + 3 + + + Qt::NoPen + + + false + + + + Name + + + + 14 + + + + + + Roll Number + + + + 14 + + + + + + Percentage + + + + 14 + + + + + + + + + 390 + 9 + 331 + 44 + + + + + + + + 12 + + + + View Records + + + + + + + + 12 + + + + Delete All Records + + + + + + + + + + + 230 + 10 + 361 + 41 + + + + + -1 + 75 + true + + + + Student Management System + + + Qt::AlignCenter + + + + + + + + 75 + true + true + true + + + + + + + + + diff --git a/GUI/readme.md b/GUI/readme_gui1.md similarity index 100% rename from GUI/readme.md rename to GUI/readme_gui1.md diff --git a/GUI/student.py b/GUI/student.py index b3bbdc8..9ed84f2 100644 --- a/GUI/student.py +++ b/GUI/student.py @@ -1,11 +1,8 @@ -# student.py - - class Student: def __init__(self): self.roll = 0 self.name = "" - self.per = 0 + self.per = 0.0 def add_record(self, roll, name, per): self.roll = roll @@ -13,4 +10,7 @@ def add_record(self, roll, name, per): self.per = per def display_record(self): - return f"Roll Number: {self.roll}\nName: {self.name}\nPercentage: {self.per}" + return f"{self.roll},{self.name},{self.per}" + + def __str__(self): + return f"Roll: {self.roll}, Name: {self.name}, Percentage: {self.per}" diff --git a/GUI/stylesheet.qss b/GUI/stylesheet.qss new file mode 100644 index 0000000..b776c9e --- /dev/null +++ b/GUI/stylesheet.qss @@ -0,0 +1,196 @@ +#MainWindow,#centralwidget { + background-color: #003d5a; + border: 2px solid #aaffff; + border-radius: 9px; +} +#widget_Main{ + background-color: rgb(0, 0, 0); + border: 2px solid #aaffff; + border-radius: 9px; +} +#stackedWidget { + background-color: #000000; + border: 2px solid #aaffff; + border-radius: 9px; +} +QLabel { + font-size: 24px; + color: #ffffff; +} +#label,#label_5,#label_6,#label_3{ +color: #ffffff; +font-weight: bold; +} + +#image_label { + border-radius: 10px; + background-color: #F5F5F5; + padding-left: 2px; +} +#image_label:hover { + border-color: #3A6F91; + background-color: #E8F1F8; +} + +QProgressBar { + border: 2px solid #4CAF; + border-radius: 5px; + text-align: center; + background-color: #E0E0E0; +} + +QProgressBar::chunk { + background-color: #4CAF50; + border-radius: 5px; +} +/* Table Widget Styling */ +QTableWidget { + border: 1px solid #dcdcdc; + gridline-color: #dcdcdc; + background-color: #f9f9f9; + selection-background-color: #aed6f1; + selection-color: #000000; + color: #2c3e50; + font: 14px "Arial", sans-serif; +} + +QHeaderView::section { + background-color: #2980b9; + color: #ffffff; + padding: 4px; + font-weight: bold; + border: 1px solid #dcdcdc; +} + +QTableWidget::item { + padding: 8px; + border: 1px solid #dcdcdc; +} + +QTableWidget::item:alternate { + background-color: #ecf0f1; +} + +QScrollBar:horizontal { + height: 12px; + background-color: #f9f9f9; +} + +QScrollBar:vertical { + width: 12px; + background-color: #f9f9f9; +} + +QScrollBar::handle { + background-color: #2980b9; + border-radius: 6px; +} + +QScrollBar::add-line, QScrollBar::sub-line { + background: none; +} +QHeaderView::section:horizontal {margin-right: 8; border: 1px solid} +#tableWidget{ +padding-left: 3px; +padding-right: 3px; +padding-top: 2px; +border-radius: 7px; +} + +#pushButton_6,#pushButton_8{ +border: 1px solid #dcdcdc; +border-radius: 7px; +color: white; +padding: 2px; +margin-top: 4px; +margin-bottom: 4px; +height: 28px; +} + +QStatusBar{ +color: white; +font-style: italic; +} +/*For Buttons*/ +QPushButton { + background-color: #5C6BC0; + color: white; + font-size: 16px; + font-weight: bold; + padding: 5px 5px; + border-radius: 10px; + border: 2px solid #3949AB; + transition: background-color 0.3s ease, transform 0.3s ease; +} + +/* Hover State */ +QPushButton:hover { + background-color: #7986CB; + color: #E3F2FD; + transform: scale(1.05); +} + +QPushButton:pressed { + background-color: #303F9F; + transform: scale(0.98); +} + +QPushButton:disabled { + background-color: #B0BEC5; + color: #ECEFF1; + border: 2px solid #CFD8DC; +} + +#pushButton_1 { + background-color: #4CAF50; +} +#pushButton_1:hover { + background-color: #66BB6A; +} +#pushButton_1:pressed { + background-color: rgb(55, 127, 57); +} + +#pushButton_5 { + background-color: #FFCA28; +} +#pushButton_5:hover { + background-color: #FFD54F; +} +#pushButton_5:pressed { + background-color: rgb(190, 190, 0); +} + +/* Records button */ +#pushButton_7 { + background-color: #42A5F5; +} +#pushButton_7:hover { + background-color: #64B5F6; +} +#pushButton_7:pressed { + background-color: rgb(65, 131, 197); +} +/*This is for message pop-up*/ +QMessageBox { + background-color: #002b36; /* Dark background */ + border: 2px solid #aaffff; /* Cyan border */ + color: #ffffff; /* White text */ + font-size: 16px; +} +QMessageBox QLabel { + color: #ffffff; /* Ensure label text is white */ +} +QMessageBox QPushButton { + background-color: #586e75; /* Gray buttons */ + color: #ffffff; /* White text */ + padding: 6px 12px; + border-radius: 5px; + font-weight: bold; +} +QMessageBox QPushButton:hover { + background-color: #93a1a1; /* Lighter gray on hover */ +} +QMessageBox QPushButton:pressed { + background-color: #657b83; /* Darker gray on press */ +} \ No newline at end of file diff --git a/icons/11.jpg b/icons/11.jpg new file mode 100644 index 0000000..ecd7b9d Binary files /dev/null and b/icons/11.jpg differ diff --git a/icons/graduation.png b/icons/graduation.png new file mode 100644 index 0000000..da4ac9f Binary files /dev/null and b/icons/graduation.png differ diff --git a/icons/icons.qrc b/icons/icons.qrc new file mode 100644 index 0000000..66719b7 --- /dev/null +++ b/icons/icons.qrc @@ -0,0 +1,11 @@ + + + ../../../../../Downloads/png-transparent-educational-technology-learning-management-system-course-student-education-service-people-presentation.png + pen.png + students.png + graduation.png + + + ../../../../../Downloads/style_stud.qss + + diff --git a/icons/pen.png b/icons/pen.png new file mode 100644 index 0000000..4ba28e3 Binary files /dev/null and b/icons/pen.png differ diff --git a/icons/student-profile_bg.jpg b/icons/student-profile_bg.jpg new file mode 100644 index 0000000..8183a16 Binary files /dev/null and b/icons/student-profile_bg.jpg differ diff --git a/icons/students.png b/icons/students.png new file mode 100644 index 0000000..2707915 Binary files /dev/null and b/icons/students.png differ diff --git a/icons/update_img_page.jpeg b/icons/update_img_page.jpeg new file mode 100644 index 0000000..cceea19 Binary files /dev/null and b/icons/update_img_page.jpeg differ