Forum Archive

Presenting view and console

KalyJupiter

Hi, how can I present a view fullscreen and also check the console from time to time ?

I've tried view.present('panel') to get the view in a separate tab alongside the console tab, but the trouble with this is that I write to the console constantly (every second or so), and Pythonista keeps changing my tab to the console one at each print - How do I get rid of that behavior, it's annoying as hell

I've also tried presenting normally, and changing the view's alpha from a ButtonItem action to see the underneath console, but I get a black screen instead, so how could I make the presented view actually transparent ?

right now I'm stuck with the following setup: a small button presented as 'sheet', that when clicked presents the fullscreen view - when I need to check the console logs I close the view and then I re-present it from the sheeted button...

Phuket2

Maybe there is a direct solution to this. But maybe another way is to have a menu ui.ButtonItem in the menu of your view. You could click that item and show another view (style-'pop up') with a ui.TextField. Rather than writing to the console append To the TextField. Where you are writing to the console, you could see if your popup view is 'onscreen' and if so append to the TextField. Or something similar. Just meaning, you can write to other places other than the console

KalyJupiter

I've actually, wonderfully, found what I was looking for, by accident, with the following setup:

button.present('popover')
button.action ⇒ view.present()
view::button_item.action ⇒ view.alpha = 0 ⇒ clean, transparent view to the console log

without the initial popover button, setting view.alpha = 0 results in a black, opaque screen.
seems the popover enables something specific for transparency…

here's a working example:

import ui
import time

view = ui.View()
view.frame = (0, 0, 240, 240)
view.flex = 'WH'
view.background_color = 'white'

def alphaaction(sender):
    print(view.alpha)
    if view.alpha == 1.0:
        view.alpha = 0.0
        sender.tint_color = 'red'
    else:
        view.alpha = 1.0
        sender.tint_color = 'black'

alphabutton = ui.ButtonItem()
alphabutton.action = alphaaction
alphabutton.title = 'alpha'
alphabutton.tint_color = 'black'
view.right_button_items = [alphabutton]

button = ui.Button()
button.action = lambda x: view.present()
button.frame = (0,0,40,40)
button.background_color = '#9cd8a1'


if False:
    view.present() # => black screen
else:
    button.present('popover') # => transparent screen

@ui.in_background
def run():
    k = 0
    while True:
        print("### test", k, "###")
        k += 1
        time.sleep(0.5)

run()
Phuket2

@KalyJupiter , consider the example below. With the beta version of Pythonista, this would be more straight forward as custom Views have an update method. You can set the interval for When the update method is called. It's maybe not 100% fit for what you are doing, but gives a different way to approach it. Hope its helpful

import ui
import time

# only use this func to create a button for convienience
def quick_button(p, title="", **kwargs):
    # p is the parent view
    _inset = 60
    btn = ui.Button(name='btn', **kwargs)
    btn.frame = ui.Rect(0, 0, p.width, p.width).inset(_inset, _inset)
    btn.corner_radius = btn.width / 2
    btn.center = p.bounds.center()
    btn.title = title
    btn.bg_color = 'cornflowerblue'
    btn.tint_color = 'white'
    btn.font = ('Arial Rounded MT Bold', 48)
    p.add_subview(btn)
    return btn

# Create a Custom ui.View, Inherit from ui.View
class MyClass(ui.View):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # if on the beta version update_interval sets the freq that the update
        # method is called.
        self.update_interval = 0
        self.tf = None
        self.make_view()
        self.run()

    def make_view(self):
        btn = quick_button(self, title='OK', action=self.my_btn_action)
        tf = ui.TextField(width=self.width)
        self.add_subview(tf)
        self.tf = tf

    @ui.in_background
    def run(self):
        k = 0
        while True:
            # if the view is not on screen break from the loop
            if not self.on_screen:
                break
            print("### test", k, "###")
            self.tf.text = "### test {} ###".format(k)
            k += 1
            time.sleep(0.5)

    # this method does nothing unless you have the beta version of Pythonista.
    # but the run method would not be needed. 
    def update(self):
        self.tf.text = "### test {} ###".format(k)

    def my_btn_action(self, sender):
        print('do something here')

if __name__ == '__main__':
    f = (0, 0, 300, 400)
    v = MyClass(frame=f)
    v.present(style='sheet')

Edit: there are a few small mistakes here if you were using the beta version of Pythonista. The update method is wrong. k should be a member instance of the class and updated in the update method. But it was more just to show the idea. Also the writing to the TextField could be accumulated if you needed, just could make another attr to append to and write the contents to the TextField.

KalyJupiter

that update_interval looks much cleaner :) but I don't have access to the beta, didn't know there was one.

on the other hand I needed the full console because I don't have the screen real-estate for an in-view text box - and then there's the amount of logs I go through when I do check the console…

hiding the view temporarily to check the logs was just what I was looking for :)

Phuket2

@KalyJupiter , ok, I understand. Was not sure of your complete requirements. Sometimes bare bones examples can be misleading, because the implementation can get more complex. Here is a gist That shows the same example, just with the beta in mind. Forget the button code. I just have that as a snippet. So easy for me to use in examples.
Your approach seems fine, but you need to find an elegant solution to exit your loop. The small problem is that ui.ButtonItem is slightly different than other ui elements.
Ui.ButtonItem is not subclassed from ui.View like most of the ui elements. The below snippet shows they are different. So for example, uiButtonItem does not have an on_screen attr.

If you search the forum, you will find the beta sign up form. I have no idea before the beta will make it to the App Store version. I have a feeling it wont be too long, but i am just guessing.

import ui

print("ui.ButtonItem")
print(dir(ui.ButtonItem()))

print("\n\nui.Button")
print(dir(ui.Button()))
KalyJupiter

cheers, I found the beta signup form :)

and I'm not worried about exiting the loop

JonB

Hmm, for some reason I thought the console is only shown the first time a particular script prints to it... but maybe I am mistaken.

If you are doing lots of back and forth between console and ui, you might also consider an Overlay. This woeks similar to a popover, but does not disappear when you interact with the console, and can be dragged around, minimized, etc. You can also have multiple Overlays active at the same time.

KalyJupiter

That Overlay class is quite nice :) I'll keep it around for later, but it does have some quirks:

for one, the below crashes when trying to set the .text:

if __name__=='__main__':
    view = ui.View()
    view.frame = (0, 0, 240, 240)
    view.flex = 'WH'
    view.background_color = 'white'
    valuefield = ui.TextField()
    valuefield.frame = (120, 34, 120, 34)
    valuefield.text = "ana" # ⇒ crashes Pythonista
    view.add_subview(valuefield)
    o=Overlay(content=view,parent=AppWindows.root())
JonB

Hmm, I couldn't reproduce the crash, though to get to run I had to add name to view (something I should fix), and also enabled touch so the textfield is active.

Can you enable the faulthandler (see @dgelessus's pythonista_startup repo) and let me know exactly what is crashing?

from overlay import Overlay,AppWindows
import ui
if __name__=='__main__':
    view = ui.View(name='hello')
    view.frame = (0, 0, 240, 240)
    view.flex = 'WH'
    view.background_color = 'white'
    valuefield = ui.TextField()
    valuefield.frame = (120, 34, 120, 34)
    valuefield.text = "ana" # ⇒ crashes Pythonista
    view.add_subview(valuefield)
    o=Overlay(content=view,parent=AppWindows.root())
    o.content_view.touch_enabled=True
KalyJupiter

@dgelessus awesome scripts :D
@JonB the above main body I placed in overlay.py and simply ran the file ( after correcting the name issue )

Fatal Python error: Aborted

Current thread 0x000000016e117000 (most recent call first):

------------------------------------------------------------------------

Objective-C exception details:

NSInternalInconsistencyException: Only run on the main thread!

Stack trace:

0   CoreFoundation                      0x000000018316fd50 <redacted> + 148
1   libobjc.A.dylib                     0x0000000182684528 objc_exception_throw + 56
2   CoreFoundation                      0x000000018316fc0c <redacted> + 0
3   Foundation                          0x0000000183afec90 <redacted> + 88
4   UIFoundation                        0x000000018d64a70c <redacted> + 1008
5   UIFoundation                        0x000000018d64a194 <redacted> + 1672
6   UIFoundation                        0x000000018d656de8 <redacted> + 168
7   UIFoundation                        0x000000018d647494 <redacted> + 4628
8   UIFoundation                        0x000000018d647f30 <redacted> + 196
9   UIFoundation                        0x000000018d648640 <redacted> + 340
10  UIFoundation                        0x000000018d6513b0 <redacted> + 2180
11  UIFoundation                        0x000000018d67aed0 <redacted> + 332
12  UIFoundation                        0x000000018d6a0528 <redacted> + 160
13  UIKit                               0x000000018d1cd658 <redacted> + 524
14  UIKit                               0x000000018c5c4eac <redacted> + 248
15  UIKit                               0x000000018c57c000 <redacted> + 1256
16  QuartzCore                          0x000000018714d0b4 <redacted> + 184
17  QuartzCore                          0x0000000187151194 <redacted> + 332
18  QuartzCore                          0x00000001870bff24 <redacted> + 336
19  QuartzCore                          0x00000001870e6340 <redacted> + 540
20  QuartzCore                          0x00000001870e7180 <redacted> + 92
21  CoreFoundation                      0x00000001831178b8 <redacted> + 32
22  CoreFoundation                      0x0000000183115270 <redacted> + 412
23  CoreFoundation                      0x00000001830362f8 CFRunLoopRunSpecific + 468
24  Foundation                          0x0000000183a5e6e4 <redacted> + 304
25  Foundation                          0x0000000183a7dafc <redacted> + 96
26  Py3Kit                              0x0000000102687508 -[PYK3Interpreter setupInterpreterThreadRunLoop] + 252
27  Foundation                          0x0000000183b5f860 <redacted> + 996
28  libsystem_pthread.dylib             0x0000000182d9c32c <redacted> + 308
29  libsystem_pthread.dylib             0x0000000182d9c1f8 <redacted> + 0
30  libsystem_pthread.dylib             0x0000000182d9ac38 thread_start + 4

End of exception details.
JonB

mmkay, looks like we need a @on_main_thread someplace. Could you try adding this in front of the init method of Overlay?

KalyJupiter

yep, that worked :D I put it on attach

@on_main_thread
def attach(self):

now that Overlay is awesome for a quick settings screen :)

struct_engineer

Using Overlays

My first post to the forum.

Using the Overlay class successfully — added the decorator to the attach function as was mentioned.

    @on_main_thread
    def attach(self):
        self.parent.addSubview_(self)

In my demo file, I have a main image overlay (resizable) and two auxilary image overlays (resizable). Each of the overlays could have any kind of UI elements; just showing images for proof of concept.

I have found that, for each overlay, the following statements must be in the order shown or else the overlay window will not resize.

    ooo=Overlay(name='abc', content=vvv, parent=AppWindows.root())
    ooo.content_view.border_width=2
    vvv.content_mode=ui.CONTENT_SCALE_ASPECT_FIT

Here is my demo file; provide your own PNG files (foo.png and fooe.png).

from overlay import Overlay, AppWindows
from gestures import Gestures
import ui

if __name__=='__main__':

    iv0=ui.ImageView(frame=(0,0,300,300))
    iv0.image=ui.Image.named('test:Peppers')
    iv0.name='OMAIN▪️'

    iv1=ui.ImageView(frame=(0,0,200,200))
    iv1.image=ui.Image.named('foo.png')
    iv1.name='ICR▪️'
    iv1.alpha=1
    overlay1=Overlay(content=iv1,parent=AppWindows.root())
    overlay1.content_view.border_width=2
    iv1.border_width=1
    iv1.content_mode=ui.CONTENT_SCALE_ASPECT_FIT

    iv2=ui.ImageView(frame=(0,0,200,200))
    iv2.image=ui.Image.named('fooe.png')
    iv2.name='E▪️'
    iv2.alpha=1
    overlay2=Overlay(content=iv2,parent=AppWindows.root())
    overlay2.content_view.border_width=2
    iv2.border_width=1
    iv2.content_mode=ui.CONTENT_SCALE_ASPECT_FIT

    omain=Overlay(name='abc', content=iv0, parent=AppWindows.root())
    omain.content_view.border_width=2
    iv0.border_width=1
    iv0.content_mode=ui.CONTENT_SCALE_ASPECT_FIT
mikael

@struct_engr, excellent first post.

struct_engr_

@struct_engr

Had to register as a new account.
Could not access my old account.
Several recover password attempts did not work.
So my new username is struct_engr_ (added underline character at the end)

mikael

@struct_engr_ , did your recover password attempts result in no email arriving, or was there some other issue?

struct_engr_

@mikael
each time:
no email arrived ASFAIK
carefully checked spam folder
possible that email hit blacklisted IP addresses
FYI
certain IP addresses blocked because of spam

mikael

@struct_engr_ , I think the problem is that for the past 6 months or so, no emails from this forum get actually sent. Neither the password resets or other notifications. @omz, any chance you could take a look?