first commit
This commit is contained in:
342
.qtcreator/pyproject.toml.user
Normal file
342
.qtcreator/pyproject.toml.user
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE QtCreatorProject>
|
||||||
|
<!-- Written by QtCreator 18.0.2, 2026-02-01T17:38:34. -->
|
||||||
|
<qtcreator>
|
||||||
|
<data>
|
||||||
|
<variable>EnvironmentId</variable>
|
||||||
|
<value type="QByteArray">{d278bd62-883b-4816-a712-738287b946d3}</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||||
|
<value type="qlonglong">0</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||||
|
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||||
|
<value type="QString" key="language">Cpp</value>
|
||||||
|
<valuemap type="QVariantMap" key="value">
|
||||||
|
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||||
|
</valuemap>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||||
|
<value type="QString" key="language">QmlJS</value>
|
||||||
|
<valuemap type="QVariantMap" key="value">
|
||||||
|
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||||
|
</valuemap>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||||
|
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
|
||||||
|
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||||
|
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||||
|
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">2</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||||
|
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||||
|
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
|
||||||
|
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||||
|
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||||
|
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.CTest">false</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||||
|
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="bool" key="AutoTest.ApplyFilter">false</value>
|
||||||
|
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||||
|
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
|
||||||
|
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||||
|
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||||
|
<valuemap type="QVariantMap" key="ClangTools">
|
||||||
|
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||||
|
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||||
|
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||||
|
<value type="int" key="ClangTools.ParallelJobs">2</value>
|
||||||
|
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
|
||||||
|
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||||
|
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||||
|
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||||
|
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="RcSync">0</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="QString" key="DeviceType">Desktop</value>
|
||||||
|
<value type="bool" key="HasPerBcDcs">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Python 3.14.2</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Python 3.14.2</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{bf68bfb7-0daf-4abe-a8f9-98ada8c9b297}</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||||
|
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Python.PysideBuildStep</value>
|
||||||
|
<value type="QString" key="Python.PySideProjectTool">/home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv/bin/pyside6-project</value>
|
||||||
|
<value type="QString" key="Python.PySideUic">/home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv/bin/pyside6-uic</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">构建</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">构建</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">清除</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">清除</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||||
|
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Python 3.14.2 Virtual Environment</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Python.PySideBuildConfiguration</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">部署</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">部署</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">mainwindow.py</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/smart/qtpj/diskmananger/mainwindow.py</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
|
<value type="QString" key="PythonEditor.RunConfiguation.Script">/home/smart/qtpj/diskmananger/mainwindow.py</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/smart/qtpj/diskmananger</value>
|
||||||
|
<value type="QString" key="RunConfiguration.X11Forwarding">:0.0</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.1">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">system_info.py</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/smart/qtpj/diskmananger/system_info.py</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
|
<value type="QString" key="PythonEditor.RunConfiguation.Script">/home/smart/qtpj/diskmananger/system_info.py</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/smart/qtpj/diskmananger</value>
|
||||||
|
<value type="QString" key="RunConfiguration.X11Forwarding">:0.0</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.2">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">logger_config.py</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/smart/qtpj/diskmananger/logger_config.py</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
|
<value type="QString" key="PythonEditor.RunConfiguation.Script">/home/smart/qtpj/diskmananger/logger_config.py</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/smart/qtpj/diskmananger</value>
|
||||||
|
<value type="QString" key="RunConfiguration.X11Forwarding">:0.0</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.3">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">disk_operations.py</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/smart/qtpj/diskmananger/disk_operations.py</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
|
<value type="QString" key="PythonEditor.RunConfiguation.Script">/home/smart/qtpj/diskmananger/disk_operations.py</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/smart/qtpj/diskmananger</value>
|
||||||
|
<value type="QString" key="RunConfiguration.X11Forwarding">:0.0</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">4</value>
|
||||||
|
<value type="QString" key="python">/home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv/bin/python</value>
|
||||||
|
<value type="QString" key="venv">/home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">部署</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">部署</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||||
|
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">mainwindow.py</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/smart/qtpj/diskmananger/mainwindow.py</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
|
<value type="QString" key="PythonEditor.RunConfiguation.Script">/home/smart/qtpj/diskmananger/mainwindow.py</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/smart/qtpj/diskmananger</value>
|
||||||
|
<value type="QString" key="RunConfiguration.X11Forwarding">:0.0</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.1">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">system_info.py</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/smart/qtpj/diskmananger/system_info.py</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
|
<value type="QString" key="PythonEditor.RunConfiguation.Script">/home/smart/qtpj/diskmananger/system_info.py</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/smart/qtpj/diskmananger</value>
|
||||||
|
<value type="QString" key="RunConfiguration.X11Forwarding">:0.0</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.2">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">logger_config.py</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/smart/qtpj/diskmananger/logger_config.py</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
|
<value type="QString" key="PythonEditor.RunConfiguation.Script">/home/smart/qtpj/diskmananger/logger_config.py</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/smart/qtpj/diskmananger</value>
|
||||||
|
<value type="QString" key="RunConfiguration.X11Forwarding">:0.0</value>
|
||||||
|
</valuemap>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.3">
|
||||||
|
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||||
|
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||||
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||||
|
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">disk_operations.py</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/smart/qtpj/diskmananger/disk_operations.py</value>
|
||||||
|
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||||
|
<value type="QString" key="PythonEditor.RunConfiguation.Script">/home/smart/qtpj/diskmananger/disk_operations.py</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/smart/qtpj/diskmananger</value>
|
||||||
|
<value type="QString" key="RunConfiguration.X11Forwarding">:0.0</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">4</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||||
|
<value type="qlonglong">1</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>Version</variable>
|
||||||
|
<value type="int">22</value>
|
||||||
|
</data>
|
||||||
|
</qtcreator>
|
||||||
BIN
__pycache__/disk_operations.cpython-314.pyc
Normal file
BIN
__pycache__/disk_operations.cpython-314.pyc
Normal file
Binary file not shown.
BIN
__pycache__/logger_config.cpython-314.pyc
Normal file
BIN
__pycache__/logger_config.cpython-314.pyc
Normal file
Binary file not shown.
BIN
__pycache__/system_info.cpython-314.pyc
Normal file
BIN
__pycache__/system_info.cpython-314.pyc
Normal file
Binary file not shown.
BIN
__pycache__/ui_form.cpython-314.pyc
Normal file
BIN
__pycache__/ui_form.cpython-314.pyc
Normal file
Binary file not shown.
237
disk_operations.py
Normal file
237
disk_operations.py
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
# disk_operations.py
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from PySide6.QtWidgets import QMessageBox, QInputDialog
|
||||||
|
from system_info import SystemInfoManager # 引入 SystemInfoManager 来复用 _run_command
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__) # 获取当前模块的 logger 实例
|
||||||
|
|
||||||
|
class DiskOperations:
|
||||||
|
def __init__(self):
|
||||||
|
self.system_manager = SystemInfoManager() # 复用 SystemInfoManager 的命令执行器
|
||||||
|
|
||||||
|
def _get_device_path(self, device_name):
|
||||||
|
"""
|
||||||
|
辅助函数:将设备名(如 'sda1')转换为完整路径(如 '/dev/sda1')。
|
||||||
|
"""
|
||||||
|
if not device_name.startswith('/dev/'):
|
||||||
|
return f'/dev/{device_name}'
|
||||||
|
return device_name
|
||||||
|
|
||||||
|
def mount_partition(self, device_name, mount_point=None):
|
||||||
|
"""
|
||||||
|
挂载指定的分区。
|
||||||
|
如果未提供挂载点,会尝试查找现有挂载点或提示用户输入。
|
||||||
|
"""
|
||||||
|
dev_path = self._get_device_path(device_name)
|
||||||
|
logger.info(f"尝试挂载设备: {dev_path}")
|
||||||
|
|
||||||
|
if not mount_point:
|
||||||
|
# 尝试从系统信息中获取当前挂载点
|
||||||
|
devices = self.system_manager.get_block_devices()
|
||||||
|
found_mount_point = None
|
||||||
|
for dev in devices:
|
||||||
|
if dev.get('name') == device_name:
|
||||||
|
found_mount_point = dev.get('mountpoint')
|
||||||
|
break
|
||||||
|
if 'children' in dev:
|
||||||
|
for child in dev['children']:
|
||||||
|
if child.get('name') == device_name:
|
||||||
|
found_mount_point = child.get('mountpoint')
|
||||||
|
break
|
||||||
|
if found_mount_point:
|
||||||
|
break
|
||||||
|
|
||||||
|
if found_mount_point and found_mount_point != '[SWAP]' and found_mount_point != '':
|
||||||
|
# 如果设备已经有挂载点,并且不是SWAP,则直接使用
|
||||||
|
mount_point = found_mount_point
|
||||||
|
logger.info(f"设备 {dev_path} 已经挂载到 {mount_point}。")
|
||||||
|
QMessageBox.information(None, "信息", f"设备 {dev_path} 已经挂载到 {mount_point}。")
|
||||||
|
return True # 已经挂载,视为成功
|
||||||
|
else:
|
||||||
|
# 如果没有挂载点,或者挂载点是SWAP,则提示用户输入
|
||||||
|
mount_point, ok = QInputDialog.getText(None, "挂载分区",
|
||||||
|
f"请输入 {dev_path} 的挂载点 (例如: /mnt/data):",
|
||||||
|
text=f"/mnt/{device_name}")
|
||||||
|
if not ok or not mount_point:
|
||||||
|
logger.info("用户取消了挂载操作或未提供挂载点。")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 确保挂载点目录存在
|
||||||
|
if not os.path.exists(mount_point):
|
||||||
|
try:
|
||||||
|
os.makedirs(mount_point, exist_ok=True)
|
||||||
|
logger.info(f"创建挂载点目录: {mount_point}")
|
||||||
|
except OSError as e:
|
||||||
|
logger.error(f"创建挂载点目录失败 {mount_point}: {e}")
|
||||||
|
QMessageBox.critical(None, "错误", f"创建挂载点目录失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
stdout, stderr = self.system_manager._run_command(["mount", dev_path, mount_point], root_privilege=True)
|
||||||
|
logger.info(f"成功挂载 {dev_path} 到 {mount_point}")
|
||||||
|
QMessageBox.information(None, "成功", f"成功挂载 {dev_path} 到 {mount_point}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"挂载 {dev_path} 失败: {e}")
|
||||||
|
QMessageBox.critical(None, "错误", f"挂载 {dev_path} 失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def unmount_partition(self, device_name):
|
||||||
|
"""
|
||||||
|
卸载指定的分区。
|
||||||
|
"""
|
||||||
|
dev_path = self._get_device_path(device_name)
|
||||||
|
logger.info(f"尝试卸载设备: {dev_path}")
|
||||||
|
|
||||||
|
# 尝试从系统信息中获取当前挂载点
|
||||||
|
current_mount_point = None
|
||||||
|
devices = self.system_manager.get_block_devices()
|
||||||
|
for dev in devices:
|
||||||
|
if dev.get('name') == device_name:
|
||||||
|
current_mount_point = dev.get('mountpoint')
|
||||||
|
break
|
||||||
|
if 'children' in dev:
|
||||||
|
for child in dev['children']:
|
||||||
|
if child.get('name') == device_name:
|
||||||
|
current_mount_point = child.get('mountpoint')
|
||||||
|
break
|
||||||
|
if current_mount_point:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not current_mount_point or current_mount_point == '[SWAP]' or current_mount_point == '':
|
||||||
|
logger.warning(f"设备 {dev_path} 未挂载或无法确定挂载点,无法卸载。")
|
||||||
|
QMessageBox.warning(None, "警告", f"设备 {dev_path} 未挂载或无法确定挂载点,无法卸载。")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 尝试通过挂载点卸载,通常更可靠
|
||||||
|
stdout, stderr = self.system_manager._run_command(["umount", current_mount_point], root_privilege=True)
|
||||||
|
logger.info(f"成功卸载 {current_mount_point} ({dev_path})")
|
||||||
|
QMessageBox.information(None, "成功", f"成功卸载 {current_mount_point} ({dev_path})")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"卸载 {current_mount_point} ({dev_path}) 失败: {e}")
|
||||||
|
QMessageBox.critical(None, "错误", f"卸载 {current_mount_point} ({dev_path}) 失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_partition(self, device_name):
|
||||||
|
"""
|
||||||
|
删除指定的分区。
|
||||||
|
此操作不可逆,会丢失数据。
|
||||||
|
"""
|
||||||
|
dev_path = self._get_device_path(device_name)
|
||||||
|
logger.info(f"尝试删除分区: {dev_path}")
|
||||||
|
|
||||||
|
# 安全检查: 确保是分区,而不是整个磁盘
|
||||||
|
# 简单的检查方法是看设备名是否包含数字 (如 sda1, nvme0n1p1)
|
||||||
|
if not any(char.isdigit() for char in device_name):
|
||||||
|
QMessageBox.warning(None, "警告", f"{dev_path} 看起来不是一个分区。为安全起见,不执行删除操作。")
|
||||||
|
logger.warning(f"尝试删除整个磁盘 {dev_path},已阻止。")
|
||||||
|
return False
|
||||||
|
|
||||||
|
reply = QMessageBox.question(None, "确认删除分区",
|
||||||
|
f"你确定要删除分区 {dev_path} 吗?\n"
|
||||||
|
"此操作不可逆,将导致数据丢失!",
|
||||||
|
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||||||
|
if reply == QMessageBox.No:
|
||||||
|
logger.info(f"用户取消了删除分区 {dev_path} 的操作。")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 首先尝试卸载分区(如果已挂载)
|
||||||
|
try:
|
||||||
|
# umount 如果设备未挂载会返回非零退出码,所以这里 check_output=False
|
||||||
|
self.system_manager._run_command(["umount", dev_path], root_privilege=True, check_output=False)
|
||||||
|
logger.info(f"尝试卸载 {dev_path} (如果已挂载)。")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"卸载 {dev_path} 失败或未挂载: {e}")
|
||||||
|
# 继续执行,因为即使卸载失败也可能能删除
|
||||||
|
|
||||||
|
# 使用 parted 来删除分区
|
||||||
|
# parted 需要父磁盘设备名和分区号
|
||||||
|
# 例如,对于 /dev/sda1,需要 /dev/sda 和分区号 1
|
||||||
|
partition_number_str = ''.join(filter(str.isdigit, device_name)) # 从 sda1 提取 "1"
|
||||||
|
if not partition_number_str:
|
||||||
|
QMessageBox.critical(None, "错误", f"无法从 {device_name} 解析分区号。")
|
||||||
|
logger.error(f"无法从 {device_name} 解析分区号。")
|
||||||
|
return False
|
||||||
|
|
||||||
|
parent_disk_name = device_name.rstrip(partition_number_str) # 从 sda1 提取 "sda"
|
||||||
|
parent_disk_path = self._get_device_path(parent_disk_name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# parted -s /dev/sda rm 1
|
||||||
|
stdout, stderr = self.system_manager._run_command(
|
||||||
|
["parted", "-s", parent_disk_path, "rm", partition_number_str],
|
||||||
|
root_privilege=True
|
||||||
|
)
|
||||||
|
logger.info(f"成功删除分区 {dev_path}")
|
||||||
|
QMessageBox.information(None, "成功", f"成功删除分区 {dev_path}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除分区 {dev_path} 失败: {e}")
|
||||||
|
QMessageBox.critical(None, "错误", f"删除分区 {dev_path} 失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def format_partition(self, device_name, fstype=None):
|
||||||
|
"""
|
||||||
|
格式化指定的分区。
|
||||||
|
此操作不可逆,会丢失数据。
|
||||||
|
"""
|
||||||
|
dev_path = self._get_device_path(device_name)
|
||||||
|
logger.info(f"尝试格式化分区: {dev_path}")
|
||||||
|
|
||||||
|
# 安全检查: 确保是分区
|
||||||
|
if not any(char.isdigit() for char in device_name):
|
||||||
|
QMessageBox.warning(None, "警告", f"{dev_path} 看起来不是一个分区。为安全起见,不执行格式化操作。")
|
||||||
|
logger.warning(f"尝试格式化整个磁盘 {dev_path},已阻止。")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not fstype:
|
||||||
|
# 提示用户选择文件系统类型
|
||||||
|
fstypes = ["ext4", "xfs", "fat32", "ntfs"] # 常用文件系统
|
||||||
|
fstype, ok = QInputDialog.getItem(None, "格式化分区",
|
||||||
|
f"请选择 {dev_path} 的文件系统类型:",
|
||||||
|
fstypes, 0, False) # 默认选择 ext4
|
||||||
|
if not ok or not fstype:
|
||||||
|
logger.info("用户取消了格式化操作或未选择文件系统。")
|
||||||
|
return False
|
||||||
|
|
||||||
|
reply = QMessageBox.question(None, "确认格式化分区",
|
||||||
|
f"你确定要格式化分区 {dev_path} 为 {fstype} 吗?\n"
|
||||||
|
"此操作不可逆,将导致数据丢失!",
|
||||||
|
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||||||
|
if reply == QMessageBox.No:
|
||||||
|
logger.info(f"用户取消了格式化分区 {dev_path} 的操作。")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 首先尝试卸载分区(如果已挂载)
|
||||||
|
try:
|
||||||
|
self.system_manager._run_command(["umount", dev_path], root_privilege=True, check_output=False)
|
||||||
|
logger.info(f"尝试卸载 {dev_path} (如果已挂载)。")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"卸载 {dev_path} 失败或未挂载: {e}")
|
||||||
|
|
||||||
|
# 执行 mkfs 命令
|
||||||
|
try:
|
||||||
|
mkfs_cmd = []
|
||||||
|
if fstype == "ext4":
|
||||||
|
mkfs_cmd = ["mkfs.ext4", "-F", dev_path] # -F 强制执行
|
||||||
|
elif fstype == "xfs":
|
||||||
|
mkfs_cmd = ["mkfs.xfs", "-f", dev_path] # -f 强制执行
|
||||||
|
elif fstype == "fat32":
|
||||||
|
mkfs_cmd = ["mkfs.fat", "-F", "32", dev_path]
|
||||||
|
elif fstype == "ntfs":
|
||||||
|
mkfs_cmd = ["mkfs.ntfs", "-f", dev_path] # -f 强制执行
|
||||||
|
else:
|
||||||
|
raise ValueError(f"不支持的文件系统类型: {fstype}")
|
||||||
|
|
||||||
|
stdout, stderr = self.system_manager._run_command(mkfs_cmd, root_privilege=True)
|
||||||
|
logger.info(f"成功格式化分区 {dev_path} 为 {fstype}")
|
||||||
|
QMessageBox.information(None, "成功", f"成功格式化分区 {dev_path} 为 {fstype}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"格式化分区 {dev_path} 失败: {e}")
|
||||||
|
QMessageBox.critical(None, "错误", f"格式化分区 {dev_path} 失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
141
form.ui
Normal file
141
form.ui
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1000</width>
|
||||||
|
<height>700</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Linux 存储管理工具</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab_block_devices">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>块设备概览</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeWidget" name="treeWidget_block_devices">
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>设备名</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>类型</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>大小</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>挂载点</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>文件系统</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>只读</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>UUID</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>PARTUUID</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>厂商</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>型号</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>序列号</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>主次号</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>父设备名</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_raid">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>RAID 管理</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_lvm">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>LVM 管理</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="refreshButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>刷新数据</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTextEdit" name="logOutputTextEdit">
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1000</width>
|
||||||
|
<height>22</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
54
logger_config.py
Normal file
54
logger_config.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# logger_config.py
|
||||||
|
import logging
|
||||||
|
from PySide6.QtWidgets import QTextEdit
|
||||||
|
from PySide6.QtCore import Signal, QObject
|
||||||
|
|
||||||
|
class QTextEditLogger(logging.Handler, QObject):
|
||||||
|
"""
|
||||||
|
自定义的 logging 处理器,将日志消息发送到 QTextEdit 控件。
|
||||||
|
"""
|
||||||
|
# 定义一个信号,用于在 GUI 线程中更新 QTextEdit
|
||||||
|
append_text = Signal(str)
|
||||||
|
|
||||||
|
def __init__(self, widget: QTextEdit):
|
||||||
|
super().__init__()
|
||||||
|
QObject.__init__(self) # 初始化 QObject 部分
|
||||||
|
self.widget = widget
|
||||||
|
self.widget.setReadOnly(True) # 确保日志区域是只读的
|
||||||
|
# 连接信号到 QTextEdit 的 append 方法
|
||||||
|
self.append_text.connect(self.widget.append)
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""
|
||||||
|
处理日志记录,将其格式化并通过信号发送到 QTextEdit。
|
||||||
|
"""
|
||||||
|
msg = self.format(record)
|
||||||
|
self.append_text.emit(msg)
|
||||||
|
|
||||||
|
def setup_logging(text_edit_widget: QTextEdit):
|
||||||
|
"""
|
||||||
|
配置全局 logging,使其输出到控制台和指定的 QTextEdit 控件。
|
||||||
|
"""
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
# 设置日志级别,可以根据需要调整 (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||||
|
root_logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
# 清除现有的处理器,防止重复输出(如果多次调用此函数)
|
||||||
|
for handler in root_logger.handlers[:]:
|
||||||
|
root_logger.removeHandler(handler)
|
||||||
|
|
||||||
|
# 1. 控制台处理器 (可选,用于调试)
|
||||||
|
console_handler = logging.StreamHandler()
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
root_logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
# 2. QTextEdit 处理器
|
||||||
|
text_edit_handler = QTextEditLogger(text_edit_widget)
|
||||||
|
text_edit_handler.setFormatter(formatter)
|
||||||
|
root_logger.addHandler(text_edit_handler)
|
||||||
|
|
||||||
|
return root_logger
|
||||||
|
|
||||||
|
# 获取一个全局的 logger 实例,方便其他模块使用
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
189
mainwindow.py
Normal file
189
mainwindow.py
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
# mainwindow.py
|
||||||
|
import sys
|
||||||
|
import logging # 导入 logging 模块
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import (QApplication, QMainWindow, QTreeWidgetItem,
|
||||||
|
QMessageBox, QHeaderView, QMenu, QInputDialog)
|
||||||
|
from PySide6.QtCore import Qt, QPoint
|
||||||
|
|
||||||
|
# 导入自动生成的 UI 文件
|
||||||
|
from ui_form import Ui_MainWindow
|
||||||
|
# 导入我们自己编写的系统信息管理模块
|
||||||
|
from system_info import SystemInfoManager
|
||||||
|
# 导入日志配置
|
||||||
|
from logger_config import setup_logging, logger # 导入日志配置函数和 logger 实例
|
||||||
|
# 导入磁盘操作模块
|
||||||
|
from disk_operations import DiskOperations
|
||||||
|
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.ui = Ui_MainWindow()
|
||||||
|
self.ui.setupUi(self)
|
||||||
|
|
||||||
|
# 设置日志输出到 QTextEdit
|
||||||
|
setup_logging(self.ui.logOutputTextEdit)
|
||||||
|
logger.info("应用程序启动。")
|
||||||
|
|
||||||
|
# 初始化系统信息管理器和磁盘操作管理器
|
||||||
|
self.system_manager = SystemInfoManager()
|
||||||
|
self.disk_ops = DiskOperations()
|
||||||
|
|
||||||
|
# 连接刷新按钮的信号到槽函数
|
||||||
|
if hasattr(self.ui, 'refreshButton'):
|
||||||
|
self.ui.refreshButton.clicked.connect(self.refresh_block_devices_info)
|
||||||
|
else:
|
||||||
|
logger.warning("Warning: refreshButton not found in UI. Please add it in form.ui and regenerate ui_form.py.")
|
||||||
|
|
||||||
|
# 启用 treeWidget 的自定义上下文菜单
|
||||||
|
self.ui.treeWidget_block_devices.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
|
self.ui.treeWidget_block_devices.customContextMenuRequested.connect(self.show_block_device_context_menu)
|
||||||
|
|
||||||
|
# 初始化时刷新一次数据
|
||||||
|
self.refresh_block_devices_info()
|
||||||
|
logger.info("块设备信息已初始化加载。")
|
||||||
|
|
||||||
|
def refresh_block_devices_info(self):
|
||||||
|
"""
|
||||||
|
刷新块设备信息并显示在 QTreeWidget 中。
|
||||||
|
"""
|
||||||
|
self.ui.treeWidget_block_devices.clear() # 清空现有内容
|
||||||
|
|
||||||
|
# 定义所有要显示的列头和对应的 lsblk 字段名
|
||||||
|
columns = [
|
||||||
|
("设备名", 'name'),
|
||||||
|
("类型", 'type'),
|
||||||
|
("大小", 'size'),
|
||||||
|
("挂载点", 'mountpoint'),
|
||||||
|
("文件系统", 'fstype'),
|
||||||
|
("只读", 'ro'),
|
||||||
|
("UUID", 'uuid'),
|
||||||
|
("PARTUUID", 'partuuid'),
|
||||||
|
("厂商", 'vendor'),
|
||||||
|
("型号", 'model'),
|
||||||
|
("序列号", 'serial'),
|
||||||
|
("主次号", 'maj:min'),
|
||||||
|
("父设备名", 'pkname'),
|
||||||
|
]
|
||||||
|
|
||||||
|
headers = [col[0] for col in columns]
|
||||||
|
self.field_keys = [col[1] for col in columns] # 保存字段键,供后续使用
|
||||||
|
|
||||||
|
self.ui.treeWidget_block_devices.setColumnCount(len(headers)) # 确保列数正确
|
||||||
|
self.ui.treeWidget_block_devices.setHeaderLabels(headers)
|
||||||
|
|
||||||
|
# 调整列宽以适应内容
|
||||||
|
for i in range(len(headers)):
|
||||||
|
self.ui.treeWidget_block_devices.header().setSectionResizeMode(i, QHeaderView.ResizeToContents)
|
||||||
|
|
||||||
|
try:
|
||||||
|
devices = self.system_manager.get_block_devices()
|
||||||
|
for dev in devices:
|
||||||
|
self._add_device_to_tree(self.ui.treeWidget_block_devices, dev)
|
||||||
|
|
||||||
|
# 自动调整列宽
|
||||||
|
for i in range(len(headers)):
|
||||||
|
self.ui.treeWidget_block_devices.resizeColumnToContents(i)
|
||||||
|
logger.info("块设备信息刷新成功。")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "错误", f"刷新块设备信息失败: {e}")
|
||||||
|
logger.error(f"刷新块设备信息失败: {e}")
|
||||||
|
|
||||||
|
def _add_device_to_tree(self, parent_item, dev_data):
|
||||||
|
"""
|
||||||
|
辅助函数,将单个设备及其子设备添加到 QTreeWidget。
|
||||||
|
parent_item 可以是 QTreeWidget 本身,也可以是另一个 QTreeWidgetItem。
|
||||||
|
"""
|
||||||
|
item = QTreeWidgetItem(parent_item)
|
||||||
|
for i, key in enumerate(self.field_keys):
|
||||||
|
value = dev_data.get(key)
|
||||||
|
if key == 'ro': # 特殊处理布尔值
|
||||||
|
item.setText(i, "是" if value else "否")
|
||||||
|
elif value is None: # None 值显示为空字符串
|
||||||
|
item.setText(i, "")
|
||||||
|
else:
|
||||||
|
item.setText(i, str(value))
|
||||||
|
|
||||||
|
# 将原始设备数据存储在 item 的 data 属性中,方便后续操作时获取
|
||||||
|
item.setData(0, Qt.UserRole, dev_data)
|
||||||
|
|
||||||
|
# 如果有子设备(分区),也显示出来
|
||||||
|
if 'children' in dev_data:
|
||||||
|
for child in dev_data['children']:
|
||||||
|
self._add_device_to_tree(item, child)
|
||||||
|
item.setExpanded(True) # 默认展开父节点,以便看到分区
|
||||||
|
|
||||||
|
def show_block_device_context_menu(self, pos: QPoint):
|
||||||
|
"""
|
||||||
|
显示块设备列表的右键上下文菜单。
|
||||||
|
"""
|
||||||
|
item = self.ui.treeWidget_block_devices.itemAt(pos)
|
||||||
|
if item:
|
||||||
|
dev_data = item.data(0, Qt.UserRole) # 获取存储的原始设备数据
|
||||||
|
if not dev_data:
|
||||||
|
logger.warning(f"无法获取设备 {item.text(0)} 的详细数据。")
|
||||||
|
return
|
||||||
|
|
||||||
|
device_name = dev_data.get('name')
|
||||||
|
device_type = dev_data.get('type')
|
||||||
|
mount_point = dev_data.get('mountpoint')
|
||||||
|
|
||||||
|
menu = QMenu(self)
|
||||||
|
|
||||||
|
# 挂载/卸载操作
|
||||||
|
# 只有 'part' (分区) 和 'disk' (整个磁盘,但通常只挂载分区) 可以被挂载/卸载
|
||||||
|
if device_type in ['part', 'disk']:
|
||||||
|
if not mount_point or mount_point == '' or mount_point == 'N/A': # 未挂载
|
||||||
|
mount_action = menu.addAction(f"挂载 {device_name}...")
|
||||||
|
mount_action.triggered.connect(lambda: self._handle_mount(device_name))
|
||||||
|
elif mount_point != '[SWAP]': # 已挂载且不是SWAP
|
||||||
|
unmount_action = menu.addAction(f"卸载 {device_name}")
|
||||||
|
unmount_action.triggered.connect(lambda: self._handle_unmount(device_name))
|
||||||
|
|
||||||
|
# 分隔符,用于区分操作
|
||||||
|
if menu.actions():
|
||||||
|
menu.addSeparator()
|
||||||
|
|
||||||
|
# 删除分区和格式化操作
|
||||||
|
# 这些操作通常只针对 'part' (分区)
|
||||||
|
if device_type == 'part':
|
||||||
|
delete_action = menu.addAction(f"删除分区 {device_name}")
|
||||||
|
delete_action.triggered.connect(lambda: self._handle_delete_partition(device_name))
|
||||||
|
|
||||||
|
format_action = menu.addAction(f"格式化分区 {device_name}...")
|
||||||
|
format_action.triggered.connect(lambda: self._handle_format_partition(device_name))
|
||||||
|
|
||||||
|
if menu.actions(): # 只有当菜单中有动作时才显示
|
||||||
|
menu.exec(self.ui.treeWidget_block_devices.mapToGlobal(pos))
|
||||||
|
else:
|
||||||
|
logger.info(f"设备 {device_name} 没有可用的操作。")
|
||||||
|
else:
|
||||||
|
logger.info("右键点击了空白区域。")
|
||||||
|
|
||||||
|
def _handle_mount(self, device_name):
|
||||||
|
"""处理挂载操作,并刷新UI。"""
|
||||||
|
if self.disk_ops.mount_partition(device_name):
|
||||||
|
self.refresh_block_devices_info() # 操作成功后刷新UI
|
||||||
|
|
||||||
|
def _handle_unmount(self, device_name):
|
||||||
|
"""处理卸载操作,并刷新UI。"""
|
||||||
|
if self.disk_ops.unmount_partition(device_name):
|
||||||
|
self.refresh_block_devices_info() # 操作成功后刷新UI
|
||||||
|
|
||||||
|
def _handle_delete_partition(self, device_name):
|
||||||
|
"""处理删除分区操作,并刷新UI。"""
|
||||||
|
if self.disk_ops.delete_partition(device_name):
|
||||||
|
self.refresh_block_devices_info() # 操作成功后刷新UI
|
||||||
|
|
||||||
|
def _handle_format_partition(self, device_name):
|
||||||
|
"""处理格式化分区操作,并刷新UI。"""
|
||||||
|
if self.disk_ops.format_partition(device_name):
|
||||||
|
self.refresh_block_devices_info() # 操作成功后刷新UI
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
widget = MainWindow()
|
||||||
|
widget.show()
|
||||||
|
sys.exit(app.exec())
|
||||||
5
pyproject.toml
Normal file
5
pyproject.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[project]
|
||||||
|
name = "PySide Widgets Project"
|
||||||
|
|
||||||
|
[tool.pyside6-project]
|
||||||
|
files = ["disk_operations.py", "form.ui", "logger_config.py", "mainwindow.py", "system_info.py"]
|
||||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PySide6
|
||||||
147
system_info.py
Normal file
147
system_info.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# system_info.py
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import logging # 导入 logging 模块
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__) # 获取当前模块的 logger 实例
|
||||||
|
|
||||||
|
class SystemInfoManager:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _run_command(self, cmd, root_privilege=False, check_output=True):
|
||||||
|
"""
|
||||||
|
执行shell命令并返回stdout和stderr。
|
||||||
|
如果需要root权限,会尝试使用sudo。
|
||||||
|
所有输出和错误都会通过 logger 记录。
|
||||||
|
"""
|
||||||
|
full_cmd = []
|
||||||
|
if root_privilege:
|
||||||
|
# 检查当前是否已经是root用户
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
full_cmd.append("sudo")
|
||||||
|
|
||||||
|
full_cmd.extend(cmd)
|
||||||
|
cmd_str = ' '.join(full_cmd) # 用于日志记录的完整命令字符串
|
||||||
|
|
||||||
|
logger.info(f"执行命令: {cmd_str}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# text=True 自动解码为字符串,encoding='utf-8' 确保正确处理中文
|
||||||
|
# check=check_output 表示如果命令返回非零退出码,则抛出CalledProcessError
|
||||||
|
result = subprocess.run(
|
||||||
|
full_cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=check_output,
|
||||||
|
encoding='utf-8',
|
||||||
|
# 设置LANG环境变量,确保命令输出使用UTF-8编码,避免解析问题
|
||||||
|
env=dict(os.environ, LANG="en_US.UTF-8")
|
||||||
|
)
|
||||||
|
if result.stdout:
|
||||||
|
logger.debug(f"命令输出 (stdout):\n{result.stdout.strip()}")
|
||||||
|
if result.stderr:
|
||||||
|
logger.warning(f"命令输出 (stderr):\n{result.stderr.strip()}")
|
||||||
|
return result.stdout.strip(), result.stderr.strip()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
# 捕获命令执行失败的情况
|
||||||
|
error_msg = f"命令执行失败: {cmd_str}\n" \
|
||||||
|
f"退出码: {e.returncode}\n" \
|
||||||
|
f"标准输出: {e.stdout}\n" \
|
||||||
|
f"标准错误: {e.stderr}"
|
||||||
|
logger.error(error_msg) # 使用 logger 记录错误
|
||||||
|
raise ValueError(error_msg) # 抛出更易于处理的异常
|
||||||
|
except FileNotFoundError:
|
||||||
|
error_msg = f"命令未找到: {full_cmd[0]}。请确保已安装。"
|
||||||
|
logger.error(error_msg)
|
||||||
|
raise FileNotFoundError(error_msg)
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"执行命令时发生未知错误: {e}"
|
||||||
|
logger.error(error_msg)
|
||||||
|
raise RuntimeError(error_msg)
|
||||||
|
|
||||||
|
def get_block_devices(self):
|
||||||
|
"""
|
||||||
|
获取所有块设备的信息,以JSON格式返回。
|
||||||
|
使用 lsblk -J 命令。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
stdout, _ = self._run_command(["lsblk", "-J", "-o", "NAME,FSTYPE,SIZE,MOUNTPOINT,RO,TYPE,UUID,PARTUUID,VENDOR,MODEL,SERIAL,MAJ:MIN,PKNAME"], root_privilege=False)
|
||||||
|
data = json.loads(stdout)
|
||||||
|
logger.info("成功获取块设备信息。")
|
||||||
|
return data.get('blockdevices', [])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取块设备信息失败: {e}") # 使用 logger 记录错误
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_mdadm_arrays(self):
|
||||||
|
"""
|
||||||
|
获取所有RAID阵列的详细信息。
|
||||||
|
使用 mdadm --detail --scan 命令。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
stdout, _ = self._run_command(["mdadm", "--detail", "--scan"], root_privilege=False)
|
||||||
|
logger.info("成功获取RAID阵列信息。")
|
||||||
|
return stdout
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取RAID阵列信息失败: {e}")
|
||||||
|
return "无法获取RAID阵列信息。"
|
||||||
|
|
||||||
|
def get_lvm_info(self):
|
||||||
|
"""
|
||||||
|
获取LVM的物理卷、卷组、逻辑卷信息。
|
||||||
|
使用 pvs, vgs, lvs 命令,并尝试获取JSON格式。
|
||||||
|
"""
|
||||||
|
lvm_info = {}
|
||||||
|
try:
|
||||||
|
stdout_pvs, _ = self._run_command(["pvs", "--reportformat", "json"], root_privilege=False)
|
||||||
|
lvm_info['pvs'] = json.loads(stdout_pvs).get('report', [])[0].get('pv', [])
|
||||||
|
logger.info("成功获取物理卷信息。")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取物理卷信息失败: {e}")
|
||||||
|
lvm_info['pvs'] = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
stdout_vgs, _ = self._run_command(["vgs", "--reportformat", "json"], root_privilege=False)
|
||||||
|
lvm_info['vgs'] = json.loads(stdout_vgs).get('report', [])[0].get('vg', [])
|
||||||
|
logger.info("成功获取卷组信息。")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取卷组信息失败: {e}")
|
||||||
|
lvm_info['vgs'] = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
stdout_lvs, _ = self._run_command(["lvs", "--reportformat", "json"], root_privilege=False)
|
||||||
|
lvm_info['lvs'] = json.loads(stdout_lvs).get('report', [])[0].get('lv', [])
|
||||||
|
logger.info("成功获取逻辑卷信息。")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取逻辑卷信息失败: {e}")
|
||||||
|
lvm_info['lvs'] = []
|
||||||
|
|
||||||
|
return lvm_info
|
||||||
|
|
||||||
|
# 示例用法 (可以在此模块中添加测试代码)
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 为了在单独运行时也能看到日志输出,这里简单配置一下
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
manager = SystemInfoManager()
|
||||||
|
logger.info("--- 块设备信息 ---")
|
||||||
|
devices = manager.get_block_devices()
|
||||||
|
for dev in devices:
|
||||||
|
logger.info(f" NAME: {dev.get('name')}, TYPE: {dev.get('type')}, SIZE: {dev.get('size')}, MOUNTPOINT: {dev.get('mountpoint')}")
|
||||||
|
|
||||||
|
logger.info("\n--- RAID 阵列信息 ---")
|
||||||
|
raid_info = manager.get_mdadm_arrays()
|
||||||
|
logger.info(raid_info)
|
||||||
|
|
||||||
|
logger.info("\n--- LVM 信息 ---")
|
||||||
|
lvm_info = manager.get_lvm_info()
|
||||||
|
logger.info("物理卷 (PVs):")
|
||||||
|
for pv in lvm_info['pvs']:
|
||||||
|
logger.info(f" {pv.get('pv_name')} (VG: {pv.get('vg_name')}, Size: {pv.get('pv_size')})")
|
||||||
|
logger.info("卷组 (VGs):")
|
||||||
|
for vg in lvm_info['vgs']:
|
||||||
|
logger.info(f" {vg.get('vg_name')} (Size: {vg.get('vg_size')}, PVs: {vg.get('pv_count')}, LVs: {vg.get('lv_count')})")
|
||||||
|
logger.info("逻辑卷 (LVs):")
|
||||||
|
for lv in lvm_info['lvs']:
|
||||||
|
logger.info(f" {lv.get('lv_name')} (VG: {lv.get('vg_name')}, Size: {lv.get('lv_size')})")
|
||||||
102
ui_form.py
Normal file
102
ui_form.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Form generated from reading UI file 'form.ui'
|
||||||
|
##
|
||||||
|
## Created by: Qt User Interface Compiler version 6.10.1
|
||||||
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||||
|
QFont, QFontDatabase, QGradient, QIcon,
|
||||||
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||||
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||||
|
from PySide6.QtWidgets import (QApplication, QHeaderView, QMainWindow, QMenuBar,
|
||||||
|
QPushButton, QSizePolicy, QStatusBar, QTabWidget,
|
||||||
|
QTextEdit, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
|
||||||
|
QWidget)
|
||||||
|
|
||||||
|
class Ui_MainWindow(object):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
if not MainWindow.objectName():
|
||||||
|
MainWindow.setObjectName(u"MainWindow")
|
||||||
|
MainWindow.resize(1000, 700)
|
||||||
|
self.centralwidget = QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName(u"centralwidget")
|
||||||
|
self.verticalLayout = QVBoxLayout(self.centralwidget)
|
||||||
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||||
|
self.tabWidget = QTabWidget(self.centralwidget)
|
||||||
|
self.tabWidget.setObjectName(u"tabWidget")
|
||||||
|
self.tab_block_devices = QWidget()
|
||||||
|
self.tab_block_devices.setObjectName(u"tab_block_devices")
|
||||||
|
self.verticalLayout_2 = QVBoxLayout(self.tab_block_devices)
|
||||||
|
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||||
|
self.treeWidget_block_devices = QTreeWidget(self.tab_block_devices)
|
||||||
|
self.treeWidget_block_devices.setObjectName(u"treeWidget_block_devices")
|
||||||
|
|
||||||
|
self.verticalLayout_2.addWidget(self.treeWidget_block_devices)
|
||||||
|
|
||||||
|
self.tabWidget.addTab(self.tab_block_devices, "")
|
||||||
|
self.tab_raid = QWidget()
|
||||||
|
self.tab_raid.setObjectName(u"tab_raid")
|
||||||
|
self.tabWidget.addTab(self.tab_raid, "")
|
||||||
|
self.tab_lvm = QWidget()
|
||||||
|
self.tab_lvm.setObjectName(u"tab_lvm")
|
||||||
|
self.tabWidget.addTab(self.tab_lvm, "")
|
||||||
|
|
||||||
|
self.verticalLayout.addWidget(self.tabWidget)
|
||||||
|
|
||||||
|
self.refreshButton = QPushButton(self.centralwidget)
|
||||||
|
self.refreshButton.setObjectName(u"refreshButton")
|
||||||
|
|
||||||
|
self.verticalLayout.addWidget(self.refreshButton)
|
||||||
|
|
||||||
|
self.logOutputTextEdit = QTextEdit(self.centralwidget)
|
||||||
|
self.logOutputTextEdit.setObjectName(u"logOutputTextEdit")
|
||||||
|
self.logOutputTextEdit.setReadOnly(True)
|
||||||
|
|
||||||
|
self.verticalLayout.addWidget(self.logOutputTextEdit)
|
||||||
|
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QMenuBar(MainWindow)
|
||||||
|
self.menubar.setObjectName(u"menubar")
|
||||||
|
self.menubar.setGeometry(QRect(0, 0, 1000, 22))
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName(u"statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
|
||||||
|
self.tabWidget.setCurrentIndex(0)
|
||||||
|
|
||||||
|
|
||||||
|
QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
# setupUi
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"Linux \u5b58\u50a8\u7ba1\u7406\u5de5\u5177", None))
|
||||||
|
___qtreewidgetitem = self.treeWidget_block_devices.headerItem()
|
||||||
|
___qtreewidgetitem.setText(12, QCoreApplication.translate("MainWindow", u"\u7236\u8bbe\u5907\u540d", None));
|
||||||
|
___qtreewidgetitem.setText(11, QCoreApplication.translate("MainWindow", u"\u4e3b\u6b21\u53f7", None));
|
||||||
|
___qtreewidgetitem.setText(10, QCoreApplication.translate("MainWindow", u"\u5e8f\u5217\u53f7", None));
|
||||||
|
___qtreewidgetitem.setText(9, QCoreApplication.translate("MainWindow", u"\u578b\u53f7", None));
|
||||||
|
___qtreewidgetitem.setText(8, QCoreApplication.translate("MainWindow", u"\u5382\u5546", None));
|
||||||
|
___qtreewidgetitem.setText(7, QCoreApplication.translate("MainWindow", u"PARTUUID", None));
|
||||||
|
___qtreewidgetitem.setText(6, QCoreApplication.translate("MainWindow", u"UUID", None));
|
||||||
|
___qtreewidgetitem.setText(5, QCoreApplication.translate("MainWindow", u"\u53ea\u8bfb", None));
|
||||||
|
___qtreewidgetitem.setText(4, QCoreApplication.translate("MainWindow", u"\u6587\u4ef6\u7cfb\u7edf", None));
|
||||||
|
___qtreewidgetitem.setText(3, QCoreApplication.translate("MainWindow", u"\u6302\u8f7d\u70b9", None));
|
||||||
|
___qtreewidgetitem.setText(2, QCoreApplication.translate("MainWindow", u"\u5927\u5c0f", None));
|
||||||
|
___qtreewidgetitem.setText(1, QCoreApplication.translate("MainWindow", u"\u7c7b\u578b", None));
|
||||||
|
___qtreewidgetitem.setText(0, QCoreApplication.translate("MainWindow", u"\u8bbe\u5907\u540d", None));
|
||||||
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_block_devices), QCoreApplication.translate("MainWindow", u"\u5757\u8bbe\u5907\u6982\u89c8", None))
|
||||||
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_raid), QCoreApplication.translate("MainWindow", u"RAID \u7ba1\u7406", None))
|
||||||
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_lvm), QCoreApplication.translate("MainWindow", u"LVM \u7ba1\u7406", None))
|
||||||
|
self.refreshButton.setText(QCoreApplication.translate("MainWindow", u"\u5237\u65b0\u6570\u636e", None))
|
||||||
|
# retranslateUi
|
||||||
|
|
||||||
Reference in New Issue
Block a user