michis-python-sammlung 0.8__tar.gz → 0.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- michis_python_sammlung-0.8.2/PKG-INFO +184 -0
- michis_python_sammlung-0.8.2/README.md +167 -0
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/pyproject.toml +1 -1
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung/pyqt/pyqt_util.py +23 -4
- michis_python_sammlung-0.8.2/src/michis_python_sammlung/pyqt/widgets/__init__.py +15 -0
- {michis_python_sammlung-0.8/src/michis_python_sammlung/pyqt → michis_python_sammlung-0.8.2/src/michis_python_sammlung/pyqt/widgets}/clickable_label.py +15 -6
- {michis_python_sammlung-0.8/src/michis_python_sammlung/pyqt → michis_python_sammlung-0.8.2/src/michis_python_sammlung/pyqt/widgets}/expandable_widget.py +13 -17
- michis_python_sammlung-0.8.2/src/michis_python_sammlung/pyqt/widgets/flow_layout.py +126 -0
- michis_python_sammlung-0.8.2/src/michis_python_sammlung/pyqt/widgets/password_widget.py +83 -0
- michis_python_sammlung-0.8.2/src/michis_python_sammlung/pyqt/widgets/progress_widget.py +135 -0
- michis_python_sammlung-0.8.2/src/michis_python_sammlung.egg-info/PKG-INFO +184 -0
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung.egg-info/SOURCES.txt +7 -3
- michis_python_sammlung-0.8/PKG-INFO +0 -16
- michis_python_sammlung-0.8/README.md +0 -0
- michis_python_sammlung-0.8/src/michis_python_sammlung.egg-info/PKG-INFO +0 -16
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/setup.cfg +0 -0
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung/__init__.py +0 -0
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung/pyqt/__init__.py +0 -0
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung/pyqt/icons/__init__.py +0 -0
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung/pyqt/image_paths.py +0 -0
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung.egg-info/dependency_links.txt +0 -0
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung.egg-info/requires.txt +0 -0
- {michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: michis_python_sammlung
|
|
3
|
+
Version: 0.8.2
|
|
4
|
+
Summary: Personal collection of mostly QT related stuff
|
|
5
|
+
Author: Michael Mischko
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mischkomichael/Michis_python_sammlung
|
|
8
|
+
Project-URL: Repository, https://github.com/mischkomichael/Michis_python_sammlung
|
|
9
|
+
Project-URL: Issues, https://github.com/mischkomichael/Michis_python_sammlung/issues
|
|
10
|
+
Keywords: pyqt6,widgets,gui
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: PyQt6<7,>=6.11
|
|
17
|
+
|
|
18
|
+
# Publishing a New Release
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
* Maintain project metadata in `pyproject.toml`.
|
|
23
|
+
* Keep the project in a dedicated virtual environment (`.venv`).
|
|
24
|
+
* Runtime dependencies are declared in `pyproject.toml` under `dependencies`, **not** by freezing the virtual environment.
|
|
25
|
+
* The GitHub repository is connected to the existing PyPI project via **Trusted Publishing**.
|
|
26
|
+
|
|
27
|
+
## Local Build
|
|
28
|
+
|
|
29
|
+
Build the package locally before creating a release:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
python -m build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This creates:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
dist/
|
|
39
|
+
package_name-x.y.z.tar.gz
|
|
40
|
+
package_name-x.y.z-py3-none-any.whl
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Optionally verify the package:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python -m twine check dist/*
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Releasing
|
|
50
|
+
|
|
51
|
+
1. Update the version in `pyproject.toml`.
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
|
|
55
|
+
```toml
|
|
56
|
+
version = "1.2.3"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
2. Commit and push the changes.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git add .
|
|
63
|
+
git commit -m "Release 1.2.3"
|
|
64
|
+
git push
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
3. Create a matching Git tag.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git tag v1.2.3
|
|
71
|
+
git push origin v1.2.3
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
4. GitHub Actions builds the package and publishes it to PyPI.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
# Important Caveats
|
|
79
|
+
|
|
80
|
+
## Always increase the version
|
|
81
|
+
|
|
82
|
+
PyPI does **not** allow uploading a package with the same version twice.
|
|
83
|
+
|
|
84
|
+
Every release must have a unique version number.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## If the GitHub Action fails because the version was wrong
|
|
89
|
+
|
|
90
|
+
Simply pressing **Re-run jobs** is **not sufficient** if the commit itself is incorrect.
|
|
91
|
+
|
|
92
|
+
Instead:
|
|
93
|
+
|
|
94
|
+
1. Fix `pyproject.toml`.
|
|
95
|
+
|
|
96
|
+
2. Commit and push the fix.
|
|
97
|
+
|
|
98
|
+
3. Delete the old tag locally.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
git tag -d v1.2.3
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
4. Delete the tag on GitHub.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
git push origin :refs/tags/v1.2.3
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
5. Create the tag again on the corrected commit.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
git tag v1.2.3
|
|
114
|
+
git push origin v1.2.3
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This triggers the workflow again using the corrected source.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
# Common Build Issues
|
|
122
|
+
|
|
123
|
+
### Missing README
|
|
124
|
+
|
|
125
|
+
If `pyproject.toml` contains
|
|
126
|
+
|
|
127
|
+
```toml
|
|
128
|
+
readme = "README.md"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
then `README.md` must exist.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### License configuration
|
|
136
|
+
|
|
137
|
+
Use the modern license expression, for example:
|
|
138
|
+
|
|
139
|
+
```toml
|
|
140
|
+
license = "MIT"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Do **not** also include the old license classifier:
|
|
144
|
+
|
|
145
|
+
```toml
|
|
146
|
+
"License :: OSI Approved :: MIT License"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Modern versions of `setuptools` reject this combination.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
# Useful Commands
|
|
154
|
+
|
|
155
|
+
Build:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
python -m build
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Check package:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
python -m twine check dist/*
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Delete local release tag:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
git tag -d vX.Y.Z
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Delete remote release tag:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
git push origin :refs/tags/vX.Y.Z
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Create and push release tag:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
git tag vX.Y.Z
|
|
183
|
+
git push origin vX.Y.Z
|
|
184
|
+
```
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Publishing a New Release
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
* Maintain project metadata in `pyproject.toml`.
|
|
6
|
+
* Keep the project in a dedicated virtual environment (`.venv`).
|
|
7
|
+
* Runtime dependencies are declared in `pyproject.toml` under `dependencies`, **not** by freezing the virtual environment.
|
|
8
|
+
* The GitHub repository is connected to the existing PyPI project via **Trusted Publishing**.
|
|
9
|
+
|
|
10
|
+
## Local Build
|
|
11
|
+
|
|
12
|
+
Build the package locally before creating a release:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
python -m build
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This creates:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
dist/
|
|
22
|
+
package_name-x.y.z.tar.gz
|
|
23
|
+
package_name-x.y.z-py3-none-any.whl
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Optionally verify the package:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
python -m twine check dist/*
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Releasing
|
|
33
|
+
|
|
34
|
+
1. Update the version in `pyproject.toml`.
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
|
|
38
|
+
```toml
|
|
39
|
+
version = "1.2.3"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2. Commit and push the changes.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git add .
|
|
46
|
+
git commit -m "Release 1.2.3"
|
|
47
|
+
git push
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
3. Create a matching Git tag.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git tag v1.2.3
|
|
54
|
+
git push origin v1.2.3
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
4. GitHub Actions builds the package and publishes it to PyPI.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
# Important Caveats
|
|
62
|
+
|
|
63
|
+
## Always increase the version
|
|
64
|
+
|
|
65
|
+
PyPI does **not** allow uploading a package with the same version twice.
|
|
66
|
+
|
|
67
|
+
Every release must have a unique version number.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## If the GitHub Action fails because the version was wrong
|
|
72
|
+
|
|
73
|
+
Simply pressing **Re-run jobs** is **not sufficient** if the commit itself is incorrect.
|
|
74
|
+
|
|
75
|
+
Instead:
|
|
76
|
+
|
|
77
|
+
1. Fix `pyproject.toml`.
|
|
78
|
+
|
|
79
|
+
2. Commit and push the fix.
|
|
80
|
+
|
|
81
|
+
3. Delete the old tag locally.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git tag -d v1.2.3
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
4. Delete the tag on GitHub.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git push origin :refs/tags/v1.2.3
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
5. Create the tag again on the corrected commit.
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
git tag v1.2.3
|
|
97
|
+
git push origin v1.2.3
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This triggers the workflow again using the corrected source.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
# Common Build Issues
|
|
105
|
+
|
|
106
|
+
### Missing README
|
|
107
|
+
|
|
108
|
+
If `pyproject.toml` contains
|
|
109
|
+
|
|
110
|
+
```toml
|
|
111
|
+
readme = "README.md"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
then `README.md` must exist.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### License configuration
|
|
119
|
+
|
|
120
|
+
Use the modern license expression, for example:
|
|
121
|
+
|
|
122
|
+
```toml
|
|
123
|
+
license = "MIT"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Do **not** also include the old license classifier:
|
|
127
|
+
|
|
128
|
+
```toml
|
|
129
|
+
"License :: OSI Approved :: MIT License"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Modern versions of `setuptools` reject this combination.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
# Useful Commands
|
|
137
|
+
|
|
138
|
+
Build:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
python -m build
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Check package:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
python -m twine check dist/*
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Delete local release tag:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
git tag -d vX.Y.Z
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Delete remote release tag:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git push origin :refs/tags/vX.Y.Z
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Create and push release tag:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
git tag vX.Y.Z
|
|
166
|
+
git push origin vX.Y.Z
|
|
167
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from PyQt6.QtWidgets import QWidget, QGraphicsOpacityEffect, QLayout, QMainWindow
|
|
1
|
+
from PyQt6.QtWidgets import QWidget, QGraphicsOpacityEffect, QLayout, QMainWindow, QVBoxLayout
|
|
2
2
|
from PyQt6.QtGui import QPainter, QColor, QColorConstants, QPen, QGuiApplication
|
|
3
|
-
from PyQt6.QtCore import QRect, pyqtBoundSignal
|
|
3
|
+
from PyQt6.QtCore import QRect, pyqtBoundSignal, QPoint
|
|
4
4
|
from contextlib import contextmanager
|
|
5
5
|
from typing import Generator, TypeVar
|
|
6
6
|
|
|
@@ -86,12 +86,31 @@ def add_rect_paint_to_widget(widget: QWidget, thickness: int = 2, color: QColor
|
|
|
86
86
|
paintEvent(event)
|
|
87
87
|
widget.paintEvent = paintRect
|
|
88
88
|
|
|
89
|
-
def clear_layout(layout: QLayout) -> None:
|
|
89
|
+
def clear_layout(layout: QLayout, delete_widgets: bool = False, of_type = None) -> None:
|
|
90
90
|
while layout.count() > 0:
|
|
91
91
|
item = layout.takeAt(0)
|
|
92
92
|
if item.widget():
|
|
93
|
-
item.widget().
|
|
93
|
+
item.widget().setParent(None)
|
|
94
|
+
if delete_widgets:
|
|
95
|
+
item.widget().deleteLater()
|
|
94
96
|
elif item.layout():
|
|
95
97
|
clear_layout(item.layout())
|
|
96
98
|
|
|
99
|
+
def index_at_v_pos(layout: QVBoxLayout, pos: QPoint) -> int:
|
|
100
|
+
# this is assuming NOT involved widgets at first and last position in the layout
|
|
101
|
+
category_item_index = 1
|
|
102
|
+
for i in range(1, layout.count()-1):
|
|
103
|
+
item = layout.itemAt(i)
|
|
104
|
+
w = item.widget()
|
|
105
|
+
|
|
106
|
+
if w is None or not w.isVisible():
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
geo = w.geometry()
|
|
110
|
+
|
|
111
|
+
if pos.y() < geo.bottom():
|
|
112
|
+
return category_item_index
|
|
113
|
+
category_item_index += 1
|
|
114
|
+
return category_item_index-1 # before final stretch, if you have one
|
|
115
|
+
|
|
97
116
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .flow_layout import FlowLayout
|
|
2
|
+
from .clickable_label import Clickable_label, Togglable_clickable_label
|
|
3
|
+
from .expandable_widget import Spoiler_widget, Expandable_widget
|
|
4
|
+
from .password_widget import PasswordDialog
|
|
5
|
+
from .progress_widget import ProgressWidget
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"FlowLayout",
|
|
9
|
+
"Clickable_label",
|
|
10
|
+
"Togglable_clickable_label",
|
|
11
|
+
"Spoiler_widget",
|
|
12
|
+
"Expandable_widget",
|
|
13
|
+
"PasswordDialog",
|
|
14
|
+
"ProgressWidget"
|
|
15
|
+
]
|
|
@@ -8,7 +8,7 @@ from numbers import Number
|
|
|
8
8
|
from typing import Callable
|
|
9
9
|
|
|
10
10
|
from PyQt6.QtGui import QImage
|
|
11
|
-
from ..
|
|
11
|
+
from ..image_paths import Image_paths
|
|
12
12
|
|
|
13
13
|
def create_alpha_mask(image: QImage | QPixmap) -> QBitmap:
|
|
14
14
|
# Ensure the image has an alpha channel
|
|
@@ -30,10 +30,12 @@ class Clickable_label(QLabel):
|
|
|
30
30
|
cursor: QPixmap | Qt.CursorShape = Qt.CursorShape.ArrowCursor,
|
|
31
31
|
scaled: float = None,
|
|
32
32
|
heuristic_mask: bool = True,
|
|
33
|
-
parent: QWidget = None
|
|
33
|
+
parent: QWidget = None,
|
|
34
|
+
press: bool = False
|
|
34
35
|
):
|
|
35
36
|
super().__init__(parent)
|
|
36
37
|
self.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
38
|
+
self._press = press
|
|
37
39
|
self._scaled = scaled
|
|
38
40
|
self.configure_pixmaps(pixmap, pixmap_hover, scaled=scaled)
|
|
39
41
|
self._hovering = False
|
|
@@ -65,7 +67,7 @@ class Clickable_label(QLabel):
|
|
|
65
67
|
self,
|
|
66
68
|
widget_to_replace: QWidget,
|
|
67
69
|
delete_old: bool = True
|
|
68
|
-
) -> Clickable_label
|
|
70
|
+
) -> "Clickable_label":
|
|
69
71
|
"""
|
|
70
72
|
Replace an existing QWidget with self. Return self if delete_old is True or a tuple (self, replaced_widget) if not.
|
|
71
73
|
"""
|
|
@@ -101,9 +103,16 @@ class Clickable_label(QLabel):
|
|
|
101
103
|
super().leaveEvent(a0)
|
|
102
104
|
|
|
103
105
|
def mouseReleaseEvent(self, ev):
|
|
104
|
-
self.
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
if not self._press:
|
|
107
|
+
self._on_click_action()
|
|
108
|
+
self.sg_clicked.emit(ev)
|
|
109
|
+
return super().mouseReleaseEvent(ev)
|
|
110
|
+
|
|
111
|
+
def mousePressEvent(self, ev):
|
|
112
|
+
if self._press:
|
|
113
|
+
self._on_click_action()
|
|
114
|
+
self.sg_clicked.emit(ev)
|
|
115
|
+
return super().mousePressEvent(ev)
|
|
107
116
|
|
|
108
117
|
def set_mask(self, new_size: QSize | None = None, heuristic_mask: bool=True) -> None:
|
|
109
118
|
new_size = new_size if new_size is not None else self._pixmap_hover.size()
|
|
@@ -3,7 +3,7 @@ from PyQt6.QtWidgets import QWidget, QApplication, QVBoxLayout, QHBoxLayout, QLa
|
|
|
3
3
|
from PyQt6.QtGui import QTransform, QPixmap, QPainter, QPen
|
|
4
4
|
import PyQt6.sip as sip
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from ..pyqt_util import clear_layout
|
|
7
7
|
from .clickable_label import Clickable_label, Togglable_clickable_label, Image_paths
|
|
8
8
|
|
|
9
9
|
import faulthandler
|
|
@@ -14,6 +14,7 @@ class Expandable_widget(QWidget):
|
|
|
14
14
|
ANIMATION_DURATION = 150
|
|
15
15
|
sg_animation_finished = pyqtSignal()
|
|
16
16
|
sg_animation_started = pyqtSignal()
|
|
17
|
+
sg_toggle = pyqtSignal(bool)
|
|
17
18
|
|
|
18
19
|
_horizontal_directions = ("left", "right")
|
|
19
20
|
_vertical_directions = ("up", "down")
|
|
@@ -161,12 +162,12 @@ class Expandable_widget(QWidget):
|
|
|
161
162
|
g.moveLeft(right)
|
|
162
163
|
self.setGeometry(g)
|
|
163
164
|
|
|
164
|
-
def
|
|
165
|
-
|
|
166
|
-
bottom =
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
self.setGeometry(
|
|
165
|
+
def _set_outer_height_anchor_bottom(self, outer_h: int) -> None:
|
|
166
|
+
geometry = self.geometry()
|
|
167
|
+
bottom = geometry.y() + geometry.height()
|
|
168
|
+
geometry.setHeight(outer_h)
|
|
169
|
+
geometry.moveTop(bottom - outer_h)
|
|
170
|
+
self.setGeometry(geometry)
|
|
170
171
|
|
|
171
172
|
@_content_maximum_width.setter
|
|
172
173
|
def _content_maximum_width(self, new_width: int) -> None:
|
|
@@ -213,8 +214,10 @@ class Expandable_widget(QWidget):
|
|
|
213
214
|
|
|
214
215
|
def _setup_widget(self) -> None:
|
|
215
216
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, True)
|
|
216
|
-
self.setWindowFlags(
|
|
217
|
-
|
|
217
|
+
self.setWindowFlags(
|
|
218
|
+
Qt.WindowType.Widget |
|
|
219
|
+
Qt.WindowType.FramelessWindowHint
|
|
220
|
+
)
|
|
218
221
|
|
|
219
222
|
def _execute_toggle(self, animated=False):
|
|
220
223
|
if animated:
|
|
@@ -248,6 +251,7 @@ class Expandable_widget(QWidget):
|
|
|
248
251
|
self._execute_toggle(animated)
|
|
249
252
|
if button_toggle is not None:
|
|
250
253
|
self.toggle_button.toggle(button_toggle)
|
|
254
|
+
self.sg_toggle.emit(expand)
|
|
251
255
|
|
|
252
256
|
def _animation_value_changed(self, new_max_heigt: QSize) -> None: ...
|
|
253
257
|
# self.layout().activate()
|
|
@@ -466,14 +470,6 @@ class Spoiler_widget(Expandable_widget):
|
|
|
466
470
|
self.content_widget.setMaximumHeight(self.content_widget.sizeHint().height())
|
|
467
471
|
self.update()
|
|
468
472
|
|
|
469
|
-
# def sizeHint(self):
|
|
470
|
-
# header_h = self.header_widget.size().height()
|
|
471
|
-
# content_h = self.content_widget.maximumHeight()
|
|
472
|
-
# return QSize(
|
|
473
|
-
# max(self.header_widget.sizeHint().width(), self.content_widget.sizeHint().width()),
|
|
474
|
-
# header_h + content_h
|
|
475
|
-
# )
|
|
476
|
-
|
|
477
473
|
def main():
|
|
478
474
|
import sys
|
|
479
475
|
app = QApplication(sys.argv)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from PyQt6.QtWidgets import QVBoxLayout, QSizePolicy, QLayout, QWidgetItem
|
|
2
|
+
from PyQt6.QtCore import Qt, QSize, QPoint, QRect, QMargins
|
|
3
|
+
|
|
4
|
+
class FlowLayout(QLayout):
|
|
5
|
+
def __init__(self, parent=None):
|
|
6
|
+
super().__init__(parent)
|
|
7
|
+
|
|
8
|
+
if parent is not None:
|
|
9
|
+
self.setContentsMargins(QMargins(0, 0, 0, 0))
|
|
10
|
+
|
|
11
|
+
self._item_list = []
|
|
12
|
+
|
|
13
|
+
def __del__(self):
|
|
14
|
+
item = self.takeAt(0)
|
|
15
|
+
while item:
|
|
16
|
+
item = self.takeAt(0)
|
|
17
|
+
|
|
18
|
+
def addItem(self, item):
|
|
19
|
+
self._item_list.append(item)
|
|
20
|
+
|
|
21
|
+
def count(self):
|
|
22
|
+
return len(self._item_list)
|
|
23
|
+
|
|
24
|
+
def itemAt(self, index):
|
|
25
|
+
if 0 <= index < len(self._item_list):
|
|
26
|
+
return self._item_list[index]
|
|
27
|
+
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
def takeAt(self, index):
|
|
31
|
+
if 0 <= index < len(self._item_list):
|
|
32
|
+
return self._item_list.pop(index)
|
|
33
|
+
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
def expandingDirections(self):
|
|
37
|
+
return Qt.Orientation(0)
|
|
38
|
+
|
|
39
|
+
def hasHeightForWidth(self):
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
def heightForWidth(self, width):
|
|
43
|
+
height = self._do_layout(QRect(0, 0, width, 0), True)
|
|
44
|
+
return height
|
|
45
|
+
|
|
46
|
+
def setGeometry(self, rect):
|
|
47
|
+
super(FlowLayout, self).setGeometry(rect)
|
|
48
|
+
self._do_layout(rect, False)
|
|
49
|
+
|
|
50
|
+
def sizeHint(self):
|
|
51
|
+
return self.minimumSize()
|
|
52
|
+
|
|
53
|
+
def minimumSize(self):
|
|
54
|
+
size = QSize()
|
|
55
|
+
|
|
56
|
+
for item in self._item_list:
|
|
57
|
+
size = size.expandedTo(item.minimumSize())
|
|
58
|
+
|
|
59
|
+
size += QSize(2 * self.contentsMargins().top(), 2 * self.contentsMargins().top())
|
|
60
|
+
return size
|
|
61
|
+
|
|
62
|
+
def insertWidget(self, index, widget):
|
|
63
|
+
if index < 0:
|
|
64
|
+
index = 0
|
|
65
|
+
elif index > len(self._item_list):
|
|
66
|
+
index = len(self._item_list)
|
|
67
|
+
|
|
68
|
+
self.addChildWidget(widget)
|
|
69
|
+
|
|
70
|
+
item = QWidgetItem(widget)
|
|
71
|
+
self._item_list.insert(index, item)
|
|
72
|
+
|
|
73
|
+
self.invalidate()
|
|
74
|
+
|
|
75
|
+
def _do_layout(self, rect, test_only):
|
|
76
|
+
x = rect.x()
|
|
77
|
+
y = rect.y()
|
|
78
|
+
line_height = 0
|
|
79
|
+
spacing = self.spacing()
|
|
80
|
+
|
|
81
|
+
for item in self._item_list:
|
|
82
|
+
style = item.widget().style()
|
|
83
|
+
layout_spacing_x = style.layoutSpacing(
|
|
84
|
+
QSizePolicy.ControlType.PushButton, QSizePolicy.ControlType.PushButton,
|
|
85
|
+
Qt.Orientation.Horizontal
|
|
86
|
+
)
|
|
87
|
+
layout_spacing_y = style.layoutSpacing(
|
|
88
|
+
QSizePolicy.ControlType.PushButton, QSizePolicy.ControlType.PushButton,
|
|
89
|
+
Qt.Orientation.Vertical
|
|
90
|
+
)
|
|
91
|
+
space_x = spacing + layout_spacing_x
|
|
92
|
+
space_y = spacing + layout_spacing_y
|
|
93
|
+
next_x = x + item.sizeHint().width() + space_x
|
|
94
|
+
if next_x - space_x > rect.right() and line_height > 0:
|
|
95
|
+
x = rect.x()
|
|
96
|
+
y = y + line_height + space_y
|
|
97
|
+
next_x = x + item.sizeHint().width() + space_x
|
|
98
|
+
line_height = 0
|
|
99
|
+
|
|
100
|
+
if not test_only:
|
|
101
|
+
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
|
|
102
|
+
|
|
103
|
+
x = next_x
|
|
104
|
+
line_height = max(line_height, item.sizeHint().height())
|
|
105
|
+
|
|
106
|
+
return y + line_height - rect.y()
|
|
107
|
+
|
|
108
|
+
def index_at_cursor_pos(self, pos: QPoint) -> int:
|
|
109
|
+
# this is assuming NOT involved widgets at first postion in the layout
|
|
110
|
+
image_item_index = 1
|
|
111
|
+
for i in range(1, self.count()):
|
|
112
|
+
item = self.itemAt(i)
|
|
113
|
+
w = item.widget()
|
|
114
|
+
|
|
115
|
+
if w is None or not w.isVisible():
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
geo = w.geometry()
|
|
119
|
+
|
|
120
|
+
if pos.y() < geo.bottom() and pos.x() < geo.right():
|
|
121
|
+
return image_item_index
|
|
122
|
+
image_item_index += 1
|
|
123
|
+
return image_item_index
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from PyQt6.QtWidgets import QVBoxLayout, QDialog, QLabel, QDialogButtonBox, QLineEdit
|
|
2
|
+
|
|
3
|
+
class PasswordDialog(QDialog):
|
|
4
|
+
|
|
5
|
+
def __init__(self, parent=None):
|
|
6
|
+
super().__init__(parent)
|
|
7
|
+
|
|
8
|
+
self.setWindowTitle("Authentication")
|
|
9
|
+
self.setModal(True)
|
|
10
|
+
self.setFixedWidth(420)
|
|
11
|
+
|
|
12
|
+
layout = QVBoxLayout(self)
|
|
13
|
+
layout.setContentsMargins(28, 24, 28, 24)
|
|
14
|
+
layout.setSpacing(18)
|
|
15
|
+
|
|
16
|
+
self.title_label = QLabel("Password authentication")
|
|
17
|
+
self.title_label.setObjectName("title")
|
|
18
|
+
|
|
19
|
+
self.description_label = QLabel(
|
|
20
|
+
"Enter password: "
|
|
21
|
+
)
|
|
22
|
+
self.description_label.setWordWrap(True)
|
|
23
|
+
self.description_label.setObjectName("description")
|
|
24
|
+
|
|
25
|
+
self.password_edit = QLineEdit()
|
|
26
|
+
self.password_edit.setEchoMode(QLineEdit.EchoMode.Password)
|
|
27
|
+
self.password_edit.setPlaceholderText("Password")
|
|
28
|
+
|
|
29
|
+
buttons = QDialogButtonBox(
|
|
30
|
+
QDialogButtonBox.StandardButton.Ok
|
|
31
|
+
| QDialogButtonBox.StandardButton.Cancel
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
buttons.accepted.connect(self.accept)
|
|
35
|
+
buttons.rejected.connect(self.reject)
|
|
36
|
+
|
|
37
|
+
layout.addWidget(self.title_label)
|
|
38
|
+
layout.addWidget(self.description_label)
|
|
39
|
+
layout.addWidget(self.password_edit)
|
|
40
|
+
layout.addStretch()
|
|
41
|
+
layout.addWidget(buttons)
|
|
42
|
+
|
|
43
|
+
self.setStyleSheet("""
|
|
44
|
+
PasswordDialog {
|
|
45
|
+
background: #f4f4f4;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
QLabel#title_label {
|
|
49
|
+
font-size: 20px;
|
|
50
|
+
font-weight: 600;
|
|
51
|
+
color: #202020;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
QLabel#description_label {
|
|
55
|
+
color: #606060;
|
|
56
|
+
font-size: 14px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
QLineEdit {
|
|
60
|
+
padding: 8px;
|
|
61
|
+
border: 1px solid #b8b8b8;
|
|
62
|
+
border-radius: 6px;
|
|
63
|
+
background: white;
|
|
64
|
+
font-size: 14px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
QPushButton {
|
|
68
|
+
min-width: 90px;
|
|
69
|
+
padding: 6px 12px;
|
|
70
|
+
}
|
|
71
|
+
""")
|
|
72
|
+
|
|
73
|
+
self.password_edit.setFocus()
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def password(self):
|
|
77
|
+
return self.password_edit.text()
|
|
78
|
+
|
|
79
|
+
def set_title(self, title: str) -> None:
|
|
80
|
+
self.title_label.setText(title)
|
|
81
|
+
|
|
82
|
+
def set_description(self, desc: str) -> None:
|
|
83
|
+
self.description_label.setText(desc)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
from PyQt6.QtWidgets import QVBoxLayout, QWidget, QLabel, QSizePolicy, QProgressBar, QHBoxLayout, QApplication, QPushButton
|
|
2
|
+
from PyQt6.QtCore import Qt
|
|
3
|
+
|
|
4
|
+
class ProgressWidget(QWidget):
|
|
5
|
+
|
|
6
|
+
def __init__(self, parent=None):
|
|
7
|
+
super().__init__(parent, flags=Qt.WindowType.Window)
|
|
8
|
+
|
|
9
|
+
self.setWindowTitle("Progress")
|
|
10
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
|
|
11
|
+
|
|
12
|
+
self._setup_ui()
|
|
13
|
+
self._apply_style()
|
|
14
|
+
|
|
15
|
+
def _setup_ui(self) -> None:
|
|
16
|
+
self.setMinimumWidth(420)
|
|
17
|
+
self.setFixedHeight(300)
|
|
18
|
+
|
|
19
|
+
main_layout = QVBoxLayout(self)
|
|
20
|
+
main_layout.setContentsMargins(28, 24, 28, 24)
|
|
21
|
+
main_layout.setSpacing(16)
|
|
22
|
+
|
|
23
|
+
self.title_label = QLabel("Progress of Task")
|
|
24
|
+
self.title_label.setObjectName("titleLabel")
|
|
25
|
+
|
|
26
|
+
self.task_description_label = QLabel()
|
|
27
|
+
self.task_description_label.setObjectName("taskDescriptionLabel")
|
|
28
|
+
self.task_description_label.setWordWrap(True)
|
|
29
|
+
self.task_description_label.setAlignment(
|
|
30
|
+
Qt.AlignmentFlag.AlignLeft
|
|
31
|
+
| Qt.AlignmentFlag.AlignTop
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
self.current_task_info_label = QLabel(
|
|
35
|
+
"Preparing deployment..."
|
|
36
|
+
)
|
|
37
|
+
self.current_task_info_label.setObjectName(
|
|
38
|
+
"currentTaskLabel"
|
|
39
|
+
)
|
|
40
|
+
self.current_task_info_label.setWordWrap(True)
|
|
41
|
+
self.current_task_info_label.setAlignment(
|
|
42
|
+
Qt.AlignmentFlag.AlignLeft
|
|
43
|
+
| Qt.AlignmentFlag.AlignVCenter
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
self.progress_bar = QProgressBar()
|
|
47
|
+
self.progress_bar.setObjectName("progressBar")
|
|
48
|
+
self.progress_bar.setMinimum(0)
|
|
49
|
+
self.progress_bar.setMaximum(100)
|
|
50
|
+
self.progress_bar.setValue(0)
|
|
51
|
+
self.progress_bar.setTextVisible(True)
|
|
52
|
+
self.progress_bar.setMinimumHeight(24)
|
|
53
|
+
self.progress_bar.setSizePolicy(
|
|
54
|
+
QSizePolicy.Policy.Expanding,
|
|
55
|
+
QSizePolicy.Policy.Fixed,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
main_layout.addWidget(self.title_label)
|
|
59
|
+
main_layout.addWidget(self.task_description_label)
|
|
60
|
+
main_layout.addStretch()
|
|
61
|
+
main_layout.addWidget(self.current_task_info_label)
|
|
62
|
+
main_layout.addWidget(self.progress_bar)
|
|
63
|
+
self.ok_button = QPushButton("Schließen")
|
|
64
|
+
button_layout = QHBoxLayout()
|
|
65
|
+
button_layout.addWidget(self.ok_button)
|
|
66
|
+
button_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
67
|
+
self.ok_button.setMaximumWidth(80)
|
|
68
|
+
self.ok_button.clicked.connect(self.hide)
|
|
69
|
+
main_layout.addLayout(button_layout)
|
|
70
|
+
|
|
71
|
+
def _apply_style(self) -> None:
|
|
72
|
+
self.setStyleSheet("""
|
|
73
|
+
ProgressWidget {
|
|
74
|
+
background-color: #f4f4f4;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
QLabel#titleLabel {
|
|
78
|
+
font-size: 20px;
|
|
79
|
+
font-weight: 600;
|
|
80
|
+
color: #202020;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
QLabel#taskDescriptionLabel {
|
|
84
|
+
font-size: 14px;
|
|
85
|
+
color: #505050;
|
|
86
|
+
line-height: 1.4;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
QLabel#currentTaskLabel {
|
|
90
|
+
font-size: 13px;
|
|
91
|
+
color: #707070;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
QProgressBar#progressBar {
|
|
95
|
+
border: 1px solid #b8b8b8;
|
|
96
|
+
border-radius: 7px;
|
|
97
|
+
background-color: #ffffff;
|
|
98
|
+
text-align: center;
|
|
99
|
+
min-height: 22px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
QProgressBar#progressBar::chunk {
|
|
103
|
+
background-color: #3c8dbc;
|
|
104
|
+
border-radius: 6px;
|
|
105
|
+
}
|
|
106
|
+
""")
|
|
107
|
+
|
|
108
|
+
def show_ok_button(self, show: bool) -> None:
|
|
109
|
+
self.ok_button.setVisible(show)
|
|
110
|
+
|
|
111
|
+
def set_title(self, title: str) -> None:
|
|
112
|
+
self.title_label.setText(title)
|
|
113
|
+
|
|
114
|
+
def reset_progress(self) -> None:
|
|
115
|
+
self.progress_bar.reset()
|
|
116
|
+
|
|
117
|
+
def set_progress(
|
|
118
|
+
self,
|
|
119
|
+
info: str,
|
|
120
|
+
step_count: int,
|
|
121
|
+
) -> None:
|
|
122
|
+
self.current_task_info_label.setText(info)
|
|
123
|
+
self.progress_bar.setValue(step_count)
|
|
124
|
+
QApplication.processEvents()
|
|
125
|
+
|
|
126
|
+
def set_maximum_steps(self, maximum: int) -> None:
|
|
127
|
+
self.progress_bar.setMaximum(maximum)
|
|
128
|
+
|
|
129
|
+
def set_current_task_info(self, info: str) -> None:
|
|
130
|
+
self.current_task_info_label.setText(info)
|
|
131
|
+
QApplication.processEvents()
|
|
132
|
+
|
|
133
|
+
def set_task_desc(self, desc: str) -> None:
|
|
134
|
+
self.task_description_label.setText(desc)
|
|
135
|
+
QApplication.processEvents()
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: michis_python_sammlung
|
|
3
|
+
Version: 0.8.2
|
|
4
|
+
Summary: Personal collection of mostly QT related stuff
|
|
5
|
+
Author: Michael Mischko
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mischkomichael/Michis_python_sammlung
|
|
8
|
+
Project-URL: Repository, https://github.com/mischkomichael/Michis_python_sammlung
|
|
9
|
+
Project-URL: Issues, https://github.com/mischkomichael/Michis_python_sammlung/issues
|
|
10
|
+
Keywords: pyqt6,widgets,gui
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: PyQt6<7,>=6.11
|
|
17
|
+
|
|
18
|
+
# Publishing a New Release
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
* Maintain project metadata in `pyproject.toml`.
|
|
23
|
+
* Keep the project in a dedicated virtual environment (`.venv`).
|
|
24
|
+
* Runtime dependencies are declared in `pyproject.toml` under `dependencies`, **not** by freezing the virtual environment.
|
|
25
|
+
* The GitHub repository is connected to the existing PyPI project via **Trusted Publishing**.
|
|
26
|
+
|
|
27
|
+
## Local Build
|
|
28
|
+
|
|
29
|
+
Build the package locally before creating a release:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
python -m build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This creates:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
dist/
|
|
39
|
+
package_name-x.y.z.tar.gz
|
|
40
|
+
package_name-x.y.z-py3-none-any.whl
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Optionally verify the package:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python -m twine check dist/*
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Releasing
|
|
50
|
+
|
|
51
|
+
1. Update the version in `pyproject.toml`.
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
|
|
55
|
+
```toml
|
|
56
|
+
version = "1.2.3"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
2. Commit and push the changes.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git add .
|
|
63
|
+
git commit -m "Release 1.2.3"
|
|
64
|
+
git push
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
3. Create a matching Git tag.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git tag v1.2.3
|
|
71
|
+
git push origin v1.2.3
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
4. GitHub Actions builds the package and publishes it to PyPI.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
# Important Caveats
|
|
79
|
+
|
|
80
|
+
## Always increase the version
|
|
81
|
+
|
|
82
|
+
PyPI does **not** allow uploading a package with the same version twice.
|
|
83
|
+
|
|
84
|
+
Every release must have a unique version number.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## If the GitHub Action fails because the version was wrong
|
|
89
|
+
|
|
90
|
+
Simply pressing **Re-run jobs** is **not sufficient** if the commit itself is incorrect.
|
|
91
|
+
|
|
92
|
+
Instead:
|
|
93
|
+
|
|
94
|
+
1. Fix `pyproject.toml`.
|
|
95
|
+
|
|
96
|
+
2. Commit and push the fix.
|
|
97
|
+
|
|
98
|
+
3. Delete the old tag locally.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
git tag -d v1.2.3
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
4. Delete the tag on GitHub.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
git push origin :refs/tags/v1.2.3
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
5. Create the tag again on the corrected commit.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
git tag v1.2.3
|
|
114
|
+
git push origin v1.2.3
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This triggers the workflow again using the corrected source.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
# Common Build Issues
|
|
122
|
+
|
|
123
|
+
### Missing README
|
|
124
|
+
|
|
125
|
+
If `pyproject.toml` contains
|
|
126
|
+
|
|
127
|
+
```toml
|
|
128
|
+
readme = "README.md"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
then `README.md` must exist.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### License configuration
|
|
136
|
+
|
|
137
|
+
Use the modern license expression, for example:
|
|
138
|
+
|
|
139
|
+
```toml
|
|
140
|
+
license = "MIT"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Do **not** also include the old license classifier:
|
|
144
|
+
|
|
145
|
+
```toml
|
|
146
|
+
"License :: OSI Approved :: MIT License"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Modern versions of `setuptools` reject this combination.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
# Useful Commands
|
|
154
|
+
|
|
155
|
+
Build:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
python -m build
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Check package:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
python -m twine check dist/*
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Delete local release tag:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
git tag -d vX.Y.Z
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Delete remote release tag:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
git push origin :refs/tags/vX.Y.Z
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Create and push release tag:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
git tag vX.Y.Z
|
|
183
|
+
git push origin vX.Y.Z
|
|
184
|
+
```
|
|
@@ -7,8 +7,12 @@ src/michis_python_sammlung.egg-info/dependency_links.txt
|
|
|
7
7
|
src/michis_python_sammlung.egg-info/requires.txt
|
|
8
8
|
src/michis_python_sammlung.egg-info/top_level.txt
|
|
9
9
|
src/michis_python_sammlung/pyqt/__init__.py
|
|
10
|
-
src/michis_python_sammlung/pyqt/clickable_label.py
|
|
11
|
-
src/michis_python_sammlung/pyqt/expandable_widget.py
|
|
12
10
|
src/michis_python_sammlung/pyqt/image_paths.py
|
|
13
11
|
src/michis_python_sammlung/pyqt/pyqt_util.py
|
|
14
|
-
src/michis_python_sammlung/pyqt/icons/__init__.py
|
|
12
|
+
src/michis_python_sammlung/pyqt/icons/__init__.py
|
|
13
|
+
src/michis_python_sammlung/pyqt/widgets/__init__.py
|
|
14
|
+
src/michis_python_sammlung/pyqt/widgets/clickable_label.py
|
|
15
|
+
src/michis_python_sammlung/pyqt/widgets/expandable_widget.py
|
|
16
|
+
src/michis_python_sammlung/pyqt/widgets/flow_layout.py
|
|
17
|
+
src/michis_python_sammlung/pyqt/widgets/password_widget.py
|
|
18
|
+
src/michis_python_sammlung/pyqt/widgets/progress_widget.py
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: michis_python_sammlung
|
|
3
|
-
Version: 0.8
|
|
4
|
-
Summary: Personal collection of mostly QT related stuff
|
|
5
|
-
Author: Michael Mischko
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/mischkomichael/Michis_python_sammlung
|
|
8
|
-
Project-URL: Repository, https://github.com/mischkomichael/Michis_python_sammlung
|
|
9
|
-
Project-URL: Issues, https://github.com/mischkomichael/Michis_python_sammlung/issues
|
|
10
|
-
Keywords: pyqt6,widgets,gui
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Requires-Python: >=3.10
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
Requires-Dist: PyQt6<7,>=6.11
|
|
File without changes
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: michis_python_sammlung
|
|
3
|
-
Version: 0.8
|
|
4
|
-
Summary: Personal collection of mostly QT related stuff
|
|
5
|
-
Author: Michael Mischko
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/mischkomichael/Michis_python_sammlung
|
|
8
|
-
Project-URL: Repository, https://github.com/mischkomichael/Michis_python_sammlung
|
|
9
|
-
Project-URL: Issues, https://github.com/mischkomichael/Michis_python_sammlung/issues
|
|
10
|
-
Keywords: pyqt6,widgets,gui
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Requires-Python: >=3.10
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
Requires-Dist: PyQt6<7,>=6.11
|
|
File without changes
|
{michis_python_sammlung-0.8 → michis_python_sammlung-0.8.2}/src/michis_python_sammlung/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|