پرچمداران
-
در همه بخش ها
- همه بخش ها
- فایل
- دیدگاه فایل
- نقد و بررسی فایل
- مقالات
- مقاله دیدگاه
- مقاله نقد و بررسی
- صفحات استاتیک
- صفحه دیدگاه
- صفحه نقد و بررسی
- کتابخانهها
- کتابخانه دیدگاه
- کتابخانه نقد و بررسی
- رویداد
- دیدگاه های رویداد
- بازبینی رویدادها
- تصاویر
- دیدگاه های تصویر
- نقد های تصویر
- آلبوم ها
- نظر های آلبوم
- نقد های آلبوم
- پست ها
- نوشتههای وبلاگ
- دیدگاه های وبلاگ
- بروزرسانی وضعیت
- پاسخ های دیدگاه ها
-
تاریخ سفارشی
-
همه زمان ها
4 خرداد 1397 - 28 اردیبهشت 1403
-
سال
28 اردیبهشت 1402 - 28 اردیبهشت 1403
-
ماه
30 فروردین 1403 - 28 اردیبهشت 1403
-
هفته
22 اردیبهشت 1403 - 28 اردیبهشت 1403
-
امروز
29 اردیبهشت 1403 - 28 اردیبهشت 1403
-
تاریخ سفارشی
جمعه, 22 تیر 1397 - جمعه, 22 تیر 1397
-
همه زمان ها
مطالب محبوب
در حال نمایش مطالب دارای بیشترین امتیاز در جمعه, 22 تیر 1397 در همه بخش ها
-
1 امتیاز
نگارش 1.69.0
62 دریافت
کتابخانه Boost منبعی از کتابخانههای قابل حمل و آزاد ++C را فراهم میکند که از اصطلاح Peer-Reviewed پشتیبانی میکند. ما بر این تاکید میکنیم، Boost کتابخانه ای است که به خوبی با کتابخانه ی استاندارد ++C کار میکند. بوست مجموعه ای از کتابخانههای برنامه نویسی برای زبان برنامه نویسی سی پلاس پلاس است که پشتیبانی از کارهایی مانند جبر خطی، ساخت اعداد شبه تصادفی, ریسهبندی, پردازش تصویر, عبارت باقاعده و آزمایش واحد را ارائه میدهد. بیشتر این کتابخانه ها تحت پروانه نرم افزار بوست منتشر میشوند که به بوست اجازه میدهد هم در یک نرم افزار آزاد و هم در یک نرم آفزار انحصاری استفاده شود. بسیاری از پدید آورندگان بوست عضو کمیته استانداردسازی سی++ هستند و چند کتابخانه بوست برای الحاق به استانداردهای TR1 و C++11 پذیرفته شده اند.رایگان
-
1 امتیاز
-
1 امتیازتوابع Image Filtering شاید در تصویرمان بخواهیم عملیات زوم کردن را انجام دهیم در این صورت ما از توابع pyrUp،pyrDownیا تابع buildPyramid استفاده کنیمکهتابع buildPyramid تا هر چند برابر که بخواهیم تصویر را دور میکند اما بیش از حد ان باعث نابودی پیکسل های تصویر میشود. تابع pyrUp: تصویر را نزدیک میکند(ZoomIn) این تابع الگوریتم متفاوتی دارد و باید طبق این الگوریتم پیش رفت تا مشکلی پیش نیاید الگوریتم این تابع به این صورت است که میگوید شما میخواهید تصویر را دوبرابر کنید پس باید در پارمتر سومی که سایز را از مامیخواهد سطر و ستون تصویر ضرب در2 شود اینگونه سطر و ستون دوبرابر خواهند شد و خروجی مورد نظر را میدهد اما اگر در مقداری به جز مقدار 2 ضرب یا بعلاوه شود در این صورت تصویر انقدر بزرگ میشود که امکان نمایش ان وجود ندارد البته هر چند برای دوبرابر کردن تصویر که سایز ضرب در 2 میشود هم محدودیت وجود دارد و تا حد مشخصی میشود تصویر را نزدیک کرد بعد از ان با خطا مواجه میشویم یک مثال از این تابع را مشاهده کنید: #include <stdio.h> #include <stack> using namespace cv; Mat output_image; Mat image_read; void Zoom_In() { pyrUp(image_read, output_image, Size(image_read.cols * 2, image_read.rows * 2)); imshow("Zoom In", output_image); image_read = output_image; } void Zoom_Out() { pyrDown(image_read, output_image, Size(image_read.cols / 2, image_read.rows / 2)); imshow("Zoom Out", output_image); image_read = output_image; } void ZoomIn_And_ZoomOut(int position, void* user) { std::cout << "Position: " << position << " " << " user data " <<(int)user<< std::endl; } int main() { void* userdata = (void*)10; TrackbarCallback zoom_in = (TrackbarCallback)Zoom_In; TrackbarCallback zoom_out = (TrackbarCallback)Zoom_Out; image_read = imread("C:\\Users\\Mohamad4030\\Desktop\\c9ac_d2ynkbk.jpg", IMREAD_COLOR); imshow("befor",image_read);//befor filtering ShowWindow(GetConsoleWindow(), SW_HIDE); createTrackbar("TrackBar1", "befor", 0, 10,zoom_in); waitKey(0); } تابع createTrackBar: یک اسلایدربار را به یک پنجره متصل میکند ورودی اول نام اسلایدر بار ورودی دوم نام پنجره برای متصل شدن ورودی سوم حداقل مقدار اسلایدربار ورودی چهارم حداکثر مقدار ورودی پنجم یک رویداد برای اینکه اگر موقعیت اسلایدربار تعغیر کرد چه کاری صورت گیرد که اینجا ما میتوانیم به دو صورت این ورودی را بدهیم با موقعیت و دیتاهای کاربر که باید بصورت اشارگر باشد و در خود تابع به نوع مناسب تبدیل شود یا اینکه یک TrackbarCallback تعریف کرده همونطور که در کد میبینید و تابع را نوشته و تبدیل میکنیم که تابع ما ZoomIn است ورودی اخر این تابع دیتای های کاربر هست که به پارامتر void* user منتقل میشود در تابع ZoomIn دقت کنید که باید بعد از عملیات زوم image_read برابر با output_image قرار بگیرد تا در عملیات بعد سایز image_read که در پارامتر سوم مورد نیاز است برای زوم بعدی مورد استفاده قرار گیرد. حاصل اجرای کد بالا بصورت زیر خواهد بود: تابع pyrDown: برای بتوانیم تصویر رادور کنیم(ZoomOut) از این تابع استفاده میکنیم برای اینکه از سطر و ستون تصویر بکاهیم و تصویر را تا اندازه ی مشخص دور کنیم باید اینجا سطر و ستون تقسیم بر 2 شود که اگر به عنوان مثال ضرب در 2 شود تصویر نزدیک میشود در همان حالت اگر تصویر تقسیم بر 2 شود به حالت اولیه خود برمیگردد یک مثال از این تابع را مشاهده کنید: #include <opencv2\opencv.hpp> #include <Windows.h> #include <iostream> #include <stdio.h> #include <stack> using namespace cv; Mat output_image; Mat image_read; void Zoom_In() { pyrUp(image_read, output_image, Size(image_read.cols * 2, image_read.rows * 2)); imshow("Zoom In", output_image); image_read = output_image; } void Zoom_Out() { pyrDown(image_read, output_image, Size(image_read.cols / 2, image_read.rows / 2)); imshow("Zoom Out", output_image); image_read = output_image; } void ZoomIn_And_ZoomOut(int position, void* user) { std::cout << "Position: " << position << " " << " user data " <<(int)user<< std::endl; } int main() { void* userdata = (void*)10; TrackbarCallback zoom_in = (TrackbarCallback)Zoom_In; TrackbarCallback zoom_out = (TrackbarCallback)Zoom_Out; image_read = imread("C:\\Users\\Mohamad4030\\Desktop\\c9ac_d2ynkbk.jpg", IMREAD_COLOR); imshow("befor",image_read);//befor filtering ShowWindow(GetConsoleWindow(), SW_HIDE); createTrackbar("TrackBar1", "befor", 0, 10,zoom_in); createTrackbar("TracBar2", "befor", 0, 10, zoom_out); waitKey(0); } حاصل اجرای کد بالا بصورت زیر خواهد بود: تابع buildPyramid: کار تابع pyrDown راانجام میدهد این کار توسط پارامتر سوم صورت میگیرد فقط تنها نکته ای که وجود دارد پارامتر دوم این تابع یک InputOutputOfArray که منظورش یک ورودی و خروجی از ارایه است دریافت می کند یعنی باید یک ارایه ای دریافت کند که ورودی و خروجی ان فقط از همان ارایه باشد در دو تابع ذکر شده ما امدیم وبعد از عملیات تصویر اصلی را برابر تصویر که عملیات روی ان صورت گرفته قرار میدادیم تا در عملیات بعدی نتیجه مطلوب را دریافت کنیم اما در اینجا فقط با دادن مقداری که میخواهیم عملیات ZoomOut را انجام دهیم تصویر را دور میکند یک مثال از این تابع مشاهده کنید: #include <opencv2\opencv.hpp> #include <Windows.h> #include <iostream> #include <stdio.h> #include <stack> using namespace cv; Mat output_image; Mat image_read; /*void Zoom_In() { pyrUp(image_read, output_image, Size(image_read.cols * 2, image_read.rows * 2)); imshow("Zoom In", output_image); image_read = output_image; } void Zoom_Out() { pyrDown(image_read, output_image, Size(image_read.cols / 2, image_read.rows / 2)); imshow("Zoom Out", output_image); image_read = output_image; } void ZoomIn_And_ZoomOut(int position, void* user) { std::cout << "Position: " << position << " " << " user data " <<(int)user<< std::endl; }*/ int main() { vector<Mat> vecmat; image_read = imread("C:\\Users\\Mohamad4030\\Desktop\\c9ac_d2ynkbk.jpg", IMREAD_COLOR); buildPyramid(image_read, vecmat,4); for (int i = 0; i < vecmat.size(); i++) imshow("ZoomIn"+i, vecmat[i]); ShowWindow(GetConsoleWindow(), SW_HIDE); waitKey(0); } دقت کنید که کلاس vector از خود کتابخانه ی OpenCV است و باید از همین کلاس برای این تابع استفاده کنید جز این با خطا مواجه میشوید. حاصل اجرای کد بالا بصورت زیر است:
-
1 امتیازکامپایلر Cling یک مترجم تعاملی برای سیپلاسپلاس است، این مترجم تحت بالاترین کتابخانههای Clang و LLVM ساخته شده است. در واقع از آنجایی که کامپایلر Clang از آخرین ویژگیها و استانداردهای زبان سیپلاسپلاس پشتیبانی میکند، Cling اجازه میدهد تا توسعهدهندگان اسکریپتهای خود را با استفاده از C و C++ بنویسند. اگر شما به طور مستقیم مترجم را اجرا کنید، یک محیط زنده برای آغاز برنامه نویسی با سیپلاسپلاس را خواهید داشت که به عنوان بخشی از استاندارد نحو سی و سیپلاسپلاس به شمار میآید. همچنین میتوانید دیگر دستورات را با نقطهی "." آغاز در اختیار داشته باشید. وقتی از مترجم تعاملی استفاده میکنید، میتوانید کد زیر را بنویسید: #include <stdio.h> printf("hello world\n"); همانطور که میبینید نیازی نیست تا در مورد حوزهی دامنهها نگران باشید؛ کافی است شما تابع مورد نظر خود را صدا بزنید. اگر قصد شما این است که از Cling به عنوان یک مترجم برای ساخت اسکریپتها استفاده کنید، باید همه چیز را در داخل یک تابع قرار دهید.چرا که نقطهی ورود به اسکریپت به طور پیشفرض همانند نام فایل میباشد. میتوان آن را برای صدا زدن دیگر توابع سفارشی سازی کرد. بنابراین مثال قبل میتوانید به شکل زیر تغییر کند: #include <stdio.h> void _01_hello_world() { printf("foo\n"); } یک نسخهی دیگر در قالب سیپلاسپلاس #include <iostream> void _02_hello_world() { std::cout << "Hello world" << std::endl; } مثالها کاملاً ساده هستند، اما آنها به شما نشان میدهند که چگونه باید شروع کنید. در مورد کیوت چطور؟ #include <QtWidgets/qapplication.h> #include <QtWidgets/qpushbutton.h> void _03_basic_qt() { int argc = 0; QApplication app(argc, nullptr); QPushButton button("Hello world"); QObject::connect(&button, &QPushButton::pressed, &app, &QApplication::quit); button.show(); app.exec(); } اما توجه داشته باشید که کد قبلی کار نخواهد کرد، شما باید برخی از پارامترهای سفارشی را در Cling مشخص کنید. cling -I/usr/include/x86_64-linux-gnu/qt5 -fPIC -lQt5Widgets 03_basic_qt.cpp شما میتوانید Cling را برای خودتان بر اساس آن چیزی که برای اسکریپت خود نیاز دارید سفارشی سازی کنید. همچنین شما میتوانید Cling را به عنوان یک کتابخانه در اپلیکیشنهای خود آورده و از سیپلاسپلاس به عنوان زبان برنامهنویسی استفاده کنید. این پُست در آینده ادامه خواهد داشت. ?
-
1 امتیاز
نگارش 3.2.1
15 دریافت
گرافیک کامپیوتری همواره از جمله جذابترین جنبه های کامپیوترها بوده است. بازیها، برنامه های طراحی گرافیکی دو بعدی و سه بعدی و شبیه سازیها همگی به نوعی از قابلیتهای گرافیکی یک کامپیوتر بهره میبرند. مهمترین نکته در این زمینه، برقراری تعادلی منطقی بین کیفیت تصاویر نمایش داده شده بر روی صحنه و سرعت اجرای برنامه میباشد. کتابخانهی GLFW ابزاری برای استفاده با اوپنجیال (OpenGL) است که امکاناتی برای برنامهنویس برای قابلیت کشیدن و مدیریت پنجرهها و کانتکست اوپنجیال و ورودی جوساستیک، صفحهکلید و موشواره فراهم میکند.رایگان
-
1 امتیازساخت یک کامپوننت آپلود در انگولار(Uploader Component) میتواند کار سختی باشد. به این دلیل که با فایل ها در جاوا اسکریپت سر و کار داریم. در این آموزش یک روش خوب را برای آپلود فایلها آموزش میدهم. به علاوه این که چطور از روند آپلود در لحظه خبر داشته باشیم و با نمایش درصد دادههای آپلود شده تجربهی خوبی را برای کاربران فراهم کنیم. نکته: در این آموزش من به مباحث سمت سرور و دریافت فایل توسط آن نمیپردازم و به این شکل در نظر میگیرم که شما سروری آمادهی دریافت فایل دارید. حالا در سمت کلاینت که انگولار 6 یا بالاتر است، میخواهیم این فایل را با یک درخواست از نوع POST به سمت سرور بفرستیم و در حین آپلود گزارشی از روند آپلود در انگولار را نمایش دهیم. در این مورد من از کامپوننتی (Component) از انگولار متریال به نام نوار پیشرفت (Progress Bar) استفاده میکنم. میتوانید نمونه نهایی این پروژه که برای آموزش ساخته شده را در مخزن گیتهاب مشاهده کنید. ساخت پروژه انگولار برای شروع، در دایرکتوری (Directory) مد نظر، پروژهی انگولار خود را با استفاده از رابط خط فرمان انگولار (Angular CLI) به نام آپلودر میسازیم. پس دستور زیر را وارد میکنیم: ng new uploader وابستگیهای خارجی از آنجایی که ما نیاز به عناصر پیچیدهی رابط کاربری (UI)، مثل نوار پیشرفت داریم؛ تصمیم گرفتم تا از کتابخانهی انگولار متریال استفاده کنیم. برای نصب این کتابخانه همچنین انیمیشنها از دو دستور زیر استفاده کنید: npm install --save @angular/material @angular/cdk npm install --save @angular/animations برای این که بتوانیم از سیاساس (CSS) این ماژول استفاده کنیم، لازم است که آن را در فایل استایل سراسری خود وارد (Import) کنیم: @import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; ساخت ماژول (Module) امکانات برای این که کامپوننت خوب ما تا حد ممکن قابلیت استفادهی دوباره را داشته باشد، تصمیم گرفتم آن را در یک ماژول جدا بستهبندی کنم. برای ساخت این ماژول دستور زیر را استفاده کنید: ng generate module upload ساخت کامپوننت آپلود در انگولار برای ساخت کامپوننت آپلود کافی است از دستور زیر استفاده کنید. همانطور که از نام این کامپوننت مشخص است، برای آپلود فایلهای (File) خود از یک دیالوگ استفاده خواهیم کرد. ng generate component upload/dialog افزودن ماژولهای خارجی بعد، نیاز خواهیم داشت که تعداد زیادی ماژول خارجی را در ماژول جدید خود یعنی upload.module.ts وارد کنیم. برای مثال، نیاز داریم تا تمام عناصر رابط کاربریای که استفاده خواهیم کرد را به این شکل وارد کنیم: import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; import { DialogComponent } from './dialog/dialog.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MatButtonModule, MatDialogModule, MatListModule, MatProgressBarModule } from '@angular/material'; @NgModule({ imports: [ CommonModule, HttpClientModule, BrowserAnimationsModule, MatButtonModule, MatDialogModule, MatListModule, MatProgressBarModule ], declarations: [DialogComponent], exports: [DialogComponent], entryComponents: [DialogComponent] }) export class UploadModule { } توجه کنید در صورتی که دقیقاً دستورهای گفته شده را وارد کردید، کامپوننت شما توسط CLI در ماژول upload تعریف خواهد شد. درصورتی که شما از ماژولی استفاده نمیکنید، کامپوننت جدید شما در فایل app.module.ts تعریف میشود. همچنین برای این که کامپوننت دیالوگ ما به خوبی کار کند لازم است تا آن را به عنوان EntryComponent در ماژول خود اضافه کنیم. این کار در ماژول نمونه بالا انجام شده است. حالا برای این که بتوانیم از کامپوننت جدید خود استفاده کنیم باید ماژول تعریف شده را در app.module.ts وارد کنیم تا به AppComponent اضافه شود. سرویس آپلود در انگولار قبل از ساخت محتوای نمایشی کامپوننت آپلودر، لازم است ابتدا منطق آپلود را پیادهسازی کنیم. این منطق در سرویس آپلود قرار خواهد گرفت. آن سرویس را با استفاده از دستور زیر ایجاد میکنیم: ng generate service upload/upload داخل این سرویس نیاز داریم تا از HttpClient استفاده کنیم. این سرویس تنها دارای یک تابع به نام upload خواهد بود و برای هر فایل یک Observable برمیگرداند. این Observable ها حاوی روند آپلود و درصد آن خواهد بود. /** * @description A Http request to upload files * @param file */ upload(file: File): Observable<any> { const formData: FormData = new FormData(); let url = ''; formData.append('video', file, file.name); url = 'http://localhost:1500/api/upload/videos'; const req = new HttpRequest('POST', url, formData, { reportProgress: true, responseType: 'text' }); const progress = new Subject<any>(); this.http.request(req).subscribe(event => { if (event.type === HttpEventType.UploadProgress) { const percentDone = Math.round(100 * event.loaded / event.total); progress.next({ percent: percentDone, loaded: event.loaded }); } else if (event instanceof HttpResponse) { progress.complete(); } }); return progress.asObservable(); } توجه داشته باشید که برای استفاده از این تابع باید موارد زیر را وارد کنید: import { HttpClient, HttpEventType, HttpResponse, HttpRequest } from '@angular/common/http'; import { Observable, Subject } from 'rxjs'; همچنین نمونهای از HttpClient را در سازنده (Constructor) کلاس تعریف میکنیم تا بتوانیم درخواست http ارسال کنیم: constructor(private http: HttpClient) { } در تابع آپلود، فایل دریافتی را در یک فرم بسته بندی کرده، یک درخواست http از نوع POST ساخته و آن درخواست را همراه با فرم به عنوان دادهی همراه درخواست ارسال میکنیم. سپس به روند آپلود فایل گوش میدهیم، درصد آپلود را محاسبه میکنیم و مقدار آن را به جریان داده پاس میدهیم. حالا برای فایل مورد نظر observable ای از روند آپلود را در اختیار داریم. سرانجام نیاز داریم تا آن سرویس را به عنوان provider در ماژول خود اضافه کنیم. برای این کار کافی است سرویس خود را در فایل ماژول وارد (Import) کنیم (اگر توسط CLI وارد نشده بود) و آن را به این صورت در قسمت providers ماژول معرفی کنیم: @NgModule({ imports: [ CommonModule, HttpClientModule, BrowserAnimationsModule, MatButtonModule, MatDialogModule, MatListModule, MatProgressBarModule ], declarations: [DialogComponent], exports: [DialogComponent], entryComponents: [DialogComponent], providers: [UploadService] }) export class UploadModule { } نکته: در پروژه نمونه برای این که در App Component از دکمههای متریال استفاده میشود، MatButtonModule را در قسمت exports هم قرار دادم. باز کردن دیالوگ آپلودر برای باز کردن دیالوگ در فایل .ts کامپوننت اصلی یعنی App ماژول دیالوگ و کامپوننت دیالوگ خود را وارد ( import) میکنیم: import { MatDialog } from '@angular/material'; import { DialogComponent } from './upload/dialog/dialog.component'; حالا در سازنده کلاس (constructor) نمونهٰای از MatDialog تعریف میکنیم: constructor(public dialog: MatDialog) { } با استفاده از تابع زیر میتوانید دیالوگ ساخته شده توسط خود را باز کنید. این تابع را در کلاس کامپوننت App قرار میدهیم: openDialog() { this.dialog.open(DialogComponent, { width: '720px' }); } برای استفاده از این تابع تنها کافی است آن را به رویداد (event) کلیک روی دکمه بایند (Bind) کنیم. پس عبارت زیر را روی تگ دکمهای که برای باز کردن دیالوگ در نظر گرفتهایم قرار میدهیم: (click)="openDialog()" اضافه کردن فایلها اولین کاری که باید در این قسمت انجام دهیم اضافه کردن یک عنصر input از نوع فایل به دیالوگ است. این عنصر input تنها راه تحریک منوی انتخاب فایل سیستم عامل برای باز شدن است. <input type="file" #file style="display: none" (change)="onFilesAdded()" multiple /> اما برای این که ظاهر زشتی دارد قرار است که آن را با استفاده از CSS مخفی کنیم. سپس آن را با استفاده از رویداد کلیک، از طریق منطق کامپوننت باز میکنیم. برای این کار در فایل ts کامپوننت یک ارجاع به آن عنصر نیاز داریم. برای این کار از دایرکتیوی (directive) به نام ViewChild استفاده میکنیم. همچنین متغیری نیاز داریم تا فایل را در آن ذخیره کنیم. همانطور که میبینید برای استفاده از سرویس آپلود نمونهای از آن را در سازنده کلاس تعریف کردهام. import { Component, OnInit, ViewChild } from '@angular/core'; import { UploadService } from '../upload.service'; @Component({ selector: 'app-dialog', templateUrl: './dialog.component.html', styleUrls: ['./dialog.component.scss'] }) export class DialogComponent implements OnInit { @ViewChild('file') file; addedFile; constructor(private uploadService: UploadService) { } ngOnInit() { } } حالا با شبیهسازی یک کلیک منوی انتخاب فایل سیستم عامل را باز میکنیم. addFiles() { this.file.nativeElement.click(); } طبق عنصر input که بالاتر نوشتهام، هنگامی که انتخاب فایل توسط کاربر انجام شد تابعی به نام onFilesAdded صدا زده میشود که شکل زیر را دارد: onFilesAdded() { this.addedFile = this.file.nativeElement.files[0]; console.log('Added File:', this.addedFile); } حالا فایل ذخیره شده را داریم که آمادهی آپلود است برای آپلود تابعی را تعریف کرده و از سرویسی که قبلا تهیه کردیم استفاده میکنیم. برای آن که درصد فایل آپلود شده و مقدار دادهی آپلود شده را داشته باشیم دو متغیر جدید تعریف میکنیم و به شکل زیر درصد آپلود و مقدار داده آپلود شده را بدست میآوریم: upload() { const progress = this.uploadService.upload(this.addedFile); progress.subscribe((result) => { this.percent = result.percent; this.loaded = result.loaded; }); } حالا برای مثال میتوانیم روند پیشرفت آپلود را به شکل زیر به نمایش بگذاریم: <mat-progress-bar mode="determinate" [value]="percent"></mat-progress-bar> نمونهی پروژهی آماده شده برای این آموزش را میتوانید از اینجا دریافت کنید. امیدوارم آموزش مفیدی بوده باشه. خوشحال میشوم نظرات شما را درباره این آموزش بدانم.