Немного исправил скрипт, разобрал строку (хотя, как-то неуклюже получилось). Зависаний вроде нет.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, glob, os, locale, subprocess, cPickle
from PyQt4 import QtGui, QtCore
class Dialog(QtGui.QDialog):
def __init__(self, parent=None, elem=None):
QtGui.QDialog.__init__(self, parent)
self.setMinimumWidth(400) # минимальная ширина окна диалога
self.setFixedHeight(100) # запрет изменения высоты
lay = QtGui.QGridLayout(self)
label1 = QtGui.QLabel(parent.tr.translate('QuickStarter', 'Name:'), self) # метка
label1.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
lay.addWidget(label1, 0, 0)
self.ln_edit1 = QtGui.QLineEdit('', self) # строковое поле ввода
self.ln_edit1.setMaxLength(100)
lay.addWidget(self.ln_edit1, 0, 1, 1, 2)
label2 = QtGui.QLabel(parent.tr.translate('QuickStarter', 'Command:'), self) # метка
label2.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
lay.addWidget(label2, 1, 0)
self.ln_edit2 = QtGui.QLineEdit('', self) # строковое поле ввода
self.ln_edit2.setMaxLength(255)
lay.addWidget(self.ln_edit2, 1, 1, 1, 2)
boxlay = QtGui.QHBoxLayout()
lay.addLayout(boxlay, 2, 2)
button1 = QtGui.QPushButton(parent.tr.translate('QuickStarter', 'Ok'), self) # кнопка
button1.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.connect(button1, QtCore.SIGNAL('clicked()'), lambda: self.done(1))
boxlay.addWidget(button1)
button2 = QtGui.QPushButton(parent.tr.translate('QuickStarter', 'Cancel'), self) # кнопка
button2.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.connect(button2, QtCore.SIGNAL('clicked()'), lambda: self.done(0))
boxlay.addWidget(button2)
if elem == None:
self.setWindowTitle(parent.tr.translate('QuickStarter', 'New element')) # заголовок окна
else:
self.setWindowTitle(parent.tr.translate('QuickStarter', 'Element edit')) # заголовок окна
self.ln_edit1.setText(elem.data(0, QtCore.Qt.DisplayRole).toString())
self.ln_edit2.setText(elem.data(1, QtCore.Qt.DisplayRole).toString())
class Translator(QtCore.QTranslator):
def __init__(self, parent=None, lang='QuickStarter_en_EN.qm'):
QtCore.QTranslator.__init__(self, parent)
self.load(lang)
def translate(self, context, sourceText):
res = QtCore.QTranslator.translate(self, context, sourceText)
if len(res) == 0:
res = QtCore.QString(sourceText)
return res
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QWidget.__init__(self)
home = os.path.realpath(os.path.dirname(sys.argv[0])) # каталог скрипта
self.setWindowIcon(QtGui.QIcon(home + '/qs64.bmp')) # иконка окна
menubar = self.menuBar() # строка меню
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) # поверх всех окон
# объект для сохранения настроек приложения:
self.settings = QtCore.QSettings('script-coding.info', 'QuickStarter')
# восстановление языковой настройки:
self.lang = self.settings.value('lang', QtCore.QVariant(u'QuickStarter_en_EN.qm')).toString()
self.tr = Translator(None, self.lang)
# восстановление настройки геометрии окна:
self.setGeometry(self.settings.value('geometry', QtCore.QVariant(QtCore.QRect(300, 300, 350, 200))).toRect())
# заголовок окна:
self.setWindowTitle(self.tr.translate('QuickStarter', 'Quick starter'))
# меню "Language choice":
menuLang = menubar.addMenu(self.tr.translate('QuickStarter', 'Language choice'))
pointName = self.tr.translate('QuickStarter', 'By default')
point = QtGui.QAction(pointName, self)
self.connect(point, QtCore.SIGNAL('triggered()'), lambda: self.lang_menu('QuickStarter_en_EN.qm'))
menuLang.addAction(point)
for filename in glob.glob(home + '/QuickStarter_*_*.qm'):
pointName = os.path.basename(filename)
point = QtGui.QAction(pointName, self)
self.connect(point, QtCore.SIGNAL('triggered()'), lambda: self.lang_menu(pointName))
menuLang.addAction(point)
# выход по Escape:
menuLang.addSeparator()
exit = QtGui.QAction(self.tr.translate('QuickStarter', 'Exit'), self)
exit.setShortcut('Escape')
self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
menuLang.addAction(exit)
# дерево
self.tree = QtGui.QTreeWidget(self)
self.setCentralWidget(self.tree)
self.tree.headerItem().setHidden(True)
# обработчик двойного щелчка по дереву
self.connect(self.tree, QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem *, int)'), self.doubleClick)
# восстановление содержимого дерева:
def restoreTreeItem(lst, parent = None):
item = QtGui.QTreeWidgetItem()
item.setData(0, QtCore.Qt.DisplayRole, QtCore.QVariant(lst[0]))
item.setData(1, QtCore.Qt.DisplayRole, QtCore.QVariant(lst[1]))
if parent:
parent.addChild(item)
else:
self.tree.addTopLevelItem(item)
if lst[3]:
self.tree.expandItem(item)
if lst[4]:
self.tree.setCurrentItem(item)
for elem in lst[2]:
restoreTreeItem(elem, item)
dump = str(self.settings.value('tree', QtCore.QVariant('(lp1\n.')).toString())
self.treeList = cPickle.loads(dump)
for elem in self.treeList:
restoreTreeItem(elem)
# меню "Edit"
menuEdit = menubar.addMenu(self.tr.translate('QuickStarter', 'Edit'))
nm = self.tr.translate('QuickStarter', 'Add an element of the root')
self.addRoot = QtGui.QAction(nm, self)
self.connect(self.addRoot, QtCore.SIGNAL('triggered()'), self.addRootEvent)
menuEdit.addAction(self.addRoot)
nm = self.tr.translate('QuickStarter', 'Add an element')
self.add = QtGui.QAction(nm, self)
self.connect(self.add, QtCore.SIGNAL('triggered()'), self.addEvent)
menuEdit.addAction(self.add)
nm = self.tr.translate('QuickStarter', 'Delete the element')
self.delete = QtGui.QAction(nm, self)
self.delete.setShortcut('Delete')
self.connect(self.delete, QtCore.SIGNAL('triggered()'), self.deleteEvent)
menuEdit.addAction(self.delete)
nm = self.tr.translate('QuickStarter', 'Edit the element')
self.edit = QtGui.QAction(nm, self)
self.edit.setShortcut('Return')
self.connect(self.edit, QtCore.SIGNAL('triggered()'), self.editEvent)
menuEdit.addAction(self.edit)
# меню "Running"
menuEdit = menubar.addMenu(self.tr.translate('QuickStarter', 'Running'))
nm = self.tr.translate('QuickStarter', 'Run')
self.run = QtGui.QAction(nm, self)
self.run.setShortcut('Space')
self.connect(self.run, QtCore.SIGNAL('triggered()'), self.runEvent)
menuEdit.addAction(self.run)
nm = self.tr.translate('QuickStarter', 'Run and do not exit')
self.runNotExit = QtGui.QAction(nm, self)
self.runNotExit.setShortcut('Ctrl+Space')
self.connect(self.runNotExit, QtCore.SIGNAL('triggered()'), lambda: self.runEvent(False))
menuEdit.addAction(self.runNotExit)
def runEvent(self, exit = True):
# Обработчик пункта меню "Run".
currItem = self.tree.currentItem()
if currItem == None:
caption = self.tr.translate('QuickStarter', 'Quick starter')
text = self.tr.translate('QuickStarter', 'Do not chosen element!')
QtGui.QMessageBox.information(self, caption, text, QtGui.QMessageBox.Ok)
return
commandLine = unicode(currItem.data(1, QtCore.Qt.DisplayRole).toString())
commandLine = commandLine.encode(locale.getdefaultlocale()[1])
commandLine = os.path.expanduser(commandLine) # обработка "~"
lst = commandLine.split()
i = 0
part = ''
resList = []
while i < len(lst):
piece = lst[i]
begin = piece[0]
end = piece[-1]
if begin <> '"' and end <> '"':
if len(part) == 0:
resList.append(piece)
else:
part = part + ' ' + piece
elif begin == '"' and end == '"':
resList.append(piece[1:-1])
elif begin == '"' and end <> '"':
part += piece[1:]
else: # begin <> '"' and end == '"':
part = part + ' ' + piece[:-1]
resList.append(part)
part = ''
i += 1
if len(part) > 0: # незакрытая кавычка
resList.append(part)
try:
subprocess.Popen(resList) # запуск
# subprocess.Popen(commandLine, shell = True) # запуск
if exit:
self.close()
except:
caption = self.tr.translate('QuickStarter', 'Quick starter')
text = self.tr.translate('QuickStarter', 'Failed to run the application.') + '\n' + \
self.tr.translate('QuickStarter', 'Please check the command line and / or your permission to the files.')
QtGui.QMessageBox.information(self, caption, text, QtGui.QMessageBox.Ok)
def doubleClick(self, item, column):
# Обработчик двойного щелчка по дереву.
self.editEvent()
def contextMenuEvent(self, event):
# Обработчик контекстного меню.
menu = QtGui.QMenu(self)
menu.addAction(self.addRoot)
menu.addAction(self.add)
menu.addAction(self.delete)
menu.addAction(self.edit)
menu.addSeparator()
menu.addAction(self.run)
menu.addAction(self.runNotExit)
menu.exec_(event.globalPos())
def addRootEvent(self):
# Обработчик пункта меню "Add an element of the root".
dlg = Dialog(self)
if dlg.exec_(): # нажата кнопка "Ок"
item = QtGui.QTreeWidgetItem()
nm = dlg.ln_edit1.text()
if len(nm) == 0: nm = self.tr.translate('QuickStarter', 'Untitled')
item.setData(0, QtCore.Qt.DisplayRole, QtCore.QVariant(nm))
item.setData(1, QtCore.Qt.DisplayRole, QtCore.QVariant(dlg.ln_edit2.text()))
self.tree.addTopLevelItem(item)
self.tree.sortItems(0, QtCore.Qt.AscendingOrder)
dlg.destroy()
def addEvent(self):
# Обработчик пункта меню "Add an element".
parentItem = self.tree.currentItem()
if parentItem == None:
caption = self.tr.translate('QuickStarter', 'Quick starter')
text = self.tr.translate('QuickStarter', 'Do not chosen parent element!')
QtGui.QMessageBox.information(self, caption, text, QtGui.QMessageBox.Ok)
return
dlg = Dialog(self)
if dlg.exec_(): # нажата кнопка "Ок"
item = QtGui.QTreeWidgetItem()
nm = dlg.ln_edit1.text()
if len(nm) == 0: nm = self.tr.translate('QuickStarter', 'Untitled')
item.setData(0, QtCore.Qt.DisplayRole, QtCore.QVariant(nm))
item.setData(1, QtCore.Qt.DisplayRole, QtCore.QVariant(dlg.ln_edit2.text()))
parentItem.addChild(item)
self.tree.sortItems(0, QtCore.Qt.AscendingOrder)
dlg.destroy()
def deleteEvent(self):
# Обработчик пункта меню "Delete the element".
currItem = self.tree.currentItem()
if currItem == None:
caption = self.tr.translate('QuickStarter', 'Quick starter')
text = self.tr.translate('QuickStarter', 'Do not chosen element!')
QtGui.QMessageBox.information(self, caption, text, QtGui.QMessageBox.Ok)
return
caption = self.tr.translate('QuickStarter', 'Quick starter')
text1 = self.tr.translate('QuickStarter', 'Deleting element')
text2 = self.tr.translate('QuickStarter', 'Continue?')
nm = ' "' + currItem.data(0, QtCore.Qt.DisplayRole).toString() + '".\n '
text = text1 + nm + text2
reply = QtGui.QMessageBox.question(self, caption, text, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.No:
return
parent = currItem.parent()
if parent != None:
parent.takeChild(parent.indexOfChild(currItem))
else:
self.tree.takeTopLevelItem(self.tree.indexOfTopLevelItem(currItem))
def editEvent(self):
# Обработчик пункта меню "Edit the element".
currItem = self.tree.currentItem()
if currItem == None:
caption = self.tr.translate('QuickStarter', 'Quick starter')
text = self.tr.translate('QuickStarter', 'Do not chosen element!')
QtGui.QMessageBox.information(self, caption, text, QtGui.QMessageBox.Ok)
return
dlg = Dialog(self, currItem)
if dlg.exec_(): # нажата кнопка "Ок"
nm = dlg.ln_edit1.text()
if len(nm) == 0: nm = self.tr.translate('QuickStarter', 'Untitled')
currItem.setData(0, QtCore.Qt.DisplayRole, QtCore.QVariant(nm))
currItem.setData(1, QtCore.Qt.DisplayRole, QtCore.QVariant(dlg.ln_edit2.text()))
self.tree.sortItems(0, QtCore.Qt.AscendingOrder)
dlg.destroy()
def lang_menu(self, pointName):
# Обработчик любого пункта колонки меню "Language choice".
self.lang = pointName
caption = self.tr.translate('QuickStarter', 'Quick starter')
text = self.tr.translate('QuickStarter', 'In order for language setting to come into force, restart the application.')
QtGui.QMessageBox.information(self, caption, text, QtGui.QMessageBox.Ok)
def closeEvent(self, event):
# сохранение языковой настройки:
self.settings.setValue('lang', QtCore.QVariant(self.lang))
# сохранение настройки геометрии окна:
self.settings.setValue('geometry', QtCore.QVariant(self.geometry()))
# сохранение содержимого дерева:
self.treeList = []
for i in xrange(self.tree.topLevelItemCount()):
topLevelItem = self.tree.topLevelItem(i)
lst = []
self.treeList.append(lst)
self.serializeTreeItem(topLevelItem, lst)
dump = cPickle.dumps(self.treeList)
self.settings.setValue('tree', QtCore.QVariant(dump))
def serializeTreeItem(self, item, lst):
nm = item.data(0, QtCore.Qt.DisplayRole).toString()
cm = item.data(1, QtCore.Qt.DisplayRole).toString()
lst.append(nm) # первый элемент - наименование
lst.append(cm) # второй элемент - командная строка
lstChild = []
lst.append(lstChild) # третий элемент - список дочерних
lst.append(item.isExpanded()) # четвёртый элемент - признак "раскрытости"
if self.tree.currentItem() == item:
lst.append(True) # пятый элемент - признак текущего элемента
else:
lst.append(False) # пятый элемент - признак текущего элемента
for i in xrange(item.childCount()):
elem = []
lstChild.append(elem)
self.serializeTreeItem(item.child(i), elem)
if __name__=="__main__":
app = QtGui.QApplication(sys.argv)
app.setStyle("Plastique")
main = MainWindow()
main.show()
sys.exit(app.exec_())
Спасибо за тест :).
Это и всё подобное должно просто не вызывать зависаний процессов, и не более того - прога предназначена исключительно для запуска GUI-приложений.
Может, не “вкладка”, а меню? Что есть “couter”? Это исполняемый файл или папка? Если второе, то надо запускать что-то вроде “nautilus /home/mike/couter/couter”. Что именно и как “виснет”? Я ведь тебя не понял вначале именно из-за этого неясного “виснет” (я думал, что виснет сам python-скрипт, а не процесс оболочки).
Не понял :). Если убрать команду “Запустить”, скрипт будет бесполезен полностью.