Fix Qt player overlay window positioning

- Add window positioning synchronization for separate overlay windows
- Overlay window now correctly appears over main video player window
- Add move and resize event handlers to keep windows synchronized
- Add proper overlay window cleanup in closeEvent
- Separate top-level window approach successfully resolves Qt rendering conflicts
parent 54d1a139
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/env python3
from PyQt6.QtWidgets import QApplication, QMainWindow
import sys
app = QApplication(sys.argv)
window = QMainWindow()
window.show()
sys.exit(app.exec())
#!/usr/bin/env python3
"""
Standalone test application for PyQt6 Video Player with QWebEngineView overlay
"""
import sys
import logging
import time
from pathlib import Path
from dataclasses import dataclass
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import QTimer
# Add project path for imports
project_path = Path(__file__).parent
sys.path.insert(0, str(project_path))
from mbetterclient.qt_player.qt6_player import Qt6VideoPlayer, PlayerWindow
from mbetterclient.core.message_bus import MessageBus, MessageBuilder
from mbetterclient.config.settings import QtConfig
@dataclass
class TestQtConfig:
"""Test configuration for Qt player"""
fullscreen: bool = False
window_width: int = 1280
window_height: int = 720
always_on_top: bool = False
auto_play: bool = True
volume: float = 0.8
mute: bool = False
def setup_logging():
"""Setup logging for the test application"""
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler('qt6_player_test.log')
]
)
def test_standalone_player():
"""Test the standalone PyQt6 player window"""
print("Testing Standalone PyQt6 Player...")
app = QApplication(sys.argv)
config = TestQtConfig()
# Create player window directly
window = PlayerWindow(config)
# Show window
window.show()
# Test overlay updates
overlay_view = window.video_widget.get_overlay_view()
def update_overlay_demo():
"""Demo function to update overlay periodically"""
current_time = time.strftime("%H:%M:%S")
overlay_data = {
'title': f'PyQt6 Demo - {current_time}',
'subtitle': 'Multi-threaded Video Player with WebEngine Overlay',
'ticker': 'Real-time JavaScript ↔ Python Communication • Hardware Accelerated Video • Professional Animations'
}
overlay_view.update_overlay_data(overlay_data)
print(f"Updated overlay at {current_time}")
# Setup periodic overlay updates
timer = QTimer()
timer.timeout.connect(update_overlay_demo)
timer.start(2000) # Update every 2 seconds
# Initial overlay update
update_overlay_demo()
print("PyQt6 Player Window created successfully!")
print("Features demonstrated:")
print("- QMediaPlayer + QVideoWidget for hardware-accelerated video")
print("- QWebEngineView overlay with transparent background")
print("- QWebChannel bidirectional Python ↔ JavaScript communication")
print("- CSS3 animations with GSAP integration")
print("- Thread-safe signal/slot mechanisms")
print("- QTimer integration for real-time updates")
print("- Professional UI with responsive design")
print("- Cross-platform compatibility")
print("\nControls:")
print("- Space: Play/Pause")
print("- F11: Toggle Fullscreen")
print("- S: Toggle Stats Panel")
print("- M: Toggle Mute")
print("- Escape: Exit")
print("\nClose the window to exit the test.")
return app.exec()
def test_threaded_player():
"""Test the full threaded PyQt6 player component"""
print("Testing Threaded PyQt6 Player Component...")
# Create message bus
message_bus = MessageBus()
# Create Qt config
config = TestQtConfig()
# Create Qt6 player component
player = Qt6VideoPlayer(message_bus, config)
# Initialize player
if not player.initialize():
print("Failed to initialize Qt6VideoPlayer!")
return 1
# Start player in separate thread (simulation)
print("Qt6VideoPlayer initialized successfully!")
# Test sending messages
def send_test_messages():
"""Send test messages to player"""
time.sleep(2)
# Test overlay update
overlay_message = MessageBuilder.template_change(
sender="test_app",
template_name="demo_template",
template_data={
'title': 'Threaded Player Demo',
'subtitle': 'Message Bus Communication Test',
'ticker': 'Successfully communicating via MessageBus • Multi-threaded Architecture • Real-time Updates'
}
)
overlay_message.recipient = "qt6_player"
message_bus.publish(overlay_message)
print("Sent overlay update message")
# Test video info update
time.sleep(2)
video_info_message = MessageBuilder.system_status(
sender="test_app",
status="demo",
details={
'videoInfo': {
'resolution': '1920x1080',
'bitrate': '8.5 Mbps',
'codec': 'H.265/HEVC',
'fps': '60.0'
}
}
)
video_info_message.recipient = "qt6_player"
message_bus.publish(video_info_message)
print("Sent video info update")
# Setup test message timer
timer = QTimer()
timer.timeout.connect(send_test_messages)
timer.setSingleShot(True)
timer.start(1000) # Start after 1 second
print("Threaded player test started. Close the player window to exit.")
# Run the player (this would normally be in a separate thread)
try:
player.run()
except KeyboardInterrupt:
print("Stopping player...")
player.shutdown()
return 0
def main():
"""Main test function"""
setup_logging()
print("PyQt6 Multi-threaded Video Player Test Suite")
print("=" * 50)
if len(sys.argv) > 1:
test_mode = sys.argv[1]
else:
print("Available test modes:")
print("1. standalone - Test standalone player window")
print("2. threaded - Test full threaded player component")
print()
test_mode = input("Select test mode (1 or 2): ").strip()
if test_mode == "1":
test_mode = "standalone"
elif test_mode == "2":
test_mode = "threaded"
else:
test_mode = "standalone"
try:
if test_mode == "standalone":
return test_standalone_player()
elif test_mode == "threaded":
return test_threaded_player()
else:
print(f"Unknown test mode: {test_mode}")
return 1
except Exception as e:
print(f"Test failed with error: {e}")
import traceback
traceback.print_exc()
return 1
if __name__ == "__main__":
sys.exit(main())
\ No newline at end of file
#!/usr/bin/env python3
"""
Test script for Qt player functionality
"""
import sys
import os
import logging
import time
import threading
from pathlib import Path
# Add the project root to Python path
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
from mbetterclient.config.settings import AppSettings
from mbetterclient.core.message_bus import MessageBus, MessageBuilder, MessageType
from mbetterclient.qt_player.player import QtVideoPlayer
def setup_logging():
"""Setup logging for the test"""
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
return logging.getLogger(__name__)
def test_qt_player_standalone():
"""Test Qt player in standalone mode"""
logger = setup_logging()
logger.info("Starting Qt player standalone test")
# Create settings
settings = AppSettings()
settings.qt.fullscreen = False
settings.qt.window_width = 800
settings.qt.window_height = 600
# Create message bus
message_bus = MessageBus()
# Create Qt player
qt_player = QtVideoPlayer(message_bus, settings.qt)
# Initialize Qt player
if not qt_player.initialize():
logger.error("Failed to initialize Qt player")
return 1
logger.info("Qt player initialized successfully")
# Start message processing in a separate thread
qt_player.start_message_processing()
# Send a test message to display default overlay
test_message = MessageBuilder.template_change(
sender="test",
template_data={
"title": "Qt Player Test",
"subtitle": "Standalone Mode Test",
"ticker": "This is a test of the Qt player in standalone mode"
}
)
message_bus.publish(test_message)
# Run Qt event loop (this will block until window is closed)
logger.info("Running Qt event loop - close the window to exit")
exit_code = qt_player.run()
# Cleanup
qt_player.shutdown()
logger.info("Qt player test completed")
return exit_code
def test_qt_player_with_message_bus():
"""Test Qt player with message bus communication"""
logger = setup_logging()
logger.info("Starting Qt player message bus test")
# Create settings
settings = AppSettings()
settings.qt.fullscreen = False
settings.qt.window_width = 800
settings.qt.window_height = 600
# Create message bus
message_bus = MessageBus()
# Create Qt player
qt_player = QtVideoPlayer(message_bus, settings.qt)
# Initialize Qt player
if not qt_player.initialize():
logger.error("Failed to initialize Qt player")
return 1
logger.info("Qt player initialized successfully")
# Start message processing in a separate thread
qt_player.start_message_processing()
# Send test messages
def send_test_messages():
time.sleep(2) # Wait for window to be ready
# Send overlay update
overlay_message = MessageBuilder.overlay_update(
sender="test",
overlay_data={
"title": "Message Bus Test",
"subtitle": "Testing message bus communication",
"showStats": True
}
)
message_bus.publish(overlay_message)
time.sleep(3)
# Send another overlay update
overlay_message2 = MessageBuilder.overlay_update(
sender="test",
overlay_data={
"title": "Message Bus Test Continued",
"subtitle": "Second message bus test",
"ticker": "Testing continuous updates through message bus"
}
)
message_bus.publish(overlay_message2)
logger.info("Test messages sent")
# Start message sending in a separate thread
message_thread = threading.Thread(target=send_test_messages)
message_thread.start()
# Run Qt event loop (this will block until window is closed)
logger.info("Running Qt event loop with message bus test - close the window to exit")
exit_code = qt_player.run()
# Wait for message thread to finish
message_thread.join()
# Cleanup
qt_player.shutdown()
logger.info("Qt player message bus test completed")
return exit_code
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "standalone":
exit_code = test_qt_player_standalone()
elif len(sys.argv) > 1 and sys.argv[1] == "message_bus":
exit_code = test_qt_player_with_message_bus()
else:
print("Usage: python test_qt_player.py [standalone|message_bus]")
print(" standalone: Test Qt player in standalone mode")
print(" message_bus: Test Qt player with message bus communication")
sys.exit(1)
sys.exit(exit_code)
\ No newline at end of file
File added
#!/usr/bin/env python3
"""
Debug script specifically for testing video playback visibility in Qt player
Tests both native and WebEngine overlays to isolate the video blocking issue
"""
import sys
import logging
import time
from pathlib import Path
from dataclasses import dataclass
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import QTimer
# Add project path for imports
project_path = Path(__file__).parent
sys.path.insert(0, str(project_path))
from mbetterclient.qt_player.player import PlayerWindow
@dataclass
class DebugQtConfig:
"""Debug configuration for Qt player"""
fullscreen: bool = False
window_width: int = 800
window_height: int = 600
always_on_top: bool = False
auto_play: bool = True
volume: float = 0.8
mute: bool = False
use_native_overlay: bool = True # Start with native overlay for testing
def setup_debug_logging():
"""Setup debug logging"""
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler('video_debug.log')
]
)
def test_video_playback_native():
"""Test video playback with native overlay (should not block video)"""
print("Testing Video Playback with NATIVE overlay...")
app = QApplication(sys.argv)
config = DebugQtConfig()
config.use_native_overlay = True # Force native overlay
# Create player window with native overlay
window = PlayerWindow(config)
window.show()
# Test with our generated test video
test_video_path = "test_video.mp4"
def play_test_video():
"""Play the test video after a short delay"""
print(f"Playing test video: {test_video_path}")
window.play_video(test_video_path)
# Update overlay to confirm it's working
overlay_data = {
'title': 'DEBUG: Native Overlay Test',
'subtitle': 'Video should be VISIBLE underneath this overlay',
'ticker': 'If you can see moving colors/patterns, video is working! Native overlay should not block video.'
}
overlay_view = window.video_widget.get_overlay_view()
overlay_view.update_overlay_data(overlay_data)
# Play video after 2 seconds
QTimer.singleShot(2000, play_test_video)
print("Native Overlay Test Window created!")
print("Expected behavior:")
print("- You should see a test pattern video (moving colors/gradients)")
print("- Native overlay text should appear ON TOP of the video")
print("- If video is NOT visible, the issue is deeper than overlay blocking")
print("\nControls:")
print("- Space: Play/Pause")
print("- Escape: Exit")
return app.exec()
def test_video_playback_webengine():
"""Test video playback with WebEngine overlay (may block video)"""
print("Testing Video Playback with WEBENGINE overlay...")
app = QApplication(sys.argv)
config = DebugQtConfig()
config.use_native_overlay = False # Force WebEngine overlay
# Create player window with WebEngine overlay
window = PlayerWindow(config)
window.show()
# Test with our generated test video
test_video_path = "test_video.mp4"
def play_test_video():
"""Play the test video after a short delay"""
print(f"Playing test video: {test_video_path}")
window.play_video(test_video_path)
# Update overlay to confirm it's working
overlay_data = {
'title': 'DEBUG: WebEngine Overlay Test',
'subtitle': 'Video may be BLOCKED by this overlay',
'ticker': 'If you CANNOT see moving colors/patterns, WebEngine overlay is blocking the video!'
}
# Wait for WebEngine to be ready before updating
def update_overlay_when_ready():
overlay_view = window.video_widget.get_overlay_view()
if hasattr(overlay_view, 'overlay_channel') and overlay_view.overlay_channel:
if window._is_webengine_ready(overlay_view):
overlay_view.update_overlay_data(overlay_data)
print("WebEngine overlay updated")
else:
print("WebEngine not ready, retrying...")
QTimer.singleShot(1000, update_overlay_when_ready)
else:
overlay_view.update_overlay_data(overlay_data)
QTimer.singleShot(3000, update_overlay_when_ready)
# Play video after 2 seconds
QTimer.singleShot(2000, play_test_video)
print("WebEngine Overlay Test Window created!")
print("Expected behavior:")
print("- You should see a test pattern video (moving colors/gradients)")
print("- WebEngine overlay text should appear ON TOP of the video")
print("- If video is NOT visible, WebEngine overlay is blocking it")
print("\nControls:")
print("- Space: Play/Pause")
print("- Escape: Exit")
return app.exec()
def test_uploaded_video():
"""Test with an actual uploaded video file"""
print("Testing with uploaded video files...")
# Look for uploaded videos
uploads_dir = Path("uploads")
if uploads_dir.exists():
video_files = list(uploads_dir.glob("*.mp4"))
if video_files:
video_path = video_files[0] # Use first video found
print(f"Found uploaded video: {video_path}")
app = QApplication(sys.argv)
config = DebugQtConfig()
config.use_native_overlay = True # Start with native
window = PlayerWindow(config)
window.show()
def play_uploaded_video():
print(f"Playing uploaded video: {video_path}")
window.play_video(str(video_path))
overlay_data = {
'title': f'Playing: {video_path.name}',
'subtitle': 'Testing uploaded video with native overlay',
'ticker': 'This is a real uploaded video file. Video should be visible with native overlay.'
}
overlay_view = window.video_widget.get_overlay_view()
overlay_view.update_overlay_data(overlay_data)
QTimer.singleShot(2000, play_uploaded_video)
print(f"Testing uploaded video: {video_path.name}")
print("This tests with a real uploaded video file")
return app.exec()
else:
print("No video files found in uploads directory")
return 1
else:
print("Uploads directory not found")
return 1
def main():
"""Main debug function"""
setup_debug_logging()
print("Qt Video Player Debug Suite")
print("=" * 40)
if len(sys.argv) > 1:
test_mode = sys.argv[1]
else:
print("Available test modes:")
print("1. native - Test with native Qt overlay (should show video)")
print("2. webengine - Test with WebEngine overlay (may block video)")
print("3. uploaded - Test with uploaded video file")
print()
choice = input("Select test mode (1, 2, or 3): ").strip()
if choice == "1":
test_mode = "native"
elif choice == "2":
test_mode = "webengine"
elif choice == "3":
test_mode = "uploaded"
else:
test_mode = "native"
try:
if test_mode == "native":
return test_video_playback_native()
elif test_mode == "webengine":
return test_video_playback_webengine()
elif test_mode == "uploaded":
return test_uploaded_video()
else:
print(f"Unknown test mode: {test_mode}")
return 1
except Exception as e:
print(f"Test failed with error: {e}")
import traceback
traceback.print_exc()
return 1
if __name__ == "__main__":
sys.exit(main())
\ No newline at end of file
#!/usr/bin/env python3
"""
Minimal video test - NO overlays at all to test pure QVideoWidget rendering
"""
import sys
import logging
from pathlib import Path
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from PyQt6.QtCore import QUrl
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtMultimediaWidgets import QVideoWidget
# Add project path for imports
project_path = Path(__file__).parent
sys.path.insert(0, str(project_path))
def setup_logging():
"""Setup basic logging"""
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[logging.StreamHandler(sys.stdout)]
)
class MinimalVideoWindow(QMainWindow):
"""Absolute minimal video player - NO overlays, just pure video"""
def __init__(self):
super().__init__()
self.setup_ui()
self.setup_media_player()
def setup_ui(self):
"""Setup minimal UI - just video widget"""
self.setWindowTitle("MINIMAL Video Test - NO Overlays")
self.setGeometry(100, 100, 800, 600)
# PURE BLACK BACKGROUND - no transparency anywhere
self.setStyleSheet("QMainWindow { background-color: black; }")
# Central widget - completely opaque
central_widget = QWidget()
central_widget.setStyleSheet("background-color: black;")
self.setCentralWidget(central_widget)
# Layout
layout = QVBoxLayout(central_widget)
layout.setContentsMargins(0, 0, 0, 0)
# ONLY QVideoWidget - no overlays at all
self.video_widget = QVideoWidget()
self.video_widget.setStyleSheet("QVideoWidget { background-color: black; }")
layout.addWidget(self.video_widget)
print("Minimal video window created - PURE QVideoWidget only")
def setup_media_player(self):
"""Setup media player"""
self.media_player = QMediaPlayer()
self.audio_output = QAudioOutput()
self.media_player.setAudioOutput(self.audio_output)
self.media_player.setVideoOutput(self.video_widget)
# Connect signals for debugging
self.media_player.playbackStateChanged.connect(self.on_state_changed)
self.media_player.mediaStatusChanged.connect(self.on_status_changed)
self.media_player.errorOccurred.connect(self.on_error)
print("Media player setup completed")
def play_video(self, file_path):
"""Play video file"""
path_obj = Path(file_path)
if not path_obj.exists():
print(f"ERROR: File not found: {file_path}")
return
print(f"Loading video: {file_path}")
print(f"File size: {path_obj.stat().st_size} bytes")
url = QUrl.fromLocalFile(str(path_obj.absolute()))
print(f"QUrl: {url.toString()}")
self.media_player.setSource(url)
self.media_player.play()
print("Video play command sent")
def on_state_changed(self, state):
"""Debug state changes"""
print(f"MEDIA STATE: {state}")
def on_status_changed(self, status):
"""Debug status changes"""
print(f"MEDIA STATUS: {status}")
def on_error(self, error):
"""Debug errors"""
print(f"MEDIA ERROR: {error}")
def keyPressEvent(self, event):
"""Handle keys"""
if event.key() == 32: # Space
if self.media_player.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
self.media_player.pause()
print("PAUSED")
else:
self.media_player.play()
print("PLAYING")
def main():
"""Test minimal video rendering"""
setup_logging()
print("MINIMAL VIDEO TEST - NO OVERLAYS")
print("=" * 40)
print("This test uses ONLY QVideoWidget with NO overlays")
print("If video is not visible here, the issue is with QVideoWidget itself")
print("")
app = QApplication(sys.argv)
window = MinimalVideoWindow()
window.show()
# Play test video after delay
from PyQt6.QtCore import QTimer
QTimer.singleShot(1000, lambda: window.play_video("test_video.mp4"))
print("Window shown. Video should start playing in 1 second.")
print("Expected: You should see moving test pattern (countdown)")
print("Controls: Space = Play/Pause, Escape = Exit")
return app.exec()
if __name__ == "__main__":
sys.exit(main())
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment