Exemple #1
0
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
Exemple #2
0
    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)
Exemple #3
0
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)
Exemple #4
0
	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)
Exemple #5
0
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()
Exemple #6
0
    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
Exemple #7
0
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)
Exemple #9
0
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()
Exemple #10
0
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))
Exemple #11
0
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())