Exemplo n.º 1
0
class NexEventPoller(QRunnable):
    def __init__(self, device: NexDevice, poll_interval_ms: int):
        super().__init__()
        self._device = device
        self._logger = logging.getLogger("pynextion.NexEventPoller")
        self._run_poll_loop = True
        self._run_poll_loop_lock = QReadWriteLock()
        self._thread_local = threading.local()
        self._poll_interval_ms = poll_interval_ms

    def stop(self):
        self._logger.info("Stopping Nextion event poller loop")
        try:
            self._run_poll_loop_lock.lockForWrite()
            self._run_poll_loop = False
        finally:
            self._run_poll_loop_lock.unlock()

    def run(self):
        self._logger.info(
            "Starting Nextion event poller with a %d [ms] interval",
            self._poll_interval_ms)
        while True:
            try:
                self._run_poll_loop_lock.lockForRead()
                self._thread_local.run = self._run_poll_loop
            finally:
                self._run_poll_loop_lock.unlock()

            if not self._thread_local.run:
                self._logger.info("Exiting Nextion event poller loop")
                break

            self._device.poll()
            QThread.msleep(self._poll_interval_ms)
Exemplo n.º 2
0
class CSerWind(QMainWindow):
    """
    主窗口
    """
    def __init__(self, inp_host, inp_port):
        super().__init__()

        self.lock = QReadWriteLock()

        self.recordSignal = RecordSignal()
        self.recordSignal.sendSignal.connect(self.my_record)

        self.tcpServer = TcpServer(self.recordSignal, self.lock)
        if not self.tcpServer.listen(QHostAddress(inp_host), inp_port):
            QMessageBox.critical(
                self, '交易服务器',
                '服务器启动失败:{0}'.format(self.tcpServer.errorString()))
            rec_text = my_cur_time() + ' 服务器启动失败!'
            try:
                self.lock.lockForWrite()
                self.recordSignal.sendSignal.emit(rec_text)
            finally:
                self.lock.unlock()
            self.close()
            return
        else:
            self._initUI()
            rec_text = my_cur_time() + ' 开启交易服务器!'
            try:
                self.lock.lockForWrite()
                self.recordSignal.sendSignal.emit(rec_text)
            finally:
                self.lock.unlock()

    def _initUI(self):
        self.setFixedSize(600, 400)
        self.move(0, 60)
        self.setWindowTitle('服务器:交易跟单——复制信号')
        self.setWindowIcon(QIcon('myIcon.ico'))

        # TableView设置MT4账号及Files文件夹路径
        self.model = QStandardItemModel(2, 2)
        self.model.setHorizontalHeaderLabels(['MT4账号', 'Files文件夹路径'])
        row = 0
        for key, value in account_dir.items():
            self.model.setItem(row, 0, QStandardItem(key))
            self.model.setItem(row, 1, QStandardItem(value))
            row += 1
        self.table = QTableView(self)
        self.table.setModel(self.model)
        self.table.resize(500, 180)
        self.table.move(10, 15)
        self.table.horizontalHeader().setStretchLastSection(True)

        tbn_append = QPushButton('添 加', self)
        tbn_append.resize(70, 25)
        tbn_append.move(520, 30)
        tbn_append.clicked.connect(self.my_btn_append_clicked)
        btn_delete = QPushButton('删 除', self)
        btn_delete.resize(70, 25)
        btn_delete.move(520, 90)
        btn_delete.clicked.connect(self.my_btn_delete_clicked)
        btn_save = QPushButton('保 存', self)
        btn_save.resize(70, 25)
        btn_save.move(520, 150)
        btn_save.clicked.connect(self.my_btn_save_clicked)

        # 逐条显示交易服务器操作的每个记录
        self.text_browser = QTextBrowser(self)
        self.text_browser.resize(580, 180)
        self.text_browser.move(10, 210)

        self.show()

    def closeEvent(self, event):
        reply = QMessageBox.question(self, '操作提示!', '您确定要关闭“交易服务器”?',
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            rec_text = my_cur_time() + ' 关闭交易服务器!'
            try:
                self.lock.lockForWrite()
                self.recordSignal.sendSignal.emit(rec_text)
            finally:
                self.lock.unlock()
            event.accept()
        else:
            event.ignore()

    def keyPressEvent(self, e):
        if e.key() == Qt.Key_Escape:
            self.showMinimized()

    def my_btn_append_clicked(self):
        """
        增加一个MT4账户
        """
        self.model.appendRow([QStandardItem(''), QStandardItem('')])

    def my_btn_delete_clicked(self):
        """
        删除当前行的MT4账号信息
        """
        reply = QMessageBox.question(self, '操作提示!', '确定要删除这条数据?',
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            index = self.table.currentIndex()
            self.model.removeRow(index.row())

    def my_btn_save_clicked(self):
        """
        保存MT4账户信息到account_dir.txt文件中
        """
        reply = QMessageBox.question(self, '操作提示!', '确定要覆盖原有数据?',
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            account_dir_new = {}
            rows = self.model.rowCount()
            for row in range(rows):
                key = self.model.item(row, 0).text()
                # 对MT4账号进行正则表达式匹配检查
                if not re.match(r'^[1-9]\d+$', key):
                    QMessageBox.critical(
                        self, '错误提示!',
                        self.tr('第 %s 行MT4账号格式不对!' % str(row + 1)))
                    return
                value = self.model.item(row, 1).text()
                if not os.path.exists(value) or value.find('\MQL4\Files') < 0:
                    QMessageBox.critical(
                        self, '错误提示!',
                        self.tr('第 %s 行Files文件路径不对!' % str(row + 1)))
                    return
                account_dir_new[key] = value
            # 保存文件
            try:
                account_dir = account_dir_new
                with open('account_dir.txt', 'w') as file_object:
                    file_object.write(str(account_dir))
            except Exception as e:
                QMessageBox.critical(
                    self, '错误提示!',
                    self.tr('保存MT4账户信息出现错误!{0}'.format(str(Exception))))
            # 记录这一操作
            rec_text = my_cur_time() + ' 已经保存了当前列表中MT4账户信息!'
            try:
                self.lock.lockForWrite()
                self.recordSignal.sendSignal.emit(rec_text)
            finally:
                self.lock.unlock()

    def my_record(self, inp_text):
        """
        记录服务器操作日志保存到日志文件,并在主窗口TextBrowser组件中显示出来
        """
        now = time.localtime()
        file_name = 'ser_' + time.strftime('%Y-%m-%d', now) + '.txt'
        rec_text = inp_text + '\n'

        with open(file_name, 'a+', encoding='UTF-8') as file_object:
            file_object.write(rec_text)

        # text_browser只显示当天的操作记录
        pre_text = self.text_browser.toPlainText()
        if len(pre_text) > 0 and pre_text[0:10] != inp_text[0:10]:
            self.text_browser.setText('')
        self.text_browser.append(inp_text)
Exemplo n.º 3
0
class Form(QDialog):
    def __init__(self, parent=None):
        super(Form, self).__init__(parent)

        self.mutex = QMutex()
        self.fileCount = 0
        self.filenamesForWords = collections.defaultdict(set)
        self.commonWords = set()
        self.lock = QReadWriteLock()
        self.path = QDir.homePath()
        pathLabel = QLabel("Indexing path:")
        self.pathLabel = QLabel()
        self.pathLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.pathButton = QPushButton("Set &Path...")
        self.pathButton.setAutoDefault(False)
        findLabel = QLabel("&Find word:")
        self.findEdit = QLineEdit()
        findLabel.setBuddy(self.findEdit)
        commonWordsLabel = QLabel("&Common words:")
        self.commonWordsListWidget = QListWidget()
        commonWordsLabel.setBuddy(self.commonWordsListWidget)
        filesLabel = QLabel("Files containing the &word:")
        self.filesListWidget = QListWidget()
        filesLabel.setBuddy(self.filesListWidget)
        filesIndexedLabel = QLabel("Files indexed")
        self.filesIndexedLCD = QLCDNumber()
        self.filesIndexedLCD.setSegmentStyle(QLCDNumber.Flat)
        wordsIndexedLabel = QLabel("Words indexed")
        self.wordsIndexedLCD = QLCDNumber()
        self.wordsIndexedLCD.setSegmentStyle(QLCDNumber.Flat)
        commonWordsLCDLabel = QLabel("Common words")
        self.commonWordsLCD = QLCDNumber()
        self.commonWordsLCD.setSegmentStyle(QLCDNumber.Flat)
        self.statusLabel = QLabel("Click the 'Set Path' "
                                  "button to start indexing")
        self.statusLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)

        topLayout = QHBoxLayout()
        topLayout.addWidget(pathLabel)
        topLayout.addWidget(self.pathLabel, 1)
        topLayout.addWidget(self.pathButton)
        topLayout.addWidget(findLabel)
        topLayout.addWidget(self.findEdit, 1)
        leftLayout = QVBoxLayout()
        leftLayout.addWidget(filesLabel)
        leftLayout.addWidget(self.filesListWidget)
        rightLayout = QVBoxLayout()
        rightLayout.addWidget(commonWordsLabel)
        rightLayout.addWidget(self.commonWordsListWidget)
        middleLayout = QHBoxLayout()
        middleLayout.addLayout(leftLayout, 1)
        middleLayout.addLayout(rightLayout)
        bottomLayout = QHBoxLayout()
        bottomLayout.addWidget(filesIndexedLabel)
        bottomLayout.addWidget(self.filesIndexedLCD)
        bottomLayout.addWidget(wordsIndexedLabel)
        bottomLayout.addWidget(self.wordsIndexedLCD)
        bottomLayout.addWidget(commonWordsLCDLabel)
        bottomLayout.addWidget(self.commonWordsLCD)
        bottomLayout.addStretch()
        layout = QVBoxLayout()
        layout.addLayout(topLayout)
        layout.addLayout(middleLayout)
        layout.addLayout(bottomLayout)
        layout.addWidget(self.statusLabel)
        self.setLayout(layout)

        self.walkers = []
        self.completed = []
        self.pathButton.clicked.connect(self.setPath)
        self.findEdit.returnPressed.connect(self.find)
        self.setWindowTitle("Page Indexer")

    def stopWalkers(self):
        for walker in self.walkers:
            if isAlive(walker) and walker.isRunning():
                walker.stop()
        for walker in self.walkers:
            if isAlive(walker) and walker.isRunning():
                walker.wait()
        self.walkers = []
        self.completed = []

    def setPath(self):
        self.stopWalkers()
        self.pathButton.setEnabled(False)
        path = QFileDialog.getExistingDirectory(self, "Choose a Path to Index",
                                                self.path)
        if not path:
            self.statusLabel.setText("Click the 'Set Path' "
                                     "button to start indexing")
            self.pathButton.setEnabled(True)
            return
        self.statusLabel.setText("Scanning directories...")
        QApplication.processEvents()  # Needed for Windows
        self.path = QDir.toNativeSeparators(path)
        self.findEdit.setFocus()
        self.pathLabel.setText(self.path)
        self.statusLabel.clear()
        self.filesListWidget.clear()
        self.fileCount = 0
        self.filenamesForWords = collections.defaultdict(set)
        self.commonWords = set()
        nofilesfound = True
        files = []
        index = 0
        for root, dirs, fnames in os.walk(str(self.path)):
            for name in [
                    name for name in fnames if name.endswith((".htm", ".html"))
            ]:
                files.append(os.path.join(root, name))
                if len(files) == 1000:
                    self.processFiles(index, files[:])
                    files = []
                    index += 1
                    nofilesfound = False
        if files:
            self.processFiles(index, files[:])
            nofilesfound = False
        if nofilesfound:
            self.finishedIndexing()
            self.statusLabel.setText("No HTML files found in the given path")

    def processFiles(self, index, files):
        thread = walker.Walker(index, self.lock, files, self.filenamesForWords,
                               self.commonWords, self)
        thread.indexed[str, int].connect(self.indexed)
        thread.finished[bool, int].connect(self.finished)
        thread.finished.connect(thread.deleteLater)
        self.walkers.append(thread)
        self.completed.append(False)
        thread.start()
        thread.wait(300)  # Needed for Windows

    def find(self):
        word = str(self.findEdit.text())
        if not word:
            try:
                self.mutex.lock()
                self.statusLabel.setText("Enter a word to find in files")
            finally:
                self.mutex.unlock()
            return
        try:
            self.mutex.lock()
            self.statusLabel.clear()
            self.filesListWidget.clear()
        finally:
            self.mutex.unlock()
        word = word.lower()
        if " " in word:
            word = word.split()[0]
        try:
            self.lock.lockForRead()
            found = word in self.commonWords
        finally:
            self.lock.unlock()
        if found:
            try:
                self.mutex.lock()
                self.statusLabel.setText("Common words like '{0}' "
                                         "are not indexed".format(word))
            finally:
                self.mutex.unlock()
            return
        try:
            self.lock.lockForRead()
            files = self.filenamesForWords.get(word, set()).copy()
        finally:
            self.lock.unlock()
        if not files:
            try:
                self.mutex.lock()
                self.statusLabel.setText("No indexed file contains "
                                         "the word '{0}'".format(word))
            finally:
                self.mutex.unlock()
            return
        files = [
            QDir.toNativeSeparators(name)
            for name in sorted(files, key=str.lower)
        ]
        try:
            self.mutex.lock()
            self.filesListWidget.addItems(files)
            self.statusLabel.setText(
                "{0} indexed files contain the word '{1}'".format(
                    len(files), word))
        finally:
            self.mutex.unlock()

    def indexed(self, fname, index):
        try:
            self.mutex.lock()
            self.statusLabel.setText(fname)
            self.fileCount += 1
            count = self.fileCount
        finally:
            self.mutex.unlock()
        if count % 25 == 0:
            try:
                self.lock.lockForRead()
                indexedWordCount = len(self.filenamesForWords)
                commonWordCount = len(self.commonWords)
            finally:
                self.lock.unlock()
            try:
                self.mutex.lock()
                self.filesIndexedLCD.display(count)
                self.wordsIndexedLCD.display(indexedWordCount)
                self.commonWordsLCD.display(commonWordCount)
            finally:
                self.mutex.unlock()
        elif count % 101 == 0:
            try:
                self.lock.lockForRead()
                words = self.commonWords.copy()
            finally:
                self.lock.unlock()
            try:
                self.mutex.lock()
                self.commonWordsListWidget.clear()
                self.commonWordsListWidget.addItems(sorted(words))
            finally:
                self.mutex.unlock()

    def finished(self, completed, index):
        done = False
        if self.walkers:
            self.completed[index] = True
            if all(self.completed):
                try:
                    self.mutex.lock()
                    self.statusLabel.setText("Finished")
                    done = True
                finally:
                    self.mutex.unlock()
        else:
            try:
                self.mutex.lock()
                self.statusLabel.setText("Finished")
                done = True
            finally:
                self.mutex.unlock()
        if done:
            self.finishedIndexing()

    def reject(self):
        if not all(self.completed):
            self.stopWalkers()
            self.finishedIndexing()
        else:
            self.accept()

    def closeEvent(self, event=None):
        self.stopWalkers()

    def finishedIndexing(self):
        self.filesIndexedLCD.display(self.fileCount)
        self.wordsIndexedLCD.display(len(self.filenamesForWords))
        self.commonWordsLCD.display(len(self.commonWords))
        self.pathButton.setEnabled(True)
        QApplication.processEvents()  # Needed for Windows
Exemplo n.º 4
0
class __Config(object):
    def __init__(self, debug=False):
        self.debug = debug
        self._dict = dict()
        self._prepare_dir()
        self._remove_tmps()
        self._lock = QReadWriteLock()

    def __getitem__(self, key):
        self._lock.lockForRead()
        r = self._dict[key]
        self._lock.unlock()
        return r

    def __setitem__(self, key, value):
        self._lock.lockForWrite()
        self._dict[key] = value
        self._lock.unlock()

    def get(self, key, default=None):
        self._lock.lockForRead()
        r = self._dict.get(key, default)
        self._lock.unlock()
        return r

    def pop(self, key, default=None):
        self._lock.lockForWrite()
        r = self._dict.pop(key, default)
        self._lock.unlock()
        return r

    def __contains__(self, key):
        self._lock.lockForRead()
        r = self._dict.__contains__(key)
        self._lock.unlock()
        return r

    def __str__(self):
        self._lock.lockForRead()
        s = "".join(
            [
                "Config(",
                ", ".join("{0}: {1}".format(k, v) for (k, v) in self._dict),
                ")",
            ]
        )
        self._lock.unlock()
        return s

    @property
    def _config_dir(self):
        # Windows
        if sys.platform.startswith("win"):
            if "LOCALAPPDATA" in os.environ:
                return os.path.join(os.environ["LOCALAPPDATA"], "LDOCE5Viewer")
            else:
                return os.path.join(os.environ["APPDATA"], "LDOCE5Viewer")
        # Mac OS X
        elif sys.platform.startswith("darwin"):
            return os.path.expanduser("~/Library/Application Support/LDOCE5Viewer")
        # Linux
        else:
            base = os.path.join(os.path.expanduser("~"), ".config")
            # XDG
            try:
                import xdg.BaseDirectory

                base = xdg.BaseDirectory.xdg_config_home
            except ImportError:
                if "XDG_CONFIG_HOME" in os.environ:
                    base = os.environ["XDG_CONFIG_HOME"]
            return os.path.join(base, "ldoce5viewer")

    @property
    def _data_dir(self):
        # Windows
        if sys.platform.startswith("win"):
            return self._config_dir
        # Mac OS X
        elif sys.platform.startswith("darwin"):
            return self._config_dir
        # Linux
        else:
            base = os.path.join(os.path.expanduser("~"), ".local/share/")
            # XDG
            try:
                import xdg.BaseDirectory

                base = xdg.BaseDirectory.xdg_data_home
            except ImportError:
                if "XDG_DATA_HOME" in os.environ:
                    base = os.environ["XDG_DATA_HOME"]
            return os.path.join(base, "ldoce5viewer")

    @property
    def app_name(self):
        return "LDOCE5 Viewer"

    @property
    def _config_path(self):
        return os.path.join(self._config_dir, "config.pickle")

    @property
    def filemap_path(self):
        return os.path.join(self._data_dir, "filemap.cdb")

    @property
    def variations_path(self):
        return os.path.join(self._data_dir, "variations.cdb")

    @property
    def incremental_path(self):
        return os.path.join(self._data_dir, "incremental.db")

    @property
    def fulltext_hwdphr_path(self):
        return os.path.join(self._data_dir, "fulltext_hp")

    @property
    def fulltext_defexa_path(self):
        return os.path.join(self._data_dir, "fulltext_de")

    @property
    def scan_tmp_path(self):
        return os.path.join(self._data_dir, "scan" + self.tmp_suffix)

    @property
    def tmp_suffix(self):
        return ".tmp"

    def _remove_tmps(self):
        for name in os.listdir(self._config_dir) + os.listdir(self._data_dir):
            if name.endswith(self.tmp_suffix):
                path = os.path.join(self._config_dir, name)
                try:
                    if os.path.isfile(path):
                        os.remove(path)
                    elif os.path.isdir(path):
                        shutil.rmtree(path)
                except OSError:
                    pass

    def _prepare_dir(self):
        if not os.path.exists(self._config_dir):
            os.makedirs(self._config_dir)
        if not os.path.exists(self._data_dir):
            os.makedirs(self._data_dir)

    def load(self):
        self._lock.lockForWrite()
        try:
            with open(self._config_path, "rb") as f:
                self._dict.clear()
                try:
                    data = pickle.load(f)
                except:
                    pass
                else:
                    self._dict.update(data)
        except IOError:
            self._dict.clear()
        self._lock.unlock()

    def save(self):
        self._lock.lockForRead()

        if sys.platform == "win32":
            with open(self._config_path, "wb") as f:
                pickle.dump(self._dict, f)
        else:
            f = tempfile.NamedTemporaryFile(
                dir=self._config_dir, delete=False, suffix=self.tmp_suffix
            )
            pickle.dump(self._dict, f, protocol=0)
            f.close()
            os.rename(f.name, self._config_path)

        self._lock.unlock()
Exemplo n.º 5
0
class __Config(object):
    def __init__(self, debug=False):
        self.debug = debug
        self._dict = dict()
        self._prepare_dir()
        self._remove_tmps()
        self._lock = QReadWriteLock()

    def __getitem__(self, key):
        self._lock.lockForRead()
        r = self._dict[key]
        self._lock.unlock()
        return r

    def __setitem__(self, key, value):
        self._lock.lockForWrite()
        self._dict[key] = value
        self._lock.unlock()

    def get(self, key, default=None):
        self._lock.lockForRead()
        r = self._dict.get(key, default)
        self._lock.unlock()
        return r

    def pop(self, key, default=None):
        self._lock.lockForWrite()
        r = self._dict.pop(key, default)
        self._lock.unlock()
        return r

    def __contains__(self, key):
        self._lock.lockForRead()
        r = self._dict.__contains__(key)
        self._lock.unlock()
        return r

    def __str__(self):
        self._lock.lockForRead()
        s = ''.join([
            'Config(',
            ', '.join("{0}: {1}".format(k, v) for (k, v) in self._dict), ')'
        ])
        self._lock.unlock()
        return s

    @property
    def _config_dir(self):
        # Windows
        if sys.platform.startswith('win'):
            if 'LOCALAPPDATA' in os.environ:
                return os.path.join(os.environ['LOCALAPPDATA'], 'LDOCE5Viewer')
            else:
                return os.path.join(os.environ['APPDATA'], 'LDOCE5Viewer')
        # Mac OS X
        elif sys.platform.startswith('darwin'):
            return os.path.expanduser(
                '~/Library/Application Support/LDOCE5Viewer')
        # Linux
        else:
            base = os.path.join(os.path.expanduser('~'), '.config')
            # XDG
            try:
                import xdg.BaseDirectory
                base = xdg.BaseDirectory.xdg_config_home
            except ImportError:
                if 'XDG_CONFIG_HOME' in os.environ:
                    base = os.environ['XDG_CONFIG_HOME']
            return os.path.join(base, 'ldoce5viewer')

    @property
    def _data_dir(self):
        # Windows
        if sys.platform.startswith('win'):
            return self._config_dir
        # Mac OS X
        elif sys.platform.startswith('darwin'):
            return self._config_dir
        # Linux
        else:
            base = os.path.join(os.path.expanduser('~'), '.local/share/')
            # XDG
            try:
                import xdg.BaseDirectory
                base = xdg.BaseDirectory.xdg_data_home
            except ImportError:
                if 'XDG_DATA_HOME' in os.environ:
                    base = os.environ['XDG_DATA_HOME']
            return os.path.join(base, 'ldoce5viewer')

    @property
    def app_name(self):
        return 'LDOCE5 Viewer'

    @property
    def _config_path(self):
        return os.path.join(self._config_dir, 'config.pickle')

    @property
    def filemap_path(self):
        return os.path.join(self._data_dir, 'filemap.cdb')

    @property
    def variations_path(self):
        return os.path.join(self._data_dir, 'variations.cdb')

    @property
    def incremental_path(self):
        return os.path.join(self._data_dir, 'incremental.db')

    @property
    def fulltext_hwdphr_path(self):
        return os.path.join(self._data_dir, 'fulltext_hp')

    @property
    def fulltext_defexa_path(self):
        return os.path.join(self._data_dir, 'fulltext_de')

    @property
    def scan_tmp_path(self):
        return os.path.join(self._data_dir, 'scan' + self.tmp_suffix)

    @property
    def tmp_suffix(self):
        return '.tmp'

    def _remove_tmps(self):
        for name in os.listdir(self._config_dir) + os.listdir(self._data_dir):
            if name.endswith(self.tmp_suffix):
                path = os.path.join(self._config_dir, name)
                try:
                    if os.path.isfile(path):
                        os.remove(path)
                    elif os.path.isdir(path):
                        shutil.rmtree(path)
                except OSError:
                    pass

    def _prepare_dir(self):
        if not os.path.exists(self._config_dir):
            os.makedirs(self._config_dir)
        if not os.path.exists(self._data_dir):
            os.makedirs(self._data_dir)

    def load(self):
        self._lock.lockForWrite()
        try:
            with open(self._config_path, 'rb') as f:
                self._dict.clear()
                try:
                    data = pickle.load(f)
                except:
                    pass
                else:
                    self._dict.update(data)
        except IOError:
            self._dict.clear()
        self._lock.unlock()

    def save(self):
        self._lock.lockForRead()

        if sys.platform == 'win32':
            with open(self._config_path, 'wb') as f:
                pickle.dump(self._dict, f)
        else:
            f = tempfile.NamedTemporaryFile(dir=self._config_dir,
                                            delete=False,
                                            suffix=self.tmp_suffix)
            pickle.dump(self._dict, f, protocol=0)
            f.close()
            os.rename(f.name, self._config_path)

        self._lock.unlock()