-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add style sheet in pyqtgraph #2664
base: master
Are you sure you want to change the base?
Conversation
Feel free to use #1625 for inspiration That PR never got merged in large part due to the size/scope and my reluctance to merge something with such a big diff... I do regret not reviewing that more thoroughly in a timely fashion. |
pyqtgraph/__init__.py
Outdated
### Set style options | ||
## All options related to style are set here | ||
## Init default style | ||
from .style.core import loadDefautStyle |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should that be "DefaultStyle" throughout, maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't get that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spelling: "Defaut" != "Default"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol that's a very French mistake to do ^^ .
Thanks.
Hi @edumur , It is great to see someone working on this, I hope you can get this merged! But there's some things in that earlier attempt that might be worth looking at. Two things come to mind: It would be great to expose some of the style colors through the existing color dictionary. For example, so that user generated graphs (drawn with a "foreground color") can function in both light and dark mode. I had a working method to send redraw requests to all pyqtgraph items. That made it possible to apply changes to existing graphs. I had been expecting to find something built in on the Qt level, but somehow that all failed to work. There's also a number of color palettes for styling already :) Best of luck! Unfortunately, I looks like I'll be in zero-free-time mode until at least end of May. |
Thanks for sharing the previous request! |
Is there any docstring style recommanded? |
numpy docstyle for sure, ... maybe one evening if I'm too tired to do any coding, I can start converting existing docstrings over... |
Of course I started with google style... |
Is there a reason why some objects are defined with an Item in there name such as "LabelItem" and others without such as "ScaleBar"? |
So first I want to acknowledge that our API consistency isn't as good as it should be. That said, PyQtGraph classes that end in Item typically inherit from We recently added this diagram to help with understanding the relationships between PyQtGraph and parent Qt classes. https://pyqtgraph.readthedocs.io/en/latest/api_reference/uml_overview.html |
Thanks, I am still confused with "item", "object", "view" and, "widget". |
SomethingObject inherits from QGraphicsObject, and SomethingView from QGraphicsView, same with QGraphicsWidget for being SomethingWidget. What complicates things is that QGraphicsObject inherits from QGraphicsItem, and QGraphicsWidget inherits from QGraphicsObject 😆 Also what can complicate matters is that I don't believe you can do multiple inheritances of QObject sublcasses All that said, we do have inconsistencies in our naming scheme which definitely does not help. |
Hi all, Some items have their style option stored in an attribute |
I low-key dislike |
Can you link a few locations where Also -- Is there a reason to define a |
Sure: here. style.core is made to host any general style related code. |
I'm reluctant towards yaml as that would involve an extra dependency. Python 3.11 introduced |
Thanks, I was doing a search for
ConfigParser is easily customizable: from configparser import ConfigParser, Interpolation
import ast
class TypedInterpolation(Interpolation):
def before_get(self, parser, section, option, value, defaults):
try:
return ast.literal_eval(value)
except (ValueError, SyntaxError):
return super().before_get(parser, section, option, value, defaults)
typed_config = ConfigParser(interpolation=TypedInterpolation())
ini_file = """
[linePlot]
string = 'hello'
tuple = (1, 2, 3)
list = [1, 2, 3]
dict = {'a': 1, 'b': 2, 'c': 3}
int = 1
float = 1.0
bool = True
none = None
"""
typed_config.read_string(ini_file)
for key, value in typed_config.items('linePlot'):
print(key, value, type(value))
# string hello <class 'str'>
# tuple (1, 2, 3) <class 'tuple'>
# list [1, 2, 3] <class 'list'>
# dict {'a': 1, 'b': 2, 'c': 3} <class 'dict'>
# int 1 <class 'int'>
# float 1.0 <class 'float'>
# bool True <class 'bool'>
# none None <class 'NoneType'> This implementation essentially allows any builtin python type, defaulting to a string otherwise |
@@ -1268,7 +1707,7 @@ | |||
else: | |||
return lv.mouseDragEvent(event, axis=0) | |||
|
|||
def mouseClickEvent(self, event): | |||
def mouseClickEvent(self, event: MouseClickEvent) -> None: |
Check notice
Code scanning / CodeQL
Explicit returns mixed with implicit (fall through) returns Note
# if not isinstance(labelColor, ConfigColorHint): | ||
# raise ValueError('The given labelColor type is "{}", must a "ConfigColorHint"'.format(type(labelColor).__name__)) |
Check notice
Code scanning / CodeQL
Commented-out code Note
from math import ceil, floor, isfinite, log, log10 | ||
from typing import Any, List, Optional, Tuple, TypedDict, Union |
Check notice
Code scanning / CodeQL
Unused import Note
@@ -1254,7 +1693,7 @@ | |||
lv.wheelEvent(event, axis=0) | |||
event.accept() | |||
|
|||
def mouseDragEvent(self, event): | |||
def mouseDragEvent(self, event: MouseDragEvent) -> None: |
Check notice
Code scanning / CodeQL
Explicit returns mixed with implicit (fall through) returns Note
Dear all, To keep consistency across items, I think to rename ax.setStyle(showValues=show_value) After ax.setStyle('showValues', show_value)
ax.setStyles(showValues=show_value) We could maybe do differently, it is not set in stone. |
It is better to avoid breaking changes immediately; instead, consider a deprecation warning while keeping old behavior: def setStyle(...):
warnings.warn("`setStyle` is deprecated in favor of `setStyles`, see XXX", FutureWarning)
... Also, for pieces of the library that are as old as this, deprecation may not even be an option (884 results on github alone). We might just want to leave a comment in the docstring that new behavior is preferred, but keep everything else. Curious about others' thoughts, though |
That's fine, I think I can keep the old name by renaming my new method. I already put warnings around like this one in warnings.warn('Argument "bold" is deprecated, "fontweight" should be used instead.',
DeprecationWarning,
stacklevel=2) |
Hi all, Just push the last update.
|
Hi there @edumur Looks like read the docs doesn't like some of the enum namespacing;
I think this should be |
I need to go through this diff more closely; but at first glance, I would steer away from |
The read the docs error in the CI has already been fixed on |
To be sure I do not f** up things. Thanks. |
Since you made a PR, I actually have push rights to your branch; I'd be happy to do it if you'd like. If you want to do it, these are the steps I would do... # go to your branch
git switch style
# create a backup in case things get hosed up
git switch -c style-backup
git switch master
# sync up with upstream locally on your disk
git pull upstream master
git switch style
git rebase master When you run that last command, you're going to get merge conflicts that stop the rebase process, on each one of those, you'll have to resolve the conflict in the file, then run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CodeQL found more than 10 potential problems in the proposed changes. Check the Files changed tab for more details.
Read the docs reports the following errors (read the docs is configured to treat warnings as errors).
|
Is there a way to run these tests locally? Currently I am running (plotter_env) PS C:\Users\etien\Desktop\edumur\script\pyqtgraph> python .\test.py
sys.platform: win32
sys.version: 3.10.11 | packaged by Anaconda, Inc. | (main, May 16 2023, 00:55:32) [MSC v.1916 64 bit (AMD64)]
qt bindings: PyQt5 5.15.9 Qt 5.15.2
pyqtgraph: 0.13.4.dev0; None
config:
{'crashWarning': False,
'editorCommand': None,
'enableExperimental': False,
'exitCleanup': True,
'imageAxisOrder': 'col-major',
'leftButtonPan': True,
'mouseRateLimit': 100,
'segmentedLineMode': 'auto',
'useCupy': False,
'useNumba': False,
'useOpenGL': False}
============================================================================================= test session starts ==============================================================================================
platform win32 -- Python 3.10.11, pytest-7.4.0, pluggy-1.2.0
rootdir: C:\Users\etien\Desktop\edumur\script\pyqtgraph
configfile: pytest.ini
collected 417 items
pyqtgraph\examples\test_examples.py ...................................s..................................s.s................Timeout (0:01:00)!
Thread 0x00005268 (most recent call first):
File "C:\Users\etien\Desktop\edumur\script\pyqtgraph\pyqtgraph\examples\test_examples.py", line 164 in testExamples
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\python.py", line 194 in pytest_pyfunc_call
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_callers.py", line 80 in _multicall
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_manager.py", line 112 in _hookexec
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_hooks.py", line 433 in __call__
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\python.py", line 1788 in runtest
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\runner.py", line 169 in pytest_runtest_call
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_callers.py", line 80 in _multicall
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_manager.py", line 112 in _hookexec
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_hooks.py", line 433 in __call__
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\runner.py", line 262 in <lambda>
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\runner.py", line 341 in from_call
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\runner.py", line 261 in call_runtest_hook
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\runner.py", line 222 in call_and_report
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\runner.py", line 133 in runtestprotocol
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\runner.py", line 114 in pytest_runtest_protocol
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_callers.py", line 80 in _multicall
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_manager.py", line 112 in _hookexec
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_hooks.py", line 433 in __call__
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\main.py", line 349 in pytest_runtestloop
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_callers.py", line 80 in _multicall
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_manager.py", line 112 in _hookexec
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_hooks.py", line 433 in __call__
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\main.py", line 324 in _main
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\main.py", line 270 in wrap_session
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\main.py", line 317 in pytest_cmdline_main
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_callers.py", line 80 in _multicall
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_manager.py", line 112 in _hookexec
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\pluggy\_hooks.py", line 433 in __call__
File "C:\Users\etien\anaconda3\envs\plotter_env\lib\site-packages\_pytest\config\__init__.py", line 166 in main
File "C:\Users\etien\Desktop\edumur\script\pyqtgraph\test.py", line 24 in <module> |
Nevermind, I think I solved it . |
Let me know if you need help debugging test failures! |
Dear all,
I thought it will be a nice feature to have style sheet, in the "same" way as matplotlib.
Here, I have just implemented a very simple version of that feature in hope to have feedback before going further.
Currently nothing is changed (and normally nothing is crashing) except all style options are now on a dedicated
default
style sheet.How it works
When pyqtgraph is loaded, the default style is loaded from the new
core.style
file.All the style options are stored in
configStyle
which is a dictionnary.Throughout the lib, default style options are taken from that dictionnary.
To use other styles, use
core.use
and load another style file.Some remarks
pstyle
... Do we keep it?Tasks
Item to go through and transfer to the new styling paradigms:
What I plan to do if we decide to move forwards
To be checked:
Example
White official style
Breaking change
Currently None.
Other Tasks
Bump Dependency Versions
Files that need updates
I confirm that I didn't touch that yet.
README.md
setup.py
tox.ini
.github/workflows/main.yml
and associatedrequirements.txt
and condaenvironemt.yml
filespyproject.toml
binder/requirements.txt
Pre-Release Checklist
Pre Release Checklist
I confirm that I didn't touch that yet.
__init__.py
CHANGELOG
primarily using contents from automated changelog generation in GitHub release pagePost-Release Checklist
Steps To Complete
I confirm that I didn't touch that yet.
.dev0
to__version__
in__init__.py