Logo    
Деловая газета CitCity.ru CITKIT.ru - все об Open Source Форумы Все публикации Учебный центр Курилка
CitForum    CITForum на CD    Подписка на новости портала Море(!) аналитической информации! :: CITFORUM.RU
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

23.04.2017

Google
WWW CITForum.ru
С Новым годом!
2006 г.

Создание апплета GNOME
Часть 1

Юревич Юрий, http://gorod-omsk.ru/blog/pythy/

Этот цикл посвящен теме создания апплетов для панели GNOME. Если кратко, апплет - это маленькое приложение, которое встраивается в панель и либо информирует о чем-либо (например, погоде, или о состоянии батареи), либо выполняет какие-либо одноэтапные действия (запускает поиск, изменяет громкость и т.д.).

Я буду создавать простой апплет для включения/выключения прокси в GNOME.

Прежде чем начать, стоит упомянуть один документ, который описывает создание апплета средствами Python и PyGTK: это GNOME applets with Python. Однако, на мой взгляд, у него есть ряд недостатков, которые и побудили меня осветить эту тему по-своему.

Итак, приступим.

В первой части я буду создавать скелет апплета и регистрировать его в GNOME, во второй буду писать функциональную часть, а в третьей - заниматься "полировкой" и украшательствами.

Скелет апплета

Перво-наперво, выделю то, что необходимо для функционирования любого апплета, вне зависимости от его природы:

  • некий виджет-контейнер (например, HBox)
  • некий "полезный" виджет (у меня это будет Label)
  • всплывающая подсказка
  • некое действие по левой кнопки мыши
  • контекстное меню
  • возможность запуска как отдельного приложения (для отладки)
  • регистрация в GNOME как апплета к панели

Займемся реализацией:


import sys
import gtk
import gtk.gdk
import gnome.ui
import gnomeapplet

class GnomeAppletSkeleton(gnomeapplet.Applet):
    """Simple applet skeleton"""

    def __init__(self, applet):
        """Create applet"""

        self.applet = applet
        self.__init_core_widgets()
        self.init_additional_widgets()
        self.init_ppmenu()
        self.__connect_events()
        self.applet.connect("destroy", self._cleanup)
        self.after_init()
        self.applet.show_all()

Прежде чем приступить к пояснениям, скажу о конвенции насчет имен методов. Если имя метод начинается с двух подчеркиваний, то перегружать (переопределять) такой метод нежелательно. Если же имя метода начинается с буквы, то такой метод можно практически безболезненно перегружать. Но все же, если Вы будете писать свой апплет, то все же гляньте код соответствующего метода GnomeAppletSkeleton прежде чем перегружать его.

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

def __init_core_widgets(self):
    """Create internal widgets"""
    self.tooltips = gtk.Tooltips()
    self.hbox = gtk.HBox()
    self.ev_box = gtk.EventBox()
    self.applet.add(self.hbox)
    self.hbox.add(self.ev_box)

Поскольку апплет - безоконный виджет (у него нет окна), то для того, чтобы была возможность реагировать на события, я помещаю EventBox в него. А уж все дополнительные виджеты (в моем случае это будет только Label) добавляются к ev_box.

def init_additional_widgets(self):
    """Create additional widgets"""
    self.label = gtk.Label("Dummy")
    self.ev_box.add(self.label)

Далее, указываю необходимую информацию для контекстного меню (popup menu):

def init_ppmenu(self):
    """Create popup menu"""
    self.ppmenu_xml = """
    <popup name="button3">

    <menuitem name="About Item" verb="About" stockid="gtk-about" />
    </popup>
    """

    self.ppmenu_verbs = [
        ("About", self.on_ppm_about),
    ]

Заметьте, что в XML-описании пункта меню "О программе" нет собственно названия пункта, а лишь его StockID. Это сделано по той простой причине, что пункт меню "О программе" стандартен для большинства приложений и в случае указания StockID Вы получаете:

  • стандартную иконку для данного пункта (причем, с изменением темы оформления GNOME эта иконка может меняться)
  • стандартное название пункта меню, причем автоматически переведенное на нужный язык

Каждый пункт меню имеет "глагол"-действие, который ставится ему в соответствие. self.ppmenu_verbs же задает соответствие между "глаголом"-действием и callback-функцией.

Следующий шаг по созданию апплета - "соединение" callback-функций и событий:

def __connect_events(self):
    """Connect applet's events to callbacks"""
    self.ev_box.connect("button-press-event", self.on_button)
    self.ev_box.connect("enter-notify-event", self.on_enter)
    self.button_actions = {
        1: lambda: None,
        2: lambda: None,
        3: self._show_ppmenu,
    }

Еще раз отмечу, что апплет - безоконный виджет, поэтому все события генерирует ev_box. В данном случае, я соединил события "нажатие на кнопку" с callback-функцией self.on_button и событие "попадание курсора в область виджета" с callback-функцией self.on_enter. Здесь же при помощи словаря self.button_actions задал соответствие между кнопками мыши и функциями-действиями. Стоит заметить, что callback-функции, соединенные с событиями, должны быть определенной сигнатуры (об этом чуть позже), а функции-действия не должны принимать ни один параметр.

Следующий по порядку вызов - это метод after_init. В скелете он пустой, предназначен специально для переопределения в потомках.

С этапами создания апплета вроде завершил, остались callback-функции… Я не буду пересказывать PyGTK reference, лишь перечислю типы callback-функций и их сигнатуры, которые встречаются у меня:

  • callback-функция на событие destroy апплета. Сигнатура function(event). Реализация - _cleanup
  • callback-функция на события ev_box. Сигнатура function(widget, event). Реализации - on_enter, on_button
  • callback-функция на пункт меню. Сигнатура function(event, data=None). Реализация - on_ppm_about
  • функция-действие (мое название) на нажатие одной из кнопок мыши. Сигнатура function(). Реализация - _show_ppmenu.

Содержимое callback-функции _cleanup не буду приводить - оно слишком тривиально (удаляется объект self.applet) для того, чтобы занимать место, а кому интересно - гляньте в полном исходном тексте апплета. Что касается остальных callback-функций, я их приведу и прокомментирую, поскольку они все же представляют интерес.

def on_button(self, widget, event):
    """Action on pressing button in applet"""

    if event.type == gtk.gdk.BUTTON_PRESS:
        self.button_actions[event.button]()

Callback-функция on_button вызывается при нажатии любой кнопки мыши внутри виджета. И внутри этой функции я, во-первых, убеждаюсь, что присоединили к правильному событию (нажатию на клавишу), а, во-вторых, вызываю нужную функцию-действие, выбирая (в event.button хранится номер кнопки, нажатие на которую и вызвало появление данного события) из ранее описанного словаря self.button_actions. Для кнопок 1 и 2 у меня пустые действия, для 3 - контекстное меню. Показ контекстного меню - специальный метод класса Applet - setup_menu. Первый аргумент - XML-описание меню, второй - "глаголы"-действия, третий - пользовательские данные (передаются третьим параметром в callback-функцию).

def _show_ppmenu(self):
    """Show popup menu"""
    self.applet.setup_menu(self.ppmenu_xml, self.ppmenu_verbs, None)

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

def on_enter(self, widget, event):
    """Action on entering"""
    info = "Hey, it just skeletonnAnd on_enter event time is %d" % 
           event.time
    self.tooltips.set_tip(self.ev_box, info)


И последняя callback-функция - на вызов пункта меню "О программе". Здесь я воспользуюсь стандартным диалогом из модуля gnome.ui:

def on_ppm_about(self, event, data=None):
    """Action on choosing 'about' in popup menu"""
    gnome.ui.About("GNOME Applet Skeleton", "0.1", "GNU General Public License v.2",
                    "Simple skeleton for Python powered GNOME applet",
                    ["Pythy <the.pythy@gmail.com>",]
                   ).show()


Класс-костяк апплета написан, теперь нужно описать его "фабрику":

def applet_factory(applet, iid):
    GnomeAppletSkeleton(applet, iid)
    return True

Ух. С первым этапом закончил. Костяк апплета сделан. Осталось дело за малым. Запустить и посмотреть, что же получилось :)

Запуск апплета в отдельном окне

Для начала нужно отладить апплет, для этого пишу код, позволяющий запускать апплет в отдельном окне:

def run_in_window():
    main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
    main_window.set_title("GNOME Applet Skeleton")
    main_window.connect("destroy", gtk.main_quit)
    app = gnomeapplet.Applet()
    applet_factory(app, None)
    app.reparent(main_window)
    main_window.show_all()
    gtk.main()
    sys.exit()

def main(args):
    if len(args) == 2 and args[1] == "run-in-window":
        run_in_window()
    else:
        run_in_panel()

if __name__ == '__main__':
    main(sys.argv)


Небольшой комментарий по коду: если файл запускается как скрипт, то выполняется функция main, в ней, в зависимости от того, передан ли аргумент run-in-window, апплет запускается либо в окне (функция run_in_window), либо в панели (run_in_panel). Про функцию run_in_panel чуть ниже, а в run_in_window стоит обратить внимание на строчку app.reparent(main_window). Этим собственно и достигается, что апплет запускается в отдельном окне.

Как выглядит костяк апплета, можно увидеть на скриншотах:

Скелет апплета, в оконном режиме, вместе с всплывающей подсказкой

Скелет апплета, в оконном режиме, вместе с контекстным меню

Скелет апплета, в оконном режиме, вместе с диалогом “О программе”

Регистрация апплета в панели GNOME

Если выше был обычный Python-код, с некоторой PyGTK-спецификой, то сейчас будет сплошная магия ;) Это, кстати, одна из слабых сторон GNOME-Python - отсутствие систематической документации (для gnomeapplet вообще никакой документации нет, за исключением пары примеров и вышеупомянутой "методички"). К примеру, при регистрации апплета вызывается функция applet_bonobo_factory, однако нигде не упоминается, какие параметры в нее передаются. Чтобы узнать это, нужно лезть в исходные тексты. Я, конечно, понимаю, что "Use code, Luke!", но все же качество документации по PyGTK в целом хромает (например, сплошь и рядом в документации рекомендуются методы, которые уже пару версий назад как уже объявлены устаревшими).

Общая идеология регистрации апплета такова:

  • описываем мета-информацию в специальном .server файле
  • в апплете вызываем специальный интерфейс

Вначале закончу дело с кодом апплета:

def run_in_panel():
    gnomeapplet.bonobo_factory("OAFIID:GNOME_AppletSkeleton_Factory",
                               GnomeAppletSkeleton.__gtype__,
                               "Applet skeleton",
                               "0",
                               applet_factory)

это и есть вызов "специального интерфейса". Параметры такие: IID (уникальный идентификатор сервиса в GNOME), тип (это остатки C-природы GTK, тип GObject), имя, версия, callback-функция.

Теперь, что касается "описания мета-информации". Пишем следующий XML (он для всех апплетов будет идентичным, специфичные для моего апплета данные я выделил полужирным):

<oaf_info>

<oaf_server iid="OAFIID:GNOME_AppletSkeleton_Factory"
                    type="exe"                    location="/usr/local/lib/pygnomeapplet/applet_skeleton.py">

    <oaf_attribute name="repo_ids" type="stringv">
        <item value="IDL:Bonobo/GenericFactory:1.0" />
        <item value="IDL:Bonobo/Unknown:1.0" />

    </oaf_attribute>
    <oaf_attribute name="name" type="string" value="Applet skeleton factory" />
    <oaf_attribute name="name-ru" type="string" value="Фабрика скелета апплета" />

    <oaf_attribute name="description" type="string" value="Factory of simple applet skeleton" />
    <oaf_attribute name="description-ru" type="string" value="Фабрика скелета простейшего апплета" />

</oaf_server>
<oaf_server iid="OAFIID:GNOME_AppletSkeleton"
                    type="factory"                    location="OAFIID:GNOME_AppletSkeleton_Factory">
    <oaf_attribute name="repo_ids" type="stringv">

        <item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0" />
        <item value="IDL:Bonobo/Control:1.0" />
        <item value="IDL:Bonobo/Unknown:1.0" />

    </oaf_attribute>
    <oaf_attribute name="name" type="string" value="Applet skeleton" />
    <oaf_attribute name="name-ru" type="string" value="Скелет апплета" />

    <oaf_attribute name="description" type="string" value="Simple applet skeleton, do nothing" />
    <oaf_attribute name="description-ru" type="string" value="Скелет простого апплета, ни делает ни чего" />

    <oaf_attribute name="panel:category" type="string" value="Accessories" />
    <oaf_attribute name="panel:icon" type="string" value="gnome-panel.png" />

</oaf_server>
</oaf_info>

Так, что тут: два раздела, фабрика и сам апплет. Для каждого определены IID, у фабрики IID должен совпадать с тем, что указали в вызове bonobo_factory в апплете. Дополнительно отмечу, что тут же можно задавать переводы названия/описания апплета (в данном случае будет на русском, если у Вас русская локаль и на английском во всех остальных случаях). Называем этот файл GNOME_AppletSkeleton.server и "скармливаем" его Bonobo Activation Server. Существует несколько вариантов этого "действа":

  1. Поместить .server в каталог /usr/lib/bonobo/servers
  2. Изменить /etc/bonobo-activation/bonobo-activation-config.xml (там есть несколько примеров), добавить нужный путь (скажем, /usr/local/lib/bonobo/servers) и положить .server туда
  3. В переменную BONOBO_ACTIVATION_PATH добавить каталог, где лежит .server.

Мне наиболее правильным показался второй вариант, я его и использовал.

После этого скрещиваем пальцы и пытаемся добавить апплет на панель. Если .server правильно "скормили", то апплет-скелет появляется в списке кандидатов на добавление. Если и все остальное сделали верно, то добавление пройдет гладко. И Вы получите примерно такой результат:

Рисунок. Скелет апплета, в панельном режиме, добавление апплета на панель

Скелет апплета, в панельном режиме, вместе с всплывающей подсказкой

Скелет апплета, в панельном режиме, вместе с контекстным меню

Стоит отметить, что контекстное меню в "режиме окна" и в "режиме панели" отличаются - для панели появляются дополнительные пункты меню.

На сегодня, я думаю, достаточно. Полный код примера Вы можете взять здесь. В следующий раз скелет будет обрастать мясом ;)


Часть 2

Размещение рекламы — тел. +7 495 4119920, ICQ 232284597

Подписка на новости IT-портала CITForum.ru
(библиотека, CITKIT.ru, CitCity)

Новые публикации:

24 декабря

CITKIT.ru:

  • Новогодние поздравления
  • Сергей Кузнецов. Цикл Операционные системы: Ностальгия по будущему:

  • Алексей Федорчук. OpenSolaris 2008.11 Release

  • Сергей Голубев:

  • Евгений Чайкин aka StraNNik (Блогометки):

    17 декабря

  • С.Д.Кузнецов. Базы данных. Вводный курс

    10 декабря

    CITKIT.ru:

  • OpenSolaris 2008.11 Release

  • Альтернативные ОС: две грустные истории (С.Кузнецов)
  • Nokia N810 — доведение до ума
  • CitCity:

  • Платформа 2009: заоблачные перспективы Microsoft

    4 декабря

  • Лекция С.Д.Кузнецова Понятие модели данных. Обзор разновидностей моделей данных

    CITKIT.ru:

  • OpenSolaris 2008.11 Release. Первые впечатления

  • Linux vs FreeBSD: продолжим "Священные войны"?

  • Nokia N810 as is

  • Индульгенция для FOSS

  • Друзья СПО'2008

    26 ноября

  • Нечеткое сравнение коллекций: семантический и алгоритмический аспекты

    CitCity:

    CITKIT.ru:

  • Глава из книги А.Федорчука
    Сага о FreeBSD:
  • 19 ноября

  • Проблемы экономики производства крупных программных продуктов

  • Язык модификации данных формата XML функциональными методами

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

    Заметки к книге:

  • FreeBSD: монтирование сменных устройств и механизм HAL
  • Текстовый редактор ee

    12 ноября

  • Правило пяти минут двадцать лет спустя, и как флэш-память изменяет правила (Гоц Грейф, перевод: Сергей Кузнецов)

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:
  • OSS в России: взгляд правоведа (В.Житомирский)

  • Новая статья из цикла С.Голубева "Железный марш":

    29 октября

  • О некоторых задачах обратной инженерии

  • Веб-сервисы и Ruby

  • Тестирование web-приложений с помощью Ruby

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

  • PuppyRus Linux - беседа с разработчиком (С.Голубев)

  • Сергей Кузнецов. Заметка не про Linux

    22 октября

  • Обзор методов описания встраиваемой аппаратуры и построения инструментария кросс-разработки

    CITKIT.ru:

  • Сергей Кузнецов. Почему я равнодушен к Linux

  • Глава из книги А.Федорчука
    Сага о FreeBSD:
  • Что надо иметь
    3. Базовые познания

    CitCity:

  • Управление IT-инфраструктурой на основе продуктов Microsoft

    15 октября

  • Методы бикластеризации для анализа интернет-данных

    CitCity:

  • Разъемы на ноутбуках: что они дают и зачем их так много?
  • AMD Puma и Intel Centrino 2: кто лучше?

    CITKIT.ru:

  • Новый цикл статей С.Голубева
    Железный марш:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

    8 октября

  • Автоматизация тестирования web-приложений, основанных на скриптовых языках
  • Опыт применения технологии Azov для тестирования библиотеки Qt3

    Обзоры журнала Computer:

  • SOA с гарантией качества
  • Пикоджоуль ватт бережет
  • ICT и всемирное развитие

    CitCity:

  • Пиррова победа корпорации Microsoft

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

    Статья из архива:

  • Я живу в FreeBSD (Вадим Колонцов)

    Новые Блогометки:

  • Перекройка шаблона Blogger или N шагов к настоящему
  • Blogger. Comment style
  • Screenie или глянцевый снимок экрана

    2 октября

    CITKIT.ru:

  • Сага о FreeBSD (А. Федорчук)

    Zenwalk: пакет недели

  • Банинг — интеллектуальное развлечение (С.Голубев)

    CitCity:

    25 сентября

  • Клермонтский отчет об исследованиях в области баз данных

    CITKIT.ru:

  • Пользователям просьба не беспокоиться... (В.Попов)

  • Снова про ZFS: диск хорошо, а два лучше
  • Командная оболочка tcsh (А.Федорчук)

    Zenwalk: пакет недели

    17 сентября

  • T2C: технология автоматизированной разработки тестов базовой функциональности программных интерфейсов
  • Технология Azov автоматизации массового создания тестов работоспособности

    CITKIT.ru:

  • FreeBSD: ZFS vs UFS, и обе-две — против всех (А.Федорчук)

    Zenwalk: пакет недели

  • Дачнет — практика без теории (С.Голубев)

    10 сентября

  • За чем следить и чем управлять при работе приложений с Oracle
  • Планировщик заданий в Oracle
    (В.Пржиялковский)

    CITKIT.ru:

  • Microsoft: ответный "боян" (С.Голубев)

  • Причуды симбиоза, или снова "сделай сам" (В.Попов)

  • Файловые системы современного Linux'а: последнее тестирование
  • Zsh. Введение и обзор возможностей
    (А.Федорчук)

    Описания пакетов Zenwalk: Zsh, Thunar, Thunar-bulk-rename, Xfce4-places-plugin, Xfce4-fsguard-plugin

    Блогометки:

  • Google Chrome
  • Лончер для ASUS Eee PC 701

    3 сентября

    CITKIT.ru:

  • Заметки о ядре (А.Федорчук):

    Добавлены описания пакетов Zenwalk: Galculator, Screenshot, Gnumeric, Pidgin

    В дискуссинном клубе:

  • И еще о Википедии и Google Knol

  • Лекция для начинающего линуксоида (С.Голубев)

    26 августа

  • Транзакционная память (Пересказ: С. Кузнецов)

    CITKIT.ru:

  • Открыт новый проект Zenwalk: пакет недели

  • Статья Текстовые процессоры и их быстродействие: конец еще одной легенды?

    21 августа

    CITKIT.ru:

  • Почему школам следует использовать только свободные программы (Ричард Столлман)
  • Беседа Сергея Голубева с учителем В.В.Михайловым

  • Википедия или Гуглезнание? Приглашение к обсуждению (Алексей Федорчук)
  • Народная энциклопедия от Google (StraNNik)

  • Обзор Mandriva 2009.0 Beta 1 Thornicrofti
  • Новичок в Линукс: Оптимизируем Mandriva 2008.1

  • Книга Zenwalk. Приобщение к Linux:

    13 августа

    CitCity:

  • Мирный Atom на службе человеку. Обзор платы Intel D945GCLF с интегрированным процессором
  • Обзор процессоров Intel Atom 230 на ядре Diamondville

  • iPhone - год спустя. Скоро и в России?

    CITKIT.ru:

  • Интермедия 3.4. GRUB: установка и настройка (из книги Zenwalk. Приобщение к Linux)

    6 августа

  • СУБД с хранением данных по столбцами и по строкам: насколько они отличаются в действительности? (Пересказ: С. Кузнецов)

    CITKIT.ru:

  • Интермедия 2.2. Что неплохо знать для начала (из книги Zenwalk. Приобщение к Linux)

  • И снова про шрифты в Иксах (А.Федорчук)

  • 20 самых быстрых и простых оконных менеджеров для Linux

  • Дело о трех миллиардах (С.Голубев)

    30 июля

  • OLTP в Зазеркалье (Пересказ: С. Кузнецов)

    CitCity:

  • Будущее BI в облаках?
  • Тиражные приложения и заказная разработка. Преимущества для заказчика
  • Дискуссия со сторонниками заказной разработки

    CITKIT.ru:

  • Новые главы книги Zenwalk. Приобщение к Linux:
  • Глава 8. Пакеты: средства установки, системы управления, системы построения
  • Глава 9. Zenwalk: репозитории, пакеты, методы установки

    23 июля

    CITKIT.ru:

  • Все против всех. 64 vs 32, Intel vs AMD, tmpfs vs ext3
  • Две головы от Intel

  • Zenwalk: обзор штатных приложений (глава из книги "Zenwalk. Приобщение к Linux")

  • Нормально, Григорий...

    16 июля

    Обзоры журнала Computer:

  • Перспективы и проблемы программной инженерии в XXI веке
  • Большие хлопоты с большими объемами данных
  • Перспективы наноэлектроники

    CITKIT.ru:

  • Интермедия о лицензиях (А.Федорчук. "Zenwalk. Приобщение к Linux")

  • Есть ли будущее у KDE?

  • Linux в школе: альтернативный вариант в задачах

  • Шифр (приключения агента Никодима)

    10 июля

    CITKIT.ru:

  • Новые разделы книги А. Федорчука Zenwalk. Приобщение к Linux:
  • Интермедия вступительная. Linux или GNU/Linux? Как вас теперь называть?
  • Глава 5. Среда Xfce
  • Глава 6. Xfce: приложения и плагины

  • ZUR (Zenwalk User Repository) FAQ

    2 июля

  • Персистентность данных в объектно-ориентированных приложениях (С. Кузнецов)

    CITKIT.ru:

  • Новые разделы книги А. Федорчука Zenwalk. Приобщение к Linux:
  • Интермедия 1.2. Дорога к Zenwalk'у. Период бури и натиска
  • Интермедия 3.3. Немного о Linux'е и "железе"
  • Глава 4. Настройка: инструментами и руками
  • Интермедия 4.1. Zenpanel и конфиги: поиски корреляции

  • Интервью с Жан-Филиппом Гийоменом, создателем дистрибутива Zenwalk

  • Linux в школе: первые итоги (С. Голубев)

    25 июня

    CITKIT.ru:

  • Zenwalk. Приобщение к Linux (А. Федорчук)

  • Логика и риторика (С.Голубев)

  • Технология Tru64 AdvFS

  • Ханс Райзер предлагает отвести полицейских к телу Нины

    18 июня

  • Проекты по управлению данными в Google (Пересказ: С. Кузнецов)

    CITKIT.ru:

  • ОС и поддержка "железа": мифы и реальность (А. Федорчук)

  • Linux в школе: другие дистрибутивы

  • Пинок (С. Голубев)

    4 июня

  • Ландшафт области управления данными: аналитический обзор (С. Кузнецов)

    CITKIT.ru:

  • Linux в школе: слово заинтересованным лицам

  • SlackBuild: пакеты своими руками

  • Linux от компании Novell. Установка и обзор openSUSE Linux

    Все публикации >>>




  • IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

    Информация для рекламодателей PR-акции, размещение рекламы — тел. +7 495 4119920, ICQ 232284597 Пресс-релизы — pr@citcity.ru
    Послать комментарий
    Информация для авторов
    Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
    Copyright © 1997-2000 CIT, © 2001-2007 CIT Forum
    Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...