View Full Version : GUI in python
ehp_kz6597
دوشنبه 30 آبان 1390, 09:44 صبح
Graphical User Interface in python
سلام دوستان
در این سلسله مقالات آموزشی قصد داریم که با هم یه مطالعه موردی روی GUI در پاتون داشته باشیم . از اونجایی که در نسخه های جدیدتر (3) به TKinter توجه ویژه ای شده لذا با این ماژول شروع می کنیم ولی باتوجه به اینکه هنوز در لینوکس نسخه های قدیمی تر(2) GTK به صورت پیش فرض نصب و قابل استفاده می باشد لذا (اگه خدا بخواد و حالی باقی بمونه ) بعد از پایان این سلسله مباحث آموزشهایی درباره GTK هم خواهیم داشت .
واضح است که آشنایی در حد متوسط با کد نویسی در پایتون به عنوان پیش نیاز این آموزش است.
از آنجایی که هممون هر روز ، هنگام کار با کامپوتر با اشیایی که در ادامه درباره آنها صحبت خواهم کرد ، روبرو هستیم ، پس نیاز بهتوضیحات خیلی ریز درباره کارکرد اشیا نیست
برای آن دسته از دوستان که تازه شروع به یاد گیری کرده اند کتاب a byte of python (نویسنده : Swaroop ) میتواند منبع مفید و سریع جهت یادگیری باشد . ( این کتاب (توسط آقای محمودی) به زبان فارسی نیز ترجمه شده است و از طریق اینترنت قابل دسترسی می باشد )
-----------------------------------------------
از اساتید گرامی خواهش میکنم در صورت مشاهده هرگونه مشکل در مفهوم ، نگارش و ... با پیغام خصوصی اطلاع داده تا پیوستگی مطالب حفظ شود
ehp_kz6597
دوشنبه 30 آبان 1390, 09:50 صبح
با کمک GUI شما قادر خواهید بود تا برنامه هایی با ظاهری کاربر پسند (و درواقع ویژوال )تولید کنید و سبب می شود که کاربر کار با نرم افزار تولید شده را راحت تر یاد بگیرد . در اینجا دیگر فقط یک صفحه سیاه ساده مقابل کاربر قرار ندارد بلکه مجموعه ای از دگمه ها ، اشکال ، متون و منوهایی مشاهده می کند که انجام عملیات مختلف را ساده تر خواهد کرد . شما بوسیله Tool Command Language(TCL) و Tool Kit (Tk) قادر به تولید برنامهGUI خواهید بود . در زبان پایتونTkinter کتابخانه پیشفرض رابط گرافیکی کاربر است که بر پایه TK بنا شده است . لیست کامپوننت ها و شرح مختصری از آنها در زیر آمده است :
78174
http://parsaspace.com/files/2830224884/?c=727
بعضی از توضیحات (خیلی :لبخند: ) گنگ هست لذا شما در ادامه کار با مشاهده قابلیت های آن ها به نحوه عملکردشان پی خواهید برد
اضافه کردن ماژول Tkinter به برنامه
برای شروع لازمه که ماژولtkinter فراخوانی شود. این کار هم به دو طریق صورت میگیرد (که همتون میدونین) که البته روش دوم متداول و در هنگام استفاده راحت تر است (که در ادامه به آن اشاره خواهم کرد) :
import tkinter
from tkinter import *
توجه داشته باشید که در اکثر منابع آموزشی برای فراخوانی ، نام این تابع را با حرف T آغازی بزرگ بکار می برند ولی از قرار معلوم در نسخه 3.2.2 (که کدهای ارائه شده درآن تست شده اند) میبایست با حرف کوچک نوشته شود
ehp_kz6597
دوشنبه 30 آبان 1390, 10:02 صبح
ساخت فرم اصلی برنامه
به عنوان اولین قدم با کد زیر به سادگی میتوانید یک frame ، دراختیار داشته باشید
from tkinter import *
Frame().mainloop()
البته بهتر است که از Tk یه نمونه بسازید تا راحت تر کار کنید :
from tkinter import *
form1 = Tk()
form1.mainloop()
لازم به ذکر است که اگر برای فراخوانی ماژول از حالت اول استفاده کرده بودید خط دوم کد فوق به صورت
form1 = Tkinter.Tk()
تغییر می کرد .شی ای که توسط خط بالا (و خط دوم کد قبل) برگشت داده می شود معمولا به شی root ارجاع داده می شود . خب حالا میتوانید یه سری تنظیمات مربوط به پنجره نمایش رو اضافه کنید :
form1.geometry('200x300+50+20')
آرگومانها به ترتیب از چپ به راست : پهنای پنجره /X/ ارتفاع پنجره / فاصله از سمت چپ / فاصله از بالا
ehp_kz6597
سه شنبه 01 آذر 1390, 07:36 صبح
برای تکمیل پست قبلی :
برای تعیین عنوان برای فرم ، از کد زیر میتوانید استفاده کنید :
form1.title('form 1')
همونطور که در کد بالا معلومه ، متن بین علامت (‘ ‘) عنوان پنجره شما خواهد بود
-----------------------------------------------------------
قرار دادن Label روی فرم
Label(form1,text = 'salam')
کد بالا جهت قرار دادن یک Label روی فرم استفاده می شود . این تابع دارای دو آرگومان است که آرگومان اول برای تعیین کامپوننتی است که میخواهید لیبل روی آن قرار گیرد و آرگومان دوم هم متنی است که به نمایش در می آید.
! توجه کنید که به جهت پشتیبانی از یونیکد شما میتوانید از متن فارسی نیز برای نمایش استفاده کنید . اما اگر قرار به نمایش متن در cmd باشد نمی توانید از زبان فارسی استفاده کنید .
همچنین میتوانید از همین تابع با تغییر آرگومان ، جهت نمایش آیکن های ویژه ای استفاده کنید . کد فوق به صورت زیر تغییر می یابد :
Label(form1, bitmap = "warning" )
در جدول زیر لیست اشکال و نام مربوط به هر یک از آنها را جهت فراخوانی مشاهده می کنید :
78222
متدpack() جهت تعیین سایز و موقعیت کامپوننت ها در فضای کامپوننت والد استفاده می شوند و شامل تنظیماتی است که به شرح سه مورد پر کاربرد آن می پردازیم :
expand : مقداریست از نوع Booleanکه با مقادیر 0 و 1 و یا YES و NO میتوان مقدار دهی کرد . (YES,NO حتما با حروف بزرگ نوشته شوند) . این پارامتر به TK می گوید که میخواهیم فریم شی مورد نظر به اندازه فریم پنجره والد آن گسترش یابد یا نه
fill : این آرگومان مشخص می کند که چه مقدار از فضای اطراف کامپوننت که به صورت پیشفرض در نظر گرفته شده است به عنوان فضای کامپوننت اشغال شود . شامل چهار مقدار x, y, both, none می باشد
Side : چگونگی تراز شدن کامپوننت با والد خود را تعیین می کند . و شامل مقادیر left, right, top, bottom میباشد .
به زودی کارکرد های این متد را خواهید دید
! برای نمایش کامپوننت مورد نظر درحالت عادی شما میبایست ، پس از تعریف کامپوننت ، از این متد بدون آرگومان استفاده کنید .
برای استفاده راحت تر از کامپوننت ها بهتر است که کامپوننت های مورد نیاز و تنظیمات مربوطه را به صورت یک تابع تعریف کرده و هرجا نیاز به آن بود ، تابع مورد نظر را فراخوانی کنید . به این ترتیب ازنوشتن کدهای اضافه که باعث شلوغی و کاهش خوانایی برنامه میشود . برای مثال به کد زیر نگاه کنید :
from tkinter import *
form1 = Tk()
form1.title('label with function')
form1.geometry('250x50')
def label(txt):
L= Label(form1,text = txt)
L.pack()
label1 = label('سلام')
label2 = label('به دنیای پایتون خوش آمدید')
form1.mainloop()
متغیر های Label 1 , 2 فقط جهت درک بیشتر کد استفاده شده است و بدون وجود آنها نیز برنامه بدون مشکل اجرا خواهد شد.
تصویر اجرا شده کدهای بالا :
78223
نمونه کامل کدهای توضیح داده شده در این چند پست را ضمیمه کردم
ehp_kz6597
پنج شنبه 03 آذر 1390, 08:11 صبح
قرار دادن Button روی فرم
برای این کار میتوان از کد زیر استفاده کرد :
button1 = Button(form1,text = 'button1')
button1.pack()
طبق معمول یک متغییر ایجاد کرده و مقدار متد مورد نظر (در اینجا Button ( را به آن اختصاص می دهیم .
تا اینجا جز نام متد مورد وجه تمایز دیگری مشاهده نمیشود.
تعیین رویداد on-click برای شی Button
این کار با افزودن آرگومان command به متد Button قابل انجام است . به کد زیر توجه کنید :
button1 = Button(form1,text = 'button1' , command = exit)
در اینجا افزودن دستور command = exit سبب می شود تا با کلیک بر رویbutton برنامه خاتمه یابد. نکته جالب و کارآمد این است که علاوه بر دستورات پیش فرض شما میتوانید توابعی را که خودتان تعریف می کنید در اینجا فراخوانی کنید . به مثال زیر توجه کنید
from tkinter import *
form1 = Tk()
form1.title('label with function')
deflabel(txt):
L= Label(form1,text = txt)
L.pack()
label1 = label('سلام')
label2 = label('به دنیای پایتون خوش آمدید')
defbutton_click() :
print ('روی دگمه کلیک شده است')
button1 = Button(form1,text = 'button1' , command = button_click)
button1.pack()
form1.mainloop()
78316نتیجه اجرای کد بالا
با افزودن کد زیر و قرار دادن عکس مورد نظر در مسیر اجرای برنامه شما میتوانید بر روی دگمه خود عکس قرار دهید
myimage = PhotoImage(file ='pic.gif')
button1 = Button(form1,image = myimage , command = button_click)
78317
! عکسهای استفاده شده باید در فرمتهای gif , bmp باشند
! قید روبرو در مورد فایلهای bmp ذکر شده است لذا در استفاده از این فایلها دقت لازم را داشته باشید : PPM/PGM color bitmaps
کد های مربوط به این بخش هم ضمیمه شد
ehp_kz6597
سه شنبه 08 آذر 1390, 15:10 عصر
سلام
خب ، به نظر میاد که تقریبا با مسائل اولیه و نحوه کار با tkinter آشنایی کافی پیدا کردیم . حالا لازمه که کمی به موارد مهمتر و البته جذاب تر بپردازیم.
MessageBox
زمان خوبی برای معرفی ماژول tkmessagebox است .
یکی از پرکاربردترین رفتارها در برنامه نویسی رابط گرافیکی نمایش پیغام متناسب با شرایط ، به کاربر است . این ماژول دارای چند تابع مفید به شرح زیر است :
showwarning, showerror, Showinfo
askquestion, askokcancel, askyesno , askyesnocancel , askretrycance
دو تابع اول جهت اطلاع رسانی بدون حق انتخاب به کاربر است و توابع ردیف دوم جهت نمایش پیام و دریافت نظر کاربر جهت ادامه عملیات است .
همه این توابع دو آرگومان اصلی دارند که به ترتیب چپ به راست ، نام پنجره و متن پیام ظاهر شده است . شما به استفاده از مقادیر برگشتی توابع دریف دوم قادر خواهید بود تا رابطه کاربر و برنامه را کنترل کنید. برای درک بهتر مطالب ، به مثالی آماده کردم توجه کنید :
[
from tkinter import *
from tkinter.messagebox import *
root = Tk()
lbl = Label(root, text='I love python ')
lbl.pack()
def bc_color():
lbl.config(bg='yellow')
showinfo ('message' ,'backgroung color is yellow')
def txt_color():
res = askquestion ('message' ,'do yuo want to change color')
if res == YES:
lbl.config(fg='red')
showwarning('Warning', 'the text color is changed')
if res == NO :
showerror('Error', 'color not changed')
btn1 = Button(root, text='backgorund', command = bc_color)
btn1.pack()
btn2 = Button(root, text='text', command = txt_color)
btn2.pack()
root.mainloop()
در خط دوم فراخوانی ماژول tkMessageBox را مشاهده می کنید .
در خط ششم یک تابع تعریف نموده و رنگ زمینه Label که در چند خط قبل تعریف کردیم را تغییر می دهد . و سپس بوسیله تابع showinfo و نمایش یک پیام ، این تغییر را به کاربر اعلام میکند.
7851078511
تابع بعدی جهت تغییر رنگ متن بکار می رود . در ابتدا به وسیله متغیر res مقدار بازگشتی از تابع askquestion را دریافت می کند و سپس با استفاده از دو شرط این مقدار را بررسی کرده و متناسب با آن ، پیامی برای کاربر ارسال می نماید . (جهت نمایش این پیام ها از توابع متفاوت استفاده شده است تا نحوه نمایش آنها را ملاحظه کنید)
785127851378514
تصاویر مربوط به کلیک بر روی دگمه text و انتخاب گزینه های yes و no
ehp_kz6597
سه شنبه 08 آذر 1390, 15:26 عصر
یه محدودیت دیگه درفروم
بیشتر از 5 فایل در یک پست نمیشه ارسال کرد !!!!!!!!
کد برنامه پست قبل رو ضمیمه کردم
ehp_kz6597
یک شنبه 13 آذر 1390, 08:12 صبح
ایجاد شی Entry و نحوه چیدمان اشیاء
تا اینجا اشیا به صورت پیش فرض در فرم قرار میگرفتند و کنترلی بر نحوه چیدمان آنها نداشتیم . در ادامه ضمن ضمن بررسی نحوه کارکرد شی Entry ، به موضوع جدیدتری تحت عنوان Grid می پردازیم . برای روشن شدن موضوع به کد زیر نگاهی بیاندازیم :
from tkinter import *
def close_window() :
form1.destroy()
def DoInputForm(form1):
Label(form1, text="Enter name:").grid(row=0 , column = 0)
Label(form1, text="Enter address:").grid(row=1 , column = 0)
Entry(form1).grid(row=0, column=1)
Entry(form1).grid(row=1, column=1)
Button(form1, text="Ok", command=close_window).grid(row=2, column=0)
Button(form1, text="Cancel", command=close_window).grid(row=2, column=1)
form1 = Tk()
DoInputForm(form1)
form1.mainloop()
با توجه به توضیحات ارائه شده در موارد قبل ، دیگر نیاز به توضیح اضافی درباره شی Entry نیست .
اما موضوع جذاب grid :
ابتدا به جدول مقابل نگاه کنید :78696
شما با استفاده از دستور grid درواقع یک جدول نامرعی روی فرم ایجاد می کنید
سپس می توانید هر شی را در خانه مورد نظر قرار دهید . فقط کافی است تا کد بالا را با تصویر برنامه اجرا شده و جدول ارائه شده مقایسه کنید .
78697
ehp_kz6597
شنبه 19 آذر 1390, 10:24 صبح
ایجاد کلاس برای مدیریت رابط کاربری
درواقع دو دلیل برای استفاده از توابع وجود دارد . یکی آسان شدن کد نویسی و ویرایش آن و دیگری قابلیت استفاده مجدد و قابلیت گسترش پذیری برنامه است . در توضیحات مربوط به Label به این موضوع اشاره کردم که تعریف و استفاده از اشیا در تابع چگونه است . حال به این نکته توجه کنید که برنامه گسترده تر باشد . در اینجا نیاز به وجود کلاس بیشتر احساس می شود . شما با ایجاد کلاس درواقع قابلیت خوانایی و گسترش پذیری برنامه خود را بالا برده اید و ازطرفی هنگام خطایابی و کنترل برنامه ، با مشکلات کمتری مواجه می شوید .
کد برنامه پست قبل بوسیله کلاس نیز پیاده شده است که در ضمیمه کردم . با توجه به سطح دانش افراد مخاطب این آموزش ، نیازی به توضیحات اضافه تر احساس نمی شود.
سعی میکنم در ادامه بیشتر از کلاس استفاده کنم
ehp_kz6597
شنبه 19 آذر 1390, 10:27 صبح
کار با شی Listbox
این شی در واقع لیستی از رشته ها را نمایش می دهد که به کاربر امکان یکسری اعمال همانند درج ، حذف و ... را می دهد . به قطعه کد زیر توجه کنید :
self.list_box_1 = Listbox(self.root, selectmode=EXTENDED)
self.list_box_1.pack()
آرگومان اول که به دفعات معرفی شده است ، همان کامپوننتی است که Listbox روی آن قرار میگیرد و آرگومان دوم حالت انتخاب اعضای لیست است که شامل چند مقدار پیش فرض است :
SINGLE : انتخاب یک مورد در هر لحظه
BROWSE : انتخاب یک مورد در هر لحظه
MULTIPLE : انتخاب چند مورد فقط بوسیله کلیک بر روی مورد دلخواه و عدم انتخاب هم به همین صورت
EXTENDED : انتخاب چند مورد با کلیک ماوس و کشیدن آن و یا نگهداشتن کلید Ctrl و انتخاب موارد دلخواه
لیست برخی از متدهای آن به شرح زیر است :
Active(index) : ایندکس مشخص شده را فعال می کند ( با نمایش زیر خط). توجه کنید که ایندکس از صفر شروع می شود .
curselection() : لیستی(list) از ایندکس عناصر انتخاب شده را برمیگرداند.
Delete() : جهت حذف عنصر و یا عناصر مشخص شده به دو صورت زیر بکار می رود
delete(index) : عنصر مشخص شده را پاک میکند
delete(first, last): عناصر مشخص شده را پاک می کند . همانطور که مشخص است باید اولین و آخرین عنصر در بازه انتخابی مشخص باشد. برای پاک کردن کل عناصر لیست میتوان از delete(0,END) استفاده کرد.
get() : برای مشخص عنصر مورد نظر بکار میرود و مانند delete به دوصورت زیر است :
get(index) : خروجی یک رشته (عنصر انتخاب شده) می باشد
get(first,last) : خروجی یک لیست از عناصر انتخاب شده است . با آرگومانهای 0 END , همانند متد قبل میتوانیم لیست تمام عناصر را در اختیار داشته باشیم .
index(index) : عدد ایندکس متناظر با عنصر مشخص شده را برمیگرداند.
Insert(index,items) : جهت درج یک یا چند عنصر در لیست استفاده می شود .
---------------------------
ببخشید دیگه ، نتونستم کاری کنم که متن دوزبانه تمیز دربیاد (منظورم متدها و توضیحاتشونه)
یه نمونه برنامه ضمیمه کردم . کدهاشو با توضیحات ارائه شده در اینجا مقایسه کنید همه چیز دستتون میاد
ehp_kz6597
شنبه 03 دی 1390, 14:14 عصر
کار با شی Menu
می توان گفت menu جزء اصلی در برنامه نویسی رابط گرافیکی است.
به کد زیر نگاه کنید :
from tkinter import *
classMenuTest :
defNewFile(self) :
print ("new")
defOpenFile(self) :
print ("Open")
defClose(self) :
self.root.destroy()
def__init__(self) :
#--------------------------
self.root = Tk()
#1--------------------------
self.main_menu = Menu(self.root)
self.root.config( menu = self.main_menu )
#2--------------------------
fileMenu = Menu(self.main_menu , tearoff=0)
#3--------------------------
self.main_menu.add_cascade( label="File", menu=fileMenu )
#4--------------------------
fileMenu.add_command( label="New", command=self.NewFile )
fileMenu.add_command( label="Open", command=self.OpenFile )
fileMenu.add_separator()
fileMenu.add_command( label="Exit", command=self.Close )
self.root.mainloop()
#--------------------------
mt = MenuTest()
براساس شماره درج شده در بین کد :
توسط کد بخش 1 ، همانند سایر اشیا که تا اینجا با آنها آشناشدیم ، نوار منو را ایجاد کرده و به فرم اصلی برنامه نصبت می دهیم .
در کد بخش 2 ، شی منوی فایل را ایجاد میکنیم که در واقع یک پنجره popup است که شامل یک دستور است .
توجه کنید که در انتهای این دستور عبارت tearoff را مشاهده می کنید که با 0 مقدار دهی شده است. چنانچه این مقدار با 1 جایگزین شود به منوی شما یک خط چین اضافه می شود که با کلیک بر روی آن میتوانید منو را به صورت آزاد و جدای از فرم اصلی داشته باشید
در کد بخش 3 ، منوی فایل را به نوار منو اضافه می کنیم .
در بخش 4 ، به منوی فایل چند زیر منو اضافه کرده و دستورات هرکدام را که قبلا بصورت تابع تعریف کرده ایم ، به آنها نصبت میدهیم .
ehp_kz6597
چهارشنبه 07 دی 1390, 10:00 صبح
زیر منو
زیر منو در واقع یک منو است که به منوی دیگری متصل می شود
به کدهای زیر بادقت نگاه کنید :
submenu = Menu(fileMenu)
submenu.add_command(label="new 1",command=self.NewFile)
submenu.add_command(label="edit 1")
submenu.add_command(label="exit 1")
تا اینجا نکته خاصی نداره . فقط شما یه نمونه از شی فایل منو که قبلا تعریف کردیم میسازین و برای اون گزینه های منو را مشخص میکنید .
fileMenu.add_cascade(label='submenu', menu=submenu, underline=0)و در اینجا مشخص میکنیم که این زیر منو مربوط به کدام منو اصلی است . تنها نکته باقی مانده اینکه در هر کجا که این کد نوشته شود در همانجا ظاهر می گردد. اگه بخوام واضح تر بگم این میشه اینکه مثلا شما میخواین که این زیر منو بین گزینه های new و open قرار بگیره . خب در اینجاست که باید کد بالا رو بین کد مربوط به ایجاد این دو منو درج کنید یعنی به صورت زیر :
fileMenu.add_command( label=”New”, command=self.NewFile )
fileMenu.add_cascade(label=’submenu’, menu=submenu, underline=0)
fileMenu.add_command( label=”Open”, command=self.OpenFile )
به گزینه underline توجه کنید که با صفر ، مقدار دهی شده است. این گزینه سبب میشود که شما با زدن دگمه Alt و کلید حرف مشخص شده به آن دسترسی داشته باشید .
ehp_kz6597
چهارشنبه 07 دی 1390, 10:06 صبح
منوی شناور - Popup menu
Popup یک منوی شناور است که می توان به میتواند به اشیاء مختلف انتساب داد. کد زیر رو بررسی کنیم
self.menu = Menu(self.parent, tearoff=0)
self.menu.add_command(label="Beep", command=self.beep)
self.menu.add_command(label="Exit", command=self.onExi
self.parent.bind("<Button-3>", self.showMenu)
self.pack() خب خط اول که تعریف یک منو هست که در بالا آشنا شدیم . و سپس گزینه های منو را به آن اضافه می کنیم .
تفاوت در اینجاست که ما به جای اضافه کردن منو به نوار منو ، اون رو به کلیک چپ موس میچسبونیم (bind می کنیم ) و دیگر هیچ .
کد توابع استفاده شده برای گزینه های منو هم در زیر نوشتم و فکر کنم نیازی به توضیح ندارن.
def showMenu(self, e):
self.menu.post(e.x_root, e.y_root)
def beep(self):
self.bell()
def onExit(self):
self.quit()
ehp_kz6597
چهارشنبه 07 دی 1390, 10:19 صبح
سلام دوستان
آموزشهای این بخش تموم شد . با این حال سعی میکنم که باز هم اینجا نمونه کد برنامه های کاملتر و ترکیبی نوشته شده با این ماژول رو بذارم
البته انتظار استقبال بیشتری داشتم ولی خب دیگه از قرار معلوم دوستان خیلی حرفه ای هستن و آموزشهای مبتدی ، بکارشون نمیاد :متفکر:
این بحث ، خـــــــــــــــــــــیلی گسترده تر از چیزیه که اینجا اومده ولی سعی کردم مواردی رو که برای شروع به کار نیازه که بدونین رو مطرح کنم.
امیدوارم که برای دوستانی که مطالب رو دنبال کردن و یا احتمالا بعدا میخونن مفید باشه .
میخواستم مبحث gtk رو بررسی کنم که از قرار معلوم خریدار نداره ، اما انشاء الله بعد از امتحانات یه سری برنامه ویژه دارم . تا چه شود ...
شاد و موفق باشید :لبخند:
sahama
جمعه 16 دی 1390, 18:06 عصر
سلام
آقا دستت طلا
ولی به نظرم می رسه pyqt بیشتر مشتری داشته باشه اگر تونستی در مورد اونم مطلب بنویسی عالیه
اگر چه نیازی به کمک نداری ولی اگر کمک خواستی تا جایی که بتونم راهنمایی می کنم
ehp_kz6597
شنبه 17 دی 1390, 10:06 صبح
سلام
لطف دارین
انشاء الله به زودی پای این موضوع رو هم به این سایت باز خواهم کرد
چرا اتفاقا .
من حرفه ای نسیتم (هرچند اگر هم بودم باز هم نیاز به همکاری احساس میشد )، فقط فکر میکنم که یکی از راههای پیشرفت خود و دیگران به اشتراک گذاری مطالبی هست که یاد گرفتیم که من این چند مدت سعی کردم به این باور خودم عمل کنم
اول هر پستی کلی داد میزنم دوستان اگه فکر میکنن جایی کم و کاستی ، غلطی ، چیزی وجود داره یه پیغام خصوصی بدن ولی انگار یا دوستان اون بخش از مطالب رو نمیخونن و یا کارم خیلی درسته :لبخند:
شاد باشید :قهقهه:
vBulletin® v4.2.5, Copyright ©2000-1404, Jelsoft Enterprises Ltd.