From 12b5fdaa3e992b2c0da1c21075ce17fcbb38f060 Mon Sep 17 00:00:00 2001
From: linxiao <chunjinge@gmail.com>
Date: Sun, 24 Jun 2018 22:25:28 +0800
Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E7=AA=97=E5=8F=A3=E6=B7=BB?=
 =?UTF-8?q?=E5=8A=A0=E4=BF=9D=E5=AD=98=E5=92=8C=E5=8F=96=E6=B6=88=E6=8C=89?=
 =?UTF-8?q?=E9=92=AE,=E6=B7=BB=E5=8A=A0cpu=E7=BA=BF=E7=A8=8B=E8=AE=BE?=
 =?UTF-8?q?=E7=BD=AE=EF=BC=9B=E8=AE=BE=E7=BD=AE=E4=BF=AE=E6=94=B9=E5=AE=9E?=
 =?UTF-8?q?=E6=97=B6=E7=94=9F=E6=95=88=EF=BC=9B=E4=BB=A3=E7=A0=81=E7=AE=80?=
 =?UTF-8?q?=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 RecordConfig.py     |   2 +-
 RecordVideo.py      | 109 ++++++++++++++---
 RecordWindow.py     | 112 ++++++++++++-----
 SettingWindow.py    | 289 +++++++++++++++++++++++++++++++-------------
 configByLinxiao.ini |  22 ++--
 5 files changed, 387 insertions(+), 147 deletions(-)

diff --git a/RecordConfig.py b/RecordConfig.py
index 64182d7..92df421 100644
--- a/RecordConfig.py
+++ b/RecordConfig.py
@@ -45,7 +45,7 @@ class RecordConfig():
         
         conf.add_section('author')
         conf.set('author', 'name', 'linxiao')
-        conf.set('author', 'mail', '940950943@qq.com')
+        conf.set('author', 'mail', '940950943@qqqqqqqqqqqqqqqqqqqqqqqqqqqq.com')
         
         self.config = conf
         self.write()
diff --git a/RecordVideo.py b/RecordVideo.py
index f43430f..583d59f 100644
--- a/RecordVideo.py
+++ b/RecordVideo.py
@@ -1,4 +1,4 @@
-import datetime,time,sys,os,signal,re
+import datetime,time,sys,os,signal,re,winreg
 from datetime import datetime
 import subprocess,threading
 from subprocess import CalledProcessError
@@ -12,6 +12,7 @@ from RecordConfig import *
 import logging
 import RunCMD
 from RunCMD import get_ffmpeg_path
+from winreg import HKEY_CURRENT_USER, OpenKey, QueryInfoKey, EnumValue, SetValueEx, CloseKey, REG_SZ, KEY_READ, KEY_SET_VALUE
 
 class RecordVideo():
 
@@ -38,21 +39,24 @@ class RecordVideo():
         self.record_thread=None
         
         self.file_dir = ''
-        self.load_config()
-        
-    
-    def load_config(self):
+        self.load()
         
+    def load(self):
         #日志
         self.logger = logging.getLogger(__name__)
         self.logger.setLevel(level = logging.INFO)
         handler = logging.FileHandler('log.txt')
         handler.setLevel(logging.INFO)
         formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
-        handler.setFormatter(formatter)
+        handler.setFormatter(formatter)    
         
         self.logger.addHandler(handler)
         
+        self.load_config()
+        
+        
+    def load_config(self):
+  
         rc = RecordConfig()
         self.config = rc.config
         
@@ -92,11 +96,19 @@ class RecordVideo():
             self.logger.info('录制中...')
             # print('cmd:\n%s' % cmd)
             start_time = datetime.now()
-            self.process=subprocess.Popen(cmd, shell=shell, universal_newlines = True, stdin = subprocess.PIPE, stderr = subprocess.STDOUT, stdout = subprocess.PIPE)
+            self.process=subprocess.Popen(cmd, shell=shell, universal_newlines = False, stdin = subprocess.PIPE, stderr = subprocess.STDOUT, stdout = subprocess.PIPE)
             line = ''
             while self.recording:
-            
-                line += self.process.stdout.readline()
+                
+                # print(cmd)
+                # tmp_out = self.process.stdout.readline()
+                # print('test tmp out:%s' % tmp_out)
+                # line += self.process.stdout.readline()
+                #文字输出编码错误记录
+                #异常:UnicodeDecodeError: 'gbk' codec can't decode byte 0xb4 in position 2881: illegal multibyte sequence
+                #原因:cmd输出包含中文字符
+                #解决方案:universal_newlines = False
+                
                 now = datetime.now()
                 if (now - start_time).total_seconds() >2:
                     # self.logger.info('recording...')
@@ -104,6 +116,7 @@ class RecordVideo():
                     if self.process:
                         ffmpeg_running = self.process.poll() is None
                         log_text = 'ffmpeg 运行状态:%s' % ('运行中' if ffmpeg_running else '终止')
+                        
                         print(log_text)
                         self.logger.info(log_text) 
                         
@@ -118,14 +131,14 @@ class RecordVideo():
                         raise CalledProcessError(self.process.returncode, cmd)
                     
                     # self.logger.info(line)
-                    print(line)
+                    # print(line)
                     line = ''
                     start_time = now
                     
                 # print(line)
                 
                 if not self.recording:
-                    self.process.stdin.write('q')
+                    self.process.stdin.write(b'q')
                     print(self.process.communicate())
                     break
         
@@ -147,7 +160,7 @@ class RecordVideo():
             print('cmd: \n%s' % cmd)
             self.logger.info('record cmd:\n %s' % cmd)
             self.record_thread = Thread(name=self.record_thread_name, target= target, args = (cmd,), daemon=True)
-            th.start()
+            self.record_thread.start()
             self.recording=True
             self.exception_exit = False
             
@@ -231,8 +244,72 @@ class RecordVideo():
         print(l_msg)
         self.logger.info(l_msg)
         return ready
-         
+                    
+             
+    def check_run_state(self):
+        #验证运行有效性逻辑:
+        #一、判断是否正常安装
+        #判断条件:软件安装时在注册表“HKEY_CURRENT_USER\\SOFTWARE\\Gutin\\Record“记录下安装目录
+        #二、非正常安装有效时间为三个月,且只能发生一次
+        
+        #验证当前运行目录是否存在注册表中
+        qualified = False
+        run_dir = os.path.abspath('.')
+        # print('run_dir:%s' % run_dir)
+        reg_path = 'SOFTWARE\\Gutin\\Record'
+        feature_name ='InstallDir'
+        key = OpenKey(HKEY_CURRENT_USER, reg_path, access = KEY_READ)
+        items = QueryInfoKey(key)
+        for i in range(items[1]):
+            item = EnumValue(key, i)
+            name = item[0]
+            value = item[1]
+            type = item[2]
+            if name and name.find(feature_name)>=0:
+                if os.path.samefile(value, run_dir):
+                    qualified = True
+                    break;
+        
+        if not qualified:
+            #查找非正常安装目录记录
+            #记录以运行目录的hash值作为键名,值为首次运行的时间
+            time_format= '%Y-%m-%d %H:%M:%S'
+            run_time = None
+            unqualified_key_name  = 'Unqualified'
+            has_unqualified = False
+            for i in range(items[1]):
+                item = EnumValue(key, i)
+                name = item[0]
+                value = item[1]
+                type = item[2]
+                if name == unqualified_key_name:
+                    has_unqualified = True
+                    run_time = value
+          
+            if not has_unqualified:
+                #如果不存在,创建       
+                run_time = datetime.now().strftime(time_format)
+                key = OpenKey(HKEY_CURRENT_USER, reg_path, access = KEY_SET_VALUE)
+                SetValueEx(key, unqualified_key_name, 0, REG_SZ, run_time)            
+                      
+            #判断时限
+            # now = datetime.strptime('2018-06-22 12:11:51', time_format)
+            now = datetime.now()
+            run_time_obj = datetime.strptime(run_time, time_format)
+            print('first run_time:%s' % run_time)
+            print('now:%s' % now.strftime(time_format))
             
+            # qual_days = 1 - (now - run_time_obj).days
+            # print('qualified days:%d' % (qual_days))
+            qual_hours = (now - run_time_obj).total_seconds() // 3600
+            # if qual_days > 0:
+            if 5 - qual_hours > 0:
+                qualified = True
+         
+        CloseKey(key)
+        return qualified
+
+    
     def debug_camera(self):
         try:
             play_cmd = ['ffplay','-f','dshow','-i','video={}'.format(self.camera_name),'-window_title','按q退出','-noborder']
@@ -262,17 +339,19 @@ class RecordVideo():
         
     def get_file_name(self):
         date_dir = datetime.now().strftime('%Y-%m-%d')
-        time_str = datetime.now().strftime('%H_%M')
+        time_str = datetime.now().strftime('%Y-%m-%d-%H%M%S')
         video_type_name = ''
         if self.record_type == RecordType.Camera:
+            # video_type_name = 'camera'
             video_type_name = '摄像头'
         if self.record_type == RecordType.Screen:
             video_type_name = '屏幕'
+            # video_type_name = 'screen'
         
         today_file_dir = os.path.join(self.file_dir, date_dir)
         if not os.path.exists(today_file_dir):
             os.mkdir(today_file_dir)
-        file_name = os.path.join(today_file_dir, '{}-{}-{}{}'.format(video_type_name, time_str, self.resolution, self.file_suffix))
+        file_name = os.path.join(today_file_dir, '{}-{}{}'.format(video_type_name, time_str, self.file_suffix))
         print('recording file name: %s' % file_name)
         return file_name
         
diff --git a/RecordWindow.py b/RecordWindow.py
index 8ca7aec..424e242 100644
--- a/RecordWindow.py
+++ b/RecordWindow.py
@@ -25,7 +25,7 @@ class RecordWindow(QtWidgets.QWidget):
         self.load_modules()
         #更新设置
         self.need_update_config = False
-        self.need_hide = True
+        # self.need_hide = True
         #初始化状态
         print('初始化状态...')
         self.update_state()
@@ -48,8 +48,9 @@ class RecordWindow(QtWidgets.QWidget):
         # print('close window.')
         # self.close_signal.emit()
         # self.close()
-        # if self.recording:
-        
+        if self.recording:
+            
+            self.stop_record(force = True)
             # question = QMessageBox(self)
             # question.setText('系统正在录制中,确定要退出吗?')
             # question.setWindowTitle('提示')
@@ -275,7 +276,10 @@ class RecordWindow(QtWidgets.QWidget):
         self.rti.update_state(self.recording, self.record_type)
     
     def stop_record(self, force = False):
-    
+        #force应用在两种情况:
+        #1.timer实时刷新ffmpeg进程状态,当出现异常退时force=true,即force=exception_exit。
+        #2.应用退出时
+        
         if self.recording or force:
             self.stop_timer()    
             
@@ -318,23 +322,37 @@ class RecordWindow(QtWidgets.QWidget):
             self.start_timer()
             self.update_state()
         else:
-            self.need_hide = False
-            question = QMessageBox.information(self, '提示', '检测到录制设备缺失,无法进行录制,请先完善设备设置。', QMessageBox.Yes)
+            # self.need_hide = False
+            # question = QMessageBox.information(self, '提示', '检测到录制设备缺失,无法进行录制,请先完善设备设置。', QMessageBox.Yes)
             # self.need_hide = True
-            # question = QMessageBox()
-            # question.setText('检测到录制设备缺失,无法进行录制,请先完善设备设置。')
-            # question.setWindowTitle('提示')
-            # question.setIcon(QMessageBox.Question)
-            # question.addButton(QMessageBox.Yes)
+            question = QMessageBox()
+            question.setText('检测到录制设备缺失,无法进行录制,请先完善设备设置。')
+            question.setWindowTitle('提示')
+            question.setIcon(QMessageBox.Question)
+            question.addButton(QMessageBox.Yes)
+            tmp_btn = question.button(QMessageBox.Yes)
+            tmp_btn.setText('确定')
+            
+            question.adjustSize()
+            screen_center = QApplication.desktop().screenGeometry()
+            question.move(((screen_center.width() - question.width()) /2), ((screen_center.height() - question.height())/2))
+            
             # question.addButton(QMessageBox.No)
             # question.setDefaultButton(QMessageBox.No)
-            # ret = question.exec()
+            ret = question.exec()
             # if ret == QMessageBox.Yes:
                 # print('软件将退出.')
                 
     def show_setting(self):
-        self.need_hide = False
+        # QWidget.connect(self.sw, update_setting(bool), self, self.update_setting(bool))
+        self.sw.update_setting.connect(self.update_setting)
         self.sw.showSettingWindow()
+        
+    def update_setting(self, changed):
+        if changed:
+            print('update setting..')
+            self.rv.load_config()
+    
 
     ''''
         鼠标拖动窗体
@@ -376,27 +394,55 @@ class RecordWindow(QtWidgets.QWidget):
     
     def monitor_shortcut(self):
         
-        sc = Shortcut()
+        if self.rv.check_run_state():
         
-        camera_key_group = self.rc.config.get('shortcut','camera')
-        screen_key_group = self.rc.config.get('shortcut','screen')
-        stop_record_key_group = self.rc.config.get('shortcut','stop')
-        
-        camera_shortcut = [int(key) for key in camera_key_group.split(',')]
-        screen_shortcut = [int(key) for key in screen_key_group.split(',')]
-        stop_shortcut = [int(key) for key in stop_record_key_group.split(',')]
-        
-        print('camera shortcut: %s' % camera_shortcut)
-        print('screen shortcut: %s' % screen_shortcut)
-        print('stop shortcut: %s' % stop_shortcut)
-        
-        if camera_key_group:
-            sc.add(1, camera_shortcut, lambda: self.record(RecordType.Camera))
-        if screen_key_group:
-            sc.add(2, screen_shortcut, lambda: self.record(RecordType.Screen))
-        if stop_record_key_group:
-            sc.add(3, stop_shortcut, self.stop_record)
-        sc.monitor()
+            sc = Shortcut()
+            
+            camera_key_group = self.rc.config.get('shortcut','camera')
+            screen_key_group = self.rc.config.get('shortcut','screen')
+            stop_record_key_group = self.rc.config.get('shortcut','stop')
+            
+            camera_shortcut = [int(key) for key in camera_key_group.split(',')]
+            screen_shortcut = [int(key) for key in screen_key_group.split(',')]
+            stop_shortcut = [int(key) for key in stop_record_key_group.split(',')]
+            
+            print('camera shortcut: %s' % camera_shortcut)
+            print('screen shortcut: %s' % screen_shortcut)
+            print('stop shortcut: %s' % stop_shortcut)
+            
+            if camera_key_group:
+                sc.add(1, camera_shortcut, lambda: self.record(RecordType.Camera))
+            if screen_key_group:
+                sc.add(2, screen_shortcut, lambda: self.record(RecordType.Screen))
+            if stop_record_key_group:
+                sc.add(3, stop_shortcut, self.stop_record)
+                
+            
+            sc.monitor()
+            
+        else:
+            question = QMessageBox(self)
+            question.setText('检测到软件非正常运行,以下功能将被禁用(如有疑问,请联系开发商):<br/><ul><li>快捷键</li></ul>')
+            question.setWindowTitle('限制使用模式')
+            question.setIcon(QMessageBox.Warning)
+            question.addButton(QMessageBox.Yes)
+            # question.setButtonText(QMessageBox.Yes, QString('确定'))
+            btn = question.button(QMessageBox.Yes)
+            # print(btn.text())
+            btn.setText('确定')
+            # question.addButton('确定', QMessageBox.ButtonRole.AcceptRole)
+            
+            # print('screen x:%d,y:%d; question x:%d,y:%d.' % (screen_center.x(), screen_center.y(), question.x(), question.y()))
+            # print(question.frameGeometry())
+            question.adjustSize()
+            screen_center = QApplication.desktop().screenGeometry()
+            #居中x和y的值计算与我想的不一样
+            #原因:question在exec之前无法准确的获取其size。(485,170)是exec之后的实际size。
+            question.move(((screen_center.width() - 485)/2), ((screen_center.height() - 170)/2))
+            # question.move((screen_center.width() / 2 - question.width()), (screen_center.height() /2 - question.height()))
+            # print('screen x:%d,y:%d; question x:%d,y:%d; question width:%d,height:%d.' % (screen_center.width(), screen_center.height(), question.geometry().width(), question.geometry().height(), question.width(), question.height()))
+            # question.move(QApplication.desktop().screenGeometry().center()- self.rect().center())
+            ret = question.exec()
         
     '''
         窗体事件
diff --git a/SettingWindow.py b/SettingWindow.py
index 92321a4..809fb5f 100644
--- a/SettingWindow.py
+++ b/SettingWindow.py
@@ -7,19 +7,25 @@ from DevicesInfo import *
 import RecordConfig
 from RecordConfig import *
 from PyQt5.QtWidgets import QMessageBox
+import resource
+import psutil
 
 class SettingWindow(QDialog):
     
+    
+    update_setting = pyqtSignal(bool)
     def __init__(self, parent = None):
         super(SettingWindow,self).__init__(parent)
+        
+        self.changed = False
         self.setupUi()
         self.load()
 
     def setupUi(self):
         self.setObjectName("SettingWindow")
-        self.resize(386, 238)
+        self.setFixedSize(383, 280)
         icon = QtGui.QIcon()
-        icon.addPixmap(QtGui.QPixmap("resource/gutin.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        icon.addPixmap(QtGui.QPixmap(":/resource/gutin.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
         self.setWindowIcon(icon)
         self.tabWidget = QtWidgets.QTabWidget(self)
         self.tabWidget.setGeometry(QtCore.QRect(10, 10, 371, 221))
@@ -108,11 +114,12 @@ class SettingWindow(QDialog):
         self.tab_record = QtWidgets.QWidget()
         self.tab_record.setObjectName("tab_record")
         self.gridLayoutWidget = QtWidgets.QWidget(self.tab_record)
-        self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 10, 321, 161))
+        self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 10, 311, 161))
         self.gridLayoutWidget.setObjectName("gridLayoutWidget")
         self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
         self.gridLayout.setContentsMargins(0, 0, 0, 0)
         self.gridLayout.setHorizontalSpacing(0)
+        self.gridLayout.setVerticalSpacing(16)
         self.gridLayout.setObjectName("gridLayout")
         self.label_14 = QtWidgets.QLabel(self.gridLayoutWidget)
         self.label_14.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
@@ -166,6 +173,28 @@ class SettingWindow(QDialog):
         self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
         self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
         self.frame.setObjectName("frame")
+        
+        self.threads_label = QtWidgets.QLabel(self.gridLayoutWidget)
+        self.threads_label.setLayoutDirection(QtCore.Qt.LeftToRight)
+        self.threads_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+        self.threads_label.setObjectName("threads_label")
+        self.gridLayout.addWidget(self.threads_label, 5, 0, 1, 1)
+        self.threads_spinBox = QtWidgets.QSpinBox(self.gridLayoutWidget)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.threads_spinBox.sizePolicy().hasHeightForWidth())
+        self.threads_spinBox.setSizePolicy(sizePolicy)
+        self.threads_spinBox.setObjectName("record.threads")
+        self.gridLayout.addWidget(self.threads_spinBox, 5, 1, 1, 1)
+        self.tabWidget.addTab(self.tab_record, "")
+        
+        self.save_button = QtWidgets.QPushButton(self)
+        self.save_button.setGeometry(QtCore.QRect(180, 240, 75, 23))
+        self.save_button.setObjectName("save_button")
+        self.cancel_button = QtWidgets.QPushButton(self)
+        self.cancel_button.setGeometry(QtCore.QRect(270, 240, 75, 23))
+        self.cancel_button.setObjectName("cancel_button")
 
         self.retranslateUi()
         self.tabWidget.setCurrentIndex(0)
@@ -188,13 +217,13 @@ class SettingWindow(QDialog):
         self.label_13.setText(_translate("Form", "文件保存目录:"))
         self.label_12.setText(_translate("Form", "分辨率:"))
         self.btn_file_dir.setText(_translate("Form", "选择文件目录"))
+        self.threads_label.setText(_translate("Form", "CPU线程:"))
         self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_record), _translate("Form", "录制参数"))
+        self.save_button.setText(_translate("Form", "保存"))
+        self.cancel_button.setText(_translate("Form", "取消"))
         
-   
-   
-    # def set_combobox_select_index_by_name_or_data(self, combobox, name_or_data):
-        # for i in range(len()
     def load_combobox_data(self, combox, data):
+        #下拉列表数据加载
         for obj in data:
             name = obj[0]
             value = name 
@@ -205,13 +234,56 @@ class SettingWindow(QDialog):
     def get_key_group_name(self, key_tuple):
         return key_tuple.replace(r'(','').replace(r')','')
      
+    def load_config(self):
+        '''
+            加载设置         
+        '''  
+        #设备
+        video_device_name = self.rc.config.get('devices','camera_device_name')
+        voice_device_name = self.rc.config.get('devices','voice_device_name')
+        screen_device_name = self.rc.config.get('devices','screen_device_name')
+        system_voice_device_name = self.rc.config.get('devices','system_voice_device_name')
         
+        # self.cb_camera_devices.setCurrentIndex(1)
+        video_cur_index = self.cb_camera_devices.findData(video_device_name)
+        # print('video name:\n%s\ndevice index:%d' % (video_device_name, video_cur_index))
+        self.cb_camera_devices.setCurrentIndex(video_cur_index)
+        
+        self.cb_voice_devices.setCurrentIndex(self.cb_voice_devices.findData(voice_device_name))
+        self.cb_screen_devices.setCurrentIndex(self.cb_screen_devices.findData(screen_device_name))
+        self.cb_system_voice_devices.setCurrentIndex(self.cb_system_voice_devices.findData(system_voice_device_name))
+        
+        #快捷键
+        record_camera_key_group = self.rc.config.get('shortcut','camera')
+        record_screen_key_group = self.rc.config.get('shortcut','screen')
+        record_stop_key_group = self.rc.config.get('shortcut','stop')
+        
+        record_camera_key_group_name = self.get_key_group_name(record_camera_key_group)
+        record_screen_key_group_name = self.get_key_group_name(record_screen_key_group)
+        record_stop_key_group_name = self.get_key_group_name(record_stop_key_group)
+        
+        self.le_start_record_camera_shortcut.setText(record_camera_key_group_name)
+        self.le_start_record_screen_shortcut.setText(record_screen_key_group_name)
+        self.le_start_stop_exit_shortcut.setText(record_stop_key_group_name)
+        
+        #录制参数
+        record_resolution = self.rc.config.get('record','resolution')
+        record_vcodec = self.rc.config.get('record','vcodec')
+        record_frame_rate = self.rc.config.getfloat('record','frame_rate')
+        record_file_dir = self.rc.config.get('record','file_dir')
+        record_file_dir = os.path.abspath(record_file_dir)
+        record_threads = int(self.rc.config.get('record','threads'))
+        
+        self.cb_resolution.setCurrentIndex(self.cb_resolution.findData(record_resolution))
+        self.cb_vcodec.setCurrentIndex(self.cb_vcodec.findData(record_vcodec))
+        self.dsb_frame_rate.setValue(record_frame_rate)
+        self.le_file_path.setText(record_file_dir)
+        
+        self.threads_spinBox.setValue(record_threads)
+     
     def load(self):
-    
         
-        rc = RecordConfig()
-        self.rc = rc
-        self.changed = False
+        self.rc = RecordConfig()
         '''
             数据初始化
         '''
@@ -237,75 +309,38 @@ class SettingWindow(QDialog):
         self.load_combobox_data(self.cb_resolution, resolutions)
         self.load_combobox_data(self.cb_vcodec, vcodec)
         
+        self.dsb_frame_rate.setDecimals(1)
+        cpu_count = psutil.cpu_count(logical=True)
+        self.threads_spinBox.setMaximum(cpu_count)
+        
         # print(self.cb_camera_devices.currentData())
         # print('视频设备列表:')
         # print(di.video_devices)
         # print('音频设备列表:')
         # print(di.voice_devices)
         
-        '''
-            加载设置         
-        '''  
-        #设备
-        video_device_name = rc.config.get('devices','camera_device_name')
-        voice_device_name = rc.config.get('devices','voice_device_name')
-        screen_device_name = rc.config.get('devices','screen_device_name')
-        system_voice_device_name = rc.config.get('devices','system_voice_device_name')
-        
-        # self.cb_camera_devices.setCurrentIndex(1)
-        video_cur_index = self.cb_camera_devices.findData(video_device_name)
-        # print('video name:\n%s\ndevice index:%d' % (video_device_name, video_cur_index))
-        self.cb_camera_devices.setCurrentIndex(video_cur_index)
-        
-        self.cb_voice_devices.setCurrentIndex(self.cb_voice_devices.findData(voice_device_name))
-        self.cb_screen_devices.setCurrentIndex(self.cb_screen_devices.findData(screen_device_name))
-        self.cb_system_voice_devices.setCurrentIndex(self.cb_system_voice_devices.findData(system_voice_device_name))
-        
-        #快捷键
-        record_camera_key_group = rc.config.get('shortcut','camera')
-        record_screen_key_group = rc.config.get('shortcut','screen')
-        record_stop_key_group = rc.config.get('shortcut','stop')
-        
-        record_camera_key_group_name = self.get_key_group_name(record_camera_key_group)
-        record_screen_key_group_name = self.get_key_group_name(record_screen_key_group)
-        record_stop_key_group_name = self.get_key_group_name(record_stop_key_group)
-        
-        self.le_start_record_camera_shortcut.setText(record_camera_key_group_name)
-        self.le_start_record_screen_shortcut.setText(record_screen_key_group_name)
-        self.le_start_stop_exit_shortcut.setText(record_stop_key_group_name)
-        
-        #录制参数
-        record_resolution = self.rc.config.get('record','resolution')
-        record_vcodec = self.rc.config.get('record','vcodec')
-        record_frame_rate = self.rc.config.getfloat('record','frame_rate')
-        record_file_dir = self.rc.config.get('record','file_dir')
-        record_file_dir = os.path.abspath(record_file_dir)
-        
-        self.cb_resolution.setCurrentIndex(self.cb_resolution.findData(record_resolution))
-        self.cb_vcodec.setCurrentIndex(self.cb_vcodec.findData(record_vcodec))
-        self.dsb_frame_rate.setDecimals(1)
-        self.dsb_frame_rate.setValue(record_frame_rate)
-        self.le_file_path.setText(record_file_dir)
+        self.load_config()
         
         '''
             关联事件
         '''
         #设备
-        self.cb_camera_devices.currentIndexChanged.connect(lambda: self.indexChangedEvent(self, self.cb_camera_devices))
-        self.cb_voice_devices.currentIndexChanged.connect(lambda: self.indexChangedEvent(self, self.cb_voice_devices))
-        self.cb_screen_devices.currentIndexChanged.connect(lambda: self.indexChangedEvent(self, self.cb_screen_devices))
-        self.cb_system_voice_devices.currentIndexChanged.connect(lambda: self.indexChangedEvent(self, self.cb_system_voice_devices))
+        self.cb_camera_devices.currentIndexChanged.connect(self.stateChangedEvent)
+        self.cb_voice_devices.currentIndexChanged.connect(self.stateChangedEvent)
+        self.cb_screen_devices.currentIndexChanged.connect(self.stateChangedEvent)
+        self.cb_system_voice_devices.currentIndexChanged.connect(self.stateChangedEvent)
         
         #快捷键
-        self.le_start_record_camera_shortcut.textChanged.connect(lambda: self.textChangedEvent(self.le_start_record_camera_shortcut))
-        self.le_start_record_screen_shortcut.textChanged.connect(lambda: self.textChangedEvent(self.le_start_record_screen_shortcut))
-        self.le_start_stop_exit_shortcut.textChanged.connect(lambda: self.textChangedEvent(self.le_start_stop_exit_shortcut))
+        self.le_start_record_camera_shortcut.textChanged.connect(self.stateChangedEvent)
+        self.le_start_record_screen_shortcut.textChanged.connect(self.stateChangedEvent)
+        self.le_start_stop_exit_shortcut.textChanged.connect(self.stateChangedEvent)
         
         #录制参数
-        self.cb_resolution.currentIndexChanged.connect(lambda: self.indexChangedEvent(self, self.cb_resolution))
-        self.cb_vcodec.currentIndexChanged.connect(lambda: self.indexChangedEvent(self, self.cb_vcodec))
-        self.dsb_frame_rate.valueChanged.connect(self.valueChangedEvent)
-        self.le_file_path.textChanged.connect(lambda: self.textChangedEvent(self.le_file_path))
+        self.cb_resolution.currentIndexChanged.connect(self.stateChangedEvent)
+        self.cb_vcodec.currentIndexChanged.connect(self.stateChangedEvent)
+        self.dsb_frame_rate.valueChanged.connect(self.stateChangedEvent)
+        self.le_file_path.textChanged.connect(self.stateChangedEvent)
+        self.threads_spinBox.valueChanged.connect(self.stateChangedEvent)
             
         # self.le_start_record_camera_shortcut.keyPressEvent = self.record_keypress
         # print(dir(self.le_start_record_camera_shortcut.keyPressEvent))
@@ -313,6 +348,10 @@ class SettingWindow(QDialog):
         
         # self.le_file_path.setText()
         self.btn_file_dir.clicked.connect(self.file_dir_select)
+        self.save_button.clicked.connect(self.save_setting)
+        self.cancel_button.clicked.connect(self.cancel)
+        
+        self.update_state()
         
     
     def record_keypress(self, event):
@@ -323,8 +362,11 @@ class SettingWindow(QDialog):
         
     def file_dir_select(self, event):
         current_dir = self.le_file_path.text()
-        if os.path.exists(current_dir):
-            selected_dir = QFileDialog.getExistingDirectory(self, '选择录像保存目录', current_dir, QFileDialog.ShowDirsOnly)
+        # print('current dir:%s' % current_dir)
+        if not os.path.exists(current_dir):
+            current_dir = os.path.abspath('.')
+        selected_dir = QFileDialog.getExistingDirectory(self, '选择录像保存目录', current_dir, QFileDialog.ShowDirsOnly)
+        if selected_dir and os.path.exists(selected_dir):
             self.le_file_path.setText(selected_dir)
         
     def showSettingWindow(self):
@@ -346,12 +388,70 @@ class SettingWindow(QDialog):
             print('parent is None?%s' % (self.parent is None))
             self.changed = False
             
-        self.setVisible(False)
-        event.ignore()
+        # self.setVisible(False)
+        # event.ignore()
     
     def save_setting(self):
+        
+        
+        #获取改动值
+        #设备
+        camera_device_name = self.cb_camera_devices.currentData()
+        voice_device_name = self.cb_voice_devices.currentData()
+        screen_device_name = self.cb_screen_devices.currentData()
+        system_voice_device_name = self.cb_system_voice_devices.currentData()
+        # print(camera_device_name)
+        
+        #快捷键
+        record_camera_key_group_name = '(' +self.le_start_record_camera_shortcut.text()+ ')'
+        record_screen_key_group_name = '(' +self.le_start_record_screen_shortcut.text()+ ')'
+        record_stop_key_group_name = '(' +self.le_start_stop_exit_shortcut.text()+ ')'
+        # print(record_camera_key_group_name)
+        
+        #录制
+        resolution = self.cb_resolution.currentData()
+        video_codec = self.cb_vcodec.currentData()
+        frame_rate = self.dsb_frame_rate.value()
+        file_dir = self.le_file_path.text()
+        threads = self.threads_spinBox.value()
+        # print(threads)
+        
+        #保存
+        conf = self.rc.config
+        
+        devices_section_name = 'devices'    
+        conf.set(devices_section_name,'camera_device_name', camera_device_name)
+        conf.set(devices_section_name,'voice_device_name', voice_device_name)
+        conf.set(devices_section_name,'screen_device_name', screen_device_name)
+        conf.set(devices_section_name,'system_voice_device_name', system_voice_device_name)
+        
+        shortcut_section_name = 'shortcut'
+        conf.set(shortcut_section_name,'camera', record_camera_key_group_name)
+        conf.set(shortcut_section_name,'screen', record_screen_key_group_name)
+        conf.set(shortcut_section_name,'stop', record_stop_key_group_name)
+        
+        record_section_name = 'record'
+        conf.set(record_section_name,'resolution', resolution)
+        conf.set(record_section_name,'vcodec', video_codec)
+        conf.set(record_section_name,'frame_rate', str(frame_rate))
+        conf.set(record_section_name,'file_dir', file_dir)
+        conf.set(record_section_name,'threads', str(threads))
+        
         self.rc.write()
+        self.update_setting.emit(self.changed)
+        self.changed = False
+        self.update_state()
     
+    def update_state(self):
+    
+        self.save_button.setDisabled(not self.changed)
+        self.cancel_button.setDisabled(not self.changed)
+        
+    def cancel(self):
+        self.load_config()
+        self.changed=False
+        self.update_state()
+        
     def setting(self, obj_name, value):
         
         section = ''
@@ -374,32 +474,47 @@ class SettingWindow(QDialog):
             pass
             print('not found this section or name.')
         
-    def indexChangedEvent(self, index, obj):
+    # def indexChangedEvent(self, index, obj):
         
         # print('in data changed event.')
-        print(index)
-        print('event obj:%s' % type(obj))
-        print('obj name:%s' % obj.objectName())
+        # print(index)
+        # print('event obj:%s' % type(obj))
+        # print('obj name:%s' % obj.objectName())
         
-        value = obj.currentData()
-        obj_name = obj.objectName()
-        self.setting(obj_name, value)
+        # value = obj.currentData()
+        # obj_name = obj.objectName()
+        # self.setting(obj_name, value)
+    def stateChangedEvent(self, new_value):
+        print('changed value is :%s' % new_value)
+        self.changed = True
+        self.update_state()
         
-    def textChangedEvent(self, obj):
+    # def textChangedEvent(self, obj):
     
         # print('type of self:%s' % type(self))
         # print('type of text:%s' % type(text))
         # print('type of obj:%s' % type(obj))
-        value = obj.text()
-        obj_name = obj.objectName()
-        self.setting(obj_name, value)
+        # value = obj.text()
+        # obj_name = obj.objectName()
+        # self.setting(obj_name, value)
         
-    def valueChangedEvent(self):
-        print('in value changed event.')
-        obj = self.dsb_frame_rate
-        value = obj.value()
-        print('value:%1.f' % value)
-        obj_name = obj.objectName()
-        self.setting(obj_name, str(value))
+    # def valueChangedEvent(self, new_value):
+        # print('changed  value is :%s.' % new_value)
+        
+        # self.changed = True
+        # self.update_state()
+        # obj = self.dsb_frame_rate
+        # value = obj.value()
+        # print('value:%1.f' % value)
+        # obj_name = obj.objectName()
+        # self.setting(obj_name, str(value))
+        
+    # def valueChangedEvent_spinBox(self):
+        # print('in value changed event.')
+        # obj = self.threads_spinBox
+        # value = obj.value()
+        # print('value:%1.f' % value)
+        # obj_name = obj.objectName()
+        # self.setting(obj_name, str(value))
         
 
diff --git a/configByLinxiao.ini b/configByLinxiao.ini
index 211af45..8853499 100644
--- a/configByLinxiao.ini
+++ b/configByLinxiao.ini
@@ -1,22 +1,22 @@
 [devices]
-camera_device_name = 
-voice_device_name = 
-screen_device_name = 
-system_voice_device_name = 
+camera_device_name = @device_pnp_\\?\usb#vid_04f2&pid_b354&mi_00#7&30d7ad30&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global
+voice_device_name = @device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{571529B3-7DB3-42A3-ADEF-BBD82925C15D}
+screen_device_name = @device_sw_{860BB310-5D01-11D0-BD3B-00A0C911CE86}\{4EA69364-2C8A-4AE6-A561-56E4B5044439}
+system_voice_device_name = @device_sw_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\{8E146464-DB61-4309-AFA1-3578E927E935}
 
 [shortcut]
-camera = 160,162,164,65
-screen = 160,162,164,66
-stop = 160,162,164,67
+camera = (160,162,164,65)
+screen = (160,162,164,66)
+stop = (160,162,164,67)
 
 [record]
-resolution = 1920x1080
+resolution = 1280x1024
 vcodec = libx264
-frame_rate = 7.0
-file_dir = .
+frame_rate = 30.0
+file_dir = C:/Users/lv/ctest/record-camera-and-screen
 threads = 4
 
 [author]
 name = linxiao
-mail = 940950943@qq.com
+mail = 940950943@qqqqqqqqqqqqqqqqqqqqqqqqqqqq.com