天天看點

QTdesigner前後端互動--結節算法實戰

目錄

前端界面與背景功能對接

功能1:點選button後,實作界面跳轉(不卡頓)

功能2:選擇檔案路徑按鈕,并将選擇後的路徑顯示在文本框中。

功能3:【上傳】按鈕,将本地的檔案上傳到伺服器中,實作進度條顯示

功能4:在lable區域讀出CT圖像後,用滑鼠事件,添加 繪圖功能,繪制十字坐标線。

功能5. 從伺服器下載下傳檢測結果到本地,seeJJ.py

功能6:實作 菜單欄的各個跳轉功能

功能7:python與excle表格互動:讀取資訊 + python讀寫操作

功能8:從伺服器下載下傳檔案到本地

功能9:從伺服器下載下傳結節檔案,清單讀取本地檔案

功能10 :添加【軟體說明】界面,添加新界面,并将新界面的button功能實作

功能11:生成報告。python将資訊寫入word文檔。

功能12:選中結節和取消選中 的樣式改變,選中後及時儲存選中檔案

功能13:登入界面的跳轉(project-hjq)

功能14:添加新界面,并實作其中的各個功能。

功能15:添加登入界面,賬号密碼登陸成功實作跳轉

功能16:下拉框選中文字,實作在文本框中自動粘貼的功能。

功能17:克服桶排序。顯示前24個結節。

前端界面與背景功能對接

1.前端代碼由QTdesigner生成的ui檔案,經過指令行産生,我們不妨 放在ui檔案夾下,ui\seekJJ.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'SeekJJ.ui'
#
# Created by: PyQt5 UI code generator 5.15.3
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(814, 694)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("../image/icon.jpg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        MainWindow.setWindowIcon(icon)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout_9 = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout_9.setObjectName("horizontalLayout_9")
        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox.setMaximumSize(QtCore.QSize(250, 16777215))
        self.groupBox.setTitle("")
        self.groupBox.setObjectName("groupBox")
        self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.groupBox)
        self.horizontalLayout_8.setObjectName("horizontalLayout_8")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize)
        self.verticalLayout.setObjectName("verticalLayout")
        self.read_ct_label = QtWidgets.QLabel(self.groupBox)
        self.read_ct_label.setMinimumSize(QtCore.QSize(0, 35))
        self.read_ct_label.setMaximumSize(QtCore.QSize(16777215, 40))
        font = QtGui.QFont()
        font.setFamily("黑體")
        font.setPointSize(12)
        self.read_ct_label.setFont(font)
        self.read_ct_label.setStyleSheet("background-color:rgb(190, 217, 238)")
        self.read_ct_label.setAlignment(QtCore.Qt.AlignCenter)
        self.read_ct_label.setObjectName("read_ct_label")
        self.verticalLayout.addWidget(self.read_ct_label)
        self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
        self.ct_path = QtWidgets.QLineEdit(self.groupBox)
        self.ct_path.setMinimumSize(QtCore.QSize(0, 30))
        self.ct_path.setObjectName("ct_path")
        self.horizontalLayout_6.addWidget(self.ct_path)
        self.verticalLayout.addLayout(self.horizontalLayout_6)
        self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_7.setObjectName("horizontalLayout_7")
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_7.addItem(spacerItem)
        self.choose_ct_path = QtWidgets.QPushButton(self.groupBox)
        self.choose_ct_path.setMinimumSize(QtCore.QSize(0, 30))
        self.choose_ct_path.setObjectName("choose_ct_path")
        self.horizontalLayout_7.addWidget(self.choose_ct_path)
        self.upload_ct = QtWidgets.QPushButton(self.groupBox)
        self.upload_ct.setMinimumSize(QtCore.QSize(0, 30))
        self.upload_ct.setObjectName("upload_ct")
        self.horizontalLayout_7.addWidget(self.upload_ct)
        self.verticalLayout.addLayout(self.horizontalLayout_7)
        self.upload_progressBar = QtWidgets.QProgressBar(self.groupBox)
        self.upload_progressBar.setProperty("value", 0)
        self.upload_progressBar.setObjectName("upload_progressBar")
        self.verticalLayout.addWidget(self.upload_progressBar)
        self.now_layers_label = QtWidgets.QLabel(self.groupBox)
        self.now_layers_label.setObjectName("now_layers_label")
        self.verticalLayout.addWidget(self.now_layers_label)
        self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.ajust_layer_label = QtWidgets.QLabel(self.groupBox)
        self.ajust_layer_label.setMinimumSize(QtCore.QSize(0, 30))
        self.ajust_layer_label.setObjectName("ajust_layer_label")
        self.horizontalLayout_5.addWidget(self.ajust_layer_label)
        self.layer_slider = QtWidgets.QSlider(self.groupBox)
        self.layer_slider.setMinimumSize(QtCore.QSize(0, 25))
        self.layer_slider.setOrientation(QtCore.Qt.Horizontal)
        self.layer_slider.setObjectName("layer_slider")
        self.horizontalLayout_5.addWidget(self.layer_slider)
        self.verticalLayout.addLayout(self.horizontalLayout_5)
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.ajust_light_label = QtWidgets.QLabel(self.groupBox)
        self.ajust_light_label.setObjectName("ajust_light_label")
        self.horizontalLayout_4.addWidget(self.ajust_light_label)
        self.light_slider = QtWidgets.QSlider(self.groupBox)
        self.light_slider.setMinimumSize(QtCore.QSize(0, 25))
        self.light_slider.setOrientation(QtCore.Qt.Horizontal)
        self.light_slider.setObjectName("light_slider")
        self.horizontalLayout_4.addWidget(self.light_slider)
        self.verticalLayout.addLayout(self.horizontalLayout_4)
        spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.verticalLayout.addItem(spacerItem1)
        self.ct_pre_deal_label = QtWidgets.QLabel(self.groupBox)
        self.ct_pre_deal_label.setMinimumSize(QtCore.QSize(0, 35))
        self.ct_pre_deal_label.setMaximumSize(QtCore.QSize(16777215, 40))
        font = QtGui.QFont()
        font.setFamily("黑體")
        font.setPointSize(12)
        self.ct_pre_deal_label.setFont(font)
        self.ct_pre_deal_label.setStyleSheet("background-color:rgb(190, 217, 238)")
        self.ct_pre_deal_label.setAlignment(QtCore.Qt.AlignCenter)
        self.ct_pre_deal_label.setObjectName("ct_pre_deal_label")
        self.verticalLayout.addWidget(self.ct_pre_deal_label)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.generate_mhd = QtWidgets.QPushButton(self.groupBox)
        self.generate_mhd.setMinimumSize(QtCore.QSize(0, 30))
        self.generate_mhd.setObjectName("generate_mhd")
        self.horizontalLayout_3.addWidget(self.generate_mhd)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.generate_clean_label = QtWidgets.QPushButton(self.groupBox)
        self.generate_clean_label.setMinimumSize(QtCore.QSize(0, 30))
        self.generate_clean_label.setObjectName("generate_clean_label")
        self.verticalLayout.addWidget(self.generate_clean_label)
        self.generate_lbb_pbb = QtWidgets.QPushButton(self.groupBox)
        self.generate_lbb_pbb.setMinimumSize(QtCore.QSize(0, 30))
        self.generate_lbb_pbb.setObjectName("generate_lbb_pbb")
        self.verticalLayout.addWidget(self.generate_lbb_pbb)
        spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.verticalLayout.addItem(spacerItem2)
        self.vessel_apart_label = QtWidgets.QLabel(self.groupBox)
        self.vessel_apart_label.setMinimumSize(QtCore.QSize(0, 35))
        self.vessel_apart_label.setMaximumSize(QtCore.QSize(16777215, 40))
        font = QtGui.QFont()
        font.setFamily("黑體")
        font.setPointSize(12)
        self.vessel_apart_label.setFont(font)
        self.vessel_apart_label.setStyleSheet("background-color:rgb(190, 217, 238)")
        self.vessel_apart_label.setAlignment(QtCore.Qt.AlignCenter)
        self.vessel_apart_label.setObjectName("vessel_apart_label")
        self.verticalLayout.addWidget(self.vessel_apart_label)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.ct_2_jpg = QtWidgets.QPushButton(self.groupBox)
        self.ct_2_jpg.setMinimumSize(QtCore.QSize(0, 30))
        self.ct_2_jpg.setObjectName("ct_2_jpg")
        self.horizontalLayout_2.addWidget(self.ct_2_jpg)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.vessel_line_mark = QtWidgets.QPushButton(self.groupBox)
        self.vessel_line_mark.setMinimumSize(QtCore.QSize(0, 30))
        self.vessel_line_mark.setObjectName("vessel_line_mark")
        self.verticalLayout.addWidget(self.vessel_line_mark)
        self.line_deal = QtWidgets.QPushButton(self.groupBox)
        self.line_deal.setMinimumSize(QtCore.QSize(0, 30))
        self.line_deal.setObjectName("line_deal")
        self.verticalLayout.addWidget(self.line_deal)
        spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.verticalLayout.addItem(spacerItem3)
        self.generate_jj_zb_label = QtWidgets.QLabel(self.groupBox)
        self.generate_jj_zb_label.setMinimumSize(QtCore.QSize(0, 35))
        self.generate_jj_zb_label.setMaximumSize(QtCore.QSize(16777215, 40))
        font = QtGui.QFont()
        font.setFamily("黑體")
        font.setPointSize(12)
        self.generate_jj_zb_label.setFont(font)
        self.generate_jj_zb_label.setStyleSheet("background-color:rgb(190, 217, 238)")
        self.generate_jj_zb_label.setAlignment(QtCore.Qt.AlignCenter)
        self.generate_jj_zb_label.setObjectName("generate_jj_zb_label")
        self.verticalLayout.addWidget(self.generate_jj_zb_label)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.generate_jj_zb = QtWidgets.QPushButton(self.groupBox)
        self.generate_jj_zb.setMinimumSize(QtCore.QSize(0, 30))
        self.generate_jj_zb.setObjectName("generate_jj_zb")
        self.horizontalLayout.addWidget(self.generate_jj_zb)
        self.see_jjzb = QtWidgets.QPushButton(self.groupBox)
        self.see_jjzb.setMinimumSize(QtCore.QSize(0, 30))
        self.see_jjzb.setObjectName("see_jjzb")
        self.horizontalLayout.addWidget(self.see_jjzb)
        self.verticalLayout.addLayout(self.horizontalLayout)
        spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.verticalLayout.addItem(spacerItem4)
        self.horizontalLayout_8.addLayout(self.verticalLayout)
        self.horizontalLayout_9.addWidget(self.groupBox)
        self.ct_img = PaintArea(self.centralwidget)
        self.ct_img.setMinimumSize(QtCore.QSize(600, 600))
        self.ct_img.setText("")
        self.ct_img.setScaledContents(False)
        self.ct_img.setAlignment(QtCore.Qt.AlignCenter)
        self.ct_img.setObjectName("ct_img")
        self.horizontalLayout_9.addWidget(self.ct_img)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 814, 23))
        self.menubar.setObjectName("menubar")
        self.file_menu = QtWidgets.QMenu(self.menubar)
        self.file_menu.setObjectName("file_menu")
        self.seek_jj_menu = QtWidgets.QMenu(self.menubar)
        self.seek_jj_menu.setObjectName("seek_jj_menu")
        self.classify_jj_menu = QtWidgets.QMenu(self.menubar)
        self.classify_jj_menu.setObjectName("classify_jj_menu")
        self.setting_menu = QtWidgets.QMenu(self.menubar)
        self.setting_menu.setObjectName("setting_menu")
        self.help_menu = QtWidgets.QMenu(self.menubar)
        self.help_menu.setObjectName("help_menu")
        MainWindow.setMenuBar(self.menubar)
        self.exit_action = QtWidgets.QAction(MainWindow)
        self.exit_action.setObjectName("exit_action")
        self.seek_jj_action = QtWidgets.QAction(MainWindow)
        self.seek_jj_action.setObjectName("seek_jj_action")
        self.cut_jj_action = QtWidgets.QAction(MainWindow)
        self.cut_jj_action.setObjectName("cut_jj_action")
        self.classify_jj_action = QtWidgets.QAction(MainWindow)
        self.classify_jj_action.setObjectName("classify_jj_action")
        self.to_index_action = QtWidgets.QAction(MainWindow)
        self.to_index_action.setObjectName("to_index_action")
        self.file_menu.addAction(self.to_index_action)
        self.file_menu.addAction(self.exit_action)
        self.seek_jj_menu.addAction(self.seek_jj_action)
        self.seek_jj_menu.addAction(self.cut_jj_action)
        self.classify_jj_menu.addAction(self.classify_jj_action)
        self.menubar.addAction(self.file_menu.menuAction())
        self.menubar.addAction(self.seek_jj_menu.menuAction())
        self.menubar.addAction(self.classify_jj_menu.menuAction())
        self.menubar.addAction(self.setting_menu.menuAction())
        self.menubar.addAction(self.help_menu.menuAction())

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "肺結節多種病理類型人工智能檢測系統"))
        self.read_ct_label.setText(_translate("MainWindow", "讀取CT檔案"))
        self.choose_ct_path.setText(_translate("MainWindow", "選擇CT路徑"))
        self.upload_ct.setText(_translate("MainWindow", "上傳CT"))
        self.now_layers_label.setText(_translate("MainWindow", "目前層數:0/0"))
        self.ajust_layer_label.setText(_translate("MainWindow", "調整層數:"))
        self.ajust_light_label.setText(_translate("MainWindow", "調整亮度:"))
        self.ct_pre_deal_label.setText(_translate("MainWindow", "影像預處理"))
        self.generate_mhd.setText(_translate("MainWindow", "生成mhd"))
        self.generate_clean_label.setText(_translate("MainWindow", "生成clean和label"))
        self.generate_lbb_pbb.setText(_translate("MainWindow", "生成lbb和pbb"))
        self.vessel_apart_label.setText(_translate("MainWindow", "血管分割"))
        self.ct_2_jpg.setText(_translate("MainWindow", "影像生成圖檔"))
        self.vessel_line_mark.setText(_translate("MainWindow", "血管輪廓标記"))
        self.line_deal.setText(_translate("MainWindow", "輪廓處理"))
        self.generate_jj_zb_label.setText(_translate("MainWindow", "生成結節坐标"))
        self.generate_jj_zb.setText(_translate("MainWindow", "生成結節坐标"))
        self.see_jjzb.setText(_translate("MainWindow", "檢視結節坐标"))
        self.file_menu.setTitle(_translate("MainWindow", "檔案"))
        self.seek_jj_menu.setTitle(_translate("MainWindow", "肺結節檢測"))
        self.classify_jj_menu.setTitle(_translate("MainWindow", "分類診斷"))
        self.setting_menu.setTitle(_translate("MainWindow", "設定"))
        self.help_menu.setTitle(_translate("MainWindow", "幫助"))
        self.exit_action.setText(_translate("MainWindow", "退出"))
        self.exit_action.setShortcut(_translate("MainWindow", "Esc"))
        self.seek_jj_action.setText(_translate("MainWindow", "肺結節檢測"))
        self.cut_jj_action.setText(_translate("MainWindow", "裁剪肺結節"))
        self.classify_jj_action.setText(_translate("MainWindow", "肺結節影像良惡性診斷"))
        self.to_index_action.setText(_translate("MainWindow", "系統首頁"))
from frame.myQLabel import PaintArea
           

2.後端代碼由我們建立,frame\seekJJ.py

導入本界面

from ui.SeekJJ import Ui_MainWindow
           

然後對裡面的button函數的書寫構造。線程類和函數類。

class UploadThread(QThread):
    sigout = pyqtSignal(float)

    def __init__(self):
        super().__init__()
        self.nativePath = ''
        self.uploadPath = ''

    def run(self):
        n = 0
        fileLength = len(os.listdir(self.nativePath)) - 1
        for i in os.listdir(self.nativePath):
            print(self.nativePath+i)
            sftp.put(self.nativePath + i, self.uploadPath + i)
            self.sigout.emit((n/fileLength)*100)
            n += 1
           

記得這段代碼:調用界面的,下面會說到。 def __init__(self, parent=None):函數初始化各個參數用的。

def __init__(self, parent=None):
        super().__init__(parent)  # 調用父類構造函數,建立窗體
        self.ui = Ui_MainWindow()  # 建立UI對象
           
QTdesigner前後端互動--結節算法實戰

标題

 界面中的相關按鍵的函數實作。

在新界面中的

class SeeJJ(QMainWindow):類
           

表示這是個新視窗類  SeekJJ.show()函數就是打開這個界面的

class SeekJJ(QMainWindow):
    def resizeEvent(self, e):
        try:
            img = QPixmap(self.ct_file)
            width = self.ui.ct_img.width()
            height = self.ui.ct_img.height()
            if img is None:
                return
            if width > height:
                small = height
            else:
                small = width
            self.ct_img_small_size = small
            img = img.scaled(small, small, Qt.KeepAspectRatio, Qt.SmoothTransformation)
            self.ui.ct_img.setPixmap(img)
            self.ui.ct_img.ct2mark()
        except Exception as e:
            print(e)

    def __init__(self, parent=None):
        super().__init__(parent)  # 調用父類構造函數,建立窗體
        self.ui = Ui_MainWindow()  # 建立UI對象
        self.ui.setupUi(self)  # 構造UI界面
        self.serverWorkSpace = 'E:/LYC/lungDetection-V1/'
        self.upload_thread = UploadThread()
        self.upload_thread.uploadPath = self.serverWorkSpace + 'detection/test_mask/0006/'
        self.serverPython = 'D:/ProgramData/Anaconda3/envs/lung/python'
        self.ct_path_text = ''
        self.picSize = 0

        # self.ct_img_small_size = 512
        self.ct_file = ''
        width = self.ui.ct_img.width()
        height = self.ui.ct_img.height()
        self.ui.ct_img.slider = self.ui.layer_slider
        if width > height:
            self.ct_img_small_size = height
        else:
            self.ct_img_small_size = width

        self.function()

    def function(self):
        self.ui.choose_ct_path.clicked.connect(lambda: self._choose_ct_path())
        self.ui.upload_ct.clicked.connect(lambda: self._upload_ct())
        self.ui.generate_mhd.clicked.connect(lambda: self._generate_mhd())
        self.ui.generate_clean_label.clicked.connect(lambda: self._generate_clean_lable())
        self.ui.generate_lbb_pbb.clicked.connect(lambda: self._generate_lbb_pbb())
        self.ui.ct_2_jpg.clicked.connect(lambda: self._ct_2_jpg())
        self.ui.vessel_line_mark.clicked.connect(lambda: self._vessel_line_mark())
        self.ui.line_deal.clicked.connect(lambda: self._line_deal())
        self.ui.generate_jj_zb.clicked.connect(lambda: self._generate_jj_zb())
        self.ui.layer_slider.valueChanged.connect(self._layer_slider_changed)
        self.ui.see_jjzb.clicked.connect(lambda: self._to_see_jjzb())
        self.ui.menubar.triggered[QAction].connect(self.processtrigger)

    def _to_see_jjzb(self):
        self.seeJJ = SeeJJ()
        self.close()
        self.seeJJ.show()

    def _layer_slider_changed(self, value):
        if self.ct_path_text == '':
            return
        self.ct_file = "../picture/%d.jpg" % (value+1)
        img = QtGui.QPixmap(self.ct_file)
        img = img.scaled(self.ct_img_small_size, self.ct_img_small_size, Qt.KeepAspectRatio)
        self.ui.ct_img.setPixmap(img)
        self.ui.now_layers_label.setText('目前層數:' + str(value+1) + '/' + str(self.picSize))

    def _choose_ct_path(self):
        try:
            self.ct_path_text = QFileDialog.getExistingDirectory(None, '請選擇CT檔案路徑')
            if self.ct_path_text != "":
                for i in os.listdir('../picture/'):
                    os.remove('../picture/' + i)
                self.picSize = len(os.listdir(self.ct_path_text))
                for i, id in enumerate(os.listdir(self.ct_path_text)):
                    RefDs = sitk.ReadImage(self.ct_path_text + '/' + id)
                    data = sitk.GetArrayFromImage(RefDs)[0]
                    ###
                    data = (np.minimum(np.maximum(data, -1000), 400) + 1000)/5.46875
                    cv2.imwrite('../picture/%s.jpg' % (self.picSize-i), data, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
            else:
                return
            self.ui.ct_path.setText(self.ct_path_text)
            self.upload_thread.nativePath = self.ct_path_text + '/'
            self.ui.layer_slider.setMaximum(self.picSize-1)
            self.ui.layer_slider.setMinimum(0)
            self.ui.now_layers_label.setText('目前層數:1/' + str(self.picSize))
            self.ct_file = "../picture/1.jpg"
            img = QtGui.QPixmap(self.ct_file)
            img = img.scaled(self.ct_img_small_size, self.ct_img_small_size, Qt.KeepAspectRatio)
            self.ui.ct_img.setPixmap(img)
        except Exception as e:
            print(e)

    def _upload_ct(self):
        try:
            if self.ui.ct_path.text() == '':
                QMessageBox().information(self, '提示', '請選擇CT檔案路徑', QMessageBox.Yes)
                # QtWidgets.QMessageBox(QMessageBox.Warning, '提示', '請選擇CT檔案路徑').exec_()
                return

            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace + ';python clearCT.py')
            print(stdout.read())
            print(stderr.read())

            self.upload_thread.start()
            self.upload_thread.sigout.connect(self._change_progress)
        except Exception as e:
            print(e)

    def _change_progress(self, value):
        self.ui.upload_progressBar.setValue(value)
        print(value)
        if value == 100:
            QMessageBox().information(self, '提示', "上傳完成", QMessageBox.Yes)

    def _generate_mhd(self):
        try:
            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace +
                                                     'detection/;' + self.serverPython + ' dcm2mhd.py')
            print(stdout.read())
            print(stderr.read())
            QMessageBox().information(self, '提示', "成功生成mhd檔案", QMessageBox.Yes)
        except Exception as e:
            print(e)

    def _generate_clean_lable(self):
        try:
            print('cd ' + self.serverWorkSpace + 'detection/;' + self.serverPython + ' prepares.py')
            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace +
                                                     'detection/;' + self.serverPython + ' prepares.py')
            print(stdout.read())
            print(stderr.read())

            QMessageBox().information(self, '提示', "成功生成clean和label檔案", QMessageBox.Yes)
        except Exception as e:
            print(e)

    def _generate_lbb_pbb(self):
        try:
            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace +
                                                     'detection/;' + self.serverPython + ' detections.py')
            print(stdout.read())
            print(stderr.read())
            QMessageBox().information(self, '提示', "成功生成lbb和pbb檔案", QMessageBox.Yes)
        except Exception as e:
            print(e)

    def _ct_2_jpg(self):
        try:
            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace +
                                                     'vessel_seg/;' + self.serverPython + ' FrangiFilter2D.py')
            print(stdout.read())
            print(stderr.read())
            QMessageBox().information(self, '提示', "成功生成影像圖檔", QMessageBox.Yes)
        except Exception as e:
            print(e)

    def _vessel_line_mark(self):
        try:
            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace +
                                                     'vessel_seg/;' + self.serverPython + ' meijering.py')
            print(stdout.read())
            print(stderr.read())
            QMessageBox().information(self, '提示', "成功标記血管輪廓", QMessageBox.Yes)
        except Exception as e:
            print(e)

    def _line_deal(self):
        try:
            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace +
                                                     'vessel_seg/;' + self.serverPython + ' vesselMask.py')
            print(stdout.read())
            print(stderr.read())
            QMessageBox().information(self, '提示', "成功處理輪廓", QMessageBox.Yes)
        except Exception as e:
            print(e)

    def _generate_jj_zb(self):
        try:
            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace +
                                                     'merge/;' + self.serverPython + ' Isvessel.py')
            print(stdout.read())
            print(stderr.read())
            QMessageBox().information(self, '提示', "成功生成結節坐标", QMessageBox.Yes)
        except Exception as e:
            print(e)

    def processtrigger(self, action):
        if action.text() == '退出':
            self.close()
        elif action.text() == '系統首頁':
            pass
        elif action.text() == '裁剪結節':
            pass
        elif action.text() == '肺結節影像良惡性診斷':
            pass


# ============窗體測試程式 ================================
if __name__ == "__main__":  # 用于目前窗體測試
    app = QApplication(sys.argv)  # 建立GUI應用程式
    form = SeekJJ()  # 建立窗體
    form.show()
    sys.exit(app.exec_())
           

功能1:點選button後,實作界面跳轉(不卡頓)

函數:

def _to_see_jjzb(self):
# 在class SeekJJ(QMainWindow)下,需要self表示目前類
        self.seeJJ = SeeJJ() # 打開seeJJ界面
        self.close()         # 目前界面關閉
        self.seeJJ.show()    # seeJJ界面開啟
           

點選 【檢視結節坐标界面】,

self.ui.see_jjzb.clicked.connect(lambda: self._to_see_jjzb())
           

ui.SeekJJ.py界面的相關接口:(在做ui時自動生成的)

self.see_jjzb = QtWidgets.QPushButton(self.groupBox)
        self.see_jjzb.setMinimumSize(QtCore.QSize(0, 30))
        self.see_jjzb.setObjectName("see_jjzb")
        self.horizontalLayout.addWidget(self.see_jjzb)
           

 .setText函數裡面的參數設定了button的名字

self.see_jjzb.setText(_translate("MainWindow", "檢視結節坐标"))
           

功能2:選擇檔案路徑按鈕,并将選擇後的路徑顯示在文本框中。

QTdesigner前後端互動--結節算法實戰

前端選擇CT路徑的button的觸發事件函數:

def function(self):
        self.ui.choose_ct_path.clicked.connect(lambda: self._choose_ct_path())
           

 選擇CT路徑的觸發函數

def _choose_ct_path(self):
        try:
            # 彈出選擇檔案夾路徑的标題欄title名字
            self.ct_path_text = QFileDialog.getExistingDirectory(None, '請選擇CT檔案路徑')
            # 如果路徑的文本框是空的,清空picture檔案夾下的所有檔案
            if self.ct_path_text != "":
                for i in os.listdir('../picture/'):
                    os.remove('../picture/' + i)
                # os.listdir()傳回指定路徑下的檔案和檔案夾清單。
                self.picSize = len(os.listdir(self.ct_path_text))
                for i, id in enumerate(os.listdir(self.ct_path_text)):
                    RefDs = sitk.ReadImage(self.ct_path_text + '/' + id)
                    data = sitk.GetArrayFromImage(RefDs)[0]
                # -1000到400,的CT影像轉化為256個灰階值,1400除以256等于5.46875
                    data = (np.minimum(np.maximum(data, -1000), 400) + 1000)/5.46875
# 寫入data,[int(cv2.IMWRITE_JPEG_QUALITY), 100]的意思是将畫質調整為最好的100
                    cv2.imwrite('../picture/%s.jpg' % (self.picSize-i), data, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
            else:
                return

# 将路徑的名字讀入到文本框中
            self.ui.ct_path.setText(self.ct_path_text)
# 設定多線程上傳檔案夾的路徑
            self.upload_thread.nativePath = self.ct_path_text + '/'
# 設定【調整層數】滑動條的最大值和最小值
            self.ui.layer_slider.setMaximum(self.picSize-1)
            self.ui.layer_slider.setMinimum(0)
# 初始化目前層數,1/n
            self.ui.now_layers_label.setText('目前層數:1/' + str(self.picSize))
# 預設将1.jpg作為主界面的顯示
            self.ct_file = "../picture/1.jpg"
            img = QtGui.QPixmap(self.ct_file)
# 顯示圖檔,保持橫縱比
            img = img.scaled(self.ct_img_small_size, self.ct_img_small_size, Qt.KeepAspectRatio)
            self.ui.ct_img.setPixmap(img)
# 列印報錯資訊
        except Exception as e:
            print(e)
           

初始化了主界面的滑動條是1/n,預設将第一幅圖像作為顯示圖像,在滑動進度條的時候,動态改變進度條的層數,動态改變圖檔的顯示。

是以這倆動态變化的功能需要在滑動條函數中寫:

前端滑動條的滑鼠點選觸發事件:

self.ui.layer_slider.valueChanged.connect(self._layer_slider_changed)
           

 滑動條函數:

def _layer_slider_changed(self, value):
# 判斷非空路徑
        if self.ct_path_text == '':
            return
# 設定顯示的圖檔的路徑
        self.ct_file = "../picture/%d.jpg" % (value+1)
# 繼續保持橫縱比不變
        img = QtGui.QPixmap(self.ct_file)
        img = img.scaled(self.ct_img_small_size, self.ct_img_small_size, Qt.KeepAspectRatio)
        self.ui.ct_img.setPixmap(img)
# 動态顯示目前層數
        self.ui.now_layers_label.setText('目前層數:' + str(value+1) + '/' + str(self.picSize))
           

功能3:【上傳】按鈕,将本地的檔案上傳到伺服器中,實作進度條顯示

前端upload_ctbutton元件的滑鼠點選觸發事件:

self.ui.upload_ct.clicked.connect(lambda: self._upload_ct())
           

 上傳函數_upload_ct的具體實作:

def _upload_ct(self):
        try:
# 判斷路徑非空
            if self.ui.ct_path.text() == '':
# 路徑空值時彈出提示框
                QMessageBox().information(self, '提示', '請選擇CT檔案路徑', QMessageBox.Yes)
                return
# stdin, stdout, stderr,輸入資訊,指令行輸出資訊,輸出錯誤資訊,需要引入paramiko的ssh
            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace + ';python clearCT.py')
# 列印指令行産生的輸出和錯誤資訊
            print(stdout.read())
            print(stderr.read())
# upload_thread多線程上傳
            self.upload_thread.start()
# 上傳進度條事件
            self.upload_thread.sigout.connect(self._change_progress)
        except Exception as e:
            print(e)
           

 關于多線程上傳的,在此一并說了:

重寫run函數,線程啟動的時候會直接執行run()方法,我們在此處進行重寫,這樣線程啟動的 時候會執行我們寫的這個run方法。

我們使用 self.upload_thread.start()方法這個來啟用線程,但是線程的啟用會先來找run方法

class UploadThread(QThread):
# 設定多線程類
# 剛才看到的sigout在此設定的,pyqtSignal(float)是信号的傳遞,作業系統中的哲學家就餐問題的一個簡
# 化,防止程序死鎖,以float類型的參數進行傳遞
    sigout = pyqtSignal(float)

# 初始化,設定常參數
    def __init__(self):
# 繼承 __init__ 類中的所有
        super().__init__()
# 在繼承的基礎上添加初始化的路徑參數
        self.nativePath = ''
        self.uploadPath = ''


# 重寫run函數,線程啟動的時候會直接執行run()方法,我們在此處進行重寫,這樣線程啟動的 時候會執行我# 們寫的這個run方法。
# 我們使用 self.upload_thread.start()。這個來啟用線程,但是線程的啟用會先來找run方法
    def run(self):
        n = 0
        fileLength = len(os.listdir(self.nativePath)) - 1

        for i in os.listdir(self.nativePath):
            print(self.nativePath+i)
# 将本地檔案上傳到伺服器nativePath + i是本地路徑+i檔案的意思
            sftp.put(self.nativePath + i, self.uploadPath + i)
# 發送信号
            self.sigout.emit((n/fileLength)*100)
            n += 1
           
self.sigout.emit((n/fileLength)*100),上傳到100%
           

前端界面的上傳進度條的進度百分比的顯示功能函數的書寫:

def _change_progress(self, value):
# 進度條傳入的value值
        self.ui.upload_progressBar.setValue(value)
        print(value)
        if value == 100:
# 彈出框顯示上傳完成
            QMessageBox().information(self, '提示', "上傳完成", QMessageBox.Yes)
           
QTdesigner前後端互動--結節算法實戰

功能4:在lable區域讀出CT圖像後,用滑鼠事件,添加 繪圖功能,繪制十字坐标線。

from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import QRect, QPoint, Qt
from PyQt5.QtGui import QPen, QBrush, QPainter, QPixmap
from PyQt5.QtWidgets import QLabel
# 導入依賴包

# 定義繪畫類,這個地方注意一下,因為是(QLabel)參數,是以繪畫的都是QLabel區域,坐标也是這個裡面# 的區域
class PaintArea(QLabel):
    def __init__(self, parent=None):
        super().__init__(parent)
# 初始化各個參數
# 設定對其方式,居中對齊 AlignCenter
        self.setAlignment(QtCore.Qt.AlignCenter)
        self.markX = 0  # 在圖像中的标記
        self.markY = 0
        self.ctX = 256  # 對應CT影像中的真實坐标
        self.ctY = 256
        self.mark = False
        self.slider = None


# 重寫繪畫事件
    def paintEvent(self, event):
# 繼承繪畫類
        super().paintEvent(event)
# 因為self.mark = False,是以初始狀态不執行繪畫事件,滑鼠點選後将mark改為true
        if self.mark:
            painter = QPainter(self)
# setPen(self,color)方法,(0, 0, 255)設定為藍色,1為線段的粗細程度為1
            painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 255), 1))
# .drawLine(self,l)方法畫線,這都是建立在self.mark = True的基礎之上的
            painter.drawLine(self.markX, 0, self.markX, self.height())
            painter.drawLine(0, self.markY, self.width(), self.markY)

    def wheelEvent(self, e):
        if e.angleDelta().y() < 0:
            # 放大圖檔
            if self.slider is not None:
                self.slider.setValue(self.slider.value()+1)
        elif e.angleDelta().y() > 0:
            # 縮小圖檔
            if self.slider is not None:
                self.slider.setValue(self.slider.value()-1)

# 重寫滑鼠點選事件
    def mousePressEvent(self, event):
# 擷取滑鼠點選的橫縱坐标
        self.markX = event.x()
        self.markY = event.y()
        self.mark2ct()
        self.mark = True
# repaint方法重畫界面,算是重新整理
        self.repaint()

# biaoji zuobiao 
    def mark2ct(self):
        if self.width() > self.height():
# 點選滑鼠事件,标記坐标
# 這個地方需要注意,CT是512*512的,是以初始化ctx應該是(256,256)位中間點的坐标
# ctX是指圖檔的ctx的坐标,和lable沒關系
# ctx指的是坐标在長>高的時候,x的相對位置,{x-[(b-a)/2]}/a*512
            self.ctX = (self.markX - (self.width() - self.height())/2)/self.height()*512
            self.ctY = self.markY/self.height()*512
        else:
            self.ctX = self.markX/self.width()*512
            self.ctY = (self.markY-(self.height()-self.width())/2)/self.width()*512
# 如果已經有坐标,拖動視窗的大小,滑鼠坐标自适應改變,不會錯位
    def ct2mark(self):
        if self.width() > self.height():
            self.markX = (self.width()-self.height())/2+(self.ctX/512)*self.height()
            self.markY = (self.ctY/512)*self.height()
        else:
            self.markX = (self.ctX/512)*self.width()
            self.markY = (self.height()-self.width())/2+(self.ctY/512)*self.width()
# 自适應改完重新畫坐标點
        self.repaint()

    def setCtXY(self, x, y):
        self.ctX = x
        self.ctY = y
        self.mark = True
        self.ct2mark()
           

功能5. 從伺服器下載下傳檢測結果到本地,seeJJ.py

QTdesigner前後端互動--結節算法實戰

 首先介紹界面以及初始化:

class SeeJJ(QMainWindow):
    def resizeEvent(self, e):
        try:
            img = QPixmap(self.ct_file)
            width = self.ui.ct_img.width()
            height = self.ui.ct_img.height()
            if img is None:
                return
            if width > height:
                small = height
            else:
                small = width
            self.ct_img_small_size = small

            img = img.scaled(small, small, Qt.KeepAspectRatio, Qt.SmoothTransformation)
            self.ui.ct_img.setPixmap(QPixmap(''))
            self.ui.ct_img.setPixmap(img)
            self.ui.ct_img.ct2mark()
        except Exception as e:
            print(e)


# 初始化參數
    def __init__(self, parent=None):
        super().__init__(parent)  # 調用父類構造函數,建立窗體
# Ui_MainWindow()為SeeJJ.py中的類名
        self.ui = Ui_MainWindow()  # 建立UI對象
        self.ui.setupUi(self)  # 構造UI界面
        self.serverWorkSpace_jiance = 'E:/LYC/lungDetection-V1/'
        self.serverWorkSpace_fenlei = 'E:/LYC/fenlei/'
        self.serverPython = 'D:/ProgramData/Anaconda3/envs/G_capsenv/python'
        self.zb_list = []
        self.ui.zb_listWidget.addItem('序号          x             y             z')
# 在伺服器中的檔案儲存在'../picture/'路徑下,是以要讀取本路徑下的檔案的規模
        self.picSize = len(os.listdir('../picture/'))
# 滑動塊的最大值
        self.ui.layer_ajust_slider.setMaximum(self.picSize-1)
        self.ui.layer_ajust_slider.setMinimum(0)
# 初始化滑塊的值1/n
        self.ui.now_layer_label.setText('目前層數:1/' + str(self.picSize))

        # self.ct_img_small_size = 512  # ct影像尺寸标準,以寬高小者為準
# 寬度 = ct_img的寬度
        width = self.ui.ct_img.width()
        height = self.ui.ct_img.height()
# 滑塊?将ui裡面的滑塊指派給這個類裡面的滑塊
        self.ui.ct_img.slider = self.ui.layer_ajust_slider
        if width > height:
            self.ct_img_small_size = height
        else:
            self.ct_img_small_size = width
# 記錄圖檔層數,初始化界面顯示為第一層
        self.ct_file = '../picture/1.jpg'  # 記錄目前ct層數的圖檔檔案
        img = QPixmap(self.ct_file)
# img.scaled(self,width,height,aspectRatioMode,TransformMode)方法
# 最小值作為邊長的正方形,保持橫縱比,光滑變換
        img = img.scaled(self.ct_img_small_size, self.ct_img_small_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        self.ui.ct_img.setPixmap(img)
# 執行lambda函數集合
        self.function()
           

從伺服器下載下傳檔案到本地:

# 定義元件的接口函數
    def function(self):
        self.ui.down_load.clicked.connect(lambda: self._down_load_zb())

# 對接口函數 _down_load_zb() 的實作

    def _down_load_zb(self):
        try:

# 從伺服器将2020NewData.xlsx檔案下載下傳到本地的data檔案夾下
            sftp.get(self.serverWorkSpace_jiance + '2020NewData.xlsx', '../data/new data.xlsx')
# 完事以後,彈出成功界面
            QMessageBox().information(self, '提示', "成功下載下傳到本地", QMessageBox.Yes)
        except Exception as e:
            print(e)
           

關于 sftp 方法,用的是 paramiko 函數庫進行實作,關于這個伺服器功能單獨寫一個connect.py檔案實作與伺服器的連接配接:

import paramiko

transport = paramiko.Transport(('伺服器ip', 端口号))
# 建立連接配接
transport.connect(username='Fizz', password='密碼')
# 将sshclient的對象的transport指定為以上的transport
ssh = paramiko.SSHClient()
ssh._transport = transport
# sftp在此處指派
sftp = paramiko.SFTPClient.from_transport(transport)
           

用這麼一句代碼 :sftp.get(self.serverWorkSpace_jiance + '2020NewData.xlsx', '../data/new data.xlsx')

即可以實作檔案從伺服器的下載下傳。

功能6:實作 菜單欄的各個跳轉功能

def processtrigger(self, action):
        if action.text() == '退出':
            self.close()
        elif action.text() == '系統首頁':
# Index.py檔案中的Index()類指派給本類的self.index
            self.index = Index.Index()
            self.index.show()
            self.close()
        elif action.text() == '裁剪肺結節':
            self.seeJJ = SeeJJ.SeeJJ()
            self.seeJJ.show()
            self.close()
        elif action.text() == '肺結節影像良惡性診斷':
# from frame import ClassifyJJ
            self.classifyJJ = ClassifyJJ.ClassifyJJ()
            self.classifyJJ.show()
            self.close()
           

功能7:python與excle表格互動:讀取資訊 + python讀寫操作

QTdesigner前後端互動--結節算法實戰

 滑鼠點選button事件:

self.ui.read_zb.clicked.connect(lambda: self._read_zb())
           

 左側坐标顯示欄的函數實作:

def _read_zb(self):
# 初始化清空清單
        self.ui.zb_listWidget.clear()
        self.zb_list.clear()
        self.ui.zb_listWidget.addItem('序号          x             y             z')
# from openpyxl import load_workbook;加載excle的方法,讀入
        wb = load_workbook('../data/new data.xlsx')
        sheet = wb.active
        i = 2
        while True:
# 讀入M列的值,空值則停
            content = sheet["M%d" % i].value
            if content is None or content == "":
                break
# 從左側的第一個元素,到倒數第一個元素。
            content = content[1:-1]
# 逗号分隔開,這樣就把坐标中的三個數取出來了
            index = content.split(",")
            x, y, z = index[0], index[1], index[2]
            self.zb_list.append((index[0], index[1], index[2]))
            self.ui.zb_listWidget.addItem(' ' + str(i-1) + '          ' + x + '          ' + y + '          ' + z)
            i += 1
           

 Classfy.py檔案。分類檢測

QTdesigner前後端互動--結節算法實戰

本處附帶伺服器代碼,關于python寫入Excel的方法:

from openpyxl import Workbook
import cv2


def write_zb_excel(indexList,jjdx):
    wb = Workbook()
    sheet = wb.active
    sheet.title='Sheet1'

    sheet['A1'].value = '影像号'
    sheet['M1'].value = '坐标'
    sheet['N1'].value = '結節大小'
    sheet['O1'].value = '病理類型'
    sheet['AF1'].value = '年份'
    sheet["A2"].value = '10203040'
    img = cv2.imread('E:/LYC/lungDetection-V1/detection/savejpg2/0006/0.jpg')
    w, h = img.shape[0], img.shape[1]

    f = open(r'..\detection\extendbox.txt', 'r')
    extendbox = f.readlines()
    startX = int(extendbox[0])
    startY = int(extendbox[2])
    startZ = int(extendbox[4])
    f.close()

    f = open(r'..\detection\spacing.txt', 'r')
    space = f.readline()
    space = space.split(',')
    spaceZ = float(space[0])
    spaceXY = float(space[1])
    f.close()

    for i, index in enumerate(indexList):
        x, y, z = index[0], index[1], index[2]
        x = round((x + startX) / spaceXY)
        y = round((y + startY) / spaceXY)
        z = round((z + startZ + 1) / spaceZ)

        content = '(' + str(x) + ',' + str(y) + ',' + str(z) + ')'
        sheet["M%d" % (i + 2)].value = content
        sheet["O%d" % (i + 2)].value = 2
        sheet["AF%d" % (i + 2)].value = 2020

    for i,jj in enumerate(jjdx):
        sheet["N%d" % (i + 2)].value = jj

    wb.save('../2020NewData.xlsx')


           

如下,jjdx(結節大小)為一清單[1,2,3,1,1]之類似,enumerate函數的作用是一個數一個數的去拿;第二行是存在N列的每一行中。 

for i,jj in enumerate(jjdx):
        sheet["N%d" % (i + 2)].value = jj
           

功能8:從伺服器下載下傳檔案到本地

self.ui.download_jj_file.clicked.connect(lambda: self._download_jj_file())
           

函數實作:

def _download_jj_file(self):
        try:
            npyList = os.listdir('../npy')
            for npy in npyList:
                os.remove('../npy/' + npy)
            fileArray = sftp.listdir(self.serverWorkSpace + 'prework/dataset17-20_cf/test30')


            for file in fileArray:
                sftp.get(self.serverWorkSpace + 'prework/dataset17-20_cf/test30/' + file, '../npy/' + file)

            QMessageBox().information(self, '提示', "成功下載下傳到本地", QMessageBox.Yes)
        except Exception as e:
            print(e)
           

功能9:從伺服器下載下傳結節檔案,清單讀取本地檔案

點選【讀取結節檔案】按鈕,實作清單框中讀取。

QTdesigner前後端互動--結節算法實戰
QTdesigner前後端互動--結節算法實戰
QTdesigner前後端互動--結節算法實戰
QTdesigner前後端互動--結節算法實戰
def _read_jj(self):
# 清空清單
        self.ui.jj_listWidget.clear()
        jjList = os.listdir('../npy/')
# 加載檔案
        for jj in jjList:
            self.ui.jj_listWidget.addItem(jj)
           

 滑鼠點選事件,實作界面的動态變化。

QTdesigner前後端互動--結節算法實戰
def _jj_listWidget_clicked(self, item):
        try:
            if item is None:
                return
            file = item.text()

# 本段顯示結節圖像
            path = '../npy/' + file
            image_data = np.load(path)
# //取整,/浮點數
            image_data = image_data[image_data.shape[0] // 2]
            ###
            image_data = (np.minimum(np.maximum(image_data, -1000), 400) + 1000)/5.46875

            img_pil = Image.fromarray(np.uint8(image_data)).resize((150, 150))
            self.ui.jj_img.setPixmap(img_pil.toqpixmap())

            # 根據檔案字尾數字确定序号,擷取xlsx檔案中的坐标
# 正規表達式找 10203040-1.npy之類的檔案,findall
            id = re.findall(r'.*?-(.*?).npy', file)[0]
            index = self.zbList[int(id)]
            index = index[1:-1]
            print(index)
            index = index.split(',')

            # 在結節部分顯示坐标
            self.ui.jj_zb_label.setText('坐标: X:%s  Y:%s  Z:%s' % (index[0], index[1], index[2]))

            # 在CT中标記結節
            self.ui.ct_img.setCtXY(int(index[0]), int(index[1]))

            # ct切換到相應頁面
            self.ui.layer_ajust_slider.setValue(int(index[2]))
            self.ct_file = '../picture/' + index[2] + '.jpg'
            img = QPixmap(self.ct_file)
            img = img.scaled(self.ct_img_small_size, self.ct_img_small_size, Qt.KeepAspectRatio,Qt.SmoothTransformation)
            self.ui.ct_img.setPixmap(img)

            if len(self.resList) == 0:
                return
            result = list(map(float, self.resList[int(id)]))
            result = np.exp(result)/np.sum(np.exp(result), axis=0)
# 顯示百分比
            self.ui.result_lx_progressBar.setValue(float(result[0])*100)
            self.ui.result_la_progressBar.setValue(float(result[1])*100)
            self.ui.result_xa_progressBar.setValue(float(result[2])*100)
        except Exception as e:
            print(e)
           

功能10 :添加【軟體說明】界面,添加新界面,并将新界面的button功能實作

1.在UI界面【幫助】下添加【軟體使用說明】的QAction,添加方法為,先點選type here,輸入wohenshuai

QTdesigner前後端互動--結節算法實戰
QTdesigner前後端互動--結節算法實戰

右側的QAction屬性欄如下:

QTdesigner前後端互動--結節算法實戰
QTdesigner前後端互動--結節算法實戰
QTdesigner前後端互動--結節算法實戰

調整frame格式:

選中要編輯的frame子產品,右鍵stylesheet,複制裡面的格式代碼,到新的界面粘貼

QTdesigner前後端互動--結節算法實戰

參考裡面的代碼,可寫更多樣式。

我在【幫助】下添加的是【軟體使用說明】,QAction是rjsm

def processtrigger(self, action):
        if action.text() == '退出':
            self.close()
        elif action.text() == '系統首頁':
            self.index = Index.Index()
            self.index.show()
            self.close()
        elif action.text() == '肺結節檢測':
            self.seekJJ = SeekJJ.SeekJJ()
            self.seekJJ.show()
            self.close()
        elif action.text() == '肺結節影像良惡性診斷':
            self.classifyJJ = ClassifyJJ.ClassifyJJ()
            self.classifyJJ.show()
            self.close()
        elif action.text() == '軟體使用說明':
            self.rjsm = gnsm.Gnsm()
            self.rjsm.show()
           

 彈出界面不關閉

QTdesigner前後端互動--結節算法實戰

然後

from frame import gnsm
           

即可。self.rjsm為新命名而已,不指向其他東西;gnsm.Gnsm()指的是import的gnsm界面下的Gnsm類

添加新界面之後,如果界面中有函數,要初始化函數,并且執行之。這點十分重要,我将在下面舉例說明!!!!!!!!

QTdesigner前後端互動--結節算法實戰

添加完一個軟體使用說明以後,在其他界面進行添加就比較容易了。

第一步:修改ui界面,在ui界面添加【幫助】-【軟體使用說明】

第二步:添加elif功能

QTdesigner前後端互動--結節算法實戰

第三步:

from frame import gnsm
           

功能11:生成報告。python将資訊寫入word文檔。

關于word的操作,大概可以如下:

QTdesigner前後端互動--結節算法實戰
def _submit(self):
# 判斷資訊完整性
        if self.ui.patient_name.text() == ''\
                or self.ui.patient_id.text() == ''\
                or self.ui.ct_describe_textEdit.toPlainText() == ''\
                or self.ui.doctor_advice_textEdit.toPlainText() == '':
# 彈出未完善的提示資訊
            QMessageBox().information(self, '提示', '請完善資訊', QMessageBox.Ok)
            return
# 選擇儲存路徑的選擇框
        save_path_text = QFileDialog.getExistingDirectory(None, '請選擇報告儲存路徑')
# 判斷儲存路徑非空
        if save_path_text == '':
            QMessageBox().information(self, '提示', '未選擇儲存路徑', QMessageBox.Ok)
            return
# 讀入word模闆
        doc = docx.Document('../word/model.docx')  # 讀取模闆
# 寫入資訊
        doc.paragraphs[1].text = '病曆号:%s             姓名:%s                 檢查時間:%s' % \
                                 (self.ui.patient_id.text(), self.ui.patient_name.text(), self.ui.diagnose_date.text())

# 第三段添加圖檔,選中的肺結節圖檔
        run = doc.paragraphs[3].add_run()
        for file in os.listdir('../npy_img/'):
            run.add_picture('../npy_img/' + file)
            run = doc.paragraphs[3].add_run('  ')
# 将文檔中的文字資訊寫入word的表格中
        doc.tables[0].rows[0].cells[0].text = self.ui.ct_describe_textEdit.toPlainText()
        doc.tables[1].rows[0].cells[0].text = self.ui.doctor_advice_textEdit.toPlainText()
# 将寫好的文檔儲存在本地的絕對路徑中,命名使用患者id命名
        doc.save(save_path_text + '/' + self.ui.patient_id.text() + '.docx')  # 要寫絕對路徑
        QMessageBox().information(self, '提示', '成功生成報告', QMessageBox.Ok)
           

功能12:選中結節和取消選中 的樣式改變,選中後及時儲存選中檔案

QTdesigner前後端互動--結節算法實戰

 點選左側的npy檔案,點選【寫入檢測報告】,選中結節,将結節圖檔檔案儲存起來;

點選選中的結節,點選【移除檢測報告】,取消選中,并将儲存的結節檔案删除掉。

QTdesigner前後端互動--結節算法實戰

具體的

self.ui.put_jj_in_report.clicked.connect(lambda: self._put_jj_in_report())
           

的實作如下:

def _put_jj_in_report(self):
# 我們規定選的結節個數不超過6個
        try:
            if self.put_npy_num >= 6:
                QMessageBox().information(self, '提示', '寫入結節數不能超過6個', QMessageBox.Ok)
                return
# 選中結節後
            row = self.ui.jj_listWidget.currentRow()
# 設定選中時的顔色,設定選中時的icon圖示
            self.ui.jj_listWidget.item(row).setBackground(QColor(190, 217, 238))
            self.ui.jj_listWidget.item(row).setIcon(QIcon('../image/t7.ico'))
# 并将本結節檔案儲存起來,生成報告的時候直接使用,用npy檔案的名字命名jpg
            self.ui.jj_img.pixmap().save('../npy_img/%s.jpg' % self.ui.jj_listWidget.item(row).text())
            self.put_npy_num += 1
        except Exception as e:
            print(e)
           

移除結節檔案的函數實作:

def _remove_jj_from_report(self):
        try:
# 選中本行
            row = self.ui.jj_listWidget.currentRow()
# 設定顔色,圖示設定為空值
            self.ui.jj_listWidget.item(row).setBackground(QColor(255, 255, 255))
            self.ui.jj_listWidget.item(row).setIcon(QIcon(''))
# 删除儲存的檔案
            os.remove('../npy_img/%s.jpg' % self.ui.jj_listWidget.item(row).text())
            self.put_npy_num -= 1
        except Exception as e:
            print(e)
           

這倆功能,将結果顯示出來,讀取坐标值。不好解釋,不做解釋。

def _jj_listWidget_clicked(self, item):
        try:
            if item is None:
                return
            file = item.text()

            # 顯示結節圖像
            path = '../npy/' + file
            image_data = np.load(path)
            image_data = image_data[image_data.shape[0] // 2]
            ###
            image_data = (np.minimum(np.maximum(image_data, -1000), 400) + 1000)/5.46875

            img_pil = Image.fromarray(np.uint8(image_data)).resize((150, 150))
            self.ui.jj_img.setPixmap(img_pil.toqpixmap())

            # 根據檔案字尾數字确定序号,擷取xlsx檔案中的坐标
            id = re.findall(r'.*?-(.*?).npy', file)[0]
            index = self.zbList[int(id)]
            index = index[1:-1]
            print(index)
            index = index.split(',')

            # 在結節部分顯示坐标
            self.ui.zb_info_label.setText('坐标: X:%s  Y:%s  Z:%s' % (index[0], index[1], index[2]))

            if len(self.resList) == 0:
                return
            result = list(map(float, self.resList[int(id)]))
            result = np.exp(result)/np.sum(np.exp(result), axis=0)
            self.ui.res_lx_progressBar.setValue(float(result[0])*100)
            self.ui.res_la_progressBar.setValue(float(result[1])*100)
            self.ui.res_xa_progressBar.setValue(float(result[2])*100)
        except Exception as e:
            print(e)

    def _read_jj(self):
        # 讀取已經下載下傳的坐标值
        self.zbList.clear()
        wb = load_workbook('../data/new data.xlsx')
        sheet = wb.active
        i = 2
        while True:
            content = sheet["M%d" % i].value
            if content is None or content == "":
                break
            self.zbList.append(content)
            i += 1

        # 讀取已經下載下傳的識别結果
        self.resList.clear()
        resNum = len(os.listdir('../result'))
        for i in range(resNum):
            f = open('../result/tes_prob_batch' + str(i) + '.txt')
            batches = f.readlines()
            for batch in batches:
                batch = batch[0:-2]  # 去除換行符
                number = batch.split(' ')
                self.resList.append((number[1][0:8], number[2][0:8], number[3][0:8]))
            f.close()

        # 将結節檔案顯示在清單
        self.ui.jj_listWidget.clear()
        jjList = os.listdir('../npy/')
        for jj in jjList:
            self.ui.jj_listWidget.addItem(jj)
           

功能13:登入界面的跳轉(project-hjq)

def yanzheng(self):
        self.n=0
# 賬号密碼存放在同一級目錄的11.txt下
        f=open('./11.txt')
        ma=self.username.text()+'.'+self.password.text()
        for i in f:
# 判定登陸成功
            if i[:-1]==ma:
                QMessageBox.information(self, " ", "登入成功!", QMessageBox.Yes)
# 登陸成功後打開新界面 main
                import main
                self.hide()
        # self.form.hide()  # 如果沒有self.form.show()這一句,關閉Demo1界面後就會關閉程式
# main界面打開
                self.ex = main.Example()
                self.ex.show()
                self.n=1
        if self.n==0:
# 這裡将三種報錯資訊放在了一起,報錯資訊彈框是一樣的,不一樣的地方是圖示不一樣。
            QMessageBox.information(self, " ", "登入成功!", QMessageBox.Yes)
            QMessageBox.critical(self, " ", "密碼錯誤!", QMessageBox.Yes)
            QMessageBox.warning(self, " ", "查無此人!", QMessageBox.Yes)

# 記得關閉txt
        f.close()
           

功能14:添加新界面,并實作其中的各個功能。

第一步:制作ui界面,并且将ui界面的各個button記錄好,然後用 pyuic5 -o 指令轉化為py程式。這一步不在贅述

第二步:在背景程式的檔案夾(frame)中建立實作本界面以及功能的py檔案,

提示:ui檔案和第一步生成的界面py檔案在ui檔案夾下;我要實作的背景程式的py檔案在frame檔案夾下,是以,我在frame建立的程式yjjc.py引入ui檔案即可。

from ui.Yjjc import Ui_MainWindow
           

然後實作主方法,顯示本界面:

if __name__ == "__main__":  # 用于目前窗體測試
    app = QApplication(sys.argv)  # 建立GUI應用程式
    form = Yjjc()  # 建立窗體
    form.show()
    sys.exit(app.exec_())
           

這段代碼為避免将來忘記,在此解釋一下,隻需要改 form = Yjjc() 這一句即可,就是聲明一個新界面為form,這個Yjjc就是本主界面的類:

QTdesigner前後端互動--結節算法實戰

這樣ui界面的内容就可以顯示了

第三步:實作界面中的按鍵的功能

首先寫

def __init__(self, parent=None):
           

這個函數,繼承父類的窗體,固定代碼:

def __init__(self, parent=None):
        super().__init__(parent)  # 調用父類構造函數,建立窗體
        self.ui = Ui_MainWindow()  # 建立UI對象
        self.ui.setupUi(self)  # 構造UI界面
           

這說明這個界面已經繼承了父類的窗體。

繼承完窗體,然後找ui界面中的一鍵檢測的按鈕的名字。我命名為yjjc

于是,實作這個功能

def function(self):
        self.ui.yjjc.clicked.connect(lambda: self._Yjjcsb())
           

建立一個function類,聲明各個元件的對應的函數,在這裡一定要注意注意再注意,聲明的function函數類必須要初始化!!!

也就是在def __init__ 中添加這麼一句代碼:

self.function()
           

這樣才可以正常執行,否則不可以!如圖:

QTdesigner前後端互動--結節算法實戰

将用到的初始化的 東西全都寫在__init__函數中

第四步:寫具體的實作函數,就是function中lambda的函數

def _Yjjcsb(self):
        try:
            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace +
                                                     'detection/;' + self.serverPython + ' dcm2mhd.py')
            print(stdout.read())
            print(stderr.read())
            QMessageBox().information(self, '提示', "成功生成mhd檔案", QMessageBox.Yes)
        except Exception as e:
            print(e)
           

這樣就可以了。

以上四步實作的是按鈕的功能的具體實作,下面寫的是選擇檔案路徑,并将檔案上傳的路基順序:

第一步:實作選擇檔案路徑按鈕的函數

self.ui.choose_ct_path.clicked.connect(lambda: self._choose_ct_path())
           

第二步:實作           lambda: self._choose_ct_path() 函數

def _choose_ct_path(self):
        try:
            self.ct_path_text = QFileDialog.getExistingDirectory(None, '請選擇CT檔案路徑')
            if self.ct_path_text != "":
                for i in os.listdir('../picture/'):
                    os.remove('../picture/' + i)
                self.picSize = len(os.listdir(self.ct_path_text))
                for i, id in enumerate(os.listdir(self.ct_path_text)):
                    data = pydicom.dcmread(self.ct_path_text + '/' + id).pixel_array
                    data = (np.minimum(np.maximum(data, 0), 4096)) / 6
                    ###
                    plt.imsave('../picture/%s.jpg' % (self.picSize-i),
                               data, cmap='gray', vmin=0, vmax=255)
            else:
                return
            self.ui.ct_path.setText(self.ct_path_text)
            self.upload_thread.nativePath = self.ct_path_text + '/'
            self.ui.layer_slider.setMaximum(self.picSize-1)
            self.ui.layer_slider.setMinimum(0)
            self.ui.now_layers_label.setText('目前層數:1/' + str(self.picSize))
            self.ct_file = "../picture/1.jpg"
            img = QtGui.QPixmap(self.ct_file)
            img = img.scaled(self.ct_img_small_size, self.ct_img_small_size, Qt.KeepAspectRatio)
            self.ui.ct_img.setPixmap(img)
        except Exception as e:
            print(e)
           

這段代碼下面的    self.ui....  屬于初始化的參數值 

 self.ui.ct_path.setText(self.ct_path_text)  指的是顯示路徑的文本框;

下面這兩句:

self.ui.layer_slider.setMaximum(self.picSize-1)

self.ui.layer_slider.setMinimum(0)
           

指的是滑動條的初始化的最大值最小值

QTdesigner前後端互動--結節算法實戰

ct_path.setText和layer_slider在上圖已标注。

接下來要實作上傳功能,首先書寫上傳按鈕的實作方法:

def _upload_ct(self):
        try:
            if self.ui.ct_path.text() == '' or self.ct_path_text == '':
                QMessageBox().information(self, '提示', '請選擇CT檔案路徑', QMessageBox.Yes)
                # QtWidgets.QMessageBox(QMessageBox.Warning, '提示', '請選擇CT檔案路徑').exec_()
                return

            stdin, stdout, stderr = ssh.exec_command('cd ' + self.serverWorkSpace + ';python clearCT.py')
            print(stdout.read())
            print(stderr.read())

            self.upload_thread.start()
            self.upload_thread.sigout.connect(self._change_progress)
        except Exception as e:
            print(e)
           

書寫上傳的 程序類

class UploadThread(QThread):
    sigout = pyqtSignal(float)

    def __init__(self):
        super().__init__()
        self.nativePath = ''
        self.uploadPath = ''

    def run(self):
        n = 0
        fileLength = len(os.listdir(self.nativePath)) - 1
        for i in os.listdir(self.nativePath):
            print(self.nativePath+i)
            sftp.put(self.nativePath + i, self.uploadPath + i)
            self.sigout.emit((n/fileLength)*100)
            n += 1
           

因為本處的 self.uploadPath = '' 上傳路徑設定為空值,是以需要在初始化裡面設定好上傳路徑的具體指,否則程式雖然會上傳但是卻傳不到指定路徑下。

self.upload_thread.uploadPath = self.serverWorkSpace + 'detection/test_mask/0006/'
           

一定要記得初始化!!

self.upload_thread = UploadThread()
           

初始化函數裡有這句話才行的。重寫了run方法實作上傳功能。

上傳進度為百分之百的時候,彈窗提示上傳完成:

def _change_progress(self, value):
        self.ui.upload_progressBar.setValue(value)
        print(value)
        if value == 100:
            QMessageBox().information(self, '提示', "上傳完成", QMessageBox.Yes)
           

另一個功能:實作滑塊的拖動

self.ui.layer_slider.valueChanged.connect(self._layer_slider_changed)
           

實作本函數的方法:

def _layer_slider_changed(self, value):
        if self.ct_path_text == '':
            return
        self.ct_file = "../picture/%d.jpg" % (value+1)
        img = QtGui.QPixmap(self.ct_file)
        img = img.scaled(self.ct_img_small_size, self.ct_img_small_size, Qt.KeepAspectRatio)
        self.ui.ct_img.setPixmap(img)
        self.ui.now_layers_label.setText('目前層數:' + str(value+1) + '/' + str(self.picSize))
           

滑鼠的滑輪實作圖檔的滾動浏覽等會再寫

功能15:添加登入界面,賬号密碼登陸成功實作跳轉

在之前的界面基礎上,添加新界面。思路比較簡單,添加login.py界面即可,使登陸成功後,跳轉至index.py。

隻需打包login.py即可。

具體實作方法如下:

      界面添加上txt文本驗證,賬号密碼在文本中,就登陸成功。否則失敗。

      至于txt的賬号密碼内容,可在伺服器端實作修改,每次運作下載下傳至本地。

# -*- coding: utf-8 -*-
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QFileDialog, QMessageBox
from PyQt5 import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import paramiko,os
import socket,time
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
import paramiko
transport = paramiko.Transport(('ip', 端口))
# 建立連接配接
transport.connect(username='伺服器賬号', password='密碼')
# 将sshclient的對象的transport指定為以上的transport
ssh = paramiko.SSHClient()
ssh._transport = transport
sftp = paramiko.SFTPClient.from_transport(transport)
sftp.get('E:/LYC/lungDetection-V1/users.txt', '../data/users.txt')
class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.setupUi()
    def setupUi(self):
        self.resize(993, 550)
        # self.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint)
        # self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.setFixedSize(self.width(),self.height())
        s1 = '肺結節多種病理類型人工智能檢測系統'
        s2 = '公司'
        s3 = '山東大學'
        self.setWindowTitle('肺結節多種病理類型人工智能檢測系統-登入界面')

        self.background = QtWidgets.QLabel(self)
        self.background.setGeometry(QtCore.QRect(0, 0, 1025, 550))
        self.background.setPixmap(QtGui.QPixmap("../image/background.png"))

        self.left = QtWidgets.QLabel(self)
        self.left.setGeometry(QtCore.QRect(20, 180, 261, 211))
        self.left.setPixmap(QtGui.QPixmap("../image/pic.png"))
        self.left.setScaledContents(True)

        self.pic_1 = QtWidgets.QLabel(self)
        self.pic_1.setGeometry(QtCore.QRect(320, 170, 150, 150))
        self.pic_1.setPixmap(QtGui.QPixmap("../image/1.jpg"))
        self.pic_1.setScaledContents(True)

        self.pic_2 = QtWidgets.QLabel(self)
        self.pic_2.setGeometry(QtCore.QRect(500, 170, 150, 150))
        self.pic_2.setPixmap(QtGui.QPixmap("../image/2.jpg"))
        self.pic_2.setScaledContents(True)

        self.pic_3 = QtWidgets.QLabel(self)
        self.pic_3.setGeometry(QtCore.QRect(320, 340, 150, 150))
        self.pic_3.setPixmap(QtGui.QPixmap("../image/3.jpg"))
        self.pic_3.setScaledContents(True)

        self.pic_4 = QtWidgets.QLabel(self)
        self.pic_4.setGeometry(QtCore.QRect(500, 340, 150, 150))
        self.pic_4.setPixmap(QtGui.QPixmap("../image/4.jpg"))
        self.pic_4.setScaledContents(True)

        self.font = QtGui.QFont()
        self.font.setFamily("黑體")
        self.font.setPointSize(30)
        self.font.setItalic(True)

        self.text_1 = QtWidgets.QLabel(self)
        self.text_1.setGeometry(QtCore.QRect(300, 160, 261, 71))
        self.text_1.setFont(self.font)

        self.text_2 = QtWidgets.QLabel(self)
        self.text_2.setGeometry(QtCore.QRect(360, 290, 271, 61))
        self.text_2.setFont(self.font)

        self.text_3 = QtWidgets.QLabel(self)
        self.text_3.setGeometry(QtCore.QRect(420, 420, 261, 61))
        self.text_3.setFont(self.font)

        # self.text_1.setText("<html><head/><body><p><span style=\" color:#ff0000;\">智能資料擷取</span></p></body></html>")
        # self.text_2.setText("<html><head/><body><p><span style=\" color:#ff0000;\">智能資料處理</span></p></body></html>")
        # self.text_3.setText("<html><head/><body><p><span style=\" color:#ff0000;\">智能診斷應用</span></p></body></html>")

        self.frame = QtWidgets.QFrame(self)
        self.frame.setGeometry(QtCore.QRect(690, 180, 261, 271))
        self.frame.setStyleSheet("background:rgb(240, 240, 240)")

        h2_font = QtGui.QFont()
        h2_font.setFamily("新宋體")
        h2_font.setPointSize(11)
        h2_font.setBold(True)
        h2_font.setWeight(75)

        self.h2 = QtWidgets.QLabel(self.frame)
        self.h2.setGeometry(QtCore.QRect(96, 20, 71, 21))
        self.h2.setFont(h2_font)
        self.h2.setText("賬号登入")

        font = QtGui.QFont()
        font.setFamily("新宋體")
        font.setPointSize(10)

        self.zhl = QtWidgets.QLabel(self.frame)
        self.zhl.setGeometry(QtCore.QRect(20, 80, 41, 25))
        self.zhl.setFont(font)
        self.zhl.setText("賬号:")

        self.mml = QtWidgets.QLabel(self.frame)
        self.mml.setGeometry(QtCore.QRect(20, 130, 41, 25))
        self.mml.setFont(font)
        self.mml.setText("密碼:")

        self.checkBox = QtWidgets.QCheckBox(self.frame)
        self.checkBox.setGeometry(QtCore.QRect(170, 170, 71, 21))
        self.checkBox.setText("記住密碼")

        self.login = QtWidgets.QPushButton(self.frame)
        self.login.setGeometry(QtCore.QRect(75, 210, 111, 31))
        self.login.setStyleSheet("background:rgb(255, 97, 76)")
        self.login.clicked.connect(self.yanzheng)
        self.login.setText("登  錄")
        self.login.setShortcut('enter')

        self.username = QtWidgets.QLineEdit(self.frame)
        self.username.setGeometry(QtCore.QRect(70, 80, 171, 25))

        self.password = QtWidgets.QLineEdit(self.frame)
        self.password.setGeometry(QtCore.QRect(70, 130, 171, 25))
        self.password.setEchoMode(QtWidgets.QLineEdit.Password)




        self.title = QtWidgets.QLabel(self)
        self.title.setGeometry(QtCore.QRect(110, 50, 801, 51))
        title_font = QtGui.QFont()
        title_font.setFamily("黑體")
        title_font.setPointSize(32)
        self.title.setFont(title_font)
        self.title.setText(
            "<html><head/><body><p><span style=\" color:#aaffff;\">肺結節多種病理類型人工智能檢測系統</span></p></body></html>")

        # self.setCentralWidget(self)
        # self.menubar = QtWidgets.QMenuBar(self)
        # self.menubar.setGeometry(QtCore.QRect(0, 0, 993, 23))
        # self.setMenuBar(self.menubar)

    def yanzheng(self):
        self.n=0
        f=open('../data/users.txt')
        ma=self.username.text()+'.'+self.password.text()
        for i in f:
            # a,b=str(i[-1]).split('.',2)
            # print(a,b)
            if i[:-1]==ma:
                QMessageBox.information(self, " ", "登入成功!", QMessageBox.Yes)
                import Index
                self.hide()
                # self.form.hide()  # 如果沒有self.form.show()這一句,關閉Demo1界面後就會關閉程式
                self.ex = Index.Index()
                self.ex.show()
                self.n=1
        if self.n==0:
            # QMessageBox.information(self, " ", "登入成功!", QMessageBox.Yes)
            QMessageBox.critical(self, " ", "賬号或密碼錯誤!", QMessageBox.Yes)
            # QMessageBox.warning(self, " ", "查無此人!", QMessageBox.Yes)
        f.close()


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())
           

功能16:下拉框選中文字,實作在文本框中自動粘貼的功能。

# pyqt界面的  ct_discribe和doctor_advice元件點選粘貼事件
self.ui.ct_discribe.currentTextChanged.connect(lambda :self.Clickme_ct())
self.ui.doctor_advice.currentTextChanged.connect(lambda :self.Clickme_doc())
           

 點選将文字粘貼至下拉框中。

def Clickme_ct(self):
        print(self.ui.ct_discribe.currentText())
        ct_dis = self.ui.ct_describe_textEdit.toPlainText()
        ct_dis += self.ui.ct_discribe.currentText()
        self.ui.ct_describe_textEdit.setText(ct_dis)
        print("文字粘貼到CT影像描述文本框")

    def Clickme_doc(self):
        print(self.ui.doctor_advice.currentText())
        doc_adv = self.ui.doctor_advice_textEdit.toPlainText()
        doc_adv += self.ui.doctor_advice.currentText()
        self.ui.doctor_advice_textEdit.setText(doc_adv)
           

功能17:克服桶排序。顯示前24個結節。

思路是:桶排序按照字元長度排序的,同一字元同一排序,是以按照len()方法長度排序即可。

顯示前24個結節也比較簡單,循環到24,break即可。

def _read_jj(self):
        # 讀取已經下載下傳的坐标值
        self.zbList.clear()
        wb = load_workbook('../data/new data.xlsx')
        sheet = wb.active
        i = 2
        while True:
            content = sheet["M%d" % i].value
            if content is None or content == "":
                break
            self.zbList.append(content)
            i += 1

        # 讀取已經下載下傳的識别結果
        self.resList.clear()
        resNum = len(os.listdir('../result'))
        for i in range(resNum):
            f = open('../result/tes_prob_batch' + str(i) + '.txt')
            batches = f.readlines()
            for batch in batches:
                batch = batch[0:-2]  # 去除換行符
                number = batch.split(' ')
                self.resList.append((number[1][0:8], number[2][0:8], number[3][0:8]))
            f.close()

        # 将結節檔案顯示在清單
        self.ui.jj_listWidget.clear()
        jjList = os.listdir('../npy/')
        jjList.sort(key=functools.cmp_to_key(self.jj_sort2))
        i = 0



        for jj in jjList:
            # print(jj)
            # s = jj.split("-")[-1]
            self.ui.jj_listWidget.addItem(jj)
            i += 1
            if i >= 24:
                break
            # self.ui.jj_listWidget.setSortingEnabled(True)

    def jj_sort2(self,x,y):
        return len(x)-len(y)