توفر Qt للمبرمج ميزات غنية جدًا ، ولكن مجموعة الأدوات محدودة. إذا لم يكن أي من الخيارات المتاحة مناسبًا ، فعليك رسم شيء خاص بك. إن أبسط طريقة - باستخدام الصور الجاهزة - لها عيوب خطيرة: الحاجة إلى تخزين الصور في ملف أو موارد ، ومشاكل قابلية التوسع وتنقل تنسيقات الصور. فيما يلي وصف لاستخدام مبادئ الرسومات المتجهة دون استخدام الصور المتجهة الفعلية.
الديباجة
بدأ كل شيء بحقيقة أنه بمجرد الحاجة إلى إشارة إلى علامات بت واحدة. يتلقى بعض التطبيقات بعض البيانات على بعض المنافذ ، ويجب تفكيك الحزمة وعرضها على الشاشة. سيكون من الجيد في نفس الوقت تقليد لوحة القيادة المألوفة بطريقة أو بأخرى. لعرض البيانات الرقمية ، تقدم Qt فئة "QLCDNumber" "خارج الصندوق" ، على غرار المؤشرات السبعة المألوفة ، ولكن هناك شيء غير مرئي في المصابيح الفردية.
يعد استخدام العلامات (فهي مربعات اختيار) والمفاتيح (إنها أزرار اختيار) لهذه الأغراض أمرًا سيئًا ، وفيما يلي قائمة بالأسباب:
- هذا خطأ دلالة. الأزرار - إنها أزرار ، وهي مخصصة لإدخال المستخدم ، وليس لإظهار أي شيء له.
- هذا يعني الثاني: يسعى المستخدم لكزة في مثل هذه الأزرار. إذا لم يكن تحديث المعلومات في الوقت نفسه سريعًا بشكل خاص ، فستكمن الإشارة ، وسيبلغ المستخدم عن برنامج معطل ، يضحك بشدة.
- إذا قمت بقفل الزر للضغط على (setEnabled (false)) ، فإنه يصبح رماديًا قبيحًا. أتذكر أنه في دلفي ، في منطقة الإصدار 6 ، كان هناك مثل هذا الخداع مع الأذنين: يمكنك وضع علامة على اللوحة وتعطيل إتاحة اللوحة ، وليس العلم ، ثم لم يكن العلم رماديًا أو نشطًا. هذه الحيلة لا تعمل هنا.
- الأزرار لها تركيز الإدخال. وفقًا لذلك ، إذا كانت هناك عناصر إدخال في النافذة ، وكان المستخدم يمشي عليها باستخدام مفتاح Tab ، فسيتعين عليه السير على طول عناصر الإخراج ، وهو أمر غير مريح وقبيح.
- في النهاية ، تبدو هذه الأزرار بشكل غير جمالي ، خاصةً بجوار الجزء السابع.
الخلاصة: تحتاج إلى رسم مصباح كهربائي بنفسك.
طحين الاختيار
في البداية بحثت عن حلول جاهزة. في ذلك الوقت ، عندما استخدمت دلفي ، يمكنك العثور على كمية هائلة من المكونات النهائية ، سواء من الشركات الجادة أو الشركات المصنعة للهواة. لدى Qt الكثير من المتاعب مع هذا. لدى QWT بعض العناصر ، ولكن ليس هذا. لم أر هواة على الإطلاق. على الأرجح ، إذا قمت بالحفر بشكل صحيح على Github ، فيمكنك العثور على شيء ما ، ولكن ربما سأفعل ذلك بشكل أسرع بنفسي.
أول شيء اقترح نفسه من الملف المنزلي هو استخدام ملفي صور مع صور للضوء وإيقاف تشغيله. سيء:
- من الضروري العثور على صور جيدة (أو الرسم ، لكني لست فنانًا) ؛
- السؤال المبدئي هو: الربط ليس جيدا ، حتى الصور ، حتى الكذب تحت قدميك.
- يجب تخزينها في مكان ما. الملفات سيئة للغاية: تم محوها عن طريق الخطأ - ولا توجد أزرار. الموارد أفضل ، لكنني لا أشعر أيضًا إذا كان بإمكاني تجاوز الأمر ؛
- لا قابلية للتوسع ؛
- يمكن تحقيق التخصيص (الألوان ، على سبيل المثال) عن طريق إضافة الملفات فقط. أي أن الموارد كثيفة وغير مرنة.
الشيء الثاني الذي يتبع من الأول هو استخدام الصور المتجهة بدلاً من الصور. علاوة على ذلك ، يمكن لـ Qt تقديم SVG. هنا أصبح الأمر أسهل قليلاً مع البحث عن الصورة نفسها: هناك الكثير من الدروس حول الرسومات المتجهة في الشبكة ، يمكنك العثور على شيء أكثر أو أقل ملاءمة وتكييفه مع احتياجاتك. لكن يبقى السؤال عن التخزين والتخصيص ، والعرض ليس مجانيًا للموارد. البنسات ، بالطبع ، ولكن لا يزال ...
والثالث يتبع من الثانية: يمكنك استخدام مبادئ الرسومات المتجهة للصور ذاتية الرسم! يشير ملف صورة متجه في شكل نص إلى كيفية الرسم. يمكنني تحديد نفس الرمز باستخدام دروس المتجهات. لحسن الحظ ، يحتوي كائن QPainter على الأدوات اللازمة: قلم ، وفرشاة ، وتدرج ورسم بدائي ، وحتى تعبئة نسيج. نعم ، الأدوات بعيدة كل البعد عن: لا توجد أقنعة ، أوضاع مزج ، ولكن لا حاجة على الإطلاق إلى الواقعية الواقعية.
بحثت عن بعض الأمثلة على الشبكة. أخذ الدرس الأول الذي جاء فيه: "نرسم زرًا في محرر رسومات Inkscape" من موقع "من السهل الرسم". الزر من هذا الدرس يشبه المصباح الكهربائي أكثر من الزر الذي يناسبني تمامًا. أقوم بإعداد مسودة: بدلاً من Inkscape ، مشروع في Qt.
اختبار الريشة
أقوم بإنشاء مشروع جديد. أختار اسم المشروع rgbled (لأنني أريد أن أفعل شيئًا مثل RGB LED) والمسار إليه. اخترت QWidget من الفئة الأساسية واسم RgbLed ، أرفض إنشاء ملف نموذج. المشروع بشكل افتراضي بعد التشغيل يجعل نافذة فارغة ، لا يزال غير مهم.
التحضير للرسم
هناك فراغ. الآن أنت بحاجة إلى الحصول على الأعضاء الخاصين في الفصل ، والذي سيحدد هندسة الصورة. الميزة الأساسية للرسومات المتجهة هي قابليتها للتوسع ، لذلك يجب أن يكون هناك حد أدنى من الأرقام الثابتة ، وهي تحدد النسب فقط. سيتم إعادة حساب الأبعاد في حدث resizeEvent () ، الذي يجب إعادة تعريفه.
في البرنامج التعليمي للرسم المستخدم ، يتم تحديد الأبعاد بالبكسل أثناء التنقل. أحتاج إلى تحديد ما سأستخدمه وكيفية إعادة الحساب مسبقًا.
تتكون الصورة المرسومة من العناصر التالية:
- الحلقة الخارجية (مائلة إلى الخارج ، جزء من حافة محدبة)
- الحلقة الداخلية (مائلة للداخل)
- غلاف مصباح LED ، "زجاج"
- ظل على حافة الزجاج
- تسليط الضوء على أعلى
- توهج القاع
يتم تحديد الدوائر المركزة ، أي كل شيء باستثناء الوهج ، من خلال موضع المركز ونصف القطر. يتم تحديد الوهج بواسطة المركز والعرض والارتفاع ، ويتوافق موضع المراكز الساطعة مع الوضعية مع موضع المركز X للصورة بأكملها.
لحساب عناصر الهندسة ، تحتاج إلى تحديد أيهما أكبر - العرض أو الارتفاع ، لأن المصباح دائري ويجب أن يتناسب مع مربع يساوي جانبه أصغر من البعدين. لذا ، أضفت الأعضاء الخاصين المطابقين إلى ملف الرأس.
كودprivate: int height; int width; int minDim; int half; int centerX; int centerY; QRect drawingRect; int outerBorderWidth; int innerBorderWidth; int outerBorderRadius; int innerBorderRadius; int topReflexY; int bottomReflexY; int topReflexWidth; int topReflexHeight; int bottomReflexWidth; int bottomReflexHeight;
ثم أعد تحديد الوظيفة المحمية التي يتم استدعاؤها عند تغيير حجم الأداة.
كود protected: void resizeEvent(QResizeEvent *event); void RgbLed::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); this->height = this->size().height(); this->width = this->size().width(); this->minDim = (height > width) ? width : height; this->half = minDim / 2; this->centerX = width / 2; this->centerY = height / 2; this->outerBorderWidth = minDim / 10; this->innerBorderWidth = minDim / 14; this->outerBorderRadius = half - outerBorderWidth; this->innerBorderRadius = half - (outerBorderWidth + innerBorderWidth); this->topReflexY = centerY - (half - outerBorderWidth - innerBorderWidth) / 2; this->bottomReflexY = centerY + (half - outerBorderWidth - innerBorderWidth) / 2; this->topReflexHeight = half / 5; this->topReflexWidth = half / 3; this->bottomReflexHeight = half / 5; this->bottomReflexWidth = half / 3; drawingRect.setTop((height - minDim) / 2); drawingRect.setLeft((width - minDim) / 2); drawingRect.setHeight(minDim); drawingRect.setWidth(minDim); }
هنا ، يتم حساب جانب المربع الذي تم تسجيل المصباح فيه ، ومركز هذا المربع ، ونصف قطر الحافة الذي يشغل أقصى مساحة ممكنة ، وعرض الحافة ، التي يجب أن يكون الجزء الخارجي منها 1/10 من القطر ، والداخل 1/14. ثم يتم حساب موضع الوهج ، الذي يقع في منتصف نصف القطر العلوي والسفلي ، ويتم تحديد العرض والارتفاع بالعين.
بالإضافة إلى ذلك ، في الحقول المحمية ، سأضيف على الفور مجموعة من الألوان لاستخدامها.
كود QColor ledColor; QColor lightColor; QColor shadowColor; QColor ringShadowDarkColor; QColor ringShadowMedColor; QColor ringShadowLightColor; QColor topReflexUpColor; QColor topReflexDownColor; QColor bottomReflexCenterColor; QColor bottomReflexSideColor;
من خلال الأسماء ، من الواضح تقريبًا أن هذه هي ألوان المصباح الكهربائي ، والجزء المضيء من الظل ، والجزء المظلم من الظل ، والألوان الثلاثة للظل الحلقي حول المصباح الكهربائي وألوان التدرجات اللامعة.
يجب تهيئة الألوان ، لذلك سأكمل قطعة عمل المصمم.
كود RgbLed::RgbLed(QWidget *parent) : QWidget(parent), ledColor(Qt::green), lightColor(QColor(0xE0, 0xE0, 0xE0)), shadowColor(QColor(0x70, 0x70, 0x70)), ringShadowDarkColor(QColor(0x50, 0x50, 0x50, 0xFF)), ringShadowMedColor(QColor(0x50, 0x50, 0x50, 0x20)), ringShadowLightColor(QColor(0xEE, 0xEE, 0xEE, 0x00)), topReflexUpColor(QColor(0xFF, 0xFF, 0xFF, 0xA0)), topReflexDownColor(QColor(0xFF, 0xFF, 0xFF, 0x00)), bottomReflexCenterColor(QColor(0xFF, 0xFF, 0xFF, 0x00)), bottomReflexSideColor(QColor(0xFF, 0xFF, 0xFF, 0x70)) { }
أيضا ، لا تنس أن تدرج في ملف الرأس شوائب الفئات التي ستكون مطلوبة عند الرسم.
كود #include <QPainter> #include <QPen> #include <QBrush> #include <QColor> #include <QGradient>
يتم تجميع هذا الرمز بنجاح ، ولكن لم يتغير شيء في نافذة الأداة. حان الوقت لبدء الرسم.
رسم
أدخلت وظيفة مغلقة
void drawLed(const QColor &color);
وإعادة تعريف الوظيفة المحمية
void paintEvent(QPaintEvent *event);
سيتسبب حدث إعادة الرسم في الرسم الفعلي الذي يتم تمرير لون "الزجاج" إليه كمعلمة.
كود void RgbLed::paintEvent(QPaintEvent *event) { QWidget::paintEvent(event); this->drawLed(ledColor); }
حتى الآن. ونبدأ في ملء وظيفة الرسم تدريجيًا.
كود void RgbLed::drawLed(const QColor &color) { QPainter p(this); QPen pen; pen.setStyle(Qt::NoPen); p.setPen(pen); }
أولاً ، يتم إنشاء كائن فنان ، والذي سيشارك في الرسم. ثم يتم إنشاء قلم رصاص مطلوب حتى لا يكون هناك قلم رصاص: في هذه الصورة ، لا يلزم حد المخطط التفصيلي ، ولكنه غير ضروري على الإطلاق.
ثم يتم رسم الدائرة الأولى بشكل تقريبي مع الدرس حول الرسومات المتجهة: دائرة كبيرة مليئة بتدرج شعاعي. يحتوي التدرج على نقطة ربط خفيفة في الأعلى ، ولكن ليس على الحافة نفسها ، ونقطة داكنة في الأسفل ، ولكن ليس أيضًا على الحافة نفسها. يتم إنشاء الفرشاة بناءً على التدرج ، حيث يرسم رسام الفرشاة دائرة (أي ، القطع الناقص المدرج في مربع). اتضح مثل هذا الرمز
كود QRadialGradient outerRingGradient(QPoint(centerX, centerY - outerBorderRadius - (outerBorderWidth / 2)), minDim - (outerBorderWidth / 2))
تؤكد البيئة على معلمة اللون للدالة drawLed لأنها غير مستخدمة. دعه يتحمل ، ليست هناك حاجة إليه بعد ، لكنه سيحتاجه قريبًا. ينتج المشروع الذي تم إطلاقه النتيجة التالية:
أضف دفعة أخرى من التعليمات البرمجية.
كود QRadialGradient innerRingGradient(QPoint(centerX, centerY + innerBorderRadius + (innerBorderWidth / 2)), minDim - (innerBorderWidth / 2))
تقريبًا نفس الدائرة ، أصغر حجمًا ومقلوبًا. نحصل على الصورة التالية:
ثم تحتاج في النهاية إلى لون الزجاج:
كود QColor dark(color.darker(120)); QRadialGradient glassGradient(QPoint(centerX, centerY), innerBorderRadius); glassGradient.setColorAt(0, color); glassGradient.setColorAt(1, dark); QBrush glassBrush(glassGradient); p.setBrush(glassBrush); p.drawEllipse(QPoint(centerX, centerY), innerBorderRadius, innerBorderRadius);
هنا ، باستخدام وظيفة أغمق من اللون المرسل ، يتم الحصول على نفس اللون ، ولكن أغمق ، لتنظيم التدرج. معامل 120 محدد بالعين. ها هي النتيجة:
أضف ظلًا حلقيًا حول الزجاج. يتم ذلك في الدرس حول الرسومات المتجهة ، وهذا يجب أن يضيف الحجم والواقعية:
كود QRadialGradient shadowGradient(QPoint(centerX, centerY), innerBorderRadius); shadowGradient.setColorAt(0, ringShadowLightColor); shadowGradient.setColorAt(0.85, ringShadowMedColor); shadowGradient.setColorAt(1, ringShadowDarkColor); QBrush shadowBrush(shadowGradient); p.setBrush(shadowBrush); p.drawEllipse(QPoint(centerX, centerY), innerBorderRadius, innerBorderRadius);
هناك تدرج من ثلاث خطوات ، بحيث يكون الظل أكثر سمكًا للحافة ويتحول إلى شاحب نحو المركز. اتضح مثل هذا:
أضف النقاط البارزة في نفس الوقت. يتم تمييز الجزء العلوي ، على عكس الجزء السفلي (وجميع العناصر الأخرى) ، بتدرج خطي. فنان لي كئيب ، آخذ كلمتي لمؤلف الدرس. ربما هناك بعض الحقيقة في ذلك ، لن أجرب أنواعًا مختلفة من التدرجات.
كود QLinearGradient topTeflexGradient(QPoint(centerX, (innerBorderWidth + outerBorderWidth)), QPoint(centerX, centerY))
هذا ، في الواقع ، كل شيء ، مصباح كهربائي جاهز ، كما هو الحال في KDPV.
تتأثر رؤية الوهج وانتفاخ الزجاج باللون ، أو بالأحرى كم هو مظلم. قد يكون من المنطقي إضافة تعديل لسطوع الوهج ومعامل التعتيم في الوظيفة الداكنة اعتمادًا على الظلام ، ولكن هذا هو الكمالية ، على ما أعتقد.
فيما يلي مثال للاستخدام في نافذة البرنامج.
التدليل
من أجل المتعة ، يمكنك اللعب بالورود. على سبيل المثال ، تجاوز حدث النقر بالماوس المحمي
void mousePressEvent(QMouseEvent *event);
بهذه الطريقة:
كود void RgbLed::mousePressEvent(QMouseEvent *event) { static int count = 0; if (event->button() == Qt::LeftButton) { switch (count) { case 0: ledColor = Qt::red; count++; break; case 1: ledColor = Qt::green; count++; break; case 2: ledColor = Qt::blue; count++; break; case 3: ledColor = Qt::gray; count++; break; default: ledColor = QColor(220, 30, 200); count = 0; break; } this->repaint(); } QWidget::mousePressEvent(event); }
عدم نسيان إضافة أحداث الماوس إلى الرأس:
#include <QMouseEvent>
الآن ، سيؤدي النقر بالماوس على المكون إلى تبديل لون المصباح الكهربائي: الأحمر والأخضر والأزرق والرمادي وبعض الضوء العشوائي المحدد من المصباح.
الخاتمة
بالنسبة للرسم ، هذا كل شيء. وينبغي أن تضيف القطعة الوظيفة. في حالتي ، تمت إضافة حقل منطقي "حالة استخدام" ، وحقل منطقي آخر يحدد الحالة "تشغيل" أو "إيقاف التشغيل" والألوان الافتراضية لهذه الحالات ، بالإضافة إلى الأحرف المفتوحة والمستوطنين لكل هذا. يتم استخدام هذه الحقول في وظيفة paintEvent () لتحديد اللون الذي تم تمريره إلى drawLed () كمعلمة. ونتيجة لذلك ، يمكنك إيقاف استخدام الحالات وضبط المصباح على أي لون ، أو تشغيل الحالات وتشغيل المصباح أو إيقاف تشغيله وفقًا للأحداث. ومن الملائم بشكل خاص جعل أداة ضبط الحالة فتحة مفتوحة و ملتصقين سواء كان ذلك مع الإشارة التي يجب رصدها.
يوضح استخدام mousePressEvent أنه لا يمكن إنشاء عنصر واجهة مستخدم فقط كمؤشر ، بل وأيضًا زر ، مما يجعله مضغوطًا ، أو متحررًا ، أو منحنيًا ، أو ملتويًا ، أو ملونًا وكل ما تريد لأحداث التأشير والنقر والإفراج.
لكن هذا لم يعد أساسيا. كان الهدف هو إظهار أين يمكنك أن تأخذ قدوة عند رسم الحاجيات الخاصة بك وكيف أن هذا الرسم سهل التنفيذ دون استخدام الصور النقطية أو المتجهة ، في الموارد أو الملفات.