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.
UniPAGe as a bridge between Kivy and Pythonista
-
I am not sure why but the editor messed up the first parts of the classes definitions that I pasted into it. If you are interested in this code maybe it would be better to download it from github at https://github.com/Tileyon/UniPAGe. Sorry for the inconvenience.
-
Remember that you can edit your own posts by clicking the word edit at the top right of the post.
https://forum.omz-software.com/topic/1891/threebackticks-py-utility-to-format-code-snippets-for-this-forum Describes how to format Python code in this forum.
-
Thanks @ccc I tried to edit the code after I posted it but it would not let me go beyond the first paragraph which is just an introduction and not part of the script.
-
Below is the script reformatted using @ccc's routine. Thanks again @ccc.
class unipage(object): def __init__(self, kivy, screen_size): self.kivy = kivy self.screen_size = screen_size self.unibuttons = [] self.unitexts = [] self.unilabels = [] self.unimages = [] self.uniframes = [] def setscreen(self): if self.kivy: from kivy.uix.floatlayout import FloatLayout from kivy.core.window import Window self.root = FloatLayout() Window.size = self.screen_size else: import ui self.root = ui.View(frame=(0,0,self.screen_size[0], self.screen_size[1])) def unibutton(self, params): xratio = self.screen_size[0] / 800.0 yratio = self.screen_size[1] / 600.0 self.unibuttons.append([]) if len(params) == 6: function = params[5] else: function = nofunction if self.kivy: from kivy.uix.button import Button self.unibuttons[len(self.unibuttons) - 1] = Button( text = params[4], size_hint_y = None, size_hint_x = None, height = params[3] * yratio, width = params[2] * xratio, pos = (params[0] * xratio, params[1] * yratio), on_press = function ) self.root.add_widget(self.unibuttons[len(self.unibuttons) - 1]) else: import ui self.unibuttons[len(self.unibuttons) - 1] = ui.Button(frame= \ (params[0] * xratio, (600 - params[1] - params[3]) * yratio, \ params[2] * xratio, params[3] * yratio), title = params[4]) self.unibuttons[len(self.unibuttons) - 1].background_color \ = (0.4,0.4,0.4) self.unibuttons[len(self.unibuttons) - 1].action = function self.unibuttons[len(self.unibuttons) - 1].height = params[3] * xratio self.unibuttons[len(self.unibuttons) - 1].width = params[2] * yratio self.unibuttons[len(self.unibuttons) - 1].tint_color = 'white' self.root.add_subview(self.unibuttons[len(self.unibuttons) - 1]) def unitext(self, params): xratio = self.screen_size[0] / 800.0 yratio = self.screen_size[1] / 600.0 self.unitexts.append([]) if self.kivy: from kivy.uix.textinput import TextInput self.unitexts[len(self.unitexts) - 1] = TextInput ( id = 'text' + str(len(self.unitexts) - 1), size_hint_y = None, size_hint_x = None, height = params[3] * yratio, width = params[2] * xratio, text = params[4], multiline = True, pos = (params[0] * xratio, params[1] * yratio)) self.root.add_widget(self.unitexts[len(self.unitexts) - 1]) else: import ui self.unitexts[len(self.unitexts) - 1] = ui.TextField(frame= (params[0] * xratio, (600 - params[1] - params[3]) * \ yratio, params[2] * xratio, params[3] * yratio)) self.unitexts[len(self.unitexts) - 1].bordered = False self.unitexts[len(self.unitexts) - 1].background_color = 'white' self.unitexts[len(self.unitexts) - 1].font = ('<system>', 23 * xratio) self.unitexts[len(self.unitexts) - 1].text = params[4] self.root.add_subview(self.unitexts[len(self.unitexts) - 1]) def unilabel(self, params): xratio = self.screen_size[0] / 800.0 yratio = self.screen_size[1] / 600.0 self.unilabels.append([]) if self.kivy: from kivy.uix.label import Label self.unilabels[len(self.unilabels) - 1] = Label(pos = \ (params[0] * xratio, params[1] * yratio), \ size_hint=(1.0,1.0), halign="left", \ valign="bottom", text = params[4]) self.unilabels[len(self.unilabels) - 1].bind(size= \ self.unilabels[len(self.unilabels) - 1].setter('text_size')) self.root.add_widget(self.unilabels[len(self.unilabels) - 1]) else: import ui self.unilabels[len(self.unilabels) - 1] = ui.Label(frame= \ (params[0] * xratio, (600 - params[1] - params[3]) * yratio, \ params[2] * xratio, params[3] * yratio)) self.unilabels[len(self.unilabels) - 1].text = params[4] self.unilabels[len(self.unilabels) - 1].text_color = 'white' self.unilabels[len(self.unilabels) - 1].alignment = ALIGN_LEFT = True self.unilabels[len(self.unilabels) - 1].font = ('<system>', 18 * xratio) self.root.add_subview(self.unilabels[len(self.unilabels) - 1]) def unimage(self, params): xratio = self.screen_size[0] / 800.0 yratio = self.screen_size[1] / 600.0 self.unimages.append([]) if self.kivy: from kivy.uix.image import Image self.unimages[len(self.unimages) - 1] = Image( source= params[4], allow_stretch = True, size_hint = (None, None), size=(params[2] * xratio, params[3] * yratio), pos=(params[0] * xratio, params[1] * yratio)) self.root.add_widget(self.unimages[len(self.unitexts) - 1]) else: import ui self.unimages[len(self.unimages) - 1] = (ui.ImageView (name = 'Image', frame = (params[0] * xratio, \ (600 - params[1] - params[3]) * yratio, \ params[2] * xratio, params[3] * yratio))) self.root.add_subview (self.unimages[len(self.unimages) - 1]) self.unimages[len(self.unitexts) - 1].image = ui.Image.named(params[4]) def uniframe(self, params): xratio = self.screen_size[0] / 800.0 yratio = self.screen_size[1] / 600.0 if self.kivy: from kivy.graphics import Color from kivy.graphics import Rectangle self.root.canvas.add(Color (params[4][0],params[4][1], params[4][2])) self.root.canvas.add(Rectangle(pos = (params[0] * xratio, \ params[1] * yratio), size = (params[2] * xratio, \ params[3] * yratio))) else: import ui xratio = self.screen_size[0] / 800.0 yratio = self.screen_size[1] / 600.0 self.uniframes.append([]) self.uniframes[len(self.uniframes) - 1] = ui.View(frame=(params[0] * xratio, \ (600 - params[1] - params[3]) * yratio, \ params[2] * xratio, params[3] * yratio)) self.uniframes[len(self.uniframes) - 1].background_color = (params[4][0],params[4][1], params[4][2],1.0) self.root.add_subview(self.uniframes[len(self.uniframes) - 1]) def showpage(self): if self.kivy: from kivy.base import runTouchApp runTouchApp(self.root) else: self.root.present('sheet') class uniscreen(unipage): screendef = [] def __init__(self, screendef): try: from kivy.uix.floatlayout import FloatLayout kivy = True except: import ui kivy = False unipage.__init__(self, kivy, screendef[0]) self.setscreen() self.screendef = screendef def setpage(self): for k in range(1, len(self.screendef)): self.screendef[k][0](self, self.screendef[k][1]) def closepage(sender): if mypage.kivy: from kivy.utils import platform as core_platform from kivy.core.window import Window import sys if core_platform == 'android': sys.exit() else: Window.close() else: mypage.root.close() def function_1(sender): mypage.unitexts[0].text = 'Oh! You clicked my button.' def nofunction(sender): pass if __name__ == '__main__': unilabel = unipage.unilabel uniframe = unipage.uniframe unitext = unipage.unitext unibutton = unipage.unibutton unimage = unipage.unimage widgets = [(600, 450), (uniframe,(0, 0, 600, 450,(.6,.6,.6))), (unilabel,(80, 10, 240, 20, 'Hey I am just a simple label.')), (unibutton,(40, 40, 100, 40, 'Click me', function_1)), (unibutton,(460, 40, 100, 40, 'Close me', closepage)), (unitext,(40, 120, 300, 40, 'I am a text field')), (unimage,(460, 310, 100, 100,'insidelogo.png')) ] mypage = uniscreen(widgets) mypage.setpage() mypage.showpage()
-
Nice. Here is a slightly different approach.
- Rather than using new set of API, pythonista API is used as standard. For example, "example1.py" (modified example of yours) runs directly in pythonista (currently it requires one line change from kivy code)
- In Kivy, you need an extra file "ui.py". All the code is in this file. It is just a wrapper on kivy objects.
- This code is not complete and a lot needs to be done.
import ui def closepage(sender): v.close() def function_1(sender): v['label1'].text = 'Oh! You clicked my button.' #uncomment or comment lines below based on platform #v = ui.MainView(frame=(0, 0, 600, 450)) # kivy v = ui.View(frame=(0, 0, 600, 450)) # pythonista v.add_subview(ui.Label(frame=(80, 10, 240, 20), name='label1', text='Hey I am just a simple label.')) v.add_subview(ui.Button(frame=(40, 40, 100, 40), title='Click me', action=function_1)) v.add_subview(ui.Button(frame=(460, 40, 100, 40), title='Close me', action=closepage)) v.add_subview(ui.TextField(frame=(40, 120, 300, 40), name='textfield1', text='I am a text field')) v.add_subview(ui.ImageView(frame=(460, 310, 100, 100), image=ui.Image('insidelogo.png'))) v.present('sheet')
from kivy.uix.floatlayout import FloatLayout from kivy.core.window import Window from kivy.base import runTouchApp from kivy.utils import platform as core_platform import sys from kivy.uix.label import Label as KivyLabel from kivy.uix.button import Button as KivyButton from kivy.uix.textinput import TextInput from kivy.uix.image import Image as KivyImage from kivy.graphics import Color from kivy.graphics import Rectangle class View(object): screen_size = (800, 600) xratio = screen_size[0] / 800.0 yratio = screen_size[1] / 600.0 def __init__(self, frame=(0,0,100,100), name=''): self.frame = frame self.name = name self.superview = None self.uniobject = None self.rootdict = {} class MainView(View): def __init__(self, frame=(0,0,100,100), name=''): super().__init__(frame=frame, name=name) self.root = FloatLayout() Window.size = View.screen_size self.root.canvas.add( Color(1.0, 1.0, 1.0)) self.root.canvas.add( Rectangle(pos = (self.frame[0] * View.xratio, self.frame[1]*View.yratio), size = (frame[2] * View.xratio, frame[3] * View.yratio))) def add_subview(self, v): v.superview = self if v.name: self.rootdict[v.name] = v.kivyobject self.root.add_widget(v.kivyobject) def __getitem__(self, key): return self.rootdict[key] def close(self): if core_platform == 'android': sys.exit() else: Window.close() def present(self, style): #todo: screen size, style runTouchApp(self.root) class Button(View): def __init__(self, frame=(0,0,100,100), title='', name='', font=('Helvetica', 20), action=None): super().__init__(frame=frame, name=name) self.kivyobject = (KivyButton(text=title, size_hint_y = None, size_hint_x = None, width = self.frame[2]* View.xratio, height = self.frame[3]* View.yratio, pos = (self.frame[0] * View.xratio, self.frame[1] * View.yratio), on_press = action)) class Label(View): def __init__(self, frame=(0,0,100,100), text='', alignment='', name='', font=('Helvetica', 20), text_color='blue'): super().__init__(frame=frame, name=name) label = KivyLabel(text=text, id=name, size_hint=(1.0, 1.9), halign="left", valign="bottom", pos = (self.frame[0] * View.xratio, self.frame[1] * View.yratio)) #label.bind(size=label.setter('text_size')) self.kivyobject = (label) class TextField(View): def __init__(self, frame=(0,0,100,100), text='', name='', font=('Helvetica', 20), alignment='', text_color='blue'): #action=None): super().__init__(frame=frame, name=name) self.kivyobject = (TextInput(text=text, size_hint_y = None, size_hint_x = None, height = self.frame[3]* View.yratio, width = self.frame[2]* View.xratio, multiline = True, pos = (self.frame[0] * View.xratio, self.frame[1] * View.yratio))) #on_press = action)) class ImageView(View): def __init__(self, frame=(0,0,100,100), name='', image=None): super().__init__(frame=frame, name=name) if image: image_source = image.source else: image_source = None self.kivyobject = ( KivyImage(source=image_source, allow_stretch = True, size = (self.frame[2]* View.xratio, self.frame[3]* View.yratio), pos = (self.frame[0] * View.xratio, self.frame[1] * View.yratio))) class Image(object): def __init__(self, source): self.source = source
Copy of the code in gist.
https://gist.github.com/balachandrana/4e9accfc894785682f230c54dc5da816#file-ui-py -
Frankly @abcabc I do not comprehend where you want to go with this. Why do you want to create a Kivy warper inside Pythonista when it is shown that the task can be much simpler. That approach is also probably too Pythonista centric and demean the fact that Kivy is a robust, stable and well maintained platform with a vibrant community. Kivy, Pythonista's UI and even Python are simply tools that we use to solve given problems not the problem itself. Graphic interfaces consist of a main container in which you drop components that may themselves become containers. OOP and that whole concept was designed at PARC (Xerox Palo Alto Research Center) to ease that process not make it harder. Besides you seem to miss the main point about UniPAGe which is a quest for universality at the source code level using Python. With UniPAGe you just insert a few lines in the main() section of your code that describe your widgets/views and you interface is ready to be displayed without modifications to the source code in any platform supporting Kivy or the Pythonista's UI. You may then spend your time solving the real problem addressed by your app. At any rate I which you success with your approach. However, UniPAGe is fully functional, quite robust and simple to use even for a neophyte. Finally, I should mention that publishing this script is a testimony of my gratitude to Ole Zorn for Pythonista, Mathieu Virbel and his team for Kivy and Guido van Rossum for Python who provided me with powerful tools to express my abstractions.
-
I am sorry if I hurt your feelings. I thought that this code will help somebody like me who is familiar with pythonista but not with kivy. I just spent two to three hours to code this. I do not have any projects related to this and I am not associated with any company. I am a retired old man and my only intention is to help others. Once again I am sorry if I hurt your feelings.
-
No need for apologies @abcabc I absolutely did not take anything personally. It was only an impassionate plea for simplicity and balance in coding. If my prose sounded rude I myself apologize.
-
Did you try my code on mac or PC? (I have tested on PC ,(windows 10, kivy 1.9.1, python 3.6) it works fine.)
-
@abcabc I really like the direction that you have taken with this... Making a ui module that allows Pythonista ui code to work unmodified on non-Pythonista platforms is intriguing. The app makes one import of
ui
and under the covers this code could manage ~10 different kivy imports. That is a nice bit of complexity hiding. If you would be willing to convert https://gist.github.com/balachandrana/4e9accfc894785682f230c54dc5da816#file-ui-py into a repo with a reasonable license (Apache or MIT) then I would send you some pull requests. -
I will put it in a github repo with MIT license after two/three weeks ( Need to do some cleanup, add some more features, add few more
examples) -
Come on @abcabc what do you mean you are going to put it on github with "MIT license"? You ripped off my code, turn it into a pythonista bastard that it was not intended to be and now you want to get a license on it. This is a subject that two days ago, above, you said you do not know anything about. Did you not read at the very beginning of this post that it is already copyrighted material under "Ti Leyon". What you are doing constitute copyright infringement and is against the law. This is why I never really publish in forums. Definitely @omz has to look into this kind of practices and establish rules to avoid this type of blatant theft of intellectual property.
-
Of course it's not okay (and illegal) to publish someone else's code with a license that the original author never intended. You can't just assume that everyone uses the MIT license, just because it's common.
In @abcabc's defense though, it is fairly unusual to post source code publicly, and not apply any open-source license. This makes it impossible to collaborate in any meaningful way, and if collaboration is not desired, it's not clear what the purpose of posting the code in the first place would be.
That said, this is @tileyon's decision, of course. Btw, it's possible to edit existing posts, in case you want to remove your code from this forum.
-
Thank you @omz for participating in this discussion. I published this post primarily as an educative material. Users of this forum have been requesting and commenting for years about integrating other platforms within Pythonista’s UI. Kivy loyalists for their part seem to remain oblivious to the virtues of Pythonista. My intents are clearly stated in the introduction to this discussion or even in its title. The codes and other files are on Github where anyone can start a branch and initiate a pull request. Although this type of collaboration is welcomed, plagiarizing the concept without any real creativity or functional contribution is definitely undesirable. Masquerading the original script as a parasitic afterthought of the UI is even more disturbing. This clearly defeats the spirit of unity between the targeted platforms and can not be tolerated. Is it not the norm in the industry? Is it not the motive of lawsuits between Sun Microsystem and Microsoft in the late 90’s about Java or more recently between Oracle and Google concerning the same development platform? Even with Linux, one of the first and widely adopted open source project, nothing goes in the kernel without the blessing of Linus Torvalds. The introduction of UniPAGe to the Kivy community will probably attract countless users of that group into the realm of Pythonista. This post also constitutes a small step to help users of this forum easily foray into alien GUI structures. Therefore, there is no need to antagonize adepts of either platform. Since it seems that a license is expected, I hereby propose the following license to be applied to UniPAGe. The J.U.L.I.A. G.E.E.K.S. license or julia geeks (all lower case) license is an educational licensure and an acronym for "Just Understand, Learn, Innovate And Go Enhance Every Known Statements”. julia geeks strongly discourage all form of plagiarism or mindless alteration of a given intellectual production. It promotes innovative, constructive and original addition or improvement to the said production. Such additions will be added to the current release and their authors credited in the subsequent “commits”. In the unfortunate event that the material should be reproduced, transformed or otherwise manipulated in whole or in parts then current local, state and international copyright laws should apply. julia geeks is also a reminder of Albert’s observation that “imagination is more important than knowledge”. Thank you again @omz for your leadership as well as the accuracy and fairness of your position.
-
Thanks for sharing.
One comment, you might consider using a ui.Transform on your root view, to set the scale. Somewhat simpler, in that scale only needs to be set in one location. This would also allow future enhancements that make use of pinch zooming or panning to show ui's at their native aspect ratio (for instance, showing a landscape ui in portrait), or in cases such as images where aspect ratio might make a difference.
I think the ideal kivy <--> pythonista bridge would either look like a ui.py on kivy systems, or kivy.py on pythonista systems, which mock the interfaces of each other, rather than creating an entirely new api. That way, existing scripts would "just work" with the addition of one file. I didn't read abcabc's post as trying to steal your work, but instead was trying to build on your idea, and offer a refactored version from a mainly pythonista user perspective. Someone who works mostly in pythonista generally wants to be able to
import ui
and have thier existing scripts work, load pyui's developed on pythonista's ui editor, and so forth. I would imagine someone with lots of work already in kivy would want something similar on pythonista.Of course mocking of the full
ui
, or fullkivy
would be a ton of work to capture all of the little quirks, features etc of either system. Without a ton of effort, any sort of "universal" api means giving up functionality and performance -- but no doubt still useful to many. -
@tileyon What are you trying to achieve with that attitude? I'm still not sure what the issue is with abcabc's code that you keep insulting. Is it that the code was based on your code? (Then why did you post your code on a public forum?) Is it that they wanted to put a license on it? (That could have been solved by putting a license on the original code in the first place.) Is it that it's modeled after Pythonista's instead of Kivy's API? (I don't see what's wrong with that.)
Can this "Julia Geeks" license be found anywhere? A web search didn't reveal anything useful. I'd be interested in seeing how exactly it differentiates between "innovative, constructive, original addition or improvement" and "mindless alteration".
-
Glad to see you here @JonB. The first thing I considered while designing this scheme was native scaling in Kivy and Pythonista. However I did not have a good experience dealing with it in Kivy. It either did not work or I did not know enough. I also read in one of these posts that in Pythonista fonts lose their quality during scaling. Since I must take full responsibility for the functioning of my codes I decided to implement it myself. That way I retain full control of it and can adapt it whichever way I want. You are right about the redundancy of scaling ration. This is an oversight on my part and can be easily improved by setting the vertical and horizontal ratios as class variables and assigning their values at the initialization phase. The future enhancements that you mentioned could also come handy in a visual designer. A few months ago I wrote a simple Python IDE for the Android platform that included a visual designer which produced hybrid Pythonista/Kivy codes. If I can spare the time I will probably adapt it to generate UniPAGe scripts. Once I finish the modification I will place the app on the Android market for free with no adds and publish the code in Github under, uhhh..., the julia geeks license. I wrote it in “RFO BASIC”, an excellent free BASIC interpreter written by Paul Laughton a veteran BASIC interpreter/compiler developer who designed the BASICs versions of Atari, Apple II, Amiga computers back in the days. I wanted to see how far I could push RFO BASIC and also because a developer who goes by the handle Mougino publish and app that compiles “RFO BASIC” scripts straight into Android APKs and a matter minutes. It was a delightful experience dealing with both. In case I decide to port that IDE to Python through UniPAGe zooming, pinching and panning will be a must. Maybe, if you want to, you can help me with the Pythonista part. I will try to insert a screenshot of that designer below. Concerning the post that you mentioned above my reaction results from the logical implication of the following events. The modification was rush into Github just a day after I posted the original; That post did not mention my copyright or this thread; Intent to assign licenses implies a declaration of ownership through copyright or other means. You may judge for yourself. I do not know if you tried UniPAGe but it can run on any platform without modification to the code whatsoever. What would be the point of making it more complicated by having to modify codes depending on platforms? I must also tell you that I use your “PhoneManager.py” on a regular basis. I modified it to suit my needs and style. However, I never posted those modifications in any forum, let alone claiming ownership of the script and seek a license for it. Even if I judged the modifications important or original enough to be posted I would always refer to it as “modifications to YOUR script”. Thank you for participating @JonB.
By the way I intend to modify Einstein's well known and "well publicized" equation E = m * c ^ 2 to m = E / c ^ 2 because I am more interested in mass than energy. Therefore it will become "my" equation and I will apply for a patent for it. I am sure that a few participants in this thread will vouch for me at the patent office.
Sorry about the screenshot I could not find an easy way to insert it.
-
Hi Ti Leyon, I haven't found a "PhoneManager.py" at https://github.com/jsbain. If you used this PhoneManager by chance you're free to make everything, except selling the code. I'm a bit lazy so I never care about copyright things :), sorry. So what's the best license for my purpose?
-
@brumm https://choosealicense.com/ is a guide from GitHub to help you choose a license.
-
Look's like every license has a permission for commercial use :(. licenses