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.


    Aligning scene nodes while ui sceneView is animately resized

    Pythonista
    6
    18
    4901
    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.
    • rownn
      rownn last edited by rownn

      Hey everyone,

      awkward title of the topic, I know. I struggled with fitting nodes in resizing sceneViews. I have a sceneView, which is resized via animation by pushing a button. The question was how to stick the scene at the top of the view. I have found a solution, but because of the fact I am new to that stuff I‘d like to post the code here with the silent question if there is a better way or if it is a common way.

      from scene import *
      import ui
      
      class MyScene(Scene):
      	def __init__(self):
      		Scene.__init__(self)
      		self.background_color= '#eee'
      		self.frm = ShapeNode(parent=self, fill_color='red', stroke_color='clear')
      	
      	def setup(self):
      		self.viewSizeHasChanged()
      		
      	def viewSizeHasChanged(self):
      		self.frm.position = (150,self.view.height/2-(500-self.view.height)/2)
      		self.frm.path = ui.Path.rect(0,0,250,450)
      
      class GUI(ui.View):
      	def __init__(self):
      		self.background_color = '#ddd'
      		self.scales = (100, 500)
      		self.state = True
      		self.separator_H = 10
      		
      		self.mainView = ui.View(frame=(50,50,300,500), background_color='#fff')
      		self.add_subview(self.mainView)
      		
      		self.sn = SceneView()
      		self.sn.frame = self.mainView.bounds
      		self.mainView.add_subview(self.sn)
      		self.sn.scene = MyScene()
      		
      		self.btn_Do = ui.Button(name='Do', title='Do', background_color= '#ddd', corner_radius = 12, action = self.btn_tapped)
      		self.btn_Do.frame = (175, 275, 50, 50)
      		self.add_subview(self.btn_Do)
      		
      	def btn_tapped(self, sender):
      		if sender.name == 'Do':
      			self.state = (self.state+1)%2
      			self.sn.scene.viewSizeHasChanged()
      			self.animate(self.scales[self.state])
      				
      	def animate(self, H):
      		def animation(): self.mainView.frame = (50,(600-H)/2,300,H)
      		ui.animate(animation, duration=1.0)
      		
      if __name__ == '__main__':
          GUI().present('fullscreen')
      

      Thx for every hint guys
      rownn

      stephen 2 Replies Last reply Reply Quote 0
      • stephen
        stephen @rownn last edited by stephen

        @rownn are you trying to shrink the scene also or just hide it?

        1 Reply Last reply Reply Quote 0
        • stephen
          stephen @rownn last edited by

          @rownn Also you dont need to import uiwhen you import all from scene 🤓 scene already imports ui implicitly

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

            Hey @stephen,

            good question. Actually I wanted to shrink the scene, too. But than I realized that it wouldnt effect the appearance I wish. Besides this I wasnt able to get the scene shinking smoothly with the sceneView :( Is there a way to achieve this?

            Thanks for the tip regarding the ui importing, you are right, of course :)
            And thanks for looking though the code

            mikael 1 Reply Last reply Reply Quote 1
            • mikael
              mikael @rownn last edited by mikael

              @rownn, here’s how I would do it, to have the scene contents shrink smoothly with the view.

              The main changes are the use of Scene’s update method to update the layout, and the use of the scripter to drive the animation. Like always, the regular ui.animate just did not want to work with me.

              from scene import *
              import ui
              from scripter import *
              
              class MyScene(Scene):
              
                  def setup(self):
                      self.background_color= '#eee'
                      self.frm = ShapeNode(parent=self, fill_color='red', stroke_color='clear')
                      
                  def update(self):
                      self.frm.path = ui.Path.rect(
                          *self.view.bounds.inset(20, 20)
                      )
                      self.frm.position = self.size/2
                      
              
              class GUI(ui.View):
                  def __init__(self):
                      self.background_color = '#ddd'
                      self.open = True
                      
                      self.main_view = ui.View(
                          frame=(50,50,300,500),
                          flex='RLTB',
                          background_color='#fff')
                      self.main_view.center = self.bounds.center()
                      self.add_subview(self.main_view)
                      
                      self.sn = SceneView(
                          frame = self.main_view.bounds,
                          flex='WH')
                      self.main_view.add_subview(self.sn)
                      self.sn.scene = MyScene()
                      
                      self.btn_Do = ui.Button(
                          title='Just Do It', 
                          tint_color='black',
                          background_color= '#ddd', 
                          corner_radius = 12, 
                          action = self.btn_tapped,
                          flex='RLTB')
                      self.btn_Do.size_to_fit()
                      self.btn_Do.frame = self.btn_Do.frame.inset(-8, -16)
                      self.btn_Do.center = self.bounds.center()
                      self.add_subview(self.btn_Do)
                      
                  def btn_tapped(self, sender):
                      self.open = self.open == False
                      self.animation()
                      
                  @script
                  def animation(self):
                      height(self.main_view, 500 if self.open else 100)
                      center(self.main_view, self.bounds.center())
                      yield
              
                      
              if __name__ == '__main__':
                  GUI().present('fullscreen')
              

              There are also several suggested changes to create the layout without calculating pixels.

              cvp 1 Reply Last reply Reply Quote 2
              • rownn
                rownn last edited by

                Hey @mikael,

                thanks alot! It is amazing to see one of my codes in a rewritten most likely better way :) I just flew over it, but there are many very nice looking changes and I‘m excited to get into it deeper soon. Until then I thank you for the time you spent and the insights which I will have.

                PS: Thought it would be clever to avoid the update method. Isnt it performance-consuming?

                stephen 2 Replies Last reply Reply Quote 0
                • stephen
                  stephen @rownn last edited by

                  @rownn Couple options. notvery clean but can be fine tuned

                  
                  from scene import *
                  
                  class MyScene1(Scene):
                  	'''
                  		unless you plan to make copies of the scene theres no need to override 
                  		__init__ ☺️ setup has everything needed
                  	'''
                  	def setup(self):
                  		self.background_color= '#eee'
                  		self.frm = ShapeNode(parent=self, fill_color='red', stroke_color=None)
                  		
                  		lbl=LabelNode(text=f'{self.view.frame}', font=('Chalkboard SE', 16), position=Point(self.size[0]/2, self.size[1]-64), color='#00d412', parent=self)
                  		
                  	def viewSizeHasChanged(self):
                  		self.frm.position = (150,self.view.height/2-(500-self.view.height)/2)
                  		self.frm.path = ui.Path.rect(0,0,250,450)
                  		
                  class MyScene2(Scene):
                  	def setup(self):
                  		self.background_color= '#eee'
                  		self.frm = ShapeNode(parent=self, fill_color='blue', stroke_color=None, anchor_point=(0.0, 1))
                  		self.frm.position = (150, (self.view.height/2-(500-self.view.height)/2))
                  		self.frm.path = ui.Path.rect(0,0,250,450)
                  		self.minimized=False
                  		self.updateSize=True
                  		self.lbl1=LabelNode(text=f'{self.view.frame}', font=('Chalkboard SE', 16), position=Point(self.size[0]/2, self.size[1]-64), color='#00d412', parent=self)
                  		self.lbl2=LabelNode(text=f'{self.frm.frame}', font=('Chalkboard SE', 16), position=Point(self.size[0]/2, self.size[1]-32), color='#00d412', parent=self)
                  		
                  	def Resize(self, node, progress):
                  		size= 50 if self.minimized else 450
                  		self.frm.size = (self.frm.size[0], size/progress)
                  			
                  	def Toggle(self):
                  		self.minimized = not self.minimized
                  		self.frm.run_action(Action.call(self.Resize, 1.0))
                  		
                  	def update(self):
                  		#self.frm.size=Size(self.view.width-50, self.view.height-50)
                  		self.frm.position= Point(25, self.size[1]-25)
                  		self.lbl2.text=f'{self.frm.frame}'
                  		
                  	def viewSizeHasChanged(self):
                  		pass
                  		
                  		
                  class GUI(ui.View):
                  	def __init__(self):
                  		self.background_color = '#ddd'
                  		self.scales = (1, .25)
                  		self.state = True
                  		self.separator_H = 10
                  		
                  		self.mainView = ui.View(frame=(50, 50, 300, 500), background_color='#fff')
                  		self.add_subview(self.mainView)
                  		
                  		self.mainView2 = ui.View(frame=(350, 50, 300, 500), background_color='#fff')
                  		self.add_subview(self.mainView2)
                  		
                  		self.sn = SceneView()
                  		self.sn.frame = self.mainView.bounds
                  		self.mainView.add_subview(self.sn)
                  		self.sn.scene = MyScene1()
                  		
                  		self.sn2 = SceneView()
                  		self.sn2.frame = self.mainView2.bounds
                  		self.mainView2.add_subview(self.sn2)
                  		self.sn2.scene = MyScene2()
                  		
                  		self.btn_1 = ui.Button(name='1', title='1', background_color= '#ddd', corner_radius = 12, action = self.btn_tapped1)
                  		self.btn_1.frame = (175, 275, 50, 50)
                  		self.add_subview(self.btn_1)
                  		
                  		self.btn_2 = ui.Button(name='2', title='2', background_color= '#ddd', corner_radius = 12, action = self.btn_tapped2)
                  		self.btn_2.frame = (475, 275, 50, 50)
                  		self.add_subview(self.btn_2)
                  		
                  		
                  		'''
                  				:ui.View.layout():
                  			now the scene's viewSizeHasChanged() will be called
                  			anytime the view is altered including initial display
                  		'''
                  	def layout(self):
                  		self.sn.scene.viewSizeHasChanged()
                  		self.sn2.scene.viewSizeHasChanged()
                  		pass
                  	def btn_tapped1(self, sender):
                  		if sender.name == '1':
                  			self.state = (self.state+1)%2
                  			self.animate(self.scales[self.state])
                  			
                  	def btn_tapped2(self, sender):
                  		self.sn2.scene.Toggle()
                  		def animation(): 
                  			self.mainView2.height = 100 if self.sn2.scene.minimized else 500
                  			self.mainView2.y = 275 if self.sn2.scene.minimized else 50
                  			
                  		ui.animate(animation, duration=1.0)
                  	
                  	def animate(self, H):
                  		'''
                  			using Transform with ani ate you can scale, move and rotate the whole 
                  			sceneView
                  			alternatively you can create an Action for the scene to change size 
                  			while changing the views
                  		'''
                  		def animation(): 
                  			self.mainView.transform = ui.Transform.scale(1, H)
                  		ui.animate(animation, duration=0.5)
                  		
                  if __name__ == '__main__':
                  	GUI().present('fullscreen')
                  
                  1 Reply Last reply Reply Quote 0
                  • stephen
                    stephen @rownn last edited by

                    @rownn said:

                    PS: Thought it would be clever to avoid the update method. Isnt it performance-consuming?

                    i thought this too before but as long as everything inside the update method does not take longer than self.dt your good 😎☺️

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

                      When you look at the code for scene, there is an _update method that is getting called, that handles the Actions. Then it calla update. So performance wise, I think Actions and update methods are similar.

                      stephen 1 Reply Last reply Reply Quote 1
                      • stephen
                        stephen @JonB last edited by

                        @JonB said:

                        When you look at the code for scene, there is an _update method that is getting called, that handles the Actions. Then it calla update. So performance wise, I think Actions and update methods are similar.

                        exactly. in my games ill actually use this to my advantage by staging animations and checks. for example you can move a node with Action.move_by and have a check in update to make sure node isnt outside the screen. this insures the move is done before the check. Alternativily you can move your check into Scene.did_evaluate_actions() only reason i dont use this method is because your check must wait until the action is finished and that could have been too long depending on current check being used.

                        heres an example if anyone wants using all three steps to move the node and rmove it if off screen.

                        
                        def did_evaluate_actions(self):
                        	self.move_node()
                        
                        def move_node(self):
                        	self.run_action(Action.move_by(-100, 0, 1.0, TIMING_EASE_IN_OUT))
                        	
                        def update(self):
                        	if self.position[0] < -self.size[0]-10:
                        		self.run_action(Action.remove())
                        	
                        	
                        
                        

                        adding a light rotate at the end and begining would give the rocking of a stop and go effect like in a cartoon. ☺️

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

                          Hey guys,

                          amazing again. I think I will study you snippets over a glass of wine this evening :)

                          stephen 1 Reply Last reply Reply Quote 0
                          • stephen
                            stephen @rownn last edited by

                            @rownn said:

                            Hey guys,

                            amazing again. I think I will study you snippets over a glass of wine this evening :)

                            🍻

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

                              @mikael said:

                              self.open = self.open == False

                              I prefer 🙄😇

                              self.open = not self.open
                              
                              stephen 1 Reply Last reply Reply Quote 1
                              • stephen
                                stephen @cvp last edited by

                                @cvp said:

                                @mikael said:

                                self.open = self.open == False

                                I prefer 🙄😇

                                self.open = not self.open
                                

                                i agree.. it still drives me nuts that i cant use..

                                boolean = !boolean
                                

                                catch myself still doing it after about a year of python lol

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

                                  For this part of the discussion alone this thread is worthy to exist. :)

                                  @rownn said: (boolean+1)%2
                                  The school math way

                                  @mikael said: boolean = boolean == False
                                  The developers way

                                  @cvp said: boolean = not boolean
                                  The pythonish way

                                  @stephen said: boolean = !boolean
                                  The beautiful but !pythonish way

                                  cvp 1 Reply Last reply Reply Quote 1
                                  • ccc
                                    ccc last edited by ccc

                                    Pythonic not Pythonish. ;-) 500+ pages of results!

                                    1 Reply Last reply Reply Quote 1
                                    • cvp
                                      cvp @rownn last edited by

                                      @rownn In my very old Fortan past, I used an integer 1 or 0, thus

                                      flag = 1 - flag
                                      
                                      mikael 1 Reply Last reply Reply Quote 2
                                      • mikael
                                        mikael @cvp last edited by

                                        @cvp, I vote for your version, which I plain forgot.

                                        1 Reply Last reply Reply Quote 2
                                        • First post
                                          Last post
                                        Powered by NodeBB Forums | Contributors