linux-kernel-manager 0.1.2__py3-none-any.whl

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.
Files changed (45) hide show
  1. linux_kernel_manager-0.1.2.dist-info/METADATA +271 -0
  2. linux_kernel_manager-0.1.2.dist-info/RECORD +45 -0
  3. linux_kernel_manager-0.1.2.dist-info/WHEEL +4 -0
  4. linux_kernel_manager-0.1.2.dist-info/entry_points.txt +3 -0
  5. linux_kernel_manager-0.1.2.dist-info/licenses/LICENSE +675 -0
  6. lkm/__init__.py +2 -0
  7. lkm/cli/__init__.py +0 -0
  8. lkm/cli/main.py +332 -0
  9. lkm/cli/output.py +57 -0
  10. lkm/core/__init__.py +0 -0
  11. lkm/core/backends/__init__.py +54 -0
  12. lkm/core/backends/apk.py +46 -0
  13. lkm/core/backends/apt.py +77 -0
  14. lkm/core/backends/base.py +81 -0
  15. lkm/core/backends/dnf.py +44 -0
  16. lkm/core/backends/nix.py +195 -0
  17. lkm/core/backends/pacman.py +78 -0
  18. lkm/core/backends/portage.py +107 -0
  19. lkm/core/backends/xbps.py +139 -0
  20. lkm/core/backends/zypper.py +44 -0
  21. lkm/core/kernel.py +95 -0
  22. lkm/core/manager.py +243 -0
  23. lkm/core/providers/__init__.py +62 -0
  24. lkm/core/providers/base.py +88 -0
  25. lkm/core/providers/distro.py +139 -0
  26. lkm/core/providers/gentoo.py +68 -0
  27. lkm/core/providers/liquorix.py +80 -0
  28. lkm/core/providers/lkf_build.py +355 -0
  29. lkm/core/providers/local_file.py +104 -0
  30. lkm/core/providers/mainline.py +106 -0
  31. lkm/core/providers/nixos.py +76 -0
  32. lkm/core/providers/void.py +69 -0
  33. lkm/core/providers/xanmod.py +81 -0
  34. lkm/core/system.py +235 -0
  35. lkm/gui/__init__.py +0 -0
  36. lkm/gui/app.py +94 -0
  37. lkm/gui/kernel_model.py +98 -0
  38. lkm/gui/main_window.py +385 -0
  39. lkm/gui/widgets/__init__.py +0 -0
  40. lkm/gui/widgets/gentoo_compile_dialog.py +132 -0
  41. lkm/gui/widgets/kernel_view.py +121 -0
  42. lkm/gui/widgets/lkf_build_dialog.py +370 -0
  43. lkm/gui/widgets/log_panel.py +78 -0
  44. lkm/gui/widgets/note_dialog.py +38 -0
  45. lkm/qt.py +122 -0
@@ -0,0 +1,370 @@
1
+ """
2
+ lkf Build dialog — GUI front-end for the lkf build pipeline.
3
+
4
+ Two modes:
5
+ Profile mode — pick a remix.toml from the discovered profile list and
6
+ run `lkf remix --file <profile>`.
7
+ Custom mode — specify version, flavor, arch, compiler flags manually
8
+ and run `lkf build`.
9
+
10
+ The dialog streams lkf output live via a QThread worker, then optionally
11
+ installs the resulting package through the system backend.
12
+
13
+ This dialog is modelled on GentooCompileDialog and uses the same LogPanel
14
+ and worker pattern.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ from pathlib import Path
19
+
20
+ from lkm.core.providers.lkf_build import LkfBuildProvider
21
+ from lkm.gui.widgets.log_panel import LogPanel
22
+ from lkm.qt import (
23
+ QCheckBox,
24
+ QComboBox,
25
+ QDialog,
26
+ QFileDialog,
27
+ QGroupBox,
28
+ QHBoxLayout,
29
+ QLabel,
30
+ QLineEdit,
31
+ QMessageBox,
32
+ QPushButton,
33
+ QSizePolicy,
34
+ QTabWidget,
35
+ QThread,
36
+ QVBoxLayout,
37
+ QWidget,
38
+ Signal,
39
+ Slot,
40
+ )
41
+
42
+ # ---------------------------------------------------------------------------
43
+ # Worker thread
44
+ # ---------------------------------------------------------------------------
45
+
46
+ class _BuildWorker(QThread):
47
+ line_ready = Signal(str)
48
+ finished_ok = Signal(str) # emits path to output package (may be "")
49
+ finished_err = Signal(str)
50
+
51
+ def __init__(
52
+ self,
53
+ provider: LkfBuildProvider,
54
+ mode: str, # "profile" | "custom"
55
+ profile_path: str = "",
56
+ version: str = "",
57
+ flavor: str = "mainline",
58
+ arch: str = "",
59
+ llvm: bool = False,
60
+ lto: str = "none",
61
+ output_fmt: str = "deb",
62
+ ) -> None:
63
+ super().__init__()
64
+ self._provider = provider
65
+ self._mode = mode
66
+ self._profile = profile_path
67
+ self._version = version
68
+ self._flavor = flavor
69
+ self._arch = arch
70
+ self._llvm = llvm
71
+ self._lto = lto
72
+ self._output_fmt = output_fmt
73
+
74
+ def run(self) -> None:
75
+ try:
76
+ if self._mode == "profile":
77
+ gen = self._provider.build_only(self._profile)
78
+ else:
79
+ gen = self._provider.build_custom(
80
+ version=self._version,
81
+ flavor=self._flavor,
82
+ arch=self._arch or None,
83
+ llvm=self._llvm,
84
+ lto=self._lto,
85
+ output_fmt=self._output_fmt,
86
+ )
87
+ for line in gen:
88
+ self.line_ready.emit(line)
89
+
90
+ # Locate the output package
91
+ from lkm.core.providers.lkf_build import _find_output_package
92
+ pkg = _find_output_package(
93
+ self._provider.output_dir,
94
+ self._version,
95
+ )
96
+ self.finished_ok.emit(str(pkg) if pkg else "")
97
+ except Exception as e:
98
+ self.finished_err.emit(str(e))
99
+
100
+
101
+ # ---------------------------------------------------------------------------
102
+ # Dialog
103
+ # ---------------------------------------------------------------------------
104
+
105
+ class LkfBuildDialog(QDialog):
106
+ """
107
+ Full-featured build dialog for the lkf pipeline.
108
+
109
+ Opened from the Build tab in the main window.
110
+ On successful build, emits build_succeeded(pkg_path) so the main window
111
+ can offer to install the result.
112
+ """
113
+
114
+ build_succeeded = Signal(str) # absolute path to the output package
115
+
116
+ def __init__(self, provider: LkfBuildProvider, parent=None) -> None:
117
+ super().__init__(parent)
118
+ self._provider = provider
119
+ self._worker: _BuildWorker | None = None
120
+ self.setWindowTitle("Build Kernel with lkf")
121
+ self.setMinimumSize(720, 580)
122
+ self._setup_ui()
123
+ self._populate_profiles()
124
+
125
+ # ------------------------------------------------------------------
126
+ # UI construction
127
+ # ------------------------------------------------------------------
128
+
129
+ def _setup_ui(self) -> None:
130
+ root = QVBoxLayout(self)
131
+
132
+ self._tabs = QTabWidget()
133
+ self._tabs.addTab(self._make_profile_tab(), "Profile")
134
+ self._tabs.addTab(self._make_custom_tab(), "Custom")
135
+ root.addWidget(self._tabs)
136
+
137
+ # Output dir label
138
+ out_row = QHBoxLayout()
139
+ out_row.addWidget(QLabel("Output dir:"))
140
+ self._out_label = QLabel(str(self._provider.output_dir))
141
+ self._out_label.setWordWrap(True)
142
+ out_row.addWidget(self._out_label, 1)
143
+ change_btn = QPushButton("Change…")
144
+ change_btn.clicked.connect(self._change_output_dir)
145
+ out_row.addWidget(change_btn)
146
+ root.addLayout(out_row)
147
+
148
+ # Log
149
+ self._log = LogPanel()
150
+ root.addWidget(self._log)
151
+
152
+ # Buttons
153
+ btn_row = QHBoxLayout()
154
+ btn_row.addStretch()
155
+ self._build_btn = QPushButton("Build")
156
+ self._build_btn.setDefault(True)
157
+ self._build_btn.clicked.connect(self._start_build)
158
+ btn_row.addWidget(self._build_btn)
159
+
160
+ self._install_btn = QPushButton("Build && Install")
161
+ self._install_btn.clicked.connect(self._start_build_and_install)
162
+ btn_row.addWidget(self._install_btn)
163
+
164
+ self._close_btn = QPushButton("Close")
165
+ self._close_btn.clicked.connect(self.reject)
166
+ btn_row.addWidget(self._close_btn)
167
+ root.addLayout(btn_row)
168
+
169
+ self._pending_install = False
170
+
171
+ def _make_profile_tab(self) -> QWidget:
172
+ w = QWidget()
173
+ layout = QVBoxLayout(w)
174
+
175
+ row = QHBoxLayout()
176
+ row.addWidget(QLabel("Profile:"))
177
+ self._profile_combo = QComboBox()
178
+ self._profile_combo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
179
+ row.addWidget(self._profile_combo)
180
+
181
+ browse_btn = QPushButton("Browse…")
182
+ browse_btn.clicked.connect(self._browse_profile)
183
+ row.addWidget(browse_btn)
184
+ layout.addLayout(row)
185
+
186
+ # Profile details (read-only)
187
+ self._profile_detail = QLabel("")
188
+ self._profile_detail.setWordWrap(True)
189
+ layout.addWidget(self._profile_detail)
190
+ layout.addStretch()
191
+
192
+ self._profile_combo.currentIndexChanged.connect(self._update_profile_detail)
193
+ return w
194
+
195
+ def _make_custom_tab(self) -> QWidget:
196
+ w = QWidget()
197
+ layout = QVBoxLayout(w)
198
+
199
+ # Version
200
+ row1 = QHBoxLayout()
201
+ row1.addWidget(QLabel("Version:"))
202
+ self._version_edit = QLineEdit()
203
+ self._version_edit.setPlaceholderText("e.g. 6.12")
204
+ row1.addWidget(self._version_edit)
205
+ layout.addLayout(row1)
206
+
207
+ # Flavor + arch
208
+ row2 = QHBoxLayout()
209
+ row2.addWidget(QLabel("Flavor:"))
210
+ self._flavor_combo = QComboBox()
211
+ for f in ["mainline", "xanmod", "cachyos", "zen", "rt", "tkg", "android", "custom"]:
212
+ self._flavor_combo.addItem(f)
213
+ row2.addWidget(self._flavor_combo)
214
+
215
+ row2.addWidget(QLabel("Arch:"))
216
+ self._arch_combo = QComboBox()
217
+ for a in ["(host)", "x86_64", "aarch64", "arm", "riscv64"]:
218
+ self._arch_combo.addItem(a)
219
+ row2.addWidget(self._arch_combo)
220
+ layout.addLayout(row2)
221
+
222
+ # Compiler options
223
+ compiler_box = QGroupBox("Compiler")
224
+ cb_layout = QHBoxLayout(compiler_box)
225
+ self._llvm_cb = QCheckBox("Clang/LLVM")
226
+ cb_layout.addWidget(self._llvm_cb)
227
+ cb_layout.addWidget(QLabel("LTO:"))
228
+ self._lto_combo = QComboBox()
229
+ for lto in ["none", "thin", "full"]:
230
+ self._lto_combo.addItem(lto)
231
+ cb_layout.addWidget(self._lto_combo)
232
+ cb_layout.addStretch()
233
+ layout.addWidget(compiler_box)
234
+
235
+ # Output format
236
+ row3 = QHBoxLayout()
237
+ row3.addWidget(QLabel("Output format:"))
238
+ self._fmt_combo = QComboBox()
239
+ for fmt in ["deb", "rpm", "pkg.tar.zst", "tar.gz", "efi-unified", "android-boot"]:
240
+ self._fmt_combo.addItem(fmt)
241
+ row3.addWidget(self._fmt_combo)
242
+ row3.addStretch()
243
+ layout.addLayout(row3)
244
+
245
+ layout.addStretch()
246
+ return w
247
+
248
+ # ------------------------------------------------------------------
249
+ # Profile helpers
250
+ # ------------------------------------------------------------------
251
+
252
+ def _populate_profiles(self) -> None:
253
+ from lkm.core.providers.lkf_build import _find_lkf_root, _find_profiles
254
+ profiles = _find_profiles(_find_lkf_root())
255
+ if not profiles:
256
+ self._profile_combo.addItem("No profiles found", "")
257
+ self._build_btn.setEnabled(self._tabs.currentIndex() != 0)
258
+ return
259
+ for p in profiles:
260
+ from lkm.core.providers.lkf_build import _parse_profile_name, _parse_profile_version
261
+ label = f"{_parse_profile_name(p)} ({_parse_profile_version(p)})"
262
+ self._profile_combo.addItem(label, str(p))
263
+
264
+ def _update_profile_detail(self, _: int) -> None:
265
+ path = self._profile_combo.currentData()
266
+ if not path:
267
+ self._profile_detail.setText("")
268
+ return
269
+ try:
270
+ text = Path(path).read_text()
271
+ # Show first 8 lines of the TOML
272
+ preview = "\n".join(text.splitlines()[:8])
273
+ self._profile_detail.setText(f"<pre>{preview}</pre>")
274
+ except OSError:
275
+ self._profile_detail.setText("")
276
+
277
+ def _browse_profile(self) -> None:
278
+ path, _ = QFileDialog.getOpenFileName(
279
+ self, "Select remix.toml", str(Path.home()), "TOML files (*.toml)"
280
+ )
281
+ if path:
282
+ self._profile_combo.insertItem(0, Path(path).name, path)
283
+ self._profile_combo.setCurrentIndex(0)
284
+
285
+ def _change_output_dir(self) -> None:
286
+ d = QFileDialog.getExistingDirectory(
287
+ self, "Select output directory", str(self._provider.output_dir)
288
+ )
289
+ if d:
290
+ self._provider._output_dir = Path(d)
291
+ self._out_label.setText(d)
292
+
293
+ # ------------------------------------------------------------------
294
+ # Build actions
295
+ # ------------------------------------------------------------------
296
+
297
+ def _start_build(self) -> None:
298
+ self._pending_install = False
299
+ self._run_build()
300
+
301
+ def _start_build_and_install(self) -> None:
302
+ self._pending_install = True
303
+ self._run_build()
304
+
305
+ def _run_build(self) -> None:
306
+ if self._worker and self._worker.isRunning():
307
+ return
308
+
309
+ self._log.clear()
310
+ self._log.show_panel()
311
+ self._build_btn.setEnabled(False)
312
+ self._install_btn.setEnabled(False)
313
+
314
+ mode = "profile" if self._tabs.currentIndex() == 0 else "custom"
315
+
316
+ if mode == "profile":
317
+ profile = self._profile_combo.currentData()
318
+ if not profile:
319
+ QMessageBox.warning(self, "No profile", "Select a profile first.")
320
+ self._build_btn.setEnabled(True)
321
+ self._install_btn.setEnabled(True)
322
+ return
323
+ self._worker = _BuildWorker(
324
+ provider=self._provider,
325
+ mode="profile",
326
+ profile_path=profile,
327
+ )
328
+ else:
329
+ version = self._version_edit.text().strip()
330
+ if not version:
331
+ QMessageBox.warning(self, "No version", "Enter a kernel version.")
332
+ self._build_btn.setEnabled(True)
333
+ self._install_btn.setEnabled(True)
334
+ return
335
+ arch_text = self._arch_combo.currentText()
336
+ arch = "" if arch_text == "(host)" else arch_text
337
+ self._worker = _BuildWorker(
338
+ provider=self._provider,
339
+ mode="custom",
340
+ version=version,
341
+ flavor=self._flavor_combo.currentText(),
342
+ arch=arch,
343
+ llvm=self._llvm_cb.isChecked(),
344
+ lto=self._lto_combo.currentText(),
345
+ output_fmt=self._fmt_combo.currentText(),
346
+ )
347
+
348
+ self._worker.line_ready.connect(self._log.append)
349
+ self._worker.finished_ok.connect(self._on_build_done)
350
+ self._worker.finished_err.connect(self._on_build_error)
351
+ self._worker.start()
352
+
353
+ @Slot(str)
354
+ def _on_build_done(self, pkg_path: str) -> None:
355
+ self._log.append("\n✓ Build complete.\n")
356
+ if pkg_path:
357
+ self._log.append(f"Package: {pkg_path}\n")
358
+ self._build_btn.setEnabled(True)
359
+ self._install_btn.setEnabled(True)
360
+
361
+ if pkg_path:
362
+ self.build_succeeded.emit(pkg_path)
363
+ if self._pending_install:
364
+ self.accept() # close dialog; main window handles install
365
+
366
+ @Slot(str)
367
+ def _on_build_error(self, msg: str) -> None:
368
+ self._log.append(f"\n✗ Build failed: {msg}\n")
369
+ self._build_btn.setEnabled(True)
370
+ self._install_btn.setEnabled(True)
@@ -0,0 +1,78 @@
1
+ """Collapsible live log panel — shared by the kernel view and build dialogs."""
2
+ from __future__ import annotations
3
+
4
+ from lkm.qt import (
5
+ QFont,
6
+ QHBoxLayout,
7
+ QPushButton,
8
+ QSizePolicy,
9
+ QTextEdit,
10
+ QVBoxLayout,
11
+ QWidget,
12
+ Slot,
13
+ )
14
+
15
+
16
+ class LogPanel(QWidget):
17
+ """
18
+ A collapsible text area that streams log output.
19
+
20
+ Call append(line) from any thread via a Signal connection.
21
+ """
22
+
23
+ def __init__(self, parent=None) -> None:
24
+ super().__init__(parent)
25
+ self._setup_ui()
26
+
27
+ def _setup_ui(self) -> None:
28
+ layout = QVBoxLayout(self)
29
+ layout.setContentsMargins(0, 0, 0, 0)
30
+
31
+ # Toggle bar
32
+ bar = QHBoxLayout()
33
+ self._toggle_btn = QPushButton("▼ Log")
34
+ self._toggle_btn.setFlat(True)
35
+ self._toggle_btn.setCheckable(True)
36
+ self._toggle_btn.setChecked(True)
37
+ self._toggle_btn.clicked.connect(self._toggle)
38
+ bar.addWidget(self._toggle_btn)
39
+
40
+ self._clear_btn = QPushButton("Clear")
41
+ self._clear_btn.setFlat(True)
42
+ self._clear_btn.clicked.connect(self.clear)
43
+ bar.addWidget(self._clear_btn)
44
+ bar.addStretch()
45
+ layout.addLayout(bar)
46
+
47
+ # Log text area
48
+ self._text = QTextEdit()
49
+ self._text.setReadOnly(True)
50
+ self._text.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
51
+ font = QFont("Monospace")
52
+ font.setStyleHint(QFont.StyleHint.TypeWriter)
53
+ font.setPointSize(9)
54
+ self._text.setFont(font)
55
+ self._text.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
56
+ layout.addWidget(self._text)
57
+
58
+ @Slot(str)
59
+ def append(self, text: str) -> None:
60
+ self._text.insertPlainText(text)
61
+ self._text.ensureCursorVisible()
62
+
63
+ def clear(self) -> None:
64
+ self._text.clear()
65
+
66
+ def show_panel(self) -> None:
67
+ self._text.setVisible(True)
68
+ self._toggle_btn.setChecked(True)
69
+ self._toggle_btn.setText("▼ Log")
70
+
71
+ def hide_panel(self) -> None:
72
+ self._text.setVisible(False)
73
+ self._toggle_btn.setChecked(False)
74
+ self._toggle_btn.setText("▶ Log")
75
+
76
+ def _toggle(self, checked: bool) -> None:
77
+ self._text.setVisible(checked)
78
+ self._toggle_btn.setText("▼ Log" if checked else "▶ Log")
@@ -0,0 +1,38 @@
1
+ """Note editing dialog."""
2
+ from __future__ import annotations
3
+
4
+ from lkm.qt import (
5
+ QDialog,
6
+ QDialogButtonBox,
7
+ QLabel,
8
+ QTextEdit,
9
+ QVBoxLayout,
10
+ )
11
+
12
+
13
+ class NoteDialog(QDialog):
14
+
15
+ def __init__(self, kernel_name: str, current_note: str = "", parent=None) -> None:
16
+ super().__init__(parent)
17
+ self.setWindowTitle(f"Note — {kernel_name}")
18
+ self.setMinimumWidth(400)
19
+ self._setup_ui(current_note)
20
+
21
+ def _setup_ui(self, current_note: str) -> None:
22
+ layout = QVBoxLayout(self)
23
+ layout.addWidget(QLabel("Note:"))
24
+ self._edit = QTextEdit()
25
+ self._edit.setPlainText(current_note)
26
+ self._edit.setMinimumHeight(80)
27
+ layout.addWidget(self._edit)
28
+
29
+ btns = QDialogButtonBox(
30
+ QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
31
+ )
32
+ btns.accepted.connect(self.accept)
33
+ btns.rejected.connect(self.reject)
34
+ layout.addWidget(btns)
35
+
36
+ @property
37
+ def note(self) -> str:
38
+ return self._edit.toPlainText().strip()
lkm/qt.py ADDED
@@ -0,0 +1,122 @@
1
+ """
2
+ PySide6 / PyQt6 compatibility shim.
3
+
4
+ Import Qt symbols from here rather than directly from PySide6 or PyQt6.
5
+ The binding is selected by the LKM_QT environment variable, falling back
6
+ to PySide6 then PyQt6.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import os
11
+
12
+ _binding = os.environ.get("LKM_QT", "")
13
+
14
+ if _binding == "PyQt6":
15
+ _use_pyqt6 = True
16
+ elif _binding == "PySide6":
17
+ _use_pyqt6 = False
18
+ else:
19
+ try:
20
+ import PySide6 # noqa: F401
21
+ _use_pyqt6 = False
22
+ except ImportError:
23
+ _use_pyqt6 = True
24
+
25
+ if _use_pyqt6:
26
+ from PyQt6.QtCore import (
27
+ QAbstractTableModel,
28
+ QModelIndex,
29
+ QSortFilterProxyModel,
30
+ Qt,
31
+ QThread,
32
+ QTimer,
33
+ )
34
+ from PyQt6.QtCore import (
35
+ pyqtSignal as Signal,
36
+ )
37
+ from PyQt6.QtCore import (
38
+ pyqtSlot as Slot,
39
+ )
40
+ from PyQt6.QtGui import QColor, QFont, QIcon
41
+ from PyQt6.QtWidgets import (
42
+ QAbstractItemView,
43
+ QAction,
44
+ QApplication,
45
+ QCheckBox,
46
+ QComboBox,
47
+ QDialog,
48
+ QDialogButtonBox,
49
+ QFileDialog,
50
+ QGroupBox,
51
+ QHBoxLayout,
52
+ QHeaderView,
53
+ QLabel,
54
+ QLineEdit,
55
+ QMainWindow,
56
+ QMenu,
57
+ QMessageBox,
58
+ QPushButton,
59
+ QSizePolicy,
60
+ QSpinBox,
61
+ QSplitter,
62
+ QStatusBar,
63
+ QTableView,
64
+ QTabWidget,
65
+ QTextEdit,
66
+ QToolBar,
67
+ QVBoxLayout,
68
+ QWidget,
69
+ )
70
+ else:
71
+ from PySide6.QtCore import (
72
+ QAbstractTableModel,
73
+ QModelIndex,
74
+ QSortFilterProxyModel,
75
+ Qt,
76
+ QThread,
77
+ QTimer,
78
+ Signal,
79
+ Slot,
80
+ )
81
+ from PySide6.QtGui import QAction, QColor, QFont, QIcon
82
+ from PySide6.QtWidgets import (
83
+ QAbstractItemView,
84
+ QApplication,
85
+ QCheckBox,
86
+ QComboBox,
87
+ QDialog,
88
+ QDialogButtonBox,
89
+ QFileDialog,
90
+ QGroupBox,
91
+ QHBoxLayout,
92
+ QHeaderView,
93
+ QLabel,
94
+ QLineEdit,
95
+ QMainWindow,
96
+ QMenu,
97
+ QMessageBox,
98
+ QPushButton,
99
+ QSizePolicy,
100
+ QSpinBox,
101
+ QSplitter,
102
+ QStatusBar,
103
+ QTableView,
104
+ QTabWidget,
105
+ QTextEdit,
106
+ QToolBar,
107
+ QVBoxLayout,
108
+ QWidget,
109
+ )
110
+
111
+ __all__ = [
112
+ "QApplication", "QMainWindow", "QWidget", "QDialog",
113
+ "QVBoxLayout", "QHBoxLayout", "QLabel", "QComboBox",
114
+ "QPushButton", "QCheckBox", "QDialogButtonBox",
115
+ "QSizePolicy", "QTabWidget", "QTextEdit", "QLineEdit",
116
+ "QSpinBox", "QGroupBox", "QSplitter", "QToolBar",
117
+ "QStatusBar", "QMessageBox", "QFileDialog", "QAction",
118
+ "QTableView", "QHeaderView", "QAbstractItemView", "QMenu",
119
+ "Qt", "QThread", "QAbstractTableModel", "QModelIndex",
120
+ "QSortFilterProxyModel", "Signal", "Slot", "QTimer",
121
+ "QFont", "QColor", "QIcon",
122
+ ]