Creation of a shutdown GUI using Gtk in Python

Δημιουργία μιας γραφικής διεπαφής κλεισίματος με τη χρήση Gtk σε Python

· Coding Προγραμματισμός · linux linux gtk gtk python python

Many window managers in Linux are lacking a graphical «shutdown» interface. Here, we show how we can implement one, quite easily, using gtk and python. In a follow-up article, we show how we can create a transparent GUI using Cairo.

Πολλοί διαχειριστές παραθύρων στο Linux δε διαθέτουν μια γραφική διεπαφή «κλεισίματος». Εδώ, θα δείξουμε πώς μπορείτε να υλοποιήσετε μία, αρκετά εύκολα, χρησιμοποιώντας gtk και python. Σε επόμενο άρθρο μας, δείχνουμε πώς μπορείτε να δημιουργήσετε μια διάφανη διεπαφή χρήστη χρησιμοποιώντας Cairo.

3x3 square shutdown GUI
You can find free icons for every use on Μπορείτε να βρείτε δωρεάν εικονίδια για κάθε χρήση στο openclipart.org

First, let's add the necessary imports:

Πρώτα από όλα, ας προσθέσουμε τα απαραίτητα import:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import gtk
from collections import OrderedDict

We define a get_resource_path function to help us find the path of our icons in the system:

Ορίζουμε μια συνάρτηση get_resource_path για να μας βοηθήσει να βρούμε την τοποθεσία των εικονιδίων μας στο σύστημα:

def get_resource_path(rel_path):
    dir_of_py_file = os.path.dirname(__file__)
    rel_path_to_resource = os.path.join(dir_of_py_file, rel_path)
    abs_path_to_resource = os.path.abspath(rel_path_to_resource)
    return abs_path_to_resource

Next, we define our SystemDialog class (inherits from gtk.Window):

Στη συνέχεια, ορίζουμε την κλάση SystemDialog (κληρονομεί από την gtk.Window):

class SystemDialog (gtk.Window):
    ...

Inside the class we define the __init__ (do not forget to call the super):

Μέσα στην κλάση μας ορίζουμε την __init__ (μην ξεχάσετε να καλέσετε την super):

def __init__(self):
    super(SystemDialog, self).__init__()
    ...

Inside the __init__ we define a dictionary with label/command key-value pairs. The reason we use an OrderedDict is that the simple dict in python does not keep the order of the keys but we do not want our buttons to appear in arbitrary order. Keep in mind that you have to edit the button commands according to your system and your needs.

Μέσα στην __init__ ορίζουμε ένα λεξικό με ζευγάρια από ετικέτες και εντολές. Ο λόγος που χρησιμοποιούμε ένα OrderedDict είναι επειδή το απλό dict στην Python δεν συγκρατεί τη σειρά των λημμάτων (=κλειδιών) αλλά εμείς δε θέλουμε τα κουμπιά μας να εμφανίζονται με αυθαίρετη σειρά. Σημειώστε ότι θα πρέπει να τροποποιήσετε τις εντολές που αντιστοιχούν στα κουμπιά ανάλογα με το σύστημα σάς και τις ανάγκες σάς.

self.actions = OrderedDict([
    ("Cancel", None),
    ("Lock", "slock &"),
    ("Restart WM", "sudo killall dwm &"),
    ("Sleep", "sudo pm-suspend &"),
    ("Hibernate", "sudo pm-hibernate &"),
    ("Sleep + Hibernate", "sudo pm-suspend-hybrid &"),
    ("Logout", "sudo killall X &"),
    ("Reboot", "sudo reboot &"),
    ("Shutdown", "sudo poweroff &")])
    
self.buttons = {}

We make our window borderless, undecorated, centered on screen and we keep it on top.

Ρυθμίζουμε το παράθυρό μας να μην έχει οριογραμμές ή άλλα διακοσμητικά στοιχεία, να είναι κεντραρισμένο στην οθόνη και να παραμένει πάνω από τα άλλα παράθυρα.

self.set_border_width(0)
self.set_decorated(False)
self.set_position(gtk.WIN_POS_CENTER)
self.set_keep_above(True)

We define one vertical box and three horizontal boxes. The vertical box will contain and align vertically the 3 horizontal boxes while each horizontal box while keep and align horizontally 3 of our buttons. Therefore, in the end, we will have a nice 3x3 button square, like this:

Ορίζουμε ένα κάθετο κουτί και τρία οριζόντια κουτιά. Το κάθετο κουτί θα εμπεριέχει και θα στοιχίζει κάθετα τα 3 οριζόντια κουτιά ενώ κάθε οριζόντιο κουτί θα περιέχει και θα στοιχίζει οριζόντια 3 από τα κουμπιά μας. Οπότε, τελικά, θα έχουμε ένα ωραίο τετράγωνο 3x3 γεμάτο με κουμπιά, όπως αυτό:

self.vbox = gtk.VBox(True, 5)
self.hboxes = [gtk.HBox(True, 5),
               gtk.HBox(True, 5),
               gtk.HBox(True, 5)]

The True values is about the homogenous (i.e. the same) size of the children and 5 is the amount of space between them.

Οι τιμές True έχουν να κάνουν με την ισόποση κατανομή του χώρου μεταξύ των περιεχομένων, ενώ το 5 είναι το κενό διάστημα ανάμεσά τους.

Now, let's create our buttons, put some icons and labels on them, connect their click event with a callback event handler and pack them inside the empty horizontal boxes (3 buttons in each horizontal box).

Τώρα, ας δημιουργήσουμε τα κουμπιά μας, ας βάλουμε μερικές ετικέτες και εικονίδια πάνω τους, ας συνδέσουμε το πάτημά τους με έναν επανακαλούμενο χειριστή συμβάντων και ας τα πακετάρουμε μέσα στα άδεια οριζόντια κουτιά (3 κουμπιά σε κάθε οριζόντιο κουτί).

c = 0
boxIndex = 0
for label in self.actions.keys():
    self.buttons[label] = gtk.Button(label)
    ico = gtk.Image()
    ico.set_from_file(get_resource_path("images/"+label+".png"))
    self.buttons[label].set_image(ico)
    self.buttons[label].set_image_position(gtk.POS_TOP)
    self.hboxes[boxIndex].pack_start(self.buttons[label])
    self.buttons[label].connect('clicked', self.callback, label)
    c += 1 
    if not (c % 3):
        boxIndex += 1

And now, let's pack the horizontal boxes inside the vertical box and the vertical box inside our window. Do not forget to show all our widgets with self.show_all().

Και τώρα, ας πακετάρουμε τα οριζόντια κουτιά μέσα στο κάθετο κουτί και το κάθετο κουτί μέσα στο παράθυρό μας. Μην ξεχάσετε να εμφανίσετε όλα τα γραφικά στοιχεία σας με την self.show_all().

for hbox in self.hboxes:
    self.vbox.pack_start(hbox)
self.add(self.vbox)
self.show_all()

Finally, we connect some more events with their callback handlers:

Τέλος, συνδέουμε μερικά ακόμη συμβάντα με τους επανακαλούμενους χειριστές τους:

#If our window is destroyed call self.callback (to exit)
self.connect("delete-event", self.callback)
#If a key is pressed call self.key_press_event
self.connect("key-press-event", self.key_press_event)

This is the our keyboard callback/event handler function. If the user has pressed Escape, we quit:

Αυτή είναι η επανακαλούμενη συνάρτησή μας για τον χειρισμό συμβάντων πληκτρολογίου. Αν ο χρήστης έχει πατήσει το Escape, βγαίνουμε από την εφαρμογή:

def key_press_event(self, widget = None, event = None):
    keyval = event.keyval
    keyval_name = gtk.gdk.keyval_name(keyval)
    if keyval_name == "Escape":
        gtk.main_quit()
    return False

If you would like to check for some key modifier/combination you can -for example- get the ctrl key status like this:

Αν τυχόν επιθυμείτε να ελέγξετε για κάποιο ειδικό πλήκτρο ή συνδυασμό μπορείτε -για παράδειγμα- να λάβετε την κατάσταση του πλήκτρου ctrl ως εξής:

state = event.state
ctrl = (state & gtk.gdk.CONTROL_MASK)

This is the our generic callback function which executes the commands:

Αυτή είναι η γενική μας επανακαλούμενη συνάρτηση που εκτελεί τις εντολές:

def callback(self, widget = None, data = None):
    if (data is not None and 
       data in self.actions and 
       self.actions[data] is not None):
            os.system(self.actions[data])
    gtk.main_quit()

When our program executes, an SystemDialog object is created and gtk.main() is called:

Όταν η εφαρμογή μας εκτελείται, δημιουργείται ένα αντικείμενο SystemDialog και καλείται η gtk.main():

if __name__ == "__main__":
    SystemDialog()
    gtk.main()

Last but not least: In order for our program to function without having to ask for a password, you need to add some lines in your /etc/sudoers file. This is a special system file and you have to run visudo with root privileges to edit it:

Σημαντικό: Προκειμένου η εφαρμογή μας να λειτουργεί χωρίς να χρειάζεται να ζητάει κωδικό, θα πρέπει να προσθέσετε μερικές γραμμές στο αρχείο /etc/sudoers. Πρόκειται για ένα ειδικό αρχείο του συστήματος το οποίο, για να το διορθώσετε, θα πρέπει να τρέξετε την εντολή visudo έχοντας δικαιώματα διαχειριστή:

# visudo
...
username ALL=(root) NOPASSWD: /sbin/reboot
username ALL=(root) NOPASSWD: /sbin/poweroff
username ALL=(root) NOPASSWD: /usr/bin/killall
username ALL=(root) NOPASSWD: /usr/sbin/pm-suspend
username ALL=(root) NOPASSWD: /usr/sbin/pm-suspend-hybrid
username ALL=(root) NOPASSWD: /usr/sbin/pm-hibernate
...

Source codeΠηγαίος κώδικας

Here is the full code listing: shutdown-manager-plain

For the gtk3 version, see here: shutdown-manager-plain-gtk3

Μπορείτε να δείτε τον πλήρη κώδικα εδώ: shutdown-manager-plain

Για την gtk3 έκδοση, δείτε εδώ: shutdown-manager-plain-gtk3

See also...

Δείτε επίσης...