Skip to content

feature More parameter item types#1844

Merged
j9ac9k merged 40 commits intopyqtgraph:masterfrom
ntjess:feature3
Jul 23, 2021
Merged

feature More parameter item types#1844
j9ac9k merged 40 commits intopyqtgraph:masterfrom
ntjess:feature3

Conversation

@ntjess
Copy link
Copy Markdown
Contributor

@ntjess ntjess commented Jun 18, 2021

It's highly likely that I failed or missed something in this effort. The pull request is an attempted direct copy from @feketeimre's older one (#665), but that passes all current master tests. If they can confirm whether anything is missing, this should be much easier to merge into the current branch and we can close the very outdated feature3 pull request

TODO:

  • Input sanitation on line-edit file parameter (or set to readonly)
  • Convert calendar to string on saveState (maybe add a format option); QDate has convenient toString method
  • Convert font to string on saveState
  • Better value saving on pen (no obvious way to include the entire pen state in the value field, so I'm currently playing with always saving a black pen and setting options like color, width, etc. to compensate. Has the downside of functioning differently from every other parameter whose value is self-contained...
  • A few Parameter bugfixes along the way (Slider optsChanged, WidgetParameterItem's signal hookup on value change

Pen: Pops up a dialouge that allows the user to customize a pen. Setting pen value is not working yet.
Progress bar: For indication things.
Slider: Easier way to set values that dont require precision.
Fonts: Picking font types. Next thing could be a Font dialog.
Calendar: For picking dates or intervals
Open/save file/files/directory: Pops up an open/save file/directory dialog to select a file/directory. Filter string and caption can be defined too.

A PenSelectorDialog widget was created for the pen parameter item too.

Also added these parameter items to the example.

@pijyoi
Copy link
Copy Markdown
Contributor

pijyoi commented Jun 19, 2021

Some bindings issues:

  1. PySide{2, 6} does not have QVariant
  2. PyQt6 requires long spelling for enums (see Convert Qt Enums to Qt6 Namespace #1818)

@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jun 19, 2021

@pijyoi wow, turns out combo boxes implicitly convert enums to ints, fun...

It should be fixed now, thanks.

tooltip = str(os.path.realpath(fn))
elif dialogType in ["getOpenFileNames","openFiles"]:
fn = dialogFunction(None, dialogCaption, self.lastDirectory, filterString)
fn = [os.path.realpath(asUnicode(f)) for f in fn if os.path.isfile(f) and f != ""]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this throws an Exception if the user clicks on Cancel in the dialog

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had more difficulty porting this over without problems, so I replaced it with a file parameter I've been using for about a 8 months without many issues. It supports almost all these features, but with (IMO) a bit smoother of a option interface.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, but you need to change the enums to the long spelling and also the exec_().
You should try running on PyQt6 6.1.1.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeez, installed pyside6 and I see what you mean. The enums should be fixed there as well.

@pijyoi
Copy link
Copy Markdown
Contributor

pijyoi commented Jun 20, 2021 via email

@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jun 27, 2021

Ok to merge? Suggestions? Comments? I want to do some housekeeping on parameter types but also want to wait until this is resolved.

@ixjlyons
Copy link
Copy Markdown
Member

I haven't looked over the code in detail yet, but a few high-level notes from playing around with the example:

  • The pen parameter should probably have the parameter name in col 0 and the button in col 1, and maybe a default button as well - it seems like it could be implemented as a WidgetParameterItem? I suppose the intent may have been that it's clear enough you're looking at a button with a "pen symbol" but in a GUI you might want multiple pen controls for different things.
  • The pen doesn't show display with its default value at first - i.e. if you open the pen dialog and cancel, it will update to red (the default).
  • The file picker would probably work best as the traditional line edit + "browse" button combo. Horizontal space in a parameter tree can get cramped, but having the file path in the push button isn't very effective.
  • We'll need a doc string for each new item that I suppose should just highlight any non-standard options, then add the new parameters/items to doc/source/parametertree/parameterTypes.rst. At a glance, the file picker looks like the only one with new options.

@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jun 28, 2021

@ixjlyons Thanks for the feedback. Do you recommend the current value of the file picker not show up anywhere visually? I'm ok with it and it's an easy change, I just wanted to be sure. Some alternatives are to just show the filename instead of full path or use ellipses (change elide mode on the label)

  • Are you OK with the file API -- existing, asFolder, fileFilter, selectMultiple, startDir? I.e., should any names be changed for clarity?
    • Currently fileFilter requires the same format as Qt (e.g. text files (*.txt);; ...), should a different format be allowed / provisioned for easier use?
  • Is DontUseNativeDialog common enough to expose at the parameter level? I know for me it caused some valuable differences in earlier Qt versions (<5.12), and people who want to add widgets to the filepicker cannot use the native choice.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jun 28, 2021

@ixjlyons Thanks for the feedback. Do you recommend the current value of the file picker not show up anywhere visually? I'm ok with it and it's an easy change, I just wanted to be sure. Some alternatives are to just show the filename instead of full path or use ellipses (change elide mode on the label)

  • Are you OK with the file API -- existing, asFolder, fileFilter, selectMultiple, startDir? I.e., should any names be changed for clarity?

This is the kind of API nitpick I can get behind!

  • asFolder I would advice using "Directory" instead of "Folder".

Is there a reason we shouldn't match up the existing Qt API here and just use directory (instead of startDir), filter (instead of fileFilter) and so on? There is likely a good idea why not, it's not the sort of thing I know much about unfortunately.

  • Currently fileFilter requires the same format as Qt (e.g. text files (*.txt);; ...), should a different format be allowed / provisioned for easier use?

Unless there is demand for another format, I would stick w/ that for a few reasons.

  1. it should be familiar to people w/ Qt experience already
  2. If someone learns about this format from pyqtgraph, they can apply it elsewhere in the Qt framework
  3. Minimizes our support footprint

If there is a feature request to add another syntax, we can evaluate that on its own merit, but even if we come up with a potentially simpler format; there is likely no guarantee it would be simpler for enough use-cases to make it worthwhile.

  • Is DontUseNativeDialog common enough to expose at the parameter level? I know for me it caused some valuable differences in earlier Qt versions (<5.12), and people who want to add widgets to the filepicker cannot use the native choice.

DontUseNativeDialog should likely go away, I think it was introduced in this library as a work-around for some issue on macOS eons ago.

@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jun 28, 2021

@j9ac9k

Is there a reason we shouldn't match up the existing Qt API here and just use directory (instead of startDir),

directory may refer to either starting directory or whether a directory should be selected, so I tried to disambiguate the two.

filter (instead of fileFilter)

I tend not to use builtin functions as variables, which is why I stayed away from filter (but it makes sense here to match the Qt API).

and so on?

The other accepted arguments are boolean versions of enum flags which are easier to store in the Parameter state and slightly simpler (my initial reasoning). I could also accept fileMode/options/etc. argument with stringified versions of desired flags, i.e.

Parameter.create(type='file', fileMode='ExistingFiles', options=['ShowDirsOnly', 'DontResolveSymlinks'], viewMode='Detail')

Additionally, I could wait until #1120 is merged and use true bitmasks for these. (My stream of consciousness for including this was to have provisions for a bitmask during saveState, but #1120 is irrelevant because these flags wouldn't be edited within the file parameter. Hence, single string or list-of-strings is probably the way to go if we proceed with this)

  • I could also override saveState to convert enums into string values before output. This means arguments like options can directly accept Option.DontResolveSymlinks|Option.ReadOnly. In general, though, I like the principle 'explicit is better than implicit' where the saved state is the same one input by the user when possible

FWIW, my initial reason for the boolean interface was simplicity and it might be intimidating to expose every aspect of a file dialog even if ~80% of it isn't used in common practice.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jun 28, 2021

@j9ac9k

Is there a reason we shouldn't match up the existing Qt API here and just use directory (instead of startDir),

directory may refer to either starting directory or whether a directory should be selected, so I tried to disambiguate the two.

Ahh good point!

filter (instead of fileFilter)

I tend not to use builtin functions as variables, which is why I stayed away from filter (but it makes sense here to match the Qt API).

Oh, missed the part this was a variable name, yeah built-ins as variable names is a no-no, could do filter_ (the same way qt bindings have navigated around exec), but I'm not strongly opinionated on this and am good w/ what you think is best.

The other accepted arguments are boolean versions of enum flags which are easier to store in the Parameter state and slightly simpler (my initial reasoning). I could also accept fileMode/options/etc. argument with stringified versions of desired flags, i.e.

Parameter.create(type='file', fileMode='ExistingFiles', options=['ShowDirsOnly', 'DontResolveSymlinks'], viewMode='Detail')

Either option (list of bools, list of stringified enums) sounds good to me; probably should support default behavior, and put a link in the documentation to the Qt docs for more info.

  • I could also override saveState to convert enums into string values before output. This means arguments like options can directly accept Option.DontResolveSymlinks|Option.ReadOnly. In general, though, I like the principle 'explicit is better than implicit' where the saved state is the same one input by the user when possible

FWIW, my initial reason for the boolean interface was simplicity and it might be intimidating to expose every aspect of a file dialog even if ~80% of it isn't used in common practice.

You've thought about this more than I have at this point, I'm good w/ it either way 👍🏻

@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jun 29, 2021

@ixjlyons

The file picker would probably work best as the traditional line edit + "browse" button combo. Horizontal space in a parameter tree can get cramped, but having the file path in the push button isn't very effective.

Fair point, horizontal space is a premium in parameter trees. How about this? If you like it, I'll push the fix.

kFpecVNWUC.mp4

@ixjlyons
Copy link
Copy Markdown
Member

That seems reasonable, definitely an improvement.

It still seems like it'd be useful to have the path in a line edit so it can be manually changed or copied/pasted. Maybe a button with just ... to save space or maybe it could use asSubItem to span both columns?

I'm totally fine saving that as an enhancement idea for later.

@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jun 29, 2021

Take 2

vokoscreenNG-2021-06-29_00-16-54.mp4

I also took the liberty of making a slightly more interactive pen parameter 🙂

vokoscreenNG-2021-06-29_00-42-45.mp4

@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jun 29, 2021

I'm realizing right after pushing that a line edit will not interact nicely with ExistingFiles, unless I use ast.literal_eval and go down that rabbit hole... Perhaps make it readonly if multiple selection is enabled?

There's also no input sanitation currently, i.e. if you edit directly you can violate all constraints set on the parameter. I don't quite know what the ideal solution is other than to leave it for now. Maybe make it readonly by default, and the adventurous user can enable it if they so choose?

@ntjess ntjess marked this pull request as draft June 29, 2021 15:05
@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jul 1, 2021

With that, I think I've addressed all comments from the previous round. @ixjlyons and @j9ac9k thanks for your prior input, are you up for another go?

File picker is readonly by default and no sanitation or completer is provided (I'm considering it out of scope for now, someone can submit a PR if they wish). Those wanting to copy-paste can specify readonly=False in the opts at their own risk.

@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jul 8, 2021

With Scipy sprints and the new release coming up I know this will get delayed for a bit, any recommendations on when I can bump again? I'm looking to do a parameterTypes restructuring PR to avoid every new type modifying the same file. However, it would be easiest to wait until after this is addressed.

Thanks :)

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 8, 2021

Hi @ntjess I think the sprints, at least on Sunday, would be a great time to review this PR actually. @ixjlyons had an interest in revisiting the PR backlog, this issue, along w/ Nils's color palette one are some big PRs that would benefit from getting more eyes on IMO. Given many of us will be there, I think we can get some real-time feedback and hopefully merge 🤞🏻

@j9ac9k j9ac9k added the sprint Tag relating to issue/PR that is a good candidate for addressing during the sprints label Jul 8, 2021
global state
state = p.saveState()

QtCore.QTimer.singleShot(0, save)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had really weird issues involving QTimer.singleShot() (see #1605 )

Would suggest creating a timer, and explicitly calling setSingleShot You can see an example here: 85e894d

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you object to a safeSingleShot function in functions.py that spawns a QTimer this way, if the paradigm needs to be reused everywhere?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the delayed call to save() needed? You have the following right before pg.exec()

## test save/restore
state = p.saveState()
p.restoreState(state)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, there was an issue where the restored state didn't match the actual state before saving, so I delayed the initial save for things like the gradient editor's size to finalize. But testing it now, the singleShot seems unnecessary.

@ntjess
Copy link
Copy Markdown
Contributor Author

ntjess commented Jul 22, 2021

Rebased onto master, merge conflicts should be gone but the CI failure is still occurring. Is that linked to any of my changes? I can't reproduce locally on my windows machine so it's difficult for me to analyze

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 22, 2021

with what appears to be a segfault, causes like this can be really tough to isolate; even worse when it only happens in CI but not locally... best you can hope for is it fails in CI consistently 😆

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 23, 2021

what a rollercoaster this one was....

A lot of thanks for this PR.

Thank you @feketeimre for the original submission
Thank you @ntjess for updating/modernizing + bearing through the CI issue
Thank you to the reviewers of this PR from the SciPy sprints.

Squashing and merging this one!

@j9ac9k j9ac9k merged commit 8182376 into pyqtgraph:master Jul 23, 2021
@ntjess ntjess deleted the feature3 branch August 5, 2021 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sprint Tag relating to issue/PR that is a good candidate for addressing during the sprints

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants