來源:https://www.cnblogs.com/donlin-zhang/p/6812675.html
在測試環境搭建的過程中,經常需要給服務器分配靜態IP地址,由於不清楚當前局域網內部哪些IP地址是空閑的,所以經常需要一個一個的去試,才能找到一個可用的IP。在之前的一家公司工作的時候,用到過一個檢測IP使用情況的工具,但是屬於內部工具,無法獲取到。於是乎便想,何不自己開發一個呢?
說做便做,開發環境使用的是Python3.6+PyQt5. 如果你的環境不一樣,可能會運行失敗。
源碼地址:https://github.com/LuckyDL/easyPing--python3.6
1、界面設計
界面用QtDesigner來畫的,先來一張原型圖如下,每次只能測試一個網段的IP占用情況,0-255個小窗格用來顯示IP地址的使用情況,默認為灰色,程序執行后,IP地址已使用的顯示綠色,IP地址未被使用的顯示紅色。
做界面的時候,255個小窗格畫起來實在是要人命,於是就僅畫出了窗口框架,將UI文件轉成Python源碼后,自己手動編寫代碼來實現255窗格布局。代碼片段如下:
self.gridlayout = QtWidgets.QGridLayout(self.widget1)
self.gridlayout.setContentsMargins(0, 0, 0, 0)
self.gridlayout.setObjectName("gridlayout")
self.gridlayout.setSpacing(7)
self.label_list = []
list_index = 0
for i in range(1, 17):
for j in range(1, 17):
label = QtWidgets.QLabel(self.widget1)
label.setMinimumSize(QtCore.QSize(32, 15))
label.setStyleSheet("")
label.setAlignment(QtCore.Qt.AlignCenter)
label.setText(QtCore.QCoreApplication.translate("MyPing", str(list_index)))
self.label_list.append(label)
self.gridlayout.addWidget(label, i-1, j-1, 1, 1)
list_index += 1
2、ping功能實現
ping的實現方法有多種,最常用的當然是通過調用系統自帶的ping功能來實現。
Python來執行系統命令的方式有os.system(), os.popen(), subprocess等方法,在Python3.6的手冊中表明,subprocess模塊是用來替代os.system等函數的。因此我們也使用subprocess模塊來調用ping
python3.5中,subprocess增加了一個run函數,run函數創建有一個子進程來運行需要執行的命令,返回一個CompletedProcess實例,該函數基本上可以處理所有的應用場景,run是對subprocess.Popen的一層封裝。
python3.5以下可以使用subprocess.call(), subprocess.check_call()等函數來實現相同的功能,具體可查閱Python手冊
def get_ping_result(self, ip):
'''
檢查對應的IP是否被占用
'''
cmd_str = "ping {0} -n 1 -w 600".format(ip)
DETACHED_PROCESS = 0x00000008 # 不創建cmd窗口
try:
subprocess.run(cmd_str, creationflags=DETACHED_PROCESS, check=True) # 僅用於windows系統
except subprocess.CalledProcessError as err:
self._ping_signal.emit(False, ip)
else:
self._ping_signal.emit(True, ip)
說明:
在默認情況下,使用subprocess.run()執行ping命令的時候,會彈出一個cmd窗口,當256個線程一起運行的時候,滿屏的窗口,想想都很酸爽。。。
在windows系統下,可以傳入creationflags參數來使subprocess創建的子進程不生成console
check參數為True時,函數將檢測執行結果是否為0(此處0表示執行成功),非零則拋出異常。
3、多線程
在使用pyqt進行GUI編程的時候,如果涉及到需要長時間后台運行的操作,一般需要使用多線程的方式,否則界面會阻塞,直到后台運行結束,造成程序卡死的假象。
在本程序實現,要實現256個地址的ping操作,如果單線程操作,肯定半天也無法得到結果,因此我們為每一個IP地址分配一個線程,最多256線程並發運行,1~2秒的時間即可得到整個網段的IP地址占用情況
多線程實現核心代碼如下:
def start_ping(self):
'''
啟動多線程
'''
self.reset_ui()
startip = self.ui.startIP.text().split('.')
endip = self.ui.endIP.text().split('.')
tmp_ip = startip
pthread_list = []
for i in range(int(startip[3]), int(endip[3]) + 1):
tmp_ip[3] = str(i)
ip = '.'.join(tmp_ip)
pthread_list.append(threading.Thread(target=self.get_ping_result, args=(ip,)))
for item in pthread_list:
item.setDaemon(True)
item.start()
子線程通過發射信號的方式來通知主程序執行結果,最終主程序根據運行結果渲染界面。
全部源碼可到GitHub下載,鏈接見文章頭部。