Киви: Как создать прозрачный фон / холст, чтобы кнопки словно плавали над рабочим столом - PullRequest
0 голосов
/ 05 августа 2020

Я пытаюсь создать приложение для воспроизведения видео, которое будет запускаться на одноплатном компьютере (в частности, OrangePi One / P C с использованием Armbian). У меня возникли проблемы с созданием плавного видео с помощью ffpyplayer, видеоплеера, используемого kivy.

Из-за этого я решил просто запустить приложение, которое генерирует кнопки управления перед видео и попытаться сделать фон в приложении, чтобы были видны и кнопки видео, и кнопки kivy. как рекомендовано в связанном сообщении: Kivy Video Player не работает на Raspberry 3B +

Я видел подобное в: https://github.com/kivy/kivy/pull/5252 Я тестирую программы на компьютере windows перед загрузкой на одноплатные компьютеры, и мне не удалось успешно создать пустой фон. В основном я пытался изменить файл .kv, однако мне не удалось найти какие-либо настройки, которые можно отрегулировать для получения желаемого результата.

Для простоты я загружаю более короткий код, который изложен в точном так же, как мой основной код:

Код Kivy: my.kv

#:kivy 1.0 
<MyGrid>

    background_color: 0, 0, 0, 0 # only creates a black colour
    #opacity: 0.5 #just affects the widgets not the background

    canvas.before:
        Color:
            rgba: 0, 0, 0, 1
        Rectangle:
            pos: self.pos
            size: self.size


    #variable name: ID
    name: name #declare global variables
    email: email 

    GridLayout:
        cols:1
        size: root.width-200, root.height-200 #make the widget fit the screen and then minus a border from it
        pos: 100, 100 #offset the position to compensate for the boarder

        GridLayout: 
            cols: 2

            Label:
                text: "Name: "

            TextInput:
                id: name
                multiline:False

            Label:
                text: "email: "

            TextInput:
                id: email 
                multiline: False

        Button:
            text: "submit"
            on_press: root.btn() #if there was an event. ie button was pressed

Python код:

    import kivy
    from kivy.app import App 
    from kivy.uix.label import Label
    from kivy.uix.gridlayout import GridLayout
    from kivy.uix.textinput import TextInput
    from kivy.network.urlrequest import UrlRequest
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.button import Button
    from kivy.uix.widget import Widget
    from kivy.properties import ObjectProperty
    from kivy.core.window import Window
    
    class MyGrid(Widget): #creates apps layout and contents
            #declaire global variables to pass to and from .kv file. note the variables names must be the same between the 2 files 
            name = ObjectProperty(None) #initialise as none and then after reading the .kv file it will populate it 
            email = ObjectProperty(None)
            
            def btn(self): #function btn which occurs. it needs to be in here 
                print ("name: ", self.name.text, " email: ", self.email.text)
                self.name.text = ""
                self.email.text = ""
    
    class MyApp(App): #creates the app
        def build(self):
            return MyGrid()
    
    if __name__ == '__main__': #runs the app 
      

  MyApp().run()

На прилагаемой фотографии показано приложение, работающее с видео с использованием ffpyplayer и является более или менее конечной целью. Задача состоит в том, чтобы зеленый цвет был рабочим столом. Пример изображения .

любое руководство будет очень признательно!

1 Ответ

0 голосов
/ 16 августа 2020

Просто чтобы дать ответ на мой вопрос на случай, если у кого-то возникнет аналогичная проблема.

Использование Kivy оказалось не для меня жизнеспособным по 2 причинам:

  1. Хотя можно сделать приложение kivy прозрачным, как в примере https://github.com/kivy/kivy/pull/5252, для этого требуется изображение шаблона. Поскольку я хотел, чтобы мое приложение работало на экранах разного размера, и я ничего не знаю о создании изображений, я решил не использовать этот метод.

  2. Сделать приложение кнопки маленьким, чтобы оно покрыло часть видео работала хорошо. Это стало проблемой: c, поскольку видео запускается с использованием другой программы, что означает, что видео будет открываться перед приложением, и я потеряю контроль, пока не нажму на приложение специально. Это можно преодолеть на Windows, запустив видео, а затем отложив создание кнопок или свернув и повторно открыв приложение после создания экрана видео. К сожалению, этот метод не переносился на одноплатный компьютер, так как для максимальной производительности воспроизведения видео я не использую среду рабочего стола, только систему X Window (X11) и даже с оконным менеджером basi c я столкнулся с множеством трудностей минимизация и максимизация windows.

Мое решение состояло в том, чтобы перейти на Tkinter, поскольку он имеет возможность заставить окно быть впереди и может быть сделано прозрачным. Ниже приведен эквивалентный код в Tkinter, хотя он не завершен, он имеет функциональность, о которой я упоминал в исходном вопросе:

from functools import partial
import tkinter as tk
import threading
import time
import vlc
global videoscreen_button_num
videoscreen_button_num = 0

global button_timer
global show_button
button_timer = 0
show_button = False


class video_window:
    def __init__(self, master, channel): #create the windows
    self.master = master
    self.frame = tk.Frame(self.master)

    reset_encoder = tk.Button(self.frame, text = 'Reset Encoder', width = 25, command = self.close_windows) #create buttons to control the video
    reset_video = tk.Button(self.frame, text = 'Refresh Video', width = 25, command = self.close_windows)
    return_button = tk.Button(self.frame, text = 'Return to Main Menu', width = 25, command = self.close_windows)

    return_button.grid(row=0, column=0)
    reset_video.grid(row=0, column=1)
    reset_encoder.grid(row=0, column=2)

    unselected_colour = "SteelBlue1" 
    selected_colour = "RoyalBlue1"

    return_button.config(background = selected_colour)
    reset_video.config(background = unselected_colour)
    reset_encoder.config(background = unselected_colour)

    self.frame.pack()

    self.video_button_ids = [] 
    self.video_button_ids.append(reset_encoder)
    self.video_button_ids.append(reset_video)
    self.video_button_ids.append(return_button)

    Instance = vlc.Instance() #'--verbose 2'.split())
    self.player = Instance.media_player_new()
    Media = Instance.media_new(channel)
    Media.get_mrl()
    self.player.set_media(Media)
    #player.set_fullscreen(False)
    self.player.play()
    self.player.set_fullscreen(True)

    master.attributes('-topmost', 'true') #force the window to be infront 

    time.sleep(5) #give player time to load before spawning the control buttons

    master.bind('<Key>', partial(self.press_button) )

    master.mainloop()

    self.timed_opacity_thread = threading.Thread(target=self.timed_opacity) #make a thread which manage when the opacity of the buttons will turn on and off
    self.timed_opacity_thread.start()

def press_button(self, event): #tells the program what to do if a button is pressed 
    print("pressed button in video screen")

    if show_button == True: #if the opacity is set to be visable simple extend the time 
        global button_timer
        button_timer = time.perf_counter()
    else: # if the opacity is off turn it back on and place it into a thread to that other tasks can be performed at the same time.
        self.timed_opacity_thread = threading.Thread(target=self.timed_opacity)
        self.timed_opacity_thread.start()

    global videoscreen_button_num
    old_button_num = videoscreen_button_num
    print("button pressed", event.keycode)
    if event.keycode == 40 : #up button
        if videoscreen_button_num == 2:
            videoscreen_button_num = 0
        else:
            videoscreen_button_num = videoscreen_button_num + 1

    if event.keycode == 38 : #down
        if videoscreen_button_num == 0 :
            videoscreen_button_num = 2
        else:
            videoscreen_button_num = videoscreen_button_num - 1
        
    if event.keycode == 37: #right
        if videoscreen_button_num == 2:
            videoscreen_button_num = 0
        else:
            videoscreen_button_num = videoscreen_button_num + 1 

    if event.keycode == 39 : #left
        if videoscreen_button_num == 0 :
            videoscreen_button_num = 2
        else:
            videoscreen_button_num = videoscreen_button_num - 1    
    
    if event.keycode == 13: #enter
        print("pressed enter")
        self.video_button_ids[videoscreen_button_num].invoke()

    print("old button = ", old_button_num)
    print ("new button = ", videoscreen_button_num)

    print ("old object = ", self.video_button_ids[old_button_num])
    print ("new object = ", self.video_button_ids[videoscreen_button_num])

    unselected_colour = "SteelBlue1" 
    selected_colour = "RoyalBlue1"

    self.video_button_ids[old_button_num].config(background = unselected_colour)
    self.video_button_ids[videoscreen_button_num].config(background = selected_colour)

    print("")

def timed_opacity(self): #make the buttons fade when no user input is detected for a while and then reappear when a user input is detected
    self.master.attributes('-alpha', 1)
    
    global button_timer 
    global show_button
    button_timer = time.perf_counter()
    show_button = True
    while time.perf_counter() - button_timer < 10:
        pass

    self.master.attributes('-alpha', .5)
    show_button = False

def close_windows(self):
    self.player.stop()
    global show_button 

    self.master.destroy() #get rid of window, app still runs, not sure how to kill app 
    
def refresh_video(self):
    print("refresh video funtion")
    self.player.Refresh()

def main(): 
    root = tk.Tk()
    app = video_window(root, 'rtsp://192.168.8.20/4') #the location of the video you wish to play
    root.mainloop()

if __name__ == '__main__':
    main()

Надеюсь, это может кому-то помочь

...