Seite 1 von 1
self statement
Verfasst: Samstag 6. Februar 2021, 15:33
von m.g.o.d
Hallo Zusammen,
vielleicht kann mir jemand folgende Situation erklären:
Ich habe in einer Klasse folgende 3 Methoden. Die ersten beiden Methoden werden aus einer anderen Klasse aufgerufen und sollen den übergebenen Positionsparameter "sliderposition" und "filepath" jeweils an self der Klasse (mit den gezeigten 3 Methoden) binden. Beide Methoden werden auch aufgerufen. Rufe ich nun die letzte Methode auf (nachdem Methode 1 und 2 bereits aufgerufen wurden), so meckert er mir an, dass meine Klasse kein self.sliderposition und auch keine self.filepath besitzt. Warum ist das so? Ich kann beim Aufruf der Methode 1 oder 2 auch mit einem print(self.sliderposition) den Inhalt sehen. Ich dachte, ich erweitere mit den beiden self Zuweisungen die Variablen vom Namensraum der Hauptklasse um die zwei Variablen, aber er erkennt sie nicht.
Ich würde mich sehr freuen, wenn mir jemand den Fehler erklären kann.
Code: Alles auswählen
def get_sliderpos(self, sliderpostion):
""" get desired frameposition for saving template """
self.sliderposition = sliderpostion
def get_filepath(self, filepath):
self.filepath = filepath
def clicked_save_template(self):
init_template = TemplateCreation(self.sliderposition, self.filepath)
Re: self statement
Verfasst: Samstag 6. Februar 2021, 16:05
von __deets__
Einen setter getter zu nennen ist schon ungewöhnlich.
Und so wie es hier steht kann das schlicht nicht stimmen. Hast du das Problem wirklich zur Laufzeit? Oder reden wir bei “erkennen” von einer IDE und deren Warnungs-Features?
Re: self statement
Verfasst: Samstag 6. Februar 2021, 16:36
von m.g.o.d
Hi, okay also hier wäre mal der modifizierte Code, wo ich versuche, das Problem anders zu lösen. Es bleibt immer der gleiche Fehler. Über die Klasse RoiAdjusting versuche ich, eine Instanz auf die KLasse VideoWindow zu bilden und darüber die Methode "request_data" aufzurufen. Diese Methode soll mir nur zwei Variablen zurückgeben, aber folgender Fehler wird geworfen:
AttributeError: 'VideoWindow' object has no attribute 'file_name'
Dabei setze ich in der Methode openFile self.file_name = fileName ?!
Code: Alles auswählen
class VideoWindow(QMainWindow):
def __init__(self, parent=None):
super(VideoWindow, self).__init__(parent)
self.setWindowTitle("ROI Editor")
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
videoWidget = QVideoWidget()
def openFile(self):
try:
fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie",
QDir.homePath())
self.file_name = fileName
if fileName != '':
self.mediaPlayer.setMedia(
QMediaContent(QUrl.fromLocalFile(fileName)))
self.playButton.setEnabled(True)
except Exception as e:
logging.exception("Exception while opening the videofile:")
def request_data(self):
return self.file_name, self.duration
class RoiAdjusting(QtWidgets.QDialog):
""" provides control widgets for Roi Form, Radius, Postion """
def __init__(self,parent=None):
super().__init__(parent)
def clicked_save_template(self):
init_videoplayer = mediaplayer.VideoWindow()
a, b = init_videoplayer.request_data()
print(a)
print(b)
Re: self statement
Verfasst: Samstag 6. Februar 2021, 16:55
von __deets__
Erstens sollte man Instanzvariablen immer auch schon im Konstruktor anlegen. Dann also zB mit None vorbelegen. Dann ist dein try/except viel zu grosszuegig. Du faengst dir *alles* weg, egal ob Programmierfehler oder Anwenderfehler.
Und wenn openFile nicht oder nicht erfolgreich aufgerufen wurde, dann kommt es eben zu dem Fehler.
Uebrigens hast du ein ueberfluessiges QVideoWidget, das du gar nicht benutzt und auch einfach nur erzeugst ohne damit jemals was zu machen, so das es gleich wieder abgerissen wird.
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:01
von m.g.o.d
Natürlich benutze ist das QVideoWidget, aber ich habe den Quellcode bewusst reduziert auf die Methoden, wo das Problem besteht. Dutzende Buttons und Geometrie Settings usw. uswf. haben ja nichts mit dem Problem zu tun. Die kann ich zwar gerne auch mit posten, dann wird aber der Post sehr viel länger und mir persönlich ist unklar, was das mit dem Problem zu tun hätte.
OpenFile kommt in meinem Fall aber nicht zu einem Fehler. Ich kann mir innerhalb der Methode die Variable self.file_name wunderbar über print() ausgeben lassen. Daran liegt es also nicht.
Die Instanzierung mit None habe ich auch schon probiert. Dann bleibt die Variable bei einem Zugriff über eine andere Klasse, wie in meinem Beispiel gezeigt, aber None.
Code: Alles auswählen
def request_data(self):
return self.file_name, self.duration
gibt in dem Fall dann None, None aus. Wie gesagt, die Funktion OpenFile funktioniert und wirft keine Exception. Ich kann mir den Inhalt der Variablen auch ausgeben lassen. Greife ich aber aus einer anderen Klasse zu, dann ist die Variable nicht mehr bekannt. Das ist mein Problem.
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:05
von __deets__
Wenn das das Problem ist, dann liegt es eben an all dem anderen Kram darum herum, den du fuer irrelevant haelst. Es schlicht so: self.irgendwas = was_anderes legt ein Attribut an. Punkt. Das geht nicht irgendwie verloren, oder kaputt, oder passiert nicht. Der Interpreter ist nicht kaputt. Es liegt also an dem ganzen drumrum, und das kann keiner beurteilen, ohne es zu sehen. Alternativ kannst du ein *funktionierendes* Beispiel bauen, welches das Problem zeigt. Aber so kann man eben nur mit den Achseln zucken - Python macht, was man von ihm verlangt. Du machst was falsch.
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:09
von kbr
Falls Du 'request_data' for 'openFile' aufrufst, dann bekommst Du diesen Fehler. Den erhältst Du aber auch dann, wenn in 'openFile' ein Fehler auftritt und anschließend 'request_data' aufgerufen wird. Die Anwendung ist so nicht fehlertolerant.
Es ist eine schlechte Idee, zur Runtime dynamisch weitere Instanzattribute anzulegen. Instanzen einer Klasse können so zur Laufzeit unterschiedliches Verhalten zeigen, was für Anwender ein unerwartetes Verhalten ist und die Fehlersuche erschweren kann.
Vermutlich ist das auch die Ursache für Dein Problem: Das Instanzattribut wurde eben doch nicht dynamisch angelegt, obgleich Du annimmst, dass dem so sein sollte.
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:11
von m.g.o.d
Anbei der vollständige Code beider Klassen:
Code: Alles auswählen
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtCore import QDir, Qt, QUrl
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import (QApplication, QFileDialog, QHBoxLayout, QLabel,QPushButton, QSizePolicy, QSlider, QStyle, QVBoxLayout, QWidget)
from PyQt5.QtWidgets import QMainWindow,QWidget, QPushButton, QAction
from PyQt5.QtGui import QIcon
from PyQt5 import QtWidgets, QtCore, uic
import sys, logging
import roi_editor
class VideoWindow(QMainWindow):
def __init__(self, parent=None):
super(VideoWindow, self).__init__(parent)
self.setWindowTitle("ROI Editor")
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
videoWidget = QVideoWidget()
self.file_name = None
self.duration = None
# Painting Init
self.pen = QtGui.QPen(QtGui.QColor(0,0,0))
self.pen.setWidth(3)
self.playButton = QPushButton()
self.playButton.setEnabled(False)
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.playButton.clicked.connect(self.play)
self.positionSlider = QSlider(Qt.Horizontal)
self.positionSlider.setRange(0, 0)
self.positionSlider.sliderMoved.connect(self.setPosition)
self.errorLabel = QLabel()
self.errorLabel.setSizePolicy(QSizePolicy.Preferred,
QSizePolicy.Maximum)
# Create new action
openAction = QAction(QIcon('open.png'), '&Open', self)
openAction.setShortcut('Ctrl+O')
openAction.setStatusTip('Open movie')
openAction.triggered.connect(self.openFile)
# Create exit action
exitAction = QAction(QIcon('exit.png'), '&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.exitCall)
# create ROI action
setRoi = QAction("adjust", self)
setRoi.setShortcut('Ctrl+N')
setRoi.triggered.connect(self.adjust_roi)
# Create menu bar and add action
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('&File')
fileMenu2 = menuBar.addMenu('ROI')
fileMenu2.addAction(setRoi)
# fileMenu.addAction(newAction)
fileMenu.addAction(openAction)
fileMenu.addAction(exitAction)
# Create a widget for window contents
wid = QWidget(self)
self.setCentralWidget(wid)
# Create layouts to place inside widget
controlLayout = QHBoxLayout()
controlLayout.setContentsMargins(0, 0, 0, 0)
controlLayout.addWidget(self.playButton)
controlLayout.addWidget(self.positionSlider)
layout = QVBoxLayout()
layout.addWidget(videoWidget)
layout.addLayout(controlLayout)
layout.addWidget(self.errorLabel)
# Set widget to contain window contents
wid.setLayout(layout)
self.mediaPlayer.setVideoOutput(videoWidget)
self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
self.mediaPlayer.positionChanged.connect(self.positionChanged)
self.mediaPlayer.durationChanged.connect(self.durationChanged)
self.mediaPlayer.error.connect(self.handleError)
def adjust_roi(self):
""" provides ROI coordinates / form (circle/rectancle) """
self.RoiAdjust = roi_editor.RoiAdjusting()
self.RoiAdjust.setGeometry(310, 400, 640, 630)
self.RoiAdjust.show()
def openFile(self):
try:
fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie",
QDir.homePath())
self.file_name = fileName
if fileName != '':
self.mediaPlayer.setMedia(
QMediaContent(QUrl.fromLocalFile(fileName)))
self.playButton.setEnabled(True)
except Exception as e:
logging.exception("Exception while opening the videofile:")
def exitCall(self):
sys.exit(app.exec_())
def play(self):
try:
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
except Exception as e:
logging.exception("Exception while playing the videofile:")
def mediaStateChanged(self, state):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.playButton.setIcon(
self.style().standardIcon(QStyle.SP_MediaPause))
else:
self.playButton.setIcon(
self.style().standardIcon(QStyle.SP_MediaPlay))
def positionChanged(self, position):
""" handover the position for creating template """
self.positionSlider.setValue(position)
frame_pos = int(position / 40)
# Handover for creating template at the choosen Videoframe
def durationChanged(self, duration):
self.duration = duration
self.positionSlider.setRange(0, duration)
def setPosition(self, position):
self.mediaPlayer.setPosition(position)
def handleError(self):
self.playButton.setEnabled(False)
self.errorLabel.setText("Error: " + self.mediaPlayer.errorString())
def request_data(self):
return self.file_name, self.duration
Code: Alles auswählen
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtCore import QDir, Qt, QUrl
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import (QApplication, QFileDialog, QHBoxLayout, QLabel,QPushButton, QSizePolicy, QSlider, QStyle, QVBoxLayout, QWidget)
from PyQt5.QtWidgets import QMainWindow,QWidget, QPushButton, QAction
from PyQt5.QtGui import QIcon
from PyQt5 import QtWidgets, QtCore, uic
import sys, logging
import database_sqllite
import cv2
import numpy as np
import mediaplayer
class PaintRoi(QtWidgets.QWidget):
""" a transparent window for adjusting the ROI """
def __init__(self, x_axis, y_axis, circle, rectangle, x_radius, y_radius, red_slider, green_slider, blue_slider, parent=None):
super().__init__(parent)
self.x_axis = int(x_axis)
self.y_axis = int(y_axis)
self.x_radius = int(x_radius)
self.y_radius = int(y_radius)
self.red_slider = int(red_slider)
self.green_slider = int(green_slider)
self.blue_slider = int(blue_slider)
self.circle = circle
self.rectangle = rectangle
self.pen = QtGui.QPen(QtGui.QColor(0,0,0))
self.pen.setWidth(3)
def paintEvent(self, event=None):
""" paints event for adjusting ROI
inputs: x/y axis, choosen form
"""
painter = QtGui.QPainter(self)
painter.setPen(self.pen)
painter.setPen(QColor(self.red_slider, self.green_slider, self.blue_slider))
if self.circle:
painter.drawEllipse(self.x_axis,self.y_axis,self.x_radius,self.y_radius)
else:
painter.drawRect(self.x_axis,self.y_axis,self.x_radius,self.y_radius)
class RoiAdjusting(QtWidgets.QDialog):
""" provides control widgets for Roi Form, Radius, Postion """
def __init__(self,parent=None):
super().__init__(parent)
self.ui = uic.loadUi(r"D:\python_dev\GRAPHIC_ALARMING_MACHINE\GUI\Roi.ui", self)
self.ui.setWindowTitle('Roi adjusting')
self.ui.assetType.addItems(["Cornerbug", "CutIn Vertical", "CutIn Horizontal", "Multipurpose"])
self.ui.setRoi.clicked.connect(self.handover_roi)
self.ui.delRoi.clicked.connect(self.del_roi)
self.ui.save_config.clicked.connect(self.handover_config)
self.ui.delRoi.setStyleSheet("background-color: red")
self.ui.setRoi.setStyleSheet("background-color: green")
self.ui.save_config.setStyleSheet("background-color: green")
self.ui.save_template.clicked.connect(self.clicked_save_template)
self.createLogging()
def handover_roi(self):
self.x_axis = str(self.ui.slider_xaxis.value())
self.y_axis = str(self.ui.slider_yaxis.value())
self.x_radius = str(self.ui.slider_radius_x.value())
self.y_radius = str(self.ui.slider_radius_y.value())
self.slider_red = str(self.ui.red.value())
self.slider_green = str(self.ui.green.value())
self.slider_blue = str(self.ui.blue.value())
self.circle = self.ui.circleCheck.isChecked()
self.rectangle = self.ui.rectangleCheck.isChecked()
temp_x = float(self.ui.slider_xaxis.value()*2.3880)
temp_x = round(temp_x)
temp_y = float(self.ui.slider_yaxis.value()*2.3946)
temp_y = round(temp_y)
temp_x_radius = float(self.ui.slider_radius_x.value()*2.3880)
temp_x_radius = round(temp_x_radius)
temp_y_radius = float(self.ui.slider_radius_y.value()*2.3946)
temp_y_radius = round(temp_y_radius)
# Setting labels with values
self.ui.label_xasis.setText(str(temp_x) + "/ 1920")
self.ui.label_yasis.setText(str(temp_y) + "/ 1080")
self.ui.label_radius_x.setText(str(temp_x_radius) + "/ 1920")
self.ui.label_radius_y.setText(str(temp_y_radius) + "/ 1080")
self.ui.label_red.setText(self.slider_red)
self.ui.label_green.setText(self.slider_green)
self.ui.label_blue.setText(self.slider_blue)
# Transparent Frame for shape adjusting
self.Roi_painting = PaintRoi(self.x_axis, self.y_axis, self.circle, self.rectangle, self.x_radius, self.y_radius, self.slider_red, self.slider_green, self.slider_blue)
self.Roi_painting.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.Roi_painting.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.Roi_painting.setGeometry(1028, 430, 804, 451) # same geometry as videoPreview Mask (1028, 430, 804, )
self.Roi_painting.show()
def del_roi(self):
self.Roi_painting.close()
def clicked_save_template(self):
init_videoplayer = mediaplayer.VideoWindow()
a, b = init_videoplayer.request_data()
print(a)
print(b)
def handover_config(self):
""" handover to Server SQL Database """
# Config Name
configname = self.ui.config_name.text()
# ROI Coordinates
x_axis = float(self.ui.slider_xaxis.value()*2.3880)
x_axis = round(x_axis)
x_axis = int(x_axis /2)
y_axis = float(self.ui.slider_yaxis.value()*2.3946)
y_axis = round(y_axis)
y_axis = int(y_axis/2)
x_radius = float(self.ui.slider_radius_x.value()*2.3880)
x_radius = round(x_radius)
x_radius = int(x_radius / 2)
y_radius = float(self.ui.slider_radius_y.value()*2.3946)
y_radius = round(y_radius)
y_radius = int(y_radius / 2)
videopath = self.ui.videopath_destination.text()
videopath.translate({ord('\n'): None})
check_intervall = self.ui.checkintervall.value()
xentaurix_delay_temp = self.ui.xentaurix_delaytime.time()
xentaurix_delay = xentaurix_delay_temp.toString()
delay_muxing = self.ui.muxing_delaytime.value()
roi_threshold = self.ui.roi_threshold.value()
# Roi Asset Type
self.roi_type = self.ui.assetType.currentText()
# Template
configlist = [configname, self.roi_type, x_axis, y_axis, x_radius, y_radius, check_intervall, roi_threshold, videopath, xentaurix_delay, delay_muxing ]
logbox = self.ui.status_saving
if self.ui.config_name:
instance = database_sqllite.DatabasePerformance()
instance.writing_process_channelconfig(configlist, logbox) # configlist to database
else:
logging.exception("Could not create instance of the class createDatabaseChannelConfig")
def createLogging(self):
logging.basicConfig(
filename= r"D:\python_dev\GRAPHIC_ALARMING_MACHINE\LOGFILES\roi_editor.log",
level= logging.DEBUG,
style= "{",
format= "{asctime} [{levelname:8}] {message}",
datefmt="%d.%m.%Y %H:%M:%S")
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:14
von m.g.o.d
kbr hat geschrieben: Samstag 6. Februar 2021, 17:09
Falls Du 'request_data' for 'openFile' aufrufst, dann bekommst Du diesen Fehler. Den erhältst Du aber auch dann, wenn in 'openFile' ein Fehler auftritt und anschließend 'request_data' aufgerufen wird. Die Anwendung ist so nicht fehlertolerant.
Es ist eine schlechte Idee, zur Runtime dynamisch weitere Instanzattribute anzulegen. Instanzen einer Klasse können so zur Laufzeit unterschiedliches Verhalten zeigen, was für Anwender ein unerwartetes Verhalten ist und die Fehlersuche erschweren kann.
Vermutlich ist das auch die Ursache für Dein Problem: Das Instanzattribut wurde eben doch nicht dynamisch angelegt, obgleich Du annimmst, dass dem so sein sollte.
Ja ich vermute, dass ist der Fehler. Wie lege ich denn das Instanzattribut "dynamisch" an? Ich muss lediglich von Klasse A auf 2 Attribute der Klasse B zugreifen. Ein Zwischenspeichern der Werte in eine Datei erscheint mir hier viel zu umständlich :-/
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:18
von __deets__
So, hier ist doch der Fehler.
Code: Alles auswählen
init_videoplayer = mediaplayer.VideoWindow()
a, b = init_videoplayer.request_data()
Du erzeugst ein Objekt, und rufst gleich request_data auf. Aber die openFile-Methode wird doch zwischendrin gar nicht aufgerufen. Woher soll dann also das Attribut kommen, bzw. ein sinnvoller Wert dafuer?
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:24
von m.g.o.d
__deets__ hat geschrieben: Samstag 6. Februar 2021, 17:18
So, hier ist doch der Fehler.
Code: Alles auswählen
init_videoplayer = mediaplayer.VideoWindow()
a, b = init_videoplayer.request_data()
Du erzeugst ein Objekt, und rufst gleich request_data auf. Aber die openFile-Methode wird doch zwischendrin gar nicht aufgerufen. Woher soll dann also das Attribut kommen, bzw. ein sinnvoller Wert dafuer?
Die Methode:
Code: Alles auswählen
def clicked_save_template(self):
init_videoplayer = mediaplayer.VideoWindow()
a, b = init_videoplayer.request_data()
print(a)
print(b)
wird ja nur dann aufgerufen, wenn der Button gedrückt wird. Deshalb habe ich sie "clicked_save..." genannt und sie ist im Konstruktor mit dem entsprechenden Button connected. Diese Funktion wird von mir nach dem Aufruf der anderen Methoden gedrückt, d.h. wenn self.file_name und self.duration schon mit sinnvollen Werten belegt sind. Aber sie bleiben bei mir im Aufruf dieser Funktion "None".
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:29
von __deets__
Bitte nicht immer ganze Posts die direkt oder kurz davor stehen zitieren.
Und init_videoplayer ist ein neues Objekt, und da ist keine openFile Methode drauf aufgerufen worden. So steht es da im Code, und so sieht das Ergebnis aus. Was sollte das damit zu tun haben, wie clicked_save_template aufgerufen wurde?
Pack die Zeile
init_videoplayer.openFile()
mal dazwischen, und du wirst sehen, dass es anders laeuft.
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:45
von __deets__
Nachtrag: nur weil du eine QAction benutzt, und die auch mal vorher fuer etwas aufgerufen hast, bedeutet das nicht, dass jedes Objekt, das sich auf diese QAction bezieht, schon vor seiner eigenen Entstehung davon etwas mitbekommt. Wie sollte das gehen?
Wenn du das willst, dann hast du das falsche Design. Du musst dann woanders (vielleicht RoiAdjusting, kann ich so nicht abschliessend beurteilen) die openFile-Methode implementieren, *da* die Daten speichern, und das Ergebnis deinem VideoWindow per Konstruktor mitgeben.
Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:46
von m.g.o.d
Achso. Okay dann lag da ein Denkfehler meinerseits zugrunde. Vielen Dank! Allerdings bleibt mir die Frage, wie ich an die Duration komme. Weil die sich erst in der Laufzeit ergibt. Also wenn ich das jetzt richtig verstanden habe, dann bilde ich eine einzelne Instanz auf die Klasse und würde über die dann auch auf die veränderten Variablen richtig zugreifen? Es macht Sinn, das eine zweite Instanz natürlich nicht die Änderungen beeinhaltet

Re: self statement
Verfasst: Samstag 6. Februar 2021, 17:51
von __deets__
Wenn eine Eigenschaft sich erst spaeter im Programmablauf ergibt, dann waere zB ein Weg damit umzugenen ein Signal zu schicken in dem Moment, in dem man die Eigenschaft erhaelt.
Re: self statement
Verfasst: Montag 8. Februar 2021, 17:14
von m.g.o.d
Vielen herzlichen Dank für all eure Anmerkungen, die mir sehr geholfen haben.