Edison Design Group po více než 30 letech zveřejnil zdrojový kód svého EDG C/C++ front-endu. Ten proslul širokou podporou standardů C++ a kompatibilitou s dialekty kompilátorů od Microsoftu, GNU, Clangu, Sunu a dokonce i s prehistorickým cfrontem. EDG byl použit například v kompilátoru Intel C++ Classic, kompilátoru NVCC od firmy NVIDIA pro platformu CUDA nebo v našeptávači kódu IntelliSense v produktech společnosti Microsoft. Projekt nyní spravuje organizace The C++ Alliance a kód je dostupný pod licencí Apache 2.0, doplněnou o výjimky projektu LLVM.
Pocket Tank je malé kapesní virtuální akvárium postavené na vývojové desce ESP32-S3-Touch-AMOLED-1.8. Na dvoujádrovém mikrokontroléru ESP32-S3 s 8 MB PSRAM a 16 MB flash paměti běží jak simulace akvária, tak i malý jazykový model určující chování rybiček. Lokální model o velikosti 7,56 MB se 14 miliony parametrů vznikl destilací modelu Gemma 4 26B, tedy učitele s 26 miliardami parametrů. Kromě firmwaru pro skutečný modul s AMOLED
… více »Open-source nástroj Jevstiller vytváří (destiluje) malý lokální model, který se průběžně učí z odpovědí komerční služby Jev. Běžné dotazy vyřizuje přímo na vlastním hardware, čímž může řádově zkrátit čas odezvy a snížit provozní náklady díky menšímu využívání zpoplatněného API. Nejisté požadavky Jevstiller posílá do Jevu, rovněž průběžně kontroluje náhodná dvě procenta dotazů Jevem. Pokud se lokální model začne s Jevem rozcházet,
… více »Konference LinuxDays 2026 proběhne již tento víkend 3. a 4. října v Praze v areálu ČVUT v Dejvicích na FIT. Konference LinuxDays 2026 znamená desítky přednášek a workshopů, zástup zajímavých osobností, místo pro setkání, spoustu nových nápadů a informací a stánky řady různých projektů: Fedora, openSUSE, vpsFree.cz, Mozilla, MacGyver - bastlíři SH, OpenAlt a mnoho dalších. Vstup je volný.
Microsoft oznámil, že WSL kontejnery (WSLC) aneb linuxové kontejnery ve Windows Subsystem for Linux (WSL) jsou již obecně dostupné. Současně popsal jejich architekturu.
openSUSE Leap 16.1 vstoupil do RC fáze. Nově lze instalovat jako standardní systém (Standard) nebo jako neměnný systém s atomickými aktualizacemi (Immutable). Samostatná neměnná distribuce openSUSE Leap Micro končí.
Byl vydán Mozilla Firefox 157.0. S nejvýraznější vizuální proměnou za poslední roky. Přehled novinek v poznámkách k vydání a poznámkách k vydání pro vývojáře. Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 157 bude brzy k dispozici také na Flathubu a Snapcraftu.
Rodina produktů Raspberry Pi se rozšířila o Raspberry Pi Smart Display Module. Deska pro Raspberry Pi Compute Module 5 se zasouvá přímo do displejů dle specifikace Intel SDM. Cena desky je 30 dolarů.
Byla vydána nová verze 2.56.0 distribuovaného systému správy verzí Git. Přispělo 104 vývojářů, z toho 39 nových. Přehled novinek v příspěvku na blogu GitHubu a v poznámkách k vydání.
Hackerská skupina ShinyHunters oznámila průnik do informačních systémů FBI, prostřednictvím zero-day zranitelnosti v platformě Oracle PeopleSoft, kterou úřad využívá mimo jiné pro náborový portál FBIJobs.gov. Útočníci tvrdí, že získali až 3 TB dat o současných i bývalých zaměstnancích a uchazečích o práci. Jako důkaz zveřejnili vzorek přibližně 5 000 záznamů obsahující jména, adresy, telefonní čísla, data narození, čísla
… více »Ne vždy je nutné, aby měl plasmoid možnost uživatelského nastavení, ale občas se to hodí – příkladem mohou být plasmoidy, které pracují s nějakou online službou vyžadující přihlášení. U nich musí uživatel nastavit přihlašovací údaje v konfiguračním dialogu. Konfigurační dialog je pouze .ui soubor (lze vytvořit například v Qt Designeru – viz Programování v Qt). CMake z něj vygeneruje hlavičkový soubor, který lze includovat do zdrojových kódu plasmoidu, a s dialogem pak můžeme pracovat jako s běžnou třídou. Jako základní ovládací prvek dialogu se používá QWidget. Načtení dat z konfiguračního souboru do prvků v dialogu, propojení signálu a slotů atd. se provádí metodou createConfigurationInterface, která jako parametr přijímá ukazatel na KConfigDialog, což je základní implementace konfiguračních dialogů v KDE. Metoda musí být deklarována jako veřejná, protože ji volá Plasma, když uživatel požádá o otevření dialogu.
Druhá věc, kterou si dnes ukážeme, je vstup dat přes drag&drop. To se dělá implementací standardní metody Qt dropEvent, jíž jediným parametrem je objekt QGraphicsSceneDragDropEvent popisující mj. přijatá data.
Já jsem se rozhodl pro velice jednoduchý plasmoid na kompresi souborů. Po přetažení souborů do plasmoidu se soubory zkomprimují (metodu budeme nastavovat v konfiguračním dialogu) a následně se plasmoid zeptá, kam ho má uložit a pod jakým názvem. Vzhůru do toho.
Začneme tím, že si v Qt Designeru vytvoříme konfigurační formulář. O Qt Designeru se podprobněji zmiňuje David Watzke ve článku Grafické programy v Qt 4 – 3 (Qt Creator a Designer). Zde je obrázek, jak může formulář vypadat a jak by se měly prvky jmenovat.
compressor.h
#ifndef COMPRESSOR_HEADER
#define COMPRESSOR_HEADER
// Hlavičkový soubor konfiguračního dialogu, který se automaticky vygeneruje z config.ui
#include "ui_config.h"
// Pár tříd, které se nám budou určitě hodit
#include <KIcon>
#include <KConfigDialog>
#include <QtGui/QGraphicsSceneDragDropEvent>
#include <QPainter>
#include <QRect>
#include <QStyleOptionGraphicsItem>
#include <Plasma/Applet>
class Compressor : public Plasma::Applet
{
Q_OBJECT
Q_ENUMS(CompressionTypes)
Q_ENUMS(CurrentStates);
public:
// Vytvoříme si vlastní výčet podporovaných kompresí
enum CompressionTypes { GZIPCompression = 0,
BZIPCompression = 1
};
enum CurrentStates { StateReady = 0,
StateWorking = 1,
StateDone = 2,
StateError = 3
};
// Konstruktor, destruktor
Compressor(QObject *parent, const QVariantList &args);
~Compressor() {}
// Metoda volaná Plasmou, když uživatel požádá o zobrazení konfiguračního dialogu
void createConfigurationInterface(KConfigDialog *parent);
// Metoda volaná Plasmou, když je applet vytvořen
void init();
private:
KIcon m_icon;
// Zde budeme uchovávat typ komprese, která se má použít
CompressionTypes m_compressionType;
// Uchovává aktuální stav appletu (připraven, pracuji, hotovo, chyba)
CurrentStates m_currentState;
// Toto je třída konfiguračního dialogu vygenerovaná z config.ui
Ui::CompressorConfig configDialog;
// Tato metoda provede kompresi
void compress(QList<QUrl> files);
// Tato metoda nám umožní kreslit si na applet
void paintInterface(QPainter *p, const QStyleOptionGraphicsItem *, const QRect &contentsRect);
protected:
// Metoda, která zajišťuje zpracování dat z drop události
void dropEvent(QGraphicsSceneDragDropEvent *event);
private slots:
// Metoda, která se vykoná, když je komprese dokončena
void compressionDone(int exitCode);
// Metoda, která je zavolána, když uživatel potvrdí konfigurační dialog
void configAccepted();
// Nastaví m_currentState na StateReady a překreslí applet
void setDefaultState();
};
// Makro na export plasmoidu do KDE
K_EXPORT_PLASMA_APPLET(compressor, Compressor)
#endif
Jak jsem zmiňoval na začátku – novinkou jsou dnes metody createConfigurationInterface pro vytvoření dialogu a dropEvent pro zpracování události drag&drop. Pokud vám vrtá hlavou, jak mohu includovat ui_config.h, když neexistuje, vězte, že jej generuje make, respektive program uic ze souboru config.ui. Přestože má soubor příponu .h, obsahuje i zdrojový kód metod, nejen jejich deklarace. Když applet zkompilujete, můžete se do souboru podívat, abyste lépe pochopili, jak to celé funguje. Uvnitř najdete třídu CompressorConfig (Ui je jmenný prostor). Název vygenerované třídy vychází z názvu hlavního QWidget. Pokud jste ho v Qt Designeru pojmenovali jinak, nezapomeňte upravit tento hlavičkový soubor, jinak se applet nepřeloží.
compressor.cpp
#include "compressor.h"
// Třída pro snazší přístup ke konfiguraci plasmoidu
#include <KConfigGroup>
#include <QFileDialog>
#include <QPixmap>
#include <QRectF>
#include <QStringList>
#include <QTimer>
// Přída pro práci s externími procesy
#include <QtCore/QProcess>
#include <QtGui/QGraphicsSceneDragDropEvent>
Compressor::Compressor(QObject *parent, const QVariantList &args)
: Plasma::Applet(parent, args),
m_icon("application-x-bzip-compressed-tar")
{
// Přijímáme drag&drop
setAcceptDrops(true);
setBackgroundHints(DefaultBackground);
// Máme k konfigurační rozhraní
setHasConfigurationInterface(true);
resize(128,128);
// Applet musí mít vždy tvar čtverce (kvůli ikoně)
setAspectRatioMode(Plasma::KeepAspectRatio);
}
void Compressor::init()
{
// Načteme, jaký typ komprese se bude používat
KConfigGroup cg = config();
m_compressionType = (CompressionTypes)cg.readEntry("compressionType",0);
}
void Compressor::paintInterface(QPainter* p, const QStyleOptionGraphicsItem* , const QRect& contentsRect)
{
QPixmap pixmap;
// Podle aktuálního stavu appletu načte ikonu
switch (m_currentState) {
case StateWorking:
// Název ikony přímo neodpovídá tomu, co v našem případě reprezentuje, ale nenašel jsem lepší ikonu
pixmap = KIcon("system-software-update").pixmap(QSize(128,128));
break;
case StateDone:
pixmap = KIcon("dialog-ok").pixmap(QSize(128,128));
break;
case StateError:
pixmap = KIcon("dialog-error").pixmap(QSize(128,128));
break;
default:
case StateReady:
pixmap = KIcon("go-down").pixmap(QSize(128,128));
break;
}
// Nakreslí zvolenou ikonu na plátno appletu
p->drawPixmap(QRectF(contentsRect.left(),contentsRect.top(),contentsRect.width(),contentsRect.height()),
pixmap,
QRectF(0,0,128,128));
}
void Compressor::createConfigurationInterface(KConfigDialog* parent)
{
// Vytvoří nový widget
QWidget *widget = new QWidget(0);
// Metoda setupUI vygeneruje a rozmístí jednotlivé ovládací prvky na QWidget tak,
// jak jsme je rozmístili v config.ui
configDialog.setupUi(widget);
// Nastaví výchozí položku comboboxu pro výběr komprese
configDialog.compressionType->setCurrentIndex((int)m_compressionType);
// Sváže signál dialogu s naším slotem
connect(parent, SIGNAL(accepted()), this, SLOT(configAccepted()));
// Přida na konfigurační dialog nově vytvořený QWidget s rozhraním
parent->addPage(widget,"General",icon());
}
void Compressor::configAccepted()
{
// Uloží vybraný typ komprese
KConfigGroup cg = config();
cg.writeEntry("compressionType",configDialog.compressionType->currentIndex());
}
void Compressor::dropEvent(QGraphicsSceneDragDropEvent *event)
{
// Oveří, jestli event obsahuje URL adresy (např. file:///home/progdan/soubor.txt)
if(event->mimeData()->hasUrls()) {
// Pokud ano, tak potvrdíme přijetí události
event->acceptProposedAction();
// Dočasně zakážeme přijímat nové drag&drop události
setAcceptDrops(false);
// Zavolámé metodu compress a předáme ji seznam URL adres
compress(event->mimeData()->urls());
}
}
void Compressor::compress(QList<QUrl> files)
{
QStringList params;
// Nastaví aktuální stav a zažádá o překreslení
m_currentState = StateWorking;
update();
// Podle typu komprese nastaví přepínče programu TAR
switch (m_compressionType) {
case GZIPCompression:
params >> "czf";
break;
case BZIPCompression:
params << "cjf";
break;
}
// Jako další za přepínači následuje název cílového souboru. Po dokončení se uživatele
// zeptáme, pod jakým skutečným názvem chce soubor uložit
params << "/tmp/compressor.$$";
// A za ním seznam jednotlivých souborů určených ke kompresi
for (int i = 0; i < files.count(); i++) {
// metoda toString převede QUrl na QString a odstraní počáteční file://
params << files.at(i).toString(QUrl::RemoveScheme);
}
// Vytvoří nový proces
QProcess *tar = new QProcess(this);
// Sváže signal finished procesu tar s naším slotem compressionDone
connect(tar,SIGNAL(finished(int)),this,SLOT(compressionDone(int)));
// spustí program tar s příslušnými parametry
tar->start("tar",params);
}
void Compressor::compressionDone(int exitCode)
{
QString filename;
QString filter;
// Pokud aplikace tar selhala
if (exitCode > 0) {
// Nastaví status error
m_currentState = StateError;
// Překreslí applet
update();
// Vytvoří timer, který za 3 sekundy zavolá slot setDefaultState, který nastaví původní ikonu
QTimer::singleShot(3000,this,SLOT(setDefaultState()));
// Obnovíme přijímání drag&drop
setAcceptDrops(true);
return;
}
// Nastaví stav hotovo
m_currentState = StateDone;
// Zažádá o překreslení appletu
update();
// Vytvoří časovač, který po třech vteřinách zavolá etodu setDefaultState, která nastaví
// stav appletu na StateReady a překreslí ikonu
QTimer::singleShot(3000,this,SLOT(setDefaultState()));
// Podle typu komprese nastaví filter pro dialog o uložení
switch (m_compressionType) {
case GZIPCompression:
filter = "Gzipped tar (*.tar.gz)";
break;
case BZIPCompression:
filter = "Bzipped tar (*.tar.bz2)";
break;
}
// Vytvoří dialog o uložení
filename = QFileDialog::getSaveFileName(0,
QString("Uložit archiv do…"),
QString(),
filter);
// Pokud uživatel dialog zavřel, bude filename prázdné, takže po sobě aspoň uklidíme
if (filename.isEmpty()) {
QProcess::execute("rm",QStringList() << "/tmp/compressor.$$");
} else {
// Pokud ho potvrdil tak přesuneme soubor z /tmp na nové místo
QProcess::execute("mv",QStringList() << "/tmp/compressor.$$" << filename);
}
// Nakonec ještě obnovíme přijímání drag&drop událostí
setAcceptDrops(true);
}
void Compressor::setDefaultState()
{
// Nastaví výchozí stav
m_currentState = StateReady;
// Zažádá o překreslení
update();
}
#include "compressor.moc"
Tady by vše mělo být jasné z komentářů. V kódu určitě chybí ošetření mnoha případů (například inicializace by měla selhat, pokud není nalezen program tar, applet by měl být trochu ukecanější ohledně toho, proč komprese selhala, měla by se kontrolovat práva na čtení a zápis atd.), ale to už si každý může zajistit sám. Pokud chcete applet pořádně otestovat a budete se snažit komprimovat velké soubory, dejte pozor, aby výsledný archiv nebyl větší než vaše /tmp (ano, i to by měl applet nějak řešit…).
plasma-applet-compressor.desktop
[Desktop Entry] Name=Compressor applet Name[cs]=Kompresor applet Type=Service X-KDE-ServiceTypes=Plasma/Applet X-KDE-Library=plasma_applet_compressor X-KDE-PluginInfo-Author=Dan Vrátil X-KDE-PluginInfo-Email=vratil@progdansoft.com X-KDE-PluginInfo-Name=compressor X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-Website=http://www.abclinuxu.cz X-KDE-PluginInfo-Category=Utilities X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true
CMakeLists.txt
# Jméno projektu
project(compressor)
# Co všechno potřebujeme?
find_package(KDE4 REQUIRED)
include(KDE4Defaults)
add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
include_directories(
${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}
${KDE4_INCLUDES}
)
# Přidá zdrojáky
set(compressor_SRCS compressor.cpp)
# Makro kde4_add_ui_files zajistí, že po spuštění make se nejprve z .ui souborů
# vygenerují C++ třídy
kde4_add_ui_files(compressor_SRCS config.ui)
# Zajístí, aby se applet vložil do KDE
kde4_add_plugin(plasma_applet_compressor ${compressor_SRCS})
# Slinkuje applet
target_link_libraries(plasma_applet_compressor
${KDE4_PLASMA_LIBS} ${KDE4_KDEUI_LIBS})
# Nainstaluje knihovnu appletu
install(TARGETS plasma_applet_compressor
DESTINATION ${PLUGIN_INSTALL_DIR})
# Nainstaluje .desktop soubor
install(FILES plasma-applet-compressor.desktop
DESTINATION ${SERVICES_INSTALL_DIR})
Do CMakeLists.txt je nutné přidat makro kde4_add_ui_files, které zajistí vygenerování ui_config.h z config.h.
Pro otestování přetáhněte pár souborů (najednou, jako výběr, nikoliv po jednom) do appletu a počkejte, než vyskočí dialog, kam chcete soubor uložit.
Nakonec ještě přikládám archiv s kompletními kódy včetně config.ui.
Tento díl je posledním dílem mého seriálu o plasmoidech. Samozřejmě, že má Plasma mnohem mnohem více možností než těch pár, které jsem tu popsal, ale i ty by vám měly stačit k pochopení, jak to vlastně celé funguje, a měli byste být schopni napsat jednodušší plasmoid. Jestli se chcete na věc podívat hlouběji, doporučuji projít si dokumentaci Plasmy a Plasma addons.
Pokud si myslíte, že je tu něco, co jsem nezmínil, a zároveň by to mohlo vydat na celý článek, projevte se prosím v diskuzi.
Nástroje: Tisk bez diskuse
Tiskni
Sdílej:
QList>QUrl< ? :)