LINUX.ORG.RU
ФорумTalks

Вышел Nirvana Launcher v0.1

 , ,


0

3

Сделал тут свой альтернативный лаунчер (написан на PyQt6) на замену всяким Wofi и dmenu и назвал его Nirvana Launcher.
Скриншот: https://i.ibb.co/spBqr8xR/2025-02-19-223914-hyprshot.png .

Да, это просто окно с надписью. Которое ожидает нажатия клавиши. Вместо того, чтобы скроллить разные списки и вбивать что-то длинное.

Конфиг лаунчера, по сути, в самом скрипте в виде условий, которые каждый может отредактировать на свой вкус и цвет.

Скачать: https://saahriktu.ru/downloads/nirvanalauncher.py .

Enjoy!

★★★★★

написан на PyQt6

Да, это просто окно с надписью.

А чего так слабо? Давай сразу Electron тяни ради окна с надписью.

P.S. Не за тем я на этот тег подписывался, ох не за тем, были же наоборот маленькие юниксвейные утилитки в духе suckless… Этот @saahriktu сломался, принесите нового.

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)

написан на PyQt6

Встаёт вопрос - как долго оно запускается? Для лаунчера довольно критичный показатель.

Bfgeshka ★★★★★
()
Ответ на: комментарий от Bfgeshka

как долго оно запускается?

У меня - мгновенно.

saahriktu ★★★★★
() автор топика
        if event.key() == Qt.Key.Key_A:
            self.launch_application("/usr/lib64/LibreOffice-still/program/scalc")
        elif event.key() == Qt.Key.Key_B:
            self.launch_application("brave")
        elif event.key() == Qt.Key.Key_C:
            self.launch_application("qtcreator")
        elif event.key() == Qt.Key.Key_D:
            self.launch_application("dia")
        elif event.key() == Qt.Key.Key_E:
            self.launch_application("emacs")
        elif event.key() == Qt.Key.Key_F:
            self.launch_application("firefox")
        elif event.key() == Qt.Key.Key_G:
            self.launch_application("gambas3")
        elif event.key() == Qt.Key.Key_I:
            self.launch_application("qbittorrent")
        elif event.key() == Qt.Key.Key_K:
            self.launch_application("konversation")
        elif event.key() == Qt.Key.Key_L:
            self.launch_application("lazarus")
        elif event.key() == Qt.Key.Key_M:
            self.launch_application("qmmp-1")
        elif event.key() == Qt.Key.Key_P:
            self.launch_application("psi-plus")
        elif event.key() == Qt.Key.Key_Q:
            self.launch_application("qalculate-gtk")
        elif event.key() == Qt.Key.Key_S:
            self.launch_application("skypeforlinux")
        elif event.key() == Qt.Key.Key_T:
            self.launch_application("telegram-desktop")
        elif event.key() == Qt.Key.Key_U:
            self.launch_application("thunderbird")
        elif event.key() == Qt.Key.Key_W:
            self.launch_application("/usr/lib64/LibreOffice-still/program/swriter")
        elif event.key() == Qt.Key.Key_Y:
            self.launch_application("yandex-browser-stable")
        elif event.key() == Qt.Key.Key_1:
            self.launch_application("gimp")
        elif event.key() == Qt.Key.Key_2:
            self.launch_application("inkscape")
        elif event.key() == Qt.Key.Key_3:
            self.launch_application("librecad")
        elif event.key() == Qt.Key.Key_0:
            self.launch_application("terminology")

Во-первых, не надо дёргать функцию столько раз, оно ж для каждой ветки запускается — один раз вызови этот event.key() и присвой значение переменной.

Ну а во-вторых… Не задолбался эти elif, и elf.launch_application() писать 100500 раз, и править это дело в случае желания изменить хоткей — тоже тот ещё кайф… Смотри:

    mappings = {
        Qt.Key.Key_A: "/usr/lib64/LibreOffice-still/program/scalc",
        Qt.Key.Key_B: "brave",
        Qt.Key.Key_C: "qtcreator",
        # ...
        # и т.д.
        # ...
        Qt.Key.Key_0: "terminology"
    }
    key = event.key()
    if key in mappings:
        self.launch_application(mappings[key])
CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 4)
Ответ на: комментарий от CrX

Вероятно, этот пример уже устарел.

$ ./hello_wayland
Ошибка сегментирования
$

saahriktu ★★★★★
() автор топика

ты даже не додумался все в массив какой загнать, а потом бы до тебя дошло, что этот масстив можно хранить в yaml/toml в файлике как обычный конфиг… Лаунчер без иконок - это срань

rtxtxtrx ★★★
()
Ответ на: комментарий от rtxtxtrx

этот масстив можно хранить в yaml/toml в файлике

Можно, но лаунчер и так прост как две копейки. С отдельным конфигом он вообще в 5 строк будет.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

Кстати, вот это # -*- coding: utf-8 -*- только для второго питона. В третьем не нужно — там только utf-8 и есть, нет этой свистопляски с кодировками.

CrX ★★★★★
()
Ответ на: комментарий от saahriktu
# эту портянку через toml сохраняешь, а потом читаешь
keybindings = [
  {
    'key': 'A',
    'exec': 'prog'
  },
  # ...
]

for kb in keybindings:
  key = gettatr(Qt.Key, 'Key_' + kb['key'], None)
  if event.key() == key:
    self.launch_application(kb['exec'])
rtxtxtrx ★★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 1)
Ответ на: комментарий от CrX

там только utf-8 и есть

В строках - да, но не в самих скриптах.

$ cat testkoi8r.py
#!/usr/bin/python3
# -*- coding: koi8-r -*-

print('������, ���')
$ python3 ./testkoi8r.py
Хэллоу, ЛОР
$

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от rtxtxtrx

Я сделал проще (event.key(), между прочим, возвращает число). Теперь конфиг в JSON'е. https://saahriktu.ru/downloads/nirvana_launcher_v0.3.tar.xz

Конфиг располагается в ~/.config/nirvana_launcher_keys.json и выглядит так:

{"65": "/usr/lib64/LibreOffice-still/program/scalc",
"66": "brave",
"67": "qtcreator",
"68": "dia",
"69": "emacs",
"70": "firefox",
"71": "gambas3",
"73": "qbittorrent",
"75": "konversation",
"76": "lazarus",
"77": "qmmp-1",
"80": "psi-plus",
"81": "qalculate-gtk",
"83": "skypeforlinux",
"84": "telegram-desktop",
"85": "thunderbird",
"87": "/usr/lib64/LibreOffice-still/program/swriter",
"89": "yandex-browser-stable",
"49": "gimp",
"50": "inkscape",
"51": "librecad",
"48": "terminology"}

saahriktu ★★★★★
() автор топика
Последнее исправление: saahriktu (всего исправлений: 2)
Ответ на: комментарий от saahriktu

так это не очень. коды кнопок никто заучивать не станет. даже непонятно как они нумеруются. и в теории кому то может понадобиться одной клавишей несколько прог запустить. и код только с гитхаба кто-то скачивать будет, иначе можно оформить все как пакет pypi. я не знаю почему ты туда залить не можешь.

rtxtxtrx ★★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 2)
Ответ на: комментарий от saahriktu

а это я китайскую нейронку попросил написать перевод сочетаний в последовательности:

from PyQt5.QtCore import Qt, QKeySequence

def parse_shortcut(shortcut_str):
    modifiers = Qt.NoModifier
    key = Qt.Key_unknown
    
    parts = [part.strip().lower() for part in shortcut_str.split('+') if part.strip()]
    
    for part in parts:
        match part:
            case 'ctrl' | 'control':
                modifiers |= Qt.ControlModifier
            case 'shift':
                modifiers |= Qt.ShiftModifier
            case 'alt':
                modifiers |= Qt.AltModifier
            case 'meta' | 'win' | 'cmd':
                modifiers |= Qt.MetaModifier
            case _:
                # Пытаемся определить клавишу
                key_sequence = QKeySequence(part)
                if not key_sequence.isEmpty():
                    key = key_sequence[0] & ~Qt.KeyboardModifierMask
                
    return modifiers, key

# Пример использования
shortcut = "Ctrl + Shift + Q"
modifiers, key = parse_shortcut(shortcut)

print(f"Modifiers: {modifiers} ({bin(modifiers)})")
print(f"Key: {key} ({QKeySequence(key).toString()})")

# Для установки в QAction
action = QAction("Exit")
action.setShortcut(QKeySequence(key | modifiers))
rtxtxtrx ★★★
()
Ответ на: комментарий от rtxtxtrx

он тут конечно намудрил. но в целом должно работать… ну и лаунчер должен иконки отображать, которые можно было бы получить, если бы он парсил десктоп-файлы. там иконки есть. либо через пакетный менеджер, смотреть список файлов в пакете и искать там десктоп файлы чтобы иконку подсосать

rtxtxtrx ★★★
()

Номинант на награду «Жирнота года».

gruy ★★★★★
()

Эка тебя жизнь помотала.

окно с надписью. Которое ожидает нажатия клавиши

Знаете ли вы что, что любое de или wm может быть настроено на выполнение команд сочетаниями клавиш?
Ну банально биндишь весь нужный софт на сочетания meta+key прямо в конфиге и всё.

sehellion ★★★★★
()

А зачем нужен такой лончер, если можно просто настроить шорткаты на запуск нужных программ в своём DE/WM?

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

А разве кто-то скроллит? Обычно просто вбиваешь пару букв из названия программы и сразу выбираешь результат.

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

Ну так-то да. Ему просто хочется кучу сочетаний запоминать. С поиском проги отпадает и проблема с иконками, так как поиск фактически по десктоп файлам, там в Name имена прог… И Qt лишний, наверняка, этот тайловый дрисняменеджер позволяет вывести окошечко со списком для выбора напрямую

rtxtxtrx ★★★
()

Это лаунчер _чего_? Под какую операционную систему? Почему на скриншоте неинформативная надпись и все?

Xintrea ★★★★★
()
Ответ на: комментарий от einhander

Этот @sаahriktu сломался, принесите нового

А я говорил, что его подменили, а мне не верят

Ага, говорят что все это теория заговора и никакие пришельцы saahriktu не похищали и не подменяли на своего шпийона

Kolins ★★★★★
()
Последнее исправление: Kolins (всего исправлений: 2)
Ответ на: комментарий от rtxtxtrx

коды кнопок никто заучивать не станет. даже непонятно как они нумеруются

Так это ASCII коды же.

import sys

if len(sys.argv) != 3 or len(sys.argv[1]) > 1:
        print("Usage: nirvanalaunchercfg.py <char> <string>")
        exit(1)

key = f'"{ord(sys.argv[1])}"'
program =f'"{sys.argv[2]}"'

print(f"{key}: {program},")
$ python3 ./nirvanalaunchercfg.py F firefox
"70": "firefox",
$ 

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от sehellion

Ну банально биндишь весь нужный софт на сочетания meta+key прямо в конфиге и всё.

Лаунчеры для того и юзают чтобы разгрузить биндинги DE/WM.

У меня, например, 96 сочетаний клавиш забиндены на переключение между 96-ю рабочими столами.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

делай поиск по названию, можно нечеткий. правильно выше написали, что оно не имеет смысла. а насчет кодов кнопок, то это проще делается, типа f12 жмешь и в консоли браузера вводишь:

'F'.charCodeAt() // 70
rtxtxtrx ★★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 1)
Ответ на: комментарий от saahriktu
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Nirvana Launcher v0.2 by saahriktu
# 2025 (c) under GNU GPLv3

import sys
import subprocess
from PyQt6.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget
from PyQt6.QtCore import Qt

# Вместо этого ты можешь из отдельного файла грузить. Ну или прям так можно, меньше кавычек и двоеточий
CONFIG_TEXT = """A /usr/lib64/LibreOffice-still/program/scalc
B brave
C qtcreator
D dia
E emacs
F firefox
G gambas3
I qbittorrent
K konversation
L lazarus
M qmmp-1
P psi-plus
Q qalculate-gtk
S skypeforlinux
T telegram-desktop
U thunderbird
W /usr/lib64/LibreOffice-still/program/swriter
Y yandex-browser-stable
1 gimp
2 inkscape
3 librecad
0 terminology"""


class NirvanaLauncher(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

        self.config = {}
        # Вот тут вместо хардкода выше грузишь из файла, но пока
        for line in CONFIG_TEXT.split('\n'):
            keyname, command = line.split(" ", 1)
            keycode = ord(keyname)
            self.config[keycode] = command


    def initUI(self):
        self.setWindowTitle("Nirvana Launcher")
        self.setGeometry(100, 100, 300, 100)

        layout = QVBoxLayout()
        label = QLabel("Nirvana Launcher", self)
        label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        layout.addWidget(label)
        self.setLayout(layout)

        self.show()

    def keyPressEvent(self, event):
        keycode = event.key()
        if keycode in self.config:
            self.launch_application(self.config[keycode])

    def launch_application(self, app_name):
        try:
            subprocess.Popen(app_name)
            QApplication.quit()
        except Exception as e:
            print(f"Error launching {app_name}: {e}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    launcher = NirvanaLauncher()
    sys.exit(app.exec())
CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 3)
Ответ на: комментарий от CrX

Да всё же проще. Чуть модифицировал код в той версии на

        key = chr(event.key())
И теперь всё работает с конфигом вида
{"A": "/usr/lib64/LibreOffice-still/program/scalc",
"B": "brave",
"C": "qtcreator",
"D": "dia",
"E": "emacs",
"F": "firefox",
"G": "gambas3",
"I": "qbittorrent",
"K": "konversation",
"L": "lazarus",
"M": "qmmp-1",
"P": "psi-plus",
"Q": "qalculate-gtk",
"S": "skypeforlinux",
"T": "telegram-desktop",
"U": "thunderbird",
"W": "/usr/lib64/LibreOffice-still/program/swriter",
"Y": "yandex-browser-stable",
"1": "gimp",
"2": "inkscape",
"3": "librecad",
"0": "terminology"}

https://saahriktu.ru/downloads/nirvana_launcher_v0.4.tar.xz

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

Чем это проще? Тем, что надо дрочить эти двоеточия, кавычи, запятые и скобочки каждый раз и надеяться точно нигде не пропустить?

Чем проще и примитивнее синтаксис конфига, тем лучше. JSON, конечно, полезен для многих задач, и много чего интересного с его помощью можно организовать, но здесь это оверкилл.

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)
Ответ на: комментарий от saahriktu

Да, надо ещё одну прослойку сделать. Чтоб писать нормальный конфиг, потом отдельной приблудой дампить в JSON, а потом из твоей чудо-запускалки парсить. А то ведь без импорта модуля json скучно — слишком простая логика и памяти недостаточно жрёт.

CrX ★★★★★
()
Ответ на: комментарий от CrX

А можно просто редактировать конфиг из коробки в vim'е.

yy, p - и остаётся только заменить старые значения на новые.

Но если руки настолько кривые, что это вариант недоступен, то, конечно, остаётся только конфигураторы писать.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

Именно ты и подобные тебе — та причина, по которой современный Web такое говно, где фреймворк на фреймворке для написания фреймворков, в итоге отображающие статическую страничку, и та причина, по которой десктопный софт тоже не избежал этой участи, хотя с ним всё и не так печально. Вот именно те, кто тащит целый Qt для отрисовки окошка с одной надписью, кто тащит парсинг JSON для примитивного конфига ключ-значение (где ещё и ключ со значением всегда одного типа), и даже не из-за лени, а готов потом ещё и колупаться с кавычками и запятыми, потому что может — руки же не кривые — всё лишь бы притащить побольше кода модулей, сделать всё потяжелее, позависеть от большего числа компонентов. Это именно вы всё вот это сделали. И нет, конечно, проблема не в руках, она в головах.

CrX ★★★★★
()
Ответ на: комментарий от CrX

Таков путь, поскольку библиотеки начали урезать и разделять функционал между разными фреймворками.

Раньше иксы могли сами рисовать, теперь разработчики иксов делают Wayland, где окошки рисуют тулкиты - GTK или Qt.

А формат конфига JSON, кстати, расширяет возможности лаунчера. Благодаря ему можно запускать программы с аргументами, а не только без аргументов.

целый Qt для отрисовки окошка с одной надписью

На самом деле, не только. Ещё для отлавливания нажатия клавиши чтобы его обработать.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от CrX

Если бы я делал бы конкретно для иксов, как раньше, то сделал бы на Tk. Но Tk не умеет работать нативно в Wayland'е, только через XWayland. А Qt и GTK умеют нативно отображаться везде.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

А формат конфига JSON, кстати, расширяет возможности лаунчера. Благодаря ему можно запускать программы с аргументами, а не только без аргументов.

В моём варианте выше — тоже можно с аргументами.

CrX ★★★★★
()
Ответ на: комментарий от saahriktu

Лаунчер программ под GNU/Linux.

Так а как одной кнопкой на экране вызывать разные программы? Почему у программ нет иконок? Откуда пользователь должен узнать что он запускает?

Xintrea ★★★★★
()
Ответ на: комментарий от saahriktu

Тут напрашивается sxhkd или какой-то его аналог под вяленький, скорее. Зато аккорды и любое количество слоёв.

skiminok1986 ★★★★★
()
Ответ на: комментарий от rtxtxtrx

Лаунчер без иконок - это срань

Уж сколько лет на ЛОРе повторяют мантру, что иконки не нужны )

Zhbert ★★★★★
()
Ответ на: комментарий от CrX
#include 
#include 
#include 
#include 
#include 
#include 
 
#include "helpers.h"
 
static const unsigned WIDTH = 320;
...
Bad_ptr ★★★★★
()
Ответ на: комментарий от Xintrea

Так а как одной кнопкой на экране вызывать разные программы?

Разными кнопками на окне лаунчера. Но по одной. На разные кнопки навешиваются биндинги на разные программы.

saahriktu ★★★★★
() автор топика
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)