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.


    Containers for photos, with scroll, drag&drop between them

    Pythonista
    2
    47
    16767
    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.
    • cvp
      cvp @jmv38 last edited by

      @jmv38 Have à look to this, far from perfect, little script

      # todo
      # - ImageView zoom and scroll
      #   - limit au min
      from  Gestures  import Gestures
      from  objc_util import *
      import os
      import photos
      import ui
      
      class PhotosPickerTableView(ui.View):
      
      	def __init__(self,w,h,d):
      		self.width = w
      		self.height = h
      		self.selected = None
      		
      		ok = ui.ButtonItem()
      		ok.image = ui.Image.named('iob:ios7_checkmark_outline_32')
      		ok.action = self.ok_action
      		self.right_button_items = (ok,)
      		
      		tbl = ui.TableView()	
      		tbl.frame = (0,0,d,h)
      		tbl.row_height = d
      		self.assets = photos.get_assets()
      		tbl.data_source = ui.ListDataSource(items=range(len(self.assets)))
      		tbl.separator_color=(1,0,0,0)
      		tbl.delegate = self
      		tbl.data_source.tableview_cell_for_row = self.tableview_cell_for_row
      		tbl.background_color = (0,0,0,0)
      		self.add_subview(tbl)
      
      		pha =ui.View(name='photo_area')		
      		pha.frame = (d,0,w-d,h)
      		self.add_subview(pha)
      		img = ui.ImageView(name='selected_photo')
      		img.frame = (0,0,w-d,h)
      		img.content_mode=ui.CONTENT_SCALE_ASPECT_FIT
      		img.background_color = (0.5,0.5,0.5,1)
      		pha.add_subview(img)
      		
      		self.tableview_did_select(tbl,0,0)
      
      		self.photo_scale = 1		
      		self.g = Gestures()
      		self.pinch = self.g.add_pinch(img,self.did_pinch)		
      		self.pan   = self.g.add_pan(img,self.did_pan,1,1)	  
      		
      	def did_pinch(self, data):
      		if data.state == 1:
      			self.scale_at_begin = self.photo_scale
      		s = data.scale
      		s_new = self.photo_scale * s
      		#print('did_pinch: data.scale=',s,'scale:',self.photo_scale,'-->',s_new,'data.state=',data.state)
      		if s_new < 1 and self.scale_at_begin >= 1:
      			# minimum size 1
      			s_new = 1
      			s = 1 / self.photo_scale
      
      		#if s_new > 20:
      		#	return
      		self.photo_scale = s_new
      		#print('scale',s_new)
      		self['photo_area']['selected_photo'].transform = self['photo_area']['selected_photo'].transform.concat(self['photo_area']['selected_photo'].transform.scale(s,s))
      		self.pinch.scale = 1.0
      		
      	def did_pan(self, data):
      		#print('before',self['photo_area']['selected_photo'].frame)
      		o = data.translation
      		o = o * self.photo_scale	# to accelerate move if zoom
      		#print('translation',o)
      		if ((self['photo_area']['selected_photo'].frame[0]+o[0]) > 0) or ((self['photo_area']['selected_photo'].frame[1]+o[1]) > 0):
      			return
      		self['photo_area']['selected_photo'].transform = self['photo_area']['selected_photo'].transform.concat(self['photo_area']['selected_photo'].transform.translation(*o))
      		self.p = CGPoint(0,0)
      		self.pan.setTranslation_inView_(self.p,self.pan.view())
      		#print('after',self['photo_area']['selected_photo'].frame)
      		
      		
      	def ok_action(self,sender):
      		self.close()
      		
      	def tableview_did_select(self, tableview, section, row):
      		ui_image = self.assets[row].get_ui_image()
      		self['photo_area']['selected_photo'].image = ui_image
      		self.selected = row
      		
      	def tableview_cell_for_row(self,tableview, section, row):
      		cell = ui.TableViewCell()
      		v = 0.4 + (row % 2)*0.4
      		cell.bg_color = (v,v,v,v)
      		selected_cell = ui.View()
      		#selected_cell.bg_color = 'blue'
      		selected_cell.border_width = 2
      		selected_cell.border_color = 'blue'
      		cell.selected_background_view = selected_cell
      		photo = ui.ImageView()
      		photo.frame = (0,0,tableview.row_height,tableview.row_height)
      		photo.image = self.assets[row].get_ui_image()
      		photo.content_mode = ui.CONTENT_SCALE_ASPECT_FIT
      		cell.content_view.add_subview(photo)
      		return cell
      		
      def main():
      			
      	# Hide script
      	w,h = ui.get_screen_size()
      	mi = min(w,h)*0.9
      	my_back = PhotosPickerTableView(mi,mi,100)
      	my_back.background_color='white'
      	my_back.name = 'Photos Picker via TableView'	
      	
      	my_back.present('sheet',hide_title_bar=False)	
      	my_back.wait_modal()
      		
      # Protect against import	
      if __name__ == '__main__':
      	main()	
      
      jmv38 1 Reply Last reply Reply Quote 0
      • jmv38
        jmv38 @cvp last edited by

        @cvp wow! that was fast!!!
        thanks a lot. This is pretty much exactly the first container i needed.
        Now i should add other empty containers (perpendicular) and the drag and drop mechanism.
        This really a good kickstart, thanks!

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

          @jmv38 That has been fast because I already had written it 😂

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

            @jmv38 Little code, extracted from a big program, to show how to select a grid type.
            Some code may seem strange because used by removed parts.
            My big program, not yet ended, is used by my wife to join pictures in several different grids with moving lines. Program not completely operational and with' French messages.

            import ImageFont
            import ui
            
            def my_list_popover(elements,val=None,x=None,y=None,color='white',multiple=False,title=None,selectable=True):
            	global grids
            	class my_list(ui.View):
            		def __init__(self,selected):
            
            			# In multiple selection mode, an "ok" button is needed
            			if multiple:
            				ok_button = ui.ButtonItem()
            				ok_button.title = 'ok'
            				ok_button.action = self.ok_button_action
            				self.right_button_items = [ok_button]
            				
            			# ListDataSource for clients TableView
            			elements_lst = ui.ListDataSource(items=lst)
            			# ListDataSource has no attribute "name"
            			elements_lst.delete_enabled = False
            			elements_lst.move_enabled = False
            			elements_txt = ui.TableView(name='elements_txt')
            			elements_txt.allows_multiple_selection = multiple
            			elements_txt.allows_selection = selectable
            			elements_txt.text_color = 'black'
            			elements_txt.font= ('Courier',12)
            			if elements == grids:
            				elements_txt.row_height = 128
            			else:
            				elements_txt.row_height = elements_txt.font[1] * 2
            			h = len(lst) *	elements_txt.row_height
            			font = ImageFont.truetype('Courier',16)
            			w = 0
            
            			if elements == grids:
            				ws, hs = ui.get_screen_size()
            				w = int(elements_txt.row_height*ws/hs)
            				h_ele = elements_txt.row_height
            				h_max = h_screen*0.9
            			else:
            				for element in lst:
            					w_ele,h_ele = font.getsize(element)
            					if w_ele > w:
            						w = w_ele
            				w = w + 32
            				h_max = h_screen*0.8
            			elements_txt.content_size = (w,h)
            			if h > h_max:
            				h = h_max
            			w_max = w_screen - 20
            			if w > w_max:
            				w = w_max
            			elements_txt.frame = (4,4,w,h)	
            			#elements_txt.border_color = 'blue'
            			#elements_txt.border_width = 3
            			elements_txt.data_source = elements_lst
            			elements_txt.data_source.tableview_cell_for_row = self.tableview_cell_for_row
            			if not multiple:
            				if selected >= 0:
            					elements_txt.selected_row = (0,selected)
            			else:
            				if selected_lst != []:
            					elements_txt.selected_rows = selected_lst
            						
            			elements_txt.delegate  = self
            			self.add_subview(elements_txt)	
            			self.width = elements_txt.width   + 8
            			self.height = elements_txt.height + 8
            			self.corner_radius = 20
            			self.border_color = 'blue'
            			self.border_width = 2
            			self.background_color = color
            			if title and multiple:
            				# title bar displayed in multiple
            				self.name = title
            			self.pending = []
            
            		def ok_button_action(self,sender):
            			lst.append(str(multiples)) # '['xxxx','yyyy']'
            			v.close()
            			
            		def tableview_did_deselect(self, tableview, section, row):
            			# Called when a row was deselected (multiple only)
            			if multiple:
            				multiples.remove(lst[row])
            				#print(multiples)
            				
            		def tableview_did_select(self, tableview, section, row):
            			# Called when a row was selected
            			if multiple:
            				multiples.append(lst[row]) 				# ['xxxx','yyyy']
            				#print(multiples)
            			else:
            				lst.append(lst[row])
            				v.close()
            		
            		def tableview_accessory_button_tapped(self, tableview, section, row):
            			# Called when the accessory (ex: info) tapped
            			if multiple:
            				multiples.append('tapped'+lst[row])
            				lst.append(str(multiples)) 		# '['xxxx','yyyy','tapped.....']'
            			else:
            				lst.append('tapped'+lst[row]) # 'tapped...'
            			v.close()
            			
            		def tableview_cell_for_row(self, tableview, section, row):
            			cell = ui.TableViewCell()
            			selected_cell = ui.View()
            			selected_cell.border_color = 'black'
            			selected_cell.border_width = 2
            			selected_cell.corner_radius = 10
            			data = tableview.data_source.items[row]
            			if elements == grids:
            				selected_cell.bg_color = 'lightgray'
            			else:
            				selected_cell.bg_color = 'cornflowerblue'
            			cell.selected_background_view = selected_cell
            
            			cell.text_label.text = data
            			cell.text_label.alignment = ui.ALIGN_LEFT
            			ch1 = cell.text_label.text[0]						# 1st char of text
            			if ch1 == '🈸':
            				g = ui.Button()
            				g.background_color = 'white'
            				g.image = ui.Image.named('iob:grid_32')
            				g.width  = 28
            				g.height = 28
            				g.x = 12
            				g.y = 0
            				cell.content_view.add_subview(g)	
            			cell.text_label.text_color = 'black'
            			cell.bg_color = color
            				
            			if elements == grids:
            				cell.text_label.text = ''
            				h = tableview.row_height
            				w = tableview.width
            				rows = data.split('/')
            				ny = len(rows)
            				x0 = 2
            				y0 = 5
            				dy = int((h-2*y0)/ny)	
            				y = y0
            				bs = {}
            				for iy in range(0,ny):				# loop on rows
            					x = x0
            					nx = len(rows[iy])					# should be equal on each row
            					dx = int((w-2*x0)/nx)	
            					for ix in range(0,nx):
            						c = rows[iy][ix]					# cell n°
            						if c not in bs:
            							b = ui.Button()
            							b.enabled = False
            							if ix < (nx-1):
            								wx = dx
            							else:
            								wx = (w-2*x0) - x # force all last right same
            							b.border_width = 1
            							b.border_color = 'blue'
            							b.title = c
            							b.tint_color ='blue'
            							b.frame = (x,y,wx,dy)
            						else:
            							# split cell on several rows or columns: dx or dy will change
            							b = bs[c]
            							if b.y == y:
            								b.width = (x + wx - 1) - b.x + 1 # same row
            							else:
            								b.height = (y + dy - 1) - b.y + 1 # same col
            						bs[c] = b							
            						k = 'Photo ' + c
            						cell.content_view.add_subview(b)
            						x = x + dx - 1
            					y = y + dy - 1
            				del bs
            			return cell		
            	
            	lst = []
            	i = 0
            	selected = -1
            	if type(elements) is dict:
            		elements_list = sorted(elements.keys())
            	elif type(elements) is list:
            		elements_list = elements
            	if multiple:
            		# val = ['xxx','yyy']
            		if val:  # not None
            			val_lst = ast.literal_eval(val) # convert str -> dict
            		else:
            			val_lst = []
            		selected_lst = []
            		multiples = []
            	for element in elements_list:
            		lst.append(element)
            		if not multiple:
            			if element == val:
            				selected = i
            		else:
            			if element in val_lst:
            				selected_lst.append((0,i))
            				multiples.append(element)
            		i = i + 1
            	w_screen,h_screen = ui.get_screen_size()
            	if not x:
            		x = int(w_screen/2)
            	if not y:
            		y = int(h_screen/2)
            	v = my_list(selected)
            	# lst already copied into elements_lst, thus we can change lst
            	# to contain iniital value in case of tap outside
            	if selected == -1:
            		lst.append('')
            	else:
            		lst.append(lst[selected])
            	if not multiple:
            		v.present('popover',popover_location=(x,y),hide_title_bar=True)
            	else:
            		v.present('popover',popover_location=(x,y),hide_title_bar=False)
            	v.wait_modal()
            	return lst[len(lst)-1]	# return last
            	
            def main():
            	global grids
            	w,h = ui.get_screen_size()
            	mi = min(w,h)*0.9
            	mv = ui.View()
            	mv.frame = (0,0,mi,mi)
            	mv.name = 'Example of grids button'
            	mv.background_color = 'white'
            	grid = ui.ButtonItem()
            	grid.image = ui.Image.named('iob:grid_32')
            	grids = ['1', '12', '1/2','123', '11/23', '12/33',  '1/2/3', '12/32', '12/13', '1234', '111/234', '12/34', '123/444', '11/22/34', '12/33/44', '11/23/44', '1/2/3/4', '12/13/14', '12/32/42', '11/23/43', '123/143', '123/425', '1234/5678/9abc/defg', '12345/67890/abcde/fghij/klmno'] #, '5', '1/4', '4/1', '2/3', f'3/2', '1/1/3', '1/3/1', '2/1/2', '2/2/1', '1/2/2', '3/1/1','1/1/1/2', '1/1/2/1', '1/2/1/1', '2/1/1/1', '1/1/1/1/1']		
            	def grid_action(sender):
            		x = mi - 50
            		y = 50
            		g = my_list_popover(grids,-1,x=x,y=y,color='white')
            	grid.action = grid_action
            	mv.right_button_items = (grid,)	
            	mv.present('sheet')
            	
            # Protect against import	
            if __name__ == '__main__':
            	main()
            
            1 Reply Last reply Reply Quote 0
            • cvp
              cvp last edited by cvp

              I could agree to put in GitHub my program but I don't want to provide any support because it often crashes with big or multiple photos 😢 and I don't have enough free time to debug it.

              jmv38 1 Reply Last reply Reply Quote 0
              • cvp
                cvp last edited by

                See also PhotosPickerView.py

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

                  @cvp thanks for the 2nd example, it is inspiring.

                  Actually i dont use grids i prefer to place and zoom and cut each photo manually.

                  If you put your whole program in github i certainly wont demand support!

                  I think I’ll post my work step by step in gists and this forum as I progress. Not sure how fast I’ll go though...

                  Btw, I am french too (Grenoble) so no pb with the langage. I’ll continue anyway to write in english by respect for people in this forum.

                  Thanks.

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

                    @cvp from the doc, it seems the tableView is vertical only. Is there a way to have is horizontal too? Initially i wanted my camera roll container to be vertical, but the photo group containers to be horizontal (and themselves inside a scrollable vertical container too).
                    So i should pbly use a scrollview instead for my base photo container?

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

                      @cvp mmm PhotosPickerView.py seems a bit over my head, with all this objC functions...

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

                        @jmv38 said:

                        f you put your whole program in github i certainly wont demand support!

                        here

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

                          @jmv38 said:

                          seems a bit over my head, with all this objC functions...

                          It is not so complex as it seems...

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

                            @cvp thanks for juxtapositionDePhotos.py
                            all the bricks i need are there
                            => this is really going to help me.
                            thanks.

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

                              @jmv38 quick and dirty horizontal scroll view

                              import photos
                              import ui
                              
                              def main():
                              	assets = photos.get_assets()
                              	#assets = photos.pick_asset(assets=assets,multi=True)
                              	d = 100
                              	wsv = min(400,d * len(assets))
                              	sv = ui.ScrollView()
                              	sv.frame = (0,0,wsv,d)
                              	x = 0
                              	for i in range(0,len(assets)):
                              		b = ui.Button(name=str(i))
                              		#b = ui.ImageView(name=str(i))
                              		b.border_color = 'blue'
                              		b.border_width = 1
                              		ui_image = assets[i].get_ui_image().with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
                              		w,h = ui_image.size
                              		w = d * w/h
                              		b.frame = (x,0,w,d)
                              		b.image = ui_image
                              		def b_action(sender):
                              			sender.superview.name = 'tapped=' + sender.name
                              		b.action = b_action
                              		sv.add_subview(b)
                              		x = x + w
                              	sv.content_size = (x,d)
                              	sv.present('sheet')
                              
                              if __name__ == '__main__':
                              	main()
                              

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

                                @cvp excellent! thanks.
                                It is nice to have the function in a really small piece of code.
                                I had to limit the number of photos because i have 1600 and that crashes my app. I’ll manage that.

                                Another thing: for the interractions between panels i learned (from coding in Codea) that it is much easier to wire things together via a register/trigger message system. Do you have something available?

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

                                  @jmv38 said:

                                  it is much easier to wire things together via a register/trigger message system. Do you have something available?

                                  No, never done like that. I always use normal way of ui. Good luck

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

                                    @jmv38 said:

                                    limit the number of photos because i have 1600

                                    Perhaps, use collections or get_moments

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

                                      @cvp here an example of a very simple event class i found on google and modified for my needs. I also added an example of usage.

                                      class EventHook(object):
                                      
                                          def __init__(self):
                                              self.__handlers = []
                                      
                                          def __iadd__(self, handler):
                                              self.__handlers.append(handler)
                                              return self
                                      
                                          def __isub__(self, handler):
                                              self.__handlers.remove(handler)
                                              return self
                                      
                                          def __call__(self, *args, **keywargs):
                                              for handler in self.__handlers:
                                                  handler(*args, **keywargs)
                                      
                                          def clearObjectHandlers(self, inObject):
                                              for theHandler in self.__handlers:
                                                  if theHandler.im_self == inObject:
                                                      self -= theHandler
                                                      
                                      if __name__ == '__main__':
                                        class Watcher():
                                            def __init__(self):
                                                self.incoming = EventHook()
                                                self.leaving = EventHook()
                                        
                                        class Greeter():
                                          def __init__(self,me):
                                            self.me = me
                                          def sayHello(self, name):
                                            print('{}: hello Mister {}'.format(self.me,name))
                                          def sayGoodbye(self, name):
                                            print('{}: goobye Mister {}'.format(self.me,name))
                                            
                                        class Maid():
                                          def __init__(self,me):
                                            self.me = me
                                          def sayHello(self, name):
                                            print('{}: hello Mister {}'.format(self.me,name))
                                          def sayGoodbye(self, name):
                                            if name != 'DSK':
                                              print('{}: goobye Mister {}'.format(self.me,name))
                                            else:
                                              print('{}: You f... s.. o. a b...!'.format(self.me))
                                        
                                        from console import clear
                                        clear()
                                        Isee = Watcher()
                                        deskMan = Greeter('desk man Georges')
                                        maid = Maid('maid Sandra')
                                        
                                        # make the connections:
                                        Isee.incoming += lambda name: print('\nM. {} is comming...'.format(name))
                                        Isee.incoming += deskMan.sayHello
                                        Isee.incoming += maid.sayHello
                                        
                                        Isee.leaving += lambda name: print('\nM. {} is leaving...'.format(name))
                                        Isee.leaving += maid.sayGoodbye
                                        Isee.leaving += deskMan.sayGoodbye
                                        
                                        # remove listener from the event
                                        
                                        # fire event
                                        Isee.incoming('Clinton')
                                        Isee.leaving('Clinton')
                                      
                                        
                                      
                                      cvp 1 Reply Last reply Reply Quote 0
                                      • cvp
                                        cvp @jmv38 last edited by

                                        @jmv38 Too complex for me 😀

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

                                          @cvp i can understand what you feel: it is exactly what i thought the first time saw this! It took me several months to start to understand it, but once i did, bam!, it opened a huge box of possibilties because it made a complex project so much simpler. But I wont try to convert you, you’ll see for yourself as i use it in my project if you think it is usefull...
                                          Thanks.

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

                                            @cvp i am in the final polishing phase of my project.
                                            I have pb with button icons: they render in color when i want a gray rendering (the one shown in the editor).
                                            my code:

                                            img = ui.Image('iow:refresh_32').with_rendering_mode(ui.RENDERING_MODE_TEMPLATE)
                                                clear_button.image = img
                                            

                                            any suggestion?
                                            thanks.

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