I am trying to write a custom software (in python) to grab frames from a frame grabber card (Hauppauge WinTV-HVR-1900), and I cannot get it to work. The bundled WinTV software works perfectly, and I am able to grab frames from usb webcams, so I know the hardware works. I manage to capture something though, since my screen is completely black but changes to the right size and resolution, so my hunch is it is some sort of decoding problem.
Could anyone help me with an indication or provide a code example for frame extraction from such hardware, please? Should I write an entire custom driver? Or maybe use the VLC python bindings (since VLC does succeed in reading the video stream)?
EDIT:
Basically, my question comes down to this: How is my frame grabber different from a webcam, since the integrated hardware encoder yields an MPEG-2 stream? Shouldn't is behave just as my webcam?
The logic file of my attempt (QT framework using pyQt4 with python 2.7 on windows 7):
#-*- coding: utf-8 -*-
import sys
from VideoCapture import Device
#import Gui files and classes
import FloGui
import deviceDialog
# PyQT4 imports
from PyQt4 import QtGui, QtCore
class devicedialog(QtGui.QDialog, deviceDialog.Ui_streamDialog):
#the logic of the streaming device choice dialog
def __init__(self,parent=None):
super(devicedialog,self).__init__(parent)
self.setupUi(self)
self.retranslateUi(self)
self.index = None
i=0
self.camlist=list()
while True:
try:
cam = Device(i,0)
name = cam.getDisplayName()
dev = [name,i]
self.camlist.append(dev)
i+=1
del cam
except:
break
for j in xrange(len(self.camlist)):
item = QtGui.QListWidgetItem(self.camlist[j][0])
self.deviceListBox.addItem(item)
self.exec_()
def accept(self):
selected = self.deviceListBox.currentItem().text()
for k in xrange(len(self.camlist)):
if self.camlist[k][0]==selected:
self.index = k
QtGui.QDialog.accept(self)
class Flolog(QtGui.QMainWindow,FloGui.Ui_MainWindow):
"""
Flolog is inherited from both QtGui.QDialog and FloGui.Ui_MainWindow
"""
def __init__(self, parent=None):
"""
Initialization of the class. Call the __init__ for the super classes
"""
super(Flolog,self).__init__(parent)
self.setupUi(self)
self.retranslateUi(self)
self.connectActions()
def streamchoice(self):
streamdialog = devicedialog()
self.VideoWidget.stop()
self.VideoWidget.path = streamdialog.index
self.VideoWidget.load()
self.playButton.setEnabled(True)
def menuloadfile(self):
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', '.')
self.VideoWidget.stop()
self.VideoWidget.path = str(filename)
self.VideoWidget.load()
self.playButton.setEnabled(True)
def playbutton(self):
self.VideoWidget.play()
self.playButton.setEnabled(False)
def main(self):
self.show()
def quit_(self):
sys.exit(0)
def connectActions(self):
"""
Connect the user interface controls to the logic
"""
self.actionFile.triggered.connect(self.menuloadfile)
self.actionStream.triggered.connect(self.streamchoice)
self.actionQuit.triggered.connect(self.quit_)
self.playButton.clicked.connect(self.playbutton)
self.stopButton.clicked.connect(self.VideoWidget.stop)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
Flologob = Flolog()
Flologob.main()
sys.exit(app.exec_())
I actually finally did solve this. The problem stemmed from the fact that one should first specify which video device should be used by the graphics driver, either manually or in the code. I solved this on Linux Ubuntu by switching devices via the v4l2 graphics driver. I know there is an equivalent maneuver to be performed on Windows.
Related
I have a Qt Python program that logs data over a serial port. I'd like this program to always log data while running even when the application is not visible. Currently, when the application is not visible, the logging will pause after about ~45 seconds. Once the application window becomes visible again, logging resumes. The logging portion of the code is in a second thread using QRunnable and QThreadPool.
I've tried searching for the cause (or solution), but have not had much luck. Part of my problem is that I'm not sure if this issue is related to the OS, IDE, language, etc.
High-level details:
OS: macOS 12.4
IDE: vscode
Language/frameworks: Python3 / Qt (pyside6)
Does anyone have any ideas on why this application/thread might be pausing? Is it possible to have the application to continue to log data even when it is not visible? My hope is that once I'm pointed in the right direction I'll be able to address the issue.
UPDATE
Example code
class LogSignals(QObject):
result = Signal(dict)
class LogWorker(QRunnable):
def __init__():
super().__init__()
self.signals = LogSignals()
def run(self):
try:
for i in range(N_LOG_SAMPLES):
result = self.getSerialData()
self.signals.result.emit(result)
except:
traceback.print_exc()
exctype, value = sys.exc_info()[:2]
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.threadpool = QThreadPool()
def startLog(self):
log_worker = LogWorker()
log_worker.signals.result.connect(self.updateLogData)
self.threadpool.start(log_worker)
#Slot()
def updateLogData(self, result: dict):
#save data
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
You would need to check if your application is active, and start or drop the logging accordingly. You can get this by using:
QWidget *QApplication::activeWindow()
This would return a nullptr if your application does not have an active window. So, you would write something like this:
if (application->activeWindow())
; // Start / Keep logging
else
; // Stop logging
I have some small PyQt program that I want to convert to Tkinter(because of commercial license). And I wonder how easy was working with multi-threading on Qt:
class DownloadFileThread(QtCore.QThread):
download_error = QtCore.pyqtSignal(str, str)
download_progress = QtCore.pyqtSignal(int)
download_finished = QtCore.pyqtSignal()
def run(self):
# some code here ...
if error:
self.download_error.emit(
f'Error',
f'error'
)
elif progress:
self.download_progress.emit(progress)
else:
self.download_finished.emit()
f.close()
# and then somewhere in GUI class:
def set_progress_bar_value(self, val):
self.progress_bar.setValue(val)
self.download_file_thread.download_progress.connect(self.set_progress_bar_value)
So I have fully independent class that allows me to connect multiple widgets and control them based on SIGNALS
But I read a lot of articles and examples and still can't figure out how to implement something like this on Tkinter. How to make fully independent class that should post events to main thread and then GUI class should handle this events and update UI.
I use the standard python thread module in python for Tkinter and find it works great..
Here is an excerpt from a program used for streaming data from an API..
Hopefully it can give you a head start but it is pure functional based..
In my case it worked a charm.. The program crashes without..
def get_data():
"""Parse and dump all data."""
fnt = font.Font(family="Courier New", size=14)
textPad = tkinter.Frame(root)
textPad.pack(expand=True, fill='both')
T = tkinter.Text(textPad, font=fnt, height=29)
scrollbar = tkinter.Scrollbar(textPad)
.....
def thread_stuff():
t = threading.Thread(target=get_data)
t.daemon = True
t.start()
I've got a Python 2.7 application running with PyQt4 that has a QWebView in it, that has two way communication to and from Javascript.
The application is multithreaded via QThreadPool, QRunnables, so I'm communicating with a ViewController class with signals.
When I run the application, the QWebView loads my HTML with external JS and CSS just fine. I'm able to interact with the Javascript functions via the main program thread and ViewController class.
Once the user selects a directory and certain criteria are met, it starts looping through QRunnable tasks one at a time. During that time it calls back to the ViewController -> Javascript via Signal slots, just as expected. The problem is when I'm calling those ViewController methods that execute evaluateJavaScript, I get a Javascript error returned,
undefined line 1: SyntaxError: Parse error
I've done lots of trial error back and forth, but can't seem to figure out why evaluateJavaScript won't run in these instances. I've tried sending simple Javascript calls ranging from test functions that don't accept any arguments (thinking maybe it was some weird encoding issue), to just sending things like Application.main.evaluateJavaScript("alert('foo')"), which normally work outside of the threads. The only other thing I can think of is that maybe self.main.addToJavaScriptWindowObject('view', self.view) needs to be called in the threads again, but I've run a dir() on Application.main and it appears to have the evaluateJavaScript method attached to it already.
Any thoughts on why this could be occurring, when the scope seems to be correct, and the ViewController appears to be communicating just fine to the QWebView otherwise? Answers in Qt C++ will probably work as well, if you've seen this happen before!
I tried to simplify the code for example purposes:
# coding: utf8
import subprocess as sp
import os.path, os, sys, time, datetime
from os.path import basename
import glob
import random
import string
from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.QtCore import QObject, pyqtSlot, QThreadPool, QRunnable, pyqtSignal
from PyQt4.QtGui import QApplication, QFileDialog
from PyQt4.QtWebKit import QWebView
from ImportController import *
class Browser(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(800,500)
self.centralwidget = QtGui.QWidget(self)
self.mainLayout = QtGui.QHBoxLayout(self.centralwidget)
self.mainLayout.setSpacing(0)
self.mainLayout.setMargin(0)
self.frame = QtGui.QFrame(self.centralwidget)
self.gridLayout = QtGui.QVBoxLayout(self.frame)
self.gridLayout.setMargin(0)
self.gridLayout.setSpacing(0)
self.html = QtWebKit.QWebView()
# for javascript errors
errors = WebPage()
self.html.setPage(errors)
self.main = self.html.page().mainFrame()
self.gridLayout.addWidget(self.html)
self.mainLayout.addWidget(self.frame)
self.setCentralWidget(self.centralwidget)
path = os.getcwd()
if self.checkNetworkAvailability() and self.checkApiAvailbility():
self.default_url = "file://"+path+"/View/mainView.html"
else:
self.default_url = "file://"+path+"/View/errorView.html"
# load the html view
self.openView()
# controller class that sends and receives to/from javascript
self.view = ViewController()
self.main.addToJavaScriptWindowObject('view', self.view)
# on gui load finish
self.html.loadFinished.connect(self.on_loadFinished)
# to javascript
def selectDirectory(self):
# This evaluates the directory we've selected to make sure it fits the criteria, then parses the XML files
pass
def evaluateDirectory(self, directory):
if not directory:
return False
if os.path.isdir(directory):
return True
else:
return False
#QtCore.pyqtSlot()
def on_loadFinished(self):
# open directory select dialog
self.selectDirectory()
def openView(self):
self.html.load(QtCore.QUrl(self.default_url))
self.html.show()
def checkNetworkAvailability(self):
#TODO: make sure we can reach the outside world before trying anything else
return True
def checkApiAvailbility(self):
#TODO: make sure the API server is alive and responding
return True
class WebPage(QtWebKit.QWebPage):
def javaScriptConsoleMessage(self, msg, line, source):
print '%s line %d: %s' % (source, line, msg)
class ViewController(QObject):
def __init__(self, parent=None):
super(ViewController, self).__init__(parent)
#pyqtSlot()
def did_load(self):
print "View Loaded."
#pyqtSlot()
def selectDirectoryDialog(self):
# FROM JAVASCRIPT: in case they need to re-open the file dialog
Application.selectDirectory()
def prepareImportView(self, displayPath):
# TO JAVASCRIPT: XML directory parsed okay, so let's show the main
Application.main.evaluateJavaScript("prepareImportView('{0}');".format(displayPath))
def generalMessageToView(self, target, message):
# TO JAVASCRIPT: Send a general message to a specific widget target
Application.main.evaluateJavaScript("receiveMessageFromController('{0}', '{1}')".format(target, message))
#pyqtSlot()
def startProductImport(self):
# FROM JAVASCRIPT: Trigger the product import loop, QThreads
print "### view.startProductImport"
position = 1
count = len(Application.data.products)
importTasks = ProductImportQueue(Application.data.products)
importTasks.start()
#pyqtSlot(str)
def updateProductView(self, data):
# TO JAVASCRIPT: Send product information to view
print "### updateProductView "
Application.main.evaluateJavaScript('updateProductView("{0}");'.format(QtCore.QString(data)) )
class WorkerSignals(QObject):
''' Declares the signals that will be broadcast to their connected view methods '''
productResult = pyqtSignal(str)
class ProductImporterTask(QRunnable):
''' This is where the import process will be fired for each loop iteration '''
def __init__(self, product):
super(ProductImporterTask, self).__init__()
self.product = product
self.count = ""
self.position = ""
self.signals = WorkerSignals()
def run(self):
print "### ProductImporterTask worker {0}/{1}".format(self.position, self.count)
# Normally we'd create a dict here, but I'm trying to just send a string for testing purposes
self.signals.productResult.emit(data)
return
class ProductImportQueue(QObject):
''' The synchronous threadpool that is going to one by one run the import threads '''
def __init__(self, products):
super(ProductImportQueue, self).__init__()
self.products = products
self.pool = QThreadPool()
self.pool.setMaxThreadCount(1)
def process_result(self, product):
return
def start(self):
''' Call the product import worker from here, and format it in a predictable way '''
count = len(self.products)
position = 1
for product in self.products:
worker = ProductImporterTask("test")
worker.signals.productResult.connect(Application.view.updateProductView, QtCore.Qt.DirectConnection)
self.pool.start(worker)
position = position + 1
self.pool.waitForDone()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
Application = Browser()
Application.raise_()
Application.show()
Application.activateWindow()
sys.exit(app.exec_())
You know, I love PyQt4 but after searching and searching, I believe this is actually a bug and not as designed.
I've since moved on and am trying to implement this in CEFPython with WxPython, which seems to have a much more elegant implementation for this specific purpose.
I use the QWebView (python 3.3 + pyside 1.1.2 + Qt 4.8) as FileViewer. Picture, Text, HTML, ... all fine, but PDF has a display problem. I tested two possible ways.
internal pdf viewer: after use webview.load(file) it loads, but
the screen is blank, after loading another file, all works fine, it
shows the file
pdf.js: after use setContent() with filebase, it
loads the webviewer.html/.js with a white page and the loading circle. The
screen only refresh if I resize the form or use the scrollbars, but
then all is fine
I don't find an event for "plugin/javascript finished loading", so I could force a repaint or so.
Here an example code for case 1:
import sys
from PySide import QtCore, QtGui, QtWebKit ##UnusedWildImport
class DialogTest(QtGui.QDialog):
def __init__(self, parent = None):
super(DialogTest, self).__init__(parent)
self.resize(620, 600)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.PreviewBox = QtWebKit.QWebView()
self.PreviewBox.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)
self.PreviewBox.settings().setAttribute(QtWebKit.QWebSettings.WebAttribute.DeveloperExtrasEnabled, True)
self.PreviewBox.settings().setAttribute(QtWebKit.QWebSettings.PrivateBrowsingEnabled, True)
self.PreviewBox.settings().setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True)
self.PreviewBox.loadFinished.connect(self._loadfinished)
self.button_test1 = QtGui.QPushButton("File 1")
self.button_test1.clicked.connect(self._onselect1)
self.button_test2 = QtGui.QPushButton("File 2")
self.button_test2.clicked.connect(self._onselect2)
layout_Buttons = QtGui.QHBoxLayout()
layout_Buttons.addWidget(self.button_test1)
#layout_Buttons.addStretch()
layout_Buttons.addWidget(self.button_test2)
layout_Main = QtGui.QVBoxLayout()
layout_Main.addLayout(layout_Buttons)
layout_Main.addWidget(self.PreviewBox)
self.setLayout(layout_Main)
def Execute(self):
self.show()
self.exec_()
def _onselect1(self):
self.PreviewBox.load(QtCore.QUrl().fromLocalFile("c:\\tmp\\test1.pdf"))
def _onselect2(self):
self.PreviewBox.load(QtCore.QUrl().fromLocalFile("c:\\tmp\\test2.pdf"))
def _loadfinished(self, ok):
#self.PreviewBox.repaint()
pass
app = QtGui.QApplication(sys.argv)
DialogTest().Execute()
Edit: Workaround
Case 1 (webkit plugin) has an otherbug, it takes the focus to itself, so this solution isn't acceptable to me. I played with the pdf.js again and found a workaroud:
self.PreviewBox.setHtml(content, baseUrl = QtCore.QUrl().fromLocalFile(path))
self.PreviewBox.hide()
QtCore.QTimer.singleShot(700, self.PreviewBox.show)
The hide() must be after the content filling and the timer haven't to be too low.
//jay
I just solved a similar problem cleaning the QWebView before every pdf load.
Be careful with the loadFinished() signal.
In your example:
self.PreviewBox.load(QUrl('about:blank'))
or, in case we don't like 'about:blank' this may be a more portable solution:
self.PreviewBox.setHtml('<html><head></head><title></title><body></body></html>')
I'm currently trying to implement some kind of file browser / "explorer" into a programme... I'm using Python and PySide in connection with the Qt-window-toolkit. More or less this youtube-video shows the behaviour I want to have at the end. However, this tutorial used C++ as programming language and I haven't been able yet to reason the right python code from the C++ example.
Basically, my problem is to get the right column (file view) showing the content of the folder clicked in the left column (tree-style folder view).
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PySide import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(600, 600)
self.fileBrowserWidget = QtGui.QWidget(self)
self.setCentralWidget(self.fileBrowserWidget)
self.dirmodel = QtGui.QFileSystemModel()
# Don't show files, just folders
self.dirmodel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.AllDirs)
self.folder_view = QtGui.QTreeView(parent=self);
self.folder_view.setModel(self.dirmodel)
self.folder_view.clicked[QtCore.QModelIndex].connect(self.clicked)
# Don't show columns for size, file type, and last modified
self.folder_view.setHeaderHidden(True)
self.folder_view.hideColumn(1)
self.folder_view.hideColumn(2)
self.folder_view.hideColumn(3)
self.selectionModel = self.folder_view.selectionModel()
self.filemodel = QtGui.QFileSystemModel()
# Don't show folders, just files
self.filemodel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Files)
self.file_view = QtGui.QListView(parent=self);
self.file_view.setModel(self.filemodel)
splitter_filebrowser = QtGui.QSplitter()
splitter_filebrowser.addWidget(self.folder_view)
splitter_filebrowser.addWidget(self.file_view)
splitter_filebrowser.setStretchFactor(0,2)
splitter_filebrowser.setStretchFactor(1,4)
hbox = QtGui.QHBoxLayout(self.fileBrowserWidget)
hbox.addWidget(splitter_filebrowser)
def set_path(self):
self.dirmodel.setRootPath("")
def clicked(self, index):
# get selected path of folder_view
index = self.selectionModel.currentIndex()
dir_path = self.dirmodel.filePath(index)
###############################################
# Here's my problem: How do I set the dir_path
# for the file_view widget / the filemodel?
###############################################
self.filemodel.setRootPath(dir_path)
app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
main.set_path()
sys.exit(app.exec_())
As you can see in my code, I've already tried to use the setRootPath-function... however, that doesn't seem to be the correct one. So I wonder, what I've got to do for getting this to work?
You need to set the root index to the appropriate one in the file model. You can do this by adding the following line to the end of the clicked() function:
self.file_view.setRootIndex(self.filemodel.index(dir_path))
I was able to figure it out from my experience using Qt in C++. The documentation for Qt in C++ is really quite good if you can figure out how it translates to Python. I was able to figure this out by looking at the QFileSystemModel documentation.
You need to set the root index of the files list view:
def clicked(self, index):
# the signal passes the index of the clicked item
dir_path = self.filemodel.filePath(index)
root_index = self.filemodel.setRootPath(dir_path)
self.file_view.setRootIndex(root_index)