omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    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.


    Finding a more elegant way of communicating between views

    Pythonista
    4
    32
    10324
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • superrune
      superrune last edited by

      Hi!

      I am trying to get better in Python/Pythonista by doing a pixel paint app. I have some problems understanding communication between views, nested and otherwise, and I was hoping you might have some suggestion to how I can make my code more elegant.

      My paint app is built up from views, there is an editor view at the "root" holding the image and tool buttons. What I want to do, is to open a file selector view on top of this, that does stuff with the editor view "below". For that I need the file view to access variables and functions that belong to the root editor.

      Right now, I have nested everything. The editor functions are inside the editor class, and the file view is a function of the editor class. Also, the file view functions are nested inside the file view function itself. But this feels real messy to me, and I'd rather have the file window as a separate class that is outside of the editor. But I'm not sure how to let the communicate back to the editor!

      I have made a super-simplified example of how my app works. Can anybody take a quick look and give me some hints?

      #!python3
      import ui
      from glob import glob
      from os.path import basename
      
      class pixelEditor(ui.View):
      	
      	def fileWindow(self, sender):
      		fileWindow = ui.View(frame=(100, 150, 300, 300), name='File window', border_width=2)
      		imagefiles = [basename(x) for x in glob('*.*')]
      		
      		def loadAction(sender):
      			# Dummy for the function that loads the image into the editor
      			selectedFile = imagefiles[filelist.selected_row[1]]
      			print ('Selected ' + selectedFile + ' from sender: ' + sender.name)
      			# Sends the selected image to the pixel Editor
      			self.subviews[1].text = selectedFile
      		
      		def fileSelected(sender):
      			filePreview.background_color = 'red'
      		
      		filelistData = ui.ListDataSource(imagefiles)
      		filelistData.delete_enabled=False
      		
      		filelist = ui.TableView(frame=(10, 10 ,150, 280), data_source=filelistData, name='filelist')
      		filelist.row_height = 24
      		filelist.action = fileSelected # Does not work...
      		fileWindow.add_subview(filelist)
      		
      		filePreview = ui.ImageView(frame=(170,10,120,100))
      		filePreview.background_color = 'black'
      		fileWindow.add_subview(filePreview)
      		
      		loadButton = ui.Button(name='Load', frame=(170,120,64,32), title='Load')
      		loadButton.background_color = 'white'
      		loadButton.action = loadAction
      		fileWindow.add_subview(loadButton)
      		
      		self.add_subview(fileWindow)
      		print('File window opened.')
      		
      		
      	def __init__(self, width=640, height=480):
      		self.bg_color = 'grey'
      	
      		fileButton = ui.Button(name='File', frame=(10,80,64,32), title='File Window')
      		fileButton.background_color = 'white'
      		fileButton.action = self.fileWindow
      		self.add_subview(fileButton)
      		
      		fileLabel = ui.Label(frame=(100, 80, 300, 32), font=('HelveticaNeue-Light', 32), text='___')
      		fileLabel.name = 'File Label'
      		self.add_subview(fileLabel)
      		
      		print(self.superview)
      	
      	
      v = pixelEditor()
      v.present('fullscreen')
      
      mikael 1 Reply Last reply Reply Quote 0
      • mikael
        mikael @superrune last edited by

        @superrune, if I understand your question correctly:

        The file picker view can be defined in a separate class and still communicate easily with the editor view via the superview attribute (after having been included in the view hierarchy, of course).

        1 Reply Last reply Reply Quote 0
        • JonB
          JonB last edited by

          If you are trying to make reusable classes, you could consider using a delegate attribute, or menu items are defined with a title, target object and a method that gets called on the target. That way a menu can target the root, or target some low level component.

          1 Reply Last reply Reply Quote 1
          • superrune
            superrune last edited by

            Thanks for answering! I’ve tried to restructure the script so that the file windows is a separate class, but it doesn’t behave quite as I expected. But views opens instantaneously, and the buttons don’t seem to work. Any pointers?

            #!python3
            import ui
            from glob import glob
            from os.path import basename
            
            class fileWindow(ui.View):
            	def __init__(self, frame=(100, 150, 300, 300)):
            		self.name = 'File window'
            		self.border_width = 2
            		
            		imagefiles = [basename(x) for x in glob('*.*')]
            		
            		def loadAction(sender):
            			selectedFile = imagefiles[filelist.selected_row[1]]
            			print ('Selected ' + selectedFile + ' from sender: ' + sender.name)
            			# Sends the selected image to the pixel Editor
            			self.subviews[1].text = selectedFile
            		
            		def fileSelected(sender):
            			# This does not work..
            			filePreview.background_color = 'red'
            		
            		filelistData = ui.ListDataSource(imagefiles)
            		filelistData.delete_enabled=False
            		
            		filelist = ui.TableView(frame=(10, 10 ,150, 280), data_source=filelistData, name='filelist')
            		filelist.row_height = 24
            		filelist.action = fileSelected # This does not work...
            		self.add_subview(filelist)
            		
            		filePreview = ui.ImageView(frame=(170,10,120,100))
            		filePreview.background_color = 'black'
            		self.add_subview(filePreview)
            		
            		loadButton = ui.Button(name='Load', frame=(170,120,64,32), title='Load')
            		loadButton.background_color = 'white'
            		loadButton.action = loadAction
            		self.add_subview(loadButton)
            		
            
            class pixelEditor(ui.View):	
            	def __init__(self, width=640, height=480):
            		self.bg_color = 'grey'
            
            		def openFileWindow():
            			fv = fileWindow()
            			fv.present()
            			print('File window opened.')
            			
            		fileButton = ui.Button(name='File', frame=(10,80,64,32), title='File Window')
            		fileButton.background_color = 'white'
            		fileButton.action = openFileWindow()
            		self.add_subview(fileButton)
            		
            		fileLabel = ui.Label(frame=(100, 80, 300, 32), font=('HelveticaNeue-Light', 32), text='___')
            		fileLabel.name = 'File Label'
            		self.add_subview(fileLabel)
            		
            		print(self.superview)
            	
            	
            v = pixelEditor()
            v.present('fullscreen')		
            	```
            mikael 1 Reply Last reply Reply Quote 0
            • mikael
              mikael @superrune last edited by

              @superrune, remove the parentheses from the end of

              fileButton.action = openFileWindow()
              

              Now you are calling the method (and immediately opening the window) and setting action to the value returned, which is None, and thus the button does nothing.

              1 Reply Last reply Reply Quote 0
              • superrune
                superrune last edited by superrune

                Thanks @mikael, for explaining this to me. That makes total sense. When I do that though, I get this error:
                TypeError: openFileWindow() takes 0 positional arguments but 1 was given
                Answers on stackoverflow mention that the error occurs when you forgot to add ‘self’ to the functions inside the class, but that doesn’t seem to make any change.

                cvp 1 Reply Last reply Reply Quote 0
                • cvp
                  cvp @superrune last edited by cvp

                  @superrune a button action needs sender as parameter to identify which object has been tapped

                          def openFileWindow(sender):
                  
                  1 Reply Last reply Reply Quote 0
                  • superrune
                    superrune last edited by superrune

                    I’m slowly moving closer to something that works! So I moved the open view function out of the class, and now opening the window works! So now I need to get these two windows to talk to each other. Inside the init of the file window I am printing the superview to make sure it’s actually a child, but superview returns none. So why isn’t the parent/child relationship set up, even though I am creating this as a sub view?

                    #!python3
                    import ui
                    from glob import glob
                    from os.path import basename
                    
                    class fileWindow(ui.View):
                    	def __init__(self):
                    		self.frame=(100, 150, 300, 300)
                    		self.name = 'File window'
                    		self.border_width = 2
                    		print ('Loader superview:', self.superview)
                    		
                    		imagefiles = [basename(x) for x in glob('*.*')]
                    		
                    		def loadAction(sender):
                    			selectedFile = imagefiles[filelist.selected_row[1]]
                    			print ('Selected ' + selectedFile + ' from sender: ' + sender.name)
                    			# Sends the selected image to the pixel Editor
                    			self.superview.text = selectedFile
                    		
                    		def fileSelected(sender):
                    			# This does not work..
                    			filePreview.background_color = 'red'
                    		
                    		filelistData = ui.ListDataSource(imagefiles)
                    		filelistData.delete_enabled=False
                    		
                    		filelist = ui.TableView(frame=(10, 10 ,150, 280), data_source=filelistData, name='filelist')
                    		filelist.row_height = 24
                    		filelist.action = fileSelected # This does not work...
                    		self.add_subview(filelist)
                    		
                    		filePreview = ui.ImageView(frame=(170,10,120,100))
                    		filePreview.background_color = 'black'
                    		self.add_subview(filePreview)
                    		
                    		loadButton = ui.Button(name='Load', frame=(170,120,64,32), title='Load')
                    		loadButton.background_color = 'white'
                    		loadButton.action = loadAction
                    		self.add_subview(loadButton)
                    		
                    def openFileWindow(sender):
                    		fv = fileWindow()
                    		sender.add_subview(fv)
                    		fv.present()
                    		#fv = fileWindow()
                    		#v.present()
                    		print('File window opened.')
                    
                    class pixelEditor(ui.View):	
                    	def __init__(self, width=640, height=480):
                    		self.bg_color = 'grey'
                    			
                    		fileButton = ui.Button(name='File', frame=(10,80,64,32), title='File Window')
                    		fileButton.background_color = 'white'
                    		fileButton.action = openFileWindow
                    		self.add_subview(fileButton)
                    		
                    		fileLabel = ui.Label(frame=(100, 80, 300, 32), font=('HelveticaNeue-Light', 32), text='___')
                    		fileLabel.name = 'File Label'
                    		self.add_subview(fileLabel)
                    		
                    		print(self.superview)
                    	
                    	
                    v = pixelEditor()
                    v.present('fullscreen')
                    
                    1 Reply Last reply Reply Quote 0
                    • superrune
                      superrune last edited by

                      I tried changing self to sender in the function that opens the window, but still the parent view is returned as None.

                      cvp 1 Reply Last reply Reply Quote 0
                      • cvp
                        cvp @superrune last edited by cvp

                        @superrune normal way is this, remark action = self.xxxxx and def xxx(self,sender) at same level as the def init

                        
                        class pixelEditor(ui.View): 
                            def __init__(self, width=640, height=480):
                                self.bg_color = 'grey'
                                    
                                fileButton = ui.Button(name='File', frame=(10,80,64,32), title='File Window')
                                fileButton.background_color = 'white'
                                fileButton.action = self.openFileWindow
                                self.add_subview(fileButton)
                                
                                fileLabel = ui.Label(frame=(100, 80, 300, 32), font=('HelveticaNeue-Light', 32), text='___')
                                fileLabel.name = 'File Label'
                                self.add_subview(fileLabel)
                                
                                print(self.superview)
                                
                            def openFileWindow(self, sender):
                                fv = fileWindow()
                                fv.present()
                                print('File window opened.')```
                        1 Reply Last reply Reply Quote 0
                        • superrune
                          superrune last edited by

                          When I do that, I get the error message 'pixelEditor' object has no attribute 'openFileWindow'

                          Moving the definition before the action assignment doesnt change anything either.

                          cvp 2 Replies Last reply Reply Quote 0
                          • cvp
                            cvp @superrune last edited by cvp

                            @superrune did you remark the new indentation of OpenFileWindow

                            It became a method of the class...

                            1 Reply Last reply Reply Quote 0
                            • cvp
                              cvp @superrune last edited by cvp

                              @superrune The other problem, of the superview, is that you use self.superview in the init.
                              The class is not yet really created. Try this and see where I use the self.superview

                              #!python3
                              import ui
                              from glob import glob
                              from os.path import basename
                              
                              class fileWindow(ui.View):
                                  def __init__(self):
                                      self.frame=(100, 150, 300, 300)
                                      self.name = 'File window'
                                      self.border_width = 2
                                      #print ('Loader superview:', self.superview)
                                      
                                      imagefiles = [basename(x) for x in glob('*.*')]
                              
                                      
                                      filelistData = ui.ListDataSource(imagefiles)
                                      filelistData.delete_enabled=False
                                      
                                      filelist = ui.TableView(frame=(10, 10 ,150, 280), data_source=filelistData, name='filelist')
                                      filelist.row_height = 24
                                      filelist.action = self.fileSelected # This does not work...
                                      self.add_subview(filelist)
                                      
                                      filePreview = ui.ImageView(frame=(170,10,120,100))
                                      filePreview.background_color = 'black'
                                      self.add_subview(filePreview)
                                      
                                      loadButton = ui.Button(name='Load', frame=(170,120,64,32), title='Load')
                                      loadButton.background_color = 'white'
                                      loadButton.action = self.loadAction
                                      self.add_subview(loadButton)
                                      
                                      ui.delay(self.x,0.01)
                                  def x(self):
                                      print ('Loader superview title = ', self.superview.title)
                                      
                                  def loadAction(self,sender):
                                      selectedFile = imagefiles[filelist.selected_row[1]]
                                      print ('Selected ' + selectedFile + ' from sender: ' + sender.name)
                                      # Sends the selected image to the pixel Editor
                                      self.superview.text = selectedFile
                                      
                                  def fileSelected(self,sender):
                                      # This does not work..
                                      filePreview.background_color = 'red'
                                      
                              class pixelEditor(ui.View): 
                                  def __init__(self, width=640, height=480):
                                      self.bg_color = 'grey'
                                          
                                      fileButton = ui.Button(name='File', frame=(10,80,64,32), title='File Window')
                                      fileButton.background_color = 'white'
                                      fileButton.action = self.openFileWindow
                                      self.add_subview(fileButton)
                                      
                                      fileLabel = ui.Label(frame=(100, 80, 300, 32), font=('HelveticaNeue-Light', 32), text='___')
                                      fileLabel.name = 'File Label'
                                      self.add_subview(fileLabel)
                                      
                                      print(self.superview)
                                      
                                  def openFileWindow(self,sender):
                                      fv = fileWindow()
                                      sender.add_subview(fv)
                                      fv.present()
                                      #fv = fileWindow()
                                      #v.present()
                                      print('File window opened.')
                              
                                  
                                  
                              v = pixelEditor()
                              v.present('fullscreen')
                              
                              1 Reply Last reply Reply Quote 0
                              • superrune
                                superrune last edited by superrune

                                Thanks, that also worked. I had the sub-window opening OK a couple steps back as well. But the next problem still remains, though, even in this version.

                                I do a print ('Loader superview:', self.superview) when I init the fileWindow, to see that there is a parent view I can put the selected file into, but that still returns None. Is there a way to get the new view properly assigned as a child of the first pixelEditor view?

                                edit: I see you have made a new function that prints the superview, but it returns fileWindow. So the windows parent is itself? Why is the superview not pixelEditor?

                                Thanks for taking the time to help me out!!

                                cvp 5 Replies Last reply Reply Quote 0
                                • cvp
                                  cvp @superrune last edited by

                                  @superrune I'll check your question but you also have another error

                                          selectedFile = self['filelist'].data_source.items[self['filelist'].selected_row[1]]
                                  
                                  1 Reply Last reply Reply Quote 0
                                  • cvp
                                    cvp @superrune last edited by

                                    @superrune You made a mistake: the superview is a button and my print in my new little x function is the title of the button, not the class.
                                    You named your button 'File Window'....

                                    1 Reply Last reply Reply Quote 0
                                    • cvp
                                      cvp @superrune last edited by cvp

                                      @superrune Then, change in OpenFileWindow, set the FileWindow as a child of self which is the pixeleditor

                                          def openFileWindow(self,sender):
                                              fv = fileWindow()
                                              self.add_subview(fv)
                                      

                                      And in def x():

                                          def x(self):
                                              print ('Loader superview type = ', type(self.superview))
                                      
                                      1 Reply Last reply Reply Quote 0
                                      • cvp
                                        cvp @superrune last edited by

                                        @superrune other big problem, when you present the FileWindow, the PixelEditor is no more presented, thus try without presenting it:

                                            def openFileWindow(self,sender):
                                                fv = fileWindow()
                                                self.add_subview(fv)
                                                #fv.present()
                                                #fv = fileWindow()
                                                #v.present()
                                                print('File window opened.')
                                        
                                        1 Reply Last reply Reply Quote 0
                                        • cvp
                                          cvp @superrune last edited by

                                          @superrune And last little problem, to "send" the selected file name to PixelEditor, use something like

                                                  # Sends the selected image to the pixel Editor
                                                  self.superview.name = selectedFile
                                          

                                          and you will see the name of the file on the title bar of the PixelEditor View.

                                          1 Reply Last reply Reply Quote 0
                                          • superrune
                                            superrune last edited by

                                            @cvp said:

                                            print ('Loader superview type = ', type(self.superview))

                                            Thanks,

                                            I still have to do a fv.present() inside the openFileWindow function, right? The window will not show up otherwise...

                                            print ('Loader superview type = ', type(self.superview)) now returns a NoneType, though.

                                            I still want to put the selected file name into fileLabel.text - how would you go about doing that?

                                            cvp 5 Replies Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors