Adding gadget to window in Python riscos_toolbox
Matthew Phillips (473) 721 posts |
I am struggling to work out how to add a gadget to a Toolbox window using the riscos-toolbox Python library. Just to be clear: the Window was created using ResEd and I’ve managed to open it fine, but to support the functionality I want, the gadget needs to be added by the application, and not by including it in the Res file. (It doesn’t help that I am not very familiar with Toolbox development anyway, or that I am trying to add the gadget to a custom SaveAs box.) I have a Res file containing “SaveCSV” derived from the SaveAs prototype. It has the “Use alternative window” option ticked. The Window is saved in the same Res file and is called “SaveCSV2”. To create and open the Save As dialogue I have the following code as part of the !RunImage: class SaveCSV(SaveAs): template = "SaveCSV" def __init__(self, *args): super().__init__(*args) This is called during the initialisation of the application: class TestApp(Application): def __init__(self): super().__init__('<TestApp$Dir>') self.saveBox = toolbox.create_object(SaveCSV.template, SaveCSV) And later, in response to receiving a message, the application opens the box: # Do ctypes gymnastics to get start address of data as int. dataptr = ctypes.c_char_p(station.csv) addr = ctypes.cast(dataptr, ctypes.c_void_p).value # Set SaveAs box data address, so saving can be done by Toolbox. self.saveBox.set_data_address(addr, len(station.csv), 0, 0) self.saveBox.file_type = 0xDFE self.saveBox.file_name = station.name self.saveBox.show() This all works. But I want to add an extra gadget to the window before opening it. I see there is an add_gadget method in the Window code, but I imagine I have to create the gadget using one of the gadget classes. As I actually want to add it to the custom SaveAs window, I reckoned I needed to create that first, and then attach the gadget to that, but thinking about it, perhaps the standard SaveAs code will have created a different instance? I tried this: 1. A class for the underlying custom window: class SaveCSVWindow(Window): template = "SaveCSV2" def __init__(self, *args): super().__init__(*args) 2. Create it in the application initialisation: self.saveWin = toolbox.create_object(SaveCSVWindow.template, SaveCSVWindow) 3. Attempt to add the gadget: # Do ctypes gymnastics to get start address of data as int. dataptr = ctypes.c_char_p(station.csv) addr = ctypes.cast(dataptr, ctypes.c_void_p).value # Create an option button gadget gadget = OptionButton(self.saveWin, 0x555) #gadget = OptionButton() #gadget.label = station.name #gadget.state = True self.saveWin.add_gadget(gadget) # Set SaveAs box data address, so saving can be done by Toolbox. self.saveBox.set_data_address(addr, len(station.csv), 0, 0) self.saveBox.file_type = 0xDFE self.saveBox.file_name = station.name self.saveBox.show() I’ve not even thought about how to control where the new gadget is to be positioned — that was going to be the next step after creating it! Having written all that, I think it would be easier if I popped all the code up on GitHub to be dissected! I’ll be back shortly… |
Matthew Phillips (473) 721 posts |
OK, code all here: https://github.com/mephillips-durham/WaterLevels/tree/main Actually, this will be rather hard for anyone to play with, as it’s an add-on for RiscOSM. I think perhaps I had better develop a simpler example and put that up. If you do want to try it with RiscOSM, you will need to:
A number of water level measuring stations should appear on the map. Ctrl-click on one of the points and you should then get an error. And by the way, the application will leave a couple of files in the root of your hard drive for diagnostics: “api-response” and “sensor-response/json”. (Did I say it was nowhere near ready for release?) |
Matthew Phillips (473) 721 posts |
OK. Here is a much simpler example which illustrates the problem. It only requires the Python riscos-toolbox library installed and no need for RiscOSM etc. It has debugging via Reporter but you can easily get rid of the “report” calls in the !RunImage. If you run the app it sits on the iconbar, but if you click on the icon you get “invalid type” error coming from the line which is calling add_gadget. Comment out the lines indicated and the window opens fine. Would be very glad of any help on this. |
Fred Graute (114) 645 posts |
Only had a quick look at this and my Python knowledge has pretty much eroded away entirely. Anyway… To add a gadget you need to pass a memory block with a template (for gadget type you wish to add) to Window_AddGadget. You seem to be passing a Python instance of the gadget so unless the Python toolbox library converts that to a template that’s not correct. As an exercise, I’d suggest adding the gadget in the res file. Before opening the window, use Window_ExtractGadgetInfo to get the gadget’s template into a memory block then remove the gadget. On the next opening of the window call Window_AddGadget with that memory block. All being well, this should put the gadget back in the same position. Hopefully this works and provides you with a useful starting point. |
Paolo Fabio Zaino (28) 1882 posts |
+1, I’d do what Fred suggests too, given it makes it easier to place the gadget and play with its options too. Just my 0.2c |
Matthew Phillips (473) 721 posts |
That’s a good idea. I think I probably need to be using the add_gadget call first to create the gadget in the window, and then probably creating the Python object which will allow easy access to its methods, so starting from an existing gadget in the Res file is probably the way to go. |