עבדתי על בונה גרור ושחרר עבור Python בשבועות האחרונים.
אתה יכול לבדוק את זה ב
קוד מקור:
מה יכול הקבלן לעשות?
בקיצור, זה יכול לעזור לך לבנות במהירות ממשק משתמש עבור Python ולייצר קוד ממשק משתמש במספר ספריות/מסגרות, כולל Tkinter ו-customtkinter. אתה יכול לקרוא עוד על
אבל אני לא רק רוצה להשיק פרויקט, אני גם רוצה לשתף אתכם בחוויה שלי. בבלוג זה אעבור על תהליך החשיבה שלי ועל סקירה ברמה גבוהה של האופן שבו בניתי את האפליקציה.
בניגוד לאמונה הרווחת, Python משמשת לעתים קרובות לבניית יישומים מהירים, והיא פופולרית במיוחד בקרב מפתחים העוסקים במדעי נתונים, אוטומציה, משימות סקריפטים וכו'. כלים פנימיים וממשקי GUI רבים, במיוחד בהגדרות מדעיות ומחקריות, נבנים עם Python בשל כך הפשטות והזמינות של מסגרות כמו Tkinter, PyQt ואחרות.
כעת, היו הרבה בוני גרירה ושחרור לאינטרנט, אבל מעט מאוד עבור ממשקי משתמשי Python, במיוחד עבור tkinter. ראיתי כמה, אבל הבעיה הייתה שהם היו מספר מוגבל מאוד של ווידג'טים או שייצרו קוד בפורמט XML, וזה לא אידיאלי אם אתה מפתח ממשק משתמש ב-Python.
אז, בהתחלה, רק רציתי לבנות בונה גרירה ושחרור ממשק משתמש מתאים רק עבור Tkinter.
המשכתי להתעסק ברעיון של בונה GUI אידיאלי (ללא משחק מילים). קיבלתי השראה מהממשק של Canva, והמצאתי כמה תכונות שיהפכו את ה-GUI שלי לאידיאלי.
אז, בסביבות סוף יולי, החלטתי להתחיל לעבוד על הפרויקט
בהתחלה זה נקרא tkbuilder, מה שמציין שזהו בונה GUI עבור ספריית ממשק המשתמש של Tkinter.
אבל, אם שמתם לב אני יכול גם להרחיב על אותו רעיון לתמוך במספר מסגרות וספריות Python GUI, מכיוון שהכל עשוי כמו תוסף וזה בדיוק מה שתכננתי לעשות.
עבור הגרסה הראשונית, לא רציתי להוסיף יותר מדי תכונות שיכריעו את המשתמשים. רציתי לבנות אותו על סמך משוב מאנשים שמשתמשים בו. ככה אני לא מבזבז זמן על בניית דברים שאנשים לא רוצים.
מההתחלה החלטתי שלא יהיה לי אחורי או כל טופס הרשמה. בדרך זו הרבה יותר פשוט עבורי לפתח ולמשתמשים המשתמשים בו. רק רציתי חזית פשוטה שאנשים יכולים להתחיל איתו.
כן, זה היה משהו שחשבתי עליו די לפעמים, רוב בוני GUI עבור Python בחוץ נבנו באמצעות Python. הבחירה הראשונה שלי עבור Python הייתה PySide.
האפליקציה מבוססת GUI המורכבת ביותר שבניתי באמצעות PyQt/Pyside הייתה א
אבל מהר מאוד הבנתי את המגבלות של השימוש בפיתון לבניית הגרסה הראשונית.
גם Typescript הייתה אופציה, אבל עם Typescript תמיד הרגשתי שזה מפורט מדי
אלה היו הדברים היחידים שמיד שמתי לב אליהם, אז הבחירה הראשונה שלי הפכה באמצעות JS.
נ.ב: מאוחר יותר המשכתי להתחרט שלא התחלתי עם TS, אבל זה יהיה סיפור לפעם אחרת.
הספרייה דמוית המסגרת שאני הכי נוחה איתה היא React.js, אבל יצירת הפשטה תדרוש שימוש בשיעורים, מה שלא מומלץ מאז הצגת ה-hooks.
הבעיה של אי שימוש במסגרת הייתה שאצטרך לבנות הכל בעצמי ולא תהיה לי גישה לספריות הרכיבים העצומות שיש ל-React להציע.
לשניהם היו פשרות, אבל עדיין ניתן להשתמש בשיעורי React, אז זו הפכה לבחירה ברורה עבורי.
התחלתי בבניית הבסיס והסרגל הצדדי בתחילת אוגוסט, ונאלצתי להפסיק בגלל חוסר כספים, אז התחלתי לעבוד אצל לקוח, שלצערי לא שילם את הסכום הסופי. ניסיתי מימון המונים אבל גם שם לא היה לי מזל.
אז, בחודש ספטמבר עם הכספים הקטנים שנותרו לי, החלטתי ללכת על כל הפרויקט הזה. בסביבות ה-9 בספטמבר התחלתי את העבודה מחדש.
הרבה מהזמן הושקע בחשיבה על ההפשטה הבסיסית, שניתן להרחיב אותה לקנה מידה כדי לענות על הצרכים.
רציתי לקבל קנבס, שניתן להגדיל ולפנות בדומה ל-Figma.
יישומון בסיס שממנו כל שאר הווידג'טים יכולים לצאת.
תכונת גרירה ושחרור לגרירה ושחרור של רכיבי ממשק משתמש לתוך הקנבס.
כדי לבנות עם React, אתה צריך לחשוב ולבנות את זה בצורה מסוימת, למרות ויכוחים אם זו ספרייה או מסגרת, זה תמיד מרגיש יותר כמו Framework מאשר ספרייה.
תמיד אהבתי איך Canva בנה את סרגל הצד שלהם, רציתי שיהיה משהו דומה עבור בונה הגרירה והשחרור שלי.
רשמתי את מה שעולה בראשי על פיסת נייר. לא האמן הכי טוב שיש 🙄
אז מי צריך להיות אחראי על גרירה, שינוי גודל, בחירה. הקנבס או הווידג'ט הבסיסי. כיצד יטופלו הווידג'טים בתוך הווידג'ט?
האם יישומון הבסיס יכיר את ילדיהם או שהוא ינוהל עם מבנה נתונים יחיד על ידי הקנבס עצמו. איך אעבד ילדים בתוך ילדים?
כיצד יפעלו הגרירה והשחרור בתוך הקנבס ושאר הווידג'טים?
כיצד ינוהלו פריסות?
אלו היו חלק מהשאלות שהתחלתי לשאול לפני בניית העניין כולו.
למרות שעכשיו ממשק המשתמש נראה פשוט יותר, הושקעה מחשבה רבה בבניית הבסיס, כך שהוא נראה הרבה יותר פשוט עבור המשתמשים.
גישה מבוססת קנבס
עכשיו ל-html יש אלמנט Canvas ברירת מחדל, שמאפשר לך לעשות הרבה דברים כמו לצייר, להוסיף תמונה וכאלה, עכשיו זה נראה כמו אלמנט אידיאלי לשימוש עבור התוכנית שלי.
אז, התחלתי לבדוק אם יש וקיים יישום של גרירה ושחרור, שינוי גודל, זום ותנועה. מצאתי
ניסיתי להתנסות עם Fabric.Js וניסיתי ליישם את כל העניין ב-stof.js כפי שאתה יכול לראות את זה
גישה לא מבוססת קנבס
כעת, לאחר ניסויים, הגישה ללא קנבס נראתה טובה יותר, מכיוון שיש לי גישה למנהל הפריסה המוגדר כברירת מחדל, בנוסף היו זמינים הרבה רכיבים מובנים מראש של ממשק המשתמש שיעשו את הבחירה האידיאלית הזו בעת שינוי קנה מידה.
תכננתי לדמות קנבס על ידי שימוש בשני div'ים שונים אחד div פנימי ו- div מיכל חיצוני.
כעת יצירת זום ופאן היו קלים למדי ליישום, שכן ל-CSS כבר היה טרנספורמציה, קנה מידה ותרגום.
ראשית, כדי ליישם את זה, הייתי צריך להיות מיכל שמכיל קנבס. כעת הקנבס הזה הוא אלמנט בלתי נראה (ללא נסתר על הצפה), זה המקום שבו כל האלמנטים נשמטים, ומיושמים קנה מידה ותרגום.
עבור זום אין הייתי צריך להגדיל את קנה המידה ולזום אאוט להקטין אותו.
נסה את הדוגמה הפשוטה הזו. (מקש +
כדי להתקרב -
כדי להתרחק)
Panning עבד באופן דומה
כשהתחלתי חקרתי על כמה ספריות כמו
לאחר מחקר ראיתי ש-react-beautiful-dnd כבר לא נשמר והתחלתי עם React dnd-kit. בתור תחילת בנייה, מצאתי את התיעוד של ה-dnd-kit מוגבל למדי למה שבניתי, בנוסף, בקרוב יצאה מהדורה חדשה עם שינויים גדולים בספרייה, אז החלטתי לבטל את react-dnd-kit עד הגרסה העיקרית.
כתבתי מחדש את החלקים שבהם השתמשתי ב-DND-kit עם ה-Drag and Drop API של HTML. המגבלה היחידה עם ממשק ה-API המקורי של גרירה ושחרור הייתה שהוא עדיין לא נתמך על ידי מכשירי מגע מסוימים, מה שלא היה חשוב לי כי בניתי למכשירים ללא מגע.
כאשר בונים אפליקציה כזו, קל לאבד את המעקב אחר כל המשתנים והשינויים. אז, אני לא יכול לקבל משתנים מרובים לעקוב אחר אותה פיסת מידע.
המידע/מצב של כל יישומון צריך להיות מוחזק על ידי הקנבס או על ידי הווידג'ט עצמו, אשר לאחר מכן מעביר את המידע לפי בקשה.
או אולי השתמש בספריית ניהול המדינה כמו redux
בחרתי לקבל את כל המידע על הווידג'טים המנוהלים על ידי רכיב Canvas לאחר ניסוי גישות שונות.
מבנה הנתונים נראה בערך כך.
[ { id: "", // id of the widget widgetType: WidgetClass, // base widget children: [], // children will also have the same datastructure as the parent parent: "", // id of the parent of the current widget initialData: {} // information about the widget's data that's about to be rendered eg: backgroundColor, foregroundColor etc. } ]
עכשיו רציתי שהנכסים שהועלו בסרגל הצד יהיו נגישים באמצעות סרגל הכלים של הווידג'טים. אבל בכל פעם שאני מחליף את הכרטיסיות הצדדיות, העיבוד מחדש גרם להיעלמות הנכסים שהועלו.
אחת המגבלות הגדולות ביותר עם Redux היא שאתה יכול לאחסן רק נתונים הניתנים לסידרה. לא ניתן לאחסן נתונים שאינם ניתנים לסידרה כגון תמונה, וידאו, נכסים אחרים ב-redux. זה יקשה על העברת נתונים משותפים סביב רכיבים שונים.
דרך אחת להתגבר על זה היא להשתמש בהקשר של React. בקצרה, React Context מספקת דרך להעביר נתונים דרך עץ הרכיבים ללא צורך להעביר אביזרים ידנית בכל רמה.
כל מה שהייתי צריך לעשות כדי לקבל את הנתונים ברכיבים שונים היה לעטוף אותם סביב ספק הקשר של React.
יצרתי ספקי הקשר משלי לשני דברים:
הנה דוגמה פשוטה לאופן שבו השתמשתי בהקשר של React עבור גרירה ושחרור.
import React, { createContext, useContext, useState } from 'react' const DragWidgetContext = createContext() export const useDragWidgetContext = () => useContext(DragWidgetContext) // Provider component to wrap around parts that need drag-and-drop functionality export const DragWidgetProvider = ({ children }) => { const [draggedElement, setDraggedElement] = useState(null) const onDragStart = (element) => { setDraggedElement(element) } const onDragEnd = () => { setDraggedElement(null) } return ( <DragWidgetContext.Provider value={{ draggedElement, onDragStart, onDragEnd }}> {children} </DragWidgetContext.Provider> ) }
כֵּן! זהו. כל מה שהייתי צריך לעשות עכשיו זה לעטוף אותו סביב הרכיב שבו הייתי צריך את ההקשר, שבמקרה שלי היה מעל Canvas וסרגל הצד.
מכיוון שכל ווידג'ט מתנהג אחרת ויש לו תכונות משלו, החלטתי שהווידג'טים חייבים להיות אחראים על יצירת הקוד שלהם ומנוע קוד יטפל רק בהתנגשויות של שמות משתנים וחיבור הקוד.
בדרך זו, הצלחתי בקלות להתרחב כדי לתמוך בווידג'טים רבים שנבנו מראש, כמו גם בכמה תוספים של ממשק משתמש של צד שלישי.
לא היה לי קצה אחורי או הרשמה והיו הרבה חברות שסיפקו אירוח בחינם לדפים סטטיים. תחילה החלטתי ללכת עם Vercel, אבל פעמים רבות ראיתי צמיג חינם של Vercel יורד אם היו יותר מדי בקשות.
זה הרגע שגיליתי על
החסרונות היחידים היו זמני הבנייה היו איטיים למדי וחסר להם לא מעט תיעוד.
החלק הכי מעצבן בשלב הבנייה היה כישלון הבנייה, זה עבד על Vercel, אבל לא על דפי cloudflare??? היומנים גם לא היו כל כך ברורים. ויש לנו צמיגים בחינם יש רק 500 בנייה בחודש, אז לא רציתי לבזבז יותר מדי
ניסיתי במשך שעות ואז החלטתי להגדיר אינטגרציה מתמשכת למחרוזת ריקה
CI='' npm install
בניתי את כל הדבר הזה בפומבי. אני מעוניין לראות את זה מתקדם מסרגל צד פשוט לבונה Drag n drop מפוצץ לחלוטין, אתה יכול לבדוק את כל
#בנה בציבור
הו! אל תשכח לעקוב אחרי עדכונים
אם אהבתם את סוג התוכן הזה, אני אכתוב בלוגים נוספים ונכנס יותר לעומק של איך אני מתכנן ובונה דברים, כדי לעקוב אחריכם תוכלו להירשם לניוזלטר המשנה שלי :)