jb-alvarado
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Hallo Allerseits, ich habe diesen Code hier: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 | #!/usr/bin/env python3
import os
import wx
from subprocess import Popen, STDOUT, PIPE
from threading import Thread
class analysing_thread(Thread):
def __init__(self, parent, in_file, duration):
Thread.__init__(self)
self._parent = parent
self.in_file = in_file
self.duration = duration
self.black_frame_list = []
self.process = None
self.is_running = True
def run(self):
cmd = [
'ffmpeg', '-hide_banner', '-i', self.in_file,
'-an', '-vf', 'blackdetect=d=0.01:pix_th=0.22', '-f', 'null', '-'
]
self.process = Popen(
cmd, stdout=PIPE, stderr=STDOUT,
universal_newlines=True)
for line in self.process.stdout:
if 'blackdetect' in line:
black_start = line.split()[3].split(':')[1]
self.black_frame_list.append(black_start)
if 'frame=' in line:
frame = int(line.split()[1])
count = frame * 100 / self.duration
evt_single = CountEventSingle(SINGLE_EVT, -1, count)
wx.PostEvent(self._parent, evt_single)
print(self.black_frame_list)
def stop(self):
if self.process is not None:
self.process.terminate()
self.is_running = False
class CountEventSingle(wx.PyCommandEvent):
def __init__(self, etype, eid, value=None):
wx.PyCommandEvent.__init__(self, etype, eid)
self._value = value
def GetValue(self):
return self._value
class AnalysingDialog(wx.Dialog):
def __init__(self, title, x, y):
super(AnalysingDialog, self).__init__(None, title=title, size=(x, y))
self.Bind(SINGLE_EVT_COUNT, self.OnSingleCount)
self.x = x
self.input = "/path/to/input.mp4"
self.InitModal()
self.Centre()
analyse = analysing_thread(
self, self.input,
3555)
analyse.start()
def OnSingleCount(self, evt):
val = evt.GetValue()
self.gauge_single.SetValue(val)
if val == 100:
self.Destroy()
def InitModal(self):
panel = wx.Panel(self)
grid = wx.GridSizer(1, 2, 2)
message = (
"Search in:\n" +
os.path.basename(self.input) +
"\nfor black frames..."
)
text = wx.StaticText(panel, -1, message)
self.gauge_single = wx.Gauge(panel, range=100, size=(self.x - 35, 15))
grid.Add(text, 0, flag=wx.ALL, border=10)
grid.Add(self.gauge_single, 0, flag=wx.ALL, border=10)
panel.SetSizer(grid)
if __name__ == "__main__":
SINGLE_EVT = wx.NewEventType()
SINGLE_EVT_COUNT = wx.PyEventBinder(SINGLE_EVT, 1)
app = wx.App(False)
AnalysingDialog(
"Analysing", 600, 150).Show()
app.MainLoop()
|
Von dieser Datei würde ich gerne analysing_thread in mein Hauptprogramm importieren. Grundsätzlich geht das auch, allerdings müsste die Klasse mit: | SINGLE_EVT = wx.NewEventType()
SINGLE_EVT_COUNT = wx.PyEventBinder(SINGLE_EVT, 1)
|
aus meinem Hauptprogramm kommunizieren und das tut sie nicht... Was ich gelesen habe, geht das wohl nicht so ohne weiteres und diese beiden Variablen müssten eigentlich in der Datei vom analysing_thread global vorhanden sein. Sind sie dort drin, funktioniert jedoch die Kommunikation nicht. Was kann man denn in solchen Fällen tun?
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4578
Wohnort: Berlin
|
@jb-alvarado: SINGLE_EVT und SINGLE_EVT_COUNT müssen bedingungslos auf Modulebene definiert werden. Dann sollte es auch funktionieren wenn man es richtig macht. Was machst Du denn? Die Klasse sollte übrigens AnalysingThread heissen. Bei Methodennamen kann man den Style Guide ja mal grosszügiger auslegen wenn das verwendete Rahmenwerk andere Konventionen verwendet (weil die Namen aus einer anderen Programmiersprache kommen), aber Klassennamen werden auch bei wxWidgets nicht in Kleinbuchstaben mit Unterstrichen geschrieben.
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Marc_BlackJack_Rintsch schrieb: @jb-alvarado: SINGLE_EVT und SINGLE_EVT_COUNT müssen bedingungslos auf Modulebene definiert werden. Dann sollte es auch funktionieren wenn man es richtig macht.
Modulebene heißt in meinem Fall in der Datei die analysing_thread enthält? Genau das geht nicht, zumindest nicht so wie ich es versucht hatte.
Was machst Du denn?
Ich habe eine Situation die ähnlich hier beschrieben ist:
https://wiki.wxpython.org/Non-Blocking%20Gui Ich habe diesen Thread, der eine Datei Analysiert. Das kann unter Umständen bis zu 30 Minuten dauern, daher der Thread. Den Output von dem Prozess, in dem Thread, soll eine Progressbar in einem Modal Fenster ansteuern. Das Modal Fenster wird aber über mein Hauptprogramm aufgerufen. Ich kann auch einfach den Analyse Thread in mein Programm integrieren, aber der Code wird halt mit der Zeit unübersichtlicher, daher würde ich gerne Teile auslagern. Importiert hatte ich die Klasse mit: | from analyse import analysing_thread
|
Über SINGLE_EVT und SINGLE_EVT_COUNT kommuniziert die Progressbar mit dem Thread.
Die Klasse sollte übrigens AnalysingThread heissen. Bei Methodennamen kann man den Style Guide ja mal grosszügiger auslegen wenn das verwendete Rahmenwerk andere Konventionen verwendet (weil die Namen aus einer anderen Programmiersprache kommen), aber Klassennamen werden auch bei wxWidgets nicht in Kleinbuchstaben mit Unterstrichen geschrieben.
Danke! Du hattest mich ja schon mal drauf hingewiesen ☺. Hatte mir den Styleguid auch angeschaut, aber das wohl vergessen. Hatte in Erinnerung, dass die neuere Konvention generell Kleinbuchstaben und Unterstriche sind. Werde das gerne berücksichtigen.
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4578
Wohnort: Berlin
|
@jb-alvarado: Ja, Modulebene heisst in dem Modul in dem analyze_thread definiert ist, aber da eben auch wenn man das Modul importiert, und nicht nur wenn man es als Programm ausführt. Jetzt hast Du aber immer noch nicht verraten was „Geht nicht“ denn nun konkret bedeutet, denn das sollte gehen. Explodiert der Toaster? Stirbt die Katze? Oder gibt es vielleicht eine Fehlermeldung? Wenn ja, welche und bei welchem Code? Wir brauchen mehr Informationen! ☺
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Marc_BlackJack_Rintsch schrieb: @jb-alvarado: Ja, Modulebene heisst in dem Modul in dem analyze_thread definiert ist, aber da eben auch wenn man das Modul importiert, und nicht nur wenn man es als Programm ausführt.
Ok, aber ich kann die beiden Variablen ja nicht in die zu importierende Klasse mit rein packen.
Jetzt hast Du aber immer noch nicht verraten was „Geht nicht“ denn nun konkret bedeutet, denn das sollte gehen. Explodiert der Toaster? Stirbt die Katze? Oder gibt es vielleicht eine Fehlermeldung? Wenn ja, welche und bei welchem Code? Wir brauchen mehr Informationen! ☺
Sorry ☺. Also die Kommunikation zwischen den Analyse Thread und der Progressbar funktioniert nicht, sprich der Balken bewegt sich nicht. Wenn ich Klasse direkt in meinem Hauptprogramm drin habe, geht es. Wenn ich sie importiere und die beiden Variablen separat über der Kasse in der gleichen Datei habe, kommt zwar keine Fehlermeldung aber die Progressbar aktualisiert sich eben nicht. Wenn ich die beiden Variablen in meinem Hauptprogramm habe, meckert der Analyse Thread dass er die Variablen nicht findet. Habe mal zwei Dateien vorbereitet: Hauptprogramm: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 | #!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import wx
from subprocess import Popen, STDOUT, PIPE
from threading import Thread
from analyse import AnalysingThread
class CountEventSingle(wx.PyCommandEvent):
def __init__(self, etype, eid, value=None):
wx.PyCommandEvent.__init__(self, etype, eid)
self._value = value
def GetValue(self):
return self._value
class AnalysingDialog(wx.Dialog):
def __init__(self, title, x, y):
super(AnalysingDialog, self).__init__(None, title=title, size=(x, y))
self.Bind(SINGLE_EVT_COUNT, self.OnSingleCount)
self.x = x
self.input = (
"videodatei_mit_schwarz_frames.mp4")
self.InitModal()
self.Centre()
analyse = AnalysingThread(
self, self.input,
3555)
analyse.start()
def OnSingleCount(self, evt):
val = evt.GetValue()
self.gauge_single.SetValue(val)
if val == 100:
self.Destroy()
def InitModal(self):
panel = wx.Panel(self)
grid = wx.GridSizer(1, 2, 2)
message = (
"Search in:\n" +
os.path.basename(self.input) +
"\nfor black frames..."
)
text = wx.StaticText(panel, -1, message)
self.gauge_single = wx.Gauge(panel, range=100, size=(self.x - 35, 15))
grid.Add(text, 0, flag=wx.ALL, border=10)
grid.Add(self.gauge_single, 0, flag=wx.ALL, border=10)
panel.SetSizer(grid)
if __name__ == "__main__":
SINGLE_EVT = wx.NewEventType()
SINGLE_EVT_COUNT = wx.PyEventBinder(SINGLE_EVT, 1)
app = wx.App(False)
AnalysingDialog(
"Analysing", 600, 150).Show()
app.MainLoop()
|
Modul: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 | #!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import wx
from subprocess import Popen, STDOUT, PIPE
from threading import Thread
SINGLE_EVT = wx.NewEventType()
SINGLE_EVT_COUNT = wx.PyEventBinder(SINGLE_EVT, 1)
class AnalysingThread(Thread):
def __init__(self, parent, in_file, duration):
Thread.__init__(self)
self._parent = parent
self.in_file = in_file
self.duration = duration
self.black_frame_list = []
self.process = None
self.is_running = True
def run(self):
cmd = [
'ffmpeg', '-hide_banner', '-i', self.in_file,
'-an', '-vf', 'blackdetect=d=0.01:pix_th=0.22', '-f', 'null', '-'
]
self.process = Popen(
cmd, stdout=PIPE, stderr=STDOUT,
universal_newlines=True)
for line in self.process.stdout:
if 'blackdetect' in line:
black_start = line.split()[3].split(':')[1]
self.black_frame_list.append(black_start)
if 'frame=' in line:
frame = int(line.split()[1])
count = frame * 100 / self.duration
evt_single = CountEventSingle(SINGLE_EVT, -1, count)
wx.PostEvent(self._parent, evt_single)
print(self.black_frame_list)
def stop(self):
if self.process is not None:
self.process.terminate()
self.is_running = False
|
Vom Modul würde ich nun gerne AnalysingThread importierten. Beim vorbereiten ist mir aber noch der Fehler aufgefallen, dass das Modul auch CountEventSingle bräuchte um richtig zu funktionieren. Das ist noch ein zusätzliches Problem. Das müsste ich vielleicht auch noch auslagern und in beiden Dateien importieren.
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4578
Wohnort: Berlin
|
@jb-alvarado: Wieso solltest Du die Variablen auch in die Klasse packen? Wie gesagt, im Modul reicht. In Deinen beiden Modulen hast Du SINGLE_EVT und SINGLE_EVT_COUNT in beiden Modulen definiert. Das analyse -Modul sendet das im analyse -Modul definierte Event, das Hauptmodul erwartet aber das im Hauptmodul definierte Event. Das die Werte innerhalb der Module an die gleichen Namen gebunden sind, ändert nichts daran, das es verschiedene Objekte sind. Du musst in Hauptprogramm die Event-Objekte aus dem analyse -Modul verwenden. CountEventSingle wird im Hauptprogramm definiert, aber nicht verwendet. Das gehört in analyse und muss auch nicht ins Hauptprogramm importiert werden wenn es dort nicht verwendet wird.
Die Importe solltest Du dann auch mal aufräumen — das Hauptprogramm importiert einiges was überhaupt nicht verwendet wird. AnalysingThread.is_running ist komisch. Das wird auf True gesetzt bevor da tatsächlich etwas läuft, und wird nur auf False gesetzt wenn stop() aufgerufen wird, aber zum Beispiel nicht wenn run() normal bis zum Ende durch läuft. Ich bin mir auch ziemlich sicher, dass die run() -Methode mit dem stop() nicht richtig/sauber umgehen kann. Ausserdem wird der Rückgabecode von dem Prozess nie abgerufen was mindestens temporär zu einem Zombie führt und nicht sauber ist. Auswerten möchte man den vielleicht auch.
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Marc_BlackJack_Rintsch schrieb: @jb-alvarado: Wieso solltest Du die Variablen auch in die Klasse packen? Wie gesagt, im Modul reicht. In Deinen beiden Modulen hast Du SINGLE_EVT und SINGLE_EVT_COUNT in beiden Modulen definiert. Das analyse -Modul sendet das im analyse -Modul definierte Event, das Hauptmodul erwartet aber das im Hauptmodul definierte Event. Das die Werte innerhalb der Module an die gleichen Namen gebunden sind, ändert nichts daran, das es verschiedene Objekte sind. Du musst in Hauptprogramm die Event-Objekte aus dem analyse -Modul verwenden.
Ich hätte mir etwas mehr Zeit nehmen sollen, für den Testcode. Leider ist der wirklich ziemlich überladen. Zumindest habe ich jetzt verstanden was das Problem ist, und wie ich es lösen kann! Ich importiere mir das ganze Modul, nicht nur die Klasse und kann dann so drauf zugreifen: | import analyse
...
self.Bind(analyse.SINGLE_EVT_COUNT, self.OnSingleCount)
...
search_black = analyse.AnalysingThread(
self, self.input, 3555)
|
CountEventSingle wird im Hauptprogramm definiert, aber nicht verwendet. Das gehört in analyse und muss auch nicht ins Hauptprogramm importiert werden wenn es dort nicht verwendet wird.
Verstanden.
Die Importe solltest Du dann auch mal aufräumen — das Hauptprogramm importiert einiges was überhaupt nicht verwendet wird.
Ja, wie gesagt, da war ich heute Mittag zu schnell, wollte noch schnell vor Feierabend was Posten. ☺ AnalysingThread.is_running ist komisch. Das wird auf True gesetzt bevor da tatsächlich etwas läuft, und wird nur auf False gesetzt wenn stop() aufgerufen wird, aber zum Beispiel nicht wenn run() normal bis zum Ende durch läuft. Ich bin mir auch ziemlich sicher, dass die run() -Methode mit dem stop() nicht richtig/sauber umgehen kann.
AnalysingThread.is_running stammt noch aus einem Encoding Thread, meines original Programms. Ist vielleicht nicht ganz schön, aber es macht was es soll, zumindest ist mir nichts gegenteiliges aufgefallen: In einem Encoding Thread wird eine Liste von Files per Schleife abgearbeitet. Wenn EncodingThread.stop() aufgerufen wird, wird der Prozess im Thred gestoppt und zusätzlich wird self.is_running = False gesetzt. Damit stelle ich sicher, dass nach dem Abbruch die Schleife auch beendet wird: | for item in self.compress_list:
if item[0].IsChecked() and self.is_running:
...
|
Sollte mir vielleicht dafür einen andere Namen überlegen.
Ausserdem wird der Rückgabecode von dem Prozess nie abgerufen was mindestens temporär zu einem Zombie führt und nicht sauber ist. Auswerten möchte man den vielleicht auch.
Müsste ich mir mal genauer anschauen - habe ich mir noch keine Gedanken drüber gemacht.
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4578
Wohnort: Berlin
|
@jb-alvarado: Das is_running in der __init__() gesetzt wird, halte ich aber weiterhin für komisch, denn zwischen der Methode und dem tatsächlichen Aufruf von run() kann ja beliebig viel Zeit vergehen in der nichts läuft. is_running würde man da eher am Anfang der run() -Methode auf True setzen. In der __init__() würde man eher so etwas wie stop_requested = False erwarten, was in stop() auf True gesetzt wird und in run() getestet werden kann.
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Ja da hast du recht, ist ein guter Einwand - Danke!
|