--
You received this message because you are subscribed to the Google Groups "iterm2-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to iterm2-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/iterm2-discuss/88722886-145b-419d-9cf0-00e67f60c813n%40googlegroups.com.
On 27 Sep 2021, at 0:04, George Nachman wrote:
Is there a place where examples like this can be collected?
I’ve been adding them here:
https://iterm2.com/python-api/examples/index.html. I’ve added yours and
it’ll appear next time I update the website.
OK. Thanks!
I'm using another one that displays the value of DOCKER_HOST, but that is more or less the same as the one for VIRTUAL_ENV.
Also it would be really great if we could merge status bar components and
tab titles. I.e. the tab title could consist of multiple components just
like the status bar does. Such a component would be usable both in the tab
title and the status bar.I think that doing this is harder for tab titles than for status bar
components because the space is so much smaller.
But status bar components seem to be already equipped with features that help reduce space: Empty components can be completely skipped, the component can return a list with output of varying lengths so that iTerm can pick the appropriate one and the user can configure various preferences for compression and minimum/maximum width. This seems to me to be much better that what we have for tab titles, which is basically one string returned by the tab title component. Or am I misunderstanding something here?
That’s why custom tab
titles have to be implemented by an API script. The existing components
(job name, pwd, etc.) are combined by a really complicated built-in
algorithm that tries to do something sane for all combinations of
components.
Then I could configure my tab title simply by assembling components. By
using spacers I could make some of them right aligned (and using "Remove
components with empty values" like in the status bar, I could make empty
ones disappear).It would be neat if custom title providers were configurable like status
bar components. I think that’s the best way to do this.
Indeed that would be great. I've looked at your title bar example at https://iterm2.com/python-api/examples/georges_title.html and the title bar seems to consist of "components" anyway:
parts = [make_title(auto_name, profile_name),
make_hostname(hostname, localhost),
make_pwd(user_home, localhome, pwd),
make_branch(branch)]
return " ".join(list(filter(lambda x: x, parts)))
i.e. make_title(...) returns a "component", make_hostname(...) returns one etc.
return " ".join(list(filter(lambda x: x, parts))) combines the components and skips empty ones.
So a new version could turn these pseudo components (which are just strings) into real components that could be assembled and configured by the user.
And if the HTML functionality that the tab title provides was available
in the status bar components, this would allow enhanced styling for the
status bar component.Done in commit e089ffbfe3e024a94218084cef1e9413ffafd736. You’ll need
version 1.29 of the iterm2 module. The initializer for
the StatusBarComponent class now takes a format argument. It defaults to
iterm2.StatusBarComponent.Format.PLAIN_TEXT. You should use HTML instead of
PLAIN_TEXT. It only accepts <u>, <I>, and <b> for now.
Great! I look forward to the next iTerm version to try that out.
(And while we're at it, I would want that new component based
functionality for the window title and the badge too ;))This is a bit more work but is quite reasonable.
BTW, I wondered why status bar components implement their functionality the way they do. I would have expected that I can subclass iterm2.StatusBarComponent and implement formatting by overwriting the appropriate method, i.e. something like this:
class StatusBarPythonVEnv(iterm2.StatusBarComponent):
def __init__(self):
super().__init__(
short_description="Python virtual environment",
detailed_description="Show the currently active Python virtual environment",
knobs=[
iterm2.CheckboxKnob("Show even if unset?", False, "force"),
iterm2.CheckboxKnob("Shorten to trailing directory?", True, "shorten"),
],
exemplar="🐍\N{THIN SPACE}~/pyvenvs/default",
update_cadence=None,
identifier="com.livinglogic.walter.iterm.status.python-venv"
)
async def execute(
knobs,
python_venv=iterm2.Reference("user.python_venv?"),
user_home=iterm2.Reference("user.home?"),
):
if python_venv:
if knobs.get("shorten", True):
python_venv = python_venv.rsplit("/")[-1]
return f"🐍\N{THIN SPACE}{python_venv}"
elif knobs.get("force", False):
return f"🐍\N{THIN SPACE}\N{TWO-EM DASH}"
else:
return ""
component = StatusBarPythonVEnv()
await component.async_register(connection)
Instead the component object and the function that formats the output are totally unrelated. That makes it hard to give the component state which is independent of the user configurable knobs.
Servus,
Walter
On 1 Oct 2021, at 19:01, George Nachman wrote:
[...]
It certainly could have been designed that way. If you want to have
encapsulation, you can still do it, just without subclassing.
class MyStatusBar:
def __init__(self):
…
async def async_register(self, connection):
await self.execute.async_register(connection)
@iterm2.StatusBarRPC
async def execute(…):
…
component = MyStatusBar()
await component.async_register()
Disclaimer: I haven't actually tried it, but it's at least close to correct
:)
But where does the actual component get created?
I tried the following:
class PythonVenvStatusBar(iterm2.StatusBarComponent):
def __init__(self):
super().__init__(
short_description="Python virtual environment",
detailed_description="Show the currently active Python virtual environment",
knobs=[
iterm2.CheckboxKnob("Show even if unset?", False, "force"),
iterm2.CheckboxKnob("Shorten to trailing directory?", True, "shorten"),
],
exemplar="🐍\N{THIN SPACE}~/pyvenvs/default",
update_cadence=None,
identifier="com.livinglogic.walter.iterm.status.python-venv"
)
async def async_register(self, connection):
await self.execute.async_register(connection)
@iterm2.StatusBarRPC
async def execute(
self,
knobs,
python_venv=iterm2.Reference("user.python_venv?"),
user_home=iterm2.Reference("user.home?"),
):
if python_venv:
if knobs.get("shorten", True):
python_venv = python_venv.rsplit("/")[-1]
else:
python_venv = fancy_directory_name(user_home, python_venv)
return f"🐍\N{THIN SPACE}{python_venv}"
elif knobs.get("force", False):
return f"🐍\N{THIN SPACE}\N{TWO-EM DASH}"
else:
return ""
comp = PythonVenvStatusBar()
await comp.async_register(connection)
This gives me
Traceback (most recent call last):
File "/Users/walter/.config/iterm2/AppSupport/iterm2env/versions/3.8.6/lib/python3.8/site-packages/iterm2/connection.py", line 412, in async_connect
return await coro(self)
File "/Users/walter/.config/iterm2/AppSupport/iterm2env/versions/3.8.6/lib/python3.8/site-packages/iterm2/connection.py", line 219, in async_main
result = await coro(connection)
File "/Users/walter/.config/iterm2/AppSupport/Scripts/AutoLaunch/walters_title.py", line 354, in main
await comp.async_register(connection)
File "/Users/walter/.config/iterm2/AppSupport/Scripts/AutoLaunch/walters_title.py", line 333, in async_register
await self.execute.async_register(connection)
TypeError: async_register() missing 1 required positional argument: 'component'
And if I replace
async def async_register(self, connection):
await self.execute.async_register(connection)
with
async def register(self, connection):
await self.async_register(connection, self.execute)
(and then call register() instead of async_register()), I get:
❗️ Status bar component “Python virtual environment” (com.livinglogic.walter.iterm.status.python-venv) failed.
This function call had an error:
statusbar.com.livinglogic.walter.iterm.status.python_venv.execute(knobs:__knobs,python_venv:user.python_venv?,user_home:user.home?)
The error was:
No function registered for invocation “statusbar.com.livinglogic.walter.iterm.status.python_venv.execute()”. Ensure the script is running and the function name and argument names are correct. There is a similarly named function available with a different signature: statusbar.com.livinglogic.walter.iterm.status.python_venv.execute(knobs,python_venv,self,user_home)
I don't particularly like OO inheritance so my preference is to not force
people to use it if.
But in this case I think it's the right thing to done: Combine state and behaviour in one package.
Servus,
Walter
--
You received this message because you are subscribed to the Google Groups "iterm2-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to iterm2-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/iterm2-discuss/8C521166-5D3E-4357-B8C2-7A651EDA1327%40gmail.com.
OK, but the problem with a static method is that a static method can't access the instance attributes of the component either.
I tried decorating the bound method like this:
class StatusBarPythonVEnv(iterm2.StatusBarComponent):
def __init__(self):
super().__init__(
short_description="Python virtual environment",
detailed_description="Show the currently active Python virtual environment",
knobs=[
iterm2.CheckboxKnob("Show even if unset?", False, "force"),
iterm2.CheckboxKnob("Shorten to trailing directory?", True, "shorten"),
],
exemplar="🐍\N{THIN SPACE}~/pyvenvs/default",
update_cadence=None,
identifier="com.livinglogic.walter.iterm.status.python-venv2"
)
async def register(self, connection):
execute = iterm2.StatusBarRPC(self.execute)
await self.register(connection, execute)
async def execute(
knobs,
python_venv=iterm2.Reference("user.python_venv?"),
user_home=iterm2.Reference("user.home?"),
):
if python_venv:
if knobs.get("shorten", True):
python_venv = python_venv.rsplit("/")[-1]
else:
python_venv = fancy_directory_name(user_home, python_venv)
return f"🐍\N{THIN SPACE}{python_venv}"
elif knobs.get("force", False):
return f"🐍\N{THIN SPACE}\N{TWO-EM DASH}"
else:
return ""
async def main(connection):
component = StatusBarPythonVEnv()
await component.register(connection)
But this gives me:
22.10., 18:16:03,676: /bin/zsh -c /Applications/iTerm.app/Contents/Resources/it2_api_wrapper.sh /Users/walter/.config/iterm2/AppSupport/iterm2env/versions/3.8.6/bin/python3 /Users/walter/.config/iterm2/AppSupport/Scripts/AutoLaunch/walters_title.py
22.10., 18:16:03,692: + unset PYTHONPATH
22.10., 18:16:03,692: + export PYTHONUNBUFFERED=1
22.10., 18:16:03,692: + PYTHONUNBUFFERED=1
22.10., 18:16:03,692: + /Users/walter/.config/iterm2/AppSupport/iterm2env/versions/3.8.6/bin/python3 /Users/walter/.config/iterm2/AppSupport/Scripts/AutoLaunch/walters_title.py
22.10., 18:16:04,016: Connection accepted: Script launched by user action
22.10., 18:16:04,232: Traceback (most recent call last):
22.10., 18:16:04,232: File "/Users/walter/.config/iterm2/AppSupport/iterm2env/versions/3.8.6/lib/python3.8/site-packages/iterm2/connection.py", line 412, in async_connect
22.10., 18:16:04,232: return await coro(self)
22.10., 18:16:04,232: File "/Users/walter/.config/iterm2/AppSupport/iterm2env/versions/3.8.6/lib/python3.8/site-packages/iterm2/connection.py", line 219, in async_main
22.10., 18:16:04,232: result = await coro(connection)
22.10., 18:16:04,232: File "/Users/walter/.config/iterm2/AppSupport/Scripts/AutoLaunch/walters_title.py", line 386, in main
22.10., 18:16:04,232: await component.register(connection)
22.10., 18:16:04,232: File "/Users/walter/.config/iterm2/AppSupport/Scripts/AutoLaunch/walters_title.py", line 253, in register
22.10., 18:16:04,232: execute = iterm2.StatusBarRPC(self.execute)
22.10., 18:16:04,232: File "/Users/walter/.config/iterm2/AppSupport/iterm2env/versions/3.8.6/lib/python3.8/site-packages/iterm2/registration.py", line 359, in StatusBarRPC
22.10., 18:16:04,232: func.async_register = async_register
22.10., 18:16:04,232: AttributeError: 'method' object has no attribute 'async_register'
22.10., 18:16:04,242:
22.10., 18:16:04,242: Connection closed.
22.10., 18:16:04,268:
22.10., 18:16:04,268: ** Script exited with status 1 **
Servus,
Walter
--
You received this message because you are subscribed to the Google Groups "iterm2-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to iterm2-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/iterm2-discuss/4DD11AA8-5FE6-4310-B468-252C571040E0%40gmail.com.