MJW Posted June 13, 2015 Share Posted June 13, 2015 (edited) The problem of lots of obscure plugins has bothered me too; both as a plugin writer and a plugin user. I don't know of any easy way to add instructions or a help menu to CodeLab plugins. I've never tried displaying text on the PDN image, itself, though I can look into that. The CodeLab plugins have support links, which could, I assume, be linked to the PDN forum thread on which they're explained, but I worry future forum changes might result in dead links -- and I hate dead links. The file option is a good idea, both because it's a useful option, and because it reminds the user what the plugin does. I haven't read a file into a plugin, either, so I'll have to look into that. EDIT: I'm not sure of the logistics of a File option in a CodeLab plugin. Obviously it would have to ask for the file name when that option is selected, but what would launch the file selection menu? If it were launched as soon as the plugin was run, it would seem like it might be awkward to go back to the clipboard option. If there's currently a CodeLab plugin that does something like that, maybe I can take a look at it and use it as a guide. ANOTHER EDIT: Because of mufti-threading, doing single actions, especially on demand, such opening a file, isn't really very well suited to CodeLab plugins. It's possible to do one-time initialization by using locks and flags, but those are actions that occur once automatically, not things done upon request. Edited June 13, 2015 by MJW Quote Link to comment Share on other sites More sharing options...
midora Posted June 13, 2015 Share Posted June 13, 2015 CodeLab maps to IndirectUI and creates PropertyBasedEffect plugins. This limits the possible features. IndirectUi controls allow a description at the bottom of each control. But I guess CodeLab does not allow to enter one in the moment. This may be a possible extension. Quote Link to comment Share on other sites More sharing options...
BoltBait Posted June 13, 2015 Share Posted June 13, 2015 I don't know of any easy way to add instructions or a help menu to CodeLab plugins. How about like this: #region UICode byte Amount1 = 0; // [255] Help #endregion int PreviousHelpButton = -1; void Render(Surface dst, Surface src, Rectangle rect) { if (PreviousHelpButton== -1) { PreviousHelpButton = Amount1; } if (PreviousHelpButton != Amount1) { PreviousHelpButton = Amount1; System.Windows.MessageBox.Show("This is the help text"); } ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; // TODO: Add pixel processing code here // Access RGBA values this way, for example: // CurrentPixel.R = (byte)PrimaryColor.R; // CurrentPixel.G = (byte)PrimaryColor.G; // CurrentPixel.B = (byte)PrimaryColor.B; // CurrentPixel.A = (byte)PrimaryColor.A; dst[x,y] = CurrentPixel; } } } NOTE: Don't use this code. There is published improved code here. 2 Quote Download: BoltBait's Plugin Pack | CodeLab | and a Computer Dominos Game Link to comment Share on other sites More sharing options...
Red ochre Posted June 13, 2015 Share Posted June 13, 2015 Works for me! - useful example code - thanks. (not sure about the .bat files on the desktop though) ... just me being new to Pdn4. MJW - I started something similar to 'MismatchEraser' a while back but didn't publish on the forum. I occasionally find it very useful if I've accidentally merged down an object onto a texture before saving the object. - If you have the texture and the texture + object you can then isolate the object. Good work! Quote Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings Link to comment Share on other sites More sharing options...
BoltBait Posted June 13, 2015 Share Posted June 13, 2015 Works for me! - useful example code - thanks. You're welcome. It is part of a tutorial of CodeLab tricks that I never finished. not sure about the .bat files on the desktop though Those are very handy. I'll tell you why. 1) You no longer need to run Paint.NET as an administrator in order to build dll files in CodeLab. This allows you to drag-and-drop files onto Paint.NET in order to open them--something you can't do in Admin mode. 2) It is actually a quick way to install your plugin dll file. The batch file will obtain admin rights if it doesn't have them before attempting to install the dll file. Quote Download: BoltBait's Plugin Pack | CodeLab | and a Computer Dominos Game Link to comment Share on other sites More sharing options...
Red ochre Posted June 14, 2015 Share Posted June 14, 2015 .bat files will be fine. I was confused for a second when it said it had successfully built but didn't show up in the Submenu.I think (not certain) I still need to run Pdn under admin rights so that VS 2013 can copy the .dll into the effects folder.I'm running both as admin and it works - which is convenient.When/if you get time another codelab tutorial would be very appreciated... but that would be a discussion for the dev central or codelab threads.(sorry for going off topic MJW). Quote Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings Link to comment Share on other sites More sharing options...
MJW Posted June 14, 2015 Author Share Posted June 14, 2015 Wow, that's really nifty, BoltBait! I don't remember anything about those button controls, though perhaps I've just forgotten. I'll definitely add Help options to plugins, and there are probably other uses for those button controls. Does anyone know off hand an easy way to kill the Message Box if the effect is cancelled? I noticed the cancellation hangs until the Message Box is closed, which is sometimes confusing because the Message Box can be hidden behind other windows. It's not a huge problem, but it'd be nice if the Help menu could be forced to close. Quote Link to comment Share on other sites More sharing options...
MJW Posted June 14, 2015 Author Share Posted June 14, 2015 (sorry for going off topic MJW) As if I ever felt compelled to stick to the topic. I'm the one who posted a completely different plugin to this thread. The .bat files do sort of clutter up the desktop, but they make installing the plugins easier than the old method. Quote Link to comment Share on other sites More sharing options...
BoltBait Posted June 14, 2015 Share Posted June 14, 2015 Please be aware that this button is also used to increment the seed for the random number generator. So, if you're using random numbers, this button trick will affect the number series generated.Now, in my example, I used a message box. That's not the only thing you can do in there. You could popup a modal window that is forced on top of all other windows. That would take care of the cancel problem.Code for that is an exercise left up to the reader. Quote Download: BoltBait's Plugin Pack | CodeLab | and a Computer Dominos Game Link to comment Share on other sites More sharing options...
MJW Posted June 14, 2015 Author Share Posted June 14, 2015 (edited) I knew that a button was used to update the random seed, but I've always assumed it was restricted to that use. I'm quite confused about how the Message Box works in this situation. A Message Box is a modal window, but it runs independently -- essentially modelessly -- from the main plugin dialog because it's started in a rendering thread. If I remember correctly, each time a new rendering pass is started, a single thread is first completed before the rest to the threads are started. I assume this first thread runs the Message Box and blocks till it completes, but the main dialog continues to run. I tried some things I thought might work, but they didn't, and afterward I could see why. It's sort of tricky. I think a flag that allowed an interactive rendering pass to be distinguished from a non-interactive pass would solve the problem, but alas... I don't know what kinds of forms can be built within the CodeLab structure. I also admit I've never built a form on the fly, programmatically. I've let VS or CodeLab do that for me. Edited June 14, 2015 by MJW Quote Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted June 14, 2015 Share Posted June 14, 2015 Be very careful placing MessageBox inside the render loop. If it's badly misplaced (and not well trapped) you could be looking at it popping up once for every pixel. Ouch. Quote ebook: Mastering Paint.NET | resources: Plugin Index | Stereogram Tut | proud supporter of Codelab plugins: EER's Plugin Pack | Planetoid | StickMan | WhichSymbol+ | Dr Scott's Markup Renderer | CSV Filetype | dwarf horde plugins: Plugin Browser | ShapeMaker Link to comment Share on other sites More sharing options...
BoltBait Posted June 14, 2015 Share Posted June 14, 2015 Be very careful placing MessageBox inside the render loop. If it's badly misplaced (and not well trapped) you could be looking at it popping up once for every pixel. Ouch. Been there. Done that. BTW, I checked in the CodeLab code and noticed that if you have multiple "randomize" buttons, only the last one will actually update the random number generator. For example, look at this code: #region UICode byte Amount1 = 0; // [255] Help byte Amount2 = 0; // [255] Randomize #endregion int PreviousHelpButton = -1; void Render(Surface dst, Surface src, Rectangle rect) { if (PreviousHelpButton== -1) { PreviousHelpButton = Amount1; } if (PreviousHelpButton != Amount1) { PreviousHelpButton = Amount1; System.Windows.MessageBox.Show("This is the help text"); } ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; CurrentPixel.R = (byte)RandomNumber.Next(255); CurrentPixel.G = (byte)RandomNumber.Next(255); CurrentPixel.B = (byte)RandomNumber.Next(255); CurrentPixel.A = (byte)255; dst[x,y] = CurrentPixel; } } } NOTE: Don't use this code. There is published improved code here.I'm quite confused about how the Message Box works in this situation. A Message Box is a modal window, but it runs independently -- essentially modelessly -- from the main plugin dialog because it's started in a rendering thread. Yes, that is exactly the problem. One way to solve the problem is to supply the handle to the UI window to the MessageBox.Show function. That will make the Message Box modal to the specified window. Let's see if we can come up with some code that does that! I also admit I've never built a form on the fly, programmatically. I've let VS or CodeLab do that for me. I did post a tutorial for that in another thread, here: http://forums.getpaint.net/index.php?/topic/31543-controlling-the-progress-bar/?p=423664 Quote Download: BoltBait's Plugin Pack | CodeLab | and a Computer Dominos Game Link to comment Share on other sites More sharing options...
null54 Posted June 16, 2015 Share Posted June 16, 2015 Improving on BoltBait's code above, the following adds the ability to show a MessageBox (or any other dialog) as a modal window. #region UICode byte Amount1 = 0; // [255] Help byte Amount2 = 0; // [255] Randomize #endregion private sealed class ActiveFormHandle : IWin32Window { private IntPtr handle; public IntPtr Handle { get { return handle; } } private delegate IntPtr GetHandleDelegate(); internal ActiveFormHandle() { Form active = Form.ActiveForm; // As Render is called by background threads use Invoke to marshal the call to the thread that owns the Form. this.handle = (IntPtr)active.Invoke(new GetHandleDelegate(delegate() { return active.Handle; })); } } int PreviousHelpButton = -1; ActiveFormHandle activeForm = null; void Render(Surface dst, Surface src, Rectangle rect) { if (PreviousHelpButton== -1) { PreviousHelpButton = Amount1; } if (PreviousHelpButton != Amount1) { PreviousHelpButton = Amount1; if (activeForm == null) { activeForm = new ActiveFormHandle(); } System.Windows.Forms.MessageBox.Show(activeForm, "This is the help text"); } ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; CurrentPixel.R = (byte)RandomNumber.Next(255); CurrentPixel.G = (byte)RandomNumber.Next(255); CurrentPixel.B = (byte)RandomNumber.Next(255); CurrentPixel.A = (byte)255; dst[x,y] = CurrentPixel; } } } 2 Quote Plugin Pack | PSFilterPdn | Content Aware Fill | G'MIC | Paint Shop Pro Filetype | RAW Filetype | WebP Filetype The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait Link to comment Share on other sites More sharing options...
midora Posted June 16, 2015 Share Posted June 16, 2015 I appreciate your effort null54 but IMHO such code should be published in the DeveloperZone. Plus a small explanation that a delegate is needed because the effect dialog is running in a different thread than the Render code. Quote Link to comment Share on other sites More sharing options...
BoltBait Posted June 16, 2015 Share Posted June 16, 2015 I appreciate your effort null54 but IMHO such code should be published in the DeveloperZone. I asked null54 to post his code in this thread after he solved the modal problem and sent it to me privately. My original code was written as part of a CodeLab tutorial that I never finished. Perhaps, with his additional code, I'll finally finish the next installment of my CodeLab tutorial series. Quote Download: BoltBait's Plugin Pack | CodeLab | and a Computer Dominos Game Link to comment Share on other sites More sharing options...
midora Posted June 16, 2015 Share Posted June 16, 2015 I asked null54 to post his code in this thread after he solved the modal problem and sent it to me privately. My original code was written as part of a CodeLab tutorial that I never finished. Perhaps, with his additional code, I'll finally finish the next installment of my CodeLab tutorial series. Great. Still I would say that this is an issue regarding PropertyBased effects and not an issue special to CodeLab. Quote Link to comment Share on other sites More sharing options...
MJW Posted June 16, 2015 Author Share Posted June 16, 2015 (edited) Very impressive, null54! I didn't even know that could be done. I agree with midora that this whole subject deserves a separate thread, though I have no particular opinion on what area it should be under. Edited June 16, 2015 by MJW Quote Link to comment Share on other sites More sharing options...
ReMake Posted June 16, 2015 Share Posted June 16, 2015 ...I agree with midora that this whole subject deserves a separate thread, though I have no particular opinion on what area it should be under. Maybe "How it's make..." under Plugin Developer's Central. Quote Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted June 17, 2015 Share Posted June 17, 2015 Conversation split from the original thread (http://forums.getpaint.net/index.php?/topic/31843-hsv-eraser/page-2) Quote ebook: Mastering Paint.NET | resources: Plugin Index | Stereogram Tut | proud supporter of Codelab plugins: EER's Plugin Pack | Planetoid | StickMan | WhichSymbol+ | Dr Scott's Markup Renderer | CSV Filetype | dwarf horde plugins: Plugin Browser | ShapeMaker Link to comment Share on other sites More sharing options...
BoltBait Posted June 17, 2015 Share Posted June 17, 2015 Thanks, EER! Quote Download: BoltBait's Plugin Pack | CodeLab | and a Computer Dominos Game Link to comment Share on other sites More sharing options...
BoltBait Posted June 17, 2015 Share Posted June 17, 2015 You could popup a modal window that is forced on top of all other windows. That would take care of the cancel problem. Code for that is an exercise left up to the reader. Rick Brewster offered up this solution to the modal problem (Comments added by me): #region UICode byte Amount1 = 0; // [255] Help byte Amount2 = 0; // [255] Randomize #endregion int PreviousHelpButton = -1; void Render(Surface dst, Surface src, Rectangle rect) { if (PreviousHelpButton== -1) // Initial run? { PreviousHelpButton = Amount1; // Don't show help } if (PreviousHelpButton != Amount1) // Help button pressed? { PreviousHelpButton = Amount1; // Reset help button Form.ActiveForm.Invoke(new Action(delegate() { // This line runs on the UI thread and not on the Render thread System.Windows.Forms.MessageBox.Show("This is the help text"); })); } ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; CurrentPixel.R = (byte)RandomNumber.Next(255); CurrentPixel.G = (byte)RandomNumber.Next(255); CurrentPixel.B = (byte)RandomNumber.Next(255); CurrentPixel.A = (byte)255; dst[x,y] = CurrentPixel; } } }As you can see the code is MUCH simpler and works perfectly.The idea here is as an alternative to telling the MessageBox who it's parent is, force the MessageBox.Show code to run on the UI thread instead of the Render thread. This will cause the MessageBox to be modal to the UI thus preventing you from pressing the Cancel button at all. ______________ Quote Download: BoltBait's Plugin Pack | CodeLab | and a Computer Dominos Game Link to comment Share on other sites More sharing options...
BoltBait Posted June 17, 2015 Share Posted June 17, 2015 EDIT: I'm not sure of the logistics of a File option in a CodeLab plugin. Obviously it would have to ask for the file name when that option is selected, but what would launch the file selection menu? If it were launched as soon as the plugin was run, it would seem like it might be awkward to go back to the clipboard option. If there's currently a CodeLab plugin that does something like that, maybe I can take a look at it and use it as a guide. Here is the source code to Effects > Fill > From File... // Title: BoltBait's Fill From File v1.2 // Name: From File DEMO // Submenu: Fill // URL: http://boltbait.com/pdn/ // Author: BoltBait #region UICode string Amount1 = ""; // [0,255] Image file path byte Amount2 = 0; // [255] Browse Pair<double, double> Amount3 = Pair.Create(0.0, 0.0); // Placement double Amount4 = 1; // [0.01,10] Zoom #endregion protected Surface img { get { if (_img != null) { return _img; } else { GetImageFromFile(); return _img; } } } private Surface _img = null; string PreviousPath = null; int PreviousBrowseButton = -1; string imgPath = null; private void GetImageFromFile() { if (imgPath == null) { imgPath = Amount1; } Bitmap aimg = null; try { aimg = (Bitmap)Image.FromFile(imgPath, false); } catch (Exception) { } if (aimg != null) { _img = Surface.CopyFromBitmap(aimg); } else { _img = null; } } void GetFileName() { System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog(); ofd.Title = "Open Image File"; ofd.Filter = "Image Files(*.PNG;*.BMP;*.JPG;*.GIF)|*.PNG;*.BMP;*.JPG;*.GIF|All files (*.*)|*.*"; ofd.DefaultExt = ".png"; ofd.Multiselect = false; if (ofd.ShowDialog() == DialogResult.OK) { imgPath = ofd.FileName; _img = null; PreviousPath = Amount1; } } void Render(Surface dst, Surface src, Rectangle rect) { if (IsCancelRequested) return; if (PreviousBrowseButton == -1) { PreviousBrowseButton = Amount2; } if (PreviousPath != Amount1) { _img = null; imgPath = null; PreviousPath = Amount1; } if (PreviousBrowseButton != Amount2) { PreviousBrowseButton = Amount2; System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(GetFileName)); t.SetApartmentState(System.Threading.ApartmentState.STA); t.Start(); t.Join(); } for (int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { if (IsCancelRequested) return; // we need to check this in the x loop to avoid lots of try/catches which are soooo slow if (img == null) { dst[x, y] = src[x, y]; } else { float px = (float)(((Amount3.First + 1) / 2 * img.Height) + x) * (float)Amount4; float py = (float)(((Amount3.Second + 1) / 2 * img.Width) + y) * (float)Amount4; dst[x, y] = img.GetBilinearSampleWrapped(px, py); } } } }This is an example of how to use Window's built-in File Open dialog box.This code suffers from the same "modal" problem as the original help button demo. It needs to be fixed before being used! Quote Download: BoltBait's Plugin Pack | CodeLab | and a Computer Dominos Game Link to comment Share on other sites More sharing options...
midora Posted June 17, 2015 Share Posted June 17, 2015 I'm using this HelpButton trick in several basic effects for a while now (in most cases just to open a link in the browser). Later I added it to FileTypes OnSave too but there is an additional issue I couldn't find a clean solution for. Maybe someone has an idea. The problem is that the user may press the 'Defaults' button, which resets the button to its initial value. This may be an issue too if Rick adds a global reset button to property based effects in the future. Quote Link to comment Share on other sites More sharing options...
MJW Posted June 17, 2015 Author Share Posted June 17, 2015 (edited) For your possible amusement: // Author: MJW // Author: MJW // Name: Test Help Button // Title: Test Help Button // Desc: Try to add a help button. #region UICode byte Amount1 = 0; // [255] Randomize #endregion bool hasHelpButton = false; Form form = null; void Render(Surface dst, Surface src, Rectangle rect) { if (!hasHelpButton) // Help button displayed? { hasHelpButton = true; form = Form.ActiveForm; if (form.Name == "EffectConfigDialog") { form.Invoke(new Action(delegate() { if (!form.HelpButton) { form.HelpButton = true; form.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(HelpButtonClicked); } })); } } ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; CurrentPixel.R = (byte)RandomNumber.Next(255); CurrentPixel.G = (byte)RandomNumber.Next(255); CurrentPixel.B = (byte)RandomNumber.Next(255); CurrentPixel.A = (byte)255; dst[x,y] = CurrentPixel; } } } public void HelpButtonClicked(Object sender, System.ComponentModel.CancelEventArgs e) { e.Cancel = true; System.Windows.Forms.MessageBox.Show("This is the help text"); } EDIT: Originally, I used an Invoke in the the event handler, but then I realized it would probably run under the parent form's thread, which testing confirmed.EDIT: Added a just-in-case check to make sure HelpButton is currently false before setting it and adding event handler. (Thanks for the very nice screen shots, BoltBait!) ______________ Edited June 17, 2015 by MJW I added some screenshots to your post. 1 Quote Link to comment Share on other sites More sharing options...
midora Posted June 17, 2015 Share Posted June 17, 2015 Clever MJW. It does not fit to the original intention of this help button (which switches the mouse cursor and let you get info for individual controls) but who cares ;-) Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.