Interface gráfica para feh ---bg-scale (Wallpapers)

Quem usa gerenciadores de janela em seus sistemas precisa de algum programa para definir os papéis de parede. Sei que o nitrogen é bastante popular e completo, mas por questão pessoal, sempre usei o feh na linha de comando mesmo.
Eu estava num daqueles dias que não tinha nada para fazer, então resolvi escrever esse código para otimizar mais ainda o processo de setar os wallpapers com o feh (nem sei se realmente precisava hahahah).
Dependências: tkinter, pillow ou PIL (Python Imaging Library).
opcionais: Fonte Awesome e Fonte Inter (Caso não deseje usá-las é só mudar as fontes no código).

#!/usr/bin/python3
from tkinter import *
from PIL import ImageTk, Image
import os

# Define a lista de imagens
images_path = "/home/user/Imagens/Wallpapers"
images = os.listdir(images_path)
current_image = -1  # Define -1 como valor padrão

# Define a função para mudar o papel de parede
def change_wallpaper(image_path):
    os.system(f"feh --bg-scale {image_path}")

# Define a função para exibir a imagem atual na label
def show_image(update_wallpaper=True):  # Adiciona parâmetro update_wallpaper
    global current_image
    if current_image >= 0:  # Verifica se já foi selecionada alguma imagem
        image_path = os.path.join(images_path, images[current_image])
        img = Image.open(image_path)
        img = img.resize((500, 300), Image.LANCZOS)
        img_tk = ImageTk.PhotoImage(img)
        label.config(image=img_tk)
        label.image = img_tk
        if update_wallpaper:  # Verifica se o papel de parede deve ser atualizado
            change_wallpaper(image_path)
        # Atualiza a legenda
        label_num.config(text=f"Imagem {current_image+1}/{len(images)}")

# Define a função para avançar para a próxima imagem
def next_image():
    global current_image
    current_image = (current_image + 1) % len(images)
    show_image()

# Define a função para voltar para a imagem anterior
def prev_image():
    global current_image
    current_image = (current_image - 1) % len(images)
    show_image()

# Define a função para exibir a imagem selecionada pelo usuário
def select_image():
    global current_image
    try:
        index = int(entry.get()) - 1
        if index >= 0 and index < len(images):
            current_image = index
            show_image()
    except ValueError:
        pass

# Cria a janela principal
root = Tk()
root.title("FehPy")
root.geometry('500x430')
root.minsize(500, 430)
root.maxsize(500, 430)
root.config(bg='#2B313C')

# Define a imagem padrão baseada no arquivo ${HOME}/.fehbg
fehbg = os.system('cat /home/user/.fehbg | grep ^feh | cut -c 27- > /home/user/.default-img.txt')
with open('/home/user/.default-img.txt', 'r') as feh_path:
    default_feh = feh_path.readlines()[0]
    actual_image = default_feh.replace('\n', '')
    imagem = actual_image.replace("'", "")
    img = imagem.replace(' ', '')
default_image_path = img

# Cria a label para exibir a imagem
label = Label(root, width=500, height=300)
label.pack()

# Cria a legenda para a imagem
label_num = Label(root, text="Imagem 0/0", bg='#2B313C', fg='#FFF', font=('Inter', 10))
label_num.pack()

# Cria a entrada para selecionar a imagem
entry = Entry(root, width=3, bg='#1A202B', fg='#FFF', font=('Inter', 10), insertbackground='#FFF')
entry.pack(pady=5)

# Cria o botão para exibir a imagem selecionada pelo usuário
button_select = Button(root, text="Selecionar", bg='#2B313C', fg='#FFF', font=('Inter', 10), command=select_image)
button_select.pack()

# Cria o botão para avançar para a próxima imagem
button_next = Button(root, text="", bg='#2B313C', fg='#FFF', font=('Awesome', 15), command=next_image)
button_next.pack(padx=5, pady=5, side=RIGHT)

# Cria o botão para voltar para a imagem anterior
button_prev = Button(root, text="", bg='#2B313C', fg='#FFF', font=('Awesome', 15), command=prev_image)
button_prev.pack(padx=5, pady=5, side=LEFT)

# Exibe a imagem padrão na label
img = Image.open(default_image_path)
img = img.resize((500, 300), Image.LANCZOS)
img_tk = ImageTk.PhotoImage(img)
label.config(image=img_tk)
label.image = img_tk

# Inicia o loop da interface gráfica
root.mainloop()

Para funcionar, informe o caminho da pasta com wallpapers na variável “images_path” e mude a palavra “user” para o nome do seu usuário lá na variável “fehbg” . A variável fehbg também precisa de um caminho para salvar um arquivo de texto com o nome do wallpaper extraído de do arquivo ${HOME}/.fehbg, por padrão ela cria um arquivo oculto na home .default-img.txt. Como usei fontes específicas também pode ser necessário alterálas ou até mesmo o nome dos botões.
Quem tiver alguma idéia de melhorar o código ou dar alguma outra sugestão, fique à vontade.

Segue abaixo uma imagem do programa.

7 curtidas

No Nitrogen o usuário informa o caminho na própria interface gráfica para os
Wallpapers.

images_path = “$HOME/Imagens/Wallpapers”

Obs: O ideal seria zero dependências

No Void Linux falta o tkinter (python-tkinter não resolve)

from tkinter import *
ModuleNotFoundError: No module named ‘tkinter’

1 curtida

O Nitrogen é muito bom para mudar wallpapers. Fiz esse pequeno programa apenas pq na época queria usar apenas o feh para essa finalidade.
Talvez no Void o pacote esteja com outro nome. Vc tentou python3-tkinter?

1 curtida

Instalei o python3-tkinter

$ xbps-query -Rs tkinter
[-] idle-python-2.7.18.9_2 Interpreted, interactive, object-oriented programming language - IDE for Python2 using Tkinter
[-] idle-python3-3.12.7_1 Python programming language - IDE for Python3 using Tkinter
[-] python-tkinter-2.7.18.9_2 Interpreted, interactive, object-oriented programming language - GUI toolkit for Python2
[-] python3-pmw-2.0.1_7 Python 3 Tkinter widget toolkit

[*] python3-tkinter-3.12.7_1 Python programming language - GUI toolkit for Python3

$ ./feh.py 
Traceback (most recent call last):
  File "/home/tux/shell_scripts/./feh.py", **line 51**, in <module>
    from PIL import ImageTk, Image
ModuleNotFoundError: No module named 'PIL'


linha 51 **from PIL import ImageTk, Image**

Traduzir 98% para br tem um fork no projeto oficial.

Com suporte para arquivo .webp e .avif no Nitrogen

1 curtida

images_path = “$HOME/Imagens”

Trocar todos os /home/user/ pelo $HOME tem como?

No Void Linux:

xbps-install -y python3-tkinter
xbps-install -y python3-Pillow

Captura de tela_2024-11-12_01-55-23

Abriu com vários erros:

$ ./feh.py 
Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.12/tkinter/__init__.py", line 1968, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "/home/tux/shell_scripts/./feh.py", **line 90**, in prev_image
    show_image()
  File "/home/tux/shell_scripts/./feh.py", **line 70**, in show_image
    img = Image.open(image_path)
          ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/PIL/Image.py", line 3431, in open
    fp = builtins.open(filename, "rb")
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**IsADirectoryError: [Errno 21] Is a directory: '/home/tux/Imagens/Chapolin'**

Obs: Na pasta Imagens tem vários diretórios (Ex: Chapolin) e arquivo de imagens.

linha 90 => show_image()
linha 70 => img = Image.open(image_path)

Bem lembrado do pillow… ao inves de executar com ./feh.py, tenta rodar direto com o python:
python3 feh.py

Esse código já tem mais de 2 anos eu tenho que testar aqui para ver se tem algum erro atualmente

O erro aparece quando usa as setas e encontra uma pasta.

IsADirectoryError: [Errno 21] Is a directory:

Tem como somente identificar os arquivos de imagens e não as pastas.

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.12/tkinter/__init__.py", line 1968, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "/home/tux/shell_scripts/feh.py", line 126, in next_image
    show_image()
  File "/home/tux/shell_scripts/feh.py", line 112, in show_image
    img = Image.open(image_path)
          ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/PIL/Image.py", line 3431, in open
    fp = builtins.open(filename, "rb")
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
IsADirectoryError: [Errno 21] Is a directory: '/home/tux/Imagens/goku'
sh: 1: Syntax error: "(" unexpected
sh: 1: Syntax error: "(" unexpected
Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.12/tkinter/__init__.py", line 1968, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "/home/tux/shell_scripts/feh.py", line 126, in next_image
    show_image()
  File "/home/tux/shell_scripts/feh.py", line 112, in show_image
    img = Image.open(image_path)
          ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/PIL/Image.py", line 3431, in open
    fp = builtins.open(filename, "rb")
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
IsADirectoryError: [Errno 21] Is a directory: '/home/tux/Imagens/macOS'

O programa seleciona a imagem automaticamente se o usuário ficar muito tempo na mesma tela. Acho que a seleção só deveria acontecer se o usuário somente clicar no botão selecionar.

1 curtida

Verdade. Ele não analisa subdiretorios, realmente vai apresentar erros.

Realmente é um programa que está cheio de limitações

Tem como filtrar somente os arquivos de imagens da pasta images_path?

Tipo:

lista para conter apenas arquivos

print(list(filter(os.path.isfile, os.listdir())))

Poderia colocar as imagens da pasta num arquivo txt ou em um ArrayList para depois usar as setas

def show_image(update_wallpaper=True): # Adiciona parâmetro update_wallpaper
global current_image
if current_image >= 0: # Verifica se já foi selecionada alguma imagem
image_path = os.path.join(images_path, images[current_image])
img = Image.open(image_path)
img = img.resize((500, 300), Image.LANCZOS)
img_tk = ImageTk.PhotoImage(img)
label.config(image=img_tk)
label.image = img_tk
if update_wallpaper: # Verifica se o papel de parede deve ser atualizado
change_wallpaper(image_path)
# Atualiza a legenda
label_num.config(text=f"Imagem {current_image+1}/{len(images)}")

A parte open é responsável por ler a imagem atual na tela

No site tem um bom exemplo de tela como modelo de visualização de imagens

1 curtida

Obrigado pela sugestão dos links.
infelizmente não consigo revisar ou alterar o código por esses dias. Fiz esse programa na época que eu usava o Awesome WM, atualmente estou usando o gnome não sei se vou conseguir reproduzir o programa aqui na minha máquina já que o comando feh --bg-scale não altera papéis de parede no gnome.
Suas sugestões são importantes, quando eu arrumar um tempo posso tentar implementar mais funcionalidades.
Você usa o fluxbox correto? Talvez eu teste o programa dentro de uma VM com fluxbox para ter mais clareza para implementar mais funcionalidades.

Tem como usar o módulo os para “capturar” o caminho da home do usuário ativo. Coloque essa linha depois dos import...

USER_HOME = os.path.expanduser("~")

então você pode editar todos os caminhos que tem no arquivo usando a variável USER_HOME. por exemplo:

images_path = f"{USER_HOME}/Imagens/Wallpapers"
1 curtida

Na parte da Ajuda estão os problemas conhecidos. A questão do nome com espaço na pasta falta testar em teoria funciona…

Como é feita a visualização das imagens na tela e a parte de ir e volta das imagens?

1 curtida

Estou vendo que vc está fazendo um “fork” do código original. Está ficando muito bom, parabéns.

Binário feh-gui gerado em 02/12/2024 para sistema 64 bit.

$ sha256sum feh-gui
37d3680ca3496a6ba3f6817f247e9ada39c4c5b19c5faaa1065dfefbfd4974ed feh-gui

Testado no Void Linux agora falta testar em outra distros.

1 curtida