From be5a0bc2c7c59e3723e22d89a437abd90ae4b01e Mon Sep 17 00:00:00 2001 From: zj <1052308357@qq.comm> Date: Mon, 2 Feb 2026 04:20:00 +0800 Subject: [PATCH] add raid --- .qtcreator/pyproject.toml.user | 112 ++++--- .qtcreator/pyproject.toml.user.d278bd6 | 342 ++++++++++++++++++++ .qtcreator/pyproject.toml.user.de30b19 | 342 ++++++++++++++++++++ __pycache__/disk_operations.cpython-314.pyc | Bin 12911 -> 12909 bytes __pycache__/logger_config.cpython-314.pyc | Bin 3130 -> 3128 bytes __pycache__/system_info.cpython-314.pyc | Bin 9288 -> 14069 bytes __pycache__/ui_form.cpython-314.pyc | Bin 8131 -> 9479 bytes form.ui | 24 +- mainwindow.py | 210 +++++++++--- system_info.py | 163 ++++++++-- ui_form.py | 20 +- up+.sh | 68 ++++ 12 files changed, 1162 insertions(+), 119 deletions(-) create mode 100644 .qtcreator/pyproject.toml.user.d278bd6 create mode 100644 .qtcreator/pyproject.toml.user.de30b19 create mode 100644 up+.sh diff --git a/.qtcreator/pyproject.toml.user b/.qtcreator/pyproject.toml.user index 5a508a6..f646c9c 100644 --- a/.qtcreator/pyproject.toml.user +++ b/.qtcreator/pyproject.toml.user @@ -1,10 +1,10 @@ - + EnvironmentId - {d278bd62-883b-4816-a712-738287b946d3} + {aee81d31-6056-4648-9127-61bd5a906fa5} ProjectExplorer.Project.ActiveTarget @@ -79,7 +79,7 @@ true true Builtin.DefaultTidyAndClazy - 2 + 4 true @@ -96,18 +96,16 @@ true Python 3.14.2 Python 3.14.2 - {bf68bfb7-0daf-4abe-a8f9-98ada8c9b297} + {2b4075d3-005b-46f6-8fd5-3bd8a94071db} 0 0 0 - /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv + /home/jing/qtpj/diskmanager/.qtcreator/Python_3_14_2venv_2 true Python.PysideBuildStep - /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv/bin/pyside6-project - /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv/bin/pyside6-uic 1 构建 @@ -152,16 +150,16 @@ false -e cpu-cycles --call-graph dwarf,4096 -F 250 - mainwindow.py + disk_operations.py PythonEditor.RunConfiguration. - /home/smart/qtpj/diskmananger/mainwindow.py - true + /home/jing/qtpj/diskmanager/disk_operations.py + false - /home/smart/qtpj/diskmananger/mainwindow.py + /home/jing/qtpj/diskmanager/disk_operations.py true true - /home/smart/qtpj/diskmananger - :0.0 + /home/jing/qtpj/diskmanager + :0 true @@ -173,16 +171,16 @@ false -e cpu-cycles --call-graph dwarf,4096 -F 250 - system_info.py + logger_config.py PythonEditor.RunConfiguration. - /home/smart/qtpj/diskmananger/system_info.py - true + /home/jing/qtpj/diskmanager/logger_config.py + false - /home/smart/qtpj/diskmananger/system_info.py + /home/jing/qtpj/diskmanager/logger_config.py true true - /home/smart/qtpj/diskmananger - :0.0 + /home/jing/qtpj/diskmanager + :0 true @@ -194,16 +192,16 @@ false -e cpu-cycles --call-graph dwarf,4096 -F 250 - logger_config.py + mainwindow.py PythonEditor.RunConfiguration. - /home/smart/qtpj/diskmananger/logger_config.py - true + /home/jing/qtpj/diskmanager/mainwindow.py + false - /home/smart/qtpj/diskmananger/logger_config.py + /home/jing/qtpj/diskmanager/mainwindow.py true true - /home/smart/qtpj/diskmananger - :0.0 + /home/jing/qtpj/diskmanager + :0 true @@ -215,20 +213,20 @@ false -e cpu-cycles --call-graph dwarf,4096 -F 250 - disk_operations.py + system_info.py PythonEditor.RunConfiguration. - /home/smart/qtpj/diskmananger/disk_operations.py - true + /home/jing/qtpj/diskmanager/system_info.py + false - /home/smart/qtpj/diskmananger/disk_operations.py + /home/jing/qtpj/diskmanager/system_info.py true true - /home/smart/qtpj/diskmananger - :0.0 + /home/jing/qtpj/diskmanager + :0 4 - /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv/bin/python - /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv + /home/jing/qtpj/diskmanager/.qtcreator/Python_3_14_2venv_2/bin/python + /home/jing/qtpj/diskmanager/.qtcreator/Python_3_14_2venv_2 1 @@ -254,16 +252,16 @@ false -e cpu-cycles --call-graph dwarf,4096 -F 250 - mainwindow.py + disk_operations.py PythonEditor.RunConfiguration. - /home/smart/qtpj/diskmananger/mainwindow.py - true + /home/jing/qtpj/diskmanager/disk_operations.py + false - /home/smart/qtpj/diskmananger/mainwindow.py + /home/jing/qtpj/diskmanager/disk_operations.py true true - /home/smart/qtpj/diskmananger - :0.0 + /home/jing/qtpj/diskmanager + :0 true @@ -275,16 +273,16 @@ false -e cpu-cycles --call-graph dwarf,4096 -F 250 - system_info.py + logger_config.py PythonEditor.RunConfiguration. - /home/smart/qtpj/diskmananger/system_info.py - true + /home/jing/qtpj/diskmanager/logger_config.py + false - /home/smart/qtpj/diskmananger/system_info.py + /home/jing/qtpj/diskmanager/logger_config.py true true - /home/smart/qtpj/diskmananger - :0.0 + /home/jing/qtpj/diskmanager + :0 true @@ -296,16 +294,16 @@ false -e cpu-cycles --call-graph dwarf,4096 -F 250 - logger_config.py + mainwindow.py PythonEditor.RunConfiguration. - /home/smart/qtpj/diskmananger/logger_config.py - true + /home/jing/qtpj/diskmanager/mainwindow.py + false - /home/smart/qtpj/diskmananger/logger_config.py + /home/jing/qtpj/diskmanager/mainwindow.py true true - /home/smart/qtpj/diskmananger - :0.0 + /home/jing/qtpj/diskmanager + :0 true @@ -317,16 +315,16 @@ false -e cpu-cycles --call-graph dwarf,4096 -F 250 - disk_operations.py + system_info.py PythonEditor.RunConfiguration. - /home/smart/qtpj/diskmananger/disk_operations.py - true + /home/jing/qtpj/diskmanager/system_info.py + false - /home/smart/qtpj/diskmananger/disk_operations.py + /home/jing/qtpj/diskmanager/system_info.py true true - /home/smart/qtpj/diskmananger - :0.0 + /home/jing/qtpj/diskmanager + :0 4 diff --git a/.qtcreator/pyproject.toml.user.d278bd6 b/.qtcreator/pyproject.toml.user.d278bd6 new file mode 100644 index 0000000..5a508a6 --- /dev/null +++ b/.qtcreator/pyproject.toml.user.d278bd6 @@ -0,0 +1,342 @@ + + + + + + EnvironmentId + {d278bd62-883b-4816-a712-738287b946d3} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + true + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 0 + 80 + true + true + 1 + 0 + false + true + false + 2 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + false + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 2 + true + + + + true + + 0 + + + + ProjectExplorer.Project.Target.0 + + Desktop + true + Python 3.14.2 + Python 3.14.2 + {bf68bfb7-0daf-4abe-a8f9-98ada8c9b297} + 0 + 0 + 0 + + /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv + + + true + Python.PysideBuildStep + /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv/bin/pyside6-project + /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv/bin/pyside6-uic + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + 0 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Python 3.14.2 Virtual Environment + Python.PySideBuildConfiguration + 0 + 0 + + + 0 + 部署 + 部署 + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + mainwindow.py + PythonEditor.RunConfiguration. + /home/smart/qtpj/diskmananger/mainwindow.py + true + + /home/smart/qtpj/diskmananger/mainwindow.py + true + true + /home/smart/qtpj/diskmananger + :0.0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + system_info.py + PythonEditor.RunConfiguration. + /home/smart/qtpj/diskmananger/system_info.py + true + + /home/smart/qtpj/diskmananger/system_info.py + true + true + /home/smart/qtpj/diskmananger + :0.0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + logger_config.py + PythonEditor.RunConfiguration. + /home/smart/qtpj/diskmananger/logger_config.py + true + + /home/smart/qtpj/diskmananger/logger_config.py + true + true + /home/smart/qtpj/diskmananger + :0.0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + disk_operations.py + PythonEditor.RunConfiguration. + /home/smart/qtpj/diskmananger/disk_operations.py + true + + /home/smart/qtpj/diskmananger/disk_operations.py + true + true + /home/smart/qtpj/diskmananger + :0.0 + + 4 + /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv/bin/python + /home/smart/qtpj/diskmananger/.qtcreator/Python_3_14_2venv + + 1 + + + 0 + 部署 + 部署 + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + mainwindow.py + PythonEditor.RunConfiguration. + /home/smart/qtpj/diskmananger/mainwindow.py + true + + /home/smart/qtpj/diskmananger/mainwindow.py + true + true + /home/smart/qtpj/diskmananger + :0.0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + system_info.py + PythonEditor.RunConfiguration. + /home/smart/qtpj/diskmananger/system_info.py + true + + /home/smart/qtpj/diskmananger/system_info.py + true + true + /home/smart/qtpj/diskmananger + :0.0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + logger_config.py + PythonEditor.RunConfiguration. + /home/smart/qtpj/diskmananger/logger_config.py + true + + /home/smart/qtpj/diskmananger/logger_config.py + true + true + /home/smart/qtpj/diskmananger + :0.0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + disk_operations.py + PythonEditor.RunConfiguration. + /home/smart/qtpj/diskmananger/disk_operations.py + true + + /home/smart/qtpj/diskmananger/disk_operations.py + true + true + /home/smart/qtpj/diskmananger + :0.0 + + 4 + + + + ProjectExplorer.Project.TargetCount + 1 + + + Version + 22 + + diff --git a/.qtcreator/pyproject.toml.user.de30b19 b/.qtcreator/pyproject.toml.user.de30b19 new file mode 100644 index 0000000..4030985 --- /dev/null +++ b/.qtcreator/pyproject.toml.user.de30b19 @@ -0,0 +1,342 @@ + + + + + + EnvironmentId + {de30b19d-4c33-4d24-8691-fc38518ebab9} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + true + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 0 + 80 + true + true + 1 + 0 + false + true + false + 2 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + false + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 4 + true + + + + true + + 0 + + + + ProjectExplorer.Project.Target.0 + + Desktop + true + Python 3.14.2 + Python 3.14.2 + {71fad823-1bd9-4eea-bebc-1fce880d5c55} + 0 + 0 + 2 + + /home/jing/qtpj/diskmanager/.qtcreator/Python_3_14_2venv + + + true + Python.PysideBuildStep + /home/jing/qtpj/diskmanager/.qtcreator/Python_3_14_2venv/bin/pyside6-project + /home/jing/qtpj/diskmanager/.qtcreator/Python_3_14_2venv/bin/pyside6-uic + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + 0 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Python 3.14.2 Virtual Environment + Python.PySideBuildConfiguration + 0 + 2 + + + 0 + 部署 + 部署 + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + disk_operations.py + PythonEditor.RunConfiguration. + /home/jing/qtpj/diskmanager/disk_operations.py + false + + /home/jing/qtpj/diskmanager/disk_operations.py + true + true + /home/jing/qtpj/diskmanager + :0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + logger_config.py + PythonEditor.RunConfiguration. + /home/jing/qtpj/diskmanager/logger_config.py + false + + /home/jing/qtpj/diskmanager/logger_config.py + true + true + /home/jing/qtpj/diskmanager + :0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + mainwindow.py + PythonEditor.RunConfiguration. + /home/jing/qtpj/diskmanager/mainwindow.py + false + + /home/jing/qtpj/diskmanager/mainwindow.py + true + true + /home/jing/qtpj/diskmanager + :0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + system_info.py + PythonEditor.RunConfiguration. + /home/jing/qtpj/diskmanager/system_info.py + false + + /home/jing/qtpj/diskmanager/system_info.py + true + true + /home/jing/qtpj/diskmanager + :0 + + 4 + /home/jing/qtpj/diskmanager/.qtcreator/Python_3_14_2venv/bin/python + /home/jing/qtpj/diskmanager/.qtcreator/Python_3_14_2venv + + 1 + + + 0 + 部署 + 部署 + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + disk_operations.py + PythonEditor.RunConfiguration. + /home/jing/qtpj/diskmanager/disk_operations.py + false + + /home/jing/qtpj/diskmanager/disk_operations.py + true + true + /home/jing/qtpj/diskmanager + :0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + logger_config.py + PythonEditor.RunConfiguration. + /home/jing/qtpj/diskmanager/logger_config.py + false + + /home/jing/qtpj/diskmanager/logger_config.py + true + true + /home/jing/qtpj/diskmanager + :0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + mainwindow.py + PythonEditor.RunConfiguration. + /home/jing/qtpj/diskmanager/mainwindow.py + false + + /home/jing/qtpj/diskmanager/mainwindow.py + true + true + /home/jing/qtpj/diskmanager + :0 + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + system_info.py + PythonEditor.RunConfiguration. + /home/jing/qtpj/diskmanager/system_info.py + false + + /home/jing/qtpj/diskmanager/system_info.py + true + true + /home/jing/qtpj/diskmanager + :0 + + 4 + + + + ProjectExplorer.Project.TargetCount + 1 + + + Version + 22 + + diff --git a/__pycache__/disk_operations.cpython-314.pyc b/__pycache__/disk_operations.cpython-314.pyc index a66175c37eaaacc193ea3410fa1c4e64119ed6c3..3104eaf451d0fc7a3dcfc08d7f0c2b82e0a865dc 100644 GIT binary patch delta 50 zcmaE#@-~G_n~#@^0SF9=H*zgzl+@GD$j?pH&&te8*Dow7$kI>AEY8kN%uC#Sf^oS4 E0D@8wuK)l5 delta 52 zcmaEx@;-%2n~#@^0SF|;HgYXyls3@M$j?pHFV0OYD$y@2Dag`K$t=#!P0UNo+kA|1 Gxd8x`>=7ye diff --git a/__pycache__/logger_config.cpython-314.pyc b/__pycache__/logger_config.cpython-314.pyc index ae642857ea39c58a1683cdeeef0fd40a4c8eca2a..2e4eaafb063768875c2ef3efce435b196b44b00b 100644 GIT binary patch delta 50 zcmdlbu|tAOn~#@^0SF9=H*$$FN$Tillc<3Wa+157H8)s<|S^nX8O+w E06muu!~g&Q delta 52 zcmdlXu}gwWn~#@^0SNwyZsZbUlGfAD$j?pHFV0OYD$y@2Dag`K$t=#!P0UNo+ic17 GpA!IZ&JZI2 diff --git a/__pycache__/system_info.cpython-314.pyc b/__pycache__/system_info.cpython-314.pyc index f0fc55bbba33d9b2a5eeedc68d0d76655203d7d9..b9bfdd085f66d6dc191d0f325bfbe355685fa743 100644 GIT binary patch delta 5767 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 delta 1650 zcma)6Z%h+s7=Q0y|IpF`{dd<=I6#mCYNfy?EGp`(u!3!3>45?07Fj{M5C3O1Kza%EY})K=y6JZB65 zJtzSpQIWlc_3){-xNSSyNePUAAV?A-NRmDJBxMIN)Zze1TGWZ81N0L~Mx;=#5ov^4 zkwK^vHA#IpNal&!3LxlVuWpyC&U-i(jd&ALNlAL&NygvyhULVOs5BrAOkdNL=!oy& z&jovZyuM~x1MK|So-;k^0ybS*k+yMZdwH7U?^@YUi_SaGIWKt@teck2I=gAqzf=U} z!jrUCU%fcJTsTh=t?;sK#=xMZQvetrUjy*3qOAl2uU1>&af{J~Eb?H#2A>jqX+U@# z0L36q?I)pRao5vblo9yIwXG@zZXhyoYAYpb(B@<#nUKLtlpDx|pc{M@?&Cc06z49u zH*xvy+{Ir;PUQKO7tv_=fwLKY=G0j4U7ULG)#okz?!i(--#>fd{`AN2l#>$$D#Jv> zQaC!K^LWCMq$KwbX*{09L1{o>lt$F7;6tsrFw|gJk3gUmEsl8%n=$whBxnRx@N3tW z;oUfW6Tt{r%G2nZ(rocrE%8IbxWTP+iTlWwREQ+q_`h@}s5k&m{&GpCznl`U%fQFlpFULd~(;IO;0L zpzt92PNxr51(^mv>Ud`Ic$7#yrX>4fN>oZJEr=8-r5!z#P7JFA`dASGpVUqg`=&3| zaU@}Y*Ea0b?7+MTi@bYz^p?QWlgQE|l3?jE>iE~>p-2m#(Z-L45-7|N!eBlce($51 zU*|sLtGjn4gchY8eo?>KQ-Jv2M&=%TGl8mnbw@WY;l*aO{YOK1em1~k1PAQ&ts}ai z;@ee;*PYQTk{k}n1IW9Gas|H@r47R?7&_qZKDQx(Li}$_yg@=+L!~`V5bQ&MajB02 zv~hIk2Z~#Sdm6qqrs=$sf%gOB`UTnvw>JsHCj0o{>DXv&luYYPrwwC<@!n60&fCw~ zFX&TTjmp+udSSABrfRBcmP`3|sP!*jX`2^sbYJgQyMn3C96WhdAqC7O zi>m_W1)5L)_weCOliO$fQ~ufJRO3#y!GGn@eD96E>wW6$q15gJYNw=1hgJE=Z8>&R yj;ZmXl>DxG_?UX^J=J>rmTov#QY|Q7pez1|X0D;Se|7X2J9_8=v{Nyl{rwvokGQh{ diff --git a/__pycache__/ui_form.cpython-314.pyc b/__pycache__/ui_form.cpython-314.pyc index eb3f9a84c9a10bc0e85343e0bfc3eeab1f643b20..332f16e4f21713c2a67fcd53aac8dd0b1977e41a 100644 GIT binary patch delta 1413 zcma)6O>AOS6n>ZA3-mptP$(20Z=eC8v=mCG?UbK(>`&}WIs_Xg85~Wxi7_tx*~z{1Z(Mm7-@E7B^L_W+ zb57pZsXHs&2*>J~?)v4^H;Oa%dt6wlc&u-vl=qcjP_c-Id9@2N+7!2LBVlA^nKX5? zrk+hb9zbe8yp1^|z|YzM@^py~xS|OIuZ=+l$KZm_KqZHXBx^py^-=Dw&hX?VG!Qhp z7%b^IeeN2(*hnu=7c%XNwvM4= zo}E38I{mIEj`ukx$7E2>7E|>+XZR?cBKM_ntj`~lvS*Vxcj9Hz7ikifCqx#h z$i<`DtI;wP43p?t_|V`stQ>RGmtovyfgcR4dceyBtnjPh-Bt5mL97;E%$F|bU)i}; z-4OG|&5p4u77AC3n_Gn=hqju3VWYHH?)3jBocOrYXn3_Rf&B1!UuAfvd#!kZ0I7`_ zC*UPO$|Cv+Bq7c|jZ#ox{b&x}V>j%+@b#T?A$+5_y%oM$-MtasELL7B=eP5qL!z8C z$|+%|jDr#mHb)X|oP2mnCC*w*9!W_fsWzT_2p<@u(L>K@ZL?n5FSR_gM8!>2+!A)n zcvQlp&9P_Nc;S!_)Q0P!{ZNaag1ek!h|2b&0ut^X%E5#bq!%{6F@Ay&14-o&j1PJn zb(2O7dW{8sG&z;ZtP;K$n1N;6_taKpRY!ISIcRskwRw>lKC;@7lMEiX2G1i4d}gv} zGKy|f2Vi^fK-obNSh7cu5ni@mbJ9+b;qEno_JlMcpcYr*u|25hoP*!&f-=(k_D3t~ z?y1iw2!eQp&@O??A!s+c+e%K^Sa8fL&hs9bk4k)0<`WX1Xz{5g9y-87GWJT?`|*6+ zKPCI;zw^(36>6r}n%QeD|MP9U4zD{`C^aJS5t)xme7wac|1Z@EkDP1IWk}{viQ^=( zB!LtGI#hFH7bP%3fIgJIdy<%R0%`U;#wHTp~9wjyb^8c$0 zcH!?k%$v*~iUp*e_-j>RQ}3eR8G>S_G0Hp80LhFNtb^m_oy}XNLQ*Wlx_iyMfzU2` Mf9X$#ZbTCJFMHQqpa1{> delta 719 zcmX|8O-vI(7@e=%rQ2@1TiF6>=?_rCwveV23e{5FU{oS17DIY4P*Vd&&{Eh!^ri=u zn0PRG7vn{Z7ms4&LF2`E^x(mS5DuJ7Ja|D5LgGPp*3?Pn<<0lKnfGR9bLyj^3@APi zv&KgI?WOx|TS{7Ryp$S-@LIr3O9}-ki?GYEdBUaPg5<@MPzMG*S&BpT_Ms}}@XnsnvF6mvx`)2&pPo#)+lFh{VGR#=Z-ZcC2IPX*h=Jol@;?= z0$uetyH+YHj`IU3k1cnM3_SOZ&;+`I0ett#@`>i&;z^LwEgp%pWGi&#q0_ZqxdKwh ztyeJAx<76SP3~{%9P-#!6EucDs!j#y{>z<-^tD=bF8rhLa4DHNQ$ThXR$15_$*1-3&wFTw2cShQOt+E z_!L$sfZgy8-b6Cw!?(z#)*O#yXVxP zZI|L}4jtC4b2!{&ci2zIs5pFJk|wUwKI3RkH`d}IYU3g7q`Oj~T3gt-v3T6Pif6sE R+IgZ?dUX01vx&)te*qwwpt%45 diff --git a/form.ui b/form.ui index cc653e4..4080348 100644 --- a/form.ui +++ b/form.ui @@ -100,11 +100,33 @@ RAID 管理 + + + + + + 1 + + + + + LVM 管理 + + + + + + 1 + + + + + @@ -130,7 +152,7 @@ 0 0 1000 - 22 + 23 diff --git a/mainwindow.py b/mainwindow.py index 80d1d32..47610df 100644 --- a/mainwindow.py +++ b/mainwindow.py @@ -1,18 +1,14 @@ # mainwindow.py import sys -import logging # 导入 logging 模块 +import 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 logger_config import setup_logging, logger from disk_operations import DiskOperations class MainWindow(QMainWindow): @@ -21,35 +17,39 @@ class MainWindow(QMainWindow): 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) + self.ui.refreshButton.clicked.connect(self.refresh_all_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_all_info() + logger.info("所有设备信息已初始化加载。") + + def refresh_all_info(self): + """ + 刷新所有设备信息:块设备、RAID和LVM。 + """ + logger.info("开始刷新所有设备信息...") self.refresh_block_devices_info() - logger.info("块设备信息已初始化加载。") + self.refresh_raid_info() + self.refresh_lvm_info() + logger.info("所有设备信息刷新完成。") def refresh_block_devices_info(self): """ 刷新块设备信息并显示在 QTreeWidget 中。 """ - self.ui.treeWidget_block_devices.clear() # 清空现有内容 + self.ui.treeWidget_block_devices.clear() - # 定义所有要显示的列头和对应的 lsblk 字段名 columns = [ ("设备名", 'name'), ("类型", 'type'), @@ -67,12 +67,11 @@ class MainWindow(QMainWindow): ] headers = [col[0] for col in columns] - self.field_keys = [col[1] 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.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) @@ -81,7 +80,6 @@ class MainWindow(QMainWindow): 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("块设备信息刷新成功。") @@ -98,21 +96,164 @@ class MainWindow(QMainWindow): item = QTreeWidgetItem(parent_item) for i, key in enumerate(self.field_keys): value = dev_data.get(key) - if key == 'ro': # 特殊处理布尔值 + if key == 'ro': item.setText(i, "是" if value else "否") - elif value is None: # None 值显示为空字符串 + elif value is 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) # 默认展开父节点,以便看到分区 + item.setExpanded(True) + + def refresh_raid_info(self): + """ + 刷新RAID阵列信息并显示在 QTreeWidget 中。 + """ + self.ui.treeWidget_raid.clear() + + # 定义RAID显示列头 + raid_headers = [ + "阵列设备", "级别", "状态", "大小", "活动设备", "失败设备", "备用设备", + "总设备数", "UUID", "名称", "Chunk Size" + ] + self.ui.treeWidget_raid.setColumnCount(len(raid_headers)) + self.ui.treeWidget_raid.setHeaderLabels(raid_headers) + + for i in range(len(raid_headers)): + self.ui.treeWidget_raid.header().setSectionResizeMode(i, QHeaderView.ResizeToContents) + + try: + raid_arrays = self.system_manager.get_mdadm_arrays() + if not raid_arrays: + item = QTreeWidgetItem(self.ui.treeWidget_raid) + item.setText(0, "未找到RAID阵列。") + logger.info("未找到RAID阵列。") + return + + for array in raid_arrays: + array_item = QTreeWidgetItem(self.ui.treeWidget_raid) + array_item.setText(0, array.get('device', 'N/A')) + array_item.setText(1, array.get('level', 'N/A')) + array_item.setText(2, array.get('state', 'N/A')) + array_item.setText(3, array.get('array_size', 'N/A')) + array_item.setText(4, array.get('active_devices', 'N/A')) + array_item.setText(5, array.get('failed_devices', 'N/A')) + array_item.setText(6, array.get('spare_devices', 'N/A')) + array_item.setText(7, array.get('total_devices', 'N/A')) + array_item.setText(8, array.get('uuid', 'N/A')) + array_item.setText(9, array.get('name', 'N/A')) + array_item.setText(10, array.get('chunk_size', 'N/A')) + array_item.setExpanded(True) + + # 添加成员设备作为子节点 + for member in array.get('member_devices', []): + member_item = QTreeWidgetItem(array_item) + member_item.setText(0, f" {member.get('device_path', 'N/A')}") # 缩进显示 + member_item.setText(1, f"成员") + member_item.setText(2, member.get('state', 'N/A')) + # 其他列留空或填充N/A,因为是成员设备的特有信息 + member_item.setText(3, f"RaidDevice: {member.get('raid_device', 'N/A')}") + + + for i in range(len(raid_headers)): + self.ui.treeWidget_raid.resizeColumnToContents(i) + logger.info("RAID阵列信息刷新成功。") + + except Exception as e: + QMessageBox.critical(self, "错误", f"刷新RAID阵列信息失败: {e}") + logger.error(f"刷新RAID阵列信息失败: {e}") + + def refresh_lvm_info(self): + """ + 刷新LVM信息(PVs, VGs, LVs)并显示在 QTreeWidget 中。 + """ + self.ui.treeWidget_lvm.clear() + + # 定义LVM显示列头 + lvm_headers = ["名称", "大小", "属性", "UUID", "关联", "空闲/已用", "路径"] + self.ui.treeWidget_lvm.setColumnCount(len(lvm_headers)) + self.ui.treeWidget_lvm.setHeaderLabels(lvm_headers) + + for i in range(len(lvm_headers)): + self.ui.treeWidget_lvm.header().setSectionResizeMode(i, QHeaderView.ResizeToContents) + + try: + lvm_data = self.system_manager.get_lvm_info() + + if not lvm_data.get('pvs') and not lvm_data.get('vgs') and not lvm_data.get('lvs'): + item = QTreeWidgetItem(self.ui.treeWidget_lvm) + item.setText(0, "未找到LVM信息。") + logger.info("未找到LVM信息。") + return + + # 物理卷 (PVs) + pv_root_item = QTreeWidgetItem(self.ui.treeWidget_lvm) + pv_root_item.setText(0, "物理卷 (PVs)") + pv_root_item.setExpanded(True) + if lvm_data.get('pvs'): + for pv in lvm_data['pvs']: + pv_item = QTreeWidgetItem(pv_root_item) + pv_item.setText(0, pv.get('pv_name', 'N/A')) + pv_item.setText(1, pv.get('pv_size', 'N/A')) + pv_item.setText(2, pv.get('pv_attr', 'N/A')) + pv_item.setText(3, pv.get('pv_uuid', 'N/A')) + pv_item.setText(4, f"VG: {pv.get('vg_name', 'N/A')}") + pv_item.setText(5, f"空闲: {pv.get('pv_free', 'N/A')}") + pv_item.setText(6, pv.get('pv_fmt', 'N/A')) # PV格式 + else: + item = QTreeWidgetItem(pv_root_item) + item.setText(0, "未找到物理卷。") + + + # 卷组 (VGs) + vg_root_item = QTreeWidgetItem(self.ui.treeWidget_lvm) + vg_root_item.setText(0, "卷组 (VGs)") + vg_root_item.setExpanded(True) + if lvm_data.get('vgs'): + for vg in lvm_data['vgs']: + vg_item = QTreeWidgetItem(vg_root_item) + vg_item.setText(0, vg.get('vg_name', 'N/A')) + vg_item.setText(1, vg.get('vg_size', 'N/A')) + vg_item.setText(2, vg.get('vg_attr', 'N/A')) + vg_item.setText(3, vg.get('vg_uuid', 'N/A')) + vg_item.setText(4, f"PVs: {vg.get('pv_count', 'N/A')}, LVs: {vg.get('lv_count', 'N/A')}") + vg_item.setText(5, f"空闲: {vg.get('vg_free', 'N/A')}, 已分配: {vg.get('vg_alloc_percent', 'N/A')}%") + vg_item.setText(6, vg.get('vg_fmt', 'N/A')) # VG格式 + else: + item = QTreeWidgetItem(vg_root_item) + item.setText(0, "未找到卷组。") + + # 逻辑卷 (LVs) + lv_root_item = QTreeWidgetItem(self.ui.treeWidget_lvm) + lv_root_item.setText(0, "逻辑卷 (LVs)") + lv_root_item.setExpanded(True) + if lvm_data.get('lvs'): + for lv in lvm_data['lvs']: + lv_item = QTreeWidgetItem(lv_root_item) + lv_item.setText(0, lv.get('lv_name', 'N/A')) + lv_item.setText(1, lv.get('lv_size', 'N/A')) + lv_item.setText(2, lv.get('lv_attr', 'N/A')) + lv_item.setText(3, lv.get('lv_uuid', 'N/A')) + lv_item.setText(4, f"VG: {lv.get('vg_name', 'N/A')}, Origin: {lv.get('origin', 'N/A')}") + lv_item.setText(5, f"快照: {lv.get('snap_percent', 'N/A')}%") + lv_item.setText(6, lv.get('lv_path', 'N/A')) + else: + item = QTreeWidgetItem(lv_root_item) + item.setText(0, "未找到逻辑卷。") + + + for i in range(len(lvm_headers)): + self.ui.treeWidget_lvm.resizeColumnToContents(i) + logger.info("LVM信息刷新成功。") + + except Exception as e: + QMessageBox.critical(self, "错误", f"刷新LVM信息失败: {e}") + logger.error(f"刷新LVM信息失败: {e}") def show_block_device_context_menu(self, pos: QPoint): """ @@ -120,7 +261,7 @@ class MainWindow(QMainWindow): """ item = self.ui.treeWidget_block_devices.itemAt(pos) if item: - dev_data = item.data(0, Qt.UserRole) # 获取存储的原始设备数据 + dev_data = item.data(0, Qt.UserRole) if not dev_data: logger.warning(f"无法获取设备 {item.text(0)} 的详细数据。") return @@ -131,22 +272,17 @@ class MainWindow(QMainWindow): menu = QMenu(self) - # 挂载/卸载操作 - # 只有 'part' (分区) 和 'disk' (整个磁盘,但通常只挂载分区) 可以被挂载/卸载 if device_type in ['part', 'disk']: - if not mount_point or mount_point == '' or mount_point == 'N/A': # 未挂载 + 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 + elif mount_point != '[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)) @@ -154,7 +290,7 @@ class MainWindow(QMainWindow): format_action = menu.addAction(f"格式化分区 {device_name}...") format_action.triggered.connect(lambda: self._handle_format_partition(device_name)) - if menu.actions(): # 只有当菜单中有动作时才显示 + if menu.actions(): menu.exec(self.ui.treeWidget_block_devices.mapToGlobal(pos)) else: logger.info(f"设备 {device_name} 没有可用的操作。") @@ -164,22 +300,22 @@ class MainWindow(QMainWindow): def _handle_mount(self, device_name): """处理挂载操作,并刷新UI。""" if self.disk_ops.mount_partition(device_name): - self.refresh_block_devices_info() # 操作成功后刷新UI + self.refresh_all_info() def _handle_unmount(self, device_name): """处理卸载操作,并刷新UI。""" if self.disk_ops.unmount_partition(device_name): - self.refresh_block_devices_info() # 操作成功后刷新UI + self.refresh_all_info() def _handle_delete_partition(self, device_name): """处理删除分区操作,并刷新UI。""" if self.disk_ops.delete_partition(device_name): - self.refresh_block_devices_info() # 操作成功后刷新UI + self.refresh_all_info() def _handle_format_partition(self, device_name): """处理格式化分区操作,并刷新UI。""" if self.disk_ops.format_partition(device_name): - self.refresh_block_devices_info() # 操作成功后刷新UI + self.refresh_all_info() if __name__ == "__main__": diff --git a/system_info.py b/system_info.py index a9bb265..4458373 100644 --- a/system_info.py +++ b/system_info.py @@ -2,9 +2,9 @@ import subprocess import json import os -import logging # 导入 logging 模块 +import logging -logger = logging.getLogger(__name__) # 获取当前模块的 logger 实例 +logger = logging.getLogger(__name__) class SystemInfoManager: def __init__(self): @@ -23,20 +23,17 @@ class SystemInfoManager: full_cmd.append("sudo") full_cmd.extend(cmd) - cmd_str = ' '.join(full_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: @@ -45,13 +42,12 @@ class SystemInfoManager: 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) # 抛出更易于处理的异常 + logger.error(error_msg) + raise ValueError(error_msg) except FileNotFoundError: error_msg = f"命令未找到: {full_cmd[0]}。请确保已安装。" logger.error(error_msg) @@ -72,21 +68,125 @@ class SystemInfoManager: logger.info("成功获取块设备信息。") return data.get('blockdevices', []) except Exception as e: - logger.error(f"获取块设备信息失败: {e}") # 使用 logger 记录错误 + logger.error(f"获取块设备信息失败: {e}") return [] def get_mdadm_arrays(self): """ 获取所有RAID阵列的详细信息。 - 使用 mdadm --detail --scan 命令。 + 首先使用 mdadm --detail --scan 获取阵列列表, + 然后对每个阵列使用 mdadm --detail 获取更详细的信息。 """ + all_raid_details = [] try: - stdout, _ = self._run_command(["mdadm", "--detail", "--scan"], root_privilege=False) + # 1. 获取所有RAID设备的路径 + scan_stdout, _ = self._run_command(["mdadm", "--detail", "--scan"], root_privilege=False) + array_paths = [] + for line in scan_stdout.splitlines(): + if line.startswith("ARRAY"): + parts = line.split() + if len(parts) > 1: + array_paths.append(parts[1]) # 例如 /dev/md126 + + if not array_paths: + logger.info("未找到任何RAID阵列。") + return [] + + # 2. 对每个RAID设备获取详细信息 + for path in array_paths: + try: + detail_stdout, _ = self._run_command(["mdadm", "--detail", path], root_privilege=False) + parsed_detail = self._parse_mdadm_detail_output(detail_stdout) + parsed_detail['device'] = path # 添加设备路径 + all_raid_details.append(parsed_detail) + except Exception as e: + logger.error(f"获取RAID阵列 {path} 的详细信息失败: {e}") logger.info("成功获取RAID阵列信息。") - return stdout + return all_raid_details except Exception as e: - logger.error(f"获取RAID阵列信息失败: {e}") - return "无法获取RAID阵列信息。" + logger.error(f"获取RAID阵列列表失败: {e}") + return [] + + def _parse_mdadm_detail_output(self, output_string): + """ + 解析 mdadm --detail 命令的输出字符串,提取关键信息。 + """ + details = { + 'level': 'N/A', + 'array_size': 'N/A', + 'raid_devices': 'N/A', + 'total_devices': 'N/A', + 'state': 'N/A', + 'active_devices': 'N/A', + 'working_devices': 'N/A', + 'failed_devices': 'N/A', + 'spare_devices': 'N/A', + 'chunk_size': 'N/A', + 'uuid': 'N/A', + 'name': 'N/A', + 'member_devices': [] + } + member_devices_section = False + lines = output_string.splitlines() + + for line in lines: + line = line.strip() + if not line: + continue + + if line.startswith("Number Major Minor RaidDevice State"): + member_devices_section = True + continue + + if member_devices_section: + parts = line.split() + if len(parts) >= 6: # Ensure enough parts for device info + # State can be multiple words, e.g., "active sync" + device_state_parts = parts[4:-1] + # Handle cases where device path might have spaces or be complex, though usually not for /dev/sdX + device_path = parts[-1] + + details['member_devices'].append({ + 'number': parts[0], + 'major': parts[1], + 'minor': parts[2], + 'raid_device': parts[3], + 'state': ' '.join(device_state_parts), + 'device_path': device_path + }) + else: + if ':' in line: + key, value = line.split(':', 1) + key = key.strip().lower().replace(' ', '_') + value = value.strip() + + if key == 'raid_level': + details['level'] = value + elif key == 'array_size': + details['array_size'] = value + elif key == 'raid_devices': + details['raid_devices'] = value + elif key == 'total_devices': + details['total_devices'] = value + elif key == 'state': + details['state'] = value + elif key == 'active_devices': + details['active_devices'] = value + elif key == 'working_devices': + details['working_devices'] = value + elif key == 'failed_devices': + details['failed_devices'] = value + elif key == 'spare_devices': + details['spare_devices'] = value + elif key == 'chunk_size': + details['chunk_size'] = value + elif key == 'uuid': + details['uuid'] = value + elif key == 'name': + details['name'] = value + + + return details def get_lvm_info(self): """ @@ -122,7 +222,6 @@ class SystemInfoManager: # 示例用法 (可以在此模块中添加测试代码) if __name__ == "__main__": - # 为了在单独运行时也能看到日志输出,这里简单配置一下 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') manager = SystemInfoManager() logger.info("--- 块设备信息 ---") @@ -132,16 +231,34 @@ if __name__ == "__main__": logger.info("\n--- RAID 阵列信息 ---") raid_info = manager.get_mdadm_arrays() - logger.info(raid_info) + if raid_info: + for array in raid_info: + logger.info(f" Device: {array.get('device')}, Level: {array.get('level')}, State: {array.get('state')}, Size: {array.get('array_size')}") + logger.info(f" Active: {array.get('active_devices')}, Failed: {array.get('failed_devices')}, Spare: {array.get('spare_devices')}") + for member in array.get('member_devices', []): + logger.info(f" Member: {member.get('device_path')} (State: {member.get('state')})") + else: + logger.info(" 未找到RAID阵列。") 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')})") + if lvm_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')})") + else: + logger.info(" 未找到物理卷。") + 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')})") + if lvm_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')})") + else: + logger.info(" 未找到卷组。") + 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')})") + if lvm_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')})") + else: + logger.info(" 未找到逻辑卷。") diff --git a/ui_form.py b/ui_form.py index dc228af..3efa696 100644 --- a/ui_form.py +++ b/ui_form.py @@ -43,9 +43,23 @@ class Ui_MainWindow(object): self.tabWidget.addTab(self.tab_block_devices, "") self.tab_raid = QWidget() self.tab_raid.setObjectName(u"tab_raid") + self.verticalLayout_raid = QVBoxLayout(self.tab_raid) + self.verticalLayout_raid.setObjectName(u"verticalLayout_raid") + self.treeWidget_raid = QTreeWidget(self.tab_raid) + self.treeWidget_raid.setObjectName(u"treeWidget_raid") + + self.verticalLayout_raid.addWidget(self.treeWidget_raid) + self.tabWidget.addTab(self.tab_raid, "") self.tab_lvm = QWidget() self.tab_lvm.setObjectName(u"tab_lvm") + self.verticalLayout_lvm = QVBoxLayout(self.tab_lvm) + self.verticalLayout_lvm.setObjectName(u"verticalLayout_lvm") + self.treeWidget_lvm = QTreeWidget(self.tab_lvm) + self.treeWidget_lvm.setObjectName(u"treeWidget_lvm") + + self.verticalLayout_lvm.addWidget(self.treeWidget_lvm) + self.tabWidget.addTab(self.tab_lvm, "") self.verticalLayout.addWidget(self.tabWidget) @@ -64,7 +78,7 @@ class Ui_MainWindow(object): MainWindow.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(MainWindow) self.menubar.setObjectName(u"menubar") - self.menubar.setGeometry(QRect(0, 0, 1000, 22)) + self.menubar.setGeometry(QRect(0, 0, 1000, 23)) MainWindow.setMenuBar(self.menubar) self.statusbar = QStatusBar(MainWindow) self.statusbar.setObjectName(u"statusbar") @@ -95,7 +109,11 @@ class Ui_MainWindow(object): ___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)) + ___qtreewidgetitem1 = self.treeWidget_raid.headerItem() + ___qtreewidgetitem1.setText(0, QCoreApplication.translate("MainWindow", u"1", None)); self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_raid), QCoreApplication.translate("MainWindow", u"RAID \u7ba1\u7406", None)) + ___qtreewidgetitem2 = self.treeWidget_lvm.headerItem() + ___qtreewidgetitem2.setText(0, QCoreApplication.translate("MainWindow", u"1", 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 diff --git a/up+.sh b/up+.sh new file mode 100644 index 0000000..1a6d204 --- /dev/null +++ b/up+.sh @@ -0,0 +1,68 @@ +#!/bin/bash +#set -e +################################################################################################################## +# Author : Erik Dubois +# Website : https://www.erikdubois.be +# Website : https://www.alci.online +# Website : https://www.ariser.eu +# Website : https://www.arcolinux.info +# Website : https://www.arcolinux.com +# Website : https://www.arcolinuxd.com +# Website : https://www.arcolinuxb.com +# Website : https://www.arcolinuxiso.com +# Website : https://www.arcolinuxforum.com +################################################################################################################## +# +# DO NOT JUST RUN THIS. EXAMINE AND JUDGE. RUN AT YOUR OWN RISK. +# +################################################################################################################## +#tput setaf 0 = black +#tput setaf 1 = red +#tput setaf 2 = green +#tput setaf 3 = yellow +#tput setaf 4 = dark blue +#tput setaf 5 = purple +#tput setaf 6 = cyan +#tput setaf 7 = gray +#tput setaf 8 = light blue +################################################################################################################## + +# reset - commit your changes or stash them before you merge +# git reset --hard - personal alias - grh + +echo "Deleting the work folder if one exists" +[ -d work ] && rm -rf work + +# checking if I have the latest files from github +echo "Checking for newer files online first" +git pull + +# Below command will backup everything inside the project folder +git add --all . + +# Give a comment to the commit if you want +echo "####################################" +echo "Write your commit comment!" +echo "####################################" + +read input + +# Committing to the local repository with a message containing the time details and commit text + +git commit -m "$input" + +# Push the local files to github + +if grep -q main .git/config; then + echo "Using main" + git push -u origin main +fi + +if grep -q master .git/config; then + echo "Using master" + git push -u origin master +fi + +echo "################################################################" +echo "################### Git Push Done ######################" +echo "################################################################"