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.


    Add editor buttons, changes not reflected.

    Pythonista
    2
    10
    5621
    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.
    • Webmaster4o
      Webmaster4o last edited by Webmaster4o

      Following this example, I wanted to move the button to a different place on the bar, between the new tab button and the wrench icon. When I add the button, if I look at the list of button items before and after, I can see that it has been added, but it's not visible. How can I make it show?

      My modified code:

      # coding: utf-8
      
      from objc_util import *
      
      UIApplication = ObjCClass('UIApplication')
      UIBarButtonItem = ObjCClass('UIBarButtonItem')
      
      @on_main_thread
      def main():
      	global tabVC,overviewItem
      	rootVC = UIApplication.sharedApplication().keyWindow().rootViewController()
      	tabVC = rootVC.detailViewController()
      	tabVC.tabCollectionView().collectionViewLayout().itemSize = CGSize(328,200)
      	tabVC.tabCollectionView().contentInset = UIEdgeInsets(58,0,0,0)
      	
      	overviewItem = UIBarButtonItem.alloc().initWithImage_style_target_action_(UIImage.imageNamed_('ShowTabs'), 0, tabVC, sel('showTabOverview:'))
      	
      	#Add the item
      	rightItems=list(tabVC.navigationItem().rightBarButtonItems())
      	
      	print tabVC.navigationItem().rightBarButtonItems()
      	
      	rightItems.insert(-1,overviewItem)
      	rightItems=ns(rightItems)
      	rightItems.init()
      	tabVC.navigationItem().set_rightBarButtonItems_(rightItems)
      	print tabVC.navigationItem().rightBarButtonItems()
      	
      	#tabVC.persistentLeftBarButtonItems = [overviewItem]
      	tabVC.reloadBarButtonItemsForSelectedTab()
      	
      if __name__ == '__main__':
      	main()
      

      Mentioning people who might be able to help me:
      @omz @JonB @filippocld

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

        Don't reload the tab.

        	rightItems=list(tabVC.navigationItem().rightBarButtonItems())
        	rightItems.insert(1,overviewItem)
        	tabVC.navigationItem().rightBarButtonItems=rightItems
        

        Note this does not survive tab changes... since right buttons are not persistent.

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

          @JonB that doesn't work for me.

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

            I have now tried adding the button in both places, as well as setting the frame. No luck. The best I can do is get spaces to be inserted between the buttons.

            from objc_util import *
            
            UIApplication = ObjCClass('UIApplication')
            UIBarButtonItem = ObjCClass('UIBarButtonItem')
            
            rootVC = UIApplication.sharedApplication().keyWindow().rootViewController()
            tabVC = rootVC.detailViewController()
            
            def addButton(buttonitem):
            	global OMbuttonitem
            	#Add to main thing
            	rightItems=list(tabVC.navigationItem().rightBarButtonItems())
            	rightItems.append(buttonitem)
            	rightItems=ns(rightItems)
            	tabVC.navigationItem().set_rightBarButtonItems_(rightItems)
            	#Add to toolbar
            	OMbuttonitem=ObjCClass('OMBarButton').alloc().initWithBarButtonItem_(buttonitem)
            	OMbuttonitem.setFrame_(CGRect(CGPoint(832,22),CGSize(40,40)))
            	rightItems=list(tabVC.toolbar().rightBarButtons())
            	rightItems.append(OMbuttonitem)
            	rightItems=ns(rightItems)
            	tabVC.toolbar().setRightBarButtons_(rightItems)
            
            def reset():
            	tabVC.reloadBarButtonItemsForSelectedTab()
            	tabVC.toolbar().reloadRightBarButtonItems()
            	
            @on_main_thread
            def main():
            	tabVC.tabCollectionView().collectionViewLayout().itemSize = CGSize(328,200)
            	tabVC.tabCollectionView().contentInset = UIEdgeInsets(58,0,0,0)
            	overviewItem = UIBarButtonItem.alloc().initWithImage_style_target_action_(UIImage.imageNamed_('ShowTabs'), 0, tabVC, sel('showTabOverview:'))
            	
            	print tabVC.navigationItem().rightBarButtonItems()
            	print tabVC.toolbar().rightBarButtons()
            	addButton(overviewItem)
            	print tabVC.navigationItem().rightBarButtonItems()
            	print tabVC.toolbar().rightBarButtons()
            
            if __name__ == '__main__':
            	main()
            

            It would appear that initializing the OMBarButton does not create the ImageView present in the other views.

            >>> tabVC.toolbar().rightBarButtons()[0].recursiveDescription()
            <__NSCFString: <OMBarButton: 0x13fa71180; frame = (976 22; 40 40); layer = <CALayer: 0x13e5eefb0>>
               | <UIButton: 0x13faccfa0; frame = (0 0; 40 40); opaque = NO; layer = <CALayer: 0x13e5a0150>>
               |    | <UIImageView: 0x13f8b0960; frame = (8.5 8.5; 23 23); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x13e5b8010>>>
            >>> tabVC.toolbar().rightBarButtons()[3].recursiveDescription()
            <__NSCFString: <OMBarButton: 0x13fca3f20; frame = (832 22; 40 40); layer = <CALayer: 0x13f956440>>
               | <UIButton: 0x13f9db140; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x13f9db3e0>>>
            
            1 Reply Last reply Reply Quote 0
            • Webmaster4o
              Webmaster4o last edited by

              @omz suggestion: add to the documentation a description of how the Pythonista editor is structured internally.

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

                Are you sure set_rightBarButtonItems is doing what you think? That is I think an undocumented method. You probably want either setRightBarButtonItems_(rightItems) or just rightBarButtonItems=rightItems. (the latter was introduced in one of the later beta versions, basically any properties with both a name() and setName_() method can be set as if the name was an attribute.

                This worked for me, but realize that whenever you change a tab, the buttons get reset. I suppose it might be possible to swizzle reloadBarButtonItemsForSelectedTab to first call the original method, follwed by your special method... but this is dangerous.

                Another option, which I have used before, is to just place a button as a subview to the view, flexxed to the left of the leftmost rightbutton. This requires you to make sure you manage multiple runs of the script, to ensure you don't sdd multiple copies of the button, and if making custom actions you have to manage the globals carefully, etc.

                # coding: utf-8
                
                from objc_util import *
                
                UIApplication = ObjCClass('UIApplication')
                UIBarButtonItem = ObjCClass('UIBarButtonItem')
                
                @on_main_thread
                def main():
                	rootVC = UIApplication.sharedApplication().keyWindow().rootViewController()
                	tabVC = rootVC.detailViewController()
                	tabVC.tabCollectionView().collectionViewLayout().itemSize = CGSize(328,200)
                	tabVC.tabCollectionView().contentInset = UIEdgeInsets(58,0,0,0)
                	
                	overviewItem = UIBarButtonItem.alloc().initWithImage_style_target_action_(UIImage.imageNamed_('ShowTabs'), 0, tabVC, sel('showTabOverview:'))
                	rightItems=list(tabVC.navigationItem().rightBarButtonItems())
                	rightItems.insert(1,overviewItem)
                	tabVC.navigationItem().rightBarButtonItems=rightItems
                	#tabVC.persistentLeftBarButtonItems = [overviewItem]
                
                	
                if __name__ == '__main__':
                	main()
                
                1 Reply Last reply Reply Quote 1
                • Webmaster4o
                  Webmaster4o last edited by Webmaster4o

                  @JonB

                  1. I don't see how swizzling reloadBarButtonItemsForSelectedTab that would be "dangerous", wouldn't it go away as soon as I restart the app, or launch it "cleanly" with pythonista://? (I'll hold off until you answer in case I'm wrong)

                  2. As far as swizzling, how I might do it is rename the reloadBarButtonItemsForSelectedTab method to something else, then create a new method reloadBarButtonItemsForSelectedTab that calls the copied method first, then adds in my custom button. Is this possible?

                  3. Another approach to persistence might be to look at whatever makes the persistent left button items persistent, then mimic that.

                  4. "undocumented method" are any documented? Where is said documentation!

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

                    For standard classes, such as start with UI, check apple docs. in this case:

                    https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationItem_Class/

                    Sometimes you have to look at the superclass,etc, see the class tree at the top of the page.
                    In general, the colons get replaced with underscores. Anything listed as a property with name can be accessed as name() and can be set with either setName_() or name=someotherthing. For the most part, the objc_utils is smart enough to convert common types, including dicts, lists, numbers, and a few other types.

                    For swizzling, you need to use construct an IMP (maybe a block, or a method in a custom class, I have not tried it) and use class_replaceMethod. You would need to store the pointer to the original method before you do that, or maybe something to ensure it does not get gc'd, not sure.

                    This might not be thr right method to swizzle, I saw @steventroughtonsmith recommended a different method. The reason it gets dangerous is you have a python method which is getting called by objc. If that python method has been cleared, you will crash. you have to take care to:

                    1. Probably want to check whether it has already been swizzled, if so, don't reswizzle. Or at least undo the swizzle first, then reswizzle.
                    2. The python objects need to be stored someplace where they won't be cleared by a global clearing. meaning name them with double underscores, or include them in pythonista_startup.
                      3). You might want a del method(or maybe better yet, a weakref.ref with a callback ) which removes the button, and unswizzles things in case you manually clear the method... I have not tried this, but should be possible in recent versions where global clearing is done within python.
                    1 Reply Last reply Reply Quote 1
                    • Webmaster4o
                      Webmaster4o last edited by

                      @JonB Thanks, I'll look at this. I remember @omz had some insights on global clearing here which is helpful.

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

                        I forgot that I had already written something to do this... I updated this to be a little more generic (my specific need was to be able to run a script without global clearing, which is how i like to debug)

                        https://github.com/jsbain/objc_hacks/blob/master/apphack.py

                        create_toolbar_button places a UIButton on top of the toolbar, to the left of right buttons, with an offset as specified by index. This relies on a python action function, so your example won't work exactly like this, but you could use similar ideas. I tried to make things such that they survive multiple runs or global clears. Note that if the action uses imports from custom modules not in site-packages, things may break because the modules get cleared and the function stops working. There are workarounds, that I didn't implement.

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