Skip to content

Commit e51c46b

Browse files
author
kanehekili
committed
V3.0.5 GTK2 Theming
1 parent f42250e commit e51c46b

File tree

8 files changed

+2300
-103
lines changed

8 files changed

+2300
-103
lines changed

README.md

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# VideoCut
22
Version 3.0.4
33

4-
![Download](https://github.com/kanehekili/VideoCut/releases/download/3.0.4/videocut3.0.4.tar)
4+
![Download](https://github.com/kanehekili/VideoCut/releases/download/3.0.5/videocut3.0.5.tar)
55

66
MP2/MP4 Cutter for Linux on base of mpv and ffmpeg. Cutting is lossless, the target file will not be reencoded.
77

@@ -25,19 +25,11 @@ The current version is written in python3 and uses the qt6 widget kit.
2525
* optional:(legacy) OpenCV 2.4 up to OpenCV 4.x (must be build with ffmpeg - with all its dependencies)
2626

2727
#### Set GTK Theme for this QT application
28-
If you are running a DE with GTK/Gnome (as opposed to LXQT or KDE) you need to tweak your system:
29-
* Arch users may have to install qt6gtk2 from AUR
30-
* Ubuntu/Debian users will not get a uniform look GTK theme for QT anymore. (qt5 was the last)
28+
If you are running a DE with GTK/Gnome (as opposed to LXQT or KDE) there is no way to tweak anymore:
29+
* GKT2 to QT5/QT6 libraries do not work
30+
* QT_QPA_PLATFORMTHEME should not be set in non QT systems
31+
* Videocut will set the "fusion" mode - any new ideas are welcome.
3132

32-
`sudo nano /etc/environment`
33-
34-
add the follwing line:
35-
36-
`QT_QPA_PLATFORMTHEME=gtk2` or
37-
38-
`QT_QPA_PLATFORMTHEME=qt6c` (if qt6c is installed)
39-
40-
and logout/login (or reboot)
4133

4234
### Features
4335
Cuts an mpg file into parts and joins them afterwards. All commands can be reached via the toolbar.
@@ -176,6 +168,9 @@ Copy the .desktop file and change the exec line to "Exec= python3 .../VideoCut.p
176168
Opencv will not be displaying subtitles nor frametypes.
177169

178170
### Changes
171+
26.07.25
172+
* Adapt to the new reality that GTK2 will not render QT6 widgets anymore. Upgraded to mpv 1.0.8
173+
179174
13.01.25
180175
* Display wait time on remote files. Changed slider behavior according to [tex](https://github.com/tex) - Thank you.
181176

build/build.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
version=3.0.4
1+
version=3.0.5
22
ubu0=jammy
33
ubu1=noble
44
ubu2=oracular
5-
pkgrelease=2
5+
pkgrelease=1

src/FFMPEGTools.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,25 @@ def compressor(self,source, dest):
189189
def namer(self,name):
190190
return name+".gz"
191191

192+
def is_nvidia_gpu_active(self):
193+
try:
194+
# Try to detect active GPU via `nvidia-smi` (most reliable if installed)
195+
subprocess.run(["nvidia-smi"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
196+
return True
197+
except Exception:
198+
pass
199+
200+
try:
201+
# Fallback: parse `lspci` output (less accurate but doesn't need NVIDIA tools)
202+
result = subprocess.run(["lspci"], stdout=subprocess.PIPE, text=True)
203+
return "NVIDIA" in result.stdout
204+
except Exception:
205+
return False
206+
207+
def currentDesktop(self):
208+
return os.environ.get("XDG_CURRENT_DESKTOP", "")
209+
210+
192211
class ConfigAccessor():
193212
__SECTION = "default"
194213
homeDir = OSTools().getHomeDirectory()

src/MpvPlayer.py

Lines changed: 12 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020

2121
from lib.mpv import (MPV,MpvGlGetProcAddressFn,MpvRenderContext)
2222

23-
24-
2523
Log=FFMPEGTools.Log
2624

2725
def get_proc_addr(_, name):
@@ -32,7 +30,6 @@ def get_proc_addr(_, name):
3230
addr = int(glctx.getProcAddress(qb))
3331
return addr
3432

35-
3633
class VideoWidget(QtWidgets.QFrame):
3734
""" Sized frame for mpv """
3835
trigger = pyqtSignal(float,float,float)
@@ -53,7 +50,7 @@ def updateUI(self,frameNumber,framecount,timeinfo):
5350

5451
class VideoGLWidget(QOpenGLWidget):
5552
trigger = pyqtSignal(float,float,float)
56-
53+
onMVPGLUpdate = pyqtSignal()
5754
def __init__(self, parent,mpv):
5855
QOpenGLWidget.__init__(self,parent)
5956
#setSurfaceType(QWindow.OpenGLSurface)
@@ -64,7 +61,8 @@ def __init__(self, parent,mpv):
6461
self.get_proc_addr_c = MpvGlGetProcAddressFn(get_proc_addr)
6562
self._defaultHeight = 518 #ratio 16:9
6663
self._defaultWidth = 921
67-
64+
self.onMVPGLUpdate.connect(self.__update)
65+
6866
def initializeGL(self):
6967
params = {'get_proc_address': self.get_proc_addr_c}
7068
self.ctx = MpvRenderContext(self.mpv,
@@ -90,7 +88,6 @@ def paintGL(self):
9088
'fbo': self.defaultFramebufferObject()}
9189
self.ctx.render(flip_y=True, opengl_fbo=opengl_fbo)
9290

93-
9491
@pyqtSlot()
9592
def __update(self):
9693
if self.window().isMinimized():
@@ -104,16 +101,15 @@ def __update(self):
104101
def on_update(self, ctx=None):
105102
# __update method should run on the thread that creates the
106103
# OpenGLContext, which in general is the main thread.
107-
# QMetaObject.invokeMethod can do this trick.
108-
QMetaObject.invokeMethod(self, '__update')
104+
self.onMVPGLUpdate.emit()
109105

110106
def sizeHint(self):
111107
return QtCore.QSize(self._defaultWidth, self._defaultHeight)
112108

109+
#Called by MpvPlayer#_onTimePos
113110
def updateUI(self,frameNumber,framecount,timeinfo):
114111
self.trigger.emit(frameNumber,framecount,timeinfo)
115112

116-
117113
class MpvPlayer():
118114
ERR_IDS=["No video or audio streams selected.","Failed to recognize file format."]
119115
def __init__(self):
@@ -130,6 +126,7 @@ def initPlayer(self,container):
130126
return self.mediaPlayer
131127

132128
def initGLPlayer(self):
129+
#stripes when using file explorer (initial call without file)
133130
kwArgs=self.__baseMpvArgs()
134131
kwArgs["vo"]="libmpv"
135132
self.mediaPlayer = MPV(**kwArgs)
@@ -191,7 +188,9 @@ def __baseMpvArgs(self):
191188
#"demuxer_cache_wait" : 'no', #if yes remote files take too long...
192189
#"demuxer_max_bytes " : '10000MiB',
193190
#"demuxer_backward_playback_step" : 180,
194-
"volume" : 100
191+
"volume" : 100,
192+
#"gpu_context":'x11egl', # Or explicitly specify 'egl', 'x11', etc.
193+
"opengl_early_flush":'yes'
195194
}
196195

197196
def _resetState(self):
@@ -228,35 +227,8 @@ def _hookEvents(self):
228227
self.mediaPlayer.observe_property("eof-reached",self._onPlayEnd)
229228
#mostly wrong: self.mediaPlayer.observe_property("estimated-frame-count",self._onFramecount)
230229
self.mediaPlayer.observe_property("video-frame-info",self._onFrameInfo)
231-
232-
'''
233-
TEst with jasegs event handler...
234-
def open_withEvent(self,filePath):
235-
print("open:",filePath)
236-
try:
237-
self._resetState()
238-
self.mediaPlayer.close()
239-
self.mediaPlayer.loadfile(filePath)
240-
#Try to pin down a potential file error
241-
@self.mediaPlayer.event_callback('end_file')
242-
def eofHandler(evt):
243-
self._superviseEndFileEvent(evt)
244-
self._getReady()
245-
eofHandler.unregister_mpv_events()
246-
except Exception as ex:
247-
Log.logException("Open mpv file")
248-
print(ex)
249-
250-
def _superviseEndFileEvent(self,evt):
251-
if evt['event']['reason'] != MpvEventEndFile.REDIRECT or evt['event']['reason'] != MpvEventEndFile.RESTARTED:
252-
self.lastError=self.ERR_IDS[0]
253-
with self.seekLock:
254-
print('check notify:',evt)
255-
self.seekLock.notify()
256230

257-
return True
258-
'''
259-
231+
260232
def open(self,filePath):
261233
try:
262234
#Very verbose: self.mediaPlayer.register_event_callback(self._oncallback)
@@ -471,7 +443,6 @@ def _passLog(self,loglevel, component, message):
471443
msg='{}: {}'.format(component, message)
472444
Log.error(">%s",msg)
473445
with self.seekLock:
474-
#TODO: file not recognized seems not be working"
475446
if message in self.ERR_IDS:
476447
self.lastError=message
477448
self.seekLock.notifyAll()
@@ -586,13 +557,13 @@ def _createGLWidget(self,parent):
586557
self.player= MpvPlayer()
587558
mpv=self.player.initGLPlayer()
588559
self.mpvWidget=VideoGLWidget(parent,mpv)
589-
self.player.connectTo(self.mpvWidget.updateUI)
560+
self.player.connectTo(self.mpvWidget.updateUI) #GL connect
590561
return self.mpvWidget
591562

592563
def __setupPlayer(self):
593564
if self.showGL:
594565
return #already happened
595-
if self.player:
566+
if self.player: #Non GL connect
596567
self.player.close()
597568
self.player= MpvPlayer()
598569
self.player.initPlayer(self.mpvWidget)

src/SimplePlayer.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,28 @@
1616
from PyQt6.QtCore import QByteArray, pyqtSignal, pyqtSlot, Qt
1717
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
1818
from lib.mpv import MPV, MpvGlGetProcAddressFn, MpvRenderContext
19-
import ctypes,sys
20-
19+
import sys
2120

2221
def get_process_address(_, name):
2322
glctx = QOpenGLContext.currentContext()
2423
address = int(glctx.getProcAddress(QByteArray(name)))
2524
#return ctypes.cast(address, ctypes.c_void_p).value
2625
return address
2726

27+
def getMPV():
28+
kwArgs={"log_handler":print,"log-file":"gl.txt","loglevel" : 'info',"pause":False,"audio" : "1","keep_open" : "always","vo":"libmpv"}
29+
return MPV(**kwArgs)
30+
2831
class Player(QOpenGLWidget):
2932
onUpdate = pyqtSignal()
3033
initialized = pyqtSignal()
3134

3235
def __init__(self, parent) -> None:
3336
super().__init__(parent)
34-
kwArgs={"log_handler":print,"log-file":"gl.txt","loglevel" : 'trace',"audio" : "1","keep_open" : "always","vo":"libmpv"}
35-
self.mpv = MPV(**kwArgs)
37+
self.mpv = getMPV()
3638
self.ctx = None
3739
self._proc_addr_wrapper = MpvGlGetProcAddressFn(get_process_address)
3840
self.onUpdate.connect(self.do_update)
39-
self.c = 0
40-
4141
self.setUpdateBehavior(QOpenGLWidget.UpdateBehavior.PartialUpdate)
4242

4343
def initializeGL(self) -> None:
@@ -53,29 +53,11 @@ def initializeGL(self) -> None:
5353
self.initialized.emit()
5454

5555
def paintGL(self) -> None:
56-
if self.c > 100:
57-
self.c = 0
58-
else:
59-
self.c += 1
6056
rect = self.rect()
6157
if self.ctx:
6258
fbo = self.defaultFramebufferObject()
6359
self.ctx.render(flip_y=True, opengl_fbo={'w': rect.width(), 'h': rect.height(), 'fbo': fbo})
64-
65-
66-
# Draw on bottom center
67-
'''
68-
p = QPainter(self)
69-
width = rect.size().width()
70-
height = rect.size().height()
71-
label_rect = rect.adjusted((width/2)-75, height-100, -(width/2)+75, -50)
72-
p.setPen(QColor("#ffffff"))
73-
74-
text = '.'*int((self.c/20)) + "playing" + '.'*int((self.c/20))
75-
p.fillRect(label_rect, QBrush(QColor("#ff00ff"), Qt.BrushStyle.FDiagPattern))
76-
p.drawText(label_rect, Qt.AlignmentFlag.AlignCenter, text)
77-
'''
78-
60+
7961
def do_update(self):
8062
self.update()
8163

@@ -107,25 +89,35 @@ def __init__(self, qapp,aPath=None):
10789

10890
def initUI(self):
10991
self.player = Player(self)
92+
#self.player = VideoGLWidget(self,getMPV())
11093

11194
self.uiLabel= QtWidgets.QLabel(self)
11295
self.uiLabel.setText("Player demo")
11396
self.uiPlayButton = QtWidgets.QPushButton(" Play")
97+
self.uiPlayButton.clicked.connect(self.execPlayButton)
98+
self.uiStopButton = QtWidgets.QPushButton(" Stop")
99+
self.uiStopButton.clicked.connect(self.execStopButton)
100+
114101

115102
box = self._makeLayout()
116103
wid = QtWidgets.QWidget(self)
117104
self.setCentralWidget(wid)
118105
wid.setLayout(box)
119106
self.resize(500, 600)
120107
#self.player.initialized.connect(lambda: [self.player.play("/media/disk1/UHD/gog.mp4")])
121-
self.player.initialized.connect(lambda: [self.player.mpv.loadfile("icons/film-clapper.png")])
108+
#self.player.initialized.connect(lambda: [self.player.mpv.loadfile("icons/film-clapper.png")])
109+
self.player.initialized.connect(self._initIcon)
110+
111+
def _initIcon(self):
112+
self.player.mpv.loadfile("icons/film-clapper.png")
122113

123114
def _makeLayout(self):
124115
mainBox = QtWidgets.QVBoxLayout() # for all
125116
btn1Box = QtWidgets.QHBoxLayout() # test widgets
126117
btn1Box.setSpacing(20)
127118
btn1Box.addWidget(self.uiLabel)
128119
btn1Box.addWidget(self.uiPlayButton)
120+
btn1Box.addWidget(self.uiStopButton)
129121

130122
mainBox.addWidget(self.player)
131123
mainBox.addLayout(btn1Box)
@@ -138,6 +130,14 @@ def centerWindow(self):
138130
centerPoint = self.screen().availableGeometry().center()
139131
frameGm.moveCenter(centerPoint)
140132
self.move(frameGm.topLeft())
133+
134+
def execPlayButton(self):
135+
#self.player.play("/media/disk1/UHD/gog.mp4")
136+
self.player.play("/media/disk1/makemkv/title_t00.mkv")
137+
138+
def execStopButton(self):
139+
playing = self.player.mpv.pause #property
140+
self.player.mpv.pause=not playing
141141

142142
if __name__ == '__main__':
143143

0 commit comments

Comments
 (0)