Syndicate

Syndicate content

Flattr


Flattr this

If you like this, you can use flattr. ;)

Imprint

About
eMail: wishinet at gmail . com
PGP ID: 0xCCCA5E74

Jabber: wishi@jabber.ccc.de

Tags for this post

PyQt and a SSH upload droplet

txttxt

Modern GUIs need Drag and Drop

The following is an example for a drag & drop action with PyQt4. It uses paramiko for SSH interactions. I'm well aware that it won't work on Windows that way. But that's a Windows problem. I'm also well aware that there's a password in this file. Give it a try.
The source is at GitHub. The indention seems to be broken there. But that's a GitHub problem. It seems to be broken here, too. But that's a Drupal problem. ;). Actually it isn't even a problem.

Just the imports. The os module is necessary if you want paramiko to use your private ssh-key. The sys module is needed due argv:

  1. #ff7700;font-weight:bold;">import #dc143c;">sys
  2. #ff7700;font-weight:bold;">import paramiko #808080; font-style: italic;"># for ssh
  3. #ff7700;font-weight:bold;">import #dc143c;">os  
  4. #ff7700;font-weight:bold;">from PyQt4 #ff7700;font-weight:bold;">import QtGui, QtCore
  5. #ff7700;font-weight:bold;">from mainwindow #ff7700;font-weight:bold;">import Ui_MainWindow

I pretty much guess that's self-explaining now. That's the standard header to initiate the form:

  1. #ff7700;font-weight:bold;">class gui_does(QtGui.QMainWindow):
  2.    #ff7700;font-weight:bold;">def #0000cd;">__init__(#008000;">self):
  3.       QtGui.QMainWindow.#0000cd;">__init__(#008000;">self)
  4.       #008000;">self.ui = Ui_MainWindow()
  5.       #008000;">self.ui.setupUi(#008000;">self)
  6.  
  7.   #008000;">self.setWindowTitle(#483d8b;">'To-SSH Droplet')
  8.   #808080; font-style: italic;"># to accept drops
  9.         #008000;">self.setAcceptDrops(#008000;">True)
  10.         #008000;">self.ui.lineEdit.setText(#483d8b;">"/home/wishi/")
  11.         #008000;">self.statusBar().showMessage(#483d8b;">'Ready')

To make an application accept drop-acrions simply define this in an __init__. Compared to Cocoa e. g. this is really trivial.

  1.         #ff7700;font-weight:bold;">def dragEnterEvent(#008000;">self, event):
  2.                 #ff7700;font-weight:bold;">if event.mimeData().hasUrls():
  3.                 event.acceptProposedAction()
  4.    
  5.     #483d8b;">""" the initial drop action is scp """
  6. #ff7700;font-weight:bold;">def dropEvent(#008000;">self, event):
  7.         filelist = (#483d8b;">'#000099; font-weight: bold;">\r#000099; font-weight: bold;">\n'.join([#008000;">str(url.toString()) #ff7700;font-weight:bold;">for url #ff7700;font-weight:bold;">in event.mimeData().urls()]))
  8.         #008000;">self.ui.textEdit.setText(filelist)
  9.         splitted_filelist = filelist.split(#483d8b;">"#000099; font-weight: bold;">\r#000099; font-weight: bold;">\n")
  10.         destination_path = #008000;">self.ui.lineEdit.text()

- That's it to define the event-actions. If a user drops a file, handle it:

  1.         #808080; font-style: italic;">#privatekeyfile = os.path.expanduser('~/.ssh/id_rsa')
  2.         #808080; font-style: italic;">#mykey = paramiko.RSAKey.from_private_key_file(privatekeyfile)
  3.         username = #483d8b;">'wishi'
  4.         password = #483d8b;">'bmljZSB0cnkgaWRpb3Q='
  5.         host = #483d8b;">'crazylazy.info'
  6.         port = #ff4500;">22
  7.         error = #008000;">False

So... paramiko is able to use your private key, as stated in the comment. It also supports passwords. Surely you know, that Base64 isn't used in SSH ;). However in this case it makes sense.

  1.         #ff7700;font-weight:bold;">try:
  2.                 #008000;">self.statusBar().showMessage(#483d8b;">"Uploading")
  3.                 ssh = paramiko.SSHClient()
  4.                 ssh.load_system_host_keys()
  5.                 ssh.connect(host, username = username, password = password)
  6.                 sftp = ssh.open_sftp()
  7.                
  8.                 #ff7700;font-weight:bold;">for #008000;">file #ff7700;font-weight:bold;">in splitted_filelist:
  9.                         #008000;">file = #008000;">file[#ff4500;">7:]
  10.                         patharray = #008000;">file.split(#483d8b;">"/")
  11.                         destination_path = destination_path + patharray[#008000;">len(patharray)-#ff4500;">1]
  12.                         sftp.put(#008000;">file, #008000;">str(destination_path))
  13.            
  14.                 sftp.close()
  15.                 ssh.close()
  16.            
  17.         #ff7700;font-weight:bold;">except:
  18.                 error = #008000;">True
  19.        
  20.         #ff7700;font-weight:bold;">if (error):    
  21.                 #008000;">self.statusBar().showMessage(#483d8b;">"Error")
  22.         #ff7700;font-weight:bold;">else:
  23.                 #008000;">self.statusBar().showMessage(#483d8b;">"Ready")
  24.    
  25. #ff7700;font-weight:bold;">if __name__==#483d8b;">"__main__":
  26.         app = QtGui.QApplication(#dc143c;">sys.argv)
  27.         window = gui_does()
  28.         window.show()
  29.         #dc143c;">sys.exit(app.exec_())

The interesting point here are the text conversions, which are necessary. You see that str(destionation_path) converts self.ui.lineEdit.text(). That's confusing at the beginning, but Qt handles text as unicode. Most Python installation don't.
Paramiko wants an ASCII string. So you convert this into a standard Python string-type. Furthermore you see that the string-operations introduced in nearly every Python tutorial I ever read are more than essential. Even in GUI programming they're central.

If you take a look at the for-loop: destination_path = destination_path + patharray[len(patharray)-1] -> makes sftp.put keep the filename and put it into the destination_path. You need the last element, which is len(element)-1.
The URI string normally starts with "file://". You don't want to pass these identifier-string to sftp.put. So you start at th 7th (file = file[7:]) position. That converts the URI into a general Unix path.

The rest of the app is trivial. Starting Qt Designer, clicking: the GUI just needs a lineEdit and a textEdit. The whole project including the QT Designer files are at GitHub. Surely you can enhance it with a progress bar, a menu-bar, preferences and extend it into a full featured sftp client.

This is just a general snippet on how to implement Drag and Drop actions with Qt and Python. If you want to dive into it, enhance it and:

Have fun,
wishi

Post new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.

Save the nature. Don't print this!


I provide textual exports for every blog entry. However let's save the nature together. The nature is everything around us. Every being should be respected. Save the nature - don't print too much.


Die Umgehung dieser Ausdrucksperre ist nach § 95a UrhG verboten!
Inhaltlich Verantwortlicher gemäß § 10 Absatz 3 MDStV: Marius Ciepluch - Anschrift via eMail. Die eMail Adresse entnehmen sie dem Impresseum dieser englischsprachigen Seite.
Aus Datenschutzgründen habe ich weder offiziellen noch behördlichen Schriftverkehr via eMail. Dazu ist die postalische, beim Dienstleister hinterlegte, Anschrift zu verwenden.

Datenerfassung

Es werden keine personenbezogenen Daten erfasst. Logdaten werden anonymisiert.