cwidgets 0.1.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cwidgets/Docs/Help/Guide_ar.html +527 -0
- cwidgets/Docs/Help/Guide_en.html +528 -0
- cwidgets/Docs/Help/Guide_fr.html +528 -0
- cwidgets/__init__.py +191 -0
- cwidgets/pyqt6/__init__.py +50 -0
- cwidgets/pyqt6/editor_core.py +588 -0
- cwidgets/pyqt6/editor_style.py +513 -0
- cwidgets/pyqt6/validate_parent.py +76 -0
- cwidgets/pyqt6/widgets.py +802 -0
- cwidgets/pyside6/__init__.py +50 -0
- cwidgets/pyside6/editor_core.py +588 -0
- cwidgets/pyside6/editor_style.py +513 -0
- cwidgets/pyside6/validate_parent.py +76 -0
- cwidgets/pyside6/widgets.py +792 -0
- cwidgets-0.1.0a1.dist-info/METADATA +21 -0
- cwidgets-0.1.0a1.dist-info/RECORD +19 -0
- cwidgets-0.1.0a1.dist-info/WHEEL +5 -0
- cwidgets-0.1.0a1.dist-info/licenses/LICENSE +67 -0
- cwidgets-0.1.0a1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="ar" dir="rtl">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>دليل المستخدم - مكتبة CWidgets V0.1.0a1</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
|
|
9
|
+
<h1></h1>
|
|
10
|
+
<p>دليل المستخدم - مكتبة CWidgets V0.1.0a1:</p>
|
|
11
|
+
|
|
12
|
+
<h1 id="introduction">مقدمة</h1>
|
|
13
|
+
<p>CWidgets: قوتك في البرمجة بلا حدود<br>
|
|
14
|
+
حول عوائق Qt إلى فرص إبداعية<br>
|
|
15
|
+
CWidgets هي مكتبة بايثون متخصصة تمنح المطورين المكفوفين السيطرة الكاملة على واجهات PyQt6 وPySide6.<br>
|
|
16
|
+
لقد إنتهى زمن القيود مع QTextEdit، فمنذ الآن، يمكنك تصميم واجهات تحتوي على مربعات تحرير متعددة الأسطر بكل حرية وتوافقية.<br>
|
|
17
|
+
المكتبة توفر للمطور الكفيف توافقية مطلقة فجميع العناصر مصممة لتعمل بسلاسة تامة مع قارئ الشاشة NVDA.<br>
|
|
18
|
+
إستمرارية الإبداع:لا حاجة لتعلم أدوات جديدة بل واصل كتابة أكوادك التي تعودت عليها مع الحفاظ على نفس الدوال والوظائف.<br>
|
|
19
|
+
هندسة ذكية:كل عنصر في CWidgets يرث خصائص عناصر Qt الأصلية، مما يضمن لك أداءً قياسياً مع حلول جذرية لمشاكل التوافقية.<br>
|
|
20
|
+
ما الذي سيتغير في كتابتك لبرامجك بإستخدام نوافذ PyQT6 و PySide6 ؟ .<br>
|
|
21
|
+
كل ما ستحتاجه هو استبدال الحرف الأول فقط (C عوضاً عن Q):<br>
|
|
22
|
+
CTextEdit بدلاً من QTextEdit.<br>
|
|
23
|
+
CButton بدلاً من QPushButton.<br>
|
|
24
|
+
حيث ترمز C إلى Custom أو مخصصة<br>
|
|
25
|
+
مرونة تامة في العمل ، فالمكتبة تدعم بيئة العمل التي تفضلها بنفس الكفاءة:</p>
|
|
26
|
+
<pre>
|
|
27
|
+
مع PySide6:
|
|
28
|
+
from cwidgets.pyside6 import CButton, CLabel, CLineEdit....
|
|
29
|
+
|
|
30
|
+
مع PyQt6:
|
|
31
|
+
from cwidgets.pyqt6 import CButton, CLabel, CLineEdit .....</pre>
|
|
32
|
+
<p>CWidgets: برمج بكل ثقة، وصمم بلا قيود.</p>
|
|
33
|
+
|
|
34
|
+
<h1 id="widgets-list">المكونات المتوفرة :</h1>
|
|
35
|
+
<p>7 عناصر مخصصة متوافقة تماما توفرها لك هذه المكتبة و هي :</p>
|
|
36
|
+
<ol>
|
|
37
|
+
<li>CTextEdit</li>
|
|
38
|
+
<li>CButton</li>
|
|
39
|
+
<li>CLabel</li>
|
|
40
|
+
<li>CLineEdit</li>
|
|
41
|
+
<li>CComboBox</li>
|
|
42
|
+
<li>CListWidget</li>
|
|
43
|
+
<li>CMessageBox</li>
|
|
44
|
+
</ol>
|
|
45
|
+
|
|
46
|
+
<h1 id="installation">التثبيت</h1>
|
|
47
|
+
<pre>pip install cwidgets</pre>
|
|
48
|
+
|
|
49
|
+
<h1 id="show-help">الوصول إلى المساعدة</h1>
|
|
50
|
+
<p>بعد التثبيت، أبدأ بالتعرف على المكتبة و مكوناتها و خصائصها و كيفية إستخدامها من خلال هذه الدوال :</p>
|
|
51
|
+
<pre>
|
|
52
|
+
import cwidgets
|
|
53
|
+
|
|
54
|
+
# قائمة بجميع المكونات المتاحة
|
|
55
|
+
cwidgets.widgets()
|
|
56
|
+
|
|
57
|
+
# قائمة بجميع الأقسام المتاحة
|
|
58
|
+
cwidgets.sections()
|
|
59
|
+
|
|
60
|
+
# فتح الدليل الكامل في المتصفح
|
|
61
|
+
cwidgets.show_help()
|
|
62
|
+
cwidgets.show_help(lang="ar")
|
|
63
|
+
|
|
64
|
+
# فتح أحد المكونات بالتحديد مباشرة
|
|
65
|
+
cwidgets.show_help(lang="ar", goto="CButton")
|
|
66
|
+
cwidgets.show_help(lang="ar", goto="CTextEdit")
|
|
67
|
+
|
|
68
|
+
# فتح قسم محدد مباشرة
|
|
69
|
+
cwidgets.show_help(lang="ar", goto="introduction")
|
|
70
|
+
cwidgets.show_help(lang="ar", goto="installation")
|
|
71
|
+
</pre>
|
|
72
|
+
|
|
73
|
+
<h1 id="common-issues">المشاكل الشائعة التي تم حلها</h1>
|
|
74
|
+
|
|
75
|
+
<p>#CTextEdit : مربع تحرير متعدد الأسطر: يحل CTextEdit المشكلة الأساسية لـ QTextEdit، غير المتوافق مع NVDA.</p>
|
|
76
|
+
|
|
77
|
+
<p>#CComboBox & CListWidget: فصل التنقل عن التفعيل : فصل إجراءات التنقل (الأسهم) عن إجراءات التفعيل (Enter/Space) لتجنب التفعيلات غير المقصودة. التعطيل المتاح: الحفاظ على الإعلان بواسطة NVDA حتى عندما يكون المكون معطلاً.</p>
|
|
78
|
+
|
|
79
|
+
<p>#CButton : التفعيل ب enter, return, space & mouse. توافقية حتى في حالة التعطيل.</p>
|
|
80
|
+
|
|
81
|
+
<p>#CLabel تعزيز توافقية العناوين والملصقات</p>
|
|
82
|
+
|
|
83
|
+
<p>#CLineEdit : يمكنك من إسترجاع النص من داخل العنصر من خلال الضغط enter دون أي سطر كود إضافي.</p>
|
|
84
|
+
|
|
85
|
+
<p>#CMessageBox : محاورة رسائل ذاتية الإنهيار. يمكنك تحديد زمن تختفي بعده المحاورة تلقائيا.</p>
|
|
86
|
+
|
|
87
|
+
<p>#بإستثناء CTextEdit، كل المكونات الأخرى ترث من QT و بالتالي هي تحتفظ بكل خصائصها و وظائفها الأساسية.</p>
|
|
88
|
+
|
|
89
|
+
<h1 id="widgets-details">تفاصيل المكونات :</h1>
|
|
90
|
+
<p>التعريف ،الإنشاء ، الخصائص ، الإستخدام .</p>
|
|
91
|
+
|
|
92
|
+
<h2 id="ctextedit">CTextEdit</h2>
|
|
93
|
+
|
|
94
|
+
<h3>التعريف</h3>
|
|
95
|
+
<p>CTextEdit هو مربع تحرير متعدد الأسطر متوافق تماما مع NVDA .</p>
|
|
96
|
+
|
|
97
|
+
<h3>لماذا — المشكلة الأساسية</h3>
|
|
98
|
+
<ul>
|
|
99
|
+
<li>QTextEdit الأصلي في Qt غير متوافق مع NVDA.</li>
|
|
100
|
+
<li>المطور الكفيف لا يمكنه القراءة أو الكتابة في هذا العنصر .</li>
|
|
101
|
+
</ul>
|
|
102
|
+
<p>هذا هو الحاجز الذي يحول دون المكفوفين و تصميم واجهات QT تحتوي على مربعات تحرير متعددة الأسطر .<br>
|
|
103
|
+
إلى حد ظهور هذه المكتبة لم تعرف حلولا لهذه المشكلة يستخدمها المكفوفون .</p>
|
|
104
|
+
|
|
105
|
+
<h3>الحل — RichEdit Win32 مدمج في Qt</h3>
|
|
106
|
+
<p>الحل الذي أتت به هذه المكتبة هو دمج عنصر تحكم Win32 RichEdit مباشرة في نافذة QT .<br>
|
|
107
|
+
RichEdit Win32 هو إفتراضيا متوافق تماما مع NVDA .<br>
|
|
108
|
+
المرحلة المعقدة في تصميم الحل هي دمجه في نافذة Qt مع الحفاظ على إمكانية الوصول هذه.<br>
|
|
109
|
+
كما ضمنت هذه المكتبة الإستخدام الإفتراضي للأوامر في QT .<br>
|
|
110
|
+
بنية العنصر QTextEdit :<br>
|
|
111
|
+
-QTextEdit<br>
|
|
112
|
+
-EditorStyle — الأنماط، الخط، اللون، المحاذاة<br>
|
|
113
|
+
-RichEdit Win32 — المحرك الأصلي المتوافق</p>
|
|
114
|
+
|
|
115
|
+
<h3>الميزات</h3>
|
|
116
|
+
<ul>
|
|
117
|
+
<li>إمكانية الوصول الكاملة بواسطة NVDA: القراءة، الكتابة، التنقل، التحديد.</li>
|
|
118
|
+
<li>واجهة برمجة تطبيقات متوافقة مع QTextEdit: setText, toPlainText, append, clear, setReadOnly.</li>
|
|
119
|
+
<li>نص منسق: الخط، الحجم، غامق، مائل، اللون، المحاذاة.</li>
|
|
120
|
+
<li>إدارة غير متزامنة: الأنماط والنص في الانتظار إذا لم يكن مقبض Win32 متاحًا بعد.</li>
|
|
121
|
+
<li>التركيز الذكي: استعادة التركيز عند العودة إلى التطبيق.</li>
|
|
122
|
+
</ul>
|
|
123
|
+
|
|
124
|
+
<h3>الاستخدام</h3>
|
|
125
|
+
<pre>
|
|
126
|
+
# PySide6
|
|
127
|
+
from cwidgets.pyside6 import CTextEdit
|
|
128
|
+
# PyQt6
|
|
129
|
+
from cwidgets.pyqt6 import CTextEdit
|
|
130
|
+
|
|
131
|
+
# إنشاء
|
|
132
|
+
self.editor = CTextEdit(self, accessible_name="إسم المربع")
|
|
133
|
+
layout.addWidget(self.editor)
|
|
134
|
+
|
|
135
|
+
# إدراج نص داخل المربع
|
|
136
|
+
self.editor.setText("مرحبًا!")
|
|
137
|
+
# إسترجاع النص من داخل المربع :
|
|
138
|
+
text = self.editor.text()
|
|
139
|
+
text = self.editor.toPlainText()
|
|
140
|
+
#إضافة نص إلى النص الأصلي
|
|
141
|
+
self.editor.append("سطر جديد.")
|
|
142
|
+
#مسح النص لإفراغ المربع
|
|
143
|
+
self.editor.clear()
|
|
144
|
+
|
|
145
|
+
# جعل المربع للقراءة فقط
|
|
146
|
+
self.editor.setReadOnly(True)
|
|
147
|
+
#تعطيل للقراءة فقط ليصبح المربع للكتابة و القراءة
|
|
148
|
+
self.editor.setReadOnly(False)
|
|
149
|
+
|
|
150
|
+
# الخط — QFont أو (str, int, bool, bool)
|
|
151
|
+
self.editor.setFont("Arial", 12, True, False) # الاسم، الحجم، غامق، مائل
|
|
152
|
+
|
|
153
|
+
# الألوان — اسم أو مجموعة RGB
|
|
154
|
+
self.editor.setTextColor("red")
|
|
155
|
+
self.editor.setTextColor((255, 0, 0))
|
|
156
|
+
self.editor.setBackgroundColor("yellow")
|
|
157
|
+
|
|
158
|
+
# المحاذاة
|
|
159
|
+
self.editor.setAlignment("left")
|
|
160
|
+
self.editor.setAlignment("center")
|
|
161
|
+
self.editor.setAlignment("right")</pre>
|
|
162
|
+
|
|
163
|
+
<h3>الألوان المتاحة</h3>
|
|
164
|
+
<pre>
|
|
165
|
+
# الأسماء المدعومة
|
|
166
|
+
"black", "white", "red", "green", "blue", "yellow",
|
|
167
|
+
"cyan", "magenta", "gray", "darkgray", "lightgray",
|
|
168
|
+
"orange", "purple", "violet", "pink", "brown",
|
|
169
|
+
"navy", "teal", "lime", "olive", "maroon",
|
|
170
|
+
"coral", "salmon", "gold", "silver"
|
|
171
|
+
|
|
172
|
+
# أو مجموعة RGB
|
|
173
|
+
(255, 0, 0) # أحمر
|
|
174
|
+
(0, 128, 255) # أزرق فاتح</pre>
|
|
175
|
+
|
|
176
|
+
<h3>العنوان المرئي اختياري</h3>
|
|
177
|
+
<p>accessible_name يُقرأه NVDA ولكنه غير مرئي بصريًا.<br>
|
|
178
|
+
لإضافة عنوان مرئي، أضف CLabel إلى التخطيط قبل المحرر:</p>
|
|
179
|
+
<pre>
|
|
180
|
+
self.editor = CTextEdit(self)
|
|
181
|
+
self.lbl = CLabel("مربع التحرير:", self , self.editor)
|
|
182
|
+
و هنا من الضروري التنبيه أن إضافة CLabel إلى layout يجب أن تسبق إضافة editor حتى يظهر إسم المربع فوق المربع و ليس تحته .
|
|
183
|
+
layout.addWidget(self.lbl)
|
|
184
|
+
layout.addWidget(self.editor)</pre>
|
|
185
|
+
|
|
186
|
+
<h2 id="clabel">CLabel</h2>
|
|
187
|
+
|
|
188
|
+
<h3>التعريف</h3>
|
|
189
|
+
<p>CLabel هو ملصق متوافق مع NVDA، يحل محل QLabel.</p>
|
|
190
|
+
|
|
191
|
+
<h3>لماذا؟</h3>
|
|
192
|
+
<p>QLabel الأصلي غير مرئي لـ NVDA إلا إذا كان مرتبطًا بـ buddy — وفقط عندما يكون هذا Buddy لديه التركيز.</p>
|
|
193
|
+
|
|
194
|
+
<h3>الحل</h3>
|
|
195
|
+
<p>وضعان:<br>
|
|
196
|
+
- الوضع الفردي: متوافق مع NVDA و يتعرف عليه حتى بالتنقل بالتاب .<br>
|
|
197
|
+
- وضع Buddy: عندما يكون CLabel مرتبطا بعنصر آخر .</p>
|
|
198
|
+
|
|
199
|
+
<h3>الميزات</h3>
|
|
200
|
+
<ul>
|
|
201
|
+
<li>تنظيف الاختصارات: &الاسم → "الاسم" لـ NVDA، الاختصار المرئي لـ Qt محفوظ.</li>
|
|
202
|
+
<li>prefix نص تكميلي مثال عندما نريد التعبير عن حالة status .</li>
|
|
203
|
+
<li>المزامنة الإفتراضية :<br>
|
|
204
|
+
تغيير محتوى الملصق .<br>
|
|
205
|
+
setText()<br>
|
|
206
|
+
تغيير النص التكميلي :<br>
|
|
207
|
+
setPrefix()<br>
|
|
208
|
+
يعيدان المزامنة بدون كود إضافي.</li>
|
|
209
|
+
</ul>
|
|
210
|
+
|
|
211
|
+
<h3>الاستخدام</h3>
|
|
212
|
+
<pre>
|
|
213
|
+
# PySide6
|
|
214
|
+
from cwidgets.pyside6 import CLabel, CLineEdit
|
|
215
|
+
# PyQt6
|
|
216
|
+
from cwidgets.pyqt6 import CLabel, CLineEdit
|
|
217
|
+
|
|
218
|
+
# الوضع الفردي — يقرأ NVDA النص و يمكن الوصول إليه بالتاب :
|
|
219
|
+
self.lbl = CLabel("تم حفظ الملف", self)
|
|
220
|
+
|
|
221
|
+
# مع prefix — يعلن NVDA: "حالة تم حفظ الملف"
|
|
222
|
+
self.lbl = CLabel("تم حفظ الملف", self, prefix="حالة")
|
|
223
|
+
|
|
224
|
+
# وضع Buddy — يعلن NVDA الملصق عندما يحصل الحقل على التركيز
|
|
225
|
+
# يجب إضافة الملصق إلى التخطيط قبل حقل الكتابةحتى يظهر الملصق فوق الحقل بصريا
|
|
226
|
+
self.edit = CLineEdit(self)
|
|
227
|
+
self.lbl = CLabel("الاسم:", self, self.edit)
|
|
228
|
+
layout.addWidget(self.lbl)
|
|
229
|
+
layout.addWidget(self.edit)
|
|
230
|
+
|
|
231
|
+
# التحديث الديناميكي
|
|
232
|
+
self.lbl.setText("جارٍ المعالجة")
|
|
233
|
+
self.lbl.setPrefix("خطأ")</pre>
|
|
234
|
+
|
|
235
|
+
<h2 id="cbutton">CButton</h2>
|
|
236
|
+
|
|
237
|
+
<h3>التعريف</h3>
|
|
238
|
+
<p>CButton هو زر يتميز بمزايا إضافية تجعله قابلا للإستخدام دون الحاجة لأسطر كودية يحتاج الزر الأصلي لتلك الأسطر لتفعيل هذه المزايا .</p>
|
|
239
|
+
|
|
240
|
+
<h3>لماذا؟</h3>
|
|
241
|
+
<ul>
|
|
242
|
+
<li>QPushButton الأصلي يقبل فقط Space للتفعيل — Enter و Return يتم تجاهلهما.</li>
|
|
243
|
+
<li>setEnabled(False) يجعل الزر غير مرئي لـ NVDA، حتى مع setAccessibleDescription.</li>
|
|
244
|
+
</ul>
|
|
245
|
+
|
|
246
|
+
<h3>الحل</h3>
|
|
247
|
+
<ul>
|
|
248
|
+
<li>التفعيل يشمل Enter/Return.</li>
|
|
249
|
+
<li>تعطيل الزر يتم مع مواصلة التوافقية .<br>
|
|
250
|
+
الزر يبقى بصريا "في حالة نشطة" لكنه لا يعمل.<br>
|
|
251
|
+
و هذا هو الحل: توفر إمكانية التعطيل مع إستمرار التوافقية عكس QPushButton الذي يختفي عن NVDA لو كان معطلا .</li>
|
|
252
|
+
</ul>
|
|
253
|
+
|
|
254
|
+
<h3>الميزات</h3>
|
|
255
|
+
<ul>
|
|
256
|
+
<li>التفعيل الموسع: Space، Enter، Return، النقر بالماوس.</li>
|
|
257
|
+
<li>التعطيل المتاح: غير قابل للنقر + يعلن NVDA "غير متاح".</li>
|
|
258
|
+
<li>واجهة برمجة تطبيقات مطابقة لـ QPushButton.</li>
|
|
259
|
+
</ul>
|
|
260
|
+
|
|
261
|
+
<h3>الاستخدام</h3>
|
|
262
|
+
<pre>
|
|
263
|
+
# PySide6
|
|
264
|
+
from cwidgets.pyside6 import CButton
|
|
265
|
+
# PyQt6
|
|
266
|
+
from cwidgets.pyqt6 import CButton
|
|
267
|
+
|
|
268
|
+
self.btn = CButton("حفظ", self)
|
|
269
|
+
self.btn.clicked.connect(self.on_click)
|
|
270
|
+
|
|
271
|
+
# تعطيل — يعلن NVDA "غير متاح"، الزر غير قابل للنقر
|
|
272
|
+
self.btn.setEnabled(False)
|
|
273
|
+
self.btn.setEnabled(True)
|
|
274
|
+
|
|
275
|
+
# تغيير العنوان — أصلي Qt
|
|
276
|
+
self.btn.setText("عنوان جديد")
|
|
277
|
+
|
|
278
|
+
# التحقق من الحالة
|
|
279
|
+
if self.btn.isEnabled():
|
|
280
|
+
...</pre>
|
|
281
|
+
|
|
282
|
+
<h2 id="clineedit">CLineEdit</h2>
|
|
283
|
+
|
|
284
|
+
<h3>التعريف</h3>
|
|
285
|
+
<p>CLineEdit هو حقل إدخال نص متوافق، يحل محل QLineEdit.</p>
|
|
286
|
+
|
|
287
|
+
<h3>لماذا؟</h3>
|
|
288
|
+
<p>QLineEdit يحتاج لسطر كود إضافي لإسترجاع النص .<br>
|
|
289
|
+
CLineEdit يجعل هذا التفعيل تلقائيًا عبر الإشارة validated.</p>
|
|
290
|
+
|
|
291
|
+
<h3>الميزات</h3>
|
|
292
|
+
<ul>
|
|
293
|
+
<li>الإشارة validated تُصدر بالنص الحالي عند كل Enter/Return.</li>
|
|
294
|
+
<li>placeholderText كمعامل — يُعلن بواسطة NVDA عندما يكون الحقل فارغًا.</li>
|
|
295
|
+
<li>العنوان عبر CLabel مع buddy.</li>
|
|
296
|
+
</ul>
|
|
297
|
+
|
|
298
|
+
<h3>الاستخدام</h3>
|
|
299
|
+
<pre>
|
|
300
|
+
# PySide6
|
|
301
|
+
from cwidgets.pyside6 import CLineEdit, CLabel
|
|
302
|
+
# PyQt6
|
|
303
|
+
from cwidgets.pyqt6 import CLineEdit, CLabel
|
|
304
|
+
|
|
305
|
+
# إنشاء الحقل أولاً لـ buddy
|
|
306
|
+
self.edit = CLineEdit(self)
|
|
307
|
+
self.lbl = CLabel("الاسم:", self, self.edit)
|
|
308
|
+
layout.addWidget(self.lbl)
|
|
309
|
+
layout.addWidget(self.edit)
|
|
310
|
+
|
|
311
|
+
# مع نص مبدئي
|
|
312
|
+
self.edit = CLineEdit(self, "القاهرة")
|
|
313
|
+
|
|
314
|
+
# مع نص إرشادي placeholder
|
|
315
|
+
self.edit = CLineEdit(self, placeholderText="أدخل اسمك...")
|
|
316
|
+
|
|
317
|
+
# إشارة validated
|
|
318
|
+
self.edit.validated.connect(self.on_validated)
|
|
319
|
+
|
|
320
|
+
def on_validated(self, text):
|
|
321
|
+
print(text)
|
|
322
|
+
|
|
323
|
+
# إظهار / إخفاء — أصلي Qt
|
|
324
|
+
self.edit.hide()
|
|
325
|
+
self.edit.show()</pre>
|
|
326
|
+
|
|
327
|
+
<h2 id="ccombobox">CComboBox</h2>
|
|
328
|
+
|
|
329
|
+
<h3>التعريف</h3>
|
|
330
|
+
<p>CComboBox هو قائمة منسدلة متوافقة، يحل محل QComboBox.</p>
|
|
331
|
+
|
|
332
|
+
<h3>لماذا؟</h3>
|
|
333
|
+
<p>QComboBox الأصلي يتم التفعيل داخله بواسطة الأسهم .<br>
|
|
334
|
+
و هذا يمثل مشكلة للمكفوفين الذين يتنقلون بالإسهم .</p>
|
|
335
|
+
|
|
336
|
+
<h3>الحل</h3>
|
|
337
|
+
<p>فصل صريح بين التنقل والتفعيل .<br>
|
|
338
|
+
مع المحافظة على التوافقية حتى في حالة تعطيل القائمة .</p>
|
|
339
|
+
|
|
340
|
+
<h3>الميزات</h3>
|
|
341
|
+
<ul>
|
|
342
|
+
<li>التنقل الحر: ↑ ↓ للتنقل بدون تفعيل .</li>
|
|
343
|
+
<li>التفعيل الصريح: Enter، Return، Space → بواسطة إشارة validated.</li>
|
|
344
|
+
<li>الإشارة cleared: تُصدر عندما يتحقق المستخدم من قائمة فارغة.</li>
|
|
345
|
+
<li>التعطيل المتاح: يعلن NVDA "غير متاح".</li>
|
|
346
|
+
<li>العنوان عبر CLabel مع buddy — لا يُنصح باستخدام setAccessibleName لأنه يحل محل الملصق.</li>
|
|
347
|
+
</ul>
|
|
348
|
+
|
|
349
|
+
<h3>الاستخدام</h3>
|
|
350
|
+
<pre>
|
|
351
|
+
# PySide6
|
|
352
|
+
from cwidgets.pyside6 import CComboBox, CLabel, CMessageBox, CButton
|
|
353
|
+
# PyQt6
|
|
354
|
+
from cwidgets.pyqt6 import CComboBox, CLabel, CMessageBox, CButton
|
|
355
|
+
|
|
356
|
+
# إنشاء القائمة أولاً لـ buddy
|
|
357
|
+
self.combo = CComboBox(self)
|
|
358
|
+
self.combo.addItems(["مصر", "تونس", "المغرب"])
|
|
359
|
+
self.lbl = CLabel("قائمة الدول:", self, self.combo)
|
|
360
|
+
layout.addWidget(self.lbl)
|
|
361
|
+
layout.addWidget(self.combo)
|
|
362
|
+
|
|
363
|
+
self.combo.validated.connect(self.on_selection)
|
|
364
|
+
self.combo.cleared.connect(self.on_cleared)
|
|
365
|
+
|
|
366
|
+
# زر يفرغ القائمة
|
|
367
|
+
self.btn_clear = CButton("تفريغ", self)
|
|
368
|
+
self.btn_clear.clicked.connect(self.combo.clear)
|
|
369
|
+
|
|
370
|
+
def on_selection(self):
|
|
371
|
+
text = self.combo.currentText()
|
|
372
|
+
index = self.combo.currentIndex()
|
|
373
|
+
CMessageBox.information(self, "التحديد", f"الدولة: {text}")
|
|
374
|
+
|
|
375
|
+
def on_cleared(self):
|
|
376
|
+
CMessageBox.warning(self, "تحذير", "لا توجد عناصر متاحة في القائمة.")
|
|
377
|
+
|
|
378
|
+
# تعطيل / إعادة تفعيل
|
|
379
|
+
self.combo.setEnabled(False)
|
|
380
|
+
self.combo.setEnabled(True)</pre>
|
|
381
|
+
|
|
382
|
+
<h2 id="clistwidget">CListWidget</h2>
|
|
383
|
+
|
|
384
|
+
<h3>التعريف</h3>
|
|
385
|
+
<p>CListWidget هي قائمة متوافقة ، تحل محل QListWidget.</p>
|
|
386
|
+
|
|
387
|
+
<h3>لماذا؟</h3>
|
|
388
|
+
<p>QListWidget الأصلي يتم التفعيل داخله فورًا عند التنقل باستخدام الأسهم.<br>
|
|
389
|
+
و هذا يمثل عائقا للمكفوفين و تعطيل هذا التفعيل بالأسهم يتطلب كتابة أسطر كودية إضافية .</p>
|
|
390
|
+
|
|
391
|
+
<h3>الحل</h3>
|
|
392
|
+
<ul>
|
|
393
|
+
<li>فصل بين التنقل والتفعيل .</li>
|
|
394
|
+
<li>الحفاظ على إمكانية الوصول في وضع التعطيل.</li>
|
|
395
|
+
</ul>
|
|
396
|
+
|
|
397
|
+
<h3>الميزات</h3>
|
|
398
|
+
<ul>
|
|
399
|
+
<li>التنقل الحر: ↑ ↓ للتنقل بدون تفعيل .</li>
|
|
400
|
+
<li>التفعيل الصريح: Enter، Return، Space.</li>
|
|
401
|
+
<li>في حالة التعطيل : يعلن NVDA "غير متاح".</li>
|
|
402
|
+
<li>العنوان عبر CLabel مع buddy — لا يُنصح باستخدام setAccessibleName لأنه يحل محل الملصق.</li>
|
|
403
|
+
</ul>
|
|
404
|
+
|
|
405
|
+
<h3>الاستخدام</h3>
|
|
406
|
+
<pre>
|
|
407
|
+
# PySide6
|
|
408
|
+
from cwidgets.pyside6 import CListWidget, CLabel
|
|
409
|
+
# PyQt6
|
|
410
|
+
from cwidgets.pyqt6 import CListWidget, CLabel
|
|
411
|
+
|
|
412
|
+
# إنشاء القائمة
|
|
413
|
+
self.list = CListWidget(self)
|
|
414
|
+
self.list.addItems(["العراق", "السعودية", "الكويت"])
|
|
415
|
+
self.lbl = CLabel("قائمة الدول:", self, self.list)
|
|
416
|
+
layout.addWidget(self.lbl)
|
|
417
|
+
layout.addWidget(self.list)
|
|
418
|
+
|
|
419
|
+
# إشارة أصلية Qt — مطابقة لـ QListWidget
|
|
420
|
+
self.list.itemActivated.connect(self.on_item)
|
|
421
|
+
|
|
422
|
+
def on_item(self, item):
|
|
423
|
+
text = item.text()
|
|
424
|
+
row = self.list.currentRow()
|
|
425
|
+
print(row, text)
|
|
426
|
+
|
|
427
|
+
# تعطيل / إعادة تفعيل
|
|
428
|
+
self.list.setEnabled(False)
|
|
429
|
+
self.list.setEnabled(True)
|
|
430
|
+
|
|
431
|
+
# تفريغ — أصلي Qt
|
|
432
|
+
self.list.clear()</pre>
|
|
433
|
+
|
|
434
|
+
<h2 id="cmessagebox">CMessageBox</h2>
|
|
435
|
+
|
|
436
|
+
<h3>التعريف</h3>
|
|
437
|
+
<p>CMessageBox هي محاورة رسائل متوافقة.</p>
|
|
438
|
+
|
|
439
|
+
<h3>لماذا؟</h3>
|
|
440
|
+
<p>QMessageBox الأصلي لا يوفر إغلاقًا تلقائيًا .<br>
|
|
441
|
+
نحتاجه للرسائل التي لا تتطلب تدخلا من المستخدم .</p>
|
|
442
|
+
|
|
443
|
+
<h3>الحل</h3>
|
|
444
|
+
<p>إضافة وضع تغلق فيه الرسالة بعد زمن محدد .</p>
|
|
445
|
+
|
|
446
|
+
<h3>الميزات</h3>
|
|
447
|
+
<ul>
|
|
448
|
+
<li>information: مؤقت أو غير مؤقت — بدون صوت.</li>
|
|
449
|
+
<li>warning: مؤقت أو غير مؤقت — بدون صوت.</li>
|
|
450
|
+
<li>critical: دائمًا غير مؤقت + صوت النظام — إغلاق يدوي إلزامي.</li>
|
|
451
|
+
<li>الإغلاق اليدوي ممكن دائمًا قبل انتهاء المهلة.</li>
|
|
452
|
+
</ul>
|
|
453
|
+
|
|
454
|
+
<h3>الاستخدام</h3>
|
|
455
|
+
<pre>
|
|
456
|
+
# PySide6
|
|
457
|
+
from cwidgets.pyside6 import CMessageBox
|
|
458
|
+
# PyQt6
|
|
459
|
+
from cwidgets.pyqt6 import CMessageBox
|
|
460
|
+
|
|
461
|
+
# معلومات غير مؤقتة
|
|
462
|
+
CMessageBox.information(self, "نجاح", "تم حفظ الملف.")
|
|
463
|
+
|
|
464
|
+
# معلومات مؤقتة — إغلاق تلقائي بعد 3 ثوانٍ
|
|
465
|
+
CMessageBox.information(self, "نجاح", "تم حفظ الملف.", timeout=3000)
|
|
466
|
+
|
|
467
|
+
# تحذير غير مؤقت
|
|
468
|
+
CMessageBox.warning(self, "تحذير", "مساحة القرص غير كافية.")
|
|
469
|
+
|
|
470
|
+
# تحذير مؤقت
|
|
471
|
+
CMessageBox.warning(self, "تحذير", "الاتصال غير مستقر.", timeout=4000)
|
|
472
|
+
|
|
473
|
+
# خطأ — غير مؤقت + صوت — إغلاق يدوي إلزامي
|
|
474
|
+
CMessageBox.critical(self, "خطأ", "الملف غير موجود.")
|
|
475
|
+
|
|
476
|
+
# يُنصح باستخدام المهلة فقط لـ information و warning
|
|
477
|
+
# المهلة غير متاحة لـ critical</pre>
|
|
478
|
+
|
|
479
|
+
<h1 id="best-practices">أفضل الممارسات</h1>
|
|
480
|
+
|
|
481
|
+
<ol>
|
|
482
|
+
<li>العناوين: استخدم دائمًا CLabel مع buddy .</li>
|
|
483
|
+
<pre>
|
|
484
|
+
# غير منصوح به — يحل محل label
|
|
485
|
+
self.combo.setAccessibleName("...")
|
|
486
|
+
|
|
487
|
+
# صحيح
|
|
488
|
+
self.lbl = CLabel("قائمة الدول:", self, self.combo)</pre>
|
|
489
|
+
|
|
490
|
+
<li>التعطيل: setEnabled(False) متاح افتراضيًا على جميع المكونات C*.</li>
|
|
491
|
+
|
|
492
|
+
<li>التفعيل:<br>
|
|
493
|
+
- CLineEdit و CComboBox → إشارة validated<br>
|
|
494
|
+
- CListWidget → إشارة أصلية Qt itemActivated</li>
|
|
495
|
+
|
|
496
|
+
<li>ترتيب الإنشاء مع buddy: قم دائمًا بإنشاء العنصر قبل CLabel الخاص به.</li>
|
|
497
|
+
<pre>
|
|
498
|
+
# صحيح — العنصر يُنشأ قبل الملصق
|
|
499
|
+
self.combo = CComboBox(self)
|
|
500
|
+
self.lbl = CLabel("الدولة:", self, self.combo)</pre>
|
|
501
|
+
</ol>
|
|
502
|
+
|
|
503
|
+
<h1 id="requirements">متطلبات التشغيل</h1>
|
|
504
|
+
<ul>
|
|
505
|
+
<li>PySide6 أو PyQt6</li>
|
|
506
|
+
<li>pywin32 — فقط لـ CTextEdit (دمج Win32 RichEdit)</li>
|
|
507
|
+
</ul>
|
|
508
|
+
<p>المقدمة من المكتبة:<br>
|
|
509
|
+
- validate_parent — التحقق المنتظم من النافذة parent<br>
|
|
510
|
+
- logger — تسجيل الأخطاء (الوحدة "cwidgets")</p>
|
|
511
|
+
|
|
512
|
+
<h1 id="limits">حدود المكتبة</h1>
|
|
513
|
+
<ul>
|
|
514
|
+
<li>CTextEdit يعتمد على Win32 RichEdit — متوافق مع Windows فقط.</li>
|
|
515
|
+
<li>لا يُنصح باستخدام setAccessibleName مع CComboBox و CListWidget لأنه يحل محل buddy CLabel.</li>
|
|
516
|
+
<li>CButton: التظليل الرمادي عبر stylesheet — Windows لا يقوم بتظليل الزر تلقائيًا عند الحفاظ عليه نشطًا لـ NVDA.</li>
|
|
517
|
+
</ul>
|
|
518
|
+
|
|
519
|
+
<h1 id="developer">المطوّر</h1>
|
|
520
|
+
<p>محمد الهادي بالطيب (تونس)</p>
|
|
521
|
+
<p>البريد الإلكتروني : hedidouz@gmail.com</p>
|
|
522
|
+
<p>تاريخ تصميم المكتبة : ماي 2026</p>
|
|
523
|
+
|
|
524
|
+
<h1 id="conclusion">الخاتمة</h1>
|
|
525
|
+
<p>يهدف هذا المشروع إلى جعل تطبيقات Qt متاحة بنسبة 100% للمطورين والمستخدمين المكفوفين، دون التضحية بالإنتاجية أو عادات مطوري Qt.</p>
|
|
526
|
+
</body>
|
|
527
|
+
</html>
|