Forum Archive

'pythonista' module

[deleted]

Edit: the project is hosted here on GitHub

I have the idea to create a Pythonista class to wrap the various things that can be done with the Pythonista app thanks to obcj_util and UIApplication. Examples (e.g by @omz, @JonB , @Webmaster4o) are currently scattered in various places, the class would bring them together and make them easy to find and use. Something like below (tab is just a stub and needs a lot of fleshing out)

Pythonista.py


# coding: utf-8
from objc_util import *

class Pythonista(object):
    @on_main_thread
    def __init__(self):
        self.app = UIApplication.sharedApplication()
        self.rootVC = self.app.keyWindow().rootViewController()
        self.tabVC = self.rootVC.detailViewController()
        self.consoleVC = self.app.delegate().consoleViewController()
        self.userDefaults = ObjCClass('NSUserDefaults').standardUserDefaults()

    @on_main_thread
    def setBadgeString(self, s):
        self.app.setApplicationBadgeString_(s)

    @on_main_thread
    def setBadgeNumber(self, i):
        self.app.setApplicationIconBadgeNumber_(i)

    @on_main_thread
    def openURL(self, s):
        self.app._openURL_(nsurl(s))

    @on_main_thread
    def getConsoleFont(self):
        self.consoleVC.view()
        return self.consoleVC.outputFont()

    @on_main_thread 
    def getDefaultFont(self):
        return [str(self.userDefaults.stringForKey_('OutputFontName')), self.userDefaults.integerForKey_('OutputFontSize')]

    @on_main_thread
    def addTab(self, s):
        newVC = create_objc_class('CustomViewController', ObjCClass('UIViewController'), methods=[], protocols=['OMTabContent',]).new().autorelease()
        newVC.title = s
        self.tabVC.addTabWithViewController_(newVC)


if __name__ == "__main__":
    p = Pythonista()
    #p.setBadgeString('test')
    #p.setBadgeNumber(1)
    #p.openURL('pythonita://')
    #print p.getConsoleFont()
    #print p.getDefaultFont()
    p.addTab('New')
Olaf

Assembling these from their various posts, etc. may be useful indeed. But why is Pythonista a class and not a module? What is a Pythonista (occurrence; p in your example)?

[deleted]

@Olaf If you put Pythonista.py in site-packages you can use it like this:

from Pythonista import Pythonista

p = Pythonista()
p.addTab('New')

'p' is an instance of the class. Does it answer your question?

ccc

I like better the idea of a PythonistaApp kinda like how Flask does things...

from flask import Flask
app = Flask(__name__)
Cethric

I think @Olaf was trying to ask the reason behind using a class over modules.

As a class you need to do

from Pythonista import Pythonista
p = Pythonista()
p.addTab('new')

Where as if it were a module it could just be called as

from Pythonista import addTab
addTab('new')
[deleted]

@Cethric I guess you mean a module that doesn't contain the code as a class? From the documentation at '5.'... A module is a file containing Python definitions and statements. The file name is the module name with the suffix .py appended.

I have in mind one thing that using a class in the module allows that otherwise wouldn't be possible, that is sub classing.

dgelessus

The main feature of classes is that you can create multiple instances of them, which isn't very useful for Pythonista, because there is always exactly one Pythonista app that you're working with. And if all functions are at module level (instead of methods), then subclassing isn't really necessary, if you want to add extra functions then you can simply reuse the existing module attributes and functions.

[deleted]

@dgelessus We'll see when 'tab' is fleshed out. The ability to override certain methods may be a handy way to modify the tab without having to reimplement everything.

Webmaster4o

@gueroito true. However, in its current state, subclassing doesn't have a ton of uses. It doesn't implement much core functionality besides these functions. Adding a method to a subclass could be just as easily done by simply defining a new function, and you would still be able to access the old functions. When writing a new function, instead of self.app, you could use Pythonista.app.

The main situations in which a class adds functionality are

  1. It makes sense to have multiple copies of something
  2. You need to have multiple instances, each of which stores their own properties.

If this class controlled multiple apps, you might have a different self.app for each app, but want to use the same functions on it. In this case, a class makes sense. However, in your case it really doesn't (yet).

Anywho, this is a great concept, and pretty well executed. Nice job.

[deleted]

@Webmaster4o Thanks :). Yes, in the current state... but I'm implementing now @omz 's WebTab. I'm basing the structure on the UI module, with Tab as one type of View, WebTab as a subclass... with the aim of making customising the app itself as easy as using the UI module.

Webmaster4o

@guerito another person who did some cool stuff is @stevenstroughtonsmith on github. I'll try to dig up some links…

Webmaster4o

Ok. Most notably this:

  • Show tab overview- Puts the "show tab overview" button in place for iPad. This script adds a button, but you could take the code and move it to a function.

A few more Pythonista-related things are on the first page of https://gist.github.com/steventroughtonsmith. I feel like he might have posted more stuff on Twitter, if you go through @stroughtonsmith on Twitter.

[deleted]

@Webmaster4o Gracias :)

JonB

This needs to be in a git repo. A module makes more sense to me.
Here are a collection of other useful constucts Inhave found:

# part of an editorfile class I was working on:
def _get_tab_vc(self):
        ''' return the tabvc for the current file
        '''
        if self.file:
            try:
                tabidx=[str(x) for x in tabVC.allOpenFilePaths()].index(self.file)
                return tabVC.tabViewControllers()[tabidx]
            except ValueError:
                return None
        else:
            self.file=str(tabVC.filePath())
            return tabVC.selectedTabViewController()

# documentation
docVC=rootVC.accessoryViewController().documentationViewController()
# get all open editor file paths
tabVC.allOpenFilePaths()
tabvcs=tabVC.tabViewControllers() #vc for each editor tab
tabVC.selectedTabViewController() #current open file
tvc=tabVC.selectedTabViewController()
ed=tvc.editorView() # contains i think the actual text plus tab itself
tv=ed.textView() # the main textview
ts=tv.textStorage() #this ends up being useful to style text
[deleted]

@JonB Thanks too :). Yes, it's getting more than a proof of concept now... I'm aiming to put the next version on GitHub.

dgelessus

Would it make sense to structure this as submodules, such as pythonista.editor, pythonista.console, pythonista.ui, and so on? Those modules could also from-import all functions and classes from the standard editor/console/ui/etc. modules. Then you could use both the standard Pythonista functions and the ones written with objc_util in the same way.

Phuket2

@guerito , I think it's a great idea. Another candidate to consider is Pythonista's Version information and possibly extra info as illuded to in THIS post.

[deleted]

Update:

  • Views started: Tab, WebView, ButtonItem, SearchBar

  • Views to do: MapView (from @omz 's example), Button, Label, TextField etc...

  • Tab subclassed to WebTab which adds: WebView, SearchBar and ButtonItem(s) for Back, Forward, Share

[deleted]

The conversion of @omz 's WebTab now looks like this:

class WebTab(Tab):
    @on_main_thread
    def makeSelf(self):
        self.name = 'WebTab'
        biA = ButtonItem(self, image = 'Action', action ='wtShare')
        self.right_button_items.append(biA)
        biF = ButtonItem(self, image = 'Forward', action ='wtGoForward')
        self.right_button_items.append(biF)
        biB = ButtonItem(self, image = 'Back', action = 'wtGoBack')
        self.right_button_items.append(biB)
        sb = SearchBar(self)
        self.newVC.navigationItem().titleView = sb
        wv = WebView(self)
        wv.loadRequest_(NSURLRequest.requestWithURL_(nsurl('http://www.google.com')))
        self.newVC.view = wv

    @on_main_thread
    def customVC(self):
        return create_objc_class('CustomViewController', ObjCClass('UIViewController'), methods=[wtShare, wtGoBack, wtGoForward, searchBarSearchButtonClicked_], protocols=['OMTabContent', 'UISearchBarDelegate']).new().autorelease()
ccc

Can we please move this to a repo. I can't send pull requests to this user forum.

[deleted]

@guerito said:

@JonB Thanks too :). Yes, it's getting more than a proof of concept now... I'm aiming to put the next version on GitHub.

@ccc I have MapView todo (from @omz 's example), and Button, Label, TextField etc before wrapping up the next version.

I thought too I'd see how people felt about how WebTab has turned out. I'm trying to make customising the app itself a similar thing, and as easy as, using the UI module.

Hopefully too, it gives the chance to see why classes make sense to use in that regard.

Phuket2

@guerito, is your code published anywhere yet, gist or repo. If it is, I can't see the link

[deleted]

@Phuket2 If people are happy with how WebTab looks and are on board now for the use of classes... I'll go ahead and finish the next version and post it to GitHub.

[deleted]

@guerito said:

@dgelessus We'll see when 'tab' is fleshed out. The ability to override certain methods may be a handy way to modify the tab without having to reimplement everything.

@dgelessus I asked to hold your comment until we could see how 'tab' fleshed out. Are you ok now with the use of classes for this?

Looking at the UI module, it mostly consists of classes, with a few module level utility functions at the end.

dgelessus

@guerito Don't get me wrong, I don't mean that classes are bad. I mean that your Pythonista class doesn't need to be a class. There is no use in having multiple instances of Pythonista, because there is only one Pythonista app that all instances use. And if you only ever need one instance of Pythonista, then there's no need for any of the special features that a class gives you. Subclassing is not very useful - why would you want to have two different Pythonista subclasses with different methods?

My point is that you can simply change all methods of Pythonista to normal functions, move the code from __init__ to module level before everything else, and remove the class. Now you can call the functions more easily and have not lost any important features.

If you put the code on GitHub, I can make a pull request so you can see what I mean.

[deleted]

@dgelessus Thanks... I understand what you mean.

At the time... tab was part of the Pythonista class and that was the reason. Now that it's its own class I think we are on the same page.

Most of the module will be classes and that handful of methods currently in the Pythonista class can be module level utility functions - like at the end of the UI module.

I can manage that.

Phuket2

@guerito , ok nice. I thought I had missed a link. But it's hard to follow along in this way. Not sure how many files you have. But if you had a single file for example, easy to share as a gist via the wrench menu while you are building up the code. It's still not ideal I guess, but you can share a new gist each time you make changes you want people to review or comment on. I know the best is repos, (I am still healthily skeptical about repos) but as other methionine here, it's so hard to follow code fragments in a forum post. You will not get the feed back you are looking for I think, as it's difficult make a composite of all the code that's been posted. Just trying to be constructive

[deleted]

The feedback has been great thanks, very useful.

JonB

I think dgelessus and others are saying: WebTab should be a class, since you can have multiple tabs. EditorTab should be a class. But Pythonista should be a module/package (probably some others like pythonista.editor).

Webmaster4o

I'm trying to make customising the app itself a similar thing, and as easy as, using the UI module.

This is a great idea.

One thing I could see being really powerful in this module is a ui.View type class for each element in the editor. You could have a classes for Gutter, TabVC, etc. Calling functions or setting properties on these could combine functionality from many different sources.

I think to implement this, it would make sense to have a custom theme for the module.

Say I was working with a Gutter class that describes the gutter on the left side of the editor (with the line numbers). If I called a method like add_subview on this class, it would call an objc_util function to make that happen. Setting properties could also wrap objc_util functions.

Certain properties could be set to perform other actions when set, though. If I set the property background_color of Gutter, the module would be smart enough to change the corresponding property in the JSON file of the theme, instead of calling an objc_util function, since themes are more stable/permanent.

This could create an easy programmatic interface for scripts to control the appearance of the editor, and would be a good use of classes.

In this case, we'd be using classes not for their powers of subclassing, but for their power to make code efficient, readable, and convenient.

[deleted]

@Webmaster4o said:

I'm trying to make customising the app itself a similar thing, and as easy as, using the UI module.

This is a great idea.

One thing I could see being really powerful in this module is a ui.View type class for each element in the editor. You could have a classes for Gutter, TabVC, etc. Calling functions or setting properties on these could combine functionality from many different sources.

I think to implement this, it would make sense to have a custom theme for the module.

Say I was working with a Gutter class that describes the gutter on the left side of the editor (with the line numbers). If I called a method like add_subview on this class, it would call an objc_util function to make that happen. Setting properties could also wrap objc_util functions.

Certain properties could be set to perform other actions when set, though. If I set the property background_color of Gutter, the module would be smart enough to change the corresponding property in the JSON file of the theme, instead of calling an objc_util function, since themes are more stable/permanent.

This could create an easy programmatic interface for scripts to control the appearance of the editor, and would be a good use of classes.

In this case, we'd be using classes not for their powers of subclassing, but for their power to make code efficient, readable, and convenient.

@Webmaster4o I think that of everyone who has posted here, it's you that's caught the vision and has the good sense and creativity to take it forward.

Give me a couple of days to do it, but if I send you the next version source, would you like to put it on your GitHub and run the project on behalf of everyone here who wants to contribute?

mikael

666 views - ominous.

If "hosting" a GitHub repo is all that is needed, I will be happy to do it.

Webmaster4o

@guerito I think you should put it on your GitHub (if you have one) since it's your project and reflects your work. However, if you don't have a GitHub and don't want to make one, I'd be happy to host it if you need.

[deleted]

@Webmaster4o I've emailed you the current version and I think if you would put it on your GitHub and coordinate everyone's contributions, that would be great.

it's everyone's code and needs to be a community project I think.

Thanks very much.

Webmaster4o

@guerito sure. I'll do that now. I think you should make a GitHub, though, so that you can be involved actively in collaboration. I'll add you as a collaborator and give you full access to the repo.

Webmaster4o

I've begun to make some changes on github, which I hope you're OK with, just to neaten up the structure of the project. I've added very little extra functionality. The project is live at https://github.com/The-Penultimate-Defenestrator/Pythonista-Tweaks.

@guerito Please send me a github username so I can add you as a collaborator, which will give you full control over the project. Also, if you've never used git, I'd be happy to help you out as it can be kind of confusing.

P. S. I've tentatively named it "Pythonista-Tweaks," just for the sake of a more descriptive title, but that will probably change.

[deleted]

@Webmaster4o Well done :)

Webmaster4o

I've broken the module into several submodules, now. Download the repo, and save it in site-packages as Pythonista. Then, the submodules are:

  • Pythonista.editor, which contains the Tab class and the WebTab,
  • Pythonista.console, which contains functions for getting the current and default console fonts
  • Pythonista.app, which contains functions for setting the badge string/number, clearing the badge, and opening URLs in an apex-safe way.

Note: Please do NOT use from Pythonista import *, as this could create conflicts with the default console and editor modules. Instead, use it only with import Pythonista, as this way the module's editor and console modules will not overwrite Pythonista's default ones. It is fine to use from Pythonista.app import *.

As of right now, each submodule contains only a small amount of functionality, but as they're expanded, the submodule approach will make much more sense.

My aim is that Pythonista.editor will eventually contain the classes that I described earlier, for easily controlling the editor's look and feel. The structure of these submodules, as well as their names, is likely to change.

[deleted]

@Webmaster4o Once again, well done. Way to go.

Webmaster4o

@guerito have you made a github yet :)

Webmaster4o

Also, 100 reputation πŸŽ‰πŸ˜›

[deleted]

@Webmaster4o You deserve it! Congratulations! πŸŽ‰

[deleted]

Updated the first topic post to point to the project on GitHub. See the latest additions there.

Webmaster4o

@guerito I'm almost finished with my top priority current project, and then I'll try to put some work into this. I'll see what I can put together for this soon πŸ˜„

[deleted]

@Webmaster4o You might like to add Save Attachment to the pythonista package, maybe in an extensions sub-module.

I had to use dialogs instead of console to avoid onflicts.


"""Methods relating to extensions."""

import appex, os, shutil, dialogs

__all__ = [
    "save",
]

def save():
    """Save an attachment"""
    if appex.is_running_extension():
        sFp = appex.get_file_path()
        if sFp:
            dialogs.hud_alert('Saving...')
            comps = __file__.split(os.sep)
            doc_path = os.sep.join(comps[:comps.index('Documents')+1])
            with open(sFp, 'rb') as f1:
                with open(doc_path + '/' + os.path.basename(sFp), 'wb') as f2:
                    shutil.copyfileobj(f1, f2, length=512*1024)
            dialogs.hud_alert('Saved')
            appex.finish()
Webmaster4o

I'm really hoping to get back to this soon. I haven't simply because of a combination of being busy and having other projects that I'm prioritizing in my free time right now.

Until I get around to working on this project, community contributions are still encouraged, it'd be great if anyone wants to contribute

cook

@guerito @Webmaster4o I like this project. So....above somewhere it mentioned adding things that are available through objc_utils. I had also though about that recently in playing around with this. There's a lot more functionality that can be added to pythonista and it's great if it is all in one place and easily accessible. Thanks for your work in doing this!

Here is a submission that I had on another post for HTML to AirPrint (don't know which category this fits into...or if it really falls into the scope of this project).

# coding: utf-8

from objc_util import *     

@on_main_thread
def print_html_orientation(input, orientation = 'P'):
    '''takes HTML input and formats it for printing. Uses the built in ios UI print dialogue. Orientation should be 'P' or 'L' '''
    html = ObjCClass('UIMarkupTextPrintFormatter').alloc().initWithMarkupText_(ns(input))
    printController = ObjCClass('UIPrintInteractionController').sharedPrintController()
    printInfo = ObjCClass('UIPrintInfo').printInfoWithDictionary_(None)
    printInfo.orientation = int(orientation[0].upper() == 'L')
    printController.printInfo = printInfo
    printController.setPrintFormatter_(html)
    printController.presentAnimated_completionHandler_(0, None)
Webmaster4o

@cook I'm pretty sure the scope of the project is roughly defined as "Anything that adds functionality to Pythonista" :D

I'll add this.