Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Allow defining automatic actions when opening a sheet #1681

Closed
cool-RR opened this issue Jan 16, 2023 · 19 comments
Closed

Feature request: Allow defining automatic actions when opening a sheet #1681

cool-RR opened this issue Jan 16, 2023 · 19 comments
Labels

Comments

@cool-RR
Copy link
Contributor

cool-RR commented Jan 16, 2023

Whenever I open a sheet, I want VisiData to do some things automatically for me. This is stuff that I find myself doing manually every time, and it's tedious. For example:

  1. On any kind of sheet, I immediately want the columns to be with the correct width (g_).
  2. If I'm on a folder sheet, I want to hide the directory column.

I want these things to happen even if I'm opening a derived sheet in VisiData.

Basically I want to define a set of rules that check what kind of sheet I just opened, and based on that, do a list of pre-defined actions automatically.

@geekscrapy
Copy link
Contributor

Something like #1488 ?

@cool-RR
Copy link
Contributor Author

cool-RR commented Jan 16, 2023

Thanks for that thread. I copied some of the code from here into a command and now I have one command which I can launch to config all my columns the way I like them.

Now what's missing for me is a hook that'll automatically launch this command whenever a sheet is opened... Right now I just launch it with a key binding, which is an 80% solution.

@frosencrantz
Copy link
Contributor

It might be possible to use vd.sync() to wait for the loading to finish. I'm not sure if this is an async thread or not.
Or maybe wrap the open_* functions to perform your command after creating the new sheet.

@cool-RR, please share if you get this to work. This would be very useful.

@cool-RR
Copy link
Contributor Author

cool-RR commented Jan 22, 2023

Thanks, but I've lost interest in getting it to work for now, I think I'll just use my key binding every time I open a sheet.

@frosencrantz
Copy link
Contributor

@saulpw any suggestions for how Visidata could call a function after loading a sheet, so that customization could be performed like setting column types. I would also be very interested in this.

@saulpw
Copy link
Owner

saulpw commented Jan 29, 2023

One thing you could try that should work for any sheet type which uses iterload, is this (for JsonSheet in this case):

@JsonSheet.after
def iterload(sheet):
    ... whatever you want ...

It would have to be done separately for each specific sheet type, which may or may not be a problem for what you want to do.

I hear y'all though, and I'll give some thought to how we can do this. There are some places internally that could use this too.

@frosencrantz
Copy link
Contributor

I haven't tried this suggestion of the before and after decorators. I plan to. This was in the changelog but I didn't see it.

Does this work for DirSheet?

  1. If I'm on a folder sheet, I want to hide the directory column.

I have also asked to hide the "directory" column on the DirSheet when there is only one value. I like seeing similar interests:

@cool-RR
Copy link
Contributor Author

cool-RR commented Apr 10, 2023

@saulpw I now tried the method you suggested, but it doesn't work. The code does get invoked, but it appears that the data needed to perform autotune (columns and rows?) isn't ready on the sheet yet, so autotune doesn't work properly.

@ajkerrigan
Copy link
Collaborator

ajkerrigan commented Apr 11, 2023

This is a neat idea, and was making my brain itch because I'm pretty darn sure I remember talking through possibilities with folks in the past (it would have been folks already in this thread for sure).

In any case this issue motivated me to see if a generic post-load hook would be doable purely from .visidatarc (or a plugin) without breaking too much in the process.

Sample fumblings up here which I certainly wouldn't trust to handle payroll but might be fun to play with. If nothing else my sheets seem to be auto-resizing on load now, which (similar to @cool-RR) is typically the first thing I do when opening a sheet anyway.

@cool-RR
Copy link
Contributor Author

cool-RR commented Apr 11, 2023

@ajkerrigan Thank you. I tried it now and it works for some cases, not for others. When I use VisiData to browse a folder and press Enter to load a subfolder, it doesn't work. But even in this state it's very useful to me. Thank you!

@ajkerrigan
Copy link
Collaborator

Ah cool, I figured there would be some fun edge cases! I see that same behavior on my end when browsing subfolders. Looks like when the resize fires in that case, sheet.visibleRows is empty because sheet.topRowIndex is -1 🤔 . I don't really know why, but adding sheet.execCommand("go-top") before the resize seems to fix it 🤷 . (gist updated accordingly)

@saulpw
Copy link
Owner

saulpw commented Oct 28, 2023

Okay, these both work now, and are the officially endorsed way of accomplishing the goal of running some code or executing some command after the sheet has finished loading:

@DirSheet.after
def afterLoad(sheet):
    sheet.column('directory').hide()

@Sheet.after
def afterLoad(sheet):
    vd.queueCommand('resize-cols-max', sheet=sheet)

@Sheet.after monkey-parches the following function to run after the original. (@Sheet.before works similarly). With the new Sheet reload process, there's a Sheet.beforeLoad and a Sheet.afterLoad that should be called before/after Sheet.loader/Sheet.iterload. vd.queueCommand takes the standard longname and optional input, sheet, row, col kwargs, and schedules the command to run after the next keystroke or idle timeout. (This is also how replay works now.)

Hopefully these are sufficient tools to configure VisiData to do whatever you might want in your .visidatarc!

@saulpw saulpw closed this as completed Oct 28, 2023
@cool-RR
Copy link
Contributor Author

cool-RR commented Oct 28, 2023

I'm happy to hear that. I tried it now though and it didn't work. Not even when I did this:

@DirSheet.after
def call_autotune(sheet: Sheet) -> None:
    1 / 0

Am I doing something wrong?

The commit I have installed is 5f77e12e.

@geekscrapy
Copy link
Contributor

@DirSheet.after
def call_autotune(sheet: Sheet) -> None:
    1 / 0

I think you need to decorate the sheet.afterLoad function. Like:

@Dirsheet.after
def afterLoad(sheet):
   1/0

I haven't tried it, I could be mistaken!

@cool-RR
Copy link
Contributor Author

cool-RR commented Oct 28, 2023

@geekscrapy I don't understand at all. When you say "you need to decorate the sheet.afterLoad function" the best way I can parse it is that you're talking about the afterLoad method defined on the TableSheet class in sheets.py, but that sounds unlikely because it'll mean editing the actual VisiData source code or at least monkeypatching it rather than writing my own code in my .visidatarc.

@saulpw
Copy link
Owner

saulpw commented Oct 28, 2023

@geekscrapy is right, though said properly, in order to decorate the Sheet.afterLoad function, you need to name your function afterLoad. The pattern is: to have something run after the FooSheet.whateverFunc:

@FooSheet.after
def whateverFunc(...):

where the ... are the args/kwargs passed to the original whateverFunc.

@cool-RR
Copy link
Contributor Author

cool-RR commented Oct 28, 2023

Works, thank you.

For some reason though I couldn't get vd.queueCommand('autotune', sheet=sheet) to work. When I do it, it opens these sheets on startup. But I've grown tired of trying to debug this, so I just changed it to autotune(sheet) and I hope it won't break too often because of a sheet not being fully loaded.

@frosencrantz
Copy link
Contributor

This does not seem to work as I would expect using the examples from above #1681 (comment). it looks like Visidata gets stuck on a macro sheet. And with the DirSheet, I would like to be able to do an undo, as my first to unhide only the directory column, but that doesn't work.

I try a demo, where

  1. I run vd sample_data and VisiData gets stuck on the user_macros sheet, rather than starting on the DirSheet, which would be expected.
  2. Then using SheetsSheet I go to the sample_data DirSheet, and I cannot undo the hide. I would rather not do gv unhide-cols, since I don't want to unhide all columns. I'm not sure if this should be expected, but it would be useful if some of the changes made could be undone.

https://asciinema.org/a/Tgfk13C70oFb6OYIL3jFq3xiA

tail ~/.visidatarc
@Sheet.after
def afterLoad(sheet):
   vd.queueCommand('resize-cols-max', sheet=sheet)

@DirSheet.after
def afterLoad(sheet):
  sheet.column('directory').hide()

@saulpw
Copy link
Owner

saulpw commented Nov 20, 2023

Okay, thanks @frosencrantz. I don't know exactly what's happening, but I can tell the after-afterLoad that you're providing is running for every sheet, including those internal invisible sheets like the macros sheet and the plugins sheet, and those are conflicting somehow. I'm going to use this as the impetus to transition to a simpler internal persistence mechanism and then construct these internal sheets as needed, rather than on startup. The VisiData sheet is a view into the data, and shouldn't own the data itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants