We write a useful program for KDE4 in python in two hours
A couple of free hours appeared at work and I decided to make life easier for myself.
By type of activity (and I work as a programmer) there is a lot to do on remote servers, access to which is available only via ssh. And it is most convenient to write and debug programs locally, and only then put them on a working machine. Therefore, it is convenient to use sshfs. However, I’m tired of typing the mount command every time, writing a script on the tower is too lazy. So I wanted to have a sshfs mounts graphical manager, and everything else in KDE4.
Naturally, from writing his own, he resisted to the last. Google opened up to give me answers. But I did not find anything suitable.
ksshfs earned for some reason only in KDE3
sshfsgui also did not want to work, citing some Java errors. I tried several different versions and implementations of yavamashin - it did not help.
So you have to do it yourself.
For starters, how do you create applications for KDE4? I learned this knowledge from the article “Programming for KDE4” .
The rest was helped by the documentation .
In terms of interface, I focused on sshfsgui. This is where we start.
First of all, we take the framework for the application:
I will say one thing - it will already be launched, and this thought warms my soul. It looks like this:

Since the program is in two hours, and indeed simple, I placed everything related to the interface in the __init__ method. Do not torture yourself with Qt Designer, but just write code.
It looks like this:
So, after all this, we have a program that displays us a form:

Now we need to breathe life into the framework of our program.
Since the user (i.e., I) will perform all actions by clicking on the buttons and selecting a profile in the list of saved profiles, it is necessary to install event handlers on these elements. The mechanism of signals and slots will help us with this.
It's simple:
Now it's up to you to write the handlers themselves. Let's start in order: saving a profile and deleting a profile.
We will store the profiles in the user's home directory in ~ / .pyksshfs / hosts /.
One file per profile. The file name is what is called “Name” in the form.
It is logical that at startup, the program should check whether there is such a directory and create it in case of absence.
To do this, add the following non-sophisticated code after the program description:
And to the beginning of the file with the import os program.
Thinking about how best to store the values of the form fields in the file, I thought that in python there probably is a ready-made module for storing configs. And so it happened.
A minute googling immediately gave the result: import ConfigParser
So, the onSave method:
At the end of writing the method, the idea comes that it would be nice if the new profile appeared immediately in the list, and when you open the program, you also need to display a list of saved profiles.
So we write immediately the method of receiving and listing the output and insert its call at the end of __init__ and onSave.
(For some reason, the Habr does not want to display 0 in the code, replaced it with a capital letter O).
Let's go further. Method for unmounting a remote directory. There is basically nothing to explain.
Mounting is much more interesting. I tormented this part the longest. I’ll tell you a secret that it was because of this method that I spent much more than two hours. But in fact, the problems were of such a nature that if I had known about them before, I would have completely met the deadline given in the heading.
What is the problem: the command to mount the directory through ssh is interactive and requires a password from the user. But in case authorization by keys is done, it does not require. Accordingly, it is necessary to form a command, execute, find out if the password is being asked, then ask the user. And if the password is not needed, then do not touch the user.
The sshfs command has a parameter that allows you to pass a password with stdin. But then the user will have to ask in advance what is not very good when the password is not needed.
There is another subtlety. If we have never visited the server via ssh, we will be asked - “do we trust him?” And you will need to enter yes.
In general, we need to somehow handle these cases. To solve such problems, there is a pexpect module (import pexpect). With it, you can work with interactive programs (for example telnet, ftp, ssh). Well, it's time to show the code.
I took part of the code from the linux-volume-manager-fuse-kde4 project , because At first, my code didn’t want to work, but after my code worked, I decided to leave this one, because It handles more options.
To get the password from the user, I used the ksshaskpass program. Firstly, in order not to write, and secondly, she knows how to save / receive a password from kwalletd, which is very convenient.
The original code didn’t work at all due to the fact that according to the ksshaskpass documentation, it should return a password, but instead of that, in addition to the password, it returns some other debugging line. It had to be filtered out like this:
password = pexpect.run ('ksshaskpass') .split ('\ n') [1]
By the way, if the debugging line suddenly disappears, the program will stop working.
Almost everything is ready. The last action remains: load the profile when the user selects it from the list. Immediately the code.
That's all. In just a couple of hours, using only python syntax, google and a black belt, I made quite a working program for copy-paste, which I now intend to use.
Perhaps in the article I missed some part of the code.
So it is best to download the full working pyKSshfs version .
Finally a screenshot:

By the middle of writing the program, I thought that it would be more convenient as a plasma applet. And it should look like an applet for mounting flash drives. But as I was busy with ksshaskpass, I decided to postpone it. Maybe soon I will do it. Or maybe one of you will get ahead of me - I will only be glad.
Thanks to everyone who could read all this, I know it was not easy. =)
Good luck to everyone!
By type of activity (and I work as a programmer) there is a lot to do on remote servers, access to which is available only via ssh. And it is most convenient to write and debug programs locally, and only then put them on a working machine. Therefore, it is convenient to use sshfs. However, I’m tired of typing the mount command every time, writing a script on the tower is too lazy. So I wanted to have a sshfs mounts graphical manager, and everything else in KDE4.
Alternatives
Naturally, from writing his own, he resisted to the last. Google opened up to give me answers. But I did not find anything suitable.
ksshfs earned for some reason only in KDE3
sshfsgui also did not want to work, citing some Java errors. I tried several different versions and implementations of yavamashin - it did not help.
So you have to do it yourself.
Read
For starters, how do you create applications for KDE4? I learned this knowledge from the article “Programming for KDE4” .
The rest was helped by the documentation .
Frame
In terms of interface, I focused on sshfsgui. This is where we start.
First of all, we take the framework for the application:
from PyKDE4.kdeui import KApplication, KMainWindow, KPushButton, KHBox, KVBox, KLineEdit, KListWidget
from PyKDE4.kdecore import i18n, ki18n, KAboutData, KCmdLineArgs
from PyQt4 import QtCore
from PyQt4.QtGui import Qlabel
import sys
class pyksshfsWindow(KMainWindow):
selected_name = False
def __init__(self, parent = None): #конструктор
KMainWindow.__init__(self, parent) #call parent constructor
appName = "pyksshfs"
catalog = ""
programName = ki18n("PyKSshfs")
version = "0.1"
description = ki18n ("Gui application for using sshfs")
license = KAboutData.License_GPL
copyright = ki18n ("© Akademic")
text = ki18n ("none") homePage = "email
site for the
bugEmail =" program email errors "
aboutData = KAboutData (appName, catalog, programName, version, description, license, copyright, text, homePage, bugEmail)
KCmdLineArgs.init (sys.argv, aboutData)
app = KApplication ()
w = pyksshfsWindow ()
w.show ( )
app.exec_ ()
I will say one thing - it will already be launched, and this thought warms my soul. It looks like this:

Interface
Since the program is in two hours, and indeed simple, I placed everything related to the interface in the __init__ method. Do not torture yourself with Qt Designer, but just write code.
It looks like this:
def __init __ (self, parent = None): # constructor
KMainWindow .__ init __ (self, parent) #call parent constructor
hbox = KHBox (self) # create a horizontal layer
hbox.setMargin (10) #
indent 10 pixels self.setCentralWidget (hbox) # make it main
# two vertical layers inside the main horizontal
vbox_left = KVBox (hbox)
vbox_right = KVBox (hbox)
# align the right layer on top of
hbox.layout (). setAlignment (vbox_right, QtCore.Qt.AlignTop)
# data entry fields to mount
entry_name_label = QLabel ('Name:', vbox_right)
self.entry_name = KLineEdit (vbox_right)
server_address_label = QLabel ('Server address:', vbox_right)
self.server_address = KLineEdit (vbox_right)
server_port_label = QLabel ('Server port:', vbox_right)
self.server_port = KLineEdit (vbox_right)
user_name_label = QLab )
self.user_name = KLineEdit (vbox_right)
remote_path_label QLabel = ( 'Remote path:', vbox_right)
self.remote_path = KLineEdit (vbox_right)
local_path_label QLabel = ( 'Local path:', vbox_right)
self.local_path = KLineEdit (vbox_right)
# mount and unmount buttons
# create a separate layer for them
btn_hbox_right = KHBox (vbox_right)
connect_btn = KPushButton (btn_hbox_right)
connect_btn.setText (i18n ('Connect'))
disconnect_btn = KPushButton (btn_hbox_right)
disconnect_btn.setText (i18n)' ellist_ellist_elconn_list
saved
connections Stored: ', vbox_left)
self.saved_list = KListWidget (vbox_left)
self.saved_list.setMaximumWidth (150)
# save and delete buttons profiles
btn_hbox_left = KHBox (vbox_left)
save_btn = KPushButton (btn_hbox_left)
save_btn.setText (i18n (' save ' )))
delete_btn = KPushButton (btn_hbox_left)
delete_btn.setText (i18n ('Delete'))
So, after all this, we have a program that displays us a form:

Event handling
Now we need to breathe life into the framework of our program.
Since the user (i.e., I) will perform all actions by clicking on the buttons and selecting a profile in the list of saved profiles, it is necessary to install event handlers on these elements. The mechanism of signals and slots will help us with this.
It's simple:
#binding event handlers to buttons #here save_btn is a variable containing the save button object
# QtCore.SIGNAL ('clicked ()') is a “click on a button” signal
# self.onSave is a method called to process a click
self.connect (save_btn , QtCore.SIGNAL ('clicked ()'), self.onSave)
self.connect (delete_btn, QtCore.SIGNAL ('clicked ()'), self.onDelete)
self.connect (connect_btn, QtCore.SIGNAL ('clicked ( ) '), self.onConnect)
self.connect (disconnect_btn, QtCore.SIGNAL (' clicked () '), self.onDisconnect)
# the most difficult thing was to find in the documentation the name of the signal “clicked on an item in the list”
self.connect ( self.saved_list, QtCore.SIGNAL ('itemClicked (QListWidgetItem *)'), self.onSelectServer)
Saving Profile
Now it's up to you to write the handlers themselves. Let's start in order: saving a profile and deleting a profile.
We will store the profiles in the user's home directory in ~ / .pyksshfs / hosts /.
One file per profile. The file name is what is called “Name” in the form.
It is logical that at startup, the program should check whether there is such a directory and create it in case of absence.
To do this, add the following non-sophisticated code after the program description:
config_path = os.getenv ('HOME') + '/. pyksshfs / hosts'
if not os.path.isdir (config_path):
os.makedirs (config_path, 0700)
And to the beginning of the file with the import os program.
Thinking about how best to store the values of the form fields in the file, I thought that in python there probably is a ready-made module for storing configs. And so it happened.
A minute googling immediately gave the result: import ConfigParser
So, the onSave method:
def onSave (self):
'' '
save settings
' ''
if self.entry_name.text (): # If there is a profile name
config = ConfigParser.RawConfigParser () # then create and fill in the
config.add_section ('Connection') config
config.set ('Connection', 'host', self.server_address.text ())
config.set ('Connection', 'port', self.server_port.text ())
config.set ('Connection', 'user_name ', self.user_name.text ())
config.set (' Connection ',' remote_path ', self.remote_path.text ())
config.set (' Connection ',' local_path ', self.local_path.text ())
if self.selected_name:
os.unlink (self.config_path + '/' + self.selected_name)
path = self.config_path + '/' + self.entry_name.text ()
file = open (path, 'w')
config.write (file) # save the config
file.close ()
self.selected_name = self.entry_name.text ()
self.listServers () # update the list of profiles
Profiles List
At the end of writing the method, the idea comes that it would be nice if the new profile appeared immediately in the list, and when you open the program, you also need to display a list of saved profiles.
So we write immediately the method of receiving and listing the output and insert its call at the end of __init__ and onSave.
def listServers (self):
self.saved_list.clear ()
hosts = os.listdir (self.config_path)
self.saved_list.insertItems (O, hosts) # with this call we add the list of files to the list widget
if self.selected_name: # If we have already selected a profile, then it should be selected in the list
item = self.saved_list.findItems (self.selected_name, QtCore.Qt.MatchExactly)
self.saved_list.setItemSelected (item [O], True)
(For some reason, the Habr does not want to display 0 in the code, replaced it with a capital letter O).
Unmounting
Let's go further. Method for unmounting a remote directory. There is basically nothing to explain.
def onDisconnect (self):
if (self.local_path.text ()):
os.system ('fusermount -u' + str (self.local_path.text ()))
Mounting
Mounting is much more interesting. I tormented this part the longest. I’ll tell you a secret that it was because of this method that I spent much more than two hours. But in fact, the problems were of such a nature that if I had known about them before, I would have completely met the deadline given in the heading.
What is the problem: the command to mount the directory through ssh is interactive and requires a password from the user. But in case authorization by keys is done, it does not require. Accordingly, it is necessary to form a command, execute, find out if the password is being asked, then ask the user. And if the password is not needed, then do not touch the user.
The sshfs command has a parameter that allows you to pass a password with stdin. But then the user will have to ask in advance what is not very good when the password is not needed.
There is another subtlety. If we have never visited the server via ssh, we will be asked - “do we trust him?” And you will need to enter yes.
In general, we need to somehow handle these cases. To solve such problems, there is a pexpect module (import pexpect). With it, you can work with interactive programs (for example telnet, ftp, ssh). Well, it's time to show the code.
def onConnect (self):
command = 'sshfs'
if self.user_name.text ():
command + = self.user_name.text () + '@'
command + = self.server_address.text ()
if self.remote_path.text ():
command + = ':' + self.remote_path.text ()
else :
command + = ': /'
if self.server_port.text ():
command + = '-p' + self.server_port.text ()
command + = '' + self.local_path.text ()
sshfs = pexpect.spawn (str (command), env = { 'SSH_ASKPASS': '/ dev / null' } )
ssh_newkey = 'Are you sure you want to continue connecting ''
i = sshfs.expect ([ssh_newkey, 'assword:', pexpect.EOF, pexpect.TIMEOUT])
if i == 0:
sshfs.sendline ('yes')
i = sshfs.expect ([ssh_newkey, 'assword:' , pexpect.EOF])
if i == 1:
#If no password ask for it
askpasscmd = 'ksshaskpass% s'% self.entry_name.text ()
password = pexpect.run (askpasscmd) .split ('\ n') [1]
sshfs.sendline (password)
j = sshfs.expect ([pexpect.EOF, 'assword:'])
if j == 1:
#Password incorrect, force the connection close
print "Password incorrect"
sshfs.close (True )
#p.terminate (True)
elifi == 2:
#Any problem
print "Error found:% s"% sshfs.before
elif i == 3:
#Timeout
print "Timeout:% s"% sshfs.before
print sshfs.before
I took part of the code from the linux-volume-manager-fuse-kde4 project , because At first, my code didn’t want to work, but after my code worked, I decided to leave this one, because It handles more options.
To get the password from the user, I used the ksshaskpass program. Firstly, in order not to write, and secondly, she knows how to save / receive a password from kwalletd, which is very convenient.
The original code didn’t work at all due to the fact that according to the ksshaskpass documentation, it should return a password, but instead of that, in addition to the password, it returns some other debugging line. It had to be filtered out like this:
password = pexpect.run ('ksshaskpass') .split ('\ n') [1]
By the way, if the debugging line suddenly disappears, the program will stop working.
Profile upload
Almost everything is ready. The last action remains: load the profile when the user selects it from the list. Immediately the code.
def onSelectServer (self, item):
"" "
get settings from file, when item selected in seved_list
" ""
name = item.text () # file name
self.selected_name = name # remember the choice
config = ConfigParser.RawConfigParser ()
config .readfp (open (self.config_path + '/' + name)) # open the config
# fill the form fields from the
config self.entry_name.setText (name)
self.server_address.setText (config.get ('Connection', 'host') )
self.server_port.setText (config.get ('Connection', 'port'))
self.user_name.setText (config.get ('Connection', 'user_name'))
self.remote_path.setText (config.get ('Connection', 'remote_path'))
self.local_path.setText (config.get ('Connection', 'local_path'))
Result
That's all. In just a couple of hours, using only python syntax, google and a black belt, I made quite a working program for copy-paste, which I now intend to use.
Perhaps in the article I missed some part of the code.
So it is best to download the full working pyKSshfs version .
Finally a screenshot:

Plans
By the middle of writing the program, I thought that it would be more convenient as a plasma applet. And it should look like an applet for mounting flash drives. But as I was busy with ksshaskpass, I decided to postpone it. Maybe soon I will do it. Or maybe one of you will get ahead of me - I will only be glad.
References
- Download pyKSshfs .
- "Programming for KDE4"
- PyQt documentation
- Kommander script ksshfs
- Sshfsgui java program
- Part of the code was taken here.
Thanks for attention!
Thanks to everyone who could read all this, I know it was not easy. =)
Good luck to everyone!