איך להתיידד עם qmllint
As מפתחי Qtאנו מייצרים שורות רבות של קוד QML מדי יום. כמובן, כולנו מודעים לחשיבותם של חלקי קוד הניתנים לתחזוקה ומאורגנים היטב. אנו מכירים את שיטות העבודה המומלצות, ואנו משתדלים לפעול לפיהן בכל פעם שאנו מוסיפים קוד חדש לבסיס הקיים. עם זאת, ככל שהפרויקט שלנו נעשה מורכב יותר, ייתכן שיהיה קשה לעקוב אחר כל שיטות העבודה המומלצות ולהבטיח שהקוד עומד בסטנדרטים הנדרשים. זו אחת הסיבות לכך שכדאי לשקול את qmllint כמועמד להיות החבר החדש שלך.
במאמר זה, אני רוצה להראות לכם כיצד אני עובד עם qmllint וכיצד תוכלו לשלב בדיקות qmllint במשימות היומיומיות שלכם.
מה זה qmllint?
בקצרה, qmllint הוא כלי linter רב-עוצמה שתוכנן במיוחד עבור QML. מטרתו העיקרית היא לבדוק את התקינות התחבירית של קבצי QML ולהזהיר מפני כמה anti-patterns של QML. הוא ניתן להגדרה בקלות כך שנוכל לאפשר בדיקה והדפסה רק של האזהרות שמעניינות אותנו. אני תמיד מפעיל את כולן כדי לוודא שקוד ה-QML נכתב ללא כל טעויות.
לדוגמה, כלי זה יכול להתריע בפניך על:
- גישה בלתי מורשית לנכסים
- בעיות הקשורות לקומפילציה של קוד QML
- קוד מיושן
- רבות אחרות…
השימוש בכלי בהחלט יקל ליישם את שיטות העבודה המומלצות ל-clean code ולשמור על הקוד במצב הטוב ביותר האפשרי.
מאיפה להתחיל
הכנתי קטע קוד קצר עבור מאמר זה. כמובן, השגיאות בקוד נעשו בכוונה כדי להדגים את השימוש בכלי זה.
הנה קוד ההפעלה:
import QtQuick import QtQuick.Window import QtQuick.Controls Window { width: 640 height: 480 visible: true title: qsTr("Hello World") readonly property int delegateWidth: 640 readonly property int delegateHeight: 30 ListModel { id: items ListElement { name: "red" textColor: "red" } ListElement { name: "blue" textColor: "blue" } ListElement { name: "green" textColor: "green" } } ListView { id: listView anchors.fill: parent model: items delegate: Label { text: model.name color: model.textColor width: delegateWidth height: delegateHeight horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter } } }
כעת, אנו יכולים לבדוק את הקוד עם כלי זה:
1. ודאו שהנתיב לספרייה שבה נמצא qmllint.exe מתווסף למשתני הסביבה.
2. פשוט הרץ את הפקודה.
3. qmllint <pathToFile>
4. בדוק את פלט המסוף.

כפי שניתן לראות, qmllint מציג לנו מספר אזהרות אפילו בקטע קוד קצר זה. אני יודע שלעולם לא תעשו טעויות מסוג זה, אך האמינו לי, חבריכם לצוות עלולים לעשות כן, או שאולי גם לכם יהיה יום רע ותשכחו להקפיד על שיטות העבודה המומלצות.
הבה נבחן מקרוב את הפלט. יש לנו כמה אזהרות גישה לא מוסמכת והמידע שהמודל הוזרק באופן מרומז אל ה-delegate. הוא לא רק מציג את האזהרות אלא גם נותן לנו מידע כיצד להיפטר מהן. אז הבה נפעל לפי ההצעות.
5. תקנו את האזהרות המודפסות בהתאם לפלט.
6. import QtQuick 7. import QtQuick.Window 8. import QtQuick.Controls 9. 10. Window { 11. id: root //הוספה id לרכיב השורש 12. width: 640 13. height: 480 14. visible: true 15. title: qsTr("Hello World") 16. 17. readonly property int delegateWidth: 640 18. readonly property int delegateHeight: 30 19. 20. ListModel { 21. id: items 22. 23. ListElement { 24. name: "red" 25. textColor: "red" 26. } 27. ListElement { 28. name: "blue" 29. textColor: "blue" 30. } 31. ListElement { 32. name: "green" 33. textColor: "green" 34. } 35. } 36. 37. ListView { 38. id: listView 39. anchors.fill: parent 40. model: items 41. 42. delegate: Label { 43. //הוספה מאפיין נדרש ל-delegate 44. required property string name 45. required property string textColor 46. 47. text: name 48. color: textColor 49. 50. //הוספה id לגישה ישירה למאפיינים מהשורש. 51. width: root.delegateWidth 52. height: root.delegateHeight 53. 54. horizontalAlignment: Qt.AlignHCenter 55. verticalAlignment: Qt.AlignVCenter 56. } 57. } 58. }
6. הרצת qmllint מחדש.

הוא מצא אזהרה נוספת וסיפק לנו מידע כיצד ניתן לתקן אותה. בואו נבצע את השינוי.
//נוסף pragma הכרחי בראש הקובץ
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
readonly property int delegateWidth: 640
readonly property int delegateHeight: 30
ListModel {
id: items
ListElement { name: "red"; textColor: "red" }
ListElement { name: "blue"; textColor: "blue" }
ListElement { name: "green"; textColor: "green" }
}
ListView {
id: listView
anchors.fill: parent
model: items
delegate: Label {
required property string name
required property string textColor
text: name
color: textColor
width: root.delegateWidth
height: root.delegateHeight
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
}
}
}
ההרצה הבאה של qmllint לא תדפיס דבר. תיקנו את כל הקובץ.
אוקיי, אז סיימנו את בדיקת ה-qmllint הראשונה שלנו. מה עכשיו?
יהיה נהדר להגדיר את הגדרות qmllint כך שיוכל לבדוק את כל הקבצים בפקודה אחת, לגרום ל-qmllint להיות מודע למודולים חיצוניים, לשמור את הפלט במקום כלשהו, ואולי נוכל להעניק לו יותר כוח כך שיוכל לפתור את האזהרות שלנו באופן אוטומטי.
הגדרת qmllint
ניתן להגדיר את qmllint בנפרד עבור כל פרויקט. הדבר יכול להיות שימושי אם ברצונכם להשבית חלק מהאזהרות או לספק נתיב למודולים נוספים.
אפשר להשיג את אותה תוצאה על ידי הרצת הפקודה qmllint עם אפשרויות נוספות, אך טוב בהרבה להגדיר את qmllint פעם אחת בקובץ נפרד. הוא יכול ליצור עבורנו את קובץ התצורה המוגדר כברירת מחדל.
qmllint –write-defaults
קובץ .qmllint.ini נוצר בספרייה שממנה הרצנו את הפקודה. qmllint מכיר את ההגדרות הללו אם הוא בודק את הקובץ הנמצא באותה ספרייה כמו ההגדרות או בספריות משנה. ניתן להתעלם מהגדרות אלו אם רוצים או לעקוף חלק מהאפשרויות בשורת הפקודה.
//ignore settings file qmllint --ignore-settings //overwrite CompilerWarnings option qmllint --compiler warning
כעת אנו יכולים לציין איזו קטגוריה יש להשבית או להתייחס אליה כהודעת מידע. במקרה שלנו, אני ממליץ להפעיל את CompilerWarnings. זה יזהיר אתכם לגבי חלקי קוד ה-qml שלא ניתן לקמפל על ידי qmlsc.
[Warnings]
ImportFailure=warning
ReadOnlyProperty=warning
BadSignalHandlerParameters=warning
UnusedImports=info
DuplicatedName=warning
PrefixedImportType=warning
AccessSingletonViaObject=warning
Deprecated=warning
ControlsSanity=disable
UnresolvedType=warning
LintPluginWarnings=disable
MultilineStrings=info
RestrictedType=warning
PropertyAliasCycles=warning
VarUsedBeforeDeclaration=warning
AttachedPropertyReuse=disable
RequiredProperty=warning
WithStatement=warning
InheritanceCycle=warning
UnqualifiedAccess=warning
UncreatableType=warning
MissingProperty=warning
InvalidLintDirective=warning
**CompilerWarnings=warning**
UseProperFunction=warning
NonListProperty=warning
IncompatibleType=warning
TopLevelComponent=warning
MissingType=warning
DuplicatePropertyBinding=warning
[General]
DisableDefaultImports=false
AdditionalQmlImportPaths=
OverwriteImportTypes=
DisablePlugins=
ResourcePath=
הבטחת מודעות למודולים
בואו נרחיב מעט את האפליקציה שלנו כך שתשתמש בכמה מודולים. לדוגמה, אנו רוצים ליצור כפתור מותאם אישית שישמש כנציג ב-ListView שלנו. ניתן להשתמש בכפתור גם בחלקים שונים של האפליקציה, ולכן זה בסדר לשים אותו במודול נפרד.
מבנה קבצים:

//Main.qml
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import CustomControls
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
readonly property int delegateWidth: 640
readonly property int delegateHeight: 30
ListModel {
id: items
ListElement {
name: "red"
textColor: "red"
}
ListElement {
name: "blue"
textColor: "blue"
}
ListElement {
name: "green"
textColor: "green"
}
ListElement {
name: "black"
textColor: "black"
}
}
ListView {
id: listView
anchors.fill: parent
model: items
delegate: CustomButton {
required property string name
required property string textColor
text: name
color: textColor
}
}
}
//CustomButton.qml
import QtQuick
import QtQuick.Controls.Fusion as Fusion
Fusion.Button {
id: control
property alias color: content.color
implicitWidth: 640
implicitHeight: 30
contentItem: Fusion.Label {
id: content
text: control.text
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
בואו נבצע lint ל-Main.qml.

מדוע יש כל כך הרבה אזהרות? ביצענו רק שינויים קלים בקובץ, כגון הוספת מודול ושינוי התווית ל-CustomButton. הסיבה היא שהכלי אינו מודע למודולים שיצרנו. אם כך, כיצד נוכל לגרום לו להיות מודע למודולים?
קיימות שתי דרכים:
1. אנו יכולים להריץ qmllint עם אפשרות נוספת.
qmllint -I imports Main.qml
2. הוסף את הנתיב לספריית האב של המודול בקובץ ההגדרות.
AdditionalQmlImportPaths=imports
בואו נריץ שוב את qmllint.

המודול נקרא כעת כראוי, וכל האזהרות נעלמו ללא כל שינוי בקוד. נותרה רק הודעה אחת האומרת שלקובץ יש import שאינו בשימוש. זה נותר מהגרסה הקודמת של הקוד כאשר השתמשנו ב-Label. קל לשכוח להסיר את ה-imports שאינם נחוצים עוד. לכן, בכל פעם שנוצר מודול חדש, עלינו לוודא שהנתיב מועבר כראוי להגדרות qmllint.
שמירת הפלט בקובץ JSON
במצבים מסוימים, אין די בהצגת הפלט ישירות בקונסול. לדוגמה, הפלט עשוי להיות כה ארוך שקריאת האזהרות אינה ברורה עוד, או שיש לשמור את הדוח במקום כלשהו לניתוח נוסף. זה מאפשר לנו לשמור בקלות את הפלט בפורמט JSON.
qmllint –json report.json Main.qml
הפקודה שלעיל יוצרת קובץ report.json. תוצאות הבדיקה (linting) של הקובץ Main.qml נשמרות בקובץ זה.
//report.json { "files": [ { "filename": "C:/Users/kaj/Documents/qmllint_test/Main.qml", "success": true, "warnings": [ { "charOffset": 74, "column": 1, "id": "unused-imports", "length": 6, "line": 5, "message": "Unused import", "suggestions": [], "type": "info" } ] } ], "revision": 3 }
בדיקת כל קבצי ה-QML באפליקציה
אוקיי, אנחנו יודעים כיצד לבדוק קובץ בודד. עם זאת, עבור פרויקטים גדולים יותר, יהיה הרבה יותר טוב להיות מסוגלים לבדוק את כל הקבצים בפרויקט בבדיקת qmllint אחת. ניתן להעביר מספר קבצים כארגומנטים. QMLllint יבדוק את כולם. עם זאת, זה עדיין לא הפתרון הטוב ביותר.
qmllint Main.qml imports/CustomControl/CustomButton.qml
אני ממליץ להסתיר את הרצת הפקודה בסקריפט. במקרה שלי, אני כותב סקריפט Python פשוט שבודק את כל קבצי ה-qml בספרייה ובספריות המשנה ומריץ עליהם את ה-qmllint. התוצאה נשמרת בקובץ report.json.
import os import subprocess import sys command = ["qmllint"] json_file = "report.json" directory = os.getcwd() qml_files = [] command += ["--json", json_file] for root, dirs, files in os.walk(directory): for file in files: if file.endswith(".qml"): path = os.path.join(root, file) qml_files.append(path) if not qml_files: print(f"No .qml files found in {directory}") sys.exit(1) command += qml_files print(f"Linting {len(qml_files)} QML files in {directory}") with open(json_file, "w") as f: subprocess.run(command, stdout=f, stderr=subprocess.PIPE)
qmllint עצמאי
בפרויקט גדול, ייתכן שתהיה לנו רשימה ארוכה מאוד של אזהרות במספר קבצים. רובן עשויות להיות קשורות ל-"גישה לא מוסמכת" (unqualified access). התיקון טריוויאלי, אך בוודאי ייקח זמן מה ליישם. במקרה זה, אנו יכולים להאציל את המשימה ל-qmllint לבצע זאת עבורנו. הבה ננסה זאת על הגרסה הקודמת של הקוד עם Label כ-delegate עבור ה-ListView שלנו:
delegate: Label { required property string name required property string textColor text: name color: textColor width: delegateWidth height: delegateHeight }
פלט qmllint:

כעת אנו בטוחים שהאזהרות קלות לתיקון, ו-qmllint יודע בדיוק כיצד לתקן אותן. לכן, אנו יכולים לאפשר לו לתקן אותן באופן אוטומטי. הדבר היחיד שעלינו לעשות הוא להריץ את אותה פקודה עם אפשרות נוספת "-f"
qmllint -f Main.qml
התיקון מיושם על Main.qml והגרסה הקודמת של הקובץ נשמרת בקובץ Main.qml.bak, כדי שנוכל לגבות אותה למקרה שמשהו ישתבש. Main.qml לאחר התיקון:
delegate: Label { required property string name required property string textColor text: name color: textColor width: root.delegateWidth height: root.delegateHeight }
כפי שאנו יכולים לראות, הוא עושה בדיוק את אותו הדבר שאנחנו עושים, אך אנו יכולים לחסוך זמן על ידי האצלת העבודה ל-qmllint.
תקציר
qmllint הוא כלי שימושי מאוד עבור מפתחי Qt/QML. אם משתמשים בו לעתים קרובות ובדרך הנכונה, הוא יכול להבטיח שקבצי ה-qml שלך נכתבים ללא שגיאות.
אני מקווה שהראיתי לכם מדוע כדאי להשתמש ב-qmllint בפרויקט שלכם וכיצד להשתמש בו. כמובן, ישנן דרכים רבות נוספות להשתמש בו, אך אלה שתיארתי במאמר, לדעתי, הן החשובות ביותר. הצעד הבא עבורכם הוא לשלב את בדיקות qmllint בצינור ה-CI שלכם ולראות כמה שגיאות פספסתם. זכרו ש-qmllint הוא חבר שלכם ותמיד יגיד לכם כאשר עשיתם טעות.
המומחים שלנו יעזרו לך לבנות יישומים איכותיים, עשירים בתכונות וידידותיים למשתמש באמצעות Qt. בקרו ב- עמוד ההצעות של Qt כדי לגלות עוד.
שאלות נפוצות
qmllint הוא כלי לניתוח סטטי עבור QML המסייע לזהות שגיאות, אזהרות ובעיות פוטנציאליות ביישומי Qt. הוא מאפשר למפתחים לזהות בעיות בשלב מוקדם, לפני הפעלת היישום.
שימוש ב-qmllint מסייע לשיפור איכות הקוד ומפחית שגיאות בזמן ריצה. הוא מדגיש שימוש שגוי במאפיינים, אי-התאמות טיפוסים ובעיות אחרות שעשויות שלא להיות גלויות מיד במהלך הפיתוח.
ניתן להריץ את qmllint משורת הפקודה על ידי הצבעה על קובץ QML או תיקיית פרויקט. הוא מנתח את הקוד ומחזיר רשימה של אזהרות ושגיאות הדורשות טיפול.
qmllint יכול לזהות שגיאות תחביר, קישורי מאפיינים לא חוקיים, אי-עקביות טיפוסים ובעיות נפוצות אחרות בקוד QML. הוא שימושי במיוחד לתפיסת טעויות בשלב מוקדם של תהליך הפיתוח.
arrow_circle_rightצרו קשר
בואו נדון כיצד תוכלו לשפר את האפליקציה שלכם עם טכנולוגיית Qt
arrow_circle_right מאמרים נוספים