Building reusable components with gtk glade - python

I am writing a simple application and am using glade (gtk) for the UI. I need many windows (~10), of which one will open depending upon the command line flags, other contextual stuff etc.
Now, all these windows are pretty much similar, they have 3 top level tabs, the last tab is the same in all, all have a OK and Quit button etc., so I am looking for a way to build these windows in glade. I could copy paste one window and make the changes in that, but I am looking for a better way, that will allow me to reuse the common parts of the windows.
Also, I am using pygtk for loading up the windows.

Design a widget with the common aspects you mention. Wherever you need to implement something different, put a GtkAlignment with an appropriate name. Don't forget to change the alignment and fill values of the GtkAlignment.
In PyGTK you can gtk.Builder.get_object(name) to get access to these empty regions and add the extra components within them (which can also be designed with Glade).

Ok, with the help of detly's answer, I am able to get something working. For anyone who needs it, here is what I did.
main.glade contains the window and all the common cruft that I need to be displayed in all windows. comp.glade contains a window, with a vbox component with the extra stuff I need, lets call it 'top_comp'.
Now, in main.glade, I put a gtk.Alignment component in the place where I need the extra component to load, and call it, say, 'comp_holder'. With the builder I have, I do
builder = gtk.Builder()
builder.add_from_file('main.glade'))
builder.add_from_file('comp.glade'))
builder.get_object('top_comp').reparent(builder.get_object('comp_holder'))
This method seems to work for now, but I don't know if it is the correct way to do this thing.
Any suggestions for the above welcome.

Related

WxPython Choice Field Invisible just on Mac

I am developing a cross platform app in Python using wxPython. The app is fully developed, and the graphics toolkit is set in stone, at least for the time being.
On Windows, everything looks great. On Linux, everything looks pretty good. On Mac, I am having trouble with a combobox/choice being hidden in the toolbar, even though it shows up fine on Windows.
Here is a snapshot of my app on Linux, noting the entire "CoeffConv ..." section is part of the perfectly displayed combobox:
And here is a snapshot of the same exact codebase on Mac:
I've tried with wx.ComboBox and wx.Choice with the same effect. I've made sure to call Realize() after I've added my toolbar items. I've made sure AddControl is called to actually add the object to the toolbar. It's definitely trying to render because the spacing is exactly what I would expect, given the contents of the choice field.
In fact, if I don't call AddControl, but I create the choice field with the toolbar as the parent, the box gets rendered but things aren't arranged properly due to the missing AddControl call:
As another check, I created a super simple toolbar with choice demo, and it works just fine:
So, here's the summary of things I know:
On Windows, the choice field works perfect, indicating the code isn't necessarily wrong.
On Linux, the choice field works perfect, supporting the idea that the code is actually OK.
On Mac, the choice field is present it seems, but somehow invisible, implying this is a problem with the Mac, or the Python distribution on Mac, or the combobox control in the wxPython distribution on Mac.
On Mac, I can get the choice to render (improperly but still) without an AddControl call, indicating the combobox can render properly, but something is goofy about the placement when added to the toolbar
However, On Mac, I can get a toolbar/choice to render totally fine in a dummy example, indicating it's something about my implementation...but I can't figure out what would cause it as I'm trying to make the exact same calls to the wx objects as in the dummy example.
I can't get the dummy example to reproduce the problem, but I'll keep trying. I'm happy to report out some object properties if they would be helpful in diagnosing. If someone has a clue for what could cause it to not show up, I'd really appreciate it!
While I couldn't find the root as to why it didn't work on Mac, I did find an issue that could help others.
First a little more background. In the app, we have multiple toolbars. Because of that, we are creating toolbars using plain wx.ToolBar objects and adding them to the app frame using sizers. This is in contrast to the more standard method of using self.CreateToolBar() which only allows a frame to have a single toolbar.
When I make a single change to the code to use the more natural CreateToolBar interface, the combobox immediately shows up. When I do that, the second toolbar is messed up, but at least the initial toolbar works perfectly. I'll be investigating how to get multiple toolbars on Mac, but it's a step in the right direction.
It's been a while, but there's another solution to this for wxPython 4 if you can't use CreateToolBar():
The widgets do get added to the bar and reserve the correct amount of space, but they fail to draw properly. You can resolve this by calling control.Hide() followed by control.Show() on each widget control, which then makes them draw properly.

PyQT Qtabwidget add, remove, hide, show certain tab

I am trying to build a GUI which will:
Load a file with parameters which describe certain type of problem.
Based on the parameters of the file, show only certain tab in QTabwidget (of many predefined in Qt Designer .ui)
I plan to make a QTabwidget with, say 10 tabs, but only one should be visible based on the parameters loaded. Enabling certain tab is not an option since it takes to many space and the disabled tabs are grey. I do not want to see disabled tabs.
Removing tab could be an option but the index is not related to a specific tab so I have to take care of the shift in the indices. And furthermore if user loads another file with different parameters, a good tab should be added and the current one removed.
My questions are:
How to do this effectively?
Is it better to use any other type of widget?
In Qt designer, is it possible to define many widgets one over another and then just push the good one in front. If yes, how? And how to edit and change any of them?
If using RemoveTab, how to use pointers on tabs, rather than indices?
I use PyQt4
Use a QStackedWidget, which is exactly the same as a tab-widget, but without the tab-bar (which you don't need).
This widget is available in Qt Designer. The context menu has several commands for adding/removing pages and so forth. Note that the arrow buttons in the top-right corner are just there for convenience: they won't appear in your application.
Pages can be added/removed at runtime with addWidget/removeWidget:
index = self.stack.addWidget(self.page1)
self.stack.removeWidget(self.page1)
You can access the pages using either indexes or widget references.
I see that this thread is kinda old. But I hope this will still help.
You can use the remove() method to "hide" the tab. There's no way to really hide them in pyqt4. when you remove it, it's gone from the ui. But in the back end, the tab object with all your settings still exist. I'm sure you can find a way to improvise it back. Give it a try!

In wxPython, What is the Standard Process of Making an Application Slightly More Complex Than a Wizard?

I am attempting to create my first OS-level GUI using wxPython. I have the book wxPython in Action and have looked at the code demos. I have no experience with event-driven programming (aside from some Javascript), sizers, and all of the typical GUI elements. The book is organized a little strangely and assumes I know far more about OS GUI programming than I actually do. I'm fairly recent to object-oriented programming, as well. I'm aware that I am clearly out of my depth.
My application, on the GUI side, is simple: mostly a set of reminder screens ("Turn on the scanner," "Turn on the printer," etc) and background actions in Python either in the filesystem or from hitting a web service, but it is just complex enough that the Wizard class does not quite seem to cover it. I have to change the names on the "Back" and "Next" buttons, disable them at times, and so forth.
What is the standard process for an application such as mine?
1) Create a single wxFrame, then put all of my wxPanels inside of it, hiding all but one, then performing a sequence of hides and shows as the "Next" button (or the current equivalent) are triggered?
2) Create multiple wxFrames, with one wxPanel in each, then switch between them?
3) Some non-obvious fashion of changing the names of the buttons in wxWizard and disabling them?
4) Something I have not anticipated in the three categories above.
I don't have a good understanding of your application, but trying to force wxWizard to suit your needs sounds like a bad idea.
I suggest checking out the Demos available from the wxPython website. Go through each demo and I bet you'll find one that suits your needs.
I've personally never used wxWizard as I find it too cumbersome. Instead, I create a sequence of dialogs that do what I need.

How to make PowerBuilder UI testing application?

I'm not familiar with PowerBuilder but I have a task to create Automatic UI Test Application for PB. We've decided to do it in Python with pywinauto and iaccesible libraries. The problem is that some UI elements like newly added lists record can not be accesed from it (even inspect32 can't get it).
Any ideas how to reach this elements and make them testable?
I'm experimenting with code for a tool for automating PowerBuilder-based GUIs as well. From what I can see, your best bet would be to use the PowerBuilder Native Interface (PBNI), and call PowerScript code from within your NVO.
If you like, feel free to send me an email (see my profile for my email address), I'd be interested in exchanging ideas about how to do this.
I didn't use PowerBuilder for a while but I guess that the problem that you are trying to solve is similar to the one I am trying to address for people making projects with SCADA systems like Wonderware Intouch.
The problem with such an application is that there is no API to get or set the value of a control. So a pywinauto approach can't work.
I've made a small tool to simulate the user events and to get the results from a screencapture. I am usig PIL and pytesser ORM for the analysis of the screen captures. It is not the easiest way but it works OK.
The tool is open-source and free of charge and can be downloaded from my website (Sorry in french). You just need an account but it's free as well. Just ask.
If you can read french, here is one article about testing Intouch-based applications
Sorry for the self promotion, but I was facing a similar problem with no solution so I've written my own. Anyway, that's free and open-source...
I've seen in AutomatedQa support that they a recipe recommending using msaa and setting some properties on the controls. I do not know if it works.
If you are testing DataWindows (the class is pbdwxxx, e.g. pbdw110) you will have to use a combination of clicking at specific coordinates and sending Tab keys to get to the control you want. Of course you can also send up and down arrow keys to move among rows. The easiest thing to do is to start with a normal control like an SLE and tab into the DataWindow. The problem is that the DataWindow is essentially just an image. There is no control for a given field until you move the focus there by clicking or tabbing. I've also found that the DataWindow's iAccessible interface is a bit strange. If you ask the DataWindow for the object with focus, you don't get the right answer. If you enumerate through all of the children you can find the one that has focus. If you can modify the source I also advise that you set AccessibleName for your DataWindow controls, otherwise you probably won't be able to identify the controls except by position (by DataWindow controls I mean the ones inside the DataWindow, not the DataWindow itself). If it's an MDI application, you may also find it useful to locate the MicroHelp window (class fnhelpxxx, e.g. fnhelp110, find from the main application window) to help determine your current context.
Edited to add:
Sikuli looks very promising for testing PowerBuilder. It works by recognizing objects on the screen from a saved fragment of screenshot. That is, you take a screenshot of the part of the screen you want it to find.

How to handle a glade project with many windows

I'm working on a PyGTK/glade application that currently has 16 windows/dialogs and is about 130KB, and will eventually have around 25 windows/dialogs and be around 200KB. Currently, I'm storing all the windows in one monolithic glade file. When I run a window I call it like...
self.wTree = gtk.glade.XML("interface.glade", "WindowXYZ")
I wonder if it would be a better idea to split each window into it's own glade file. Instead of one glade file with 25 windows/dialogs I'd have 25 glade files with one window/dialog each and call it like so:
self.wTree = gtk.glade.XML("windowxyz.glade")
What do you guys think is the best way to do this? Is one method more resource intensive than another? One thing that would be nice about going to individual glade files is that naming widgets would be easier. For example, I name all my OK buttons "windowxyz_ok", but I could change it to simply "ok" instead. Makes things simpler. The downside is that it may be a bit less convenient to make changes to different windows.
I'm open to any and all arguments. Thanks!
In my projects, I always have one window per glade file. I'd recommend the same for your project.
The following are the two main reasons:
It will be faster and use less memory, since each call to gtk.glade.XML() parses the whole thing. Sure you can pass in the root argument to avoid creating the widget tree for all windows, but you'd still have to parse all the XML, even if you're not interested in it.
Conceptually its easier to understand if have one toplevel per window. You easily know which filename a given dialog/window is in just by looking at the filename.
Did you take some timings to find out whether it makes a difference?
The problem is that, as far as I understand it, Glade always creates all widgets when it parses an XML file, so if you open the XML file and only read a single widget, you are wasting a lot of resources.
The other problem is that you need to re-read the file if you want to have another instance of that widget.
The way I did it before was to put all widgets that were created only once (like the about window, the main window etc) into one glade file, and separate glade files for widgets that needed to be created several times.
I use different glade files for different windows. But I keep dialog associated with a window in the same glade file. As you said, the naming problem is annoying.
I have one glade file with 2 windows. It's about 450kb in size and I have not seen any slowdowns using libglademm with GTKmm.

Categories