From 9af3ad400ba6d34d0eb978c2ccf90fed82fe39d8 Mon Sep 17 00:00:00 2001 From: Jordan Wages Date: Wed, 4 Jul 2018 18:11:25 -0500 Subject: [PATCH] Adding icons. Progress Bar. --- butterflow-ui/ButterflowWrapper.cs | 72 +++++++++++- butterflow-ui/Icon/icon.ico | Bin 0 -> 22382 bytes butterflow-ui/Icon/icon.png | Bin 0 -> 2250 bytes butterflow-ui/Icon/icon.svg | 100 +++++++++++++++++ butterflow-ui/Icon/icon_plain.svg | 103 ++++++++++++++++++ butterflow-ui/Icons.xaml | 9 ++ .../Localization/Localization.Designer.cs | 18 +++ butterflow-ui/Localization/Localization.resx | 6 + butterflow-ui/MainWindow.xaml | 51 +++++++-- butterflow-ui/MainWindow.xaml.cs | 5 +- butterflow-ui/OptionsConfiguration.cs | 2 +- butterflow-ui/butterflow-ui.csproj | 8 ++ butterflow-ui/icon.ico | Bin 0 -> 22382 bytes 13 files changed, 355 insertions(+), 19 deletions(-) create mode 100644 butterflow-ui/Icon/icon.ico create mode 100644 butterflow-ui/Icon/icon.png create mode 100644 butterflow-ui/Icon/icon.svg create mode 100644 butterflow-ui/Icon/icon_plain.svg create mode 100644 butterflow-ui/icon.ico diff --git a/butterflow-ui/ButterflowWrapper.cs b/butterflow-ui/ButterflowWrapper.cs index 1fe2b20..d2efbeb 100644 --- a/butterflow-ui/ButterflowWrapper.cs +++ b/butterflow-ui/ButterflowWrapper.cs @@ -1,4 +1,5 @@ -using System; +using csmic; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -17,8 +18,12 @@ namespace butterflow_ui /// The RegEx string for matching probed resolution. private const string REGEX_RESOLUTION = @"Resolution\s*:\s(?\d+)x(?\d+)"; - /// The RegEx string for matching the probed playback rate.. + /// The RegEx string for matching the probed playback rate. private const string REGEX_RATE = @"Rate\s*:\s(?\d+\.\d+) fps"; + /// The RegEx string for detecting progress made when rendering a video. + private const string REGEX_PROGRESS = @"To write\:\s*\w*\s*\w*\,*\w*\s*(?\d+\.*\d*)%"; + /// An alternative RegEx string for detecting progress made when rendering a video. + private const string REGEX_PROGRESS_ALT = @"\\d+\.*\d*)%\>"; /// Full pathname of the butterflow executable file. private Lazy executablePath = new Lazy(() => Path.Combine(Directory.GetCurrentDirectory(), "ThirdPartyCompiled", "butterflow.exe")); @@ -26,6 +31,12 @@ namespace butterflow_ui private string consoleOutput = string.Empty; /// True if butterflow is running, false if not. private bool isRunning; + /// The progress percentage as reported by butterflow. + private double progressPercentage; + /// The running butterflow process. + private Process runningProcess; + /// An input interpreter used for converting string values to numeric values. + private InputInterpreter interpreter = new InputInterpreter(); /// Event queue for all listeners interested in ParsedConsoleOutputRecieved events. public event EventHandler ParsedConsoleOutputRecieved; @@ -48,7 +59,7 @@ namespace butterflow_ui } } - /// Gets or sets a value indicating whether butterflow is currently running. + /// Gets a value indicating whether butterflow is currently running. /// True if butterflow is running, false if not. public bool IsRunning { @@ -56,13 +67,28 @@ namespace butterflow_ui { return this.isRunning; } - set + private set { this.isRunning = value; OnPropertyChanged(); } } + /// Gets the progress percentage as reported by butterflow. + /// The progress percentage as reported by butterflow. + public double ProgressPercentage + { + get + { + return this.progressPercentage; + } + private set + { + this.progressPercentage = value; + OnPropertyChanged(); + } + } + #endregion #region Methods @@ -76,6 +102,15 @@ namespace butterflow_ui Run(arguments); } + /// Kills the running instance of butterflow, cancelling its current operation. + public void Cancel() + { + if(this.IsRunning && this.runningProcess != null) + { + this.runningProcess.Kill(); + } + } + /// Probes a video file. /// The video file to be probed. public void Probe(string videoFile) @@ -107,6 +142,7 @@ namespace butterflow_ui process.BeginErrorReadLine(); this.IsRunning = true; + this.runningProcess = process; } } @@ -116,6 +152,7 @@ namespace butterflow_ui private void Process_Exited(object sender, EventArgs e) { this.IsRunning = false; + this.runningProcess = null; } /// @@ -131,7 +168,7 @@ namespace butterflow_ui return; } - //Test for resolution + // Test for resolution var regex = new Regex(REGEX_RESOLUTION); foreach (Match match in regex.Matches(consoleOutput)) { @@ -142,7 +179,7 @@ namespace butterflow_ui OnParsedConsoleOutputRecieved(ButterflowOutputType.Height, height, consoleOutput); } - //Test for playback rate + // Test for playback rate regex = new Regex(REGEX_RATE); foreach(Match match in regex.Matches(consoleOutput)) { @@ -150,6 +187,29 @@ namespace butterflow_ui OnParsedConsoleOutputRecieved(ButterflowOutputType.Rate, rate, consoleOutput); } + + // Test for progress being made when rendering a video + regex = new Regex(REGEX_PROGRESS); + foreach(Match match in regex.Matches(consoleOutput)) + { + var progress = match.Groups["Progress"].Value; + + this.interpreter.Interpret(progress); + this.ProgressPercentage = this.interpreter.Double; + + OnParsedConsoleOutputRecieved(ButterflowOutputType.Progress, progress, consoleOutput); + } + + regex = new Regex(REGEX_PROGRESS_ALT); + foreach (Match match in regex.Matches(consoleOutput)) + { + var progress = match.Groups["Progress"].Value; + + this.interpreter.Interpret(progress); + this.ProgressPercentage = this.interpreter.Double; + + OnParsedConsoleOutputRecieved(ButterflowOutputType.Progress, progress, consoleOutput); + } } /// Executes the parsed console output recieved action. diff --git a/butterflow-ui/Icon/icon.ico b/butterflow-ui/Icon/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..47fd522f29b1b09a61cc0968007eefd245769905 GIT binary patch literal 22382 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 literal 0 HcmV?d00001 diff --git a/butterflow-ui/Icon/icon.png b/butterflow-ui/Icon/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..08040b1ac7961d7dd91de8185e7b1c8c143aa43c GIT binary patch literal 2250 zcmV;*2sQVKP)pi1sh{hF{qN<3-R8Kf2m0rCrRdjSo z=;)Sk?qVu=>ReK~*plKomP8qvH1$N4!vKB{8aqQNB$6=_ttyy-5f(+ zjwX~x0CZT=H|Ew7AN@183wQkD;nCh@3p|+q_0~vdL^ajfS^&`Jy#5@~asrcULO_Uy zsgqooI+4Swr6FHOw~Xz-YOmRQxVt);Qtt+E!rC1AsVMQ0dT3q?$g2fr!@; z2S>4fuYO);_w`;ZUIKjrxhX$Dymx=--wXQ9G6&gyGp4xQEQ@hzxtJL44EgBIvZ~G7 zxFWa^_^6z{CRf*BN`o-;J)qy&Okj-cjrWJHh^!<8^_qduD2Bo+o~Jg93o(IB zD@%Q~ivq;w$rPc%f@~feRiN)+fU`Oy!@1-HglHfH8qbkgLRy;*z|T48(I+!)Ri;!O98D#+754&K_maJE!6c0zO*1 z{Z`(yV2+Plc)JI@E0gB>j6+^2Hr6$d^#J6iyv35J-vfdH8haj_$OGW>RyZ%Ui#e;; zwd{?JGN{wd?(#;FFJ2P!KJ%)Cu1(*sU@0!v&lrmVDCTUAjm46ff1v9;q#zH@&@w>3 z8u~UMf6}I$HT+^-*Cf^gkSA&&M2f9Ok(4(YGdEZToz?3WH#5S11`L>B5hE+9J1jvq zkM%vkO3D}PpWnsci|*7=bqh1ZDlm+@wcV6A(zSq%0F1d z(8~TWY$ee=7OQ}T)zG))^*1srQW9>4mI~nkl`0w^Kj{hx&bC(2J zOl!*ADzEFSUnt4%Ui4O5uWK|fYk{!$KEUt+rSCxZt{^3JstV;QR(oAZ-NMDmEU5P|(?2+Pfs|df}S9^M!Va5b|C)CT|DO zm=aYfV-roN*BVd8cdo4-<;(Zaz-d#uMKYrRHeZMoUX71BB>Z4!YwE>@PK{y6p;$tB z3c!a21{^!NjCJRZ{c!y?9B9bCHUDOH5Kq^Q9iaEv?oRAJ+-Ii&wTt}Nxp_1m-PMJ* z>j=0w_9Orm1p7r%T3^VcAy$Fx=F}aLvAMoQn*PNz!lBnY_X(mpIhIh?ps@4s@>E&b z7&f~I1W1YmTQ{!2rPBwo<)In~Vy+Z#+)6Zku_f{b(QUC#jG=;}E)sU;_ zf9?Ls^Bh7!A3VG(|Gc~JyeI#>5jGx%GRFiBjorBE&eMsf_kHwsBB@lx z63Qx=bRGaCRXy17b~v&3@b%)qVpzHFu!ybq0UusYVg7ypNYrlnpyg^ux;he2@fS#gltlr21!D)2fVZqOATLfHY{w;xtF& z>WmYAk9}*&`otENbxo+6WiYP+B~jl#!Z$+QVaYXPVN;?fBA*me>KXv8wiJz#RTaCV z33YeCPhJCXccx+g@4J=1oQ=e)D&1W9c$P04&5#LWShRP^D7!g3Kw8q!6ICFlN2mwZ%h0A5y0FHOM;K<(k{n6)-Th1~8wHl5rx@9} zc~@mc`8WVTPb7|1T57kWcf$oBDTvMIKWGsE;I>argOE(m0vZ1{QO@Zty{)U7+;F6 z?ih}~*+c<&-HzB0e;XVT{;8?>W8L?Exp41}^{)CS*0|@+xnq_s^MR$q-KiE4-UaO{*9mp81K2aQUP$VUA_e zr9uBG0QcxSxmb1=dT$ps0Gh|m9VjL;ayQw-O9;SZ0B!&=0G9!zKGqpII5;>sIQ;MP YA3M4J1BcMV?*IS*07*qoM6N<$g6LN`aR2}S literal 0 HcmV?d00001 diff --git a/butterflow-ui/Icon/icon.svg b/butterflow-ui/Icon/icon.svg new file mode 100644 index 0000000..89857c7 --- /dev/null +++ b/butterflow-ui/Icon/icon.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + UI + + diff --git a/butterflow-ui/Icon/icon_plain.svg b/butterflow-ui/Icon/icon_plain.svg new file mode 100644 index 0000000..1d400b3 --- /dev/null +++ b/butterflow-ui/Icon/icon_plain.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + UI + + diff --git a/butterflow-ui/Icons.xaml b/butterflow-ui/Icons.xaml index 830cf78..8e389fe 100644 --- a/butterflow-ui/Icons.xaml +++ b/butterflow-ui/Icons.xaml @@ -157,4 +157,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/butterflow-ui/Localization/Localization.Designer.cs b/butterflow-ui/Localization/Localization.Designer.cs index 4919c59..684a790 100644 --- a/butterflow-ui/Localization/Localization.Designer.cs +++ b/butterflow-ui/Localization/Localization.Designer.cs @@ -159,6 +159,24 @@ namespace butterflow_ui.Localization { } } + /// + /// Looks up a localized string similar to Cancel. + /// + public static string CancelLabel { + get { + return ResourceManager.GetString("CancelLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel the current butterflow operation.. + /// + public static string CancelTooltip { + get { + return ResourceManager.GetString("CancelTooltip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Clip a subregion in the video.. /// diff --git a/butterflow-ui/Localization/Localization.resx b/butterflow-ui/Localization/Localization.resx index eda7438..02f2572 100644 --- a/butterflow-ui/Localization/Localization.resx +++ b/butterflow-ui/Localization/Localization.resx @@ -234,6 +234,12 @@ 60 fps + + Cancel + + + Cancel the current butterflow operation. + Use Fast Pyramids diff --git a/butterflow-ui/MainWindow.xaml b/butterflow-ui/MainWindow.xaml index fbf169a..ae674e6 100644 --- a/butterflow-ui/MainWindow.xaml +++ b/butterflow-ui/MainWindow.xaml @@ -9,7 +9,8 @@ xmlns:butterflow_ui="clr-namespace:butterflow_ui" mc:Ignorable="d" x:Name="butterflowUIWindow" - Title="{x:Static loc:Localization.Title}" Height="600" Width="800"> + Title="{x:Static loc:Localization.Title}" Height="600" Width="800" + Icon="./Icon/icon.ico"> @@ -103,12 +104,24 @@ - + + + + + + + + @@ -263,9 +276,27 @@ - - - + + + + + + + + + + + + + + diff --git a/butterflow-ui/MainWindow.xaml.cs b/butterflow-ui/MainWindow.xaml.cs index acced8e..ea0a78d 100644 --- a/butterflow-ui/MainWindow.xaml.cs +++ b/butterflow-ui/MainWindow.xaml.cs @@ -94,7 +94,8 @@ namespace butterflow_ui this.OptionsConfiguration.Height = e.Value; break; case ButterflowWrapper.ButterflowOutputType.Progress: - break; + // This case doesn't need to be considered since we're binding the progress bar's value to a property on the butterflow wrapper. + // We may use this in the future, though. default: break; } @@ -112,7 +113,7 @@ namespace butterflow_ui if (result.HasValue && result.Value) { this.OptionsConfiguration.VideoInput = ofd.FileName; - + this.ButterflowWrapper.Probe(ofd.FileName); //Hack to get the first frame to display in the media preview element. diff --git a/butterflow-ui/OptionsConfiguration.cs b/butterflow-ui/OptionsConfiguration.cs index 8e24f89..0ba4d02 100644 --- a/butterflow-ui/OptionsConfiguration.cs +++ b/butterflow-ui/OptionsConfiguration.cs @@ -24,7 +24,7 @@ namespace butterflow_ui private const decimal DEFAULT_SMOOTH_DERIVATIVE_STANDARD_DEVIATION = 1.1m; private const FlowFilterType DEFAULT_FLOW_FILTER_TYPE = FlowFilterType.box; - /// An interpreter used to ensure numeric input is correctly calculated. + /// An input interpreter used for converting string values to numeric values. private InputInterpreter interpreter = new InputInterpreter(); /// The aspect ratio used for calculating heights when the aspect ratio is locked. private decimal aspectRatio = 0; diff --git a/butterflow-ui/butterflow-ui.csproj b/butterflow-ui/butterflow-ui.csproj index 3481b96..bbd4fa7 100644 --- a/butterflow-ui/butterflow-ui.csproj +++ b/butterflow-ui/butterflow-ui.csproj @@ -33,6 +33,9 @@ prompt 4 + + icon.ico + packages\csmic.1.1.4\lib\net40\csmic.dll @@ -173,6 +176,11 @@ + + + + + diff --git a/butterflow-ui/icon.ico b/butterflow-ui/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..47fd522f29b1b09a61cc0968007eefd245769905 GIT binary patch literal 22382 zcmeHP3vg8B6~6n(ZZ?nIWH%%+k0nn?LIO!3A-qCVXz^9Y0qo!t1cXurLBdl53M!1n zfMbU;1_Gv9rKr`~QNSt+)QAtL+Ei^R;wxo<5=aQ)VNSnu_x{(rH%pQw*+^#g&YYb4 z`2TzU@0|bt&$<79Hj-qJOj1;o1fC+zG)q#kBuObLp6lmve-rMNm3gkSu9Bpm^^v43 zl))p^@Z1NxY!8D80B{KIZ$s_*L)9t~0}9o1Yu`-uTz?W#cB4Ui&sBEqhKTHf+2Wox z!7WZpl9v2-t9y0Tx9+vu$h~1ZMUI(JDIN-*Jhgnqm?ol()f*de?*?%{`_%)K|2a|K zCqx7OPLzZ5=sOmy6wd`unpVED^lPHw{{n%ht=`xYKciyPpbr~qAnN7rC(8bqDC-}n ze|veG`pKw23iV3CX`{7=+kNQbd%U6y-{bJM|f;JYSUOp&c(W--K&%RH&haem8$V7V_ zKH8r+ZNbVB-=IG9GX(D_1kZZ)Qxf_K?O(hwVa-;``j*IkYxxRsUvSsBiIv$~c2MT} zKT+y$HlKE{tlExeW5sj9Z9~Vcw2i!OgScky9D9>jU(i0YW#y}vnFrP8NY?yO>V1Ds z3}%a5TyTHt2BIthl1e{F(teX9?FQ}A({9{D&wg859_KvIq=3pmJU<1Gpay~(xcoH$ zU+ZzYXsR(ZtWym(7%?ey-(fSDOig%QYDcd$gxa)45^?cs(7LdJCGSLCbITFu&9lgP z>wVvw>{xTQ-Y5;s)4$foC*YW$TUN7-|VNF}ShRVJp8uf3~yXg+DH_LZX$sTvt z0xCk;eE3%3)AJ5^{5tz)IW~Zw?lX5yr{$+ZekpK_1ddVgy+iiYQ}mVBx1bHvb?q41 z@7edLK#|Sy2*(7uihNl%#|M7OXZ)rAAsPYs!@nRJ2I3f?;N9=3&-iJK<8cr>+S@Xo z-szDK8FD-X*&Gw)h`5Mj8{G2;Kg%D*@*#IP;*Am}*pOqOp$8F*aLn@izfu3^YN-%$ z8S7of&axHkA};FRMft-tvPBHVanxXrg~0P6TPuHnB45@$%l;6t8Scg0*LnLh{*k~h z>mKrD+`!K9pDY`Fx)AXm$AK(+kk0Nx_l&(C%ZDxek)O))p=Y6cme1IQe2yVme!e1K z)xBbOt?pTVroZwRt)UT+FLckch3+~2W%-Pqbw3cqeZI|@Q*@vCLD%G~*kNm-dzM|m zdS{My4niNyLmT-!g6+;EbkFiLaP9KL{;9w}9CHu%dzR07FXBE2Qn5n^**m@=#H+7_ z#3gNwo-l*bp8JS$(VlGdH@V+I_ZgQUKSjvb*{JbdB~&ekzN$KH^=9xSsDYpceliVEJI5J3 zJ4w474J0`lNKu`lg4Fok1 z)Ijgk0K4-^poO4E6n+;tZ~hrU7dz(Q!73f`dSY)muEB@h0f#;8c+uGFj!u}pknVi& zF`9VaVj6SH6pBenKOJh1IW9>hynbiUgK}D>x*MiXpve`hWD0dA;9KpfA2&?(6)vyq zdmUj?{li6y>Iqt}iRt$X`pat|I#Qq0wV&^Mp) zuX}5gT#m~|WfWrlxmV!N3N=Z`st>#*x4ovWo+6X`B3e$_4vY_hs;w5&iOM%O%FnkQ zZX{QBF4h6I+^E$-FUG%i-Yo+^V4E@ha^6ysa_@Rsiw);fauTIlQD$-VHVRE0{kgVG zZ^e%>|H0O6vpx~n;){mCwx>pDvEe+@WRRL)JGNKW5swd}CG0%ztMfo_#gEODuQ%;~ zRK~?Ru*IcdkD?(}haXuo(y6V7)~V&pVfG7>#;S%yYD%Xwp^9^S65y%}cWCxP_z=d)B^97R8_>d_ws>T!* z^N!i>{5-^9xKLA9sqH6Z2+2BwwOh_f%Ii-2W_$D*YhvFfQ-pmd-klsk{P|dUZmOwU z>xIiP?w;n@85OMwOV?B1wXaaxGqq%OIH{)YOSPQM6-#L^enBA#sVC9C{O-hWbEh|q zs%xRX_g0)UhT58pX6t;&@V!XxzjDm5?JQ%s{cuR1D;h1Kw$_@ueLv!giKXnn*2{ZQ zybsCypq$@jb;bgJy;r%69rYBFoQ~zR?#DkG`xT|wrzqO-1v!dFH<`jC4nxjZ|KWPo z$S_9O4@M3d-IQ6qOWynQg)95bdU+34?89CRe>&q&3Jm^I1^+1MfGKDFD`bhiqRAZL z*a1YDUZ^^E5oNT6A4#rQ|AV~ur|dmmw)laW?a$abhk$9=XD#HO_sDruSa`E3JaRK& zCw2}xKHQ8UbDY;sY7%?SV(+y*{A_n7-i!8CE`56d?XO_h;^$lfrs93aDRJujb4FWO zvoSnk9dzL8NDr`mWzxb*dCytK-j4mVl>V>c$KK~phF`Hg=W)on6r4{1;(hhO?|w(I z6Q`ZQekJ#ZP0&HVcJ#0Sc`oO*eV_9V7}xdF?jio(Wis2si1Dk~+5QTCeYrH9{l`qY zR(+2rj=H4pzkGTA*Y$lZe#S266@YV2N#UOVQo_s!&YL0}&F09cx1ob^Aicks?7U`D zqt^C}KXK8Mbj6%iGR1?(fpXq!YJ)#+!IRYI-c??dvAao~{~&J)h}TTC6o+enk00^3 zV|y)rb&q}j8A@4E)f$$Wd(vnPZ8AHYuVdgZMFSSW-yL#4TqVzaoO7Ygc@a5BoPVM9 zc|7h#{*%ask@K8IPRfU*#qTf9iwlJlHa1Wy}&MV zUGzTBm+eL0SF!u(`*I!{Y%b?xf%gc0U>5T}+g)#a(f57XUf|cx`NH;`Z`Nkq`(yha z!0$Eg72C`BVRObUunXIBewA!{aDlx;+belz+E~}uyywgIfwsREf0u2qo%cGjy{7N` z8uxNOAm^k7aQthr>*u}pY%j*X$Zz!3_tiOHV|($P=ganD-qVhIIVZ5E@V7JWMc-%K z0=vHN%ej8=b@IHY*uE=1PhbzI{d>~qp}+II-=6KYK2OEJFL3@(%=x14U#idZvb`?e zxm2Gg@TUh5KjRkIFE^hj=i>r zL9U52o2{u?4Pkl3CtQ$Vr&Fr;wvF|5v91{R0)JW{?cdElFLwG;>{m&P zd~`aOWHjH8T=Q=&Ng1c&m;A1^-|j{^_g~EUUbctNXS*|{y&t&#?^c%*o$mW=t51r50)gXWCu-$#xKG68P)91M+&NyQ%- zy*v+nH9My)K9~09F%`eo_JP80#r}uh=kf2(MXV>jbM*0^uUMDA^V0BJ2V-bB@6#l7 zfLs4K72{%ytcPh2o=e+x%B%0E2B`n5_`B%y2JAXQF}Ke%g&;|9yQsc{{p0wL27?ip9e|%{+8UO$Q literal 0 HcmV?d00001