Если задаться вопросом "Кто способен реализовать программный проект сам, один и без ансамбля?", то можно дать однозначный ответ "это программист" .
У программиста может быть масса недостатков, например, отсутствие художественного вкуса, однобокое знание иностранных языков, невнятная речь, профессиональное занудство и прочее, что не мешает ему быть специалистом в своей области. Он способен понять проблему, предложить решение, детально проработать постановку (что, где, когда, кто, как и почему) и реализовать задачу выбранными средствами. Вопрос только в мотивации (время, деньги, интерес ).
Зная о своих недостатках, этот программист ещё на начальной стадии разработки заложится на то, что выполнение некоторых частей задачи лучше было бы делегировать специалистам соответствующего профиля. Например, переводы на различные языки народов мира лучше сделает переводчик, а придать неимоверно привлекательный вид элементам графического интерфейса - стилист. Надо только дать им такую возможность, а творить они смогут независимо от программиста.
Например, можно воспользоваться "методом большой кучи", исходя из принципа "грузи навалом - сервис узнает своих", как это сделано в нижеприведённом примере, основанном на коде из предыдущей заметки К вопросу о неявном назначении родительской опеки...
clipboard_probe.py:
| Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
| #!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Clipboard Probe v0.005
import glob, os, sys
from PyQt5 import QtCore, QtGui, QtWidgets
APP_NAME = 'Clipboard Probe v0.005'
def get_screen_geo_by_pos(point_pos = QtCore.QPoint(0, 0)):
screens = QtWidgets.QApplication.instance().screens()
selected_screen = screens[
QtWidgets.QApplication.desktop().primaryScreen()]
if len(screens) > 1:
for i in range(len(screens)):
if screens[i].geometry().contains(point_pos):
selected_screen = screens[i]
break
return selected_screen.availableGeometry()
def set_quarter_screen(client, screen_geo):
client.move(screen_geo.x() + screen_geo.width() // 4,
screen_geo.y() + screen_geo.height() // 4)
client.resize(screen_geo.width() // 2,
screen_geo.height() // 2)
def tr_app(context, text):
return QtWidgets.QApplication.instance().translate(context, text)
class Main_window(QtWidgets.QMainWindow):
def __init__(self, parent = None,
point_pos = QtCore.QPoint(0, 0)):
super(Main_window, self).__init__(parent)
central_widget = QtWidgets.QWidget()
status_bar = QtWidgets.QStatusBar(self)
text_editor = QtWidgets.QPlainTextEdit()
main_map = QtWidgets.QVBoxLayout()
clip_board = QtWidgets.QApplication.instance().clipboard()
main_map.addWidget(text_editor)
central_widget.setLayout(main_map)
self.setCentralWidget(central_widget)
self.setStatusBar(status_bar)
self.setWindowTitle(tr_app('mainwindow', 'Сlipboard observer'))
self.setObjectName('mainwindow')
text_editor.setLineWrapMode(QtWidgets.QPlainTextEdit.WidgetWidth)
text_editor.setPlainText(clip_board.text())
text_editor.setObjectName('texteditor')
status_bar.setObjectName('statusbar')
QtCore.QMetaObject.connectSlotsByName(self)
clip_board.dataChanged.connect(self.on_clipboard_dataChanged)
set_quarter_screen(self, get_screen_geo_by_pos(point_pos))
def on_clipboard_dataChanged(self):
text_editor = self.findChild(QtWidgets.QPlainTextEdit,
'texteditor')
status_bar = self.findChild(QtWidgets.QStatusBar, 'statusbar')
text_editor.setPlainText(self.sender().text())
status_bar.clearMessage()
def on_texteditor_textChanged(self):
clip_board = QtWidgets.QApplication.instance().clipboard()
status_bar = self.findChild(QtWidgets.QStatusBar, 'statusbar')
if self.sender().toPlainText() != clip_board.text():
status_bar.showMessage(
tr_app('mainwindow',
'Text changed! Press Ctrl-A, Ctrl-Ins to put'
' to clipboard or Ctrl-Z to drop.')
)
else:
status_bar.clearMessage()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
pack_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
qtbase_dir = QtCore.QLibraryInfo.location(
QtCore.QLibraryInfo.TranslationsPath)
style_set = ''
l_qss = [str(n) for n in glob.glob(os.path.join(pack_dir, '*.qss'))]
for n in l_qss:
with open(n, 'r') as f_qss:
style_set += '{!s}\n'.format(f_qss.read())
app.setStyleSheet(style_set)
c_lang = QtCore.QLocale().name()[0:2]
# c_lang = 'en'
l_tr = [QtCore.QTranslator()]
n_tr = 'qtbase_{!s}.qm'.format(c_lang)
if l_tr[-1].load(n_tr, qtbase_dir):
app.installTranslator(l_tr[-1])
else:
t = l_tr[-1]
l_tr.remove(t)
t.deleteLater()
l_qm = [str(n) for n in glob.glob(
os.path.join(pack_dir, '*_{!s}.qm'.format(c_lang)))]
for n in l_qm:
l_tr.append(QtCore.QTranslator())
if l_tr[-1].load(n, pack_dir):
app.installTranslator(l_tr[-1])
else:
t = l_tr[-1]
l_tr.remove(t)
t.deleteLater()
mwin = Main_window(point_pos = QtGui.QCursor.pos())
mwin.show()
sys.exit(app.exec_()) |
|
Теперь стилист может выложить в каталог, содержащий файлы кода (типа *.py), свои файлы типа *.qss с оригинальными стилевыми решениями. Все они будут загружены при старте приложения и применены к соответствующим элементам оконного интерфейса.
main.qss:
| XML | 1
2
3
| QMainWindow#mainwindow {background-color: olivedrab;}
QStatusBar#statusbar {background: white; color: maroon;}
QPlainTextEdit#texteditor {background-color: palegoldenrod; font: bold; font-family: monospace;} |
|
Переводчик тоже может выкладывать туда свои переводы (файлы типа *.qm) по мере их готовности. При старте приложения будут загружены только те переводы, которые соответствуют текущим настройкам системной локали.
Что характерно, это совершенствование стиля и расширение базы языкового покрытия будет происходить без участия программиста. Он уже сделал всё, что мог - код написан, инструкции выданы.
И даже, если программисту потребуется как-то перетасовать элементы графического интерфейса с целью достижения большего удобства пользования, то это никак не затронет всё то, что уже сделано, делается или будет делаться стилистом и переводчиком.
Однако, если вы вдруг подумали, что дизайнер вообще руки не прикладывал к разработке этой программы, то вы глубоко ошибаетесь. Потому что программист - он и есть этот самый дизайнер. Ведь дизайн - это не про красивости, как некоторые ошибочно полагают. Не царское это дело - заборы красить . Дизайн - это про эргономику, фактически - про разработку и отладку сценариев взаимодействия пользователя с программой. Дополнительно отягощает ситуацию то, что разработчик никогда не знает точно в каких условиях будет эксплуатироваться программа. Сегодня пользователь сидит в кабинете, увешанном HDMI-мониторами, а завтра - на выезде с одним чахлым ноутбуком и ему срочно надо... А всё это уже продумал и реализовал программист. И дизайн у него не монолитно-железобетонный , а подвижный, адаптирующийся под возможности техники. Вот такой дизайнер - нужен!
Потому что, если потребуется, он и с Qt Designer-ом не оплошает - сможет построить графический интерфейс с его помощью не хуже, чем в оригинале.
clipboard_probe.py:
| Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
| #!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Clipboard Probe v0.006
import glob, os, sys
from PyQt5 import uic, QtCore, QtGui, QtWidgets
APP_NAME = 'Clipboard Probe v0.006'
def get_screen_geo_by_pos(point_pos = QtCore.QPoint(0, 0)):
screens = QtWidgets.QApplication.instance().screens()
selected_screen = screens[
QtWidgets.QApplication.desktop().primaryScreen()]
if len(screens) > 1:
for i in range(len(screens)):
if screens[i].geometry().contains(point_pos):
selected_screen = screens[i]
break
return selected_screen.availableGeometry()
def set_quarter_screen(client, screen_geo):
client.move(screen_geo.x() + screen_geo.width() // 4,
screen_geo.y() + screen_geo.height() // 4)
client.resize(screen_geo.width() // 2,
screen_geo.height() // 2)
def tr_app(context, text):
return QtWidgets.QApplication.instance().translate(context, text)
class Main_window(QtWidgets.QMainWindow):
def __init__(self, parent = None,
point_pos = QtCore.QPoint(0, 0)):
super(Main_window, self).__init__(parent)
uic.loadUi(
os.path.join(
os.path.dirname(os.path.abspath(sys.argv[0])),
'mwin.ui'),
self)
clip_board = QtWidgets.QApplication.instance().clipboard()
text_editor = self.findChild(QtWidgets.QPlainTextEdit,
'texteditor')
text_editor.setPlainText(clip_board.text())
clip_board.dataChanged.connect(self.on_clipboard_dataChanged)
set_quarter_screen(self, get_screen_geo_by_pos(point_pos))
def on_clipboard_dataChanged(self):
text_editor = self.findChild(QtWidgets.QPlainTextEdit,
'texteditor')
status_bar = self.findChild(QtWidgets.QStatusBar, 'statusbar')
text_editor.setPlainText(self.sender().text())
status_bar.clearMessage()
def on_texteditor_textChanged(self):
clip_board = QtWidgets.QApplication.instance().clipboard()
status_bar = self.findChild(QtWidgets.QStatusBar, 'statusbar')
if self.sender().toPlainText() != clip_board.text():
status_bar.showMessage(
tr_app('main',
'Text changed! Press Ctrl-A, Ctrl-Ins to put'
' to clipboard or Ctrl-Z to drop.')
)
else:
status_bar.clearMessage()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
pack_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
qtbase_dir = QtCore.QLibraryInfo.location(
QtCore.QLibraryInfo.TranslationsPath)
style_set = ''
l_qss = [str(n) for n in glob.glob(os.path.join(pack_dir, '*.qss'))]
for n in l_qss:
with open(n, 'r') as f_qss:
style_set += '{!s}\n'.format(f_qss.read())
app.setStyleSheet(style_set)
c_lang = QtCore.QLocale().name()[0:2]
# c_lang = 'en'
l_tr = [QtCore.QTranslator()]
n_tr = 'qtbase_{!s}.qm'.format(c_lang)
if l_tr[-1].load(n_tr, qtbase_dir):
app.installTranslator(l_tr[-1])
else:
t = l_tr[-1]
l_tr.remove(t)
t.deleteLater()
l_qm = [str(n) for n in glob.glob(
os.path.join(pack_dir, '*_{!s}.qm'.format(c_lang)))]
for n in l_qm:
l_tr.append(QtCore.QTranslator())
if l_tr[-1].load(n, pack_dir):
app.installTranslator(l_tr[-1])
else:
t = l_tr[-1]
l_tr.remove(t)
t.deleteLater()
mwin = Main_window(point_pos = QtGui.QCursor.pos())
mwin.show()
sys.exit(app.exec_()) |
|
mwin.ui:
| XML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>mainwindow</class>
<widget class="QMainWindow" name="mainwindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>268</width>
<height>261</height>
</rect>
</property>
<property name="windowTitle">
<string>Clipboard observer</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPlainTextEdit" name="texteditor"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>268</width>
<height>32</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui> |
|
Хоть и не без греха... Например, стилист даже не заметит этой революции, а вот переводчика коснётся. Переделывать существующие переводы нужды особой нет, но к ним придётся дописывать новые, поскольку часть данных, требующих перевода, перейдёт из скриптов (*.py) в файлы дизайна (*.ui). Наверняка есть ещё какие-то сюрпризы...
Стоит ли овчинка выделки - каждый решает сам. Не был бы прибыток горше убытка... Но дизайнер должен оставаться программистом
|