On Sun, Feb 28, 2016 at 11:50 PM, Steven D'Aprano <st...@pearwood.info> wrote: > On Sun, 28 Feb 2016 07:44 pm, Chris Angelico wrote: > >> On Sun, Feb 28, 2016 at 5:34 PM, Steven D'Aprano <st...@pearwood.info> >> wrote: > [...] >>> Drag-and-drop GUI builders have the same advantages over code as Python >>> has over languages with distinct compile/execute steps: rapid >>> development, prototyping, exploration and discovery. Of course, any >>> decent modern builder won't limit you to literally drag-and-drop, but >>> will offer functionality like duplicating elements, aligning them, >>> magnetic guides, etc. >> >> Alright, but how do you go about doing, with a drag-and-drop builder, >> all those things we're used to with code - composing new named actions >> out of primitives, observing the changes to a program through source >> control, unit testing (maybe), and code review? > > These are all good questions. Let's see if I can give good answers: > > (1) "composing new named actions" -- I'm not entirely sure what you mean. Do > you mean new named *widgets*? A good builder app should give you the > ability to Group widgets into a single element, this is functionality which > has existed in vector-drawing programs since at least MacDraw in 1984 so it > shouldn't be hard. This is composition, a fundamental, powerful and rich > design pattern for making new widgets (classes) out of simpler parts. If > objects have a name, now you can refer to CompositeMenuDateColourPicker by > name. You can copy it, paste it, replicate it 30 times, whatever you like.
A good number of GUI builders do offer this functionality - composition. (Even some of what we would call primitives are actually composed of multiple widgets; a drop-down combo box is an entry field, a button, and a possibly-hidden list box.) But there are other actions than "put this widget here". For example, you could go and adjust some widget's size or style, or reposition it according to some new rules (hopefully most of your positioning rules can be codified, eg "use a grid layout, put this in this cell", but a lot of the people who ask for drag-and-drop GUI building are thinking in terms of "place this right here", rather than using layout managers; and sometimes there are rules that don't really fit the layout manager per se, or are layered on top of the layout manager's rules). These kinds of actions can be represented as functions and then applied everywhere, such that you can change the precise appearance by editing that one function. For example, I have a Dungeons & Dragons character sheet display, in which there are large numbers of entry fields (editable), labels (non-editable display, usually calculated from other fields), and less commonly, drop-down lists and multi-line text fields. Call that four primitives. Now, some of the fields need to be highlighted for the human's attention ("this is the actual value you want to be reading, most of the time"). Currently, I do this with a blue border around it and its label. Okay, so I can no doubt create a "readme display" widget that has the border (a GTK2 Frame) and two labels; and then a "readme editable" with the border, one label, and one entry field. That's already split it out into two options, where a parameterized function could simply generate a border, a label, and *whatever object it was given*. And what if I want to change the look of those readme objects? What if, instead of surrounding them with a Frame, I want to put a little icon to the right of them? With code, all I have to do is change the definition of the function; it still takes a widget and returns a widget, but now it returns (say) a horizontal box containing a label, the provided widget, and the icon. With a GUI builder, how do you redefine a function that isn't just simple composition? This additional meta-level is one that is absent from a *lot* of modern graphical environments. Look at spreadsheets - the classic Lotus 1-2-3 model has stayed with us all through, with MS Excel, OO/LO Calc, etc, etc generally following it. And that's fine; spreadsheets do a lot of very good work for a lot of people. Now, suppose in cell C2 you have this formula: "=A2+B2*.05". You can copy that down the rest of the column, and C9 will say "=A9+B9*.05", and so on; that's what spreadsheets do well. But once you've done that copy operation, those cells lose all track of the fact that they got their formula from C2. If you edit C2, you have to re-copy-down. That's not too hard for a lot of people to remember, but what happens when that's forgotten? In a spreadsheet with huge numbers of calculated cells, how are you going to debug the "oops I forgot to re-copy that" problem? There is nothing in the document itself that helps you; if you see that C2 has "=A2+B2*.051" and that this pattern continues down as far as C10 but no further, do you assume that someone was tinkering and didn't edit all down the column (which is tedious), or do you assume that it's intentional? (Of course, the tendency for spreadsheets to lack comments is a separate issue.) There is one spreadsheet (that I know of) that gives this extra meta-level: Mesa for OS/2. If you copy down like that, the cells will say "SAME(C2)" instead of actually copying anything. Then, if you change C2, it'll use that formula for all the others. Why is this a rare feature in spreadsheets? It's pretty obvious that you can do this in code - you simply call that function. In anything drag-and-drop, it has to be an explicitly provided feature. > (2) "source control" -- the world is full of document types that aren't > plain text source code, and people have found ways to manage collaborative > editing and change management for them. Why don't we ask game developers > how they manage changes to the non-code elements in their applications? > Textures, icons, player avatars, sound effects, maps, etc. Surely you don't > suggest that game developers edit their background images in a hex editor? Yes, and while you're at it, ask the critical question: What happens when two people make edits to the same object? Non-text source code is fine as long as only one person at a time is ever editing. Source control will happily track them. But merging is virtually impossible; taking one base case and two modified files and producing a unified result is hard for either a human or a machine, when the files are non-text blobs. My suspicion is that such game devs would have strict rules about simultaneous editing of files. While that quite probably works fairly well, it's a limitation that most of us would not accept in any code project. Imagine if the Python bug tracker required you to send, not a patch file, but the entire source file that has the edit you want. How would you manage those sorts of edits? > (3) "unit testing" -- I'm not sure that unit testing is what is needed for > the GUI elements of your application. It's more like integration testing, > to ensure that the entire application works together as a seamless whole. > I'm not sure what the state of the art in GUI application integration > testing is like. I suspect crap, judging by the state of the art in GUI > applications. > > But whatever it is, it will surely operate the same regardless of whether > you have built the application using code or a graphical builder. > > (The GUI framework itself may have some analogue to unit testing for the > individual widgets, but that's not your responsibility as the user of the > framework. It's not up to you to test that menus drop down when clicked, > but it is your responsibility to check that the right menus exist and that > they do what they are supposed to.) Right. Testing the widgets isn't your responsibility, although testing a composite widget might (imagine building a drop-down combo box out of the three parts - you should be able to test that in isolation). But I was stretching a bit here. Feel free to drop this one. > (4) "code review" -- the usual way to review the graphical elements of a GUI > app is to call somebody over, sit them down at the running application, and > say "What do you think of this?". They will usually answer "that icon needs > to be a bit more to the left, and can you make that button blue instead of > green?". That's if you review the entire app. I'm talking more about the equivalent of sharing patches; "here, I changed this window, do you accept my change or not?" is a hard question to ask if you can't see exactly what changed. Closely related to the source control issue - if you can make patches/diffs for one, you can do it for the other. >> The only way I know of >> to build a "function" in a DnD builder is to create a composite widget >> (eg "horizontal box containing label and entry field"), which is >> extremely useful, but limited - it's like saying that the only way to >> reuse code is single-inheritance. > > A poor analogy. Composition is equivalent to multiple inheritance, except > without any of the weaknesses of MI. The analogy is of having a grossly-restricted form of code reuse as your only form. Composition is one specific option, but that's not the only thing you can do with code. >> How would you create a higher-order >> operation in a DnD builder? How would you write something that does >> some sort of sweep over a set of widgets and does the same thing to >> them? > > In Hypercard, if it was a once-off processing task, I would create a button, > edit the button's script: > > on mouseUp: > -- my memory of HC syntax and functions is a bit rusty > -- this may not be correct > for btnNum in 1 to the number of buttons: > if btnNum is the number of me: > continue > end if > set the textsize of button btnNum to 9 > set the textstyle of button btnNum to bold,italic > if the name of button btnNum starts with "Spam": > set the icon of button btnNum to SpamIcon > end if > end for > end mouseUp > > then I would click on that button and run the script. Then, once I have > satisfied myself that it has done what was needed, I'd delete the button. This is like running a sed script over your code. That's still not the same thing as higher-order code. > If this was something that needed to run each time the application ran, I > would put the script in some other layer of the application, say, in the > card layer, in a named handler: > > on setBtnStyle: > for btnNum in 1 to the number of buttons: > set the textsize of button btnNum to 9 > set the textstyle of button btnNum to bold,italic > if the name of button btnNum starts with "Spam": > set the icon of button btnNum to SpamIcon > end if > end for > end setBtnStyle > > > then call that handler on some event: > > on openCard: > setBtnStyle > end openCard I suppose that's a reasonable simulation of higher-order code. If it's stuff you're happy to do at run time (ie it won't hurt that you can't see it in the editor), this will work. It's still not as good IMO though. >> This doesn't have to be a dichotomy. > > I didn't say it did :-) Good :) ChrisA -- https://mail.python.org/mailman/listinfo/python-list