class Sound(object): def __init__(self, filename, volume): super(Sound, self).__init__() file = os.path.join(BASEDIR, 'sounds', filename) self.volume = volume or 0 self.effect = QSoundEffect() self.effect.setSource(QUrl.fromLocalFile(file)) def play(self, volume=None, loop=True): volume = self.volume if volume is None else volume self.effect.setLoopCount(0) self.effect.setVolume(int(volume) / 100) if loop: self.effect.setLoopCount(QSoundEffect.Infinite) else: self.effect.setLoopCount(1) if not self.effect.isPlaying(): self.effect.play() def stop(self): self.effect.stop() def setVolume(self, volume): self.volume = volume
def init_sounds(): for sound_name, value in sounds_list.items(): config = DEFAULT_CONFIG.copy() if isinstance(value, dict): config.update(value) else: config['file_name'] = value volume = float(config['volume']) volumes[sound_name] = volume vec = [] for i in range(CHANNELS_PER_FILE): s = QSoundEffect(app) s.statusChanged.connect( lambda i=i, s=s: log('stateChanged: {}: {}: {}'.format(s.source().toLocalFile(), i, s.status())) ) s.setLoopCount(int(config['loop_count'])) s.setVolume(volume) s.setSource(QUrl.fromLocalFile(config['file_name'])) vec.append(s) sounds[sound_name] = tuple(vec) #log('xxx: ' + str(sounds)) # start polling for messages def proc(): #while not queue.empty(): # queue.get(False) timer.start(10) QTimer.singleShot(500, proc)
class SoftKey(QLabel): def __init__(self, num, parent): assert num >= 0 and num <= 3 labels = ['\u21ba', '\u25bc', '\u25b2', '\u23ce'] xpositions = [0, 40, 80, 120] sfxfiles = ['sfx014.wav', 'sfx013.wav', 'sfx033.wav', 'sfx061.wav'] label = labels[num] xpos = xpositions[num] sfx = sfxfiles[num] super().__init__(label, parent) self.sfx = QSoundEffect(self) self.sfx.setSource(QUrl.fromLocalFile(sfx)) self.sfx.setVolume(0.5) self.resize(40, 27) self.move(xpos, 100) self.setFrameStyle(QFrame.Panel | QFrame.Raised) self.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.showPressed(False) self.show() def showPressed(self, pressed): if pressed: self.sfx.play() self.setLineWidth(4) else: self.setLineWidth(2)
def preload_sound_effect(cls, path): if not hasattr(cls, "sound_effects"): cls.sound_effects = {} cls.se_volume = 100 if path not in cls.sound_effects: se = QSoundEffect() se.setVolume(cls.se_volume / 100) se.setSource(QUrl.fromLocalFile(path)) cls.sound_effects[path] = (se)
class noiseGenerator(QThread): def __init__(self, playnoise, volume): super().__init__() pathname = os.path.dirname(os.path.realpath('whitenoise.wav')) self.sound = QSoundEffect() self.sound.setSource(QUrl.fromLocalFile(pathname + '/whitenoise.wav')) self.sound.setLoopCount(QSoundEffect.Infinite) self.settings(playnoise, volume) def settings(self, playnoise, volume): self.playnoise = playnoise self.sound.setVolume(volume) def run(self): if self.playnoise: self.sound.play() def stop(self): self.sound.stop()
def __init__(self, parent): """ Tab window for visualising Sorting Algorithms """ super(SortingTab, self).__init__(parent) # Setup sounds self.sound_enabled = True self.sounds = [] for i in range(64): sound = QSoundEffect() sound.setSource( QUrl.fromLocalFile(f"../assets/sounds/tone-{i}.wav")) sound.setVolume(0.3) self.sounds.append(sound) self.is_sound_playing = lambda: False # Setup sorting widgets self.layout = QGridLayout(self) self.layout.setAlignment(Qt.AlignCenter) self.rendering_type = SortRenderType.BarGraph self.rainbow = False
class Notification(QMainWindow): '''Transparent window containing an image or animated gif, used to display notifications.''' def __init__(self, name, notifier, artPath, link, sound=None, *args, **kwargs): '''- name : a string - notifier : a Notifier object used to manage this notification - artPath : a string containing the path to the image file to be displayed - link : a string containing the link to be opened when left-clicking the notification - sound : a string containing the path to the object or None. Set to None for silent notifiations.''' super().__init__(*args, **kwargs) self.setCursor(QCursor(Qt.PointingHandCursor)) self.name = name self.notifier = notifier self.link = link self.artPath = artPath self.isMovie = self.artPath.endswith(".gif") if sound == None: self.sound = None else: self.sound = QSoundEffect() self.sound.setSource(QUrl.fromLocalFile(sound)) self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool) self.setAttribute(Qt.WA_TranslucentBackground) imageLabel = QLabel() self.setCentralWidget(imageLabel) if self.isMovie: self.art = QMovie(self.artPath) self.art.jumpToNextFrame() imageLabel.setMovie(self.art) self.moveToBottomRight(self.art.frameRect().width(), self.art.frameRect().height()) else: self.art = QPixmap(self.artPath) imageLabel.setPixmap(self.art) self.moveToBottomRight(self.art.width(), self.art.height()) def moveToBottomRight(self, x, y): '''Moves the notification window to the bottom-right of the screen, above the taskbar''' screen = QDesktopWidget().availableGeometry() x_pos = screen.width() - x y_pos = screen.height() - y self.move(x_pos, y_pos) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.close() webbrowser.open_new_tab(self.link) elif event.button() == Qt.RightButton: self.close() def display(self, volume=1): '''Show the notification window and plays the sound.''' super().show() if self.isMovie: self.art.start() if self.sound: self.sound.setVolume(volume) self.sound.play() def close(self): '''Updates notifier and closes window''' super().close() self.notifier.update()
class QAVButton(QPushButton): def __init__(self, label): """ builds a custom button and displays it""" # calls super constuctor super(QAVButton, self).__init__(label) self.sound = QSoundEffect() self.volume = 1. self.color = QColor(Qt.gray) self.count = 0 self.duration = 1000 self.rate = 20 self.timer = QTimer() self.timer.timeout.connect(self.update_anim) self.mode = "sin" self.pressed.connect(self.start) self.released.connect(self.stop) self.is_accelerating = False self.update_color_with_alha(1) def setSound(self, sound_file): self.sound.setSource(QUrl.fromLocalFile(sound_file)) self.sound.setLoopCount(QSoundEffect.Infinite) def start(self): self.count = 0 self.sound.play() self.timer.start(self.rate) def stop(self): self.timer.stop() self.sound.stop() def set_color(self, col): self.color = col self.update_color_with_alha(1) def update_color_with_alha(self, alpha): red = self.color.red() green = self.color.green() blue = self.color.blue() bg_style = f"background-color:rgba({red},{green},{blue}, {alpha})" self.setStyleSheet(bg_style) def update_anim(self): #logarithmic (check with perception of luminosity/brighness) #val = math.log(count + 1) #linear if self.mode == "sin": val = math.sin(self.count * 2 * math.pi) / 2 + 0.5 elif self.mode == "lin": val = 1 - self.count else: val = math.log(self.count + 1) alpha = round(val * 100) + 155 self.update_color_with_alha(alpha) amplitude = val * 0.8 + 0.2 self.sound.setVolume(amplitude) self.count = self.count + self.rate / self.duration # print(count) if self.count >= 1 - self.rate / self.duration: self.count = 0 if self.is_accelerating: self.duration = max(200, self.duration * 0.95)
class Window(QWidget): def __init__(self): super().__init__() self.title = 'CSC 690 - Project 1' self.model = Model('./data/') self.view_mode = 'thumbnails' self.mode = 'thumbnails' self.stylesheet = '' self.selected_thumbnail_stylesheet = 'border: 5px solid red;' self.window_width = 800 self.window_height = 600 if len(sys.argv) > 1: self.window_width = int(sys.argv[1]) self.window_height = self.window_width * (3/4) self.thumbnail_labels = [] self.thumbnail_pixmaps = [] self.tag_labels = [] self.window_width = 800 self.window_height = 600 if len(sys.argv) > 1: if int(sys.argv[1]) >= 600 and int(sys.argv[1]) <= 1200: self.window_width = int(sys.argv[1]) self.window_height = self.window_width * (3/4) else: print("Given width out of range. Defaulting to 600.") self.init_labels() self.init_controls() if len(self.model.nodes) > 0: self.init_UI() self.init_sounds() def init_UI(self): self.fullscreen_pixmap = QPixmap(self.model.get_current_filename()) self.setWindowTitle(self.title) self.setGeometry(100, 100, self.window_width, self.window_height) self.setStyleSheet('background: #00C0FF;') self.load_thumbnails() # start with first thumbnail selected self.thumbnail_labels[0].setStyleSheet(self.selected_thumbnail_stylesheet) self.fullscreen_label.hide() for label in self.tag_labels: label.hide() self.show() def init_labels(self): self.fullscreen_label = QLabel(self) self.fullscreen_label.resize(self.window_width / 2, self.window_height / 2) self.fullscreen_label.setStyleSheet(self.selected_thumbnail_stylesheet) self.fullscreen_label.setAlignment(Qt.AlignCenter) self.fullscreen_label.setFocusPolicy(Qt.StrongFocus) self.fullscreen_label.move((self.window_width - (self.window_width / 2)) / 2, (self.window_height - (self.window_height/ 2)) /2) for index in range(0, CONST_NUM_TAGS): temp_label = QLabel(self) temp_label.move(650, 400 - (index * 30)) self.tag_labels.append(temp_label) temp_label.hide() def init_controls(self): self.add_tag_button = QPushButton('Add tag', self) self.add_tag_button.setFocusPolicy(Qt.ClickFocus) self.add_tag_button.move((self.window_width / 2) - 180, self.window_height - 50) self.add_tag_button.clicked.connect(self.add_tag) self.add_tag_button.hide() self.search_button = QPushButton('Search', self) self.search_button.setFocusPolicy(Qt.ClickFocus) self.search_button.move(self.window_width / 3.7, self.window_height - (self.window_height / 10)) self.search_button.clicked.connect(self.search_flickr) self.save_tags_button = QPushButton('Save all tags', self) self.save_tags_button.setFocusPolicy(Qt.ClickFocus) self.save_tags_button.move((self.window_width / 2) - 10, self.window_height - 50) self.save_tags_button.clicked.connect(self.save_tags) self.save_tags_button.hide() self.tag_field = QLineEdit(self) self.tag_field.setFocusPolicy(Qt.ClickFocus) self.tag_field.setAlignment(Qt.AlignCenter) self.tag_field.move((self.window_width / 2) - 90, self.window_height - 100) self.tag_field.hide() self.search_text_field = QLineEdit(self) self.search_text_field.setFocusPolicy(Qt.ClickFocus) self.search_text_field.move(self.window_width / 28, self.window_height - (self.window_height / 10)) self.search_number_field = QLineEdit(self) self.search_number_field.setFocusPolicy(Qt.ClickFocus) self.search_number_field.move(self.window_width / 2.5, self.window_height - (self.window_height / 10)) self.search_number_field.setFixedWidth(60) self.test_button = QPushButton('Test', self) self.test_button.setFocusPolicy(Qt.ClickFocus) self.test_button.move(self.window_width / 28, self.window_height - (self.window_height / 19)) self.test_button.clicked.connect(self.test) self.save_photos_button = QPushButton('Save', self) self.save_photos_button.setFocusPolicy(Qt.ClickFocus) self.save_photos_button.move(self.window_width / 7.3, self.window_height - (self.window_height / 19)) self.save_photos_button.clicked.connect(self.save_photos) self.exit_button = QPushButton('Exit', self) self.exit_button.setFocusPolicy(Qt.ClickFocus) self.exit_button.move(self.window_width / 4.2, self.window_height - (self.window_height / 19)) self.exit_button.clicked.connect(self.close) self.delete_button = QPushButton('Delete', self) self.delete_button.setFocusPolicy(Qt.ClickFocus) self.delete_button.move(self.window_width / 2.95, self.window_height - (self.window_height / 19)) self.delete_button.clicked.connect(self.delete) def init_sounds(self): self.train_sound = QSoundEffect() self.train_sound.setSource(QUrl.fromLocalFile('./audio/TRAIN06.WAV')) self.train_sound.setVolume(0.5) self.conk_sound = QSoundEffect() self.conk_sound.setSource(QUrl.fromLocalFile('./audio/CONK.WAV')) self.conk_sound.setVolume(0.5) def delete(self): self.model.delete() self.reload_thumbnails('forward') self.delete_button.clearFocus() def add_tag(self): self.model.add_tag(self.tag_field.text()) self.tag_field.setText('') self.add_tag_button.clearFocus() self.show_tags() def hide_thumbnail_controls(self): self.search_text_field.hide() self.search_number_field.hide() def keyPressEvent(self, event): key_pressed = event.key() if key_pressed == Qt.Key_Right: if self.mode == 'thumbnails': self.next_image() elif self.mode == 'fullscreen': self.next_image() self.show_fullscreen_image() self.show_tags() elif key_pressed == Qt.Key_Left: if self.mode == 'thumbnails': self.prev_image() elif self.mode == 'fullscreen': self.prev_image() self.show_fullscreen_image() self.show_tags() elif key_pressed == Qt.Key_Up: if self.mode == 'thumbnails': self.mode = 'fullscreen' #self.conk_sound.play() self.show_fullscreen_image() self.show_fullscreen_view() elif key_pressed == Qt.Key_Down: if self.mode == 'fullscreen': self.mode = 'thumbnails' #self.conk_sound.play() self.fullscreen_label.hide() self.show_thumbnails_view() elif key_pressed == 46: if self.mode == 'thumbnails': self.train_sound.play() for _ in range(0, CONST_THUMBNAIL_COUNT): self.next_image() self.thumbnail_labels[self.model.get_current_index() % 5].setStyleSheet('') self.model.set_current_index(self.model.get_leftmost_index()) self.thumbnail_labels[self.model.get_current_index() % 5].setStyleSheet(self.selected_thumbnail_stylesheet) elif key_pressed == 44: if self.mode == 'thumbnails': self.train_sound.play() for _ in range(0, CONST_THUMBNAIL_COUNT): self.prev_image() self.thumbnail_labels[self.model.get_current_index() % 5].setStyleSheet('') self.model.set_current_index(self.model.get_leftmost_index()) self.thumbnail_labels[self.model.get_current_index() % 5].setStyleSheet(self.selected_thumbnail_stylesheet) def load_thumbnails(self): # load images into pixmap array # for _ in self.model.image_files: # self.thumbnail_pixmaps.append(QPixmap(self.model.get_current_filename()).scaled(CONST_THUMBNAIL_SIZE, CONST_THUMBNAIL_SIZE, Qt.KeepAspectRatio)) # self.model.next_filename() # create labels for index in range(0, CONST_THUMBNAIL_COUNT): # init thumbnail labels with corresponding pixmap self.thumbnail_labels.append(QLabel(self)) if(index < len(self.model.nodes)): self.thumbnail_labels[index].setPixmap(self.model.nodes[index].get_image().scaled(CONST_THUMBNAIL_SIZE, CONST_THUMBNAIL_SIZE, Qt.KeepAspectRatio)) # positioning labels self.thumbnail_labels[index].resize(CONST_THUMBNAIL_SIZE, CONST_THUMBNAIL_SIZE) self.thumbnail_labels[index].setAlignment(Qt.AlignCenter) # TODO: remove magic numbers below self.thumbnail_labels[index].move(self.window_width / (self.window_width / 30) + (index * self.window_width / 5), (self.window_height - CONST_THUMBNAIL_SIZE) / 2) #self.thumbnail_labels[index].move((self.window_width / (self.window_width / 10)) + index * self.window_width / 5, (self.window_height - CONST_THUMBNAIL_SIZE) / 2) def next_image(self): # remove red highlight from current selection self.thumbnail_labels[self.model.get_current_index() % 5].setStyleSheet('') self.model.select_next_node() self.reload_thumbnails('forward') def prev_image(self): # remove red highlight from current self.thumbnail_labels[self.model.get_current_index() % 5].setStyleSheet('') self.model.select_prev_node() self.reload_thumbnails('backward') def reload_thumbnails(self, direction): if (self.model.get_current_index() % 5 == 0): self.model.set_leftmost_index(self.model.get_current_index()) elif (self.model.get_current_index() == self.model.get_leftmost_index() - 1): self.model.set_leftmost_index(self.model.get_leftmost_index() - 5) if direction == 'forward': temp_index = self.model.get_leftmost_index() for label in self.thumbnail_labels: temp_index = self.model.check_index_bounds(temp_index, direction) label.setPixmap(self.model.nodes[temp_index].get_image().scaled(CONST_THUMBNAIL_SIZE, CONST_THUMBNAIL_SIZE, Qt.KeepAspectRatio)) temp_index += 1 self.thumbnail_labels[self.model.get_current_index() % 5].setStyleSheet(self.selected_thumbnail_stylesheet) elif direction == 'backward': temp_index = self.model.get_leftmost_index() + 4 for label in reversed(self.thumbnail_labels): temp_index = self.model.check_index_bounds(temp_index, direction) label.setPixmap(self.model.nodes[temp_index].get_image().scaled(CONST_THUMBNAIL_SIZE, CONST_THUMBNAIL_SIZE, Qt.KeepAspectRatio)) temp_index -= 1 if (self.model.get_current_index() == len(self.model.nodes) - 1): self.thumbnail_labels[4].setStyleSheet(self.selected_thumbnail_stylesheet) else: self.thumbnail_labels[self.model.get_current_index() % 5].setStyleSheet(self.selected_thumbnail_stylesheet) def save_photos(self): self.model.save_nodes() self.save_photos_button.clearFocus() def save_tags(self): self.model.save_tags('tags.txt') self.save_tags_button.clearFocus() def search_flickr(self): search_string = ''.join('%20' if char == ' ' else char for char in self.search_text_field.text()) self.model.search_flickr(search_string, self.search_number_field.text()) self.reload_thumbnails('forward') self.search_text_field.setText('') self.search_button.clearFocus() def show_fullscreen_image(self): self.fullscreen_pixmap = self.model.nodes[self.model.get_current_index()].get_image() self.fullscreen_pixmap = self.fullscreen_pixmap.scaled(self.window_width / 2, self.window_height / 2, Qt.KeepAspectRatio) self.fullscreen_label.setPixmap(self.fullscreen_pixmap) self.fullscreen_label.show() self.show_tags() def show_fullscreen_view(self): self.add_tag_button.show() self.save_tags_button.show() self.tag_field.show() self.show_tags() for label in self.thumbnail_labels: label.hide() self.search_button.hide() self.search_text_field.hide() self.search_number_field.hide() self.test_button.hide() self.save_photos_button.hide() self.exit_button.hide() self.delete_button.hide() def show_tags(self): tags = self.model.get_tags() for label in self.tag_labels: label.setText('') for label, tag in zip(self.tag_labels, tags): label.hide() label.setText(str(tag)) label.show() def show_thumbnail_controls(self): self.search_text_field.show() self.search_number_field.show() def show_thumbnails_view(self): for label in self.thumbnail_labels: label.show() self.add_tag_button.hide() self.save_tags_button.hide() self.tag_field.hide() for label in self.tag_labels: label.hide() self.search_button.show() self.search_text_field.show() self.search_number_field.show() self.search_button.show() self.search_text_field.show() self.search_number_field.show() self.test_button.show() self.save_photos_button.show() self.exit_button.show() self.delete_button.show() def test(self): self.test_button.clearFocus()
class MainWindow(QWidget): processData = pyqtSignal(list, list, int, tuple) def __init__(self): super(self.__class__, self).__init__(None) self.reader = SerialPortReader() self.experimentData = ExperimentData(self) self.receivingStarted = False self.recordingStarted = False self.recordingTime = 0 self.initGUI() sys.stdout = OutLog(self.console, sys.stdout) sys.stderr = OutLog(self.console, sys.stderr, QColor(255, 0, 0)) self.initSound() self.createWorkerThread() self.reader.timeUpdate.connect(self.onTimeUpdate) self.reader.dataReady.connect(self.onDataReady) self.reader.locatorPacket.connect(self.onLocatorPacket) self.loadSettings() #self.writer = SerialPortWriter() @QtCore.pyqtSlot(list, list, list) def onDataReady(self, a_ch0, a_ch1, T_meas): if self.recordingStarted: if self.experimentLength < 5: self.experimentData.appendData(a_ch0, a_ch1, T_meas) else: self.experimentData.appentDataToTxt(a_ch0, a_ch1, T_meas, self.txtFileName) self.processData.emit(a_ch0, a_ch1, self.intervalLength, self.settingsWidget.getValues()) @QtCore.pyqtSlot(float, float) def onLocatorPacket(self, val1, val2): self.locatorPlotWidget.appendPoint(0, val1) self.locatorPlotWidget.appendPoint(1, val2) @QtCore.pyqtSlot(int) def onTimeUpdate(self, time): if not self.recordingStarted: return self.recordingTime += time remainingTime = self.experimentLength * 60 * 1000 - self.recordingTime qtTime = QTime.fromMSecsSinceStartOfDay(remainingTime) self.timeLabel.setText(qtTime.toString()) if remainingTime == 0: if self.soundCheckBox.isChecked(): self.sound.play() self.startStopButton.toggle() def createWorkerThread(self): self.rascanWorker = RascanWorker() self.workerThread = QThread() self.rascanWorker.moveToThread(self.workerThread) self.workerThread.start() self.processData.connect(self.rascanWorker.doWork) self.rascanWorker.dataProcessed.connect(self.onRascanDataProcessed) def initSound(self): self.sound = QSoundEffect() base_url = QUrl.fromLocalFile(QDir(".").canonicalPath() + "/") file_path = base_url.resolved(QUrl("bzzz.wav")) self.sound.setSource(file_path) self.sound.setVolume(1) def initGUI(self): self.setWindowTitle('БиоРАСКАН-24') self.setWindowIcon(QIcon('Рисунок1.png')) self.settingsWidget = SettingsWidget(self) mainLayout = QGridLayout() self.setLayout(mainLayout) leftLayout = QVBoxLayout() leftLayout.setSpacing(20) mainLayout.addLayout(leftLayout, 1, 1, Qt.AlignCenter) tabWidget = QTabWidget() mainLayout.addWidget(tabWidget, 1, 3, Qt.AlignCenter) mainLayout.setRowStretch(0, 2) # empty space above ui mainLayout.setRowStretch(1, 1) # ui mainLayout.setRowStretch(2, 2) # console mainLayout.setRowStretch(3, 2) # empty space below ui mainLayout.setColumnStretch(0, 2) # empty space to the right from ui mainLayout.setColumnStretch( 2, 1) # empty space between left layout and right layout mainLayout.setColumnStretch(4, 2) # empty space to the left from ui # console output self.console = ConsoleWidget(self) self.console.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.console.setReadOnly(True) mainLayout.addWidget(self.console, 2, 1, 1, 3) # settings layout settingsLayout = QGridLayout() leftLayout.addLayout(settingsLayout) lengthSettingsText = QLabel('Длительность эксперимента') self.lengthSettingsEdit = QLineEdit('1') self.lengthSettingsEdit.setValidator(MyIntValidator(1, 30)) lmin = QLabel('мин') lmin.setObjectName("secondary") settingsLayout.addWidget(lengthSettingsText, 0, 0) settingsLayout.addWidget(self.lengthSettingsEdit, 0, 2) settingsLayout.addWidget(lmin, 0, 3) settingsLayout.setColumnMinimumWidth( 1, 30) # middle column to add some sapace intervalLayoutText = QLabel('Интервал расчета') self.intervalLayoutEdit = QLineEdit('10') self.intervalLayoutEdit.setValidator(MyIntValidator(10, 300)) imin = QLabel('сек') imin.setObjectName("secondary") settingsLayout.addWidget(intervalLayoutText, 1, 0) settingsLayout.addWidget(self.intervalLayoutEdit, 1, 2) settingsLayout.addWidget(imin, 1, 3) self.comBox = QComboBox(self) COMLayoutText = QLabel('Выбор COM-порта') COM_list = serial_ports() self.comBox.addItems(COM_list) settingsLayout.addWidget(COMLayoutText, 2, 0) settingsLayout.addWidget(self.comBox, 2, 2) # back to main layout settingsLayout.setRowMinimumHeight(3, 20) # add some space vertically self.soundCheckBox = QCheckBox('Звуковое оповещение') settingsLayout.addWidget(self.soundCheckBox, 4, 0) self.settingsButton = QPushButton('ДОПОЛНИТЕЛЬНО') self.settingsButton.setObjectName('secondary') settingsLayout.addWidget(self.settingsButton, 4, 2, 1, 2, Qt.AlignLeft) self.settingsButton.clicked.connect(lambda: self.settingsWidget.open()) self.timeLabel = QLabel('00:00:00') self.timeLabel.setObjectName('timeLabel') leftLayout.addWidget(self.timeLabel) leftLayout.setAlignment(self.timeLabel, Qt.AlignHCenter) infoLayout = QHBoxLayout() infoLayout.setSpacing(20) leftLayout.addLayout(infoLayout) infoLayout.addStretch() chss = QLabel('ЧСС') chss.setObjectName('leftBar') infoLayout.addWidget(chss) self.heartRateText = QLabel('0') self.heartRateText.setObjectName('primary') self.heartRateText.setAlignment(Qt.AlignRight) infoLayout.addWidget(self.heartRateText) udm = QLabel('уд/мин') udm.setObjectName('secondary') infoLayout.addWidget(udm) infoLayout.addSpacing(40) chd = QLabel('ЧД') chd.setObjectName('leftBar') infoLayout.addWidget(chd) self.breathRateText = QLabel('0') self.breathRateText.setObjectName('primary') self.breathRateText.setAlignment(Qt.AlignRight) infoLayout.addWidget(self.breathRateText) vdm = QLabel('вдох/мин') vdm.setObjectName('secondary') infoLayout.addWidget(vdm) infoLayout.addStretch() buttonLayout = QHBoxLayout() leftLayout.addLayout(buttonLayout) self.startStopButton = QPushButton('ЗАПУСК') self.startStopButton.setCheckable(True) self.saveButton = QPushButton('ЗАПИСЬ') self.saveButton.setCheckable(True) buttonLayout.addWidget(self.startStopButton) buttonLayout.addSpacing(20) buttonLayout.addWidget(self.saveButton) self.saveButton.toggled.connect(self.onSaveButtonClicked) self.startStopButton.toggled.connect(self.onButtonClick) # firs tab tabOneWidget = QWidget() tabOneLayout = QVBoxLayout() tabOneWidget.setLayout(tabOneLayout) tabWidget.addTab(tabOneWidget, "Сигнал локатора") self.locatorPlotWidget = PlotWidget(300, 20, 2) tabOneLayout.addWidget(self.locatorPlotWidget) tabOneButtonsLayout = QGridLayout() tabOneLayout.addLayout(tabOneButtonsLayout) locatorLeftCheckBox = QCheckBox('1-ая квадратура') tabOneButtonsLayout.addWidget(locatorLeftCheckBox, 0, 3) locatorRightCheckBox = QCheckBox('2-ая квадратура') tabOneButtonsLayout.addWidget(locatorRightCheckBox, 1, 3) locatorLeftCheckBox.setChecked(True) locatorRightCheckBox.setChecked(True) locatorLeftCheckBox.stateChanged.connect( lambda state: self.locatorPlotWidget.hideCurve( 0, False if state == 1 or state == 2 else True)) locatorRightCheckBox.stateChanged.connect( lambda state: self.locatorPlotWidget.hideCurve( 1, False if state == 1 or state == 2 else True)) # second tab tabTwoWidget = QWidget() tabTwoLayout = QVBoxLayout() tabTwoLayout.setContentsMargins(0, 0, 0, 0) tabTwoWidget.setLayout(tabTwoLayout) tabWidget.addTab(tabTwoWidget, "ЧСС/ЧД") self.heartRatePlotWidget = PlotWidget(30, 10000, 1) self.breathRatePlotWidget = PlotWidget(30, 10000, 1) tabTwoLayout.addWidget(self.heartRatePlotWidget) tabTwoLayout.addWidget(self.breathRatePlotWidget) # third tab tabThreeWidget = QWidget() tabThreeLayout = QVBoxLayout() tabThreeWidget.setLayout(tabThreeLayout) tabWidget.addTab(tabThreeWidget, "Отфильтрованный сигнал") self.heartFilteredPlotWidget = PlotWidget(300, 100, 2) self.breathFilteredPlotWidget = PlotWidget(300, 100, 2) tabThreeLayout.addWidget(self.heartFilteredPlotWidget) tabThreeLayout.addWidget(self.breathFilteredPlotWidget) @QtCore.pyqtSlot(bool) def onButtonClick(self, toggled): if toggled: #self.writer.startSend() portName = self.comBox.currentText() if portName == '': print("Устройство не подключено") self.uncheck(self.startStopButton) return print("Подождите, программа запускается...") self.intervalLength = int(self.intervalLayoutEdit.text()) try: self.reader.startListen(self.intervalLength, portName) except IOError: print("Не удается открыть указанный COM порт") self.uncheck(self.startStopButton) return self.locatorPlotWidget.reset() self.startStopButton.setText('СТОП') self.receivingStarted = True else: #self.writer.stopSend() self.startStopButton.setText('ЗАПУСК') self.reader.stopListen() self.saveButton.setChecked(False) self.receivingStarted = False def uncheck(self, button): button.blockSignals(True) button.setChecked(False) button.blockSignals(False) @QtCore.pyqtSlot(bool) def onSaveButtonClicked(self, toggled): if toggled: if not self.receivingStarted: print("Прием не начат") self.uncheck(self.saveButton) return self.intervalLength = int(self.intervalLayoutEdit.text()) self.experimentLength = int(self.lengthSettingsEdit.text()) if self.experimentLength < self.intervalLength / 60: print("Интервал расчета больше длительности эксперимента") self.uncheck(self.saveButton) return if self.experimentLength >= 5: txtFileName, ok = QInputDialog.getText( self, 'Ввод имени файла', 'Длительность эксперимента более 5 минут\n\n' 'Будет произведено сохранение в текстовый файл\n\n' 'Введите имя .txt файла без расширения') if ok: self.txtFileName = str(txtFileName) try: open(txtFileName + '.txt', 'w').close() except OSError: print("Неправильное имя файла") self.uncheck(self.saveButton) return else: self.uncheck(self.saveButton) return self.recordingTime = 0 self.experimentData.reset() self.saveButton.setText('СОХРАНИТЬ') self.recordingStarted = True self.heartRatePlotWidget.reset() self.breathRatePlotWidget.reset() self.breathFilteredPlotWidget.reset() self.heartFilteredPlotWidget.reset() self.heartRatePlotWidget.setDelta(self.intervalLength * 1000) self.breathRatePlotWidget.setDelta(self.intervalLength * 1000) self.heartRatePlotWidget.appendPoint(0, 0) self.breathRatePlotWidget.appendPoint(0, 0) self.reader.startIntervalSending(self.intervalLength) print("Запись данных начата") qtTime = QTime.fromMSecsSinceStartOfDay(self.experimentLength * 60 * 1000) self.timeLabel.setText(qtTime.toString()) else: self.recordingStarted = False print("Запись данных окончена") self.experimentData.saveToFile() self.saveButton.setText('ЗАПИСЬ') self.timeLabel.setText('00:00:00') @QtCore.pyqtSlot(float, float, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray) def onRascanDataProcessed(self, chss, chd, sig_hf1, sig_hf2, peaks_hf1, peaks_hf2, sig_bf1, sig_bf2, peaks_bf1, peaks_bf2): self.heartRateText.setText(str(int(chss))) self.breathRateText.setText(str(int(chd))) self.heartRatePlotWidget.appendPoint(0, chss) self.breathRatePlotWidget.appendPoint(0, chd) self.heartFilteredPlotWidget.appendData(0, sig_hf1.tolist(), peaks_hf1.tolist()) self.heartFilteredPlotWidget.appendData(1, sig_hf2.tolist(), peaks_hf2.tolist()) self.breathFilteredPlotWidget.appendData(0, sig_bf1.tolist(), peaks_bf1.tolist()) self.breathFilteredPlotWidget.appendData(1, sig_bf2.tolist(), peaks_bf2.tolist()) def closeEvent(self, event): self.experimentData.saveIfNeeded() self.saveSettings() event.accept() def saveSettings(self): settings = QSettings("BioRascan-24.ini", QSettings.IniFormat) if (self.isMaximized() == False): settings.setValue("geometry", self.geometry()) settings.setValue("maximized", self.isMaximized()) settings.setValue("length", self.lengthSettingsEdit.text()) settings.setValue("interval", self.intervalLayoutEdit.text()) settings.setValue("sound", self.soundCheckBox.isChecked()) settings.setValue("port", self.comBox.currentText()) lhf, hhf, lbf, hbf = self.settingsWidget.getValues() settings.setValue("lhf", lhf) settings.setValue("hhf", hhf) settings.setValue("lbf", lbf) settings.setValue("hbf", hbf) def loadSettings(self): settings = QSettings("BioRascan-24.ini", QSettings.IniFormat) screenRect = QApplication.desktop().screenGeometry() defaultWindowRect = QRect(screenRect.width() / 8, screenRect.height() * 1.5 / 8, screenRect.width() * 6 / 8, screenRect.height() * 5 / 8) self.setGeometry(settings.value("geometry", defaultWindowRect)) if (settings.value("maximized", False) == "true"): self.setWindowState(self.windowState() ^ Qt.WindowMaximized) self.lengthSettingsEdit.setText(settings.value("length", "1")) self.intervalLayoutEdit.setText(settings.value("interval", "10")) if (settings.value("sound", False) == "true"): self.soundCheckBox.setChecked(True) portName = settings.value("port", "") itemIndex = self.comBox.findText(portName) if (itemIndex != -1): self.comBox.setCurrentIndex(itemIndex) lhf = settings.value("lhf", 0.7) hhf = settings.value("hhf", 2.5) lbf = settings.value("lbf", 0.01) hbf = settings.value("hbf", 0.4) self.settingsWidget.setValues(float(lhf), float(hhf), float(lbf), float(hbf))
class MainWindow(QtWidgets.QWidget, main_window.Ui_mainWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setupUi(self) self.ICON = QIcon(str(ICON_PATH)) self.setWindowIcon(self.ICON) # BUTTONS self.buttonStart.clicked.connect(self.on_clicked_start) self.buttonSettings.clicked.connect(self.on_clicked_settings) self.buttonStatistics.clicked.connect(self.on_clicked_statistics) self.buttonExit.clicked.connect(QtWidgets.QApplication.instance().quit) self.comboBoxSelectMode.currentIndexChanged.connect( self.on_change_mode) # HIGHSCORES HANDLER self.highscore = highscores.Highscores() self.update_highscores() # DATA AND SETTINGS if DATA_FILE.is_file(): self.load_data_from_file() else: self.data = settings.DEFAULT_DATA self.comboBoxSelectMode.setCurrentIndex( self.data.get("selected_mode", 0)) # SOUND self.set_key_sound(self.get_setting("sound_filename")) # FONT self.inconsolata_bold = self.load_custom_font(str(FONT_PATH)) # Stylesheet is set in the main program after instantiation # Button methods def on_clicked_start(self) -> None: self.make_mode_window(str(self.comboBoxSelectMode.currentText())) self.show_window(self.mode_window, self.isMaximized()) self.mode_window.setStyleSheet(self.get_setting("stylesheet")) self.mode_window.set_colours(self.get_setting("rich_text_colours")) self.hide() def on_clicked_main_menu(self, window: QtWidgets.QWidget) -> None: self.update_highscores() self.show_window(self, window.isMaximized()) window.close() del window def on_clicked_settings(self) -> None: self.make_settings_window() self.show_window(self.settings_window, self.isMaximized()) self.settings_window.setStyleSheet(self.get_setting("stylesheet")) self.hide() def on_clicked_apply(self) -> None: """Executed when apply button in settings window is clicked.""" self.data["settings"] = self.settings_window.get_settings() # Key sound self.set_key_sound(self.get_setting("sound_filename")) # Stylesheet self.settings_window.setStyleSheet(self.get_setting("stylesheet")) self.setStyleSheet(self.get_setting("stylesheet")) # Save self.save_data_to_file() def on_clicked_statistics(self) -> None: self.make_stats_window() self.show_window(self.stats_window, self.isMaximized()) self.stats_window.setStyleSheet(self.get_setting("stylesheet")) self.hide() def on_clicked_reset_daily(self) -> None: """ To be executed when 'Reset today's highscore' is pressed in the stats window. """ self.highscore.delete_daily_highscore() self.update_highscores() self.update_stats_highscores() def on_clicked_reset_all_time(self) -> None: """ To be executed when 'Reset all-time highscore' is pressed in the stats window. """ self.highscore.delete_all_time_highscore() self.update_highscores() self.update_stats_highscores() def on_clicked_reset_all(self) -> None: """ To be executed when 'Reset all highscores' is pressed in the stats window. """ self.highscore.delete_all_highscores() self.update_highscores() self.update_stats_highscores() def on_change_mode(self): """ Saves the selected mode to self.data and pickles self.data so the selection is remembered. """ self.data["selected_mode"] = self.comboBoxSelectMode.currentIndex() self.save_data_to_file() # Helper Methods def get_setting(self, setting: str): """ Convenience method for getting a specific setting from self.data, or a default value. """ return self.data["settings"].get( setting, settings.DEFAULT_SETTINGS.get(setting)) def load_custom_font(self, font: str) -> int: """Adds custom font to QFontDatabase, and returns its corresponding font id.""" return QFontDatabase.addApplicationFont(font) def show_window(self, window: QtWidgets.QWidget, fullscreen: bool) -> None: """ Used to show windows, with the option to have them maximised provided. """ window.show() if fullscreen: window.setWindowState(QtCore.Qt.WindowMaximized) def make_mode_window(self, mode: str) -> None: self.mode_window = type_test.TypingWindow(self.highscore) self.mode_window.set_mode(mode) self.mode_window.setWindowIcon(self.ICON) self.mode_window.buttonMainMenu.clicked.connect( lambda: self.on_clicked_main_menu(self.mode_window)) # Sets key sound if enabled if self.get_setting("play_sound"): self.mode_window.set_key_sound(self.key_sound) def make_settings_window(self) -> None: self.settings_window = settings.SettingsWindow() self.settings_window.setWindowIcon(self.ICON) self.settings_window.buttonMainMenu.clicked.connect( lambda: self.on_clicked_main_menu(self.settings_window)) self.settings_window.buttonApply.clicked.connect(self.on_clicked_apply) # Keystroke sound toggle if self.get_setting("play_sound"): self.settings_window.toggleKeystrokeSound.setChecked(True) # Dark mode toggle if self.get_setting("dark_mode"): self.settings_window.toggleDarkMode.setChecked(True) self.set_settings_sounds_options() self.set_selected_sound_option(self.get_setting("sound_filename")) def make_stats_window(self) -> None: self.stats_window = statistics.StatsWindow() self.stats_window.setWindowIcon(self.ICON) # Update labels self.update_stats_highscores() self.update_stats_days_ago() # Set up graph self.stats_window.set_up_graph(self.highscore.get_stats_dailies(), self.get_setting("graph_colours")) # Connect buttons self.stats_window.buttonMainMenu.clicked.connect( lambda: self.on_clicked_main_menu(self.stats_window)) self.stats_window.buttonResetDaily.clicked.connect( self.on_clicked_reset_daily) self.stats_window.buttonResetAllTime.clicked.connect( self.on_clicked_reset_all_time) self.stats_window.buttonResetAll.clicked.connect( self.on_clicked_reset_all) def update_highscores(self) -> None: self.today_wpm, self.all_time_wpm = self.highscore.get_wpm() def save_data_to_file(self) -> None: """Pickles self.data into a file in the data folder.""" with open(DATA_FILE, "wb") as data_pickle: pickle.dump(self.data, data_pickle) def load_data_from_file(self) -> None: """Sets self.data to the values saved on the data.pkl file.""" with open(DATA_FILE, "rb") as data_pickle: self.data = pickle.load(data_pickle) def get_sounds_list(self) -> list: """Returns a list of the sound files present in the sounds folder.""" return os.listdir(SOUND_FOLDER) def set_settings_sounds_options(self) -> None: """ Sets up options for the dropdown menu to select keystroke sounds in the settings menu. """ for sound_file in self.get_sounds_list(): # Add sound file name to dropdown menu self.settings_window.comboSelectSound.addItem(sound_file) def find_sound_file_index(self, sound_file: str) -> int: """ Returns the index of the given file name within the settings window comboSelectSound object. """ return self.settings_window.comboSelectSound.findText( sound_file, QtCore.Qt.MatchFixedString) def set_selected_sound_option(self, sound_file: str) -> None: """ Sets the selected option for sound file from the settings window's comboSelectSound object to the given sound file name. """ index: int = self.find_sound_file_index(sound_file) if index >= 0: self.settings_window.comboSelectSound.setCurrentIndex(index) def set_key_sound(self, sound_file: str) -> None: """ Sets the given sound file to a QSoundEffect object which will be played on each keystroke in the mode window. """ self.key_sound_path = os.path.join(SOUND_FOLDER, sound_file) self.key_sound_url = QtCore.QUrl.fromLocalFile(self.key_sound_path) self.key_sound = QSoundEffect() self.key_sound.setSource(self.key_sound_url) self.key_sound.setVolume(0.5) self.key_sound.setLoopCount(1) def update_stats_highscores(self) -> None: """Updates highscores displayed in the stats window.""" self.stats_window.labelTodayScore.setText(f"{self.today_wpm} WPM") self.stats_window.labelAllTimeScore.setText(f"{self.all_time_wpm} WPM") def update_stats_days_ago(self) -> None: """ Updates the labelDaysAgo element in the stats window with the number of days since the all-time highscore was set. """ self.stats_window.update_days_ago(self.highscore.days_since_set())