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
-
The frame property lets you set size and position in one go.
Or, you can set width, height, x and y separately.
Frame can be set in the constructor, as can the background color, many other parameters can not.You basically have to do the math yourself to place on a grid. See ccc's example above, where he computes x and y for each column/row, and initializer the button using those values. His code seems like it would be a good jumping off point, and has a good interface for encoding the row,col inside the name.
-
Thanks for the reply. I've managed this so far but not sure how to manage the scope for my Constant 'status'.
Once the 'Done' button is tapped 'status' should be set to 2 and then stop the black/white toggle code using the 'if' block. I think scope is the problem here but not sure how to cure it. Any help appreciated.
Also don't know how to show my code like others do on the message sorry.import console, ui, time
crossword_rows = 4
crossword_cols = 4
gap = 1
status = 1def button_tapped1(square):
#print 'status = '+ str(status)
tup = tuple([1.0,1.0,1.0,1.0]) # white values
if status==1:
#print 'status = '+ str(status)
if str(tup) == str(square.bg_color):
square.bg_color = 'black'
else:
square.bg_color = 'white'
elif status==2:
#button_tapped2()
passdef button_tapped2(button):
#add square number
#print 'status = '+ str(status)
status = 2
button.title = 'ok'
#print 'status = '+ str(status)
passclass CrosswordView(ui.View):
def init(self):
self.present(hide_title_bar=False )
self.background_color = (0, 1.0, 2.0, 0.4)
min_dimension = min(self.width / crossword_cols, self.height / crossword_rows)
#self.squares = []
for i in xrange(crossword_cols):
x = i * min_dimension
for j in xrange(crossword_rows):
time.sleep(.05)
y = j * min_dimension
square = ui.Button(frame = (x, y, min_dimension-gap, min_dimension-gap))
#square.alignment = #ui.ALIGN_CENTER
square.bg_color = 'white'
#square.title = 'X'
self.add_subview(square)
square.action = button_tapped1button = ui.Button(frame = (x-150, y+80, min_dimension*2, min_dimension*2)) self.add_subview(button) button.title = 'Done' button.background_color = 'white' button.action = button_tapped2 #print 'another visit'
CrosswordView()
print 'status = '+ str(status) -
From help('SCOPING')
If a name is bound in a block, it is a local variable of that block.
meaning, since you are trying to modify status, it is a local to
button_tapped2
. (I learned something today!)Declare
status
as global in that function.As an aside, for posting code to the forum, if works best if you insert a blank line, then a line containing three backticks
```
, then your code, then the three backticks again.
On ios, backtick is found by long tapping the single quote, and selecting the leftmost item.
Or, go into settings app-> keyboard -> shortcuts, and add a shortcut.
I map ,,, to ```` `, allowing quick entry without having to go to the symbol page of the ipad keyboard``` Like this ```
-
Putting the word
python
directly after the first three backticks will give you syntax highlighting for Python code.@GaryGadget, it would be cool if you could create a GitHub repo of your code so that we could collaborate on it.
-
JonB,
I had been trying Global but it wasn't working but since you confirmed my thoughts I succeed thanks. I didn't realise it had to be declared global in each block!Here's my attempt so far as test code for setting up the matrix black/white and to include it in this message properly.
Thanks for your help guys.
import console, ui, time crossword_rows = 4 crossword_cols = 4 gap = 1 status = 1 def button_tapped1(square): global status print 'status = '+ str(status) tup = tuple([1.0,1.0,1.0,1.0]) # white values if status==1: #print 'status = '+ str(status) if str(tup) == str(square.bg_color): square.bg_color = 'black' else: square.bg_color = 'white' elif status==2: #button_tapped2() pass def button_tapped2(button): #add square number global status print 'status = '+ str(status) status = 2 button.title = 'Colours set.' #print 'status = '+ str(status) #pass class CrosswordView(ui.View): def __init__(self): self.present(hide_title_bar=False ) self.background_color = (0, 1.0, 2.0, 0.4) min_dimension = min(self.width / crossword_cols, self.height / crossword_rows) #self.squares = [] for i in xrange(crossword_cols): x = i * min_dimension for j in xrange(crossword_rows): time.sleep(.05) y = j * min_dimension square = ui.Button(frame = (x, y, min_dimension-gap, min_dimension-gap)) #square.alignment = #ui.ALIGN_CENTER square.bg_color = 'white' #square.title = 'X' self.add_subview(square) square.action = button_tapped1 button = ui.Button(frame = (x-150, y+80, min_dimension*2, min_dimension*2)) self.add_subview(button) button.title = 'Done' button.background_color = 'white' button.action = button_tapped2 #print 'another visit' CrosswordView() #print 'status = '+ str(status)
-
Maybe this code help you:
import ui class MakeButtonArray(object): def __init__(self): self.view = ui.View() self.view.name = 'Buttons' self.view.background_color = 'grey' self.view.present('fullscreen') self.edit = ui.TextField() self.edit.text = '' self.edit.center = (50,50) self.edit.width = self.view.width self.edit.height = 30 self.edit.border_color = 'black' self.edit.border_width = 1 self.edit.placeholder = 'single letter or whole word' self.edit.enabled = False self.edit.delegate = self self.edit.clear_button_mode = 'while_editing' self.view.add_subview(self.edit) self.button_array = [] self.word1 = 'TELEFON' for i in range(len(self.word1)): self.button_array.append(ui.Button(title=self.word1[i])) x = 100 y = 100 + (i * 50) self.button_array[i].x = x self.button_array[i].y = y self.button_array[i].width = 50 self.button_array[i].height = 50 self.button_array[i].border_color = 'black' self.button_array[i].border_width = 1 self.button_array[i].name = str(i) self.button_array[i].bg_color = 'white' self.button_array[i].action = self.button_array_action self.view.add_subview(self.button_array[i]) i += 1 def button_array_action(self, sender): self.edit.enabled = True self.edit.text = self.word1 #self.edit.text = 'sender.name:' + sender.name + ' // "' + sender.title + '" // ' + 'sender.x:' + str(sender.x) + ' sender.y:' + str(sender.y) def textfield_did_end_editing(self,textfield): #print textfield.text self.word1 = textfield.text self.edit.enabled = False self.edit.text = '' MakeButtonArray()
-
You only need the
global status
statement in those functions where you want to make a permanent change to its value:zippy = 'zippy' def read_only(): print(zippy) # zippy def local_change(): # print(zippy) # would cause the next statement to throw an error zippy = 'pinhead' # local with same name as global print(zippy) # pinhead def permanent_change(): global zippy zippy = 'pinhead' print(zippy) # pinhead read_only() # zippy local_change() # pinhead print(zippy) # zippy -- global was not changed permanent_change() # pinhead print(zippy) # pinhead -- global was changed
Read_only()
demonstrates that even without theglobal
statement, you still have read-only access to the global variable. -
A reformulation of @brumm's code above to:
- Convert
MakeButtonArray
into a subclass ofui.View
- Add
make_edit_text_field()
andmake_button()
methods - Use list comprehension with
enumerate()
to createbutton_array
- Use
format()
to create the commented out debug text
import ui class MakeButtonArray(ui.View): def __init__(self): self.name = 'Buttons' self.background_color = 'grey' self.present('fullscreen') self.edit = self.make_edit_text_field() self.add_subview(self.edit) self.word1 = 'TELEFON' self.button_array = [self.make_button(i, c) for i, c in enumerate(self.word1)] def make_edit_text_field(self): edit_tf = ui.TextField() edit_tf.center = (50, 50) edit_tf.width = self.width edit_tf.height = 30 edit_tf.border_color = 'black' edit_tf.border_width = 1 edit_tf.placeholder = 'single letter or whole word' edit_tf.enabled = False edit_tf.delegate = self edit_tf.clear_button_mode = 'while_editing' return edit_tf def make_button(self, i, c): button = ui.Button(name=str(i), title=c) button.frame = (100, 100 + i * 50, 50, 50) button.bg_color = 'white' button.border_color = 'black' button.border_width = 1 button.action = self.button_array_action self.add_subview(button) return button def button_array_action(self, sender): self.edit.enabled = True self.edit.text = self.word1 #fmt = 'sender.name:{} // "{}" // sender.x:{} sender.y:{}' #self.edit.text = fmt.format(sender.name, sender.title, # sender.x, sender.y) def textfield_did_end_editing(self,textfield): #print textfield.text self.word1 = textfield.text self.edit.enabled = False self.edit.text = '' MakeButtonArray()
- Convert
-
Thanks for the code examples. I'm sure they will help.
-
Hi chaps,
I'm still playing around with buttons for my codeword app. Struggling now with changing the view from one view to another. How is it done please or have I got the wrong end of the stick here?The idea is to firstly select which cells are black. Click done. Then selecting a white cell should present my second class as a view to choose number values from a grid. Trouble is the grid becomes too small when I call it from anywhere but the line not indented!
import console, ui, time crossword_rows = 4 crossword_cols = crossword_rows gap = 1 tupWhite = tuple([1.0,1.0,1.0,1.0]) # white values tupBlack = tuple([1.0,1.0,1.0,1.0]) # black status = 1 def button_tapped1(square): global status #print 'status = '+ str(status) #tup = tuple([1.0,1.0,1.0,1.0]) # white values if status==1: #print 'status = '+ str(status) if str(tupWhite) == str(square.bg_color): square.bg_color = 'black' else: square.bg_color = 'white' elif status==2: #button_tapped2() edit_cell_number(square) print 'step 2' #pass def button_tapped2(button): #add square number global status #print 'status = '+ str(status) status = 2 button.title = 'Colours set.' #print 'status = '+ str(status) #pass def button_tapped3(self, square): pass def edit_cell_number(square): #cyvle through 1 - 26 for cell number if str(tupWhite) == str(square.bg_color): #square.title = str(26) #NumberSelectionGrid() pass class CrosswordView(ui.View): def __init__(self): self.present(hide_title_bar=False ) self.background_color = (0, 1.0, 2.0, 0.4) min_dimension = min(self.width / crossword_cols, self.height / crossword_rows) #self.squares = [] for i in xrange(crossword_cols): x = i * min_dimension for j in xrange(crossword_rows): time.sleep(.05) y = j * min_dimension square = ui.Button(frame = (x, y, min_dimension-gap, min_dimension-gap)) #square.alignment = #ui.ALIGN_CENTER square.bg_color = 'white' #square.title = 'X' self.add_subview(square) square.action = button_tapped1 #button = ui.Button(frame = (x-150, y+80, min_dimension*2, min_dimension*2)) button = ui.Button(frame = (0,0, min_dimension*4, min_dimension*2)) self.add_subview(button) button.title = 'Done' button.background_color = 'white' button.center = (self.width * 0.5, self.height -50) button.flex = 'LRTB' button.action = button_tapped2 #print 'another visit' class NumberSelectionGrid(ui.View): def __init__(self): self.present(hide_title_bar=False ) self.background_color = (0, 1.0, 2.0, 0.4) min_dimension = min(self.width / 5, self.height / 6) #self.squares = [] squareNumber = 1 for i in xrange(5): x = i * min_dimension for j in xrange(6): #time.sleep(.05) y = j * min_dimension square = ui.Button(frame = (x, y, min_dimension-gap, min_dimension-gap)) #square.alignment = #ui.ALIGN_CENTER square.bg_color = 'white' square.title = str(squareNumber) self.add_subview(square) squareNumber = squareNumber +1 square.action = button_tapped3 CrosswordView() NumberSelectionGrid() #NumberSelectionGridd().present(hide_title_bar=False) #NumberSelectionGrid().send_to_back() #print 'status = '+ str(status)
-
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