omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular
    1. Home
    2. zrzka

    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.


    • Profile
    • Following 0
    • Followers 3
    • Topics 16
    • Posts 263
    • Best 59
    • Controversial 0
    • Groups 0

    Robert Vojta

    @zrzka

    82
    Reputation
    3485
    Profile views
    263
    Posts
    3
    Followers
    0
    Following
    Joined Last Online
    Website robertvojta.com Location Hradec Králové Age 45

    zrzka Unfollow Follow

    Best posts made by zrzka

    • How to ask a good question

      Here's a simple document describing how to ask a good question and why it is so important to include as many details as possible. I still feel that some people don't understand it and it's a good thing to have kind of reminder. Would be nice to have something like this as sticky topic (like Slack topic).

      Forum

      This forum is not a paid support. Pythonista, Editorial, ... is developed by @omz - single person. Ole tries to help everyone here, but day has 24h and he is not simply capable to answer all questions. He's fixing bugs, working on new features and also he has a life outside of Pythonista and Editorial.

      We have a lot of volunteers trying to help as well. These people are doing it for free, in their spare time, ... and they decide if they're going to help or not. This decision is based on a lot of things - your attitude, time you spent on your problem, attempts you made, ... You're going to receive help if you're polite, if you show some work to solve your problem, ... It's up to you. These people invest their time, you should invest yours into detailed description of your issue at least.

      Another important fact is that these volunteers live around the globe. You can have morning, but people capable to solve your issue can be in bed, ... Always think about this and wait for 24h before you ping your topic again, ... I can write a comment at 11pm, go to bed "in the middle of conversation" and you can feel I left you. That's not the truth, I just did go to bed for example. Others will help or I'll when I wake up.

      And the last thing is English language. It's not that hard language like Czech, Chinese, ... for example, but no one's perfect and there're different levels of English around. Keep this in mind, some sentences can sound as a personal attack, but it can be just something like not a very good knowledge of English, ...

      People are going to help, they really do, but they have a freedom to choose in what thing they're going to invest their time. Be polite, don't use words like hell, stupid acting python ide, ... if you're seeking for help.

      Short version

      What you should provide?

      • Device, iOS version, Pythonista version
      • Explain what you're trying to achieve, what's your goal
      • Attach your complete project, not just part of it
      • Include all related testing data (input files, ...) as well
      • Include complete traceback if it crashes
      • Do not try to summarise any of it, you can make a mistake

      Do I have to include everything every single time I ask? Well, it depends on the problem you're trying to solve. It's not necessary sometimes. Just stop for a while, think about it and provide whatever you think it's necessary. More is always better than less.

      Longer version

      Device, iOS version, Pythonista version

      There're differences in iOS behavior when it comes to devices like iPad vs iPhone. Same applies to different iOS versions on the same device. And same applies to different Pythonista versions on the same device and iOS.

      You should update Pythonista to the latest available version before you ask your question. Issue can be solved in the newest version.

      Because of all these things, you should include device information, iOS version and Pythonista version. It's more important in bug reports, but can help with your issue as well.

      Explain what you're trying to achieve, what's your goal

      It happens and it's not so rare. Sometimes people tend to use wrong tool a.k.a. module, approach, ... You can learn new things, get better answer / solution if you explain what you're trying to achieve.

      Attach your complete project, not just part of it

      What is a project in this context? Everything what is related to your problem. It can be single script or bunch of scripts (modules), input data (files, images, ...). It's always good to attach example one can run and which shows symptoms you're trying to solve. It leads to a quicker solution.

      It's not so rare that your issue lies elsewhere than you think. You can have two functions, trying to solve why the second one doesn't work, you paste it here and the problem can be in the first one for example.

      Also Pythonista behaves differently when your script is saved in This iPad, in site-packages, ...

      Don't try to be smart, don't hide what you did, don't feel ashamed, no one's perfect. Everyone's making mistakes. I made lot of them during my career and some of them were kind of fatal. It's a life, never ending learning process.

      Or you can create a Minimal, Complete, and Verifiable example as @dgelessus did point out in the comments below.

      Include all related testing data (input files, ...) as well

      To reproduce your problem we need your input data. Because your script can work with another set of input data. Again, this is not rare. Algorithm can be written in a wrong way and it can work when we're going to test it - input data differs - yours and ours.

      Yes, it's pretty clear what's the issue sometimes. But sometimes not, can be nasty one. Less information you provide, more time it takes.

      Include complete traceback if it crashes

      The worst thing you can do is to summarise traceback with your words. Never do this. Attach verbatim copy of it.

      Do not try to summarise any of it, you can make a mistake

      If you're not able to solve your issue, you're likely going to make a mistake in your summary as well. Code, traceback, input data, ... Please, don't do this, it just slows things down. Reproducibility and accurate info is crucial. Remember what I wrote about volunteers living around the globe? If you do not include precise description it can prolong solution for several days. We're going to ask, then bed, you'll answer, but we're sleeping, ...

      Invest some time, because you're asking us to invest our time to solve your problem. Answer questions we ask even if they sound like completely unrelated ones to you. They can lead to a solution you don't see yet.

      How to

      Code in the post

      Forum supports Markdown and you can include code in this way:

      Three backticks alone or followed by python, json, ..., your code and another three backticks. You'll get nicely formatted, highlighted, ... code, json, text, ...

      Share via Gist

      Create GitHub account and follow instructions. Do not update Gist when you already linked it here in the forum. Do you want to show something new you just did? Create another one.

      Gist is preferred for longer code, one gist can have multiple files, ...

      Attach image

      It's also helpful to attach an image sometimes. Unfortunately, forum doesn't support image uploading. You can upload image to any service and just paste link.

      If you want to include it in the post (not just link) do it via Markdown:

      ![](http://some-link-to-an-image)
      

      In case of Dropbox, copy image link, replace dl=0 at the end with raw=1:

      ![](https://www.dropbox.com/s/eolt6blu2pndow1/forum-expand-error.jpeg?raw=1)
      

      Traceback

      Do you see something like this? Tap on the expand button.

      Tap on the Print Traceback row.

      Close exception overlay and copy & paste the whole traceback here. Use three backticks.

      Unsure what to copy? Clear console, reproduce your problem again and copy & paste whole console. You'll known that you're going to copy & paste relevant information only.

      Conclusion

      I wrote this, because I see that people are doing same mistakes again and again. I'm not forcing anyone to follow these instructions. If you do, you'll receive help more likely. If you don't, it's your problem which is not going to be solved. Think about it. Get a life now, go and grab some beer :)

      posted in Pythonista
      zrzka
      zrzka
    • RE: External files: how does that work?

      I highly recommend to read Ticci's:

      • iOS 11: The MacStories Review - Files
      • And then Google for Open in Place to learn more.

      Open in Place

      To make it short, this allows you to open file from Dropbox in Pythonista for example. It also means that file is not copied to the Pythonista, but Pythonista is allowed to read / save from / to this file directly in the Dropbox location. That's the reason for in Place. By opening this file you're giving Pythonista access to modify this file, even delete it.

      External Files

      External files utilises Open in Place feature. What you see in this section is a bookmark. When you tap on it, bookmark is resolved to the real location and Pythonista opens this file. If you swipe on a file in external files section, you'll see that the destructive button title says Remove. And when you tap on it, just this bookmark is removed from Pythonista, not the original file (Dropbox location). It just stays there. Want to trash this file for real? Open Dropbox and trash it there.

      However, there's one exception, slightly different behaviour.

      EXTERNAL FILES - Top level item

      • Launch Pythonista
      • Reveal file browser
      • Tap on Open... in EXTERNAL FILES
      • Open any file
      • This file appears in EXTERNAL FILES
      • Swipe left and Remove button appears
      • This Remove button removes bookmark, not the file from Dropbox

      Same behaviour applies to folder - if you swipe left on it and it's a top level item.

      EXTERNAL FILES - Nested item

      • Launch Pythonista
      • Reveal file browser
      • Tap on Open... in EXTERNAL FILES
      • Open any folder
      • This folder appears in EXTERNAL FILES
      • Tap on this folder in Pythonista to see the listing of files / folders under it
      • Swipe left on folder / file and Trash button appears
      • This Trash button trashes the file / folder for real, even in the Dropbox location

      To sum it up, there's difference between:

      • Top level item - swipe left - Remove - trashes bookmark only, original file untouched
      • Nested item - swipe left - Trash - trashed original file / folder for real

      However there're still issues and Dropbox is not the perfect example, because of #509. And some others. It needs some time to let the things settle down.

      posted in Pythonista
      zrzka
      zrzka
    • RE: Importing files, Github, and my work flow - I need advice.

      @brumm there's no need to import / open in Working Copy. Just do this ...

      Pythonista

      • Reveal file browser
      • EXTERNAL FILES - tap on Open...
      • Select folder from Working Copy (already existing repository)

      You can now edit files in Pythonista, delete them, create new ones, ... And all these changes are automatically propagated to the Working Copy. If you'd like to commit & push, just open Working Copy and do it. That's it. It's called open in place and Pythonista is editing files directly in the Working Copy folder / repository (shared location).

      posted in Pythonista
      zrzka
      zrzka
    • RE: Dual screen support...?

      Hi,

      it's possible. Read this article from Apple about external display. Here's quickly hacked example ...

      • create new script with UI, call it hdmi for example
      • add some UI controls (I did add label with Hallo HDMI text)
      • create another script with UI, call it main for example
      • add some UI controls (I did add label with Hallo iPad text)

      Code for the hdmi.py:

      import ui
      
      def load_view():
        return ui.load_view()
      

      Code for the main.py:

      import ui
      import hdmi
      import objc_util
      
      v = ui.load_view()
      v.present('sheet')
      
      UIScreen = objc_util.ObjCClass('UIScreen')
      if len(UIScreen.screens()) > 1:
      	second_screen = UIScreen.screens()[1]
      	
      	bounds = second_screen.bounds()
      	
      	UIWindow = objc_util.ObjCClass('UIWindow')
      	second_window = UIWindow.alloc().initWithFrame_(bounds)
      	second_window.setScreen(second_screen)	
      	second_window.setHidden(False)
      	
      	hdmi_view = hdmi.load_view()
      	hdmi_view_objc = objc_util.ObjCInstance(hdmi_view)
      	hdmi_view_objc.setFrame(second_window.bounds())
      	second_window.addSubview(hdmi_view_objc)
      

      Photo of both labels on iPad & my TV.

      HTH,
      Zrzka

      posted in Pythonista
      zrzka
      zrzka
    • RE: How to ask a good question

      @Matteo

      Re 1 - There're many extensions doing this, but I don't know if NodeBB supports it. Search for them and try. I doubt that it will work.

      Re 2 - Little bit of scripting, but doable.

      Let's say you have a topic with URL like https://forum.omz-software.com/topic/4622/notification-module-api. Every comment starts with a line like Phuket2 posted a day ago. When you tap on a day ago, URL changes to https://forum.omz-software.com/topic/4622/notification-module-api/6. And now:

      • You can prepend /api to get JSON
      • /6 is a post number starting from 1, so, the index is 6-1=5

      JSON response contains posts, which is an array of dictionaries and every dictionary has key content. You have a content now. You can get <code>xxx</code> via regex CODE_PATTERN = re.compile('\<code.*?\>(.*?)\<\/code\>', re.MULTILINE | re.DOTALL).

      Here's code with fixed URL inside:

      UPDATE: <snip>...see gist in following post, removed code & sample just not to clutter this topic...</snip>

      posted in Pythonista
      zrzka
      zrzka
    • RE: Input Issue with Python

      Nice first post, welcome to the forum. Who in the hell are you? Maintain your attitude and calm down if you need help. I have no idea what's your problem, don't understand what you're trying to do. Maybe you can transform your anger into meaningful question with more details.

      posted in Pythonista
      zrzka
      zrzka
    • RE: Permission error with multiprocessing.Process

      It's b/o Apple, not b/o Pythonista. Only one process is allowed until Apple relaxes (if ever) this rule.

      posted in Pythonista
      zrzka
      zrzka
    • RE: [Help] Stop Widget View Refresh

      Not sure if I'll help you ... IIRC Today Widget is problematic, doesn't work well and I think that Ole said that he is thinking about removing it. Maybe I'm wrong, but I think I read it when I was going through all issues on the GitHub. Just try to search them (even the closed ones), maybe you'll find something.

      NotificationCenter.framework

      Every time Notification Center appears, you have the opportunity to update widget content. It's done via widgetPerformUpdateWithCompletionHandler: and then you have to call completion handler with NCUpdateResult, which can be:

      • .NewData - iOS should refresh widget,
      • .NoData - previous snapshot made by the system is still valid, no need to update it,
      • .Failed - something bad happens, exception, whatever.

      Pythonista appex

      There're two functions you should be interested in:

      • get_widget_view()
      • set_widget_view()

      Not sure how set_widget_view() works internally, because it calls _appex.set_widget_view() (have no source for _appex). And it's also not documented how these two methods cope with NCUpdateResult, what happens when set_widget_view() is not called, etc.

      We can just guess - and my guess is that it returns .NewData every single time which leads to widget refresh. Is there a way how to confirm this theory? Maybe.

      import ui
      import appex
      import datetime
      
      
      lbl = appex.get_widget_view()
      if not lbl:
          lbl = ui.Label(frame=(0, 0, 0, 44))
          lbl.alignment = ui.ALIGN_CENTER
          lbl.background_color = 'black'
          lbl.text_color = 'white'
          appex.set_widget_view(lbl)
      
      lbl.text = str(datetime.datetime.now())
      

      This simple widget gets current widget view, if it not exists, view is created and set by set_widget_view(). If it exists, set_widget_view() is not called and we're just updating text property. Every single time notification center appears, widget is updated - I see time updates. This kind of confirms my theory, because .NoData shouldn't lead to refresh and old snapshot should be displayed.

      Ideal world

      I would expect that every widget script should have function perform_update like this:

      def perform_update(widget_view) -> bool:
          # Just do your stuff, even set new view via `set_widget_view`, ...
          # Load data, do whatever you want to do
      
          # return True to simulate `.NewData`
          # return False to simulate `.NoData`
          # raise to simulate `.Failed`
      

      Or something similar, so, you can simulate all these three states. Unfortunately, we have nothing like this.

      Recommendation

      • Do not call set_widget_view if your view was already set (check via get_widget_view & .name property)
      • Do not update your widget view if it's not necessary (already displayed, not enough time elapsed, ...)

      You can still experience some animations (Show More button appears / disappears), but it's not very frequent.

      Example

      Here's example widget. What it does?

      • First time this widget is displayed, there's red background and current timestamp
      • Widget updates timestamp only if at least 10s elapsed from the initial / last update
      • When there's consequent update (10s elapsed & not initial display), background is set to black

      It shoulda kinda simulate what you want. Just use different conditions, check if you have data or not, etc.

      import time
      
      import appex
      import ui
      from objc_util import ObjCClass
      
      
      NSUserDefaults = ObjCClass('NSUserDefaults')
      
      
      class TimestampWidgetView(ui.View):
          NAME = 'TimestampWidgetView'
      
          # Can be whatever, but it should be unique per Pythonista & any script
          # Reversed domain is used followed by whatever you want
          _LAST_UPDATE_KEY = 'com.robertvojta.pythonista.widget.last_update_key'
      
          def __init__(self, update_interval=10):
              super().__init__(frame=(0, 0, 0, 44))
      
              self.update_interval = update_interval
              self._defaults = None
      
              self.label = ui.Label(frame=self.bounds)
              self.label.flex = 'WH'
              self.label.background_color = 'red'
              self.label.text_color = 'white'
              self.label.alignment = ui.ALIGN_CENTER
              self.add_subview(self.label)
      
              # WidgetView is being initialized, we should update content
              self.update_content(force=True)
      
          @property
          def defaults(self):
              if not self._defaults:
                  self._defaults = NSUserDefaults.standardUserDefaults()
              return self._defaults
      
          @property
          def last_update(self):
              return self.defaults.integerForKey_(self._LAST_UPDATE_KEY)
      
          @last_update.setter
          def last_update(self, value):
              self.defaults.setInteger_forKey_(value, self._LAST_UPDATE_KEY)
      
          def update_content(self, force=False):
              # Get the time of when the update was called
              #
              # NOTE: Casting to int, because setFloat_forKey_ and floatForKey_ on self.defaults
              #       produces weird values
              timestamp = int(time.time())
      
              if not force and timestamp - self.last_update < self.update_interval:
                  # Not enough time elapsed and we're not forced (initialisation) to update content, just skip it
                  return
      
              # Update content in whatever way
              self.label.text = str(timestamp)
      
              if not force:
                  # If update wasn't forced, change the color to black, just to see the difference
                  self.label.background_color = 'black'
      
              # Store the time, just for the next comparison
              self.last_update = timestamp
      
      
      widget_view = appex.get_widget_view()
      # Can't use isinstance(widget_view, TimestampWidgetView) here, just use .name property
      if widget_view and widget_view.name == TimestampWidgetView.NAME:
          widget_view.update_content()
      else:
          widget_view = TimestampWidgetView()
          widget_view.name = TimestampWidgetView.NAME
          appex.set_widget_view(widget_view)
      

      As I wrote, wild guesses and experiments. Not enough documentation how it actually works and based on current appex API, there's no hope to make it better.

      posted in Pythonista
      zrzka
      zrzka
    • Keychain & TouchID

      Hi,

      not sure if anyone else is interested, but here's an update of the Pythonista keychain module drop-in replacement. Available as gist now, will be included in the Black Mamba later.

      It's a proof of concept, will probably change API, add more things, ... Don't use this for serious work now.

      What's this all about? iOS keychain is powerful and allows you to specify things like:

      • when the password is accessible (unlocked device, after first unlock, ...),
      • if the password should be synchronised to other devices as well,
      • if the user presence is required whenever script wants to access password,
      • etc.

      Unfortunately, Pythonista keychain module doesn't allow us to control this. It's just a simple text password storage. Module does use system keychain, but there're no options to control all these things. That's the reason why I started to work on this enhancement.

      Why? The reason is quite simple. I want to store sensitive data, like our production keys on the iPad and I do not want to use Pythonista keychain module. Because then any other script can silently retrieve my keychain items. One can say, which script? As the author of Black Mamba, I'm going to say Black Mamba for example, just not to offend authors of other scripts. Did you install Black Mamba? Did you read the source code? How can you be sure that I'm not silently calling get_services, get_password for every service and then sending all these passwords to my server? Of course that I'm not doing this. It's just an example. But these things happen. Google for PyPI, npm, ... issues and you'll see. You can say that I shouldn't store production passwords, ... on my iPad within Pythonista when I'm using 3rd party modules. Yes, I shouldn't via keychain module. I wanted to solve this somehow and thus here's this new module allowing me to set user presence requirement, disable syncing, ...

      Here's an example:

      gp = GenericPassword('s', 'a2')
      gp.set_password('hallo3', accessible=kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, access_control=kSecAccessControlTouchIDAny)
      p = get_password('s', 'a2')
      print(p)
      

      What it does? It stores password hallo3 for the service s and account a2. Whenever you want to retrieve it, iOS system dialog appears (every single time) requiring you to place your finger on the touch ID sensor. Also you can control what happens if fingerprints are changed (you can get your password deleted), if you require same fingerprints set or any, if you require biometrics or passcode is enough, etc.

      This is system level stuff. Even when you change code to this one ...

      gp = GenericPassword('s', 'a2')
      gp.set_password('hallo3', accessible=kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, access_control=kSecAccessControlTouchIDAny)
      
      import keychain
      p = keychain.get_password('s', 'a2')
      print(p)
      

      ... you'll still be asked for your fingerprint. Also you'll be asked for your fingerprint if you'd like to change password for existing service & account which is already protected with biometrics, etc.

      I'll finish this one even even if no one is interested. But as I would like to include this in the Black Mamba, comments are appreciated.

      posted in Pythonista
      zrzka
      zrzka
    • RE: keychain module - query (service, account)

      I'm working on drop-in replacement of Pythonista keychain module. I'll include it in the Black Mamba when finished with things like Touch ID protection, labels, ... Not finished yet, but I decided to share Pythonista compatibility layer I've got in this module, so, others can learn what exactly Pythonista keychain module does, something about CF, ctypes, ... Here's the gist.

      posted in Pythonista
      zrzka
      zrzka

    Latest posts made by zrzka

    • RE: Black Mamba Installation error

      I'm alive, don't worry, but my focus shifted a bit and I no longer have time to actively develop/maintain Black Mamba/monitor forum. This is the reason why I asked if there's anyone who wants to maintain it two years ago.

      But I still passively maintain it which means that if you provide a PR, I'll test it, merge it and will release new version.

      If there's anyone who wants to take over, feel free to ping me. I'll happily add you to the project and even transfer ownership later.

      posted in Pythonista
      zrzka
      zrzka
    • RE: Crash in toggle_comments.py

      PRs welcome :)

      posted in Pythonista
      zrzka
      zrzka
    • RE: Programmatically Reading Plist Files to Access External Files in Pythonista

      Locating Files Using Bookmarks - ObjC runtime & iOS developer guide are your friends to look for.

      posted in Pythonista
      zrzka
      zrzka
    • RE: Programmatically Reading Plist Files to Access External Files in Pythonista

      Try this, worked for me (no iPad around to test it now).

      posted in Pythonista
      zrzka
      zrzka
    • Black Mamba maintainer

      Hi guys,

      you probably did realize that I did nothing in the Black Mamba project for couple of months. Quite busy with other stuff. I'm still receiving Pythonista beta versions, will try to do my best to keep it up to date (read keep it working with latest Pythonista versions), but can't promise anything. If you'd like to help, maintain it or even take over, feel free to ping me and I can add you to the repository, read the docs, ... If it will work, I can even transfer repository ownership to you.

      Zrzka

      posted in Pythonista
      zrzka
      zrzka
    • RE: How to ask a good question

      @Matteo update to 3.2 & use 3.6 as default interpreter (the error is about f strings).

      posted in Pythonista
      zrzka
      zrzka
    • RE: Sounds module on MacOS?

      Basically all modules listed here are not available outside of Pythonista. There's no repository, source code, package in PyPI, ... It has no sense, because they're Pythonista specific. If you'd like to run your script on Mac, you have to provide your own sound module with functions you do use in Pythonista.

      I was thinking about new project providing compatibility layer for Mac, because I was tired of patching, mocking, ... (Sphinx, Travis CI, ...) But then I realized that almost all build / CI / ... servers I do use are running GNU/Linux. One layer for Mac, another one for GNU/Linux, ... Decided not to do it, it's not worth it.

      You should check PyObjC If you'd like to leverage existing macOS frameworks, which gives you bidirectional bridge between Python and ObjC world. If you'd like to just play sound, it can be as simple as calling afplay /path/to/your/sound/file from the Python.

      posted in Pythonista
      zrzka
      zrzka
    • RE: Problem with ability to slide tiles.

      Hi @ETPH. Couple of generic advices:

      • Do not use magic constants in your code (like position_options) in multiple places, it's a nightmare when you want to change them. Move them outside of create_tile, touch_began and reuse them there. Like MARGIN_OF_ERROR.
      • Keep your functions / methods short. It's good when they can fit one screen (well, depends how the screen is big :), but you know what I mean. Not a hard rule, but it's about readability and crunching all these bugs quickly. It's easier when it's short. touch_moved is very long and overly complicated.
      • You shouldn't hardcode positions based on your device in case you want to run it on iPhone SE for example.
      • Split your task into several smaller ones. Replace one huge function with many small ones doing just one thing. Again, readability & easier way how to spot a bug.

      I didn't dive into your touch_moved method, sorry. But I did quickly hack an example of generic board & moving tiles. You can find it here.

      posted in Pythonista
      zrzka
      zrzka
    • RE: How to ask a good question

      @mikael check this https://gist.github.com/zrzka/b61c74975c777a77232035a3e171337d

      • Follow instructions above line 21 to get an client ID
      • This script can be run from the Pythonista or you can add it to share extensions and run it from Photos for example
      posted in Pythonista
      zrzka
      zrzka
    • RE: How to ask a good question

      @Matteo decided to make your life easier :) https://gist.github.com/zrzka/7fc25156da0714daa9aa79a17898da2f

      posted in Pythonista
      zrzka
      zrzka