add paradigm
BIN
Model/挡板.3mf
BIN
Model/脑电帽.3mf
BIN
Model/设备外壳.3mf
195
Paradigm/Multi_Language.py
Normal file
@ -0,0 +1,195 @@
|
||||
import nltk
|
||||
from nltk.corpus import brown, cess_esp, europarl_raw
|
||||
from nltk.tokenize import word_tokenize
|
||||
from collections import Counter
|
||||
from janome.tokenizer import Tokenizer
|
||||
from konlpy.tag import Okt
|
||||
import pickle
|
||||
from ctypes import *
|
||||
import os
|
||||
import pykakasi
|
||||
|
||||
|
||||
def tokenize_japanese(sentences):
|
||||
tokenizer = Tokenizer()
|
||||
return [tokenizer.tokenize(sentence, wakati=True) for sentence in sentences]
|
||||
|
||||
|
||||
def tokenize_korean(sentences):
|
||||
tokenizer = Okt()
|
||||
return [tokenizer.morphs(sentence) for sentence in sentences]
|
||||
|
||||
def save_word_freqs(word_freqs, filename):
|
||||
with open(filename, 'wb') as f:
|
||||
pickle.dump(word_freqs, f)
|
||||
|
||||
def load_word_freqs(filename):
|
||||
try:
|
||||
with open(filename, 'rb') as f:
|
||||
return pickle.load(f)
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
|
||||
def load_europarl_sentences(language):
|
||||
language_to_corpus = {
|
||||
'french': europarl_raw.french.sents,
|
||||
'german': europarl_raw.german.sents,
|
||||
'danish': europarl_raw.danish.sents,
|
||||
'dutch': europarl_raw.dutch.sents,
|
||||
'english': europarl_raw.english.sents,
|
||||
'finnish': europarl_raw.finnish.sents,
|
||||
'greek': europarl_raw.greek.sents,
|
||||
'italian': europarl_raw.italian.sents,
|
||||
'portuguese': europarl_raw.portuguese.sents,
|
||||
'spanish': europarl_raw.spanish.sents,
|
||||
'swedish': europarl_raw.swedish.sents
|
||||
}
|
||||
corpus_function = language_to_corpus.get(language, lambda: [])
|
||||
return corpus_function()
|
||||
|
||||
def load_sentences(file_path):
|
||||
with open(file_path, 'r', encoding='utf-8') as file:
|
||||
sentences = file.readlines()
|
||||
return [sentence.strip() for sentence in sentences]
|
||||
|
||||
def romaji_to_hiragana(romaji):
|
||||
kakasi = pykakasi.kakasi()
|
||||
kakasi.setMode('H', 'a') # Set to convert from Hiragana to Romaji
|
||||
converter = kakasi.getConverter()
|
||||
return converter.do(romaji)
|
||||
|
||||
def kana_to_romaji(text):
|
||||
kakasi = pykakasi.kakasi()
|
||||
kakasi.setMode('H', 'a') # H: Hiragana, a: Romaji
|
||||
kakasi.setMode('K', 'a') # K: Katakana, a: Romaji
|
||||
converter = kakasi.getConverter()
|
||||
return converter.do(text)
|
||||
|
||||
class CN_Class:
|
||||
def __init__(self, max_spell_len=100, max_out_len=50):
|
||||
# 加载 DLL
|
||||
self.lib = CDLL('./MyQtClass.dll')
|
||||
# 定义返回类型和参数类型
|
||||
self.lib.MyQtClass_new.restype = c_void_p
|
||||
self.lib.MyQtClass_delete.argtypes = [c_void_p]
|
||||
self.lib.MyQtClass_init.argtypes = [c_void_p, c_int, c_int]
|
||||
self.lib.MyQtClass_init.restype = c_bool
|
||||
self.lib.MyQtClass_deinit.argtypes = [c_void_p]
|
||||
self.lib.MyQtClass_search.argtypes = [c_void_p, c_char_p]
|
||||
self.lib.MyQtClass_search.restype = c_uint
|
||||
self.lib.MyQtClass_get_candidate.argtypes = [c_void_p, c_uint]
|
||||
self.lib.MyQtClass_get_candidate.restype = POINTER(c_char_p)
|
||||
self.lib.MyQtClass_free_results.argtypes = [c_void_p, POINTER(c_char_p), c_uint]
|
||||
self.obj = self.lib.MyQtClass_new()
|
||||
if not self.lib.MyQtClass_init(self.obj, max_spell_len, max_out_len):
|
||||
raise Exception("初始化失败!")
|
||||
|
||||
def search(self, spell):
|
||||
try:
|
||||
result_count = self.lib.MyQtClass_search(self.obj, spell.encode('utf-8'))
|
||||
if result_count == 0:
|
||||
return []
|
||||
results = self.lib.MyQtClass_get_candidate(self.obj, result_count)
|
||||
candidate_list = [results[i].decode('utf-8') for i in range(result_count)]
|
||||
self.lib.MyQtClass_free_results(self.obj, results, result_count)
|
||||
return candidate_list
|
||||
except Exception as e:
|
||||
print(f"错误异常反馈: {e}")
|
||||
return []
|
||||
|
||||
def deinit(self):
|
||||
self.lib.MyQtClass_deinit(self.obj)
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
self.deinit()
|
||||
self.lib.MyQtClass_delete(self.obj)
|
||||
except Exception as e:
|
||||
print(f"清空内存失败: {e}")
|
||||
|
||||
class MultiLangAutoComplete:
|
||||
def __init__(self, load_filename=None):
|
||||
# 加载欧洲语言
|
||||
self.languages = [
|
||||
'french', 'german', 'danish', 'dutch', 'english',
|
||||
'finnish', 'greek', 'italian', 'portuguese', 'spanish', 'swedish'
|
||||
]
|
||||
self.words = {lang: Counter() for lang in self.languages}
|
||||
|
||||
# 加载中文
|
||||
self.cn = CN_Class()
|
||||
|
||||
# 加载日语
|
||||
self.words.update({'japanese': Counter()})
|
||||
japanese_sentences = load_sentences('japan.txt')
|
||||
self.tokenized_japanese = tokenize_japanese(japanese_sentences)
|
||||
# self.words.update({'korean': Counter()})
|
||||
# korean_sentences = load_sentences('path_to_korean.txt')
|
||||
# tokenized_korean = tokenize_korean(korean_sentences)
|
||||
|
||||
if load_filename:
|
||||
loaded_words = load_word_freqs(load_filename)
|
||||
if loaded_words:
|
||||
self.words = loaded_words
|
||||
else:
|
||||
self.initialize_corpora()
|
||||
else:
|
||||
self.initialize_corpora()
|
||||
|
||||
def initialize_corpora(self):
|
||||
self.train_corpus('english', brown.sents())
|
||||
self.train_corpus('spanish', cess_esp.sents())
|
||||
self.train_corpus('japanese', self.tokenized_japanese)
|
||||
# 欧洲数据集
|
||||
for language in self.languages:
|
||||
sentences = load_europarl_sentences(language)
|
||||
self.train_corpus(language, sentences)
|
||||
|
||||
def train_corpus(self, language, sentences):
|
||||
for sentence in sentences:
|
||||
words = [word.lower() for word in sentence if isinstance(word, str)]
|
||||
self.words[language].update(words)
|
||||
|
||||
def suggest(self, language, prefix, n_suggestions=5):
|
||||
suggestions = {word: freq for word, freq in self.words[language].items() if word.startswith(prefix)}
|
||||
sorted_suggestions = sorted(suggestions, key=suggestions.get, reverse=True)
|
||||
return sorted_suggestions[:n_suggestions]
|
||||
|
||||
def save(self, filename):
|
||||
save_word_freqs(self.words, filename)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 加载已有的字词库或创建新的
|
||||
auto_complete = MultiLangAutoComplete('autocomplete_data.pkl')
|
||||
|
||||
# 添加一些新的文本到英语字词库
|
||||
text = "Hello GPT, hello GPT, zhangyutao is a great programmer. Zyjacya in love, I Zyjacya in love with a girl"
|
||||
words = word_tokenize(text.lower())
|
||||
auto_complete.train_corpus('english', [words])
|
||||
|
||||
# 使用联想输入功能
|
||||
# print(auto_complete.suggest('korean', '안')) # Korean suggestions
|
||||
# 示例
|
||||
# hiragana_text = "こんにちは"
|
||||
# romaji_result = kana_to_romaji(hiragana_text)
|
||||
# print("平假名:", hiragana_text)
|
||||
# print("罗马音:", romaji_result)
|
||||
# romaji_text = "kon"
|
||||
# hiragana_result = romaji_to_hiragana(romaji_text)
|
||||
# print("罗马字:", romaji_text)
|
||||
# print("平假名:", hiragana_result)
|
||||
# print('japan:' + str(auto_complete.suggest('japanese', hiragana_result))) # Japanese suggestions
|
||||
|
||||
# 加载中文
|
||||
try:
|
||||
candidates = auto_complete.cn.search("nihao")
|
||||
print("中文:", candidates)
|
||||
except Exception as e:
|
||||
print(f"错误异常反馈: {e}")
|
||||
|
||||
for index in auto_complete.languages:
|
||||
print(str(index) + ":" + str(auto_complete.suggest(index, 'gp'))) # Japanese suggestions
|
||||
|
||||
# 保存字词库的状态
|
||||
auto_complete.save('autocomplete_data.pkl')
|
||||
|
BIN
Paradigm/MyQtClass.dll
Normal file
BIN
Paradigm/MyQtClass.exp
Normal file
BIN
Paradigm/MyQtClass.lib
Normal file
BIN
Paradigm/__pycache__/Multi_Language.cpython-38.pyc
Normal file
BIN
Paradigm/autocomplete_data.pkl
Normal file
1
Paradigm/coordinates.json
Normal file
BIN
Paradigm/dict_pinyin.dat
Normal file
BIN
Paradigm/dict_pinyin_user.dat
Normal file
459
Paradigm/japan.txt
Normal file
@ -0,0 +1,459 @@
|
||||
合縁奇縁
|
||||
佐々木邦
|
||||
|
||||
-------------------------------------------------------
|
||||
【テキスト中に現れる記号について】
|
||||
|
||||
《》:ルビ
|
||||
(例)瑞竜《ずいりゅう》
|
||||
|
||||
|:ルビの付く文字列の始まりを特定する記号
|
||||
(例)娘|錦子《きんこ》
|
||||
|
||||
[#]:入力者注 主に外字の説明や、傍点の位置の指定
|
||||
(数字は、JIS X 0213の面区点番号またはUnicode、底本のページと行数)
|
||||
(例)※[#二の字点、1-2-22]
|
||||
|
||||
/\:二倍の踊り字(「く」を縦に長くしたような形の繰り返し記号)
|
||||
(例)ニコ/\
|
||||
-------------------------------------------------------
|
||||
|
||||
[#4字下げ]第一回の失敗[#「第一回の失敗」は中見出し]
|
||||
|
||||
「瑞竜《ずいりゅう》、お前は養子に行く気はないか? 相手にもよりけりだろうが、随明寺《ずいみょうじ》なら申分あるまい?」
|
||||
と兄貴がニコ/\して切り出した。さては来たなと僕は思った。随明寺の総領娘|錦子《きんこ》さんはナカ/\綺麗な子だった。此方が又、自慢ではないが、秀才の誉れ高かった。その辺は寺町といって、お寺ばかり十何軒並んでいるから、皆お互に見知り越しだった。中学生と女学生だから親しい面晤《めんご》はなかったが、僕は途上でチョッカイをかけたことがある。
|
||||
「もし/\。ハンカチが落ちましたよ」
|
||||
学校の帰りに擦れ違った時、注意してやった。錦子さんは振り返ったが、嘘と分って、
|
||||
「まあ! 不良さんね、イヽン」
|
||||
と言って、行ってしまった。以来僕を見かけると、空嘯《そらうそぶ》くようにして通って行く。
|
||||
さて、その錦子さんのところへ婿養子に貰われて行く問題だ。
|
||||
「今日東光寺さんが見えて、お互に身許が分っているから丁度好い縁談だと思うが、何《ど》んなものだろうと言う」
|
||||
兄貴は僕の無条件承諾を期待しているように浴びせかけた。
|
||||
「随明寺へ行けば、矢っ張りお寺を継ぐんでしょう?」
|
||||
「それは当り前だ。その為めの養子だから」
|
||||
「僕、坊主は厭です」
|
||||
「こら、坊主とは何だ?」
|
||||
「お坊さんは嫌いです」
|
||||
「何も分らないで、未だ贅沢を言っているのか?」
|
||||
「…………」
|
||||
この問答でも分る通り、僕のところはお寺だ。しかし僕は既に坊さんになりたくないことを言明していた。それに対して、兄貴は坊さんが一番間違いないから考え直せと言っていたのだった。僕は別に望みがあった。大学へ行って法科をやって官吏になりたいと思っていた。
|
||||
「すると何うする?」
|
||||
「大学へ行きたいんです」
|
||||
「しかし学資は出せないよ。お前も承知の通り、家は貧乏寺だから」
|
||||
「…………」
|
||||
「随明寺へ養子に行けば、大学へやって貰える。法科はいけないけれど、哲学を勉強すれば、先生になれる。官立学校の先生なら官吏だぞ」
|
||||
「帝大へやってくれると先方《むこう》で言うんですか?」
|
||||
「修業は充分させてくれるさ。元来お前の成績を聞き知って懇望《こんもう》するんだから、その辺は何うにでも話をつけてやる」
|
||||
「哲学以外はいけないでしょうか?」
|
||||
「無論さ。お寺を継ぐんだから、哲学も東洋哲学に限る」
|
||||
「お寺を継げば教授になれますまい?」
|
||||
「それは些《ち》っとむずかしいだろうな、豪い学者になってしまえば兎に角、初めからは」
|
||||
「…………」
|
||||
「家は貧乏だ。学資を出したいにも、ない袖は振られない。家からは絶対に上級学校へ行けないと思ってくれなければ困る」
|
||||
「はあ」
|
||||
僕は大分心が動いた。兎に角帝大へ行けるということは大きな光明だった。それから錦子さんと夫婦になれると思うと、無暗に嬉しかった。坊主だって、そんなに厭なものでない。第一、暇だ。それでいて、就職難ということが絶対にない。一切|食役《くうやく》の保証がついている。兄貴の生活を見るに、酒も飲むし、歌も唄うし、トンカツも食うし、夫婦揃って活動も見に行く。俗人と些っとも異《ことな》るところがない。僕は坊さんの好いところを列挙して、兄貴二人がやっているんだから、矢張りやろうかと考えた。
|
||||
その翌日だったか翌々日だったか、学校の帰りに偶然錦子さんと擦れ違った。錦子さんはいつものように空嘯かず、真赤になって立ち止まって、丁寧にお辞儀をして行った。僕はその刹那に決心を固めた。
|
||||
「よし、随明寺へ婿養子に行って、名僧智識になってやろう」と。
|
||||
帰って早速兄貴に喜んで貰おうと思ったら、お客さんが来ていた。檀家《だんか》で一番金持の松本さんだった。僕も挨拶に出て坐り込んだ。
|
||||
「瑞竜さんは学校の成績が飛び切り好いんですってね。こう見るからに秀才らしい。これは坊さんには惜しいですよ」
|
||||
と言って、松本さんが褒めてくれた。僕は松本さんの息子の房吉君と同級生だったから、家へも度々遊びに行ったことがある。
|
||||
「ひどいですな。それじゃ頭の悪いものばかりが坊主になるんですか?」
|
||||
と兄貴が抗議を申入れた。
|
||||
「ハッハヽヽ。これは大失策《おおしくじり》だ」
|
||||
こんな冗談話の中に僕が東京遊学の志望を述べたら、
|
||||
「瑞竜さん、中学校を卒業したら、私のところへ相談にお出なさい。何とか工夫のつかないものでもないでしょう」
|
||||
と大檀那《おおだんな》の松本さんが言ってくれた。
|
||||
「有難うございます」
|
||||
「伜も何うせ東京へ出すんですから」
|
||||
「しかし……」
|
||||
「いや、御遠慮には及びません。御相談に乗りましょう。ついては一番で卒業して下さい」
|
||||
「一番で卒業すれば、東京へやって戴けますか?」
|
||||
と僕は念を押した。
|
||||
「やって上げましょう。罪も科もないものを見す/\お坊さんにするには忍びません」
|
||||
と松本さんは又兄貴にからかった。
|
||||
「何と仰有られても、この際は苦情を申しません。瑞竜、松本さんが後援して下されば大磐石だぞ。しっかりやれよ」
|
||||
と兄貴も僕の年来の希望を知っているから、大喜びをしてくれた。
|
||||
僕は固めたばかりの決心を翻した。錦子さんは惜しいけれど、一生の方針には代えられない。兄貴を頼んで、随明寺の方を断って貰った。序ながら、その後へ隣りの井沢正覚《いざわしょうがく》が納まった。正覚は極く気立の好い子で、僕とは竹馬の友だった。文字通り、竹馬に乗って遊んでいた時転んで股の辺りを裂いたことがある。以来玉が一つしかないという評判だったから、僕が訊いたら、チャンと二つ見せてくれた。そうして、
|
||||
「君、証拠人になってくれよ」
|
||||
と頼んだのである。そこで僕はいつも正覚坊の為めに保証する役だった。小学校時代は同級だったが、卒業してからは中学と商業に分れた。お寺は貧乏でいけないから、実業家になって金を儲けるんだと言って、正覚君は商業学校へ入っていた。矢張り坊主嫌いの方で、
|
||||
「簿記棒で阿弥陀様を叩いたら、アカーンって鳴いたよ」
|
||||
なぞと勿体ないことを言った。それにも拘らず、錦子さんの話が始まると、一も二もなかった。僕が断ると間もなく口がかゝって来たらしく、
|
||||
「君、僕は感ずるところあって、矢っ張り坊主になるよ。随明寺へ養子に行くんだ」
|
||||
と報告した。
|
||||
「何を感じたんだい?」
|
||||
「十万億土の夢を見て、豁然《かつぜん》として大悟一番したんだ。一|子《し》出家《しゅっけ》の功徳《くどく》によって九族《きゅうぞく》天《てん》に生《しょう》ずというんだから素晴らしい。僕は甘んじて犠牲になる」
|
||||
「その犠牲には景品がついているんじゃないかい?」
|
||||
「えゝ?」
|
||||
「九族よりも錦子さんだろう?」
|
||||
「それを言うなよ。ヘッヘヽヽ」
|
||||
「お芽出度う」
|
||||
と僕は背中を叩いてやった。首がガクリというくらい、正覚坊は気が弛んでいた。
|
||||
西行法師《さいぎょうほうし》のように浮世の無常を感じて出家するものもあれば、正覚坊のように綺麗なお嫁さんに釣られて、商業学校から坊主に転向するものもある。こういうのは悟りを開いても知れたものだろうと肚の中で馬鹿にしていたが、僕は間もなく正覚君の境遇が羨ましくなった。当てにしていた大檀那松本さんが死んでしまったのである。一番で卒業したけれど、実は御主人に学資を出して戴く約束でしたと言って行けない。随明寺の方を断るのではなかったのにと後悔した。
|
||||
「困ったな、これは。養子に行くにしても、今更好い口がない」
|
||||
|
||||
[#4字下げ]意気軒昂の水鳥会[#「意気軒昂の水鳥会」は中見出し]
|
||||
|
||||
僕は茫然自失という形だったけれど、御方便なもので、奮起一番、方針を立て直した。こうなれば、自力でやる。天意がそこにあると思った。中学校の校長先生のところへ相談に行ったら、県庁へ紹介してくれた。丁度欠員があって、月給三十五円の雇員に採用して貰った。今なら五千円というところだろうが、十年前の話だ。食扶持《くいぶち》を入れる代りに貯金しろと兄貴が嫂の理解を得て言ってくれた。四五年やれば東京へ行く学資が出来る。夜学の大学へ通って高等文官試験を受けようという遠大な計画だった。
|
||||
「順当に行っても高等学校大学と六年かゝります。此方は廻り道ですから、少し手間がかゝりましょう。三十までに試験を取れば宜いです」
|
||||
と僕は急がず撓《たゆ》まずやることにした。
|
||||
同じような計画の青年が同僚の間に二人いた。一人は中学校の先輩尾崎君で、もう一人はその年商業学校を出た黒須君だった。目の寄るところへ玉で、三人忽ち肝胆相照らした。
|
||||
僕達は退庁後よく散歩をした。四時仕舞いだから、随分議論をしても日が暮れない。三度に一度は狐ウドンで晩餐を共にした。
|
||||
「何うだい? 三人の会を青雲会としようじゃないか? 青雲を望む会だ」
|
||||
年長で大将株の尾崎君が散歩の途上空を仰ぎながら申出た。
|
||||
「宜かろうね」
|
||||
と僕は賛成したが、黒須君には別の案があった。
|
||||
「青雲会も好いけれど、少し安っぽくはないかい?」
|
||||
「青雲の志だもの、安っぽいことはなかろう」
|
||||
「いや、露骨だからさ。もっと婉曲《えんきょく》に行きたい」
|
||||
「それじゃ何か考えがあるのかい?」
|
||||
「水鳥会は何うだろう? 水鳥は呑気そうに浮いているけれど、足は始終足掻いている。僕達もこうやってノコ/\散歩して無駄口を叩いているけれど、肚の中は立身出世を念がけて間断なく足掻いている」
|
||||
「なるほど」
|
||||
「そこを現して水鳥会さ」
|
||||
「巧いね」
|
||||
「これは好い」
|
||||
と尾崎君と僕が賛成して、会の名が水鳥会と定った。その晩は発会式の積りで天丼を食べた。
|
||||
夏の暑さで中止になった散歩が秋になってもそのまゝだった。雨降りが続いたからかも知れない。冬になってから、尾崎君の家へ遊びに行くことが始まった。尾崎君のところは僕と黒須君の家の中間にあったから、都合が好かった。相変らず、立身出世を話す。或晩、尾崎君と黒須君が揃って、僕のところへ訪ねて来た。
|
||||
「井沢君の家は何方隣りだね?」
|
||||
と黒須君が訊いた。黒須君は正覚君と商業学校で同級だった。
|
||||
「此方だよ。養子に定《きま》って東京の仏教大学へ行っている」
|
||||
「君が僕のことを知らせたと見えて、東京から手紙をくれたよ」
|
||||
「僕のところへもチョク/\来る。大変な手紙だったろう?」
|
||||
「驚いたよ。錦子さん/\だ。その人と毎日手紙の交換をしているのらしい」
|
||||
「一日一信だと書いてあった。当てられるよ」
|
||||
「君は一種の恋敵《こいがたき》ってものかな?」
|
||||
「そうじゃないよ。此方は断ったんだから」
|
||||
「何しろ手放しだ。錦子さんとの仲は、天にあらば比翼《ひよく》の鳥、地にあらば連理《れんり》の枝。それは好いが嘘字が沢山書いてあった」
|
||||
「こゝの悪い男かい?」
|
||||
と尾崎君が自分の頭を指で叩きながら乗り出した。尾崎君は偶※[#二の字点、1-2-22]《たまたま》随明寺の檀家だった。
|
||||
「悪いって程でもないが、慌てものだ。試験の時に手を挙げて『先生、カンニングをしても宜うございますか?』と訊いたくらいのものだから」
|
||||
「馬鹿だな」
|
||||
「ナイフを借りるのを合図に教えるという申合せさ。それで、ナイフを借りてと訊くのをカンニングと言ってしまったんだ」
|
||||
「ハッハヽヽ」
|
||||
「告白したようなものさ。先生は憤ったよ。余り馬鹿にすると思って」
|
||||
「カンニングをやるのかい?」
|
||||
「いや、案外正直だ。カンニングをやる積りで準備をして来るけれど、度胸がないらしい。しかしそのお蔭でいつも中軸の成績だったよ」
|
||||
「何のお蔭で?」
|
||||
「カンニング・ペーパーを拵えるお蔭で、結構覚えてしまうんだ」
|
||||
「僕はいつも思うんだが、カンニング・ペーパーに書き込む面倒を勉強に向けたら、充分準備が出来るだろうにね」
|
||||
「そこだよ。井沢君はカンニングをする気にならないと勉強が出来ないんだ。唯だと張合がなくて頭に入らないと言う」
|
||||
「変な奴だな」
|
||||
「つまりカンニングという猟奇的刺戟《りょうきてきしげき》がないと、本気になれないらしい。変な癖だけれど、それを効果的に利用していたんだから、ナカ/\隅に置けない人間だと思う」
|
||||
「品行は大丈夫かい?」
|
||||
「乙だろうな」
|
||||
「悪いね」
|
||||
「茶目だからさ。悪気《わるぎ》はないけれど、先生から始終睨まれていた」
|
||||
「そういえば、始終何かやっていたよ」
|
||||
と僕は思い出した。
|
||||
「君は親友だろうからな」
|
||||
「僕のところへ来て、一々失敗を報告する。好奇心が強いんだね。いけないということは何でもして見たいんだ。君、昨夜はカフェーへ探検に行って、あべこべに悉皆《すっかり》探検されてしまったと報告したことがある」
|
||||
「何うしたんだい?」
|
||||
「長い憧れのカフェーへ行ったのさ」
|
||||
「しかし学校の生徒は寄せつけまい」
|
||||
「そこは考えて、商店の若旦那に扮装して入り込んだ。仲間が一人ある」
|
||||
「それは君だろう?」
|
||||
と黒須君は美事言い当てた。
|
||||
「ハッハヽヽ」
|
||||
「見ろ」
|
||||
「それじゃ二人分告白する。諜し合せて、多少懐ろを温かくして出掛けたんだ。女給にはチップをやるものだと聞いていたから、入るとすぐに、五十銭銀貨を二枚握らせた。一枚宛だよ、女給は二人だったから」
|
||||
「ケチだね」
|
||||
「いや、引続いて十分毎に一枚宛寄進についた。しかし、これが拙かったらしい」
|
||||
「チップは後からやるものだろう」
|
||||
「それだからさ。此奴等雛っ子だと見られてしまった。
|
||||
『ちょっと、あなた方は中学校か商業の生徒さんじゃない?』と訊くんだ。僕達はギョッとして、顔を見合せたよ。知れゝば退学ものだからね」
|
||||
「何と答えた?」
|
||||
「馬鹿あ言えと威張って、又五十銭やった。『ちょっと、あなた方はお葬式の帰りじゃない? お線香の香《におい》が沁みているようよ』と又一人の女給が言うんだ。僕達は又顔を見合せて、又五十銭やった」
|
||||
「ハッハヽヽ」
|
||||
「するともう一人が『ちょっと、あなた』と言って、僕の手を握った。綺麗な方の奴さ」
|
||||
「涎《よだれ》が流れるぞ。ダラシがない」
|
||||
「『ちょっと、あなた、あなた方はお寺さんじゃない?』と来た。『馬鹿を言え』『でも、戴く五十銭銀貨には皆御飯粒の固りがついているわ。お布施《ふせ》をちょろまかして剥がして来たんでしょう?』と図星を指しやがった」
|
||||
「ハッハヽヽ」
|
||||
「こゝは不浄人種の来るところで、坊さんや学生さんの足を踏ん込むところじゃないからお帰りなさいと言ったけれど、チップは一向返してくれない」
|
||||
「当り前さ」
|
||||
「足許の明るい中にと思って、早速逃げて来た」
|
||||
「態《ざま》を見ろ。ハッハヽヽ」
|
||||
「坊主が女給にお説法をされるようじゃお話にならないな」
|
||||
と尾崎君が歎息した。
|
||||
「元来が正覚君の発起《ほっき》さ。頓狂者だから、事を共にすると、飛んでもないことになる」
|
||||
「そんな風で随明寺が継げるだろうかな?」
|
||||
「利巧だから、何とか誤魔化しをつけるだろう」
|
||||
「いや、僕にしても檀家だから、結局その正覚君に引導を渡して貰うことになる」
|
||||
「成程ね」
|
||||
「そんな頓狂者なら、地獄極楽を間違えないとも限らない」
|
||||
「これは痛快だ。ハッハヽヽ」
|
||||
「比翼連理じゃ浮べないよ」
|
||||
尾崎君は陰気な冗談を好む男だった。その晩はこれが切っかけになって妙に死ぬ話が続いた。
|
||||
|
||||
[#4字下げ]第二の失敗[#「第二の失敗」は中見出し]
|
||||
|
||||
水鳥会が一年続いた。黒須君は肋膜炎を患って、長らく欠勤を続けた後、退職した。死ぬのではないかと思ったが、好い塩梅に容態が好転した。しかし再び働けるまでには容易のことでない。尾崎君と僕は時々見舞いに行って元気をつけてやった。
|
||||
自然、僕は尾崎君を訪れて、差向いで話すことが多かった。お母さんも妹の町子さんも僕を歓迎してくれる。町子さんはいつしか女学校の五年生になって、見違える程器量を上げた。僕は特別女性に脆いのか何うか、会う度に惹きつけられ始めた。僕が行くと、町子さんはいつもお茶の給仕をしてくれる。或時は尾崎君と町子さんと僕とで映画を見に出掛けた。町子さんが間に挾まって坐って、僕にばかり話しかけてくれた。
|
||||
その人の面影を忍ぶと、桜の花が咲いたように周囲が明るくなる。何となく嬉しくて、胸がドキ/\する。それが恋愛だろうと僕は思っている。町子さんを貰ったら幸福だろうと考えるようになった。尤も差当りは何うすることも出来ない。高文試験を取ってからの話だけれど、今から約束を定めて、待っていて貰えば、何んなに励みになるか知れない。僕は機会を見て尾崎君に懇望して見ようかと思っていたが、尾崎君は勉強一心だった。
|
||||
「君、今は高文突破以外に何もない。道は一筋だ。その道が首相官邸まで続いているんだから有難いじゃないか?」
|
||||
と言う。僕が県知事さえ諦めかけているのに、総理大臣になれる積りだ。少し誇大妄想のようなところがあるから、恋愛問題に共鳴してくれまいと察して差控えた。
|
||||
しかし町子さんへの思慕が日増《ひまし》に募って来た。面影を浮べると周囲が花盛りのように明るくなる。つい惹きつけられて、尾崎君のところへ足が向く。尾崎君は無論未だ独身だけれど、もう家長だから、用が多い。僕が訪ねて行っている間に客が来て、その応対に出なければならないことがある。そういう折からは町子さんが話相手をしてくれるから書き入れだ。兄貴よりも妹に会いたくて訪れる。こうなっては宜しくないけれど仕方がない。
|
||||
「町子さん、あなたはもうソロ/\お嫁にいらっしゃるんでしょうね?」
|
||||
と僕は訊いて見た。
|
||||
「まあ! 厭な木下さんね」
|
||||
「何故ですか?」
|
||||
「私、お嫁になんか行きませんわ」
|
||||
「しかし来年はもう御卒業でしょう?」
|
||||
「卒業すればお嫁に行くものと定っていますの?」
|
||||
「そういう世間相場ですから」
|
||||
「相場なんて失礼よ」
|
||||
「それじゃいらっしゃらないんですか?」
|
||||
「えゝ。兄が高文試験を取るまで側についていますの」
|
||||
「五年も六年もかゝって、お婆さんになってしまいますよ」
|
||||
「三十になっても四十になっても構いませんわ」
|
||||
僕は町子さんの誇張を善意に解釈した。序に何年でも僕を待っていてくれるという意味らしい。それなら約束して置いてくれるだろうと思って、申込の手紙を書いた。決して浮いたことでない。修身の答案のような鹿爪らしいものだった。次ぎの機会に婦人雑誌を買って行って、
|
||||
「これを御覧下さい」
|
||||
と言って手渡した。無論手紙が入れてある。これは僕が数日かゝって絞った智恵だった。まさか手紙丈けを剥き出しには渡せない。
|
||||
二三日訪問を控えて返事を待っていたが、音沙汰なかった。そこで又出掛けたけれど、尾崎君が頑張っていて、機会を提供しない。殆んど日参したら、三晩目ぐらいに、尾崎君が散髪に行って、もうすぐ帰るということだった。上って話している間に、
|
||||
「町子さん、この間の僕の作文、読んで下さいましたか?」
|
||||
と冗談のように訊いて見た。
|
||||
「…………」
|
||||
「町子さん」
|
||||
「えゝ」
|
||||
「何うですか?」
|
||||
「私、御返事を書いたんですけれど……」
|
||||
「有難いです」
|
||||
「でも、やめましたわ」
|
||||
「何故?」
|
||||
「あなたのお名前、木下瑞竜でしょう。お坊さんらしくて、ロマンチックの気分が消えてしまったじゃありませんか?」
|
||||
「…………」
|
||||
「宛名まで書いて考え込んでしまいましたの。私、あなた、決して厭じゃないんですけれど……」
|
||||
そこへもう尾崎君が帰って来た。奴さん、何ういうものか、機嫌が悪かった。その翌朝だった。役所で顔が合うと直ぐに、僕の腕を捉まえて廊下へ引っ張り出した。
|
||||
「君は妹にラブ・レッターを送るなんて、不都合千万じゃないか?」
|
||||
「ラブ・レッターという程のものじゃないんだよ」
|
||||
「いや、僕は読んだ。要するに下らないことが正々堂々と書いてある」
|
||||
「…………」
|
||||
「出世以外のことを考える時か?」
|
||||
「多少は仕方あるまい。人間だもの」
|
||||
「断るよ。妹はズボは嫌いだと言っている」
|
||||
「ズボとは?」
|
||||
「坊主だ」
|
||||
「失敬な」
|
||||
「何方が失敬だ? 妹の縁談の邪魔になるから、もう一切寄りつかないでくれ給え」
|
||||
「宜いとも」
|
||||
「道で会って話しかけても困るよ」
|
||||
「安心してくれ。迷惑はかけない」
|
||||
その日、家へ帰ったら、町子さんから郵便で手紙が来ていた。
|
||||
|
||||
[#ここから1字下げ]
|
||||
あら/\走り書きよ。御免下さい。
|
||||
私、あなたが嫌いじゃありませんが、お坊さんらしいお名前が厭ですの。瑞竜さんなんてロマンチックじゃありませんわ。竜太郎とでも改めて下さい。でも、兄さんが憤ってしまったから駄目ですわ。兄さんと仲直りをして下さいよ。兄は出世以外何も考えていませんから困ります。あなたがお出下さらなければ、この手紙、これで最後になります。[#地から3字上げ]かしこ
|
||||
[#地から2字上げ]不幸な乙女
|
||||
[#ここで字下げ終わり]
|
||||
|
||||
僕は黒須君のところへ相談に駈けつけた。丁度尾崎君が役所の帰りに寄って行った後だった。僕のことを水鳥会の精神を裏切る賊徒だと言って罵ったそうだ。僕は町子さんの為めに尾崎君の御機嫌を取ったけれど、一こくな奴だから梃《てこ》でも動かない。そのまゝ絶交の形になってしまった。
|
||||
|
||||
[#4字下げ]三度目の正直[#「三度目の正直」は中見出し]
|
||||
|
||||
親友と不仲になって面白くない日を送っているところへ、松本君が訪ねて来て用談を切り出した。僕を東京へやってくれると言った松本さんの息子だ。檀家《だんか》だし、中学校の同級生だから、その後も時折顔の合う機会があったのである。
|
||||
「君、僕のところの店へ来てくれないか? 今度は小さいながらも会社組織にして、拡張したから、何《ど》うにかなる」
|
||||
「さあ」
|
||||
「無理にも来て貰いたい。実は僕も親父の頓死の為め、上の学校へも行かずに後を継いで、近頃まで天手古舞いさ。漸く目鼻がついたから相談に来た。親父は君に何か約束をしていたようじゃないか?」
|
||||
「うむ」
|
||||
「今更仕方がないけれど、親父の意志を継ぐという意味で、君を重用したいんだ。それに昔馴染だから君の実力も人格もよく分っている。失敬ながら、今県庁で幾ら貰っているんだい?」
|
||||
「三十五円で来て、一度上って、四十円さ」
|
||||
「それじゃ百円で来てくれ給え」
|
||||
「えゝ?」
|
||||
「百円出すから文句を言いっこなしにしよう」
|
||||
「まあ、二三日考えさせてくれ給え」
|
||||
と言って勿体をつけたけれど、僕の決心はもうついていた。
|
||||
松本君の会社へ移ると間もなく、僕は山へ出張を命じられた。製材会社だから、彼方此方の山に製材工場がある。そこを廻って歩いて、里へ帰ることは滅多にない。一種の島流しだ。これでは松本君、待遇を好くしてくれる筈だった。石の上にも三年というが、山の中の五年は僕に取って願ってもない修業になった。他の屈託を諦めたから、専念になって、材木そのものを研究した。何にでも学問がある。材木学というものがあったら、僕はその権威者の一人だろう。会社でも掛替のない調法者になったが、その後に三年の空白《ブランク》が続く。戦争で引張られたのである。しかし何処へ行って何をしたかは今更書き立てる勇気がない。
|
||||
終戦になって帰って来て見たら、尾崎君は戦死していた。黒須君は胸の病気が高じて、これももうこの世の人でなかった。松本君も無事帰還したが、差当り商売がやれないから、僕を東京の親会社へ推薦してくれた。以来トントン拍子が続いている。水鳥会の二人を思い出すと死ぬもの貧乏の感が深い。
|
||||
県庁時代に読んだ経済学の本に、衣食が足って生活の安定がつくと女房が欲しくなるという筋道が書いてある。僕は月給一万五千円、三十歳の青年にして、恵まれた立場だ。東京の生活に慣れた昨今、同僚の奥さんの同級生というのに心を惹かされ始めた。その面影を思い浮べると、又桜の花が咲いたように明るい気持になる。
|
||||
考えて見ると、僕を迷わせた女性が今までに三人ある。第一は随明寺の錦子さんだ。これは手に入る範囲まで来ていたのに、此方が坊主になりたがらなかった為め、見す/\逃がしてしまった。因《ちな》みに正覚君も無事で随明寺を継いでいる。第二は尾崎君の妹の町子さんだ。これもお互に憎からず思っていたのだが、今度は此方の名前が坊主臭い為めに先方で躊躇していたところへ邪魔が入って、それきりになってしまった。三度目が増山澄子さんだ。同僚河合君の家で偶然落ち合ったのが切っかけで交際が始まった。作州津山の素封家《そほうか》の娘で、目白の女子大学卒業生だ。
|
||||
「もう郷里へ帰る筈だけれど、出て来た序に此方で縁談を定めようと思って、叔母さんのところに滞在しているんだ。申込めば屹度纒まる」
|
||||
と河合君が勧めてくれた。澄子さんは河合夫人と同級生のみならず、同郷で家が隣り同志だから、姉妹のように親しくしている。
|
||||
「しかし僕は君も知っている通り、中学校を出た丈けだからね。女子大学出となると、手に余るだろう」
|
||||
「何あに、僕だって専門学校だけれど、立派に操縦している」
|
||||
「余り立派でもないようだぜ」
|
||||
「可なり操縦している」
|
||||
「それは認めるけれど」
|
||||
「僕から話して見ようか?」
|
||||
「いや、うっかり申込んで恥をかきたくないから」
|
||||
僕は中学校丈けということの外に、家がお寺だからという心配があった。女は敏感だ。まさか今まで線香の香《におい》が沁みてもいまいが、何かで覚られないとも限らない。言い落したが、名はその後竜之助と改めた。木下竜之助なら、武士と見えても、坊主とは思われまい。姓名判断の方からも決して悪くないそうだ。
|
||||
河合君が誘うものだから、日曜には必ず遊びに行った。澄子さんも大抵来ていた。河合夫婦に澄子さんと僕で郊外散歩に出掛けたこともあった。
|
||||
「何うだい? 妻から話させようか? もう大丈夫だよ。恥は決してかゝさせないと僕達は見込をつけているんだ」
|
||||
と或日河合君が会社の応接室へ僕を連れ込んで勧めた。
|
||||
「宜しく頼む。しかし若《も》し羽振が悪いようだったら、早めに切り上げてくれ給え」
|
||||
「申込むとなると、妙に気が弱いんだね。会えば冗談なんか言うくせに」
|
||||
「何しろ此方は山の中から出て来た田舎漢だから」
|
||||
「先方《むこう》だって作州津山だ。僕は行って見たが、結構山の中だよ」
|
||||
「木挽小屋に五年もいたんだから、大鋸屑《おがくず》の香がするなんて言われると困る」
|
||||
と冗談を言って、僕は厭な暗合だと思った。線香の下等なのは大鋸屑を交ぜて拵えると聞いている。
|
||||
「大丈夫だよ。申込めば一も二もない。先方じゃ待っているんだ」
|
||||
「本当かい?」
|
||||
「そうだろうと思うんだ」
|
||||
「思うんじゃ安心出来ない」
|
||||
「一つ君の身許を話してくれ給え。これ/\/\/\の家柄だということが分っていると僕も話し好い」
|
||||
「家柄も何もない。百姓だよ。僕のところは」
|
||||
「今でもやっているのかい?」
|
||||
「いや、もう足を洗ったけれど」
|
||||
「兄さんは何をしている?」
|
||||
「町の衛生委員だ」
|
||||
「衛生委員と」
|
||||
と河合君は手帳を出して記入して、
|
||||
「しかし商売は? 職業の方は?」
|
||||
「地主だよ。地所を小さく区切って貸している」
|
||||
と僕は正直だから、嘘をつくにも真実性を失わない。墓地は正に地所を小さく区切って貸すものだ。
|
||||
「畑でなくて、住宅地だね」
|
||||
「うむ。町中の閑静な住宅地だ」
|
||||
「原籍は?」
|
||||
「山形県酒田市寺町百八番地、木下一竜というのが兄貴で戸主だ」
|
||||
「山形県酒田市……」
|
||||
と河合君は書き留めて、
|
||||
「お寺のあるところかい? 寺町って」
|
||||
と僕の顔を凝っと見た。
|
||||
「昔あったらしい。今でもチョク/\ある」
|
||||
「一竜なんて変な名前だね」
|
||||
「竜一が、本当だけれど、田舎のことだものだから、届出の時、市役所の受附がアベコベに書き込んでしまったんだ」
|
||||
「君は三男だと言っていたね」
|
||||
「うむ。次の兄貴が竜二、僕が竜之助、僕のすぐ上に姉が二人あるけれど、何方も嫁に行っている。偶然何方も地主だ」
|
||||
「それぐらいで結構だよ」
|
||||
申込が直ぐに効を奏して、当事者だけの間に話が纒まった。
|
||||
「澄子さんの方の両親は何うだろうね?」
|
||||
と僕はもう一つ難関があるように感じた。
|
||||
「澄子さんが好いとなれば異議なしさ。その方は僕達が悉皆委せて貰ってある」
|
||||
「それじゃもう安心だ」
|
||||
「しかし君の方は何うだい? 君が好ければ、兄さんは無論首を縦に振るんだろうね?」
|
||||
「大丈夫だ。僕はもう長く家を離れていて何でも自分定《じぶんぎ》めでやる。兄貴は事後承諾で宜いんだ」
|
||||
「これで当事者の間はもうガッチリ動かないということになった」
|
||||
「うむ。全くお蔭様だ」
|
||||
「ところで、君、こゝに一つ澄子さんの身体について、君に特別に理解して置いて貰いたいことがある」
|
||||
と河合君は言い悪《にく》そうに切り出した。僕はハッと思った。余りスル/\ッと簡単に定ったから、澄子さんは美貌にもかゝわらず、疵物《きずもの》で、何か身体に故障があるのかと疑ったのである。
|
||||
「何だい? 一体」
|
||||
「実は君が厭がるといけないと思って差控えていたが、澄子さんの家はお寺だよ」
|
||||
「えゝ?」
|
||||
「人によると、自分の思っている女性がお寺の娘だということを発見すると陰気な感じがする。実は僕もそうだった」
|
||||
「えゝ?」
|
||||
「話が定ってから、実は恒子さんのところはお寺ですけれどもと仲人に言われたんだ」
|
||||
「君の奥さんのところもかい?」
|
||||
「うむ。里帰りに作州津山までついて行ったが、お寺だって、普通の家庭と些っとも変るところはない」
|
||||
「ハッハヽヽ」
|
||||
「庭は広くて、少し閑静な心持がするくらいのものだ。澄子さんのところは隣り合せだそうだが、こうなると思わなかったから、よく見て来なかったけれど、矢っ張り大きな庭があるようだったよ」
|
||||
「ハッハヽヽ」
|
||||
「何だい?」
|
||||
「妙なものだな。世の中は」
|
||||
「何故?」
|
||||
「何故って。ハッハヽヽ」
|
||||
と僕は可笑しくて仕方がなかった。
|
||||
[#地から1字上げ](昭和二十五年二月、キング)
|
||||
|
||||
|
||||
|
||||
底本:「佐々木邦全集 補巻5 王将連盟 短篇」講談社
|
||||
1975(昭和50)年12月20日第1刷
|
||||
初出:「キング」大日本雄辯會講談社
|
||||
1950(昭和25)年2月
|
||||
入力:特定非営利活動法人はるかぜ
|
||||
校正:芝裕久
|
||||
2020年11月27日作成
|
||||
青空文庫作成ファイル:
|
||||
このファイルは、インターネットの図書館、青空文庫(https://www.aozora.gr.jp/)で作られました。入力、校正、制作にあたったのは、ボランティアの皆さんです。
|
||||
紫陽花
|
||||
泉鏡花
|
||||
|
||||
-------------------------------------------------------
|
||||
【テキスト中に現れる記号について】
|
||||
|
||||
《》:ルビ
|
||||
(例)其《その》
|
||||
|
||||
|:ルビの付く文字列の始まりを特定する記号
|
||||
(例)年|少《わか》き
|
||||
|
||||
[#]:入力者注 主に外字の説明や、傍点の位置の指定
|
||||
(数字は、JIS X 0213の面区点番号またはUnicode、底本のページと行数)
|
||||
(例)※[#「目+爭」、第3水準1-88-85]
|
||||
|
||||
/\:二倍の踊り字(「く」を縦に長くしたような形の繰り返し記号)
|
||||
(例)きら/\
|
||||
-------------------------------------------------------
|
||||
|
||||
[#5字下げ]一[#「一」は中見出し]
|
||||
|
||||
色青く光ある蛇、おびたゞしく棲めればとて、里人は近よらず。其《その》野社《のやしろ》は、片眼の盲ひたる翁ありて、昔より斉眉《かしず》けり。
|
||||
其《その》片眼を失ひし時一たび見たりと言ふ、几帳の蔭に黒髪のたけなりし、それぞ神なるべき。
|
||||
ちかきころ水無月中旬、二十日余り照り続きたる、けふ日ざかりの、鼓子花《ひるがお》さへ草いきれに色褪せて、砂も、石も、きら/\と光を帯びて、松の老木《おいき》の梢より、糸を乱せる如き薄き煙の立ちのぼるは、木精《こだま》とか言ふものならむ。おぼろ/\と霞むまで、暑き日の静さは夜半にも増して、眼もあてられざる野の細道を、十歳《とお》ばかりの美少年の、尻を端折《はしよ》り、竹の子笠被りたるが、跣足《はだし》にて、
|
||||
「氷や、氷や。」
|
||||
と呼びもて来つ。其より市に行かんとするなり。氷は筵包《むしろづつみ》にして天秤に釣したる、其片端には、手ごろの石を藁縄《わらなわ》もて結びかけしが、重きもの荷ひたる、力なき身体のよろめく毎に、石は、ふらゝこの如くはずみて揺れつ。
|
||||
とかうして、此の社の前に来りし時、太き息つきて立停りぬ。
|
||||
笠は目深《まぶか》に被りたれど、日の光は遮らで、白き頸《うなじ》も赤らみたる、渠《かれ》はいかに暑かりけむ。
|
||||
蚯蚓《みみず》の骸《むくろ》の干乾びて、色黒く成りたるが、なかばなま/\しく、心ばかり蠢《うごめ》くに、赤き蟻の群りて湧くが如く働くのみ、葉末の揺るゝ風もあらで、平たき焼石の上に何とか言ふ、尾の尖《さき》の少し黒き蜻蛉《とんぼ》の、ひたと居て動きもせざりき。
|
||||
かゝる時、社の裏の木蔭より婦人《おんな》二人出で来れり。一人は涼傘《ひがさ》畳み持ちて、細き手に杖としたる、いま一人は、それよりも年|少《わか》きが、伸上るやうにして、背後より傘さしかけつ。腰元なるべし。
|
||||
丈高き貴女のつむりは、傘のうらに支ふるばかり、青き絹の裏、眉のあたりに影をこめて、くらく光るものあり、黒髪にきらめきぬ。
|
||||
怪しと美少年の見返る時、彼《か》の貴女、腰元を顧みしが、やがて此方《こなた》に向ひて、
|
||||
「あの、少しばかり。」
|
||||
暑さと疲労《つかれ》とに、少年はものも言ひあへず、纔《わずか》に頷きて、筵を解きて、笹の葉の濡れたるをざわ/\と掻分けつ。
|
||||
雫落ちて、雪の塊は氷室より切出したるまゝ、未だ角も失せざりき。其一角をば、鋸もて切取りて、いざとて振向く。睫《まつげ》に額の汗つたひたるに、手の塞《ふさ》がりたれば、拭ひもあへで眼を塞ぎつ。貴女の手に捧げたる雪の色は真黒なりき。
|
||||
「この雪は、何《ど》うしたの。」
|
||||
美少年はものをも言はで、直ちに鋸の刃を返して、さら/\と削り落すに、粉はばら/\とあたりに散り、ぢ、ぢ、と蝉の鳴きやむ音して、焼砂に煮え込みたり。
|
||||
|
||||
[#5字下げ]二[#「二」は中見出し]
|
||||
|
||||
あきなひに出づる時、継母の心なく嘗《かつ》て炭を挽きしまゝなる鋸を持たせしなれば、さは雪の色づくを、少年は然りとも知らで、削り落し払ふまゝに、雪の量は掌《たなそこ》に小さくなりぬ。
|
||||
別に新しきを進めたる、其もまた黒かりき。貴女は手をだに触れむとせで、
|
||||
「きれいなのでなくつては。」
|
||||
と静にかぶりをふりつゝいふ。
|
||||
「えゝ。」と少年は力を籠めて、ざら/\とぞ掻いたりける。雪は崩れ落ちて砂にまぶれつ。
|
||||
渋々捨てて、新しきを、また別なるを、更に幾度か挽いたれど、鋸につきたる炭の粉の、其都度雪を汚しつつ、はや残り少なに成りて、笹の葉に蔽はれぬ。
|
||||
貴女は身動《みじろ》きもせず、瞳をすゑて、冷かに瞻《みまも》りたり。少年は便《たより》なげに、
|
||||
「お母様《つかさん》に叱られら。お母様《つかさん》に叱られら。」
|
||||
と訴ふるが如く呟きたれど、耳にもかけざる状《さま》したりき。附添ひたる腰元は、笑止と思ひ、
|
||||
「まあ、何うしたと言ふのだね、お前、変ぢやないか。いけないね。」
|
||||
とたしなめながら、
|
||||
「可哀さうでございますから、あの……」と取做《とりな》すが如くにいふ。
|
||||
「いゝえ。」
|
||||
と、にべもなく言《い》ひすてて、袖も動かさで立ちたりき。少年は上目づかひに、腰元の顔を見しが、涙ぐみて俯《うつむ》きぬ。
|
||||
雪《ゆき》の砕《くだ》けて落散りたるが、見る/\水になりて流れて、けぶり立ちて、地の濡色も乾きゆくを、怨めしげに瞻りぬ。
|
||||
「さ、おくれよ。いゝのを、いゝのを。」
|
||||
と貴女は急込《せきこ》みてうながしたり。
|
||||
こたびは鋸を下に置きて、筵《むしろ》の中に残りたる雪の塊を、其まゝ引出して、両手に載せつ。
|
||||
「み、みんなあげよう。」
|
||||
細りたる声に力を籠めて突出すに、一掴みの風冷たく、水気むら/\と立ちのぼる。
|
||||
流るゝ如き瞳動きて、雪と少年の面を、貴女は屹《きつ》とみつめしが、
|
||||
「あら、こんなぢや、いけないツていふのに。」
|
||||
といまは苛《いら》てる状《さま》にて、はたとばかり掻退《かいの》けたる、雪は辷《すべ》り落ちて、三ツ四ツに砕けたるを、少年のあなやと拾《ひろ》ひて、拳を固めて掴むと見えし、血の色颯と頬を染めて、右手《めて》に貴女の手を扼《とりしば》り、ものをも言はで引立てつ。
|
||||
「あれ、あれ、あれえ!」
|
||||
と貴女は引かれて倒れかゝりぬ。
|
||||
風一陣、さら/\と木の葉を渡れり。
|
||||
|
||||
[#5字下げ]三[#「三」は中見出し]
|
||||
|
||||
腰元のあれよと見るに、貴女の裾、袂、はら/\と、柳の糸を絞るかのやう、細腰を捩りてよろめきつゝ、ふたゝび悲しき声たてられしに、つと駈寄りて押隔て、
|
||||
「えゝ! 失礼な、これ、これ、御身分を知らないか。」
|
||||
貴女はいき苦しき声の下に、
|
||||
「いゝから、いゝから。」
|
||||
「御前《ごぜん》――」
|
||||
「いゝから好きにさせておやり。さ、行かう。」
|
||||
と胸を圧して、馴れぬ足に、煩はしかりけむ、穿物を脱ぎ棄《す》てつ。
|
||||
引かれて、やがて蔭ある処、小川流れて一本の桐の青葉茂り、紫陽花の花、流にのぞみて、破垣《やれがき》の内外に今を盛りなる空地の此方に来りし時、少年は立停りぬ。貴女はほと息つきたり。
|
||||
少年はためらふ色なく、流に俯して、掴み来れる件の雪の、炭の粉に黒くなれるを、その流れに浸して洗ひつ。
|
||||
掌にのせてぞ透し見たる。雫ひた/\と滴りて、時の間に消え失する雪は、はや豆粒のやゝ大なるばかりとなりしが、水晶の如く透きとほりて、一点の汚もあらずなれり。
|
||||
きつと見て、
|
||||
「これでいゝかえ。」といふ声ふるへぬ。
|
||||
貴女は蒼《あお》く成りたり。
|
||||
後馳《おくれば》せに追続ける腰元の、一目見るより色を変えて、横様にしつかと抱く。其の膝に倒れかゝりつ、片手をひしと胸にあてて。
|
||||
「あ。」とくひしばりて、苦しげに空をあふげる、唇の色青く、鉄漿《かね》つけたる前歯動き、地に手をつきて、草に縋《すが》れる真白き指のさきわなゝきぬ。
|
||||
はツとばかり胸をうちて瞻《みまも》るひまに衰へゆく。
|
||||
「御前様――御前様。」
|
||||
腰元は泣声たてぬ。
|
||||
「しづかに。」
|
||||
幽《かすか》なる声をかけて、
|
||||
「堪忍《かんにん》おし、坊や、坊や。」とのみ、言ふ声も絶え入りぬ。
|
||||
呆れし少年の縋り着きて、いまは雫ばかりなる氷を其口に齎《もたら》しつ。腰元|腕《かいな》をゆるめたれば、貴女の顔のけざまに、うつとりと目を※[#「目+爭」、第3水準1-88-85]《みひら》き、胸をおしたる手を放ちて、少年の肩を抱きつゝ、ぢつと見てうなづくはしに、がつくりと咽喉に通りて、桐の葉越の日影薄く、紫陽花の色、淋しき其笑顔にうつりぬ。
|
||||
|
||||
|
||||
|
||||
底本:「花の名随筆6 六月の花」作品社
|
||||
1999(平成11)年5月10日第1刷発行
|
||||
底本の親本:「鏡花全集 第二巻」岩波書店
|
||||
1942(昭和17)年9月
|
||||
入力:門田裕志
|
||||
校正:林 幸雄
|
||||
2002年4月24日作成
|
||||
2014年8月2日修正
|
||||
青空文庫作成ファイル:
|
||||
このファイルは、インターネットの図書館、青空文庫(http://www.aozora.gr.jp/)で作られました。入力、校正、制作にあたったのは、ボランティアの皆さんです。
|
BIN
Paradigm/kernel32.dll
Normal file
BIN
Paradigm/libgcc_s_seh-1.dll
Normal file
BIN
Paradigm/libstdc++-6.dll
Normal file
161
Paradigm/login.py
Normal file
@ -0,0 +1,161 @@
|
||||
'''
|
||||
Author: ProtoDrive000
|
||||
Date: 2023-04-01 14:06:58
|
||||
LastEditTime: 2023-04-01 18:23:57
|
||||
Description:
|
||||
FilePath: \BCI_Timer\login.py
|
||||
Copyright © : 2021年 赛博智能车实验室. All rights reserved.
|
||||
'''
|
||||
|
||||
|
||||
|
||||
from ui.login_ui import Ui_MainWindow
|
||||
|
||||
import sys
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
from PyQt5.QtCore import Qt, QPropertyAnimation, QRect, QEasingCurve
|
||||
from PyQt5.QtGui import QColor
|
||||
from PyQt5.QtWidgets import QDesktopWidget
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
import time
|
||||
import os
|
||||
|
||||
class LOGIN(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.access = False
|
||||
self.setupUi(self)
|
||||
self.start_x = None
|
||||
self.start_y = None
|
||||
self.anim=None
|
||||
self.Com_Dict = None
|
||||
self.setWindowTitle("BCI登录")
|
||||
self.setWindowIcon(QIcon("ui/mylogo.jpg"))
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
self.setWindowFlags(Qt.FramelessWindowHint) # 设置窗口标志:隐藏窗口边框
|
||||
self.name.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0)
|
||||
self.password.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0)
|
||||
#self.resize(QDesktopWidget().screenGeometry().width(),
|
||||
# QDesktopWidget().screenGeometry().height()) # 主窗大小
|
||||
self.Login.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.create_Account.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
self.confirm.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
|
||||
try:
|
||||
df = pd.read_csv('./usr/data.csv')
|
||||
tmp_data = np.array(df) # 先将数据框转换为数组
|
||||
self.data = np.delete(tmp_data, 0, axis=1).tolist()
|
||||
self.data = [[str(i) for i in self.data[j]] for j in range(len(self.data))]#字符化
|
||||
except:
|
||||
self.data = []
|
||||
self.label_pix.setPixmap(QPixmap("ui/logo.png"))
|
||||
self.label_pix.setScaledContents(True)
|
||||
self.e = 1
|
||||
self.init()
|
||||
|
||||
def init(self):
|
||||
#self.Login.clicked.connect(self.my_login)
|
||||
self.create_Account.clicked.connect(self.my_sign)
|
||||
#self.confirm.clicked.connect(self.sign_confirm)
|
||||
self.check.clicked.connect(self.port_check)
|
||||
|
||||
|
||||
def my_sign(self):
|
||||
self.tabWidget.setCurrentIndex(1)
|
||||
|
||||
|
||||
|
||||
def port_check(self):
|
||||
# 检测所有存在的串口,将信息存储在字典中
|
||||
self.Com_Dict = {}
|
||||
port_list = list(serial.tools.list_ports.comports())
|
||||
|
||||
for port in port_list:
|
||||
self.Com_Dict["%s" % port[0]] = "%s" % port[1]
|
||||
|
||||
if len(self.Com_Dict) == 0:
|
||||
self.check.setText(" 无串口")
|
||||
|
||||
def keyPressEvent(self, QKeyEvent):
|
||||
"""快捷键"""
|
||||
if QKeyEvent.key() == Qt.Key_Escape: # esc
|
||||
if self.e == 1:
|
||||
self.animation_exit()
|
||||
self.close()
|
||||
elif self.e == 0:
|
||||
self.animation_start()
|
||||
|
||||
def animation_start(self):
|
||||
self.e = 1
|
||||
self.show()
|
||||
self.anim = QPropertyAnimation(self, b'geometry') # 动画类型
|
||||
self.anim.setStartValue(QRect(QDesktopWidget().screenGeometry().width() / 2 - 230, QDesktopWidget().screenGeometry().height() / 2 - 338, 461, 676))
|
||||
self.anim.setEndValue(QRect(0, 0, QDesktopWidget().screenGeometry().width(), QDesktopWidget().screenGeometry().height()))
|
||||
self.anim.setDuration(400)
|
||||
self.anim.setEasingCurve(QEasingCurve.OutBounce)
|
||||
main_opacity = QPropertyAnimation(self, b"windowOpacity", self)
|
||||
main_opacity.setStartValue(0)
|
||||
main_opacity.setEndValue(1)
|
||||
main_opacity.setDuration(400)
|
||||
main_opacity.start()
|
||||
|
||||
# self.anim.setLoopCount(-1) # 设置循环旋转
|
||||
self.anim.start()
|
||||
|
||||
def animation_exit(self):
|
||||
self.e = 0
|
||||
self.anim = QPropertyAnimation(self, b'geometry') # 动画类型
|
||||
self.anim.setStartValue(QRect(0, 0, self.width(), self.height()))
|
||||
self.anim.setEndValue(QRect(QDesktopWidget().screenGeometry().width() / 2 - 230, QDesktopWidget().screenGeometry().height() / 2 - 338, 461,676))
|
||||
self.anim.setDuration(400)
|
||||
self.anim.setEasingCurve(QEasingCurve.OutBounce)
|
||||
main_opacity = QPropertyAnimation(self, b"windowOpacity", self)
|
||||
main_opacity.setStartValue(1)
|
||||
main_opacity.setEndValue(0)
|
||||
main_opacity.setDuration(400)
|
||||
main_opacity.start()
|
||||
|
||||
# self.anim.setLoopCount(-1) # 设置循环旋转
|
||||
self.anim.start()
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
super(LOGIN, self).mousePressEvent(event)
|
||||
self.start_x = event.x()
|
||||
self.start_y = event.y()
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
self.start_x = None
|
||||
self.start_y = None
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
try:
|
||||
super(LOGIN, self).mouseMoveEvent(event)
|
||||
dis_x = event.x() - self.start_x
|
||||
dis_y = event.y() - self.start_y
|
||||
self.move(self.x() + dis_x, self.y() + dis_y)
|
||||
except:
|
||||
pass
|
||||
|
||||
def effect_shadow_style(self, widget):
|
||||
effect_shadow = QtWidgets.QGraphicsDropShadowEffect(self)
|
||||
effect_shadow.setOffset(12, 12) # 偏移
|
||||
effect_shadow.setBlurRadius(128) # 阴影半径
|
||||
effect_shadow.setColor(QColor(155, 230, 237, 150)) # 阴影颜色
|
||||
widget.setGraphicsEffect(effect_shadow)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
myWin = LOGIN()
|
||||
myWin.show()
|
||||
sys.exit(app.exec_())
|
BIN
Paradigm/msvcrt.dll
Normal file
BIN
Paradigm/qt5core.dll
Normal file
505226
Paradigm/resources_rc.py
Normal file
229
Paradigm/run.py
Normal file
@ -0,0 +1,229 @@
|
||||
'''
|
||||
Author: ProtoDrive000
|
||||
Date: 2023-04-01 14:06:58
|
||||
LastEditTime: 2023-04-01 19:10:03
|
||||
Description:
|
||||
FilePath: \BCI_Timer\run.py
|
||||
Copyright © : 2021年 赛博智能车实验室. All rights reserved.
|
||||
'''
|
||||
import os
|
||||
from login import LOGIN
|
||||
from Paradigm import SSVEPApp
|
||||
import sys
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from PyQt5.QtWidgets import *
|
||||
import time
|
||||
from ui.ui import initUI
|
||||
# def exit(ssvep_win):
|
||||
# if ssvep_win.rec_flag:
|
||||
# ssvep_win.receiver.stop()
|
||||
# ssvep_win.close()
|
||||
# login_Win.show()
|
||||
|
||||
def get_device_serial_number():
|
||||
if os.name == 'nt': # 如果是 Windows
|
||||
import platform
|
||||
if platform.system() == 'Windows':
|
||||
import wmi
|
||||
w = wmi.WMI()
|
||||
for physical_disk in w.Win32_DiskDrive():
|
||||
return physical_disk.SerialNumber
|
||||
elif os.name == 'posix': # 如果是类 Unix(Linux、macOS 等)
|
||||
try:
|
||||
with open('/sys/class/dmi/id/product_uuid', 'r') as file:
|
||||
return file.read().strip()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return None
|
||||
|
||||
def sign_confirm():
|
||||
tmp = [login_Win.reg_name.text(), login_Win.gender.text(), login_Win.age.text(), login_Win.reg_password.text()]
|
||||
flag = True
|
||||
for item in tmp:
|
||||
if item == '':
|
||||
QMessageBox.information(login_Win, "提示", "请填入完整信息", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||
flag = False
|
||||
if login_Win.reg_password.text() != login_Win.password_confirm.text():
|
||||
QMessageBox.information(login_Win, "提示", "两次密码输入不符", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||
flag = False
|
||||
|
||||
uuid = get_device_serial_number()
|
||||
print("uuid:", uuid)
|
||||
# if uuid == "0025_38BC_11B7_A3B4." or uuid == "0025_38B3_31B7_51C3.":
|
||||
# flag = True
|
||||
# QMessageBox.information(login_Win, "提示", "欢迎管理员", QMessageBox.Yes | QMessageBox.No,
|
||||
# QMessageBox.Yes)
|
||||
# else:
|
||||
# flag = False
|
||||
# QMessageBox.information(login_Win, "提示", "此设备尚未注册,请联系秦珂", QMessageBox.Yes | QMessageBox.No,
|
||||
# QMessageBox.Yes)
|
||||
|
||||
if flag:
|
||||
# ssvep_win = SSVEPApp()
|
||||
now_Time = str(time.strftime('%Y-%m-%d %H:%M:%S'))
|
||||
tmp.append(now_Time)
|
||||
login_Win.data.append(tmp)
|
||||
QMessageBox.information(login_Win, "提示", "注册成功!", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||
login_Win.access = True
|
||||
pd.DataFrame(data=login_Win.data).to_csv('./usr/data.csv')
|
||||
tmp2 = []
|
||||
tmp1 = [login_Win.reg_name.text(), login_Win.reg_password.text(), now_Time, '成功注册']
|
||||
tmp2.append(tmp1)
|
||||
try:
|
||||
df = pd.read_csv('./usr/login.csv')
|
||||
tmp_data = np.array(df) # 先将数据框转换为数组
|
||||
tmp_data = np.delete(tmp_data, 0, axis=1).tolist()
|
||||
tmp_data.append(tmp1)
|
||||
pd.DataFrame(data=tmp_data).to_csv('./usr/login.csv')
|
||||
except:
|
||||
pd.DataFrame(data=tmp2).to_csv('./usr/login.csv')
|
||||
|
||||
|
||||
if login_Win.bound_wave.currentText() == "正弦波":
|
||||
mywave = 0
|
||||
print("正弦波")
|
||||
else:
|
||||
mywave = 1
|
||||
print("方波")
|
||||
|
||||
button_text = login_Win.plainTextEdit_buttons.toPlainText()
|
||||
# 去除首尾的单引号并按逗号分隔
|
||||
split_strings = button_text.strip("'").split("', '")
|
||||
# 将结果放入列表
|
||||
button_text = [s.strip("'") for s in split_strings]
|
||||
languages = [
|
||||
'french', 'german', 'danish', 'dutch', 'english',
|
||||
'finnish', 'greek', 'italian', 'portuguese', 'spanish', 'swedish'
|
||||
]
|
||||
if login_Win.comboBox_ssvep_mode.currentText() in languages:
|
||||
pymod = login_Win.comboBox_ssvep_mode.currentText()
|
||||
else:
|
||||
pymod = 'chinese'
|
||||
if login_Win.checkBox_tri.isChecked():
|
||||
login_Win.checkBox_onlinedata.setChecked(True)
|
||||
login_Win.checkBox_analysis.setChecked(True)
|
||||
else:
|
||||
login_Win.checkBox_onlinedata.setChecked(False)
|
||||
login_Win.checkBox_analysis.setChecked(False)
|
||||
channel_text = login_Win.plainTextEdit_channel.toPlainText()
|
||||
channel_text = [int(x.strip()) for x in channel_text.split(',')]
|
||||
ssvep_win.initPara(Fs=int(login_Win.lineEdit_Fs.text()), tri_flag=login_Win.checkBox_tri.isChecked(),
|
||||
fsc_flag=login_Win.checkBox_fullscreen.isChecked(),
|
||||
rec_flag=login_Win.checkBox_onlinedata.isChecked(),
|
||||
cal_flag=login_Win.checkBox_analysis.isChecked(), my_wave=mywave,
|
||||
fre=int(login_Win.lineEdit_screenflash.text()),
|
||||
rows=int(login_Win.lineEdit_rows.text()), cols=int(login_Win.lineEdit_cols.text()),
|
||||
sta_fre=int(login_Win.lineEdit_sta_fre.text()),
|
||||
end_fre=int(login_Win.lineEdit_end_fre.text()),
|
||||
v_space=float(login_Win.lineEdit_v_space.text()),
|
||||
tlen=int(login_Win.lineEdit_tlen.text()), teeth=int(login_Win.lineEdit_tooth.text()),
|
||||
channel=channel_text, buttonNames=button_text,
|
||||
pinyin_mod=pymod)
|
||||
QApplication.processEvents()
|
||||
initUI(ssvep_win)
|
||||
ssvep_win.initEvent()
|
||||
# ssvep_win.buttonBelow.clicked.connect(lambda: exit(ssvep_win))
|
||||
ssvep_win.show()
|
||||
login_Win.close()
|
||||
def my_login():
|
||||
|
||||
sum_data = sum(login_Win.data, [])
|
||||
try:
|
||||
location = sum_data.index(login_Win.name.text())
|
||||
print("找到账号")
|
||||
login_Win.access = True
|
||||
except:
|
||||
print("不存在此帐号")
|
||||
QMessageBox.information(login_Win, "提示", "不存在此帐号", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||
login_Win.access = False
|
||||
uuid = get_device_serial_number()
|
||||
print("uuid:", uuid)
|
||||
# if uuid == "0025_38BC_11B7_A3B4." or uuid == "0025_38B3_31B7_51C3.":
|
||||
# flag = True
|
||||
# QMessageBox.information(login_Win, "提示", "欢迎管理员", QMessageBox.Yes | QMessageBox.No,
|
||||
# QMessageBox.Yes)
|
||||
# else:
|
||||
# flag = False
|
||||
# QMessageBox.information(login_Win, "提示", "此设备尚未注册,请联系秦珂", QMessageBox.Yes | QMessageBox.No,
|
||||
# QMessageBox.Yes)
|
||||
if login_Win.access:
|
||||
|
||||
if sum_data[location+3] == login_Win.password.text():
|
||||
print("成功登录")
|
||||
QMessageBox.information(login_Win, "提示", "成功登录", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||
now_Time = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
tmp = []
|
||||
tmp1 = [login_Win.name.text(), login_Win.password.text(), now_Time, '成功登录']
|
||||
tmp.append(tmp1)
|
||||
try:
|
||||
df = pd.read_csv('./usr/login.csv')
|
||||
tmp_data = np.array(df) # 先将数据框转换为数组
|
||||
tmp_data = np.delete(tmp_data, 0, axis=1).tolist()
|
||||
tmp_data.append(tmp1)
|
||||
pd.DataFrame(data=tmp_data).to_csv('./usr/login.csv')
|
||||
except:
|
||||
pd.DataFrame(data=tmp).to_csv('./usr/login.csv')
|
||||
|
||||
if login_Win.bound_wave.currentText() == "正弦波":
|
||||
mywave = 0
|
||||
print("正弦波")
|
||||
else:
|
||||
mywave = 1
|
||||
print("方波")
|
||||
button_text = login_Win.plainTextEdit_buttons.toPlainText()
|
||||
# 去除首尾的单引号并按逗号分隔
|
||||
split_strings = button_text.strip("'").split("', '")
|
||||
# 将结果放入列表
|
||||
button_text = [s.strip("'") for s in split_strings]
|
||||
languages = [
|
||||
'french', 'german', 'danish', 'dutch', 'english',
|
||||
'finnish', 'greek', 'italian', 'portuguese', 'spanish', 'swedish'
|
||||
]
|
||||
if login_Win.comboBox_ssvep_mode.currentText() in languages:
|
||||
pymod = login_Win.comboBox_ssvep_mode.currentText()
|
||||
else:
|
||||
pymod = 'chinese'
|
||||
if login_Win.checkBox_tri.isChecked():
|
||||
login_Win.checkBox_onlinedata.setChecked(True)
|
||||
login_Win.checkBox_analysis.setChecked(True)
|
||||
else:
|
||||
login_Win.checkBox_onlinedata.setChecked(False)
|
||||
login_Win.checkBox_analysis.setChecked(False)
|
||||
channel_text = login_Win.plainTextEdit_channel.toPlainText()
|
||||
channel_text = [int(x.strip()) for x in channel_text.split(',')]
|
||||
ssvep_win.initPara(Fs=int(login_Win.lineEdit_Fs.text()), tri_flag=login_Win.checkBox_tri.isChecked(),
|
||||
fsc_flag=login_Win.checkBox_fullscreen.isChecked(),
|
||||
rec_flag=login_Win.checkBox_onlinedata.isChecked(),
|
||||
cal_flag=login_Win.checkBox_analysis.isChecked(), pinyin_mod=pymod,
|
||||
my_wave=mywave, fre=int(login_Win.lineEdit_screenflash.text()),
|
||||
rows=int(login_Win.lineEdit_rows.text()), cols=int(login_Win.lineEdit_cols.text()),
|
||||
sta_fre=int(login_Win.lineEdit_sta_fre.text()),
|
||||
end_fre=int(login_Win.lineEdit_end_fre.text()),
|
||||
v_space=float(login_Win.lineEdit_v_space.text()),
|
||||
tlen=int(login_Win.lineEdit_tlen.text()), teeth=int(login_Win.lineEdit_tooth.text()),
|
||||
channel=channel_text, buttonNames=button_text)
|
||||
|
||||
QApplication.processEvents()
|
||||
initUI(ssvep_win)
|
||||
ssvep_win.initEvent()
|
||||
# ssvep_win.buttonBelow.clicked.connect(lambda: exit(ssvep_win))
|
||||
ssvep_win.show()
|
||||
login_Win.close()
|
||||
else:
|
||||
print("密码错误")
|
||||
QMessageBox.information(login_Win, "提示", "密码错误", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||
login_Win.access = False
|
||||
|
||||
|
||||
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
login_Win = LOGIN()
|
||||
ssvep_win = SSVEPApp()
|
||||
login_Win.Login.clicked.connect(my_login)
|
||||
login_Win.confirm.clicked.connect(sign_confirm)
|
||||
|
||||
login_Win.show()
|
||||
sys.exit(app.exec_())
|
55
Paradigm/src/EKF.py
Normal file
@ -0,0 +1,55 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def EKF(measurement_data):
|
||||
# Define state transition matrix A, observation matrix H, and initial values
|
||||
A = np.array([[1.0]])
|
||||
H = np.array([[1.0]])
|
||||
x_hat = np.array([0.0]) # Initial state estimate
|
||||
P = np.array([[1.0]]) # Initial state covariance
|
||||
Q = np.array([[0.01]]) # Process noise covariance
|
||||
R = np.array([[0.1]]) # Measurement noise covariance
|
||||
|
||||
# EKF algorithm
|
||||
estimated_states = []
|
||||
|
||||
for z in measurement_data:
|
||||
# Prediction step
|
||||
x_hat_predicted = np.dot(A, x_hat)
|
||||
P_predicted = np.dot(np.dot(A, P), A.T) + Q
|
||||
|
||||
# Measurement update step
|
||||
K = np.dot(np.dot(P_predicted, H.T), np.linalg.inv(np.dot(np.dot(H, P_predicted), H.T) + R))
|
||||
x_hat = x_hat_predicted + np.dot(K, z - np.dot(H, x_hat_predicted))
|
||||
P = P_predicted - np.dot(np.dot(K, H), P_predicted)
|
||||
|
||||
estimated_states.append(x_hat[0])
|
||||
return estimated_states
|
||||
|
||||
if __name__ == '__main__':
|
||||
# estimated_states now contains the EKF estimates for your data
|
||||
# Simulated measurements (replace with your actual data)
|
||||
measurement_data = np.random.randint(0, 100, size=750).tolist()
|
||||
estimated_states = EKF(measurement_data)
|
||||
|
||||
|
||||
# 创建一个新的图形
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
# 绘制第一个列表
|
||||
plt.plot(measurement_data, label='measurement', color='blue')
|
||||
|
||||
# 绘制第二个列表
|
||||
plt.plot(estimated_states, label='estimated', color='red')
|
||||
|
||||
# 添加图例
|
||||
plt.legend()
|
||||
|
||||
# 添加标题和标签
|
||||
plt.title('Comparison of Two Lists')
|
||||
plt.xlabel('Index')
|
||||
plt.ylabel('Value')
|
||||
|
||||
# 显示图形
|
||||
plt.show()
|
187
Paradigm/src/FBCCA2.py
Normal file
@ -0,0 +1,187 @@
|
||||
from scipy.signal import filtfilt, resample, butter, lfilter, decimate
|
||||
import os
|
||||
import sys
|
||||
sys.path.append("..") # 添加上级目录到模块搜索路径
|
||||
from src.filterbank import filterbank
|
||||
from scipy.fftpack import fft,ifft
|
||||
from matplotlib.pylab import plt
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from scipy.linalg import svd
|
||||
import math
|
||||
from scipy import signal
|
||||
|
||||
|
||||
def check_files_format(path):
|
||||
filename = []
|
||||
pathname = []
|
||||
|
||||
if len(path) == 0:
|
||||
raise TypeError('please select valid file')
|
||||
|
||||
elif len(path) == 1:
|
||||
(temppathname, tempfilename) = os.path.split(path[0])
|
||||
if 'edf' in tempfilename:
|
||||
filename.append(tempfilename)
|
||||
pathname.append(temppathname)
|
||||
return filename, pathname
|
||||
elif 'bdf' in tempfilename:
|
||||
raise TypeError('unsupport only one neuracle-bdf file')
|
||||
else:
|
||||
raise TypeError('not support such file format')
|
||||
|
||||
else:
|
||||
temp = []
|
||||
temppathname = r''
|
||||
evtfile = []
|
||||
idx = np.zeros((len(path) - 1,))
|
||||
for i, ele in enumerate(path):
|
||||
(temppathname, tempfilename) = os.path.split(ele)
|
||||
if 'data' in tempfilename:
|
||||
temp.append(tempfilename)
|
||||
if len(tempfilename.split('.')) > 2:
|
||||
try:
|
||||
idx[i] = (int(tempfilename.split('.')[1]))
|
||||
except:
|
||||
raise TypeError('no such kind file')
|
||||
else:
|
||||
idx[i] = 0
|
||||
elif 'evt' in tempfilename:
|
||||
evtfile.append(tempfilename)
|
||||
|
||||
pathname.append(temppathname)
|
||||
datafile = [temp[i] for i in np.argsort(idx)]
|
||||
|
||||
if len(evtfile) == 0:
|
||||
raise TypeError('not found evt.bdf file')
|
||||
|
||||
if len(datafile) == 0:
|
||||
raise TypeError('not found data.bdf file')
|
||||
elif len(datafile) > 1:
|
||||
print('current readbdfdata() only support continue one data.bdf ')
|
||||
return filename, pathname
|
||||
else:
|
||||
filename.append(datafile[0])
|
||||
filename.append(evtfile[0])
|
||||
return filename, pathname
|
||||
|
||||
|
||||
def cca_reference(list_freqs, fs, num_smpls, num_harms=3):
|
||||
num_freqs = len(list_freqs)
|
||||
tidx = np.arange(0, num_smpls) / fs # time index
|
||||
|
||||
y_ref = np.zeros((num_freqs, 2 * num_harms, num_smpls))
|
||||
for freq_i in range(num_freqs):
|
||||
tmp = []
|
||||
for harm_i in range(1, num_harms + 1):
|
||||
stim_freq = list_freqs[freq_i] # in HZ
|
||||
# Sin and Cos
|
||||
tmp.extend([np.sin(2 * np.pi * tidx * harm_i * stim_freq),
|
||||
np.cos(2 * np.pi * tidx * harm_i * stim_freq)])
|
||||
y_ref[freq_i] = tmp # 2*num_harms because include both sin and cos
|
||||
|
||||
return y_ref
|
||||
|
||||
|
||||
'''
|
||||
Base on fbcca, but adapt to our input format
|
||||
'''
|
||||
import numpy as np
|
||||
from scipy.signal import butter, lfilter, decimate
|
||||
|
||||
|
||||
def butter_lowpass(cutoff, fs, order=5):
|
||||
nyq = 0.5 * fs
|
||||
normal_cutoff = cutoff / nyq
|
||||
b, a = butter(order, normal_cutoff, btype='low', analog=False)
|
||||
return b, a
|
||||
|
||||
|
||||
def lowpass_filter(data, cutoff, fs, order=5):
|
||||
b, a = butter_lowpass(cutoff, fs, order=order)
|
||||
if len(data) < max(len(b), len(a)):
|
||||
return data # 如果数据点不足,返回原始数据
|
||||
y = lfilter(b, a, data, axis=0) # 滤波所有通道
|
||||
return y
|
||||
|
||||
|
||||
def downsample(data, original_fs, target_fs):
|
||||
factor = original_fs // target_fs
|
||||
# Lowpass filter
|
||||
cutoff = target_fs / 2.0 # Nyquist frequency
|
||||
filtered_data = lowpass_filter(data, cutoff, original_fs, order=5)
|
||||
|
||||
# Decimate (downsample) each channel
|
||||
downsampled_data = decimate(filtered_data, factor, axis=1, ftype='iir')
|
||||
|
||||
return downsampled_data
|
||||
|
||||
|
||||
def fbcca_realtime(data, list_freqs, org_fs, num_harms=3, num_fbs=3):
|
||||
target_fs = 250 # 目标采样率
|
||||
|
||||
# 检查原始采样率是否等于目标采样率
|
||||
if org_fs != target_fs:
|
||||
# 如果不相等,则执行降采样
|
||||
data = downsample(data, original_fs=org_fs, target_fs=target_fs)
|
||||
else:
|
||||
target_fs = org_fs
|
||||
|
||||
fb_coefs = [math.pow(i, -1.25) + 0.25 for i in range(1, num_fbs + 1)]
|
||||
y_ref = cca_reference(list_freqs, target_fs, data.shape[1], num_harms)
|
||||
r = np.zeros((num_fbs, len(list_freqs)))
|
||||
|
||||
fbdata = filterbank(data, target_fs, num_fbs)
|
||||
|
||||
for fb_i in range(num_fbs):
|
||||
testdata = fbdata[fb_i]
|
||||
_, p = my_cca(testdata, y_ref)
|
||||
r[fb_i] = p
|
||||
|
||||
rho = np.dot(fb_coefs, r)
|
||||
result = np.argmax(rho)
|
||||
top_3_indices = np.argsort(rho)[-3:][::-1] # 找出最大的三个索引,并按降序排列
|
||||
|
||||
# 获取最大的三个索引对应的结果
|
||||
result_top_3 = top_3_indices
|
||||
for i in range(3):
|
||||
print(f"第{i},{list_freqs[result_top_3[i]]}Hz")
|
||||
return result, abs(rho[result])
|
||||
|
||||
|
||||
def my_cca(input_x, input_y):
|
||||
p = []
|
||||
Q_x, _ = np.linalg.qr(input_x.T)
|
||||
for i_target in input_y:
|
||||
i_target = i_target.T
|
||||
Q_y, _ = np.linalg.qr(i_target)
|
||||
data_svd = np.dot(Q_x.T, Q_y)
|
||||
try:
|
||||
U, S, V = svd(data_svd)
|
||||
except np.linalg.LinAlgError:
|
||||
print("SVD did not converge for this pair of matrices.")
|
||||
continue
|
||||
rho = S[0]
|
||||
p.append(rho)
|
||||
|
||||
if p:
|
||||
result = p.index(max(p)) + 1
|
||||
else:
|
||||
result = None # 如果没有成功计算出任何结果
|
||||
|
||||
return result, p
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# 加载npz文件
|
||||
channel = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
freqlist = [15.0, 14.0, 13.0, 12.0, 11.0, 10.0, 9.0, 8.0, 15.2, 14.2, 13.2, 12.2, 11.2, 10.2, 9.2, 8.2, 15.4, 14.4,
|
||||
13.4, 12.4, 11.4, 10.4, 9.4, 8.4, 15.6, 14.6, 13.6, 12.6, 11.6, 10.6, 9.6, 8.6, 15.8, 14.8, 13.8, 12.8,
|
||||
11.8, 10.8, 9.8, 8.8]
|
||||
for item in freqlist:
|
||||
eeg_data = np.load(f'data_save/{item}Hz.npy')
|
||||
eeg_data = np.array([eeg_data[i - 1] for i in channel])
|
||||
result, score = fbcca_realtime(eeg_data, freqlist, 250, num_harms=3, num_fbs=5)
|
||||
# print(result, score)
|
||||
print(f"频率为:{freqlist[result]}Hz, 实际为:{item}Hz, 得分:{score}")
|
50
Paradigm/src/FFT_test.m
Normal file
@ -0,0 +1,50 @@
|
||||
clc,clear,close all;
|
||||
% 读取CSV文件
|
||||
data = readtable('15Hz_Raw.csv');
|
||||
|
||||
% 提取想要的列范围
|
||||
start_col = 2; % 第二列
|
||||
end_col = size(data, 2); % 最后一列
|
||||
|
||||
start_row = 2; % 第二列
|
||||
end_row = size(data, 1); % 最后一列
|
||||
|
||||
% 提取所需范围内的数据
|
||||
selected_data = data(start_row:end_row, start_col:end_col);
|
||||
% 生成示例数据
|
||||
data = table2array(selected_data);
|
||||
%%
|
||||
|
||||
% 计算FFT
|
||||
fs = 250; % 采样频率
|
||||
L = 1000; % 信号长度
|
||||
f = fs * (0:(L/2)) / L; % 频率范围
|
||||
|
||||
% 指定要标记的频率
|
||||
freq_to_label = [15, 30, 45];
|
||||
|
||||
figure;
|
||||
for i = 1:7
|
||||
Y = fft(data(i, :));
|
||||
P2 = abs(Y / L);
|
||||
P1 = P2(1:L/2 + 1);
|
||||
P1(2:end-1) = 2 * P1(2:end-1);
|
||||
|
||||
subplot(3, 3, i);
|
||||
plot(f, P1, 'LineWidth', 1.5); % 增加线条粗细
|
||||
ylim([-0.00001, 0.00001]); % 设置纵坐标范围
|
||||
xlim([-1, 60]); % 设置横坐标范围
|
||||
title(['通道 ', num2str(i)]);
|
||||
xlabel('Frequency (Hz)');
|
||||
ylabel('Magnitude');
|
||||
|
||||
% 添加数据标签
|
||||
for freq = freq_to_label
|
||||
[~, index] = min(abs(f - freq)); % 找到最接近指定频率的索引
|
||||
dip_str = num2str(freq) + "Hz"
|
||||
|
||||
text(f(index), P1(index), dip_str, 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'center', 'FontSize', 10);
|
||||
end
|
||||
end
|
||||
|
||||
sgtitle('FFT of 7 sets of data');
|
BIN
Paradigm/src/__pycache__/EKF.cpython-38.pyc
Normal file
BIN
Paradigm/src/__pycache__/FBCCA2.cpython-310.pyc
Normal file
BIN
Paradigm/src/__pycache__/FBCCA2.cpython-38.pyc
Normal file
BIN
Paradigm/src/__pycache__/filterbank.cpython-310.pyc
Normal file
BIN
Paradigm/src/__pycache__/filterbank.cpython-38.pyc
Normal file
BIN
Paradigm/src/__pycache__/stream.cpython-310.pyc
Normal file
BIN
Paradigm/src/__pycache__/stream.cpython-311.pyc
Normal file
BIN
Paradigm/src/__pycache__/stream.cpython-38.pyc
Normal file
BIN
Paradigm/src/__pycache__/trie.cpython-310.pyc
Normal file
BIN
Paradigm/src/__pycache__/trie.cpython-38.pyc
Normal file
BIN
Paradigm/src/__pycache__/tts.cpython-310.pyc
Normal file
BIN
Paradigm/src/__pycache__/tts.cpython-38.pyc
Normal file
BIN
Paradigm/src/__pycache__/ui.cpython-311.pyc
Normal file
BIN
Paradigm/src/__pycache__/ui.cpython-38.pyc
Normal file
1
Paradigm/src/coordinates.json
Normal file
34
Paradigm/src/filterbank.py
Normal file
@ -0,0 +1,34 @@
|
||||
import warnings
|
||||
from scipy import signal
|
||||
import numpy as np
|
||||
|
||||
|
||||
def filterbank(eeg, samp_rate, num_fbs):
|
||||
y = []
|
||||
# 设计陷波滤波器
|
||||
f0 = 50
|
||||
q = 30
|
||||
high_pass = 90
|
||||
passband = [8, 16, 24, 32, 40, 48, 56, 64, 72, 80]
|
||||
# passband = [6, 14, 22, 30, 38, 46, 54, 62, 70, 78]
|
||||
notch_b, notch_a = signal.iircomb(f0, q, ftype='notch', fs=samp_rate)
|
||||
eeg = signal.filtfilt(notch_b, notch_a, eeg)
|
||||
# 设计陷波滤波器
|
||||
# center_freq = 50.0 # 中心频率为50Hz
|
||||
# Q = 2.0 # 带宽参数
|
||||
# b, a = signal.iirnotch(center_freq, Q, samp_rate)
|
||||
# # 50Hz工频
|
||||
# eeg = signal.lfilter(b, a, eeg)
|
||||
|
||||
for fb_i in range(num_fbs):
|
||||
[N, Wn] = signal.cheb1ord([passband[fb_i], high_pass], [passband[fb_i]-2, high_pass + 10], 3, 40, fs=samp_rate)
|
||||
[B, A] = signal.cheby1(N, 0.5, Wn, 'bandpass', fs=samp_rate)
|
||||
y.append(signal.filtfilt(B, A, eeg))
|
||||
# fs = samp_rate/2
|
||||
# Wn = [(passband[fb_i]) / fs, high_pass / fs]
|
||||
# bandpass_B, bandpass_A = signal.butter(3, Wn, btype='bandpass')
|
||||
# y.append(signal.filtfilt(bandpass_B, bandpass_A, eeg))
|
||||
|
||||
y = np.array(y)
|
||||
return y
|
||||
|
BIN
Paradigm/src/logo.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
Paradigm/src/mylogo.jpg
Normal file
After Width: | Height: | Size: 174 KiB |
26
Paradigm/src/split.py
Normal file
@ -0,0 +1,26 @@
|
||||
from pypinyin import pinyin, Style
|
||||
|
||||
def split_pinyin(input_string):
|
||||
# 使用 pypinyin 将汉字字符串转换为拼音
|
||||
pinyin_list = pinyin(input_string, style=Style.NORMAL)
|
||||
|
||||
result = []
|
||||
current_word = ""
|
||||
|
||||
# 遍历拼音列表,按照拼音规则划分字符串
|
||||
for pinyin_word in pinyin_list:
|
||||
pinyin_word = pinyin_word[0]
|
||||
current_word += pinyin_word
|
||||
if pinyin_word.isalpha() and len(pinyin_word) == 1:
|
||||
result.append(current_word)
|
||||
current_word = ""
|
||||
|
||||
# 处理最后一个拼音
|
||||
if current_word:
|
||||
result.append(current_word)
|
||||
|
||||
return result
|
||||
|
||||
input_string = "nihao"
|
||||
pinyin_segments = split_pinyin(input_string)
|
||||
print(pinyin_segments) # 输出 ["ni", "hao"]
|
108
Paradigm/src/stream.py
Normal file
@ -0,0 +1,108 @@
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
import numpy as np
|
||||
sys.path.append("..") # 添加上级目录到模块搜索路径
|
||||
from lib.neuracle_lib.dataServer import DataServerThread
|
||||
import scipy.fftpack
|
||||
|
||||
class REC():
|
||||
def __init__(self):
|
||||
self.thread_data_server = None
|
||||
|
||||
def init_neuracle(self, time_buffer=3):
|
||||
# 创建线程
|
||||
## 配置设备 + ['TRG']
|
||||
neuracle = dict(device_name='Neuracle', hostname='127.0.0.1', port=8712,
|
||||
srate=1000, n_chan=65)
|
||||
|
||||
target_device = neuracle
|
||||
## 初始化 DataServerThread 线程
|
||||
self.thread_data_server = DataServerThread(device=target_device['device_name'], n_chan=target_device['n_chan'],
|
||||
srate=target_device['srate'], t_buffer=time_buffer)
|
||||
### 建立TCP/IP连接
|
||||
notconnect = self.thread_data_server.connect(hostname=target_device['hostname'], port=target_device['port'])
|
||||
if notconnect:
|
||||
raise TypeError("Can't connect recorder, Please open the hostport ")
|
||||
else:
|
||||
# 启动线程
|
||||
print('thread Ready!')
|
||||
|
||||
def start(self):
|
||||
if not self.thread_data_server.is_alive():
|
||||
# 启动线程
|
||||
self.thread_data_server.Daemon = True
|
||||
self.thread_data_server.start()
|
||||
print('Data server connected')
|
||||
|
||||
def stop(self):
|
||||
self.thread_data_server.stop()
|
||||
print('Data server STOP')
|
||||
|
||||
def get_data(self):
|
||||
data = self.thread_data_server.GetBufferData()
|
||||
self.thread_data_server.ResetDataLenCount()
|
||||
return data
|
||||
|
||||
|
||||
def calculate_frequency_energy(ndarray, freq_range=(1, 100), sampling_rate=1000):
|
||||
"""
|
||||
检测ndarray数组在指定频率范围内的频域能量。
|
||||
|
||||
参数:
|
||||
ndarray: 输入的一维numpy数组,形状为(1, 3000)
|
||||
freq_range: 频率范围元组,默认值为(1, 100)
|
||||
sampling_rate: 采样率,默认值为1000
|
||||
|
||||
返回:
|
||||
频域能量值
|
||||
"""
|
||||
# 确保输入数组是一维的
|
||||
if ndarray.ndim != 1:
|
||||
raise ValueError("输入数组必须是一维的")
|
||||
|
||||
# 计算FFT
|
||||
fft_values = scipy.fftpack.fft(ndarray)
|
||||
|
||||
# 计算对应的频率
|
||||
freqs = scipy.fftpack.fftfreq(len(ndarray), d=1 / sampling_rate)
|
||||
|
||||
# 仅取正频率部分
|
||||
positive_freqs = freqs[:len(freqs) // 2]
|
||||
positive_fft_values = np.abs(fft_values[:len(fft_values) // 2])
|
||||
|
||||
# 找到指定频率范围内的频率索引
|
||||
idx = np.where((positive_freqs >= freq_range[0]) & (positive_freqs <= freq_range[1]))[0]
|
||||
|
||||
# 计算指定频率范围内的能量
|
||||
energy = np.sum(positive_fft_values[idx] ** 2)
|
||||
|
||||
return energy
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = REC()
|
||||
# 设置需要缓存的时间长度
|
||||
app.init_neuracle(3) # 单位为秒
|
||||
app.start() # 开始线程
|
||||
data = None
|
||||
tp7_th = -9999
|
||||
while True:
|
||||
logging.info("-延时3秒-")
|
||||
time.sleep(3)
|
||||
data = app.get_data() # 数据大小 size = time * 65 * 1000
|
||||
logging.info("接收到的数据大小size = time * 65 * 1000:" + str(data.size))
|
||||
result = np.argmin(data[40])
|
||||
freq_range = (1, 30) # 频率范围1~100
|
||||
sampling_rate = 1000 # 假设采样率为1000Hz
|
||||
energy = calculate_frequency_energy(data[40], freq_range, sampling_rate) // 100000000
|
||||
print(f"频率范围 {freq_range[0]}~{freq_range[1]} Hz 内的频域能量: {energy}")
|
||||
# print("差值", str(-data[40][result] + tp7_th))
|
||||
if tp7_th - data[40][result] > 100:
|
||||
# print("咬牙")
|
||||
print("咬牙")
|
||||
print("开启闪烁")
|
||||
tp7_th = data[40][result]
|
||||
else:
|
||||
tp7_th = data[40][result]
|
||||
# print("接收到的数据大小size = time * 65 * 1000:",str(data.size))
|
||||
app.thread_data_server.stop() # 停止线程
|
92
Paradigm/src/trie.py
Normal file
@ -0,0 +1,92 @@
|
||||
class TrieNode:
|
||||
def __init__(self):
|
||||
self.children = {}
|
||||
self.is_end_of_word = False
|
||||
|
||||
|
||||
class Trie:
|
||||
def __init__(self):
|
||||
self.root = TrieNode()
|
||||
self.standard_inputs = ["a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian",
|
||||
"biang", "biao", "bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "cen",
|
||||
"ceng", "cha", "chai", "chan", "chang", "chao", "che", "chen", "cheng", "chi", "chong", "chou",
|
||||
"chu", "chua", "chuai", "chuan", "chuang", "chui", "chun", "chuo", "ci", "cong", "cou", "cu",
|
||||
"cuan", "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "dei", "den", "deng", "di",
|
||||
"dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan", "dui", "dun", "duo", "e", "ei",
|
||||
"en", "eng", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fiao", "fo", "fou", "fu", "ga",
|
||||
"gai", "gan", "gang", "gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan",
|
||||
"guang", "gui", "gun", "guo", "ha", "hai", "han", "hang", "hao", "he", "hei", "hen", "heng", "hong",
|
||||
"hou", "hu", "hua", "huai", "huan", "huang", "hui", "hun", "huo", "ji", "jia", "jian", "jiang",
|
||||
"jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan", "jue", "jun", "ka", "kai", "kan",
|
||||
"kang", "kao", "ke", "kei", "ken", "keng", "kong", "kou", "ku", "kua", "kuai", "kuan", "kuang",
|
||||
"kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng", "li", "lia", "lian",
|
||||
"liang", "liao", "lie", "lin", "ling", "liu", "lo", "long", "lou", "lu", "luan", "lun", "luo", "lü",
|
||||
"lüe", "ma", "mai", "man", "mang", "mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie",
|
||||
"min", "ming", "miu", "mo", "mou", "mu", "na", "nai", "nan", "nang", "nao", "ne", "nei", "nen",
|
||||
"neng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", "niu", "nong", "nou", "nu", "nuan",
|
||||
"nuo", "nü", "nüe", "o", "ong", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng", "pi",
|
||||
"pian", "piao", "pie", "pin", "ping", "po", "pou", "pu", "qi", "qia", "qian", "qiang", "qiao",
|
||||
"qie", "qin", "qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re", "ren",
|
||||
"reng", "ri", "rong", "rou", "ru", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao",
|
||||
"se", "sen", "seng", "sha", "shai", "shan", "shang", "shao", "she", "shei", "shen", "sheng", "shi",
|
||||
"shou", "shu", "shua", "shuai", "shuan", "shuang", "shui", "shun", "shuo", "si", "song", "sou",
|
||||
"su", "suan", "sui", "sun", "suo", "ta", "tai", "tan", "tang", "tao", "te", "tei", "teng", "ti",
|
||||
"tian", "tiao", "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan",
|
||||
"wang", "wei", "wen", "weng", "wo", "wu", "xi", "xia", "xian", "xiang", "xiao", "xie", "xin",
|
||||
"xing", "xiong", "xiu", "xu", "xuan", "xue", "xun", "ya", "yai", "yan", "yang", "yao", "ye", "yi",
|
||||
"yin", "ying", "yo", "yong", "you", "yu", "yuan", "yue", "yun", "za", "zai", "zan", "zang", "zao",
|
||||
"ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang", "zhao", "zhe", "zhei", "zhen", "zheng",
|
||||
"zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun", "zhuo", "zi",
|
||||
"zong", "zou", "zu", "zuan", "zui", "zun", "zuo"]
|
||||
for word in self.standard_inputs:
|
||||
self.insert(word)
|
||||
|
||||
# 插入节点
|
||||
def insert(self, word):
|
||||
node = self.root
|
||||
for char in word:
|
||||
if char not in node.children:
|
||||
node.children[char] = TrieNode()
|
||||
node = node.children[char]
|
||||
node.is_end_of_word = True
|
||||
|
||||
# 查询首字母起全字匹配
|
||||
def search(self, word):
|
||||
node = self.root
|
||||
for char in word:
|
||||
if char not in node.children:
|
||||
return False
|
||||
node = node.children[char]
|
||||
if node.is_end_of_word:
|
||||
print("找到了")
|
||||
else:
|
||||
print("没找到")
|
||||
return node.is_end_of_word
|
||||
|
||||
if __name__ == '__main__':
|
||||
# # 创建字典树并插入所有标准输入字符串
|
||||
# trie = Trie()
|
||||
# while True:
|
||||
# # 用户输入的字符串
|
||||
# user_input = input("请输入一个字符串: ")
|
||||
#
|
||||
# # 在字典树中搜索用户输入的字符串
|
||||
# if trie.search(user_input):
|
||||
# print("字符串在列表中找到了。")
|
||||
# else:
|
||||
# print("字符串不在列表中。")
|
||||
# 创建一个包含所有标准输入字符串的列表
|
||||
standard_inputs = ["apple", "banana", "cherry", "date", "fig", "grape", "kiwi", "lemon", "mango", "orange", "pear",
|
||||
"plum"]
|
||||
|
||||
# 创建一个字典(或集合)用于存储标准输入字符串
|
||||
input_set = set(standard_inputs)
|
||||
|
||||
# 用户输入的字符串
|
||||
user_input = input("请输入一个字符串: ")
|
||||
|
||||
# 检查用户输入是否在标准输入字符串列表中
|
||||
if user_input in input_set:
|
||||
print("字符串在列表中找到了。")
|
||||
else:
|
||||
print("字符串不在列表中。")
|
115
Paradigm/src/tts.py
Normal file
@ -0,0 +1,115 @@
|
||||
# coding=utf-8
|
||||
import sys
|
||||
import json
|
||||
|
||||
from urllib.request import urlopen
|
||||
from urllib.request import Request
|
||||
from urllib.error import URLError
|
||||
from urllib.parse import urlencode
|
||||
from urllib.parse import quote_plus
|
||||
import pyaudio
|
||||
import wave
|
||||
|
||||
class DemoError(Exception):
|
||||
pass
|
||||
|
||||
# PER发音人选择, 基础音库:0为度小美,1为度小宇,3为度逍遥,4为度丫丫,
|
||||
# 精品音库:5为度小娇,103为度米朵,106为度博文,110为度小童,111为度小萌,默认为度小美
|
||||
# SPD语速,取值0-15,默认为5中语速
|
||||
# PIT音调,取值0-15,默认为5中语调
|
||||
# VOL音量,取值0-9,默认为5中音量
|
||||
# AUE下载的文件格式, 3:mp3(default) 4: pcm-16k 5: pcm-8k 6. wav
|
||||
|
||||
def TTS(TEXT = "你好", PER=3, SPD=5, PIT = 5, VOL = 5, AUE = 6):
|
||||
API_KEY = ''
|
||||
SECRET_KEY = ''
|
||||
|
||||
FORMATS = {3: "mp3", 4: "pcm", 5: "pcm", 6: "wav"}
|
||||
FORMAT = FORMATS[AUE]
|
||||
|
||||
CUID = "123456PYTHON"
|
||||
|
||||
TTS_URL = 'http://tsn.baidu.com/text2audio'
|
||||
|
||||
""" TOKEN start """
|
||||
|
||||
TOKEN_URL = 'http://aip.baidubce.com/oauth/2.0/token'
|
||||
SCOPE = 'audio_tts_post' # 有此scope表示有tts能力,没有请在网页里勾选
|
||||
print("fetch token begin")
|
||||
params = {'grant_type': 'client_credentials',
|
||||
'client_id': API_KEY,
|
||||
'client_secret': SECRET_KEY}
|
||||
post_data = urlencode(params)
|
||||
post_data = post_data.encode('utf-8')
|
||||
req = Request(TOKEN_URL, post_data)
|
||||
try:
|
||||
f = urlopen(req, timeout=5)
|
||||
result_str = f.read()
|
||||
except URLError as err:
|
||||
print('token http response http code : ' + str(err.code))
|
||||
result_str = err.read()
|
||||
|
||||
result_str = result_str.decode()
|
||||
|
||||
print(result_str)
|
||||
result = json.loads(result_str)
|
||||
print(result)
|
||||
if ('access_token' in result.keys() and 'scope' in result.keys()):
|
||||
if not SCOPE in result['scope'].split(' '):
|
||||
raise DemoError('scope is not correct')
|
||||
print('SUCCESS WITH TOKEN: %s ; EXPIRES IN SECONDS: %s' % (result['access_token'], result['expires_in']))
|
||||
token = result['access_token']
|
||||
|
||||
|
||||
tex = quote_plus(TEXT) # 此处TEXT需要两次urlencode
|
||||
# print(tex)
|
||||
params = {'tok': token, 'tex': tex, 'per': PER, 'spd': SPD, 'pit': PIT, 'vol': VOL, 'aue': AUE, 'cuid': CUID,
|
||||
'lan': 'zh', 'ctp': 1} # lan ctp 固定参数
|
||||
|
||||
data = urlencode(params)
|
||||
# print('test on Web Browser' + TTS_URL + '?' + data)
|
||||
|
||||
req = Request(TTS_URL, data.encode('utf-8'))
|
||||
has_error = False
|
||||
try:
|
||||
f = urlopen(req)
|
||||
result_str = f.read()
|
||||
|
||||
headers = dict((name.lower(), value) for name, value in f.headers.items())
|
||||
|
||||
has_error = ('content-type' not in headers.keys() or headers['content-type'].find('audio/') < 0)
|
||||
except URLError as err:
|
||||
print('asr http response http code : ' + str(err.code))
|
||||
result_str = err.read()
|
||||
has_error = True
|
||||
|
||||
save_file = "error.txt" if has_error else 'result.' + FORMAT
|
||||
with open(save_file, 'wb') as of:
|
||||
of.write(result_str)
|
||||
|
||||
if has_error:
|
||||
|
||||
result_str = str(result_str, 'utf-8')
|
||||
print("tts api error:" + result_str)
|
||||
|
||||
print("result saved as :" + save_file)
|
||||
|
||||
chunk = 1024
|
||||
wf = wave.open('result.'+ FORMAT, 'rb')
|
||||
p = pyaudio.PyAudio()
|
||||
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
|
||||
channels=wf.getnchannels(),
|
||||
rate=wf.getframerate(),
|
||||
output=True)
|
||||
|
||||
data = wf.readframes(chunk)
|
||||
|
||||
while len(data) > 0:
|
||||
stream.write(data)
|
||||
data = wf.readframes(chunk)
|
||||
stream.stop_stream()
|
||||
stream.close()
|
||||
p.terminate()
|
||||
|
||||
if __name__ == '__main__':
|
||||
TTS(TEXT="你好")
|
23
Paradigm/src/wavelet.py
Normal file
@ -0,0 +1,23 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import pywt
|
||||
|
||||
# 生成示例数据,这里使用正弦波信号作为示例
|
||||
time = np.linspace(0, 1, 1000) # 时间从0到1秒
|
||||
frequency1 = 5 # 频率1 Hz
|
||||
frequency2 = 20 # 频率2 Hz
|
||||
signal = np.sin(2 * np.pi * frequency1 * time) + 0.5 * np.sin(2 * np.pi * frequency2 * time)
|
||||
|
||||
# 进行小波变换
|
||||
wavelet = 'db4' # 使用Daubechies小波函数
|
||||
coeffs = pywt.wavedec(signal, wavelet)
|
||||
plt.plot(signal, label="Org")
|
||||
# 绘制小波系数
|
||||
for i in range(len(coeffs)):
|
||||
|
||||
plt.plot(coeffs[i], label=f'Detail {i}' if i > 0 else 'Approximation')
|
||||
plt.xlabel('Sample')
|
||||
plt.ylabel('Coefficient Value')
|
||||
plt.legend()
|
||||
plt.title('Wavelet Coefficients')
|
||||
plt.show()
|
670
Paradigm/ssvep_paradigm.py
Normal file
@ -0,0 +1,670 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''库文件'''
|
||||
import math
|
||||
import random
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
import logging
|
||||
from loguru import logger
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5.QtGui import *
|
||||
import numpy as np
|
||||
from pyqtgraph import PlotWidget, mkPen
|
||||
from qfluentwidgets import *
|
||||
from qfluentwidgets import FluentIcon as FIF
|
||||
from collections import deque
|
||||
from scipy.signal import iirnotch, lfilter, welch
|
||||
|
||||
logging.basicConfig(level=logging.INFO) # logging.INFO
|
||||
import re
|
||||
|
||||
'''辅助功能'''
|
||||
from ui.ui import initUI
|
||||
# from chat_ui import *
|
||||
from src.FBCCA2 import fbcca_realtime
|
||||
from src.tts import TTS
|
||||
from Multi_Language import MultiLangAutoComplete, word_tokenize
|
||||
|
||||
import numpy as np
|
||||
from collections import deque
|
||||
from PyQt5 import QtWidgets, QtCore
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
|
||||
class SerialThread(QtCore.QThread):
|
||||
data_received = QtCore.pyqtSignal(str) # Signal to send data back to the main thread
|
||||
|
||||
def __init__(self, com_port):
|
||||
super().__init__()
|
||||
self.com_port = com_port
|
||||
data_len = 3000
|
||||
self.serial_port = serial.Serial(self.com_port, baudrate=115200, timeout=1)
|
||||
self.data_pool = [deque(maxlen=data_len) for _ in range(9)] # Data pool for each channel
|
||||
self.running = True
|
||||
|
||||
def run(self):
|
||||
self.serial_port.write(b'1') # Send '1' to start data transmission
|
||||
while self.running:
|
||||
if self.serial_port.in_waiting:
|
||||
try:
|
||||
raw_data = self.serial_port.readline().decode('utf-8').strip()
|
||||
if raw_data:
|
||||
# Emit the raw data to the main thread for display
|
||||
# self.data_received.emit(raw_data)
|
||||
|
||||
# Integrity check (matches 'Channel:' followed by 9 float values)
|
||||
match = re.match(r'Channel:([\d\.\-]+,){8}[\d\.\-]+', raw_data)
|
||||
if match:
|
||||
values = [float(x) for x in raw_data.split('Channel:')[1].split(',')]
|
||||
# Add data to the pool for each channel
|
||||
for i in range(9):
|
||||
self.data_pool[i].append(values[i])
|
||||
except:
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
self.serial_port.close()
|
||||
|
||||
def send_data(self, data):
|
||||
self.serial_port.write(data.encode('utf-8') + b'\r\n') # 发送数据到串口
|
||||
|
||||
def get_latest_data(self, size):
|
||||
"""Return the latest 'size' data from the data pool for all channels."""
|
||||
if size > len(self.data_pool[0]):
|
||||
raise ValueError("Requested size is larger than the current data pool size.")
|
||||
|
||||
# Convert the latest 'size' elements from deque to numpy array for each channel
|
||||
return np.array([list(self.data_pool[i])[-size:] for i in range(9)])
|
||||
|
||||
# Example usage:
|
||||
# serial_reader = SerialReader(serial_port)
|
||||
# data = serial_reader.get_latest_data(100)
|
||||
# serial_reader.send_window_data('1')
|
||||
|
||||
class SSVEPApp(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.timer = QTimer() # 定时器
|
||||
self.timer2 = QTimer()
|
||||
self.sta = 0
|
||||
self.end = 0
|
||||
self.beg = 0
|
||||
self.previous_psd = 0
|
||||
self.sum = 0 # 计时总误差
|
||||
self.key_fre = [] # 按钮频率
|
||||
self.key_phr = [] # 按钮相位
|
||||
self.buttons = []
|
||||
self.count = 0 # 计数器
|
||||
self.result = 0
|
||||
self.res_list = [] # 输入序列
|
||||
self.py2hz_list = [] # 备选按钮文本显示
|
||||
self.teeth_cnt = 0
|
||||
self.tri_count = 0
|
||||
# 设置显示在哪块屏幕
|
||||
# desktop = QApplication.desktop()
|
||||
# self.setGeometry(desktop.screenGeometry(1))
|
||||
self.tp7_th = -99999
|
||||
self.pinyin_mod = 'english'
|
||||
self.inputText = ""
|
||||
self.inputCount = 0
|
||||
self.all_pinyin_list = ['min', 'suan', 'deng', 'chui', 'kun', 'me', 'nie', 'zhong', 'bai', 'zha', 'dian', 'pi',
|
||||
'nuan', 'ne', 'hong', 'can', 'yang', 'chun', 'fiao', 'qian', 'lei', 'na', 'lao', 'yi',
|
||||
'zhu', 'weng', 'zun', 'ai', 'zhi', 'san', 'jiang', 'shun', 'cai', 'cun', 'tie', 'yong',
|
||||
'lv', 'zhou', 'chuan', 'mian', 'peng', 'zhuo', 'cou', 'suo', 'dai', 'er', 'mei', 'xin',
|
||||
'xian', 'kua', 'pao', 'ge', 'ru', 'jie', 'gou', 'rang', 'null', 'dou', 'sang', 'su',
|
||||
'mu', 'long', 'gong', 'zuo', 'hai', 'fa', 'lo', 'kuai', 'pian', 'heng', 'jiong', 'guai',
|
||||
'diu', 'shao', 'qun', 'bi', 'chen', 'shuo', 'tun', 'chi', 'bian', 'chai', 'xiao',
|
||||
'duan', 'fou', 'tu', 'gan', 'tiao', 'diao', 'pei', 'shuang', 'cen', 'chou', 'lai', 'an',
|
||||
'te', 'ri', 'fang', 'zeng', 'ti', 'cha', 'bu', 'ying', 'nou', 'wei', 'zai', 'pen',
|
||||
'zong', 'pa', 'liao', 'gao', 'reng', 'xiang', 're', 'ni', 'guo', 'dia', 'hu', 'yin',
|
||||
'kai', 'ruan', 'ming', 'sou', 'lin', 'wai', 'bin', 'zi', 'nian', 'hua', 'nai', 'ang',
|
||||
'qiao', 'geng', 'song', 'mie', 'jun', 'tuan', 'dun', 'fu', 'kang', 'pu', 'zhang',
|
||||
'bang', 'zhuang', 'de', 'shuan', 'cui', 'en', 'qve', 'pai', 'dang', 'shi', 'men', 'liu',
|
||||
'nuo', 'dei', 'zhao', 'ran', 'huang', 'ken', 'nen', 'she', 'ju', 'ren', 'ma', 'qiong',
|
||||
'ruo', 'bao', 'zei', 'pou', 'nu', 'shang', 'fei', 'ke', 'cheng', 'o', 'dan', 'lou',
|
||||
'zui', 'gui', 'cao', 'piao', 'ka', 'qia', 'cong', 'bing', 'zhai', 'bei', 'gun', 'tang',
|
||||
'kui', 'che', 'miao', 'za', 'keng', 'lan', 'xie', 'le', 'pang', 'chuo', 'chan', 'niu',
|
||||
'shan', 'zao', 'tuo', 'ji', 'ca', 'mao', 'shen', 'hen', 'ci', 'zuan', 'ei', 'nong',
|
||||
'tong', 'zen', 'du', 'sa', 'mi', 'ye', 'tan', 'hao', 'tao', 'ya', 'shou', 'jing',
|
||||
'guan', 'miu', 'ga', 'ping', 'a', 'dong', 'cuo', 'sheng', 'zhen', 'beng', 'mo', 'zhuan',
|
||||
'wang', 'mai', 'chong', 'zhuai', 'lun', 'wo', 'run', 'tai', 'qing', 'rong', 'xve',
|
||||
'rou', 'ku', 'kou', 'ha', 'fan', 'hou', 'teng', 'bie', 'sun', 'den', 'kuang', 'lve',
|
||||
'ou', 'ding', 'gen', 'da', 'quan', 'luan', 'nei', 'po', 'duo', 'qie', 'qiang', 'yan',
|
||||
'shu', 'zhan', 'jiao', 'bo', 'li', 'xi', 'he', 'zan', 'nun', 'shai', 'ting', 'chuang',
|
||||
'chua', 'huo', 'hei', 'luo', 'ce', 'yve', 'xiu', 'zhua', 'jin', 'dui', 'cu', 'juan',
|
||||
'xia', 'jve', 'fo', 'shua', 'wu', 'lie', 'ze', 'la', 'kan', 'shuai', 'chang', 'liang',
|
||||
'seng', 'zang', 'kong', 'qin', 'nan', 'gai', 'gu', 'hui', 'zhui', 'sen', 'ta', 'pie',
|
||||
'tou', 'ling', 'neng', 'se', 'hang', 'chu', 'chuai', 'ban', 'man', 'gua', 'mou',
|
||||
'guang', 'chao', 'qiu', 'leng', 'xing', 'gei', 'nao', 'shui', 'dao', 'cuan', 'yu', 'xu',
|
||||
'fen', 'zheng', 'biao', 'niao', 'xun', 'lang', 'nin', 'lia', 'ao', 'die', 'jia', 'rui',
|
||||
'han', 'qi', 'zhun', 'mang', 'feng', 'kao', 'sui', 'si', 'hun', 'sai', 'nve', 'niang',
|
||||
'lu', 'zu', 'tian', 'di', 'jian', 'lian', 'xiong', 'yo', 'shei', 'ning', 'yuan', 'e',
|
||||
'ba', 'yun', 'jiu', 'rao', 'tui', 'zhei', 'sao', 'kuan', 'wa', 'nang', 'pan', 'zou',
|
||||
'wen', 'yao', 'huai', 'gang', 'wan', 'meng', 'ben', 'xuan', 'huan', 'nv', 'pin', 'ceng',
|
||||
'cang', 'kei', 'qu', 'zhe', 'sha', 'you', 'kuo']
|
||||
|
||||
# 加载已有的字词库或创建新的
|
||||
self.auto_complete = MultiLangAutoComplete('autocomplete_data.pkl')
|
||||
|
||||
# 参数更新
|
||||
def initPara(self, Fs=500, tri_flag=False, fsc_flag=True, rec_flag=True, cal_flag=True, pinyin_mod='chinese',
|
||||
my_wave=0,
|
||||
fre=100, rows=8, cols=5, sta_fre=15, end_fre=8, v_space=0.2, tlen=4, teeth=41,
|
||||
channel=[0,1,2,3,4,5,6,7],
|
||||
buttonNames=["1", "2", "3", "4", "5", "6", "7", "展开",
|
||||
"Q", "W", "E", "R", "T", "Y", "U", "I",
|
||||
"O", "P", "A", "S", "D", "F", "G", "H",
|
||||
"J", "K", "L", "Z", "X", "C", "V", "B",
|
||||
"N", "M", ",", "。", "_", "确认", "语音", "退格"]):
|
||||
"""
|
||||
#########################
|
||||
SSVEP配置
|
||||
#########################
|
||||
"""
|
||||
self.orgFs = Fs # 采样采样率
|
||||
self.tri_flag = tri_flag # 脑电标记flag
|
||||
self.fsc_flag = fsc_flag # 是否全屏
|
||||
self.rec_flag = rec_flag # 是否接受在线数据
|
||||
self.cal_flag = cal_flag # 是否使用算法分析
|
||||
self.wave = my_wave # 1为方波,0为sin波形
|
||||
self.fre = fre
|
||||
self.rows, self.cols = rows, cols
|
||||
self.sta_fre = sta_fre
|
||||
self.end_fre = end_fre
|
||||
self.v_space = v_space
|
||||
self.tlen = tlen
|
||||
self.teeth = teeth
|
||||
self.channel = channel
|
||||
self.buttonNames = buttonNames
|
||||
self.pinyin_mod = pinyin_mod
|
||||
'''
|
||||
#########################
|
||||
在线接收配置
|
||||
#########################
|
||||
'''
|
||||
if self.rec_flag:
|
||||
# self.linkme = LinkMe(r"D:\Item\LinkMeDLL使用说明\LinkMe.dll")
|
||||
# self.ser = self.linkme.open_serial(port='COM14', baudrate=460800)
|
||||
#
|
||||
# self.linkme_thread = LinkMeThread(self.linkme)
|
||||
# self.linkme_thread.start()
|
||||
#
|
||||
# self.receive_thread = threading.Thread(target=self.linkme.receive_data, args=(self.ser,))
|
||||
# self.receive_thread.start()
|
||||
# Detect available COM ports and connect to the first available one
|
||||
com_port = self.detect_com_port()
|
||||
print(f"Connected to: {com_port}")
|
||||
if not com_port:
|
||||
raise Exception("No available COM ports detected.")
|
||||
|
||||
self.serial_thread = SerialThread(com_port)
|
||||
self.serial_thread.start()
|
||||
|
||||
def initEvent(self):
|
||||
|
||||
self.pre_data(self.sta_fre, self.end_fre, self.v_space, self.rows, self.cols)
|
||||
self.timer.setInterval(int(pow(10, 3) / self.fre)) # 设置计时器的间隔,单位是毫秒
|
||||
self.timer.timeout.connect(self.onTimerOut)
|
||||
|
||||
self.timer2.setInterval(200) # 设置计时器的间隔,单位是毫秒
|
||||
self.timer2.timeout.connect(self.teeth_judge)
|
||||
|
||||
self.buttonAbove.clicked.connect(self.demo)
|
||||
|
||||
def detect_com_port(self):
|
||||
"""Automatically detect available COM ports, excluding virtual ports."""
|
||||
ports = list(serial.tools.list_ports.comports())
|
||||
for port in ports:
|
||||
if 'Bluetooth' not in port.description and 'Virtual' not in port.description:
|
||||
return port.device
|
||||
return None
|
||||
|
||||
# 根据开头找到所有拼音
|
||||
def find_starting_with(self, input_list, prefix):
|
||||
return [s for s in input_list if s.startswith(prefix)]
|
||||
|
||||
def demo(self):
|
||||
if self.tri_flag:
|
||||
# self.serial_thread.send(int(self.tri_count))
|
||||
self.serial_thread.send_data("1")
|
||||
if self.tri_count > 40:
|
||||
self.tri_count = self.tri_count - 40
|
||||
# self.inputField.setText(str(self.buttonNames[self.tri_count + 1]))
|
||||
|
||||
self.timer.start()
|
||||
logging.info("开始闪烁")
|
||||
|
||||
def pre_data(self, sta_fre, end_fre, v_space, row, cul):
|
||||
# 生成频率与间隔序列
|
||||
for i in range(cul):
|
||||
tmp = np.linspace(sta_fre + i * v_space, end_fre + i * v_space, row).tolist()
|
||||
self.key_fre.append(tmp)
|
||||
logging.debug(str(tmp) + ',')
|
||||
tmp = np.linspace(0 + i * 0.5, (self.rows - 1) * 0.5 + i * 0.5, row).tolist()
|
||||
self.key_phr.append(tmp)
|
||||
logging.debug("ph:" + str(self.key_phr))
|
||||
|
||||
def set_button_background_colors(self):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.cols):
|
||||
button = self.buttons[row * self.cols + col]
|
||||
button.changeColor("white")
|
||||
|
||||
def buttonClickHandler(self):
|
||||
sender = self.sender()
|
||||
if isinstance(sender, QPushButton):
|
||||
button_text = sender.text()
|
||||
button_index = next((i for i, button in enumerate(self.buttons) if button.text() == button_text), None)
|
||||
if button_index is not None:
|
||||
self.set_button_background_colors()
|
||||
self.buttons[button_index].changeColor("red")
|
||||
QApplication.processEvents() # 刷新界面
|
||||
button_handlers = {
|
||||
"退格": self.handle_backspace_button,
|
||||
"语音": self.handle_voice_button,
|
||||
"确认": self.handle_confirm_button,
|
||||
"展开": self.handle_expand_button,
|
||||
"收缩": self.handle_shrink_button,
|
||||
"发送": self.handle_send_button,
|
||||
"↓": self.handle_down_button,
|
||||
"↑": self.handle_up_button,
|
||||
}
|
||||
|
||||
# 如果按钮名称在字典中,就调用对应的处理函数
|
||||
if button_text in button_handlers:
|
||||
button_handlers[button_text](button_index)
|
||||
|
||||
# 处理其他情况
|
||||
elif button_text.isalpha() and button_text.isupper() and len(button_text) == 1:
|
||||
self.handle_alpha_button(button_text.lower())
|
||||
else:
|
||||
self.handle_other_buttons(button_text, button_index)
|
||||
|
||||
logging.info(f"Button '{button_text}' was clicked.")
|
||||
QApplication.processEvents()
|
||||
|
||||
# 下面是各个处理函数的定义,需要根据实际情况来实现这些函数
|
||||
def handle_backspace_button(self, index):
|
||||
# 在这里实现退格按钮的处理逻辑
|
||||
if index == self.buttonNames.index("退格"):
|
||||
if self.res_list:
|
||||
if len(self.res_list[-1]) != 1:
|
||||
self.res_list[-1] = self.res_list[-1][:-1]
|
||||
else:
|
||||
self.res_list = self.res_list[:-1]
|
||||
self.py2hz_list = [] if not self.res_list else self.py2hz_list
|
||||
else:
|
||||
self.inputText = self.inputText[:-1]
|
||||
self.res_list = []
|
||||
self.py2hz_list = []
|
||||
self.update_ui_after_backspace()
|
||||
QApplication.processEvents()
|
||||
|
||||
def update_ui_after_backspace(self):
|
||||
self.inputField.setText((self.inputText + str(self.res_list)) if self.res_list else self.inputText)
|
||||
self.choseField.setText("")
|
||||
if self.res_list:
|
||||
self.update_pinyin_and_ui()
|
||||
|
||||
def handle_voice_button(self, index):
|
||||
# 在这里实现语音按钮的处理逻辑
|
||||
if index == self.buttonNames.index("语音"):
|
||||
TTS(TEXT=self.inputText)
|
||||
|
||||
def handle_send_button(self, index):
|
||||
if index == self.buttonNames.index("发送"):
|
||||
txt = self.inputText
|
||||
# # 使用串口
|
||||
# import serial
|
||||
# # 创建一个Serial对象,指定串口名称和波特率
|
||||
# ser = serial.Serial('COM1', 9600)
|
||||
# ser.write(txt)
|
||||
|
||||
# 保存文本
|
||||
# 打开文件,如果文件不存在,将会创建一个新文件
|
||||
with open("output.txt", "w") as f:
|
||||
# 将字符串写入文件
|
||||
f.write(txt)
|
||||
self.inputText = ""
|
||||
self.inputField.setText(self.inputText)
|
||||
|
||||
def handle_confirm_button(self, index):
|
||||
confirm_index = self.buttonNames.index("确认")
|
||||
if index == confirm_index:
|
||||
try:
|
||||
# model_choice = "gpt-3.5-turbo" # 这里可以换成任何其他可用的模型,如 "text-davinci-003"
|
||||
# response = chat_with_gpt(self.inputText, model=model_choice)
|
||||
# if response:
|
||||
# self.choseField.setText("GPT说:" + response)
|
||||
# else:
|
||||
# print("无法获取回应。")
|
||||
chat_app.text_input.setText(self.inputText)
|
||||
chat_app.show()
|
||||
# 创建一个 QTimer 对象
|
||||
timer = QTimer(self)
|
||||
# 将超时时间设置为 2000 毫秒(2 秒)
|
||||
timer.setInterval(1000)
|
||||
# 将超时信号连接到 send_message 方法
|
||||
timer.timeout.connect(chat_app.send_message)
|
||||
# 启动定时器(注意:定时器只会触发一次)
|
||||
timer.setSingleShot(True)
|
||||
timer.start()
|
||||
|
||||
except Exception as e:
|
||||
self.py2hz_list = []
|
||||
self.res_list = []
|
||||
logging.error(f"处理出错,请重新输入。错误详情: {str(e)}")
|
||||
|
||||
logging.debug(f"py2hz_list: {self.py2hz_list}")
|
||||
QApplication.processEvents()
|
||||
|
||||
def handle_expand_button(self, index):
|
||||
# 在这里实现展开按钮的处理逻辑
|
||||
if index == self.buttonNames.index("展开"):
|
||||
self.tablecount = 0
|
||||
self.buttons[index].changeColor("white")
|
||||
for col in range(self.cols):
|
||||
for row in range(self.rows):
|
||||
if self.tablecount < len(self.py2hz_list):
|
||||
self.buttons[col * self.rows + row].setText(self.py2hz_list[self.tablecount])
|
||||
else:
|
||||
self.buttons[col * self.rows + row].setText("")
|
||||
self.tablecount = self.tablecount + 1
|
||||
self.tablecount = self.tablecount - 3
|
||||
self.buttons[self.buttonNames.index("退格")].setText("收缩")
|
||||
self.buttons[self.buttonNames.index("确认")].setText("↑")
|
||||
self.buttons[self.buttonNames.index("语音")].setText("↓")
|
||||
|
||||
def handle_shrink_button(self, index):
|
||||
# 在这里实现收缩按钮的处理逻辑
|
||||
if index == self.buttonNames.index("退格"):
|
||||
self.update_ui()
|
||||
|
||||
def handle_down_button(self, index):
|
||||
# 在这里实现向下按钮的处理逻辑
|
||||
for col in range(self.cols):
|
||||
for row in range(self.rows):
|
||||
if self.tablecount < len(self.py2hz_list):
|
||||
self.buttons[col * self.rows + row].setText(self.py2hz_list[self.tablecount])
|
||||
else:
|
||||
self.buttons[col * self.rows + row].setText("")
|
||||
self.tablecount = self.tablecount + 1
|
||||
self.tablecount = self.tablecount - 3
|
||||
self.buttons[self.buttonNames.index("退格")].setText("收缩")
|
||||
self.buttons[self.buttonNames.index("确认")].setText("↑")
|
||||
self.buttons[self.buttonNames.index("语音")].setText("↓")
|
||||
logging.info("tablecount:" + str(self.tablecount))
|
||||
|
||||
def handle_up_button(self, index):
|
||||
# 在这里实现向上按钮的处理逻辑
|
||||
if self.tablecount >= (5 * 8 - 3):
|
||||
self.tablecount = self.tablecount - (5 * 8 - 3) * 2 # 一页只有37个备选
|
||||
for col in range(self.cols):
|
||||
for row in range(self.rows):
|
||||
if self.tablecount < len(self.py2hz_list) and self.tablecount >= 0:
|
||||
self.buttons[col * self.rows + row].setText(self.py2hz_list[self.tablecount])
|
||||
else:
|
||||
self.buttons[col * self.rows + row].setText("")
|
||||
self.tablecount = self.tablecount + 1
|
||||
self.tablecount = self.tablecount - 3
|
||||
self.buttons[self.buttonNames.index("退格")].setText("收缩")
|
||||
self.buttons[self.buttonNames.index("确认")].setText("↑")
|
||||
self.buttons[self.buttonNames.index("语音")].setText("↓")
|
||||
logging.info("tablecount:" + str(self.tablecount))
|
||||
else:
|
||||
pass
|
||||
|
||||
def handle_other_buttons(self, button_text, button_index):
|
||||
# 在这里实现其他按钮的处理逻辑
|
||||
try:
|
||||
if button_index < 8:
|
||||
text_to_add = self.py2hz_list[button_index]
|
||||
else:
|
||||
text_to_add = button_text
|
||||
|
||||
if self.pinyin_mod != 'chinese':
|
||||
self.inputText = self.inputText + " " + text_to_add
|
||||
else:
|
||||
self.inputText = self.inputText + text_to_add
|
||||
self.inputField.setText(self.inputText)
|
||||
chs_len = len(self.res_list) - len(text_to_add)
|
||||
if chs_len > 0:
|
||||
self.res_list = self.res_list[-chs_len:]
|
||||
self.update_pinyin_and_ui()
|
||||
self.inputField.setText(self.inputText + str(self.res_list))
|
||||
else:
|
||||
self.choseField.setText("")
|
||||
self.res_list = []
|
||||
self.py2hz_list = [] # 备选按钮文本显示
|
||||
self.update_ui()
|
||||
except:
|
||||
logging.error("备选失败:Error")
|
||||
|
||||
def handle_alpha_button(self, button_text):
|
||||
if self.pinyin_mod == 'chinese':
|
||||
try:
|
||||
judge_last = False
|
||||
judge_new = False
|
||||
for item in self.all_pinyin_list:
|
||||
if item.startswith(self.res_list[-1]):
|
||||
judge_last = True
|
||||
if item.startswith(self.res_list[-1] + button_text):
|
||||
judge_new = True
|
||||
if judge_last and judge_new:
|
||||
self.res_list[-1] = self.res_list[-1] + button_text
|
||||
elif judge_last and self.res_list[-1] + button_text not in self.all_pinyin_list:
|
||||
self.res_list.append(button_text)
|
||||
except:
|
||||
self.res_list.append(button_text)
|
||||
else:
|
||||
try:
|
||||
self.res_list[-1] = self.res_list[-1] + button_text
|
||||
except:
|
||||
self.res_list.append(button_text)
|
||||
# if len(self.res_list) >= 4:
|
||||
# self.res_list = self.res_list[:-1]
|
||||
|
||||
self.update_pinyin_and_ui()
|
||||
self.inputField.setText(self.inputText + str(self.res_list))
|
||||
|
||||
def update_pinyin_and_ui(self):
|
||||
try:
|
||||
self.py2hz_list = self.pinyin_2_hanzi(self.res_list)
|
||||
show_list = self.py2hz_list[:7]
|
||||
logging.debug(show_list)
|
||||
tmp_str = "备选:" + " ".join(f"{i + 1}.{chs}" for i, chs in enumerate(show_list))
|
||||
self.choseField.setText(tmp_str)
|
||||
except:
|
||||
self.py2hz_list = []
|
||||
self.res_list = []
|
||||
logging.error("请重新输入")
|
||||
logging.debug("py2hz_list:" + str(self.py2hz_list))
|
||||
QApplication.processEvents()
|
||||
|
||||
self.update_ui()
|
||||
|
||||
def pinyin_2_hanzi(self, pinyin_list):
|
||||
all_char = ''.join(pinyin_list)
|
||||
# # 添加一些新的文本到英语字词库
|
||||
# text = "Hello world, hello Zhangyutao. Zhangyutao is a great programmer."
|
||||
# words = word_tokenize(text.lower())
|
||||
# self.auto_complete.train_corpus('english', [words])
|
||||
res_comb = []
|
||||
# 使用联想输入功能
|
||||
if self.pinyin_mod != 'chinese':
|
||||
res_comb = self.auto_complete.suggest(self.pinyin_mod, all_char)
|
||||
print(self.pinyin_mod + ' : ' + str(res_comb)) # English suggestions
|
||||
# print('japan:' + str(self.auto_complete.suggest('japanese', 'こ'))) # Japanese suggestions
|
||||
# print('korean:' + str(self.auto_complete.suggest('korean', '안'))) # Korean suggestions
|
||||
else:
|
||||
# 加载中文
|
||||
try:
|
||||
res_comb = self.auto_complete.cn.search(all_char)
|
||||
print("中文:", res_comb)
|
||||
except Exception as e:
|
||||
print(f"错误异常反馈: {e}")
|
||||
|
||||
# 欧洲语言
|
||||
# for index in self.auto_complete.languages:
|
||||
# print(str(index) + ":" + str(self.auto_complete.suggest(index, 'a'))) # Japanese suggestions
|
||||
|
||||
return res_comb
|
||||
|
||||
def update_ui(self):
|
||||
for col in range(self.cols):
|
||||
for row in range(self.rows):
|
||||
self.buttons[col * self.rows + row].setText(self.buttonNames[col * self.rows + row])
|
||||
|
||||
def delay_ms(self, t):
|
||||
start, end = 0, 0
|
||||
start = time.perf_counter()
|
||||
while ((end - start) * pow(10, 3) < t):
|
||||
end = time.perf_counter()
|
||||
def check_psd(self, data, fs):
|
||||
threshold = 50
|
||||
if data is not None:
|
||||
psd_values = self.compute_psd(data, fs)
|
||||
current_psd_sum = np.sum(psd_values)
|
||||
if self.previous_psd is not None:
|
||||
# 比较当前PSD总和与上次总和的变化是否超过阈值
|
||||
if threshold < current_psd_sum - self.previous_psd < 500:
|
||||
print(f"发现向上突变,目前差值为:{(current_psd_sum - self.previous_psd)}")
|
||||
return True
|
||||
else:
|
||||
print(f"差值为:{current_psd_sum - self.previous_psd}")
|
||||
self.previous_psd = current_psd_sum
|
||||
def compute_psd(self, data, fs):
|
||||
# 定义感兴趣的频率范围
|
||||
low_freq = 3
|
||||
high_freq = 40
|
||||
|
||||
# 存储每个通道在3-40Hz的PSD值
|
||||
psd_values = []
|
||||
|
||||
for i in range(data.shape[0]):
|
||||
# 计算功率谱密度
|
||||
f, psd = welch(data[i, :], fs, nperseg=1000)
|
||||
|
||||
# 只保留3到40Hz的频段
|
||||
freq_mask = (f >= low_freq) & (f <= high_freq)
|
||||
psd_in_band = psd[freq_mask]
|
||||
|
||||
# 计算3到40Hz频段内的PSD值(通过对频段内的PSD进行积分)
|
||||
psd_band_value = np.trapz(psd_in_band, f[freq_mask])
|
||||
|
||||
# 将结果存储起来
|
||||
psd_values.append(psd_band_value)
|
||||
return psd_values
|
||||
# 咬牙检测
|
||||
def teeth_judge(self):
|
||||
QApplication.processEvents()
|
||||
eeg_data = self.serial_thread.get_latest_data(self.orgFs * self.tlen)
|
||||
eeg_data = np.array([eeg_data[i] for i in self.channel])
|
||||
if self.check_psd(eeg_data, self.orgFs) and time.time() - self.beg > 2:
|
||||
logging.info("咬牙")
|
||||
logging.info("开启闪烁")
|
||||
chat_app.hide()
|
||||
self.timer2.stop()
|
||||
self.timer.start()
|
||||
|
||||
# if energy > 9:
|
||||
# logging.info("咬牙")
|
||||
# logging.info("开启闪烁")
|
||||
# chat_app.hide()
|
||||
# self.timer2.stop()
|
||||
# self.timer.start()
|
||||
|
||||
def onTimerOut(self):
|
||||
'''
|
||||
每次闪烁:更新全部键盘颜色
|
||||
'''
|
||||
self.count += 1
|
||||
if self.count == 1:
|
||||
self.sta = time.perf_counter_ns()
|
||||
|
||||
x = float(pow(10, 3) / self.fre) * self.count / pow(10, 3)
|
||||
for col in range(self.cols):
|
||||
for row in range(self.rows):
|
||||
y = self.calculate_color(x, col, row)
|
||||
self.buttons[col * self.rows + row].changeColor(y)
|
||||
# self.buttons[col * self.rows + row].setStyleSheet(f"background-color: rgb({y}, {0}, {z});")
|
||||
|
||||
'''
|
||||
闪烁结束:计算误差
|
||||
'''
|
||||
if self.count > self.tlen * self.fre:
|
||||
self.end_blink()
|
||||
# self.timer2.start()
|
||||
|
||||
def calculate_color(self, x, col, row):
|
||||
if self.wave == 0:
|
||||
return int(255 * (1 + math.sin(
|
||||
2 * math.pi * self.key_fre[col][row] * x + self.key_phr[col][row] * math.pi)) * 0.5)
|
||||
elif self.wave == 1:
|
||||
T_count = int(pow(10, 3) * x / (0.5 * pow(10, 3) / self.key_fre[row][col]))
|
||||
return 255 if (T_count & 1) == 0 else 0
|
||||
|
||||
def end_blink(self):
|
||||
# if self.tri_flag:
|
||||
# self.serial_thread.send(int(self.tri_count))
|
||||
# 计算误差
|
||||
self.end = time.perf_counter_ns()
|
||||
self.sum = (self.end - self.sta - self.tlen * pow(10, 9)) / pow(10, 9 - 3)
|
||||
logging.warning(f"{self.tlen}s以{self.fre}Hz频率共{self.tlen * self.fre}次闪烁累计误差:{self.sum}毫秒")
|
||||
|
||||
for button in self.buttons:
|
||||
button.changeColor(255)
|
||||
# button.setStyleSheet(f"background-color: rgb({255}, {0}, {255});")
|
||||
QApplication.processEvents()
|
||||
eeg_data = None
|
||||
# 接收数据
|
||||
if self.rec_flag:
|
||||
self.delay_ms(140)
|
||||
# eeg_data = self.linkme.rec_data[-self.orgFs * self.tlen:].T
|
||||
eeg_data = self.serial_thread.get_latest_data(self.orgFs * self.tlen)
|
||||
freqlist = [item for sublist in self.key_fre for item in sublist]
|
||||
np.save(f'data_save/20241013_{freqlist[self.tri_count]}Hz.npy', eeg_data)
|
||||
eeg_data = np.array([eeg_data[i] for i in self.channel])
|
||||
eeg_data = eeg_data.reshape(len(self.channel), self.orgFs * self.tlen)
|
||||
self.tri_count = self.tri_count + 1
|
||||
|
||||
self.timer.stop()
|
||||
self.count = 0
|
||||
# 保存字词库的状态
|
||||
self.auto_complete.save('autocomplete_data.pkl')
|
||||
self.beg = time.time()
|
||||
# 计算所得数据
|
||||
if self.cal_flag:
|
||||
|
||||
freqlist = [item for sublist in self.key_fre for item in sublist]
|
||||
self.result, score = fbcca_realtime(eeg_data, freqlist, self.orgFs, num_harms=4,
|
||||
num_fbs=4)
|
||||
logging.info(f"识别结果是: {self.result} {self.buttonNames[self.result]},分数:{score}")
|
||||
# 在代码中触发按钮的点击事件
|
||||
self.buttons[self.result].click()
|
||||
|
||||
def keyPressEvent(self, QKeyEvent):
|
||||
"""快捷键"""
|
||||
if QKeyEvent.key() == Qt.Key_Escape: # esc
|
||||
self.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
QApplication.setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
|
||||
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
||||
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
ssvep_win = SSVEPApp()
|
||||
ssvep_win.initPara()
|
||||
# chat_app = ChatWindow()
|
||||
initUI(ssvep_win)
|
||||
ssvep_win.initEvent()
|
||||
|
||||
ssvep_win.show()
|
||||
sys.exit(app.exec_())
|
BIN
Paradigm/ucrtbased.dll
Normal file
BIN
Paradigm/ui/GPT_logo.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
Paradigm/ui/__pycache__/login_ui.cpython-38.pyc
Normal file
BIN
Paradigm/ui/__pycache__/ui.cpython-38.pyc
Normal file
BIN
Paradigm/ui/chat_logo.png
Normal file
After Width: | Height: | Size: 40 KiB |
1719
Paradigm/ui/login.ui
Normal file
871
Paradigm/ui/login_ui.py
Normal file
@ -0,0 +1,871 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'login.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
#
|
||||
# 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(344, 493)
|
||||
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.gridLayout_7 = QtWidgets.QGridLayout(self.centralwidget)
|
||||
self.gridLayout_7.setObjectName("gridLayout_7")
|
||||
self.frame_6 = QtWidgets.QFrame(self.centralwidget)
|
||||
self.frame_6.setMaximumSize(QtCore.QSize(16777, 16777215))
|
||||
self.frame_6.setStyleSheet("background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0,stop:0 rgb(126, 148, 190), stop:1 rgb(58, 109, 137));\n"
|
||||
"border-radius:20px ")
|
||||
self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_6.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_6.setObjectName("frame_6")
|
||||
self.gridLayout_5 = QtWidgets.QGridLayout(self.frame_6)
|
||||
self.gridLayout_5.setObjectName("gridLayout_5")
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
spacerItem = QtWidgets.QSpacerItem(5, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_2.addItem(spacerItem)
|
||||
self.tabWidget = QtWidgets.QTabWidget(self.frame_6)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(10)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.tabWidget.setFont(font)
|
||||
self.tabWidget.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
|
||||
self.tabWidget.setLayoutDirection(QtCore.Qt.LeftToRight)
|
||||
self.tabWidget.setAutoFillBackground(False)
|
||||
self.tabWidget.setInputMethodHints(QtCore.Qt.ImhNone)
|
||||
self.tabWidget.setTabShape(QtWidgets.QTabWidget.Triangular)
|
||||
self.tabWidget.setIconSize(QtCore.QSize(16, 16))
|
||||
self.tabWidget.setElideMode(QtCore.Qt.ElideMiddle)
|
||||
self.tabWidget.setDocumentMode(True)
|
||||
self.tabWidget.setTabsClosable(False)
|
||||
self.tabWidget.setTabBarAutoHide(True)
|
||||
self.tabWidget.setObjectName("tabWidget")
|
||||
self.tab = QtWidgets.QWidget()
|
||||
self.tab.setObjectName("tab")
|
||||
self.gridLayout = QtWidgets.QGridLayout(self.tab)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.frame_3 = QtWidgets.QFrame(self.tab)
|
||||
self.frame_3.setStyleSheet("QFrame{\n"
|
||||
" image: none;\n"
|
||||
"background-color: rgba(255, 255, 255, 20%);\n"
|
||||
"border-radius:6px \n"
|
||||
"}\n"
|
||||
"QLabel{\n"
|
||||
"\n"
|
||||
" background-color: rgba(255, 255, 255, 0);\n"
|
||||
"}")
|
||||
self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_3.setObjectName("frame_3")
|
||||
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.frame_3)
|
||||
self.verticalLayout_3.setContentsMargins(24, 24, 24, 8)
|
||||
self.verticalLayout_3.setSpacing(4)
|
||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||
self.widget = QtWidgets.QWidget(self.frame_3)
|
||||
self.widget.setMinimumSize(QtCore.QSize(0, 64))
|
||||
self.widget.setStyleSheet("")
|
||||
self.widget.setObjectName("widget")
|
||||
self.gridLayout_4 = QtWidgets.QGridLayout(self.widget)
|
||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
||||
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.gridLayout_4.addItem(spacerItem1, 0, 3, 1, 1)
|
||||
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.gridLayout_4.addItem(spacerItem2, 0, 0, 1, 1)
|
||||
self.label_pix = QtWidgets.QLabel(self.widget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.label_pix.sizePolicy().hasHeightForWidth())
|
||||
self.label_pix.setSizePolicy(sizePolicy)
|
||||
self.label_pix.setMinimumSize(QtCore.QSize(50, 50))
|
||||
self.label_pix.setMaximumSize(QtCore.QSize(50, 50))
|
||||
self.label_pix.setText("")
|
||||
self.label_pix.setObjectName("label_pix")
|
||||
self.gridLayout_4.addWidget(self.label_pix, 0, 2, 1, 1)
|
||||
self.verticalLayout_3.addWidget(self.widget)
|
||||
self.label = QtWidgets.QLabel(self.frame_3)
|
||||
self.label.setMaximumSize(QtCore.QSize(16777215, 26777))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(26)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.label.setFont(font)
|
||||
self.label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.label.setObjectName("label")
|
||||
self.verticalLayout_3.addWidget(self.label)
|
||||
self.line_2 = QtWidgets.QFrame(self.frame_3)
|
||||
self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.line_2.setObjectName("line_2")
|
||||
self.verticalLayout_3.addWidget(self.line_2)
|
||||
self.frame_9 = QtWidgets.QFrame(self.frame_3)
|
||||
self.frame_9.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.frame_9.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_9.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_9.setObjectName("frame_9")
|
||||
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame_9)
|
||||
self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_4.setSpacing(8)
|
||||
self.verticalLayout_4.setObjectName("verticalLayout_4")
|
||||
self.frame_7 = QtWidgets.QFrame(self.frame_9)
|
||||
self.frame_7.setMinimumSize(QtCore.QSize(0, 42))
|
||||
self.frame_7.setStyleSheet("border-radius:16px ;\n"
|
||||
"background-color: rgba(255, 255, 255, 51);")
|
||||
self.frame_7.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_7.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_7.setObjectName("frame_7")
|
||||
self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.frame_7)
|
||||
self.horizontalLayout_6.setContentsMargins(-1, 0, -1, 0)
|
||||
self.horizontalLayout_6.setSpacing(12)
|
||||
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
|
||||
self.widget_2 = QtWidgets.QWidget(self.frame_7)
|
||||
self.widget_2.setMaximumSize(QtCore.QSize(18, 18))
|
||||
self.widget_2.setStyleSheet("image: url(:/buttom_white/img/buttom_white/发送邮件_send-email.svg);\n"
|
||||
"background-color: rgba(255, 255, 255, 0);")
|
||||
self.widget_2.setObjectName("widget_2")
|
||||
self.horizontalLayout_6.addWidget(self.widget_2)
|
||||
self.name = QtWidgets.QLineEdit(self.frame_7)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.name.setFont(font)
|
||||
self.name.setStyleSheet("QLineEdit{\n"
|
||||
" background-color: rgba(255, 255, 255, 0);\n"
|
||||
" selection-color: rgb(34, 34, 34);\n"
|
||||
"}")
|
||||
self.name.setObjectName("name")
|
||||
self.horizontalLayout_6.addWidget(self.name)
|
||||
self.horizontalLayout_6.setStretch(0, 1)
|
||||
self.horizontalLayout_6.setStretch(1, 9)
|
||||
self.verticalLayout_4.addWidget(self.frame_7)
|
||||
self.frame_8 = QtWidgets.QFrame(self.frame_9)
|
||||
self.frame_8.setMinimumSize(QtCore.QSize(0, 42))
|
||||
self.frame_8.setStyleSheet("border-radius:16px ;\n"
|
||||
"background-color: rgba(255, 255, 255, 51);")
|
||||
self.frame_8.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_8.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_8.setObjectName("frame_8")
|
||||
self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.frame_8)
|
||||
self.horizontalLayout_7.setContentsMargins(-1, 0, -1, 0)
|
||||
self.horizontalLayout_7.setSpacing(12)
|
||||
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
|
||||
self.widget_3 = QtWidgets.QWidget(self.frame_8)
|
||||
self.widget_3.setMaximumSize(QtCore.QSize(18, 18))
|
||||
self.widget_3.setStyleSheet("image: url(:/buttom_white/img/buttom_white/电子锁开_electronic-locks-open.svg);\n"
|
||||
"background-color: rgba(255, 255, 255, 0);")
|
||||
self.widget_3.setObjectName("widget_3")
|
||||
self.horizontalLayout_7.addWidget(self.widget_3)
|
||||
self.password = QtWidgets.QLineEdit(self.frame_8)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.password.setFont(font)
|
||||
self.password.setStyleSheet("QLineEdit{\n"
|
||||
" background-color: rgba(255, 255, 255, 0);\n"
|
||||
" selection-color: rgb(34, 34, 34);\n"
|
||||
"}")
|
||||
self.password.setEchoMode(QtWidgets.QLineEdit.Password)
|
||||
self.password.setObjectName("password")
|
||||
self.horizontalLayout_7.addWidget(self.password)
|
||||
self.horizontalLayout_7.setStretch(0, 1)
|
||||
self.horizontalLayout_7.setStretch(1, 9)
|
||||
self.verticalLayout_4.addWidget(self.frame_8)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.horizontalLayout.addLayout(self.verticalLayout)
|
||||
self.verticalLayout_4.addLayout(self.horizontalLayout)
|
||||
self.horizontalLayout_10 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_10.setObjectName("horizontalLayout_10")
|
||||
self.checkBox_tri = QtWidgets.QCheckBox(self.frame_9)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(12)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.checkBox_tri.setFont(font)
|
||||
self.checkBox_tri.setChecked(False)
|
||||
self.checkBox_tri.setTristate(False)
|
||||
self.checkBox_tri.setObjectName("checkBox_tri")
|
||||
self.horizontalLayout_10.addWidget(self.checkBox_tri)
|
||||
self.comboBox_ssvep_mode = QtWidgets.QComboBox(self.frame_9)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(14)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.comboBox_ssvep_mode.setFont(font)
|
||||
self.comboBox_ssvep_mode.setStyleSheet("background-color: rgba(255, 255, 255, 100);\n"
|
||||
"border-radius: 5px;\n"
|
||||
"")
|
||||
self.comboBox_ssvep_mode.setObjectName("comboBox_ssvep_mode")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.comboBox_ssvep_mode.addItem("")
|
||||
self.horizontalLayout_10.addWidget(self.comboBox_ssvep_mode)
|
||||
self.verticalLayout_4.addLayout(self.horizontalLayout_10)
|
||||
self.verticalLayout_3.addWidget(self.frame_9)
|
||||
self.frame_11 = QtWidgets.QFrame(self.frame_3)
|
||||
self.frame_11.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.frame_11.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_11.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_11.setObjectName("frame_11")
|
||||
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.frame_11)
|
||||
self.verticalLayout_6.setObjectName("verticalLayout_6")
|
||||
self.line = QtWidgets.QFrame(self.frame_11)
|
||||
self.line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.line.setObjectName("line")
|
||||
self.verticalLayout_6.addWidget(self.line)
|
||||
spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout_6.addItem(spacerItem3)
|
||||
self.Login = QtWidgets.QPushButton(self.frame_11)
|
||||
self.Login.setMinimumSize(QtCore.QSize(0, 48))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(14)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.Login.setFont(font)
|
||||
self.Login.setStyleSheet("QPushButton{\n"
|
||||
" background-color: rgb(11, 11, 11);\n"
|
||||
" border-radius:20px ;\n"
|
||||
" color: rgb(255, 255, 255);\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"}")
|
||||
self.Login.setObjectName("Login")
|
||||
self.verticalLayout_6.addWidget(self.Login)
|
||||
self.verticalLayout_3.addWidget(self.frame_11)
|
||||
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_5.setSpacing(12)
|
||||
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
|
||||
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_5.addItem(spacerItem4)
|
||||
self.label_3 = QtWidgets.QLabel(self.frame_3)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(10)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.label_3.setFont(font)
|
||||
self.label_3.setObjectName("label_3")
|
||||
self.horizontalLayout_5.addWidget(self.label_3)
|
||||
self.create_Account = QtWidgets.QPushButton(self.frame_3)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(10)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.create_Account.setFont(font)
|
||||
self.create_Account.setStyleSheet("color: rgb(84, 42, 155);")
|
||||
self.create_Account.setObjectName("create_Account")
|
||||
self.horizontalLayout_5.addWidget(self.create_Account)
|
||||
spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_5.addItem(spacerItem5)
|
||||
self.verticalLayout_3.addLayout(self.horizontalLayout_5)
|
||||
self.gridLayout.addWidget(self.frame_3, 0, 0, 1, 1)
|
||||
self.tabWidget.addTab(self.tab, "")
|
||||
self.tab_2 = QtWidgets.QWidget()
|
||||
self.tab_2.setObjectName("tab_2")
|
||||
self.gridLayout_3 = QtWidgets.QGridLayout(self.tab_2)
|
||||
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||
self.frame_4 = QtWidgets.QFrame(self.tab_2)
|
||||
self.frame_4.setStyleSheet("QFrame{\n"
|
||||
" image: none;\n"
|
||||
"background-color: rgba(255, 255, 255, 20%);\n"
|
||||
"border-radius:6px \n"
|
||||
"}\n"
|
||||
"QLabel{\n"
|
||||
"\n"
|
||||
" background-color: rgba(255, 255, 255, 0);\n"
|
||||
"}")
|
||||
self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_4.setObjectName("frame_4")
|
||||
self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.frame_4)
|
||||
self.verticalLayout_7.setContentsMargins(24, 24, 24, 8)
|
||||
self.verticalLayout_7.setSpacing(4)
|
||||
self.verticalLayout_7.setObjectName("verticalLayout_7")
|
||||
self.frame_12 = QtWidgets.QFrame(self.frame_4)
|
||||
self.frame_12.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.frame_12.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_12.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_12.setObjectName("frame_12")
|
||||
self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.frame_12)
|
||||
self.verticalLayout_8.setContentsMargins(0, 0, 0, 24)
|
||||
self.verticalLayout_8.setSpacing(6)
|
||||
self.verticalLayout_8.setObjectName("verticalLayout_8")
|
||||
self.label_4 = QtWidgets.QLabel(self.frame_12)
|
||||
self.label_4.setMaximumSize(QtCore.QSize(16777215, 26777))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(26)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.label_4.setFont(font)
|
||||
self.label_4.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.label_4.setObjectName("label_4")
|
||||
self.verticalLayout_8.addWidget(self.label_4)
|
||||
self.label_5 = QtWidgets.QLabel(self.frame_12)
|
||||
self.label_5.setMaximumSize(QtCore.QSize(16777215, 16777))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(11)
|
||||
font.setBold(False)
|
||||
font.setWeight(50)
|
||||
self.label_5.setFont(font)
|
||||
self.label_5.setStyleSheet("")
|
||||
self.label_5.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.label_5.setObjectName("label_5")
|
||||
self.verticalLayout_8.addWidget(self.label_5)
|
||||
self.verticalLayout_7.addWidget(self.frame_12)
|
||||
self.frame_13 = QtWidgets.QFrame(self.frame_4)
|
||||
self.frame_13.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.frame_13.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_13.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_13.setObjectName("frame_13")
|
||||
self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.frame_13)
|
||||
self.verticalLayout_9.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_9.setSpacing(8)
|
||||
self.verticalLayout_9.setObjectName("verticalLayout_9")
|
||||
self.frame_14 = QtWidgets.QFrame(self.frame_13)
|
||||
self.frame_14.setMinimumSize(QtCore.QSize(0, 42))
|
||||
self.frame_14.setStyleSheet("border-radius:16px ;\n"
|
||||
"background-color: rgba(255, 255, 255, 51);")
|
||||
self.frame_14.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_14.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_14.setObjectName("frame_14")
|
||||
self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.frame_14)
|
||||
self.horizontalLayout_8.setContentsMargins(-1, 0, -1, 0)
|
||||
self.horizontalLayout_8.setSpacing(12)
|
||||
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
|
||||
self.widget_5 = QtWidgets.QWidget(self.frame_14)
|
||||
self.widget_5.setMaximumSize(QtCore.QSize(18, 18))
|
||||
self.widget_5.setStyleSheet("image: url(:/buttom_white/img/buttom_white/发送邮件_send-email.svg);\n"
|
||||
"background-color: rgba(255, 255, 255, 0);")
|
||||
self.widget_5.setObjectName("widget_5")
|
||||
self.horizontalLayout_8.addWidget(self.widget_5)
|
||||
self.reg_name = QtWidgets.QLineEdit(self.frame_14)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.reg_name.setFont(font)
|
||||
self.reg_name.setStyleSheet("QLineEdit{\n"
|
||||
" background-color: rgba(255, 255, 255, 0);\n"
|
||||
" selection-color: rgb(34, 34, 34);\n"
|
||||
"}")
|
||||
self.reg_name.setObjectName("reg_name")
|
||||
self.horizontalLayout_8.addWidget(self.reg_name)
|
||||
self.horizontalLayout_8.setStretch(0, 1)
|
||||
self.horizontalLayout_8.setStretch(1, 9)
|
||||
self.verticalLayout_9.addWidget(self.frame_14)
|
||||
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||
self.frame34 = QtWidgets.QFrame(self.frame_13)
|
||||
self.frame34.setMinimumSize(QtCore.QSize(0, 42))
|
||||
self.frame34.setStyleSheet("border-radius:16px ;\n"
|
||||
"background-color: rgba(255, 255, 255, 51);")
|
||||
self.frame34.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame34.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame34.setObjectName("frame34")
|
||||
self.horizontalLayout_11 = QtWidgets.QHBoxLayout(self.frame34)
|
||||
self.horizontalLayout_11.setContentsMargins(-1, 0, -1, 0)
|
||||
self.horizontalLayout_11.setSpacing(12)
|
||||
self.horizontalLayout_11.setObjectName("horizontalLayout_11")
|
||||
self.widget_11 = QtWidgets.QWidget(self.frame34)
|
||||
self.widget_11.setMinimumSize(QtCore.QSize(18, 18))
|
||||
self.widget_11.setMaximumSize(QtCore.QSize(18, 18))
|
||||
self.widget_11.setStyleSheet("image: url(:/buttom_white/img/buttom_white/男性_male.svg);\n"
|
||||
"background-color: rgba(255, 255, 255, 0);")
|
||||
self.widget_11.setObjectName("widget_11")
|
||||
self.horizontalLayout_11.addWidget(self.widget_11)
|
||||
self.gender = QtWidgets.QLineEdit(self.frame34)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.gender.setFont(font)
|
||||
self.gender.setStyleSheet("QLineEdit{\n"
|
||||
" background-color: rgba(255, 255, 255, 0);\n"
|
||||
" selection-color: rgb(34, 34, 34);\n"
|
||||
"}")
|
||||
self.gender.setObjectName("gender")
|
||||
self.horizontalLayout_11.addWidget(self.gender)
|
||||
self.horizontalLayout_11.setStretch(0, 1)
|
||||
self.horizontalLayout_11.setStretch(1, 9)
|
||||
self.horizontalLayout_3.addWidget(self.frame34)
|
||||
self.frame_19 = QtWidgets.QFrame(self.frame_13)
|
||||
self.frame_19.setMinimumSize(QtCore.QSize(0, 42))
|
||||
self.frame_19.setStyleSheet("border-radius:16px ;\n"
|
||||
"background-color: rgba(255, 255, 255, 51);")
|
||||
self.frame_19.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_19.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_19.setObjectName("frame_19")
|
||||
self.horizontalLayout_14 = QtWidgets.QHBoxLayout(self.frame_19)
|
||||
self.horizontalLayout_14.setContentsMargins(-1, 0, -1, 0)
|
||||
self.horizontalLayout_14.setSpacing(12)
|
||||
self.horizontalLayout_14.setObjectName("horizontalLayout_14")
|
||||
self.age = QtWidgets.QLineEdit(self.frame_19)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.age.setFont(font)
|
||||
self.age.setStyleSheet("QLineEdit{\n"
|
||||
" background-color: rgba(255, 255, 255, 0);\n"
|
||||
" selection-color: rgb(34, 34, 34);\n"
|
||||
"}")
|
||||
self.age.setReadOnly(False)
|
||||
self.age.setObjectName("age")
|
||||
self.horizontalLayout_14.addWidget(self.age)
|
||||
self.horizontalLayout_14.setStretch(0, 9)
|
||||
self.horizontalLayout_3.addWidget(self.frame_19)
|
||||
self.verticalLayout_9.addLayout(self.horizontalLayout_3)
|
||||
self.frame_15 = QtWidgets.QFrame(self.frame_13)
|
||||
self.frame_15.setMinimumSize(QtCore.QSize(0, 42))
|
||||
self.frame_15.setStyleSheet("border-radius:16px ;\n"
|
||||
"background-color: rgba(255, 255, 255, 51);")
|
||||
self.frame_15.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_15.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_15.setObjectName("frame_15")
|
||||
self.horizontalLayout_9 = QtWidgets.QHBoxLayout(self.frame_15)
|
||||
self.horizontalLayout_9.setContentsMargins(-1, 0, -1, 0)
|
||||
self.horizontalLayout_9.setSpacing(12)
|
||||
self.horizontalLayout_9.setObjectName("horizontalLayout_9")
|
||||
self.widget_6 = QtWidgets.QWidget(self.frame_15)
|
||||
self.widget_6.setMaximumSize(QtCore.QSize(18, 18))
|
||||
self.widget_6.setStyleSheet("image: url(:/buttom_white/img/buttom_white/电子锁开_electronic-locks-open.svg);\n"
|
||||
"background-color: rgba(255, 255, 255, 0);")
|
||||
self.widget_6.setObjectName("widget_6")
|
||||
self.horizontalLayout_9.addWidget(self.widget_6)
|
||||
self.reg_password = QtWidgets.QLineEdit(self.frame_15)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.reg_password.setFont(font)
|
||||
self.reg_password.setStyleSheet("QLineEdit{\n"
|
||||
" background-color: rgba(255, 255, 255, 0);\n"
|
||||
" selection-color: rgb(34, 34, 34);\n"
|
||||
"}")
|
||||
self.reg_password.setObjectName("reg_password")
|
||||
self.horizontalLayout_9.addWidget(self.reg_password)
|
||||
self.horizontalLayout_9.setStretch(0, 1)
|
||||
self.horizontalLayout_9.setStretch(1, 9)
|
||||
self.verticalLayout_9.addWidget(self.frame_15)
|
||||
self.reg_password_conf = QtWidgets.QFrame(self.frame_13)
|
||||
self.reg_password_conf.setMinimumSize(QtCore.QSize(0, 42))
|
||||
self.reg_password_conf.setStyleSheet("border-radius:16px ;\n"
|
||||
"background-color: rgba(255, 255, 255, 51);")
|
||||
self.reg_password_conf.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.reg_password_conf.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.reg_password_conf.setObjectName("reg_password_conf")
|
||||
self.horizontalLayout_12 = QtWidgets.QHBoxLayout(self.reg_password_conf)
|
||||
self.horizontalLayout_12.setContentsMargins(-1, 0, -1, 0)
|
||||
self.horizontalLayout_12.setSpacing(12)
|
||||
self.horizontalLayout_12.setObjectName("horizontalLayout_12")
|
||||
self.widget_7 = QtWidgets.QWidget(self.reg_password_conf)
|
||||
self.widget_7.setMaximumSize(QtCore.QSize(18, 18))
|
||||
self.widget_7.setStyleSheet("image: url(:/buttom_white/img/buttom_white/电子锁开_electronic-locks-open.svg);\n"
|
||||
"background-color: rgba(255, 255, 255, 0);")
|
||||
self.widget_7.setObjectName("widget_7")
|
||||
self.horizontalLayout_12.addWidget(self.widget_7)
|
||||
self.password_confirm = QtWidgets.QLineEdit(self.reg_password_conf)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.password_confirm.setFont(font)
|
||||
self.password_confirm.setStyleSheet("QLineEdit{\n"
|
||||
" background-color: rgba(255, 255, 255, 0);\n"
|
||||
" selection-color: rgb(34, 34, 34);\n"
|
||||
"}")
|
||||
self.password_confirm.setReadOnly(False)
|
||||
self.password_confirm.setObjectName("password_confirm")
|
||||
self.horizontalLayout_12.addWidget(self.password_confirm)
|
||||
self.horizontalLayout_12.setStretch(0, 1)
|
||||
self.horizontalLayout_12.setStretch(1, 9)
|
||||
self.verticalLayout_9.addWidget(self.reg_password_conf)
|
||||
self.verticalLayout_7.addWidget(self.frame_13)
|
||||
self.frame_16 = QtWidgets.QFrame(self.frame_4)
|
||||
self.frame_16.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.frame_16.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_16.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_16.setObjectName("frame_16")
|
||||
self.verticalLayout_10 = QtWidgets.QVBoxLayout(self.frame_16)
|
||||
self.verticalLayout_10.setObjectName("verticalLayout_10")
|
||||
spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout_10.addItem(spacerItem6)
|
||||
self.confirm = QtWidgets.QPushButton(self.frame_16)
|
||||
self.confirm.setMinimumSize(QtCore.QSize(0, 48))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(14)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.confirm.setFont(font)
|
||||
self.confirm.setStyleSheet("QPushButton{\n"
|
||||
" background-color: rgb(11, 11, 11);\n"
|
||||
" border-radius:20px ;\n"
|
||||
" color: rgb(255, 255, 255);\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"}")
|
||||
self.confirm.setObjectName("confirm")
|
||||
self.verticalLayout_10.addWidget(self.confirm)
|
||||
spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout_10.addItem(spacerItem7)
|
||||
self.verticalLayout_7.addWidget(self.frame_16)
|
||||
self.gridLayout_3.addWidget(self.frame_4, 0, 0, 1, 1)
|
||||
self.tabWidget.addTab(self.tab_2, "")
|
||||
self.tab_3 = QtWidgets.QWidget()
|
||||
self.tab_3.setObjectName("tab_3")
|
||||
self.gridLayout_2 = QtWidgets.QGridLayout(self.tab_3)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout_2.addItem(spacerItem8, 5, 0, 1, 1)
|
||||
self.verticalLayout_11 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_11.setSpacing(10)
|
||||
self.verticalLayout_11.setObjectName("verticalLayout_11")
|
||||
self.gridLayout_6 = QtWidgets.QGridLayout()
|
||||
self.gridLayout_6.setObjectName("gridLayout_6")
|
||||
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
|
||||
self.label_tlen = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_tlen.setObjectName("label_tlen")
|
||||
self.horizontalLayout_4.addWidget(self.label_tlen)
|
||||
self.lineEdit_tlen = QtWidgets.QLineEdit(self.tab_3)
|
||||
self.lineEdit_tlen.setObjectName("lineEdit_tlen")
|
||||
self.horizontalLayout_4.addWidget(self.lineEdit_tlen)
|
||||
self.gridLayout_6.addLayout(self.horizontalLayout_4, 1, 1, 1, 1)
|
||||
self.horizontalLayout_25 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_25.setObjectName("horizontalLayout_25")
|
||||
self.label_13 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_13.setObjectName("label_13")
|
||||
self.horizontalLayout_25.addWidget(self.label_13)
|
||||
self.lineEdit_Fs = QtWidgets.QLineEdit(self.tab_3)
|
||||
self.lineEdit_Fs.setObjectName("lineEdit_Fs")
|
||||
self.horizontalLayout_25.addWidget(self.lineEdit_Fs)
|
||||
self.gridLayout_6.addLayout(self.horizontalLayout_25, 0, 0, 1, 1)
|
||||
self.horizontalLayout_15 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_15.setObjectName("horizontalLayout_15")
|
||||
self.label_7 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_7.setObjectName("label_7")
|
||||
self.horizontalLayout_15.addWidget(self.label_7)
|
||||
self.lineEdit_screenflash = QtWidgets.QLineEdit(self.tab_3)
|
||||
self.lineEdit_screenflash.setObjectName("lineEdit_screenflash")
|
||||
self.horizontalLayout_15.addWidget(self.lineEdit_screenflash)
|
||||
self.gridLayout_6.addLayout(self.horizontalLayout_15, 1, 0, 1, 1)
|
||||
self.horizontalLayout_23 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_23.setObjectName("horizontalLayout_23")
|
||||
self.label_12 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_12.setObjectName("label_12")
|
||||
self.horizontalLayout_23.addWidget(self.label_12)
|
||||
self.lineEdit_v_space = QtWidgets.QLineEdit(self.tab_3)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lineEdit_v_space.sizePolicy().hasHeightForWidth())
|
||||
self.lineEdit_v_space.setSizePolicy(sizePolicy)
|
||||
self.lineEdit_v_space.setMinimumSize(QtCore.QSize(40, 0))
|
||||
self.lineEdit_v_space.setObjectName("lineEdit_v_space")
|
||||
self.horizontalLayout_23.addWidget(self.lineEdit_v_space)
|
||||
self.gridLayout_6.addLayout(self.horizontalLayout_23, 2, 0, 1, 1)
|
||||
self.horizontalLayout_26 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_26.setObjectName("horizontalLayout_26")
|
||||
self.label_15 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_15.setObjectName("label_15")
|
||||
self.horizontalLayout_26.addWidget(self.label_15)
|
||||
self.lineEdit_tooth = QtWidgets.QLineEdit(self.tab_3)
|
||||
self.lineEdit_tooth.setObjectName("lineEdit_tooth")
|
||||
self.horizontalLayout_26.addWidget(self.lineEdit_tooth)
|
||||
self.gridLayout_6.addLayout(self.horizontalLayout_26, 0, 1, 1, 1)
|
||||
self.horizontalLayout_19 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_19.setObjectName("horizontalLayout_19")
|
||||
self.label_8 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_8.setObjectName("label_8")
|
||||
self.horizontalLayout_19.addWidget(self.label_8)
|
||||
self.lineEdit_cols = QtWidgets.QLineEdit(self.tab_3)
|
||||
self.lineEdit_cols.setObjectName("lineEdit_cols")
|
||||
self.horizontalLayout_19.addWidget(self.lineEdit_cols)
|
||||
self.label_9 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_9.setObjectName("label_9")
|
||||
self.horizontalLayout_19.addWidget(self.label_9)
|
||||
self.lineEdit_rows = QtWidgets.QLineEdit(self.tab_3)
|
||||
self.lineEdit_rows.setObjectName("lineEdit_rows")
|
||||
self.horizontalLayout_19.addWidget(self.lineEdit_rows)
|
||||
self.gridLayout_6.addLayout(self.horizontalLayout_19, 2, 1, 1, 1)
|
||||
self.horizontalLayout_21 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_21.setObjectName("horizontalLayout_21")
|
||||
self.label_10 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_10.setObjectName("label_10")
|
||||
self.horizontalLayout_21.addWidget(self.label_10)
|
||||
self.lineEdit_sta_fre = QtWidgets.QLineEdit(self.tab_3)
|
||||
self.lineEdit_sta_fre.setObjectName("lineEdit_sta_fre")
|
||||
self.horizontalLayout_21.addWidget(self.lineEdit_sta_fre)
|
||||
self.gridLayout_6.addLayout(self.horizontalLayout_21, 3, 0, 1, 1)
|
||||
self.horizontalLayout_22 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_22.setObjectName("horizontalLayout_22")
|
||||
self.label_11 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_11.setObjectName("label_11")
|
||||
self.horizontalLayout_22.addWidget(self.label_11)
|
||||
self.lineEdit_end_fre = QtWidgets.QLineEdit(self.tab_3)
|
||||
self.lineEdit_end_fre.setObjectName("lineEdit_end_fre")
|
||||
self.horizontalLayout_22.addWidget(self.lineEdit_end_fre)
|
||||
self.gridLayout_6.addLayout(self.horizontalLayout_22, 3, 1, 1, 1)
|
||||
self.verticalLayout_11.addLayout(self.gridLayout_6)
|
||||
self.frame_24 = QtWidgets.QFrame(self.tab_3)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.frame_24.sizePolicy().hasHeightForWidth())
|
||||
self.frame_24.setSizePolicy(sizePolicy)
|
||||
self.frame_24.setMinimumSize(QtCore.QSize(0, 70))
|
||||
self.frame_24.setStyleSheet("border-radius:16px ;\n"
|
||||
"background-color: rgba(255, 255, 255, 51);")
|
||||
self.frame_24.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.frame_24.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.frame_24.setObjectName("frame_24")
|
||||
self.horizontalLayout_24 = QtWidgets.QHBoxLayout(self.frame_24)
|
||||
self.horizontalLayout_24.setContentsMargins(-1, 0, -1, 0)
|
||||
self.horizontalLayout_24.setSpacing(12)
|
||||
self.horizontalLayout_24.setObjectName("horizontalLayout_24")
|
||||
self.verticalLayout_12 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_12.setSpacing(0)
|
||||
self.verticalLayout_12.setObjectName("verticalLayout_12")
|
||||
self.checkBox_fullscreen = QtWidgets.QCheckBox(self.frame_24)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.checkBox_fullscreen.sizePolicy().hasHeightForWidth())
|
||||
self.checkBox_fullscreen.setSizePolicy(sizePolicy)
|
||||
self.checkBox_fullscreen.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.checkBox_fullscreen.setChecked(True)
|
||||
self.checkBox_fullscreen.setObjectName("checkBox_fullscreen")
|
||||
self.verticalLayout_12.addWidget(self.checkBox_fullscreen)
|
||||
self.checkBox_pinyin = QtWidgets.QCheckBox(self.frame_24)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.checkBox_pinyin.sizePolicy().hasHeightForWidth())
|
||||
self.checkBox_pinyin.setSizePolicy(sizePolicy)
|
||||
self.checkBox_pinyin.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.checkBox_pinyin.setChecked(True)
|
||||
self.checkBox_pinyin.setObjectName("checkBox_pinyin")
|
||||
self.verticalLayout_12.addWidget(self.checkBox_pinyin)
|
||||
self.checkBox_onlinedata = QtWidgets.QCheckBox(self.frame_24)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.checkBox_onlinedata.sizePolicy().hasHeightForWidth())
|
||||
self.checkBox_onlinedata.setSizePolicy(sizePolicy)
|
||||
self.checkBox_onlinedata.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.checkBox_onlinedata.setObjectName("checkBox_onlinedata")
|
||||
self.verticalLayout_12.addWidget(self.checkBox_onlinedata)
|
||||
self.checkBox_analysis = QtWidgets.QCheckBox(self.frame_24)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.checkBox_analysis.sizePolicy().hasHeightForWidth())
|
||||
self.checkBox_analysis.setSizePolicy(sizePolicy)
|
||||
self.checkBox_analysis.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.checkBox_analysis.setObjectName("checkBox_analysis")
|
||||
self.verticalLayout_12.addWidget(self.checkBox_analysis)
|
||||
self.horizontalLayout_24.addLayout(self.verticalLayout_12)
|
||||
self.line_3 = QtWidgets.QFrame(self.frame_24)
|
||||
self.line_3.setFrameShape(QtWidgets.QFrame.VLine)
|
||||
self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.line_3.setObjectName("line_3")
|
||||
self.horizontalLayout_24.addWidget(self.line_3)
|
||||
self.verticalLayout_13 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_13.setSpacing(0)
|
||||
self.verticalLayout_13.setObjectName("verticalLayout_13")
|
||||
self.label_6 = QtWidgets.QLabel(self.frame_24)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(11)
|
||||
font.setBold(False)
|
||||
font.setWeight(50)
|
||||
self.label_6.setFont(font)
|
||||
self.label_6.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
|
||||
self.label_6.setObjectName("label_6")
|
||||
self.verticalLayout_13.addWidget(self.label_6)
|
||||
self.bound_wave = QtWidgets.QComboBox(self.frame_24)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.bound_wave.sizePolicy().hasHeightForWidth())
|
||||
self.bound_wave.setSizePolicy(sizePolicy)
|
||||
self.bound_wave.setMinimumSize(QtCore.QSize(0, 20))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(10)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.bound_wave.setFont(font)
|
||||
self.bound_wave.setLayoutDirection(QtCore.Qt.LeftToRight)
|
||||
self.bound_wave.setAutoFillBackground(False)
|
||||
self.bound_wave.setStyleSheet("border-radius: 10px;")
|
||||
self.bound_wave.setEditable(False)
|
||||
self.bound_wave.setInsertPolicy(QtWidgets.QComboBox.InsertAlphabetically)
|
||||
self.bound_wave.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.bound_wave.setFrame(False)
|
||||
self.bound_wave.setObjectName("bound_wave")
|
||||
self.bound_wave.addItem("")
|
||||
self.bound_wave.addItem("")
|
||||
self.verticalLayout_13.addWidget(self.bound_wave)
|
||||
self.horizontalLayout_24.addLayout(self.verticalLayout_13)
|
||||
self.verticalLayout_11.addWidget(self.frame_24)
|
||||
self.verticalLayout_5 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
||||
self.label_14 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_14.setObjectName("label_14")
|
||||
self.verticalLayout_5.addWidget(self.label_14)
|
||||
self.plainTextEdit_channel = QtWidgets.QPlainTextEdit(self.tab_3)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.plainTextEdit_channel.sizePolicy().hasHeightForWidth())
|
||||
self.plainTextEdit_channel.setSizePolicy(sizePolicy)
|
||||
self.plainTextEdit_channel.setMaximumSize(QtCore.QSize(16777215, 50))
|
||||
self.plainTextEdit_channel.setStyleSheet("background-color: rgba(255, 255, 255, 80);\n"
|
||||
"border-radius: 10px;")
|
||||
self.plainTextEdit_channel.setObjectName("plainTextEdit_channel")
|
||||
self.verticalLayout_5.addWidget(self.plainTextEdit_channel)
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.label_2 = QtWidgets.QLabel(self.tab_3)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.verticalLayout_2.addWidget(self.label_2)
|
||||
self.plainTextEdit_buttons = QtWidgets.QPlainTextEdit(self.tab_3)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.plainTextEdit_buttons.sizePolicy().hasHeightForWidth())
|
||||
self.plainTextEdit_buttons.setSizePolicy(sizePolicy)
|
||||
self.plainTextEdit_buttons.setStyleSheet("background-color: rgba(255, 255, 255, 80);\n"
|
||||
"border-radius: 10px;")
|
||||
self.plainTextEdit_buttons.setObjectName("plainTextEdit_buttons")
|
||||
self.verticalLayout_2.addWidget(self.plainTextEdit_buttons)
|
||||
self.verticalLayout_5.addLayout(self.verticalLayout_2)
|
||||
self.verticalLayout_11.addLayout(self.verticalLayout_5)
|
||||
self.gridLayout_2.addLayout(self.verticalLayout_11, 1, 0, 1, 1)
|
||||
self.check = QtWidgets.QPushButton(self.tab_3)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.check.sizePolicy().hasHeightForWidth())
|
||||
self.check.setSizePolicy(sizePolicy)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("微软雅黑")
|
||||
font.setPointSize(12)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.check.setFont(font)
|
||||
self.check.setStyleSheet("color:rgb(170, 85, 255);\n"
|
||||
"background-color: rgba(255, 255, 255, 90);")
|
||||
self.check.setAutoRepeatInterval(100)
|
||||
self.check.setDefault(True)
|
||||
self.check.setObjectName("check")
|
||||
self.gridLayout_2.addWidget(self.check, 6, 0, 1, 1)
|
||||
self.tabWidget.addTab(self.tab_3, "")
|
||||
self.horizontalLayout_2.addWidget(self.tabWidget)
|
||||
spacerItem9 = QtWidgets.QSpacerItem(5, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_2.addItem(spacerItem9)
|
||||
self.gridLayout_5.addLayout(self.horizontalLayout_2, 0, 1, 1, 1)
|
||||
self.gridLayout_7.addWidget(self.frame_6, 0, 0, 1, 1)
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
|
||||
self.label.setText(_translate("MainWindow", "用户登录"))
|
||||
self.name.setPlaceholderText(_translate("MainWindow", "请输入姓名"))
|
||||
self.password.setPlaceholderText(_translate("MainWindow", "请输入密码"))
|
||||
self.checkBox_tri.setText(_translate("MainWindow", "Trigger"))
|
||||
self.comboBox_ssvep_mode.setItemText(0, _translate("MainWindow", "中文"))
|
||||
self.comboBox_ssvep_mode.setItemText(1, _translate("MainWindow", "english"))
|
||||
self.comboBox_ssvep_mode.setItemText(2, _translate("MainWindow", "german"))
|
||||
self.comboBox_ssvep_mode.setItemText(3, _translate("MainWindow", "danish"))
|
||||
self.comboBox_ssvep_mode.setItemText(4, _translate("MainWindow", "french"))
|
||||
self.comboBox_ssvep_mode.setItemText(5, _translate("MainWindow", "dutch"))
|
||||
self.comboBox_ssvep_mode.setItemText(6, _translate("MainWindow", "finnish"))
|
||||
self.comboBox_ssvep_mode.setItemText(7, _translate("MainWindow", "greek"))
|
||||
self.comboBox_ssvep_mode.setItemText(8, _translate("MainWindow", "italian"))
|
||||
self.comboBox_ssvep_mode.setItemText(9, _translate("MainWindow", "portuguese"))
|
||||
self.comboBox_ssvep_mode.setItemText(10, _translate("MainWindow", "spanish"))
|
||||
self.comboBox_ssvep_mode.setItemText(11, _translate("MainWindow", "swedish"))
|
||||
self.Login.setText(_translate("MainWindow", "登录"))
|
||||
self.label_3.setText(_translate("MainWindow", "没有账号?"))
|
||||
self.create_Account.setText(_translate("MainWindow", "创建帐户"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "登录"))
|
||||
self.label_4.setText(_translate("MainWindow", "用户注册"))
|
||||
self.label_5.setText(_translate("MainWindow", "BCI1211实验室"))
|
||||
self.reg_name.setPlaceholderText(_translate("MainWindow", "请输入姓名"))
|
||||
self.gender.setPlaceholderText(_translate("MainWindow", "请输入性别"))
|
||||
self.age.setPlaceholderText(_translate("MainWindow", "输入年龄"))
|
||||
self.reg_password.setPlaceholderText(_translate("MainWindow", "请输入密码"))
|
||||
self.password_confirm.setPlaceholderText(_translate("MainWindow", "重新输入密码"))
|
||||
self.confirm.setText(_translate("MainWindow", "确认"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "注册"))
|
||||
self.label_tlen.setText(_translate("MainWindow", "闪烁时长"))
|
||||
self.lineEdit_tlen.setText(_translate("MainWindow", "3"))
|
||||
self.label_13.setText(_translate("MainWindow", "降采样"))
|
||||
self.lineEdit_Fs.setText(_translate("MainWindow", "250"))
|
||||
self.label_7.setText(_translate("MainWindow", "屏幕刷新率"))
|
||||
self.lineEdit_screenflash.setText(_translate("MainWindow", "100"))
|
||||
self.label_12.setText(_translate("MainWindow", "行间频率间隔"))
|
||||
self.lineEdit_v_space.setText(_translate("MainWindow", "0.2"))
|
||||
self.label_15.setText(_translate("MainWindow", "咬牙通道"))
|
||||
self.lineEdit_tooth.setText(_translate("MainWindow", "41"))
|
||||
self.label_8.setText(_translate("MainWindow", "行"))
|
||||
self.lineEdit_cols.setText(_translate("MainWindow", "5"))
|
||||
self.label_9.setText(_translate("MainWindow", "列"))
|
||||
self.lineEdit_rows.setText(_translate("MainWindow", "8"))
|
||||
self.label_10.setText(_translate("MainWindow", "首行开始频率"))
|
||||
self.lineEdit_sta_fre.setText(_translate("MainWindow", "8"))
|
||||
self.label_11.setText(_translate("MainWindow", "首行结束频率"))
|
||||
self.lineEdit_end_fre.setText(_translate("MainWindow", "15"))
|
||||
self.checkBox_fullscreen.setText(_translate("MainWindow", "全屏"))
|
||||
self.checkBox_pinyin.setText(_translate("MainWindow", "拼音模式"))
|
||||
self.checkBox_onlinedata.setText(_translate("MainWindow", "在线数据"))
|
||||
self.checkBox_analysis.setText(_translate("MainWindow", "算法分析"))
|
||||
self.label_6.setText(_translate("MainWindow", "波形:"))
|
||||
self.bound_wave.setItemText(0, _translate("MainWindow", "正弦波"))
|
||||
self.bound_wave.setItemText(1, _translate("MainWindow", "方波"))
|
||||
self.label_14.setText(_translate("MainWindow", "通道"))
|
||||
self.plainTextEdit_channel.setPlainText(_translate("MainWindow", "41, 50, 51, 52, 53, 54, 57, 58, 59"))
|
||||
self.label_2.setText(_translate("MainWindow", "按钮显示"))
|
||||
self.plainTextEdit_buttons.setPlainText(_translate("MainWindow", "\'1\', \'2\', \'3\', \'4\', \'5\', \'6\', \'7\', \'展开\', \'A\', \'B\', \'C\', \'D\', \'E\', \'F\', \'G\', \'H\', \'I\', \'J\', \'K\', \'L\', \'M\', \'N\', \'O\', \'P\', \'Q\', \'R\', \'S\', \'T\', \'U\', \'V\', \'W\', \'X\', \'Y\', \'Z\', \',\', \'。\', \'_\', \'GPT4o\', \'语音\', \'退格\'"))
|
||||
self.check.setText(_translate("MainWindow", "检测串口"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "配置"))
|
||||
import resources_rc
|
BIN
Paradigm/ui/logo.png
Normal file
After Width: | Height: | Size: 216 KiB |
BIN
Paradigm/ui/mylogo.ico
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
Paradigm/ui/mylogo.jpg
Normal file
After Width: | Height: | Size: 180 KiB |
5
Paradigm/ui/res.qrc
Normal file
@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/img">
|
||||
<file>logo.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
5361
Paradigm/ui/resources.qrc
Normal file
98
Paradigm/ui/ui.py
Normal file
@ -0,0 +1,98 @@
|
||||
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
from qfluentwidgets import *
|
||||
class ColorButton(QPushButton):
|
||||
def __init__(self, initial_color, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setStyleSheet(f"background-color: {initial_color};")
|
||||
|
||||
def changeColor(self, new_color):
|
||||
if type(new_color) in [int, float]:
|
||||
self.setStyleSheet(f"background-color: rgb({new_color}, {new_color}, {new_color});")
|
||||
else:
|
||||
self.setStyleSheet(f"background-color: {new_color};")
|
||||
|
||||
def initUI(self, side_space = 20, grid_space = 80, layout_space = 50, input_space = 10, fontnum = 40):
|
||||
'''
|
||||
#########################
|
||||
UI界面配置
|
||||
#########################
|
||||
'''
|
||||
# side_space = 20 # 两侧空白宽度
|
||||
# grid_space = 80 # 键盘间隔
|
||||
# layout_space = 50 # 布局间隔
|
||||
# input_space = 10 # 输入间隔
|
||||
# fontnum = 40 # 字体大小
|
||||
|
||||
# 主窗口设置
|
||||
self.setWindowTitle("5x8 Button Matrix with SSVEP")
|
||||
if self.fsc_flag:
|
||||
self.setWindowFlags(Qt.FramelessWindowHint)
|
||||
self.resize(QDesktopWidget().screenGeometry().width(), QDesktopWidget().screenGeometry().height())
|
||||
# self.setWindowFlag(Qt.WindowStaysOnTopHint, True)
|
||||
self.setFixedSize(self.width(), self.height())
|
||||
|
||||
# 添加布局
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.gridLayout = QGridLayout()
|
||||
self.inputLayout = QVBoxLayout()
|
||||
self.topLayout = QHBoxLayout()
|
||||
# 添加控件
|
||||
self.inputField = LineEdit()
|
||||
self.choseField = LineEdit()
|
||||
self.buttonAbove = PushButton("开始")
|
||||
self.buttonAbove.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
|
||||
# 调整间隙
|
||||
self.layout.setSpacing(layout_space)
|
||||
self.gridLayout.setSpacing(grid_space)
|
||||
self.inputLayout.setSpacing(input_space)
|
||||
|
||||
# 添加侧边的间距
|
||||
leftSpacer = QSpacerItem(side_space, 20, QSizePolicy.Fixed, QSizePolicy.Expanding)
|
||||
rightSpacer = QSpacerItem(side_space, 20, QSizePolicy.Fixed, QSizePolicy.Expanding)
|
||||
downSpacer = QSpacerItem(20, 20, QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
self.gridLayout.addItem(leftSpacer, 0, 0)
|
||||
self.gridLayout.addItem(rightSpacer, 0, 6)
|
||||
self.inputField.setMaximumHeight(60)
|
||||
self.choseField.setMaximumHeight(40)
|
||||
|
||||
# 设置控件大小
|
||||
# self.setStyleSheet(f"background-color: rgb(10, 10, 10);")
|
||||
self.inputField.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
self.choseField.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
|
||||
self.buttonAbove.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
|
||||
# self.buttonBelow.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
|
||||
# 设置样式
|
||||
self.buttonAbove.setStyleSheet("background-color: grey;color: white;")
|
||||
# self.buttonBelow.setStyleSheet("background-color: grey;color: white;")
|
||||
self.inputField.setStyleSheet("background-color: grey")
|
||||
self.choseField.setStyleSheet("background-color: grey")
|
||||
self.setStyleSheet(
|
||||
f"border-radius: 10px;font-family: 微软雅黑;font-size: {fontnum}px;background-color: rgb(10, 10, 10);")
|
||||
|
||||
# 生成按钮
|
||||
for col in range(self.cols):
|
||||
for row in range(self.rows):
|
||||
try:
|
||||
button = ColorButton("white", self.buttonNames[col * self.rows + row])
|
||||
except:
|
||||
button = ColorButton("white", " ")
|
||||
button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
button.clicked.connect(self.buttonClickHandler)
|
||||
self.buttons.append(button)
|
||||
self.gridLayout.addWidget(button, col, row + 1)
|
||||
|
||||
# 添加layout
|
||||
self.inputLayout.addWidget(self.inputField)
|
||||
self.inputLayout.addWidget(self.choseField)
|
||||
self.topLayout.addLayout(self.inputLayout)
|
||||
self.topLayout.addWidget(self.buttonAbove)
|
||||
self.layout.addLayout(self.topLayout)
|
||||
self.layout.addLayout(self.gridLayout)
|
||||
self.layout.addItem(downSpacer)
|
||||
# self.layout.addWidget(self.buttonBelow, alignment=Qt.AlignCenter)
|
BIN
Paradigm/user32.dll
Normal file
48
Paradigm/usr/commond.txt
Normal file
@ -0,0 +1,48 @@
|
||||
移动到起始位置并开始绘制直升机的外轮廓:
|
||||
|
||||
$Robot_Arm$(0, 0, 0)
|
||||
$Robot_Arm$(1, 0, 0)
|
||||
$Robot_Arm$(1, 1, 0)
|
||||
$Robot_Arm$(0, 1, 0)
|
||||
$Robot_Arm$(0, 0, 0)
|
||||
绘制直升机的驾驶舱:
|
||||
|
||||
$Robot_Arm$(0.2, 0.2, 0)
|
||||
$Robot_Arm$(0.8, 0.2, 0)
|
||||
$Robot_Arm$(0.8, 0.6, 0)
|
||||
$Robot_Arm$(0.2, 0.6, 0)
|
||||
$Robot_Arm$(0.2, 0.2, 0)
|
||||
绘制直升机的机身部分:
|
||||
|
||||
$Robot_Arm$(0.8, 0.4, 0)
|
||||
$Robot_Arm$(1.2, 0.4, 0)
|
||||
$Robot_Arm$(1.2, 0.6, 0)
|
||||
$Robot_Arm$(0.8, 0.6, 0)
|
||||
绘制直升机的机尾部分:
|
||||
|
||||
$Robot_Arm$(1.2, 0.5, 0)
|
||||
$Robot_Arm$(1.4, 0.5, 0)
|
||||
$Robot_Arm$(1.4, 0.6, 0)
|
||||
$Robot_Arm$(1.2, 0.6, 0)
|
||||
绘制直升机的旋翼:
|
||||
|
||||
$Robot_Arm$(0.6, 0, 0)
|
||||
$Robot_Arm$(0.6, -0.4, 0)
|
||||
$Robot_Arm$(0.6, 0.4, 0)
|
||||
绘制直升机的起落架:
|
||||
|
||||
$Robot_Arm$(0.2, 0.8, 0)
|
||||
$Robot_Arm$(0.2, 1, 0)
|
||||
$Robot_Arm$(0.4, 1, 0)
|
||||
$Robot_Arm$(0.4, 0.8, 0)
|
||||
绘制文字“Hope Pleacple”:
|
||||
|
||||
$Robot_Arm$(0, 1.2, 0)
|
||||
$Robot_Arm$(0.1, 1.2, 0) // H
|
||||
$Robot_Arm$(0.1, 1.3, 0)
|
||||
$Robot_Arm$(0.05, 1.25, 0)
|
||||
$Robot_Arm$(0.15, 1.25, 0)
|
||||
$Robot_Arm$(0.2, 1.2, 0) // o
|
||||
$Robot_Arm$(0.3, 1.2, 0) // p
|
||||
$Robot_Arm$(0.3, 1.3, 0)
|
||||
$Robot_Arm$(0.4, 1.2, 0) // e
|
BIN
Paradigm/usr/iimmgg.png
Normal file
After Width: | Height: | Size: 110 KiB |
BIN
Paradigm/usr/imgSave.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
Paradigm/usr/logo.jpg
Normal file
After Width: | Height: | Size: 259 KiB |
BIN
Paradigm/usr/mylogo.jpg
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
Paradigm/usr/watch.jpg
Normal file
After Width: | Height: | Size: 403 KiB |
BIN
Paradigm/vcruntime140_1d.dll
Normal file
BIN
Paradigm/vcruntime140d.dll
Normal file
35
README.md
@ -1,5 +1,12 @@
|
||||
<div align="right">
|
||||
<img src="https://imgpool.protodrive.xyz/img/yank-note-picgo-img-20241105215145.png" alt="Image Description" width="150" />
|
||||
</div>
|
||||
<h1 align = "center">🌟YuEEG 8通道脑机接口设备🌟</h1>
|
||||
|
||||
|
||||
<p align = "center">
|
||||
<img src="https://imgpool.protodrive.xyz/img/yank-note-picgo-img-20241110230401.jpg" width="400" />
|
||||
</p>
|
||||
<p align = "center">
|
||||
<img src="https://img.shields.io/badge/Arduino-passing-green" />
|
||||
<img src="https://img.shields.io/badge/PlatformIO-passing-green" />
|
||||
@ -7,18 +14,12 @@
|
||||
<img src="https://img.shields.io/badge/ADS1299%20-项目-red" />
|
||||
<img src="https://img.shields.io/badge/YuEEG-V1.6-grey" />
|
||||
</p>
|
||||
<p align = "center">
|
||||
<img src="https://imgpool.protodrive.xyz/img/yank-note-picgo-img-20241110230401.jpg" width="400" />
|
||||
</p>
|
||||
|
||||
> [!CAUTION]
|
||||
> 项目基于自定义的 MIT 协议,除本项目拥有者以外该项目不允许用来参加任何商业比赛。
|
||||
|
||||
> [!NOTE]
|
||||
> 项目尚未完全更新【SSVEP 范式】,建议 Star 后,会添加更多内容。
|
||||
|
||||
> [!NOTE]
|
||||
> 项目尚未完全更新【SSVEP 范式】,建议 Star 后,会添加更多内容。
|
||||
> 项目已更新【SSVEP 范式】,后续还会添加APP以及网页波形显示,建议 Star 或者Fork后,作者会添加更多内容。
|
||||
|
||||
## 概述 🧠🔌
|
||||
|
||||
@ -61,7 +62,7 @@
|
||||
|
||||
| 正面 | 反面 |
|
||||
| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
### 外壳模型设计图:
|
||||
|
||||
@ -93,6 +94,21 @@
|
||||
| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|
||||
## 范式程序
|
||||
### 功能:
|
||||
|
||||
- **拼音输入**:通过C++编写的动态链接库。
|
||||
- **联想拼写**:隐马尔可夫链模型。
|
||||
- **分类算法**:FBCCA算法
|
||||
- **支持多语言**:NLTK模型。
|
||||
|
||||
|配置界面|运行界面|
|
||||
|-|-|
|
||||
|||
|
||||
|
||||
>运行 ssvep_paradigm.py
|
||||
|
||||
|
||||
## 使用指南 📚
|
||||
|
||||
### 硬件连接:
|
||||
@ -101,7 +117,8 @@
|
||||
- USB 接入 XiaoESP32S3 直接供电使用,同时串口发送数据
|
||||
- 背面焊接 TPS63070 锂电池转 5V 模块,然后电源接口接入 3.7V 锂电池即可
|
||||
1. 将 EEG 电极正确连接到被测试者。
|
||||
1. 使用 USB 线连接设备和电脑。
|
||||
2. 使用 USB 线连接设备和电脑。
|
||||
3. 硬件上支持BMM150以及Makerbase的MKS-142健康监测模块
|
||||
|
||||
### 引脚连接
|
||||
|
||||
|
3
Software/.idea/.gitignore
generated
vendored
@ -1,3 +0,0 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
112
Software/.idea/inspectionProfiles/Project_Default.xml
generated
@ -1,112 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="91">
|
||||
<item index="0" class="java.lang.String" itemvalue="six" />
|
||||
<item index="1" class="java.lang.String" itemvalue="wheel" />
|
||||
<item index="2" class="java.lang.String" itemvalue="python-dateutil" />
|
||||
<item index="3" class="java.lang.String" itemvalue="tzdata" />
|
||||
<item index="4" class="java.lang.String" itemvalue="PyQt5-sip" />
|
||||
<item index="5" class="java.lang.String" itemvalue="pyserial" />
|
||||
<item index="6" class="java.lang.String" itemvalue="setuptools" />
|
||||
<item index="7" class="java.lang.String" itemvalue="numpy" />
|
||||
<item index="8" class="java.lang.String" itemvalue="Pinyin2Hanzi" />
|
||||
<item index="9" class="java.lang.String" itemvalue="pandas" />
|
||||
<item index="10" class="java.lang.String" itemvalue="PyQt5-Qt5" />
|
||||
<item index="11" class="java.lang.String" itemvalue="pip" />
|
||||
<item index="12" class="java.lang.String" itemvalue="PyQt5" />
|
||||
<item index="13" class="java.lang.String" itemvalue="PyAudio" />
|
||||
<item index="14" class="java.lang.String" itemvalue="pytz" />
|
||||
<item index="15" class="java.lang.String" itemvalue="scipy" />
|
||||
<item index="16" class="java.lang.String" itemvalue="joblib" />
|
||||
<item index="17" class="java.lang.String" itemvalue="threadpoolctl" />
|
||||
<item index="18" class="java.lang.String" itemvalue="scikit-learn" />
|
||||
<item index="19" class="java.lang.String" itemvalue="dlib" />
|
||||
<item index="20" class="java.lang.String" itemvalue="opencv_python" />
|
||||
<item index="21" class="java.lang.String" itemvalue="onnxruntime-gpu" />
|
||||
<item index="22" class="java.lang.String" itemvalue="protobuf" />
|
||||
<item index="23" class="java.lang.String" itemvalue="nvidia-cuda-cupti-cu11" />
|
||||
<item index="24" class="java.lang.String" itemvalue="MarkupSafe" />
|
||||
<item index="25" class="java.lang.String" itemvalue="requests" />
|
||||
<item index="26" class="java.lang.String" itemvalue="torchvision" />
|
||||
<item index="27" class="java.lang.String" itemvalue="nvidia-cublas-cu11" />
|
||||
<item index="28" class="java.lang.String" itemvalue="opencv-python-headless" />
|
||||
<item index="29" class="java.lang.String" itemvalue="nvidia-cusparse-cu11" />
|
||||
<item index="30" class="java.lang.String" itemvalue="nvidia-cusolver-cu11" />
|
||||
<item index="31" class="java.lang.String" itemvalue="nvidia-curand-cu11" />
|
||||
<item index="32" class="java.lang.String" itemvalue="nvidia-nccl-cu11" />
|
||||
<item index="33" class="java.lang.String" itemvalue="gdown" />
|
||||
<item index="34" class="java.lang.String" itemvalue="filelock" />
|
||||
<item index="35" class="java.lang.String" itemvalue="nvidia-cudnn-cu11" />
|
||||
<item index="36" class="java.lang.String" itemvalue="lit" />
|
||||
<item index="37" class="java.lang.String" itemvalue="certifi" />
|
||||
<item index="38" class="java.lang.String" itemvalue="nvidia-cufft-cu11" />
|
||||
<item index="39" class="java.lang.String" itemvalue="soupsieve" />
|
||||
<item index="40" class="java.lang.String" itemvalue="urllib3" />
|
||||
<item index="41" class="java.lang.String" itemvalue="nvidia-cuda-runtime-cu11" />
|
||||
<item index="42" class="java.lang.String" itemvalue="beautifulsoup4" />
|
||||
<item index="43" class="java.lang.String" itemvalue="triton" />
|
||||
<item index="44" class="java.lang.String" itemvalue="nvidia-cuda-nvrtc-cu11" />
|
||||
<item index="45" class="java.lang.String" itemvalue="packaging" />
|
||||
<item index="46" class="java.lang.String" itemvalue="torch" />
|
||||
<item index="47" class="java.lang.String" itemvalue="tqdm" />
|
||||
<item index="48" class="java.lang.String" itemvalue="flatbuffers" />
|
||||
<item index="49" class="java.lang.String" itemvalue="nvidia-nvtx-cu11" />
|
||||
<item index="50" class="java.lang.String" itemvalue="cmake" />
|
||||
<item index="51" class="java.lang.String" itemvalue="typing_extensions" />
|
||||
<item index="52" class="java.lang.String" itemvalue="charset-normalizer" />
|
||||
<item index="53" class="java.lang.String" itemvalue="Pillow" />
|
||||
<item index="54" class="java.lang.String" itemvalue="transformers" />
|
||||
<item index="55" class="java.lang.String" itemvalue="altgraph" />
|
||||
<item index="56" class="java.lang.String" itemvalue="Bottleneck" />
|
||||
<item index="57" class="java.lang.String" itemvalue="brotlipy" />
|
||||
<item index="58" class="java.lang.String" itemvalue="greenlet" />
|
||||
<item index="59" class="java.lang.String" itemvalue="qt-material" />
|
||||
<item index="60" class="java.lang.String" itemvalue="cffi" />
|
||||
<item index="61" class="java.lang.String" itemvalue="cloudpickle" />
|
||||
<item index="62" class="java.lang.String" itemvalue="QtPy" />
|
||||
<item index="63" class="java.lang.String" itemvalue="mkl-random" />
|
||||
<item index="64" class="java.lang.String" itemvalue="mss" />
|
||||
<item index="65" class="java.lang.String" itemvalue="importlib-metadata" />
|
||||
<item index="66" class="java.lang.String" itemvalue="mkl-fft" />
|
||||
<item index="67" class="java.lang.String" itemvalue="gym-notices" />
|
||||
<item index="68" class="java.lang.String" itemvalue="pyOpenSSL" />
|
||||
<item index="69" class="java.lang.String" itemvalue="zipp" />
|
||||
<item index="70" class="java.lang.String" itemvalue="pywin32-ctypes" />
|
||||
<item index="71" class="java.lang.String" itemvalue="gevent" />
|
||||
<item index="72" class="java.lang.String" itemvalue="pyparsing" />
|
||||
<item index="73" class="java.lang.String" itemvalue="zope.event" />
|
||||
<item index="74" class="java.lang.String" itemvalue="auto-py-to-exe" />
|
||||
<item index="75" class="java.lang.String" itemvalue="QScintilla" />
|
||||
<item index="76" class="java.lang.String" itemvalue="pyinstaller" />
|
||||
<item index="77" class="java.lang.String" itemvalue="wincertstore" />
|
||||
<item index="78" class="java.lang.String" itemvalue="nes-py" />
|
||||
<item index="79" class="java.lang.String" itemvalue="opencv-python" />
|
||||
<item index="80" class="java.lang.String" itemvalue="pyglet" />
|
||||
<item index="81" class="java.lang.String" itemvalue="cryptography" />
|
||||
<item index="82" class="java.lang.String" itemvalue="zope.interface" />
|
||||
<item index="83" class="java.lang.String" itemvalue="QDarkStyle" />
|
||||
<item index="84" class="java.lang.String" itemvalue="pyinstaller-hooks-contrib" />
|
||||
<item index="85" class="java.lang.String" itemvalue="win-inet-pton" />
|
||||
<item index="86" class="java.lang.String" itemvalue="PySocks" />
|
||||
<item index="87" class="java.lang.String" itemvalue="gym" />
|
||||
<item index="88" class="java.lang.String" itemvalue="pypiwin32" />
|
||||
<item index="89" class="java.lang.String" itemvalue="mkl-service" />
|
||||
<item index="90" class="java.lang.String" itemvalue="numexpr" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="N801" />
|
||||
<option value="N802" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
@ -1,6 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
4
Software/.idea/misc.xml
generated
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="SSVEP" project-jdk-type="Python SDK" />
|
||||
</project>
|
8
Software/.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/software.iml" filepath="$PROJECT_DIR$/.idea/software.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
8
Software/.idea/software.iml
generated
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="SSVEP" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
6
Software/.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,271 +0,0 @@
|
||||
import sys
|
||||
import time
|
||||
from PyQt5 import QtWidgets, QtCore
|
||||
from collections import deque
|
||||
import pyqtgraph as pg
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
import re
|
||||
import numpy as np
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout
|
||||
from pyqtgraph import LegendItem
|
||||
from qfluentwidgets import LineEdit, PushButton, TextEdit, CheckBox
|
||||
from scipy.signal import welch, butter, filtfilt
|
||||
|
||||
# Set the length of the data pool to handle 750 samples (3 seconds of data at 250Hz)
|
||||
data_len = 2000
|
||||
|
||||
|
||||
class SerialThread(QtCore.QThread):
|
||||
data_received = QtCore.pyqtSignal(str) # Signal to send data back to the main thread
|
||||
|
||||
def __init__(self, com_port):
|
||||
super().__init__()
|
||||
self.com_port = com_port
|
||||
self.serial_port = serial.Serial(self.com_port, baudrate=115200, timeout=1)
|
||||
self.data_pool = [deque(maxlen=data_len) for _ in range(9)] # Data pool for each channel
|
||||
self.running = True
|
||||
|
||||
def run(self):
|
||||
self.serial_port.write(b'1') # Send '1' to start data transmission
|
||||
while self.running:
|
||||
if self.serial_port.in_waiting:
|
||||
try:
|
||||
raw_data = self.serial_port.readline().decode('utf-8').strip()
|
||||
if raw_data:
|
||||
# Emit the raw data to the main thread for display
|
||||
self.data_received.emit(raw_data)
|
||||
# print(f"{raw_data}")
|
||||
# Integrity check (matches 'Channel:' followed by 9 float values)
|
||||
match = re.match(r'Channel:([\d\.\-]+,){8}[\d\.\-]+', raw_data)
|
||||
if match:
|
||||
values = [float(x) for x in raw_data.split('Channel:')[1].split(',')]
|
||||
# Add data to the pool for each channel
|
||||
for i in range(9):
|
||||
self.data_pool[i].append(values[i])
|
||||
except:
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
self.serial_port.close()
|
||||
|
||||
def send_data(self, data):
|
||||
self.serial_port.write(data.encode('utf-8') + b'\r\n') # 发送数据到串口
|
||||
|
||||
def get_latest_data(self, size):
|
||||
"""Return the latest 'size' data from the data pool for all channels."""
|
||||
if size > len(self.data_pool[0]):
|
||||
raise ValueError("Requested size is larger than the current data pool size.")
|
||||
|
||||
# Convert the latest 'size' elements from deque to numpy array for each channel
|
||||
return np.array([list(self.data_pool[i])[-size:] for i in range(9)])
|
||||
|
||||
|
||||
class ADCPlotter(QtWidgets.QMainWindow):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.setWindowTitle("YuEEG Data Viewer")
|
||||
self.setWindowIcon(QIcon('./school_logo.ico'))
|
||||
self.setGeometry(100, 100, 800, 750)
|
||||
# Detect available COM ports and connect to the first available one
|
||||
com_port = self.detect_com_port()
|
||||
print(f"Connected to: {com_port}")
|
||||
if not com_port:
|
||||
raise Exception("No available COM ports detected.")
|
||||
|
||||
# Main layout
|
||||
self.central_widget = QtWidgets.QWidget()
|
||||
self.setCentralWidget(self.central_widget)
|
||||
self.layout = QtWidgets.QVBoxLayout(self.central_widget)
|
||||
|
||||
# Set up PyQtGraph plot
|
||||
self.plot_widget = pg.PlotWidget()
|
||||
self.layout.addWidget(self.plot_widget)
|
||||
# Create a legend
|
||||
self.legend = LegendItem(offset=(30, -30)) # Position the legend in the plot area
|
||||
self.legend.setParentItem(self.plot_widget.graphicsItem())
|
||||
# Create colored plots with thicker lines for each channel
|
||||
self.colors = ['r', 'g', 'b', 'c', 'm', 'y', 'w', 'grey', 'orange']
|
||||
self.plot_data = []
|
||||
for i in range(9):
|
||||
plot_item = self.plot_widget.plot(pen=pg.mkPen(color=self.colors[i], width=2))
|
||||
self.plot_data.append(plot_item)
|
||||
self.legend.addItem(plot_item, f"Channel {i + 1}") # Add each plot to the legend with a label
|
||||
|
||||
# Add a QTextEdit widget to display the raw data received
|
||||
self.text_box = TextEdit(self)
|
||||
self.text_box.setReadOnly(True)
|
||||
|
||||
self.send_text = LineEdit()
|
||||
self.send_button = PushButton("Send")
|
||||
self.adc_button = PushButton("Normal")
|
||||
self.adc_button.clicked.connect(self.mod_switch)
|
||||
self.send_button.clicked.connect(self.send_data)
|
||||
sendV_layout = QHBoxLayout()
|
||||
sendV_layout.addWidget(self.adc_button)
|
||||
sendV_layout.addWidget(self.send_text)
|
||||
sendV_layout.addWidget(self.send_button)
|
||||
|
||||
self.layout.addWidget(self.text_box)
|
||||
self.layout.addLayout(sendV_layout)
|
||||
|
||||
# Add checkboxes for selecting channels to display
|
||||
self.checkboxes = []
|
||||
self.checkbox_layout = QHBoxLayout()
|
||||
for i in range(9):
|
||||
checkbox = CheckBox(f"通道{i + 1}")
|
||||
checkbox.setChecked(True) # All channels checked by default
|
||||
self.checkboxes.append(checkbox)
|
||||
self.checkbox_layout.addWidget(checkbox)
|
||||
|
||||
self.layout.addLayout(self.checkbox_layout)
|
||||
|
||||
# Create a thread to handle serial data reception
|
||||
self.serial_thread = SerialThread(com_port)
|
||||
self.serial_thread.data_received.connect(self.handle_serial_data)
|
||||
self.serial_thread.start()
|
||||
|
||||
# Timer to refresh plot every 20ms
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.update_plot)
|
||||
self.timer.start(100)
|
||||
self.beg = time.time()
|
||||
self.previous_psd = None
|
||||
|
||||
def send_data(self):
|
||||
data = self.send_text.text()
|
||||
self.serial_thread.send_data(data)
|
||||
|
||||
def mod_switch(self):
|
||||
if self.adc_button.text() == "Normal":
|
||||
self.serial_thread.send_data("3")
|
||||
self.adc_button.setText("Test")
|
||||
elif self.adc_button.text() == "Test":
|
||||
self.serial_thread.send_data("1")
|
||||
self.adc_button.setText("Normal")
|
||||
def detect_com_port(self):
|
||||
"""Automatically detect available COM ports, excluding virtual ports."""
|
||||
ports = list(serial.tools.list_ports.comports())
|
||||
for port in ports:
|
||||
if 'Bluetooth' not in port.description and 'Virtual' not in port.description:
|
||||
return port.device
|
||||
return None
|
||||
|
||||
def append_text_box(self, text):
|
||||
"""Append text to the text box, keeping only the last 10 lines."""
|
||||
current_text = self.text_box.toPlainText()
|
||||
lines = current_text.split('\n')
|
||||
|
||||
# Append new text
|
||||
lines.append(text)
|
||||
|
||||
# Keep only the last 10 lines
|
||||
if len(lines) > 10:
|
||||
lines = lines[-10:]
|
||||
|
||||
# Update the text box
|
||||
self.text_box.setPlainText('\n'.join(lines))
|
||||
# Move the cursor to the end
|
||||
# self.text_box.moveCursor(QtCore.QTextCursor.End)
|
||||
def check_psd(self, data, fs):
|
||||
threshold = 50
|
||||
if data is not None:
|
||||
psd_values = self.compute_psd(data, fs)
|
||||
current_psd_sum = np.sum(psd_values)
|
||||
if self.previous_psd is not None:
|
||||
# 比较当前PSD总和与上次总和的变化是否超过阈值
|
||||
if threshold < current_psd_sum - self.previous_psd < 500:
|
||||
print(f"发现向上突变,目前差值为:{(current_psd_sum - self.previous_psd)}")
|
||||
return True
|
||||
else:
|
||||
print(f"差值为:{current_psd_sum - self.previous_psd}")
|
||||
self.previous_psd = current_psd_sum
|
||||
def compute_psd(self, data, fs):
|
||||
# 定义感兴趣的频率范围
|
||||
low_freq = 3
|
||||
high_freq = 40
|
||||
|
||||
# 存储每个通道在3-40Hz的PSD值
|
||||
psd_values = []
|
||||
|
||||
for i in range(data.shape[0]):
|
||||
# 计算功率谱密度
|
||||
f, psd = welch(data[i, :], fs, nperseg=500)
|
||||
|
||||
# 只保留3到40Hz的频段
|
||||
freq_mask = (f >= low_freq) & (f <= high_freq)
|
||||
psd_in_band = psd[freq_mask]
|
||||
|
||||
# 计算3到40Hz频段内的PSD值(通过对频段内的PSD进行积分)
|
||||
psd_band_value = np.trapz(psd_in_band, f[freq_mask])
|
||||
|
||||
# 将结果存储起来
|
||||
psd_values.append(psd_band_value)
|
||||
return psd_values
|
||||
def handle_serial_data(self, raw_data):
|
||||
"""Handle data received from the serial thread."""
|
||||
self.append_text_box(raw_data)
|
||||
|
||||
# 定义 Buterworth 滤波器函数
|
||||
def apply_lowpass_filter(self, signal, cutoff_freq=50, sample_rate=500, order=5):
|
||||
nyquist_freq = 0.5 * sample_rate
|
||||
normalized_cutoff_freq = cutoff_freq / nyquist_freq
|
||||
b, a = butter(order, normalized_cutoff_freq, btype='low', analog=False)
|
||||
filtered_signal = filtfilt(b, a, signal)
|
||||
return filtered_signal
|
||||
|
||||
def update_plot(self):
|
||||
"""Update the plot based on the current data in the data pool."""
|
||||
try:
|
||||
# Fetch the latest 750 samples from the serial thread data pool
|
||||
latest_data = self.serial_thread.get_latest_data(data_len)
|
||||
|
||||
|
||||
# Update the plot for each channel
|
||||
for i in range(9):
|
||||
if self.checkboxes[i].isChecked(): # Only update if the channel is checked
|
||||
waveform = latest_data[i]
|
||||
filtered_waveform = self.apply_lowpass_filter(waveform)
|
||||
self.plot_data[i].setData(filtered_waveform)
|
||||
else:
|
||||
self.plot_data[i].setData([]) # Hide the waveform by clearing the data
|
||||
# self.plot_data[7].setData(latest_data[7])
|
||||
# Collect data from only the selected channels
|
||||
selected_data = [latest_data[i] for i in range(9) if self.checkboxes[i].isChecked()]
|
||||
|
||||
# If there is any selected data, calculate global min and max
|
||||
if selected_data:
|
||||
global_min = np.min([np.min(data) for data in selected_data])
|
||||
global_max = np.max([np.max(data) for data in selected_data])
|
||||
# Set Y-axis range based on global min/max values
|
||||
self.plot_widget.setYRange(global_min, global_max)
|
||||
else:
|
||||
global_min = -4.5
|
||||
global_max = 4.5
|
||||
# Set Y-axis range based on global min/max values
|
||||
self.plot_widget.setYRange(global_min, global_max)
|
||||
eeg_data = latest_data
|
||||
self.channel = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
eeg_data = np.array([eeg_data[i - 1] for i in self.channel])
|
||||
if time.time() - self.beg > 1:
|
||||
psd_values = self.compute_psd(eeg_data, 500)
|
||||
current_psd_sum = np.sum(psd_values)
|
||||
print(f"psd:{current_psd_sum}")
|
||||
self.beg = time.time()
|
||||
except ValueError:
|
||||
pass # Skip plotting if not enough data is available
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Ensure the thread is stopped when the application is closed."""
|
||||
self.serial_thread.stop()
|
||||
self.serial_thread.wait()
|
||||
event.accept()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
main = ADCPlotter()
|
||||
main.show()
|
||||
sys.exit(app.exec_())
|
Before Width: | Height: | Size: 26 KiB |