Skip to content
47 changes: 43 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,18 @@ app.exec_()
- [x] Automatic label from name
- [x] Dual PEP8 and Qt syntax
- [ ] Theme support
- [x] HDPI support
- [x] Min/max values for sliders
- [ ] Wrap any instance of `argparse.ArgumentParser` with a GUI
- [ ] Argument Icons, for extra flare
- [ ] Argument Groups, e.g. tree view or side-panel

An example of use from within Maya.

<img width=600 src=https://user-images.githubusercontent.com/2152766/98442256-8a0e3880-20fb-11eb-8f1a-4524f297f324.gif>

- See [examples.py](https://github.com/mottosso/qargparse.py/blob/master/examples.py)

<br>

### Types
Expand Down Expand Up @@ -70,15 +77,13 @@ In addition to being able to subclass and create your own widgets, these are the

### Install

qargparse.py is available on PyPI.
Download or copy/paste [qargparse.py](https://github.com/mottosso/qargparse.py/archive/master.zip) into your project, there are no dependencies.

```bash
$ pip install qargparse.py
# Test it from a command-line like this
$ python -m qargparse --demo
```

> This also installs [Qt.py](https://github.com/mottosso/Qt.py) as a dependency. Skip this by passing `--no-deps` to `pip install`, note however that Qt.py is required in order for `qargparse.py` to run.

<br>

### Usage
Expand Down Expand Up @@ -125,6 +130,25 @@ parser.add_argument("age", default=54) # `int` inferred

<br>

### Default and Initial Values

There are two seemingly similar values per argument.

```py
Boolean("alive", default=True, initial=False)
```

What does this mean?

1. `False` is the initial value displayed in the UI
2. `True` is the value returned to when the value is *reset*

This way, you can store the current state of values elsewhere, such as on disk, and launch the UI with those values set, whilst still allowing the user to spot which values differs from their default values.

"Initial" is the value you would typically store on disk, keeping defaults as immutable somewhere in your application code.

<br>

### Types

In addition to standard types, QArgparse also supports user interface types.
Expand All @@ -148,6 +172,21 @@ qargparse.add_argument()

<br>

### Style

Customise the user experience, with `qargparse.DefaultStyle`.

```py
style = qargparse.DefaultStyle.copy()
style["comboboxFillWidth"] = True
parser = qargparse.QArgumentParser(style=style)
parser.show()
```

![](https://user-images.githubusercontent.com/2152766/98440756-78746300-20f2-11eb-9ff9-4475c86ac461.png)

<br>

### Signals

Respond to any change via the `.changed` signal.
Expand Down
101 changes: 101 additions & 0 deletions examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import qargparse

from qargparse import (
px,

# Reuse multi-binding logic
QtWidgets,
QtCore,
)


class BindSkinOptions(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(BindSkinOptions, self).__init__(parent)
self.setWindowTitle("Bind Skin Options")
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setMinimumWidth(px(550))
self.setMinimumHeight(px(370))

buttons = [
QtWidgets.QPushButton("Bind Skin"),
QtWidgets.QPushButton("Apply"),
QtWidgets.QPushButton("Close"),
]

central = QtWidgets.QWidget()
header = self.menuBar()
footer = QtWidgets.QWidget()

edit = header.addMenu("&Edit")
hlp = header.addMenu("&Help")

save = edit.addAction("Save Settings")
save.setEnabled(False)
reset = edit.addAction("Reset Settings")
reset.triggered.connect(self.on_reset)
hlp.addAction("Help on Bind Skin Options")

layout = QtWidgets.QHBoxLayout(footer)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(buttons[0])
layout.addWidget(buttons[1])
layout.addWidget(buttons[2])

args = [
qargparse.Enum("bindTo", items=["Joint Hierarchy",
"Selected Joints",
"Object Hierarchy"]),
qargparse.Enum("bindMethod", items=["Closest Distance",
"Closest in hierarchy",
"Geodesic voxel"]),
qargparse.Enum("skinningMethod", items=["Classic linear",
"Dual quaternion"]),
qargparse.Enum("normalizeWeights", items=["Interactive",
"Post"]),
qargparse.Enum("weightDistribution", items=["Distance",
"Neighbors"]),
qargparse.Boolean("allowMultipleBindPoses", default=True),
qargparse.Integer("maxInfluences", default=5, min=1, max=30,
initial=12),
qargparse.Boolean("maintainMaxInfluences",

# Value resetted to
default=True,

# Initial value on launch, differs from default
initial=False),
qargparse.Boolean("removeUnusedInfluences", default=True),
qargparse.Boolean("colorizeSkeleton", default=True, initial=False),
qargparse.Boolean("includeHiddenSelectionsOnCreation",
default=False),
qargparse.Float("falloff", min=0.0, max=1.0, default=0.2),
qargparse.Enum("resolution", items=["1024", "512", "256"],
default="256"),
qargparse.Boolean("validateVoxelState", default=True),
]

parser = qargparse.QArgumentParser(args)
parser.changed.connect(self.on_changed)

layout = QtWidgets.QVBoxLayout(central)
layout.addWidget(parser)
layout.addWidget(footer)

self._parser = parser
self.setCentralWidget(central)

def on_reset(self):
for arg in self._parser:
arg.write(arg["default"])

def on_changed(self, arg):
print("%s changed to %s" % (arg["name"], arg.read()))


if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = BindSkinOptions()
window.show()
app.exec_()
Loading