דילוג לתוכן
  • חוקי הפורום
  • פופולרי
  • לא נפתר
  • משתמשים
  • חיפוש גוגל בפורום
  • צור קשר
עיצובים
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • ברירת מחדל (ללא עיצוב (ברירת מחדל))
  • ללא עיצוב (ברירת מחדל)
כיווץ
מתמחים טופ
  1. דף הבית
  2. קטגוריות בהרצה
  3. תכנות
  4. הצעות לפיתוח
  5. תוכנות בביצוע
  6. שיתוף | נגן הקלדה עם מספר פיצ'רים נחמדים...

שיתוף | נגן הקלדה עם מספר פיצ'רים נחמדים...

מתוזמן נעוץ נעול הועבר תוכנות בביצוע
2 פוסטים 1 כותבים 142 צפיות 1 עוקבים
  • מהישן לחדש
  • מהחדש לישן
  • הכי הרבה הצבעות
תגובה
  • תגובה כנושא
התחברו כדי לפרסם תגובה
נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
  • 25802 מנותק
    25802 מנותק
    2580
    מדריכים
    כתב נערך לאחרונה על ידי 2580
    #1

    ייתכן ויש כבר וגם ככה זה לא דבר כזה נצרך אבל לצורך מסויים יצרתי את זה ביחד עם AI לא השקעתי בזה הרבה... בכל אופן זה התוצאה...

    הנגן מיועד לאנשים שמעוניינים להקליד שיעורים וכדו' וקשה להם להמשיך לעקוב אחרי ההמשך תוך כדי הקלדה...

    קישור למג'יקוד (הקובץ חתום) עד שמישהו ( @kasnik אולי?) יעלה את זה לדרייב...

    https://send.magicode.me/send-file/file/*****24981edbf8f81b8546e034a045dc6d6332fb1e76/view

    קוד המקור... (אל תכעסו אל הארכיטקטורה...)

    התקנות תלויות למי שמשתמש בקוד מקור

    pip install PyQt6 pynput mutagen
    

    נגן_הקלדה.py

    או הקוד עצמו בספויילר

    import sys
    import os
    from pynput import keyboard
    from PyQt6.QtCore import QThread, pyqtSignal, QObject
    from PyQt6.QtWidgets import (
        QApplication, QMainWindow, QWidget, QPushButton, QSlider, QLabel,
        QVBoxLayout, QHBoxLayout, QFileDialog, QStyle,
        QDialog, QFormLayout, QCheckBox, QDoubleSpinBox, QDialogButtonBox
    )
    from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
    from PyQt6.QtCore import QUrl, Qt, QTimer, QSize, QSettings
    from PyQt6.QtGui import QPixmap, QFont
    from mutagen.mp3 import MP3
    from mutagen.flac import FLAC
    
    # ערכת עיצוב כהה לאפליקציה
    DARK_STYLESHEET = """
    QWidget {
        background-color: #2b2b2b;
        color: #f0f0f0;
        font-family: Arial, sans-serif;
    }
    QMainWindow {
        background-color: #2b2b2b;
    }
    QPushButton {
        background-color: #555;
        border: 1px solid #666;
        padding: 8px;
        border-radius: 4px;
    }
    QPushButton:hover {
        background-color: #666;
    }
    QPushButton:pressed {
        background-color: #444;
    }
    QPushButton#ControlButton {
        border: none;
        font-size: 18px;
        font-weight: bold;
        background-color: #3c3c3c;
        border-radius: 22px;
    }
    QPushButton#ControlButton:hover {
        background-color: #505050;
    }
    QPushButton#ControlButton:pressed {
        background-color: #2a2a2a;
    }
    QPushButton#ControlButton[active="true"] {
        background-color: #5a9bcf;
        color: white;
    }
    QLabel#AlbumArtLabel {
        border: 2px solid #444;
        border-radius: 5px;
        background-color: #3c3c3c;
    }
    QLabel#TrackInfoLabel {
        font-size: 16px;
        font-weight: bold;
        padding-bottom: 5px;
    }
    QLabel#ArtistAlbumLabel {
        font-size: 12px;
        color: #ccc;
        padding-bottom: 10px;
    }
    QSlider::groove:horizontal {
        border: 1px solid #444;
        height: 8px;
        background: #3c3c3c;
        margin: 2px 0;
        border-radius: 4px;
    }
    QSlider::sub-page:horizontal {
        background: #5a9bcf;
        border: 1px solid #444;
        height: 8px;
        border-radius: 4px;
    }
    QSlider::handle:horizontal {
        background: white;
        border: 1px solid #aaa;
        width: 16px;
        margin: -5px 0;
        border-radius: 8px;
    }
    QDialog {
        background-color: #3c3c3c;
    }
    QCheckBox, QDoubleSpinBox {
        padding: 5px;
    }
    """
    
    class SettingsDialog(QDialog):
        """דיאלוג להגדרות האפליקציה."""
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setWindowTitle("הגדרות")
            self.setModal(True)
            self.settings = QSettings("MyMusicPlayer", "Settings")
    
            self.typing_pause_checkbox = QCheckBox("הפעל השהיית מוזיקה בעת הקלדה (גלובלי)")
            self.delay_spinbox = QDoubleSpinBox()
            self.delay_spinbox.setRange(0.5, 10.0)
            self.delay_spinbox.setSingleStep(0.1)
            self.delay_spinbox.setSuffix(" שניות")
    
            button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
            button_box.accepted.connect(self.accept)
            button_box.rejected.connect(self.reject)
    
            layout = QFormLayout(self)
            layout.addRow(self.typing_pause_checkbox)
            layout.addRow("זמן השהיה לחזרה:", self.delay_spinbox)
            layout.addWidget(button_box)
            self.load_settings()
    
        def load_settings(self):
            typing_pause_enabled = self.settings.value("typingPauseEnabled", True, type=bool)
            delay_time = self.settings.value("delayTime", 1.5, type=float)
            self.typing_pause_checkbox.setChecked(typing_pause_enabled)
            self.delay_spinbox.setValue(delay_time)
    
        def save_settings(self):
            self.settings.setValue("typingPauseEnabled", self.typing_pause_checkbox.isChecked())
            self.settings.setValue("delayTime", self.delay_spinbox.value())
    
        def accept(self):
            self.save_settings()
            super().accept()
    
    # מאזין גלובלי למקלדת הפועל ב-thread נפרד
    class GlobalKeyListener(QObject):
        """מאזין גלובלי למקלדת שפולט אות בעת הקשה."""
        keyPressed = pyqtSignal() # אות הנפלט בעת הקשה
    
        def __init__(self):
            super().__init__()
            self.listener = keyboard.Listener(on_press=self.on_press)
    
        def on_press(self, key):
            self.keyPressed.emit()
    
        def start_listening(self):
            # מתחיל את לולאת ההאזנה (פעולה חוסמת)
            self.listener.start()
            self.listener.join()
    
        def stop_listening(self):
            self.listener.stop()
    
    class MusicPlayer(QMainWindow):
        def __init__(self):
            super().__init__()
    
            self.normal_size = QSize(520, 600)
            self.mini_size = QSize(460, 160)
            
            self.setWindowTitle("נגן הקלדה")
            self.setGeometry(100, 100, self.normal_size.width(), self.normal_size.height())
            self.setWindowIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay))
    
            self.settings = QSettings("MyMusicPlayer", "Settings")
            self.player = QMediaPlayer()
            self.audio_output = QAudioOutput()
            self.player.setAudioOutput(self.audio_output)
            
            self.current_file_path = None
            self.is_always_on_top = False
            self.was_playing_before_typing = False
    
            self.typing_timer = QTimer(self)
            self.typing_timer.setSingleShot(True)
    
            self.load_player_settings()
            self.update_typing_timer_interval()
    
            self.init_ui()
            self.connect_signals()
            
            self.setup_global_key_listener()
            
            self.setStyleSheet(DARK_STYLESHEET)
        
        def setup_global_key_listener(self):
            """מגדיר ומפעיל את המאזין הגלובלי למקלדת ב-thread נפרד."""
            self.key_listener_thread = QThread()
            self.key_listener = GlobalKeyListener()
            
            self.key_listener.moveToThread(self.key_listener_thread)
            self.key_listener.keyPressed.connect(self.handle_global_key_press)
            self.key_listener_thread.started.connect(self.key_listener.start_listening)
            
            self.key_listener_thread.start()
    
        def handle_global_key_press(self):
            """מטפל באות הקשה גלובלי מהמאזין."""
            if self.typing_pause_enabled:
                if self.player.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
                    self.was_playing_before_typing = True
                    self.player.pause()
                self.typing_timer.start()
    
        def closeEvent(self, event):
            """מופעלת בעת סגירת החלון כדי לעצור את ה-thread של המאזין."""
            self.key_listener.stop_listening()
            self.key_listener_thread.quit()
            self.key_listener_thread.wait()
            event.accept()
            
        def load_player_settings(self):
            self.typing_pause_enabled = self.settings.value("typingPauseEnabled", True, type=bool)
            self.delay_time = self.settings.value("delayTime", 1.5, type=float)
    
        def update_typing_timer_interval(self):
            self.typing_timer.setInterval(int(self.delay_time * 1000))
    
        def init_ui(self):
            central_widget = QWidget()
            self.setCentralWidget(central_widget)
            main_layout = QVBoxLayout(central_widget)
    
            # ----- שורה עליונה -----
            top_bar_layout = QHBoxLayout()
            self.open_file_button = QPushButton("פתח קובץ")
            self.open_file_button.setToolTip("פתח קובץ מוזיקה חדש")
            
            self.pin_button = QPushButton("📌")
            self.pin_button.setToolTip("הצמד למעלה (מצב מיני)")
            self.pin_button.setFixedSize(45, 45)
            self.pin_button.setObjectName("ControlButton")
            self.pin_button.setProperty("active", self.is_always_on_top)
            font_pin = self.pin_button.font(); font_pin.setPointSize(20); self.pin_button.setFont(font_pin)
            
            self.settings_button = QPushButton("⚙️")
            self.settings_button.setToolTip("הגדרות")
            self.settings_button.setFixedSize(45, 45)
            self.settings_button.setObjectName("ControlButton")
            font_settings = self.settings_button.font(); font_settings.setPointSize(22); self.settings_button.setFont(font_settings)
    
            self.mute_button = QPushButton()
            self.mute_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaVolume))
            self.mute_button.setFixedSize(45, 45)
            self.mute_button.setObjectName("ControlButton")
            self.mute_button.setToolTip("השתק / בטל השתקה")
            
            self.volume_slider = QSlider(Qt.Orientation.Horizontal)
            self.volume_slider.setRange(0, 100)
            self.volume_slider.setValue(70)
            self.audio_output.setVolume(0.7)
            self.volume_slider.setToolTip("עוצמת שמע")
    
            top_bar_layout.addWidget(self.open_file_button)
            top_bar_layout.addStretch()
            top_bar_layout.addWidget(self.mute_button)
            top_bar_layout.addWidget(self.volume_slider)
            top_bar_layout.addStretch()
            top_bar_layout.addWidget(self.pin_button)
            top_bar_layout.addWidget(self.settings_button)
            
            # ----- אזור ראשי (תמונת אלבום ומידע) -----
            self.album_art_label = QLabel("Album Art")
            self.album_art_label.setObjectName("AlbumArtLabel")
            self.album_art_label.setScaledContents(True)
            self.album_art_label.setMinimumSize(300, 300)
            self.album_art_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
            
            self.track_info_label = QLabel("לא נטען שיר")
            self.track_info_label.setObjectName("TrackInfoLabel")
            self.track_info_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
            
            self.artist_album_label = QLabel("אמן - אלבום")
            self.artist_album_label.setObjectName("ArtistAlbumLabel")
            self.artist_album_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
    
            # ----- סליידר התקדמות -----
            progress_layout = QHBoxLayout()
            self.current_time_label = QLabel("00:00")
            self.progress_slider = QSlider(Qt.Orientation.Horizontal)
            self.total_time_label = QLabel("00:00")
            self.progress_slider.setToolTip("התקדמות השיר")
            progress_layout.addWidget(self.current_time_label)
            progress_layout.addWidget(self.progress_slider)
            progress_layout.addWidget(self.total_time_label)
    
            # ----- כפתורי שליטה -----
            controls_layout = QHBoxLayout()
            button_size = QSize(45, 45)
            
            self.seek_back_20_button = QPushButton("⏮️")
            self.seek_back_10_button = QPushButton("⏪")
            self.seek_back_5_button = QPushButton("◀️")
            self.stop_button = QPushButton()
            self.play_pause_button = QPushButton()
            self.seek_fwd_5_button = QPushButton("▶️")
            self.seek_fwd_10_button = QPushButton("⏩")
            self.seek_fwd_20_button = QPushButton("⏭️")
            
            self.stop_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaStop))
            self.play_pause_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay))
            self.play_pause_button.setIconSize(QSize(24, 24))
            self.stop_button.setIconSize(QSize(20, 20))
            
            self.control_buttons = [
                (self.seek_back_20_button, "הרץ 20 שניות אחורה"),
                (self.seek_back_10_button, "הרץ 10 שניות אחורה"),
                (self.seek_back_5_button, "הרץ 5 שניות אחורה"),
                (self.stop_button, "עצור נגינה"),
                (self.play_pause_button, "נגן / השהה"),
                (self.seek_fwd_5_button, "הרץ 5 שניות קדימה"),
                (self.seek_fwd_10_button, "הרץ 10 שניות קדימה"),
                (self.seek_fwd_20_button, "הרץ 20 שניות קדימה")
            ]
            
            controls_layout.addStretch()
            for button, tooltip in self.control_buttons:
                button.setFixedSize(button_size)
                button.setObjectName("ControlButton")
                button.setToolTip(tooltip)
                controls_layout.addWidget(button)
            controls_layout.addStretch()
            
            # ----- הרכבת הממשק -----
            main_layout.addLayout(top_bar_layout)
            main_layout.addStretch(1)
            main_layout.addWidget(self.album_art_label)
            main_layout.addWidget(self.track_info_label)
            main_layout.addWidget(self.artist_album_label)
            main_layout.addLayout(progress_layout)
            main_layout.addLayout(controls_layout)
            main_layout.addStretch(1)
    
        def connect_signals(self):
            self.open_file_button.clicked.connect(self.open_file)
            self.play_pause_button.clicked.connect(self.play_pause)
            self.stop_button.clicked.connect(self.stop_player)
            self.mute_button.clicked.connect(self.toggle_mute)
            self.settings_button.clicked.connect(self.open_settings_dialog)
            self.pin_button.clicked.connect(self.toggle_always_on_top)
    
            seek_map = {
                self.seek_back_20_button: -20, self.seek_back_10_button: -10, self.seek_back_5_button: -5,
                self.seek_fwd_5_button: 5, self.seek_fwd_10_button: 10, self.seek_fwd_20_button: 20
            }
            for button, seconds in seek_map.items():
                button.clicked.connect(lambda sec=seconds: self.seek_by_seconds(sec))
    
            self.player.positionChanged.connect(self.update_position)
            self.player.durationChanged.connect(self.update_duration)
            self.player.playbackStateChanged.connect(self.update_play_pause_icon)
            self.player.mediaStatusChanged.connect(self.handle_media_status)
            
            self.progress_slider.sliderMoved.connect(self.set_position)
            self.volume_slider.valueChanged.connect(self.set_volume)
            self.typing_timer.timeout.connect(self.resume_after_typing)
    
        def open_settings_dialog(self):
            dialog = SettingsDialog(self)
            if dialog.exec():
                self.load_player_settings()
                self.update_typing_timer_interval()
                
        def toggle_always_on_top(self):
            self.is_always_on_top = not self.is_always_on_top
            self.pin_button.setProperty("active", self.is_always_on_top)
            self.style().polish(self.pin_button)
            
            if self.is_always_on_top:
                self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint)
                self.album_art_label.setVisible(False)
                self.track_info_label.setVisible(False)
                self.artist_album_label.setVisible(False)
                self.setFixedSize(self.mini_size)
            else:
                self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowStaysOnTopHint)
                self.album_art_label.setVisible(True)
                self.track_info_label.setVisible(True)
                self.artist_album_label.setVisible(True)
                self.setMinimumSize(0, 0)
                self.setMaximumSize(QSize(16777215, 16777215))
                self.resize(self.normal_size)
            self.show()
    
        def stop_player(self):
            self.player.stop()
            self.reset_ui_to_default()
            self.current_file_path = None
            
        def reset_ui_to_default(self):
            self.track_info_label.setText("לא נטען שיר")
            self.artist_album_label.setText("אמן - אלבום")
            self.album_art_label.setText("Album Art")
            self.album_art_label.setPixmap(QPixmap())
            self.current_time_label.setText("00:00")
            self.total_time_label.setText("00:00")
            self.progress_slider.setValue(0)
        
        def open_file(self):
            file_path, _ = QFileDialog.getOpenFileName(self, "בחר קובץ מוזיקה", "", "קבצי אודיו (*.mp3 *.flac *.wav *.ogg)")
            if file_path:
                self.load_and_play_file(file_path)
    
        def load_and_play_file(self, file_path):
            self.current_file_path = file_path
            metadata = self.get_track_metadata(file_path)
            
            self.track_info_label.setText(metadata['title'])
            self.artist_album_label.setText(f"{metadata['artist']} - {metadata['album']}")
            
            if metadata['pixmap']:
                self.album_art_label.setPixmap(metadata['pixmap'])
            else:
                self.album_art_label.setText("No Art")
                self.album_art_label.setPixmap(QPixmap())
                
            self.player.setSource(QUrl.fromLocalFile(file_path))
            self.player.play()
    
        def get_track_metadata(self, file_path):
            title = os.path.basename(file_path)
            artist = "Unknown Artist"
            album = "Unknown Album"
            pixmap = None
            try:
                if file_path.lower().endswith('.mp3'):
                    audio = MP3(file_path)
                    if 'TIT2' in audio: title = audio['TIT2'].text[0]
                    if 'TPE1' in audio: artist = audio['TPE1'].text[0]
                    if 'TALB' in audio: album = audio['TALB'].text[0]
                    if 'APIC:' in audio:
                        img_data = audio.tags.getall('APIC:')[0].data
                        pixmap = QPixmap(); pixmap.loadFromData(img_data)
                elif file_path.lower().endswith('.flac'):
                    audio = FLAC(file_path)
                    if 'title' in audio: title = audio['title'][0]
                    if 'artist' in audio: artist = audio['artist'][0]
                    if 'album' in audio: album = audio['album'][0]
                    if audio.pictures:
                        img_data = audio.pictures[0].data
                        pixmap = QPixmap(); pixmap.loadFromData(img_data)
            except Exception as e:
                print(f"Error reading metadata for {file_path}: {e}")
                
            return {'title': title, 'artist': artist, 'album': album, 'pixmap': pixmap}
    
        def play_pause(self):
            if self.player.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
                self.player.pause()
            else:
                if self.current_file_path:
                    self.player.play()
                else:
                    self.open_file()
        
        def seek_by_seconds(self, seconds):
            if not self.current_file_path: return
            current_pos = self.player.position()
            new_pos = current_pos + (seconds * 1000)
            duration = self.player.duration()
    
            if new_pos < 0: new_pos = 0
            if duration > 0 and new_pos > duration: new_pos = duration
            
            self.player.setPosition(new_pos)
            
        def toggle_mute(self):
            if self.audio_output.isMuted():
                self.audio_output.setMuted(False)
                self.mute_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaVolume))
            else:
                self.audio_output.setMuted(True)
                self.mute_button.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaVolumeMuted))
                
        def set_position(self, position):
            self.player.setPosition(position)
            
        def set_volume(self, volume):
            self.audio_output.setVolume(volume / 100.0)
            
        def update_position(self, position):
            self.progress_slider.setValue(position)
            self.current_time_label.setText(self.format_time(position))
            
        def update_duration(self, duration):
            self.progress_slider.setRange(0, duration)
            self.total_time_label.setText(self.format_time(duration))
            
        def update_play_pause_icon(self, state):
            icon = QStyle.StandardPixmap.SP_MediaPause if state == QMediaPlayer.PlaybackState.PlayingState else QStyle.StandardPixmap.SP_MediaPlay
            self.play_pause_button.setIcon(self.style().standardIcon(icon))
        
        def handle_media_status(self, status):
            if status == QMediaPlayer.MediaStatus.EndOfMedia:
                self.stop_player()
    
        def format_time(self, ms):
            s = round(ms / 1000)
            m, s = divmod(s, 60)
            return f"{m:02d}:{s:02d}"
    
            
        def resume_after_typing(self):
            if self.was_playing_before_typing:
                # מונע ניגון אם השיר כבר פועל
                if self.player.playbackState() != QMediaPlayer.PlaybackState.PlayingState:
                    self.player.play()
                self.was_playing_before_typing = False
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        player_window = MusicPlayer()
        player_window.show()
        sys.exit(app.exec())
    

    הפיצ'רים כדלהלן:

    • בעת הקלדה על המקלדת הנגן מפסיק את פעולת הזרמת השמע וממשיך כאשר מפסיקים להקליד

    • ניתן לבטל את הפיצ'ר או להגדיר את זמן ההפסקה בכפתור ההגדרות

    • ניתן לנעוץ את הנגן כך שיופיע תמיד מעל כל החלונות וכך תוך כדי הקלדה לדלג קדימה/אחורה בייתר קלות...

    • בעת נעיצת הנגן הוא מצטמצם לגודל מינימאלי שלא יתפוס יותר מידי שטח מסך...

    צילומי מסך:

    3fa0027b-0be9-4ea9-b3f7-cb34d52c86c8-image.png
    החלון הראשי

    fa22755e-b380-4a34-ac76-463530343e49-image.png
    מצב נעוץ

    08d10bc6-4fa6-400b-90b9-6dbdb8c35729-image.png
    חלונית ההגדרות

    תגובה 1 תגובה אחרונה
    9
    • 25802 מנותק
      25802 מנותק
      2580
      מדריכים
      כתב נערך לאחרונה על ידי
      #2

      לכל מי שהוריד היה בעיה בתוכנה שפיצ'ר ההשהיה לא עבד כאשר לא היו בתוך חלון התוכנה הבאג תוקן והקובץ הוחלף בחדש...

      👆👆👆👆👆

      תגובה 1 תגובה אחרונה
      2

      • התחברות

      • אין לך חשבון עדיין? הרשמה

      • התחברו או הירשמו כדי לחפש.
      • פוסט ראשון
        פוסט אחרון
      0
      • חוקי הפורום
      • פופולרי
      • לא נפתר
      • משתמשים
      • חיפוש גוגל בפורום
      • צור קשר