On Fri, Jan 24, 2014 at 11:49 PM, Frank Millman <fr...@chagford.com> wrote: > > "Chris Angelico" <ros...@gmail.com> wrote in message > news:captjjmo+euy439wb0c8or+zacyenr844hakwl3i2+55dde8...@mail.gmail.com... >> On Fri, Jan 24, 2014 at 8:21 PM, Frank Millman <fr...@chagford.com> wrote: >>> I find that I am using JSON and XML more and more in my project, so I >>> thought I would explain what I am doing to see if others think this is an >>> acceptable approach or if I have taken a wrong turn. >> >> Please don't take this the wrong way, but the uses of JSON and XML >> that you describe are still completely inappropriate. Python does not >> need this sort of thing. >> > > Chris, I really am very grateful for your detailed response. There is much > food for thought there and I will have to read it a few times to glean as > much as possible from it.
Thanks for taking it the right way :) If I thought you were doing a terrible job, I'd just archive the thread and move on. The detailed response means I think you're very close to the mark, but could improve. :) > Here are some initial thoughts. > > 1. I don't really understand how your system actually works! You mention > Python code and helper functions, but I cannot picture the overall structure > of the program. I don't know if it is possible to provide a siimple example, > but it would certainly help. Yeah, it was a bit vague. Unfortunately I haven't actually built any complex UI in Python, ever, so I'll have to use C, C++, REXX, or Pike, for the example. In C, it would be using the OS/2 APIs, not very useful to anyone else. In C++, either Borland's ancient "Object Windows Library", or the Win32 APIs - either way, not particularly helpful. REXX you probably don't even know as a language, and you're unlikely to know any of the GUI libraries I've used - GpfRexx, VREXX, VX-REXX, VisPro REXX, DrRexx, or a handful of others. So the best bet is Pike, where I've used GTK, which is also available for Python - but there are distinct differences. (Oh, I've also used DBExpert, which is a superb example of how frustrating it can be to try to create a complex GUI in a "look how easy, it's just drag and drop, no code needed" system. But that's more arcane stuff from the 1990s that you most likely don't know, and definitely don't need to know.) So, here's some code from Gypsum. Firstly, here's a simple window layout (not strictly what I had in Gypsum): mainwindow = GTK2.Window(0)->add(GTK2.Vbox(0,0) ->add(GTK2.Label("About Gypsum: big long multi-line string")) ->add(GTK2.HbuttonBox() ->add(GTK2.Button("Close")) ->add(GTK2.Button("Foobar")) ) ); The window has a simple structure. It contains a vertical box, which contains a label and a horizontal button box; the latter contains two buttons. (The real about dialog has only one button, making it a poor example. I've no idea what clicking Foobar on an about dialog should do.) There's a nested structure to it, and it's all code. (It's actually all one single expression. The add() methods return the parent object, so they chain nicely.) This gives maximum flexibility, but it gets repetitive at times, especially when I do up the character sheet. Here's a screenshot of just the first page: http://rosuav.com/charsheet.png (That's how it appears on Windows, since I'm currently testing it for support there. It also has to work - and look right - on Linux and Mac OS.) It's particularly complicated when laying out a table, because the table is so powerful and flexible. GTK2.Table(6,2,0) ->attach(GTK2.Label((["label":"Keyword","xalign":1.0])),0,1,0,1,GTK2.Fill,GTK2.Fill,5,0) ->attach_defaults(win->kwd=GTK2.Entry(),1,2,0,1) ->attach(GTK2.Label((["label":"Name","xalign":1.0])),0,1,1,2,GTK2.Fill,GTK2.Fill,5,0) ->attach_defaults(win->name=GTK2.Entry(),1,2,1,2) ->attach(GTK2.Label((["label":"Host name","xalign":1.0])),0,1,2,3,GTK2.Fill,GTK2.Fill,5,0) ->attach_defaults(win->hostname=GTK2.Entry(),1,2,2,3) ->attach(GTK2.Label((["label":"Port","xalign":1.0])),0,1,3,4,GTK2.Fill,GTK2.Fill,5,0) ->attach_defaults(win->port=GTK2.Entry(),1,2,3,4) ->attach(GTK2.Label((["label":"Auto-log","xalign":1.0])),0,1,4,5,GTK2.Fill,GTK2.Fill,5,0) ->attach_defaults(win->logfile=GTK2.Entry(),1,2,4,5) ->attach(win->use_ka=GTK2.CheckButton("Use keep-alive"),1,2,5,6,GTK2.Fill,GTK2.Fill,5,0) //No separate label Horribly repetitive, and requires care to ensure that the left/right/top/bottom boundaries are correct in all cases. Technically it's possible for widgets to span rows or columns, and even to overlap, but that's extremely rare compared to the common case of just "lay these things out in a grid". (Though spanning columns was easy enough to implement, so I did it.) Here's the version with the first helper function: GTK2Table(({ ({"Keyword",win->kwd=GTK2.Entry()}), ({"Name",win->name=GTK2.Entry()}), ({"Host name",win->hostname=GTK2.Entry()}), ({"Port",win->port=GTK2.Entry()}), ({"Auto-log",win->logfile=GTK2.Entry()}), ({"",win->use_ka=GTK2.CheckButton("Use keep-alive")}), }),(["xalign":1.0])) I have to create a blank label in there to keep the positioning right (the previous version simply didn't attach anything in that slot), but that's a small price to pay. The readability has gone up hugely. And yet, there's still a little more that can be done, as I realized when I found myself duplicating the above structure a few times. two_column(({ "Keyword",win->kwd=GTK2.Entry(), "Name",win->name=GTK2.Entry(), "Host name",win->hostname=GTK2.Entry(), "Port",win->port=GTK2.Entry(), "Auto-log",win->logfile=GTK2.Entry(), "",win->use_ka=GTK2.CheckButton("Use keep-alive"), })) This is something that I could give to a non-programmer and expect him to be able to edit, as it's about as clear as the equivalent JSON or XML would be; there could be a few more tweaks done, perhaps, but it's pretty readable like that. A non-programmer could, for instance, put the "Auto-log" line above the "Host name", simply by reordering the lines of code; it would do exactly what he expects. There's as much fragility/rigidity in this as there is in any other structured system (eg if you leave out a comma, this won't work - same with JSON or XML). In the specific case of the character sheet, I frequently create entry fields (GTK2.Entry()) that are bound to specific data attributes. So in any context where it's legal to write GTK2.Entry(), you can instead write ef("some_name") and it'll make a properly bound entry field. And if it's a numeric field, num("some_name") will shrink the default size and center the text in it as well, which is what people expect of the numeric fields. Shorthand for the common cases. And then there are complex cases like the weapon stats block - it needs its keyword, name, damage roll, enchantment bonus, etc, etc, all laid out nicely; and there might be three of those weapon blocks. So that's broken out into a function. The thing is, if your XML window structure has enough power... well, that's more part of the next two points: > 2. I am not sure if you have created this to make it easy for *you* to > create forms, or for others. And if the latter, must they be programmers? > One of my goals is to allow non-programmers to modify forms and even create > them from scratch. I did mention that one benefit of my approach is that I > can design a gui that allows this, but I don't get the sense that your's > does. > > 3. Thanks for the links to the Inner Platform Effect. I had not heard of it, > but I recognise the symptoms, and I realise that I am skirting close to it > in some areas. If your XML window structure has enough power, it'll be nearly impossible for a non-programmer to make anything but the most trivial changes to it. (Changing a literal string is usually easy enough. Any decent system will make _that_ possible, at least.) If a non-programmer can truly create a form from scratch, that's a sign that the forms are extremely simple and, if you'll excuse the pun, formulaic; and if that's the case, it should be possible to make a really easy-to-use helper function like my two_column() above. Challenge: Without knowing anything about Pike, or the connect dialog, add another check box called "Passive mode" called win->passive and positioned just after the hostname and port. Like the other check box, it doesn't need a separate label. Can you do it? Could someone who knows enough to edit XML do it? I suspect probably they could. There's some boilerplate to copy and paste (same in XML or code), there's some readable text strings (names, labels, types), and there's punctuation that makes decent sense. > 4. You are right that some things cannot be totally abstracted, and can only > be handled by code. My approach is as follows. If at least 80% of the > structure can be handled without resorting to code, I think it is > worthwhile, and I think I have more than achieved that. Of the balance, if > at least 80% of requirements are 'standard', I can write the standard > functions in Python, and create an XML tag that will cause the function to > be invoked at run-time. The 'designer' still does not have to worry about > Python. That's nice in theory, but it means you have to plan everything out. That also means you have to predict your names etc, and then deprecate old names if you ever find that you made a mistake. When you start with code, you have the flexibility of looking at actual existing forms and deciding what should be broken out for simplicity. > For the remainder, I have created an XML tag called 'pyfunc' that > provides a path to a custom-written function. For this, a Python programmer > will be required. So far I have only made use of this in my 'form designer', > which is quite complex as it has to take the XML definition and 'explode' it > into a number of in-memory tables so that it can be presented in gui form. > However, the average user is not going to get their hands that dirty. That's what I meant when I said a "drop to code" system. Depending on how smooth the boundary is, that could be either horribly clunky or reasonably clean; the clean one usually requires a lot of work. On the flip side, if your GUI creation is *all* code, there's no boundary at all, and no work. My system starts by writing an absolute minimum of code and forcing the form designer to do more work, and then progressively adds code *that is actually useful* and makes the form design work easier. Trying to do it all in XML means you have to first predict what will be needed, and that inevitably means writing a whole lot of code that isn't, as it turns out, useful. That's an up-front cost before you can do anything, plus you can't get rid of any of it later without first making sure no forms are using those tags. Ultimately, a full-featured GUI is going to require that its form designer understand the underlying system anyway (in my case that's GTK), because nothing will change that; and if your forms designers have to understand how GTK works, they probably have to understand how to write GTK-using code. At very least, the basics of code (names, structure, etc) will be critical, and that's true of any GUI builder. DBExpert required that you think about all that sort of thing, and that's as close as I've ever seen to a pure "no code needed, build a UI to view a database table" system. So... instead of fighting that, embrace it! Let it truly be code. The structure (your XML interpreter) is code that has a maintenance cost. The less of that you have, the less it takes to make changes. And as a rule of thumb, less code means less bugs; and less development prior to usage means less bugs, too. Let real-world usage guide your code, and you'll spend less time building stuff you don't need. ChrisA -- https://mail.python.org/mailman/listinfo/python-list