في هذا الدرس سنتعرف على QTableWidget، وهو عنصر واجهة رسومية في PyQt يسمح لنا بعرض البيانات في شكل جدول يحتوي على صفوف وأعمدة.
يمكنك من خلاله إضافة، تعديل، أو حذف البيانات، وتنسيق الخلايا بسهولة.
أهم خصائص QTableWidget
- تحديد عدد الصفوف والأعمدة باستخدام:
table.setRowCount(عدد_الصفوف) table.setColumnCount(عدد_الأعمدة)
- تحديد عناوين الأعمدة باستخدام:
table.setHorizontalHeaderLabels(["العمود 1", "العمود 2", ...])
- إضافة بيانات إلى خلية:
table.setItem(صف, عمود, QTableWidgetItem("النص"))
- قراءة البيانات من خلية:
value = table.item(صف, عمود).text()
مثال عملي
هذا المثال يعرض جدول بيانات بسيط يحتوي على معلومات طلاب (الاسم، العمر، الدرجة).
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QTableWidget, QTableWidgetItem
class TableApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("مثال QTableWidget")
self.resize(400, 300)
layout = QVBoxLayout()
# إنشاء الجدول
self.table = QTableWidget()
self.table.setRowCount(3) # عدد الصفوف
self.table.setColumnCount(3) # عدد الأعمدة
self.table.setHorizontalHeaderLabels(["الاسم", "العمر", "الدرجة"])
# إضافة البيانات
data = [
["أحمد", "20", "90"],
["منى", "22", "85"],
["خالد", "19", "88"]
]
for row, row_data in enumerate(data):
for col, value in enumerate(row_data):
self.table.setItem(row, col, QTableWidgetItem(value))
layout.addWidget(self.table)
self.setLayout(layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = TableApp()
window.show()
sys.exit(app.exec_())
شرح الكود
- أنشأنا جدول باستخدام
QTableWidget
. - حددنا عدد الصفوف والأعمدة.
- وضعنا عناوين للأعمدة.
- أضفنا بيانات في كل خلية باستخدام
setItem()
.
📌 معلومة مهمة:
QTableWidget
مناسب للجداول الصغيرة والمتوسطة. أما الجداول الكبيرة جدًا أو التي تحتاج ربطها بقاعدة بيانات، يفضل استخدام QTableView مع نموذج بيانات.
تمرين على QTableWidget بحيث:
- يمكن إضافة صفوف ديناميكيًا من خلال إدخال البيانات في خانات نصية.
- يمكن حذف الصف المحدد بزر واحد.
التمرين مع الحل
import sys
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QTableWidget, QTableWidgetItem,
QLineEdit, QPushButton, QHBoxLayout, QMessageBox
)
class TableApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("تمرين QTableWidget - إضافة وحذف صفوف")
self.resize(500, 400)
# تصميم الواجهة
main_layout = QVBoxLayout()
# إنشاء الجدول
self.table = QTableWidget()
self.table.setColumnCount(3)
self.table.setHorizontalHeaderLabels(["الاسم", "العمر", "الدرجة"])
main_layout.addWidget(self.table)
# حقول إدخال البيانات
form_layout = QHBoxLayout()
self.name_input = QLineEdit()
self.name_input.setPlaceholderText("ادخل الاسم")
self.age_input = QLineEdit()
self.age_input.setPlaceholderText("ادخل العمر")
self.grade_input = QLineEdit()
self.grade_input.setPlaceholderText("ادخل الدرجة")
form_layout.addWidget(self.name_input)
form_layout.addWidget(self.age_input)
form_layout.addWidget(self.grade_input)
main_layout.addLayout(form_layout)
# أزرار الإضافة والحذف
btn_layout = QHBoxLayout()
btn_add = QPushButton("إضافة صف")
btn_delete = QPushButton("حذف الصف المحدد")
btn_add.clicked.connect(self.add_row)
btn_delete.clicked.connect(self.delete_row)
btn_layout.addWidget(btn_add)
btn_layout.addWidget(btn_delete)
main_layout.addLayout(btn_layout)
self.setLayout(main_layout)
def add_row(self):
name = self.name_input.text()
age = self.age_input.text()
grade = self.grade_input.text()
if not name or not age or not grade:
QMessageBox.warning(self, "خطأ", "يرجى إدخال جميع البيانات قبل الإضافة")
return
# إضافة صف جديد
row_position = self.table.rowCount()
self.table.insertRow(row_position)
self.table.setItem(row_position, 0, QTableWidgetItem(name))
self.table.setItem(row_position, 1, QTableWidgetItem(age))
self.table.setItem(row_position, 2, QTableWidgetItem(grade))
# مسح الحقول بعد الإضافة
self.name_input.clear()
self.age_input.clear()
self.grade_input.clear()
def delete_row(self):
selected_row = self.table.currentRow()
if selected_row >= 0:
self.table.removeRow(selected_row)
else:
QMessageBox.warning(self, "خطأ", "يرجى تحديد صف لحذفه")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = TableApp()
window.show()
sys.exit(app.exec_())
شرح الفكرة
- الجدول يبدأ فارغًا بعدد أعمدة محدد.
- إضافة صف:
- ندخل البيانات في QLineEdit.
- عند الضغط على "إضافة صف"، يتم إنشاء صف جديد في الجدول ووضع القيم فيه.
- حذف صف:
- نحدد الصف بالماوس.
- عند الضغط على "حذف الصف المحدد"، يتم حذفه مباشرة.
التمرين المطور بحيث:
- الجدول يحفظ البيانات تلقائيًا في ملف Excel عند الإضافة أو الحذف.
- عند فتح البرنامج، يتم تحميل البيانات من ملف Excel إذا كان موجودًا.
الكود المطور
import sys
import os
import pandas as pd
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QTableWidget, QTableWidgetItem,
QLineEdit, QPushButton, QHBoxLayout, QMessageBox
)
class TableExcelApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("QTableWidget - حفظ واسترجاع من Excel")
self.resize(500, 400)
self.excel_file = "table_data.xlsx"
# تصميم الواجهة
main_layout = QVBoxLayout()
# إنشاء الجدول
self.table = QTableWidget()
self.table.setColumnCount(3)
self.table.setHorizontalHeaderLabels(["الاسم", "العمر", "الدرجة"])
main_layout.addWidget(self.table)
# تحميل البيانات من Excel إذا موجود
self.load_from_excel()
# حقول إدخال البيانات
form_layout = QHBoxLayout()
self.name_input = QLineEdit()
self.name_input.setPlaceholderText("ادخل الاسم")
self.age_input = QLineEdit()
self.age_input.setPlaceholderText("ادخل العمر")
self.grade_input = QLineEdit()
self.grade_input.setPlaceholderText("ادخل الدرجة")
form_layout.addWidget(self.name_input)
form_layout.addWidget(self.age_input)
form_layout.addWidget(self.grade_input)
main_layout.addLayout(form_layout)
# أزرار الإضافة والحذف
btn_layout = QHBoxLayout()
btn_add = QPushButton("إضافة صف")
btn_delete = QPushButton("حذف الصف المحدد")
btn_add.clicked.connect(self.add_row)
btn_delete.clicked.connect(self.delete_row)
btn_layout.addWidget(btn_add)
btn_layout.addWidget(btn_delete)
main_layout.addLayout(btn_layout)
self.setLayout(main_layout)
def add_row(self):
name = self.name_input.text()
age = self.age_input.text()
grade = self.grade_input.text()
if not name or not age or not grade:
QMessageBox.warning(self, "خطأ", "يرجى إدخال جميع البيانات قبل الإضافة")
return
# إضافة الصف للجدول
row_position = self.table.rowCount()
self.table.insertRow(row_position)
self.table.setItem(row_position, 0, QTableWidgetItem(name))
self.table.setItem(row_position, 1, QTableWidgetItem(age))
self.table.setItem(row_position, 2, QTableWidgetItem(grade))
# حفظ إلى Excel
self.save_to_excel()
# مسح الحقول
self.name_input.clear()
self.age_input.clear()
self.grade_input.clear()
def delete_row(self):
selected_row = self.table.currentRow()
if selected_row >= 0:
self.table.removeRow(selected_row)
self.save_to_excel()
else:
QMessageBox.warning(self, "خطأ", "يرجى تحديد صف لحذفه")
def save_to_excel(self):
rows = self.table.rowCount()
cols = self.table.columnCount()
data = []
for row in range(rows):
row_data = []
for col in range(cols):
item = self.table.item(row, col)
row_data.append(item.text() if item else "")
data.append(row_data)
df = pd.DataFrame(data, columns=["الاسم", "العمر", "الدرجة"])
df.to_excel(self.excel_file, index=False)
def load_from_excel(self):
if os.path.exists(self.excel_file):
df = pd.read_excel(self.excel_file)
self.table.setRowCount(len(df))
for row in range(len(df)):
for col in range(len(df.columns)):
self.table.setItem(row, col, QTableWidgetItem(str(df.iat[row, col])))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = TableExcelApp()
window.show()
sys.exit(app.exec_())
مميزات النسخة المطورة
- عند الإضافة أو الحذف، يتم حفظ الجدول مباشرة في ملف
table_data.xlsx
. - عند إعادة تشغيل البرنامج، يتم استرجاع البيانات من الملف.
- لا تحتاج لحفظ يدوي، كل شيء يتم أوتوماتيكيًا.