• @Enez-Houad before the present() yes, not after (I think)

    import ui v = ui.View() v.frame = (0,0,600,100) v.present('sheet')
  • You may be interested in my objc viewbrowser:
    https://github.com/jsbain/viewbrowser

    You can present your view, then traverse the heirarchy to see all of the UIViews. Tapping an item highlights it onscreen.
    Also, any action/targets are shown as little chain icons, which you can tap and it prints to the console the details on the target, selector, event codes, etc. Tapping the info button shows all of the objc properties, which is also navigatable.

  • @shinyformica try this, it is the way omz wrote dialog: using wait_modal and resetting the view just after.

    import ui from objc_util import * class Popover(ui.View): def __init__(self, *args, **kws): ui.View.__init__(self, *args, **kws) self.width = 200 self.height = 50 self.label = ui.Label() self.label.text = "test" self.add_subview(self.label) self.label.x = 0 self.label.y = 0 self.label.width = self.width self.label.height = self.height def will_close(self): print("popover closing") class MyView(ui.View): def __init__(self, *args, **kws): ui.View.__init__(self, *args, **kws) self.popover = None self.button = ui.ButtonItem() self.button.title = "Show Popover" self.button.action = self.showPopover self.right_button_items = (self.button,) def showPopover(self, sender): print("show popover") if self.popover is None: self.popover = Popover() self.popover.present(style="popover", popover_location=(self.width-100,50), hide_title_bar=True) self.popover.wait_modal() self.popover = None def run(): v = MyView() v.present('full_screen') if __name__ == '__main__': run()```
  • Thanks everyone,
    @JonB You cleared my doubt on @ui.in_background, Now I'm using threading instead of ui.in_background

    import ui,threading as thr class view(): def __init__(self): self.flag = True content = ui.TableViewCell() play = ui.Button(frame = (259,0,50,content.height)) pause = ui.Button(frame = play.frame) play.image = ui.Image('iob:ios7_play_256') pause.image = ui.Image('iob:pause_256') pause.hidden = True play.action = self.Play pause.action = self.Pause label = ui.Label(frame =(0,0,0,content.height)) label.touch_enabled = False label.background_color = 'blue' self.play = play self.pause = pause self.label = label self.flag = False content.content_view.add_subview(play) content.content_view.add_subview(pause) content.content_view.add_subview(label) self.content = content def table(self): return self.content def Play(self,sender): t = thr.Thread(target=self.Playy,args=(sender,)) t.daemon = True t.start() def Pause(self,sender): t = thr.Thread(target=self.Pausee,args=(sender,)) t.daemon = True t.start() def Playy(self,sender): sender.hidden = True self.pause.hidden = False import time for a in range(320): self.label.width = a time.sleep(0.1) if self.flag: self.flag = False return def Pausee(self,sender): sender.hidden = True self.play.hidden = False self.flag = True class Llist(): def tableview_number_of_rows(self,t,s): return 40 def tableview_cell_for_row(self,t,s,r): return view().table() a=ui.TableView() a.data_source = Llist() a.allows_selection = False a.present()
  • @yas self is a delegate... It has no bgcolor!

    There is not an easy way to change the section header background color. There are some ways using objc_util, but you may want to crawl first before you walk... .

    The easiest approach is to keep track of headers on your own, and just create a cell instead of a section header.

  • Here is a way to capture all notifications:
    https://github.com/jsbain/objc_hacks/blob/master/notification_capture.py

    This could be modified to observe a specific object, selector, etc. I believe there is a notification when a view is shown, but i didnt see an obvious one when the X is pushed.

    The confusing thing about what you are asking is that sheet is already basically modal, since it blocks anything else happening. in which case wait_modal might be what you want.

    For truly non-modal "windows", you might consider the Overlay, found here. This lets you create resizable views that float, and you can use connect to register actions that get called when the view is closed.

    Finally, it would be possible to delete the standard X, and replace it with your own button.

    v.objc_instance.superview().superview().superview().subviews()[1].subviews()[1]

    gives me the X button on ipad when presenting as sheet. This might be different on iPhone, and might be different if you have leftbuttonitems or rightbuttonitems defined.

    But this lets you, for instance, hide the default X, and define your own using right_button_items.

    v=ui.View() def c(sender): v.close() print('closed') v.right_button_items=[ui.ButtonItem(action=c,title='X')] v.present('sheet') v.objc_instance.superview().superview().superview().subviews()[1].subviews()[1].hidden=True
  • Thank u so much!!! I was going crazy already...

  • Those are custom attributes not custom arguments. what happens is the view is created, with no input args:

    v=PathView()

    then, arguments are set:

    v.width =.... v.bc="oooo"

    and so on.
    if you need to take action based on the custom attributes, you need to have those as @property's, to implement a setter.

  • @psyFi, call size_to_fit on the label. Your enclosing view probably needs to be custom so that you can implement the layout method where you first size the label and then position the image. If your text changes a lot, you might also implement a text property for the enclosing class, where you can set the label text and then call size_to_fit on the enclosing view, or just the repositioning code directly.

  • Ah. Everything works now. Thank you!

  • If not running the Pythonista 3 beta, you can get periodic updates with the code below.

    This is not my idea, I just simplified an idea in the TimedRefreshView.py example program, written by cclauss, that I found at:
    https://github.com/cclauss/Pythonista_ui/blob/master/TimedRefreshView.py

    My program, named "say_random_digit.py", says a random digit from 0 to 9 every 5 seconds.

    I had to change the data member "self.update_interval" to "self.updatex_interval" and the method "update" to "updatex" so as not to conflict with the names in the Pythonista 3 beta, which I am currently running.

    I like having this functionality built into the ui.View class much more than having to implement it, so I look forward to the beta becoming the released product.

    import ui import threading import speech from random import randint class TimedUpdateView(ui.View): """ This class contains a method named updatex, which is periodically called. """ def __init__(self): self.updatex_interval = 5 self.update_after_delay() def updatex(self): """ Say a random digit from 0 to 9 every 5 seconds. """ speech.say('%s' % (randint(0, 9))) def update_after_delay(self): """ This method calls the updatex method periodically """ self.updatex() update_thread = threading.Timer(self.updatex_interval, self.update_after_delay).run() if __name__ == "__main__": v = TimedUpdateView() v.present('sheet')
  • In the line that is failing, sender is a ui.Button but you have defined CountDown as a standalone function, not as a method of ui.Button().

    Try CountDown(sender) instead of sender.CountDown()

    Also code format requires the three backticks are on a line by themselves with no other text before or after them on the same line.

  • JonB, thank you very much! That worked. I had tried to write some code that iterated over Obective C UI items, trying to find the item that mattered and changing it's color, but I never got it to work.

    Eventually, I'll try to figure out the hierarchy using your code as a guide, and match to the right names so that the code is somewhat more robust to version changes. For now, I added the following method to my ui.View-derived class, that allows me to change the title bar color dynamically.

    def change_title_bar_color(self, hex_color): """ Change the title bar color to the passed color. """ vv = ObjCInstance(self) bar_bckgnd = vv.superview().superview().superview().subviews()[1].subviews()[0] bar_bckgnd.backgroundColor = UIColor.colorWithHexString_(hex_color)
  • Thank you mikael and JonB for the help.

    @JonB said:

    You sure you have update_interval set?

    Yes. I have:

    self.update_interval = 1.0

    in my derived class's __init__ method.

    It likely I did something to break code that was working, but I don't see anything wrong. I'll debug my application - if this works for others, then I'm sure I introduced a bug somewhere. It's odd though, because I only made two very small changes and then updated the OS.

    Next time, before I upgrade the OS, I'll test before to make sure it's the OS that breaks code, and not my code changes.

  • @ihf, I just updated the example to have a copy to clipboard and clear methods and menu items. Not sure they are useful, but good to see how to add menu Items anyway, if you haven't played with them yet.
    My example is not as compact looking as it could be, but I try and following the PEP8 style now. Eg, only one import per line, 2 lines after the imports etc.

    import ui import clipboard import console class MyConsole(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.tv = None self.make_view() def make_view(self): tv = ui.TextView(frame=self.bounds, flex='wh', font=('Menlo', 24), editable=False, ) self.tv = tv self.add_subview(self.tv) ''' Create 2 ui.ButtonItem to insert into the menubar. ButtonItem's are one of those pesky ui Items that are not subclassed from ui.View Its not a big deal, just good to know they differ from ui.Button. You can see in the docs. Below, I am adding the 2 menu buttons to the right side as it seems to make sense. There is a method left_button_items also. ''' mbtn_clear = ui.ButtonItem(title='Clear', action=self.clear_console, tint_color='red') mbtn_copy = ui.ButtonItem(title='Copy', action=self.copy_console) self.right_button_items = (mbtn_clear, mbtn_copy) def write_line(self, txt): self.tv.text += "{}\n".format(txt) def clear_console(self, sender=None): ''' sender is set to None so sender is not required to call this method. The action from the menu ButtonItem needs to see it there though. But it means you can call this method on the object withouut having to pass a sender. i.e if youwanted to clear the console from your code rather than the menu button action. ie. obj.clear_console() will work. Same goes for the copy_console method below. ''' self.tv.text = "" def copy_console(self, sender=None): clipboard.set(self.tv.text) console.hud_alert('{} characters copied.'.format(len(self.tv.text))) if __name__ == '__main__': f = (0, 0, ui.get_screen_size()[0], ui.get_screen_size()[1]) v = MyConsole(frame=f, name='My Full Sceen Console') v.present(style='', animated=False) for i in range(100): v.write_line('line-{}'.format(i))
  • is this inside a button action perhaps?

    You cannot put a blocking function inside a button action, or other methods that are called on the ui thread. The calls to the form need to be called as @ui.in_background if that is the case.

    There may be an issue which I have noticed recently when dealing with for examples the photos picker, that presenting a view while another is closing might lead to issues. yOu might try adding a time.sleep() after wait_modal, since this implies you have another view which is getting closed right before this chunk executes.

    It might be helpful to see the entire method, or a complete standalone example that shows the issue.

  • Thanks for those suggestions, learning about threading was very helpful.

    Apparently, the while loop was working the whole time! I feel very silly now. It didn't appear to be working because it always updated the decibel level to be -120 (which is unusual because -160 is typically silence). It is my hypothesis that Apple restricts widgets in certain ways like recording (which is odd because I've used taptic feedback in a widget before). (Ex: the Shazam app has a widget, but it has to open the app to work.) Anyway, my new problem will be figuring out if it's possible to collect and analyze the recording elsewhere and pass that information to the widget through some sort of callback url.

  • Thank you very much!
    I wasn't checking for a few days, now I have even someone writing code for me, wasn't intended by request - @abcabc thank you very much for your effort, you shouldn't have :)
    also a big thank you @mikael! Made things clearer.
    I think I get it from here. As I'm already thanking, some heartful of thankyous also to @omz for the great Pythonista app!

Internal error.

Oops! Looks like something went wrong!