Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Crossword/Codeword grid query
-
So, the number selection grid is supposed to popup, you do something, then you see the crossword grid again?
If so, you might want to present as a 'popover' view.
You need to set width and height of the NumberSelectionGrid, otherwise it may be tiny.Alternatively, you can add the number selection grid as a subview to your root view, setting width and height to match root view. In that case, don't call present at all, you
add_subview
root thenbring_to_front
Your init would need to take width and height parameters. As an example of this method, see this input alert replacement. In that case, init doesn't set up the sizes,input_alert
sets the size to match superview (it has to be added to root already), and brings to front.send_to_back
or bring the other view, or set hidden in order to hide it. Don't mess around with trying to make a modal dialog like this, as things have to be done carefully to avoid freezes, instead your two views should interact via callbacks. -
Spot on. Thanks for that JonB. I've been having a look at the subview approach because I want it on iPhone (no popover iI believe). Managed to get it to work after a lot of trial and error.
Once I'm happy with how all the parts work I will rewrite because at this stage I am taking baby steps with the GUI programming within Pythonista so am really great full for all your help. -
tupWhite = tuple([1.0,1.0,1.0,1.0]) # white values tupBlack = tuple([1.0,1.0,1.0,1.0]) # black # [ ... ] if str(tupWhite) == str(square.bg_color): square.bg_color = 'black' else: square.bg_color = 'white'
A few questions on the above snippets of code:
- Do you really want the same values for both white and black?
- Did you know that
(1.0, 1.0, 1.0, 1.0) == tuple([1.0,1.0,1.0,1.0])
? Is there some advantage to the second over the first? - Why convert both values to strings each time you compare them? Why not just compare the tuples?
square.bg_color = 'black' if square.bg_color == tupWhite else 'white'
-
Well spotted. Yes it is a mistake. I was getting errors when trying to use 'white' in equality tests so I used the tuple values instead. I couldn't remember what the values for black were but didn't implement it in the end. I'll reuse your line when I rewrite, once I know how all the parts work. The main obstacles are my inexperience with GUI's & Classes but also trying to learn how to use these techniques on my iPhone in my spare time. I am really impressed with the shear power of Pythonista so far though. Bit fiddly editing on the phone at times though. Thanks for your help.
-
I'm trying to get the buttons to switch between views. How can I pass the size to child_view so it fills the screen like its parent?
Thanks in advance
Garyimport ui # test of view interchanging def button1_tapped(sender): child_view = SecondClass() parent_view.add_subview(child_view) def button2_tapped(sender): print 'button2 tapped' class FirstClass(ui.View): def __init__(self): self.present() self.background_color = 'black' button1 = ui.Button(frame = (1, 1, self.width, self.height)) print 'parent_view started at ', self.width button1.title = 'First Class' self.add_subview(button1) button1.action = button1_tapped class SecondClass (ui.View): def __init__(self): print 'parent_view now = ', parent_view.width self.background_color = 'white' button2 =ui.Button(frame = (1, 1, parent_view.width, parent_view.height)) button2.title = 'Second Class' parent_view.add_subview(button2) button2.action = button2_tapped parent_view = FirstClass()
-
import ui # test of view interchanging def button1_tapped(sender): root_view = sender.superview.superview for subview in root_view.subviews: root_view.remove_subview(subview) root_view.add_subview(SecondClass(root_view.bounds)) def button2_tapped(sender): print 'button2 tapped' class FirstClass(ui.View): def __init__(self, in_frame): self.frame = in_frame self.background_color = 'black' button1 = ui.Button(frame = (1, 1, self.width, self.height)) print 'FirstClass frame is ', self.frame button1.title = 'First Class' self.add_subview(button1) button1.action = button1_tapped class SecondClass(ui.View): def __init__(self, in_frame): self.frame = in_frame print 'SecondClass frame is ', self.frame self.background_color = 'white' button2 =ui.Button(frame = (1, 1, self.width, self.height)) button2.title = 'Second Class' self.add_subview(button2) button2.action = button2_tapped parent_view = ui.View() parent_view.present() parent_view.add_subview(FirstClass(parent_view.bounds))
-
Do you really need to delete the original view? If you are just swapping back and forth,
bring_to_front
orsend_to_back
work just fine.Also, I would consider adding flex so that if you change orientation, your views get resized (custom layout the would resize your button grid, etc)
-
@JonB, Great ideas! That simplifies the
button_tapped()
logic:import ui def button_tapped(sender): sender.send_to_back() parent_view = ui.View() parent_view.hidden = True buttons = [ui.Button(), ui.Button(), ui.Button(), ui.Button()] for i, b in enumerate(buttons): b.bg_color = 'white' b.action = button_tapped b.flex = 'WH' b.frame = (25, 25, 200, 200) b.name = b.title = 'Button {}'.format(i) parent_view.add_subview(b) b.send_to_back() # start with FIFO order parent_view.present() parent_view.hidden = False
-
Thanks guys,
I'm just trying to understand how all of the parts will work for my codeword GUI. Once I grasp that I'll be able to put them all together in a neater version.
Basically I wanted to swap between my 3 main views: Codeword grid, number selection grid , and letter selection grid. But keep coming up against beginner type problems with my code. In this latest problem it was getting the SecondClass object (a selection grid) to appear full screen and pass a value back.
I'll look more closely later at your posts but just wanted to thank you all for you prompt and helpful responses. It always helps speed things up but believe me I have spent ages on each stage just trying to get what looks like only a few lines of code.
As I said before 'Baby Steps' for me but learning is satisfying!
Regards
Gary -
Slightly off topic, perhaps, but here is a TabbedView, which has a tab bar, and lets you swap your main view between the different tabs. If your are literally swapping back and forth, this might be what you want, for instance I could imagine having clues on one tab, the crossword grid on another, and the key or quotation or whatever on another. Your use case might be more complex, but this might be one place to start.
Blah, backticks are behaving strange, here's a gist
https://gist.github.com/fcadaffff4be09c4ec78 -
Thanks to you all for your help so far. I have made some progress after the last post, if only a small step. I have now managed to get to grips a bit with switching 'views' at the touch of a button based on ccc's idea thanks.
The idea is still to switch between 3 different class types for my codeword, number grid and letter grid. This I have some idea of how I will tackle it but can somebody please explain the use of the 'frame' command in the context of switching between views. i.e. I don't know how to size or position my grids to fill the screen on iPhone/iPad. I have tried different ways from what I have seen but none of them seem to work. In this particular use my classes will be the grids made up of buttons that I want to fill the screen as best they fit.
Oh and I now have a bluetooth keyboard connected to my iPhone so it is much better for typing the lines of code in.
Thanks
Garyimport ui, console def button_tapped(sender): sender.superview.send_to_back() print sender.name class FirstClass(ui.View): def __init__(self): self.background_color = 'blue' button = ui.Button(frame = (0, 0, 1, 1), title = 'Class 1') button.name = button.title button.bg_color = 'white' button.flex = 'WH' self.add_subview(button) button.action = button_tapped class SecondClass(ui.View): def __init__(self): self.background_color = 'red' button = ui.Button(frame = (0, 0, 1, 1), title = 'Class 2') button.name = button.title button.bg_color = 'white' button.flex = 'WH' self.add_subview(button) button.action = button_tapped parent_view = ui.View() grids = [FirstClass(), SecondClass()] for i, b in enumerate(grids): #b.frame = (0, 0, parent_view.width, parent_view.height) b.flex = 'WH' parent_view.add_subview(b) b.send_to_back() parent_view.present('full_screen')
-
The first issue that you are facing is that before
parent_view.present()
is called, the values forparent_view.bounds
andparent_view.frame
are both set to(0.0, 0.0, 100.0, 100.0)
. It is only afterparent_view.present()
that they accurately refect screen size. This means that using .bounds, .frame, .center before .present() does not lead to the desired result. It is for this reason that increasingly I use the following approach:parent_view = ui.View() parent_view.hidden = True # hide your work parent_view.present() # create, set up, add subviews using parent_view.bounds, parent_view.center, parent_view.frame, etc. parent_view.hidden = False. # show your work
The second problem you face is that in order to lay your buttons out in a grid, your class needs to know its .frame size. In the current code, that is not set until after
.__init__()
has already run. Two alternative approaches: pass the frame size (parent_view.bounds) into the.__init__()
method or create a separatelayout_buttons()
method that you call after the lineb.frame = parent_view.bounds
has been executed. Personally, I like the first approach to solving this issue.import ui def button_tapped(sender): sender.superview.send_to_back() print sender.name def make_button(in_title='Untitled'): button = ui.Button(title=in_title) button.action = button_tapped button.bg_color = 'white' button.flex = 'TLBR' button.name = button.title return button class FirstClass(ui.View): def __init__(self, in_frame): self.frame = in_frame self.bg_color = 'blue' self.flex = 'WH' button = make_button('Class 1') button.center = self.center self.add_subview(button) class SecondClass(ui.View): def __init__(self, in_frame): self.frame = in_frame self.bg_color = 'red' self.flex = 'WH' button = make_button('Class 2') button.center = self.center self.add_subview(button) parent_view = ui.View() parent_view.hidden = True print(parent_view.bounds, parent_view.frame) parent_view.present() print(parent_view.bounds, parent_view.frame) grids = [FirstClass(parent_view.bounds), SecondClass(parent_view.bounds)] for grid in grids: parent_view.add_subview(grid) grid.send_to_back() parent_view.hidden = False
-
If you define a custom layout() method, which calls the layout_buttons method, you can add the subview before presenting, and set flex='wh', then when you present the parent view! layout gets called and the buttons get laid out. This also has the advantage of handling device orientation changes.
-
Oh yes... That is a much better approach!! Thx
import ui class MyView(ui.View): def __init__(self): print('__init__()') def layout(self): print('layout()') MyView().present()