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)
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)
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
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()
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()