Merge lp:~hmollercl/software-properties/software-properties into lp:software-properties

Proposed by Hans P. Möller
Status: Merged
Merged at revision: 1066
Proposed branch: lp:~hmollercl/software-properties/software-properties
Merge into: lp:software-properties
Diff against target: 927 lines (+712/-32)
3 files modified
data/designer/main.ui (+225/-23)
software-properties-qt (+6/-1)
softwareproperties/qt/SoftwarePropertiesQt.py (+481/-8)
To merge this branch: bzr merge lp:~hmollercl/software-properties/software-properties
Reviewer Review Type Date Requested Status
ԜаӀtеr Ⅼарсһуnѕkі Pending
Simon Quigley Pending
Review via email: mp+361331@code.launchpad.net

Commit message

Added "additional driver" tab for the Qt version

Description of the change

Added "additional driver" tab for the qt version
To maintain same numbers as gtk version it will be tab 4.
Also added --open-tab commandline option so you can open directly in one tab (same as gtk)
Added lxqt-sudo mention (along with pkexec) when not run with root privileges.
Migrate some GridLayout to BoxLayout in Qt version so windows resizing still makes things look good.

To post a comment you must log in.
1059. By Sebastien Bacher

[ Carlo Vanini ]
Fix removal of signing keys in the Authentication tab always failing.
(lp: #1656100)

1060. By Sebastien Bacher

releasing package software-properties version 0.96.30

1061. By Sebastien Bacher

Remove the migration script, it was online needed until bionic

1062. By Andrea Azzarone

[ Andrea Azzarone ]
* data/gtkbuilder/dialog-auth.ui: Update design of the authentication
  dialog.
* softwareproperties/gtk/DialogAuth.py: Implement new design of the
  authentication dialog.

1063. By Sebastien Bacher

releasing package software-properties version 0.96.31

1064. By Sebastien Bacher

* softwareproperties/gtk/DialogAuth.py:
  - Log call_ensure_credentials_finish errors, fixes the build failing
    due to pyflakes warning about an unused variable

1065. By Sebastien Bacher

releasing package software-properties version 0.96.32

1066. By Simon Quigley

Merge lp:~hmollercl/software-properties/software-properties into lp:software-properties and add changelog entry.

Revision history for this message
Hans P. Möller (hmollercl) wrote :

for testing run software-properties-qt with the --data-dir modifier to use the modified UI.
lxqt-sudo ./software-properties-qt --data-dir ./data

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'data/designer/main.ui'
--- data/designer/main.ui 2018-07-14 10:32:29 +0000
+++ data/designer/main.ui 2018-12-29 13:41:58 +0000
@@ -6,21 +6,15 @@
6 <rect>6 <rect>
7 <x>0</x>7 <x>0</x>
8 <y>0</y>8 <y>0</y>
9 <width>605</width>9 <width>706</width>
10 <height>408</height>10 <height>408</height>
11 </rect>11 </rect>
12 </property>12 </property>
13 <property name="windowTitle">13 <property name="windowTitle">
14 <string>Software Sources</string>14 <string>Software Sources</string>
15 </property>15 </property>
16 <layout class="QGridLayout">16 <layout class="QVBoxLayout" name="verticalLayout">
17 <property name="margin">17 <item>
18 <number>9</number>
19 </property>
20 <property name="spacing">
21 <number>6</number>
22 </property>
23 <item row="0" column="0">
24 <widget class="QTabWidget" name="tabWidget">18 <widget class="QTabWidget" name="tabWidget">
25 <property name="currentIndex">19 <property name="currentIndex">
26 <number>0</number>20 <number>0</number>
@@ -30,7 +24,16 @@
30 <string>Software &amp; Updates</string>24 <string>Software &amp; Updates</string>
31 </attribute>25 </attribute>
32 <layout class="QGridLayout">26 <layout class="QGridLayout">
33 <property name="margin">27 <property name="leftMargin">
28 <number>9</number>
29 </property>
30 <property name="topMargin">
31 <number>9</number>
32 </property>
33 <property name="rightMargin">
34 <number>9</number>
35 </property>
36 <property name="bottomMargin">
34 <number>9</number>37 <number>9</number>
35 </property>38 </property>
36 <property name="spacing">39 <property name="spacing">
@@ -42,7 +45,16 @@
42 <string>&lt;b&gt;Downloadable from the Internet&lt;/b&gt;</string>45 <string>&lt;b&gt;Downloadable from the Internet&lt;/b&gt;</string>
43 </property>46 </property>
44 <layout class="QGridLayout">47 <layout class="QGridLayout">
45 <property name="margin">48 <property name="leftMargin">
49 <number>9</number>
50 </property>
51 <property name="topMargin">
52 <number>9</number>
53 </property>
54 <property name="rightMargin">
55 <number>9</number>
56 </property>
57 <property name="bottomMargin">
46 <number>9</number>58 <number>9</number>
47 </property>59 </property>
48 <property name="spacing">60 <property name="spacing">
@@ -57,7 +69,16 @@
57 <enum>QFrame::Raised</enum>69 <enum>QFrame::Raised</enum>
58 </property>70 </property>
59 <layout class="QGridLayout">71 <layout class="QGridLayout">
60 <property name="margin">72 <property name="leftMargin">
73 <number>0</number>
74 </property>
75 <property name="topMargin">
76 <number>0</number>
77 </property>
78 <property name="rightMargin">
79 <number>0</number>
80 </property>
81 <property name="bottomMargin">
61 <number>0</number>82 <number>0</number>
62 </property>83 </property>
63 <property name="spacing">84 <property name="spacing">
@@ -68,7 +89,16 @@
68 <property name="spacing">89 <property name="spacing">
69 <number>6</number>90 <number>6</number>
70 </property>91 </property>
71 <property name="margin">92 <property name="leftMargin">
93 <number>0</number>
94 </property>
95 <property name="topMargin">
96 <number>0</number>
97 </property>
98 <property name="rightMargin">
99 <number>0</number>
100 </property>
101 <property name="bottomMargin">
72 <number>0</number>102 <number>0</number>
73 </property>103 </property>
74 </layout>104 </layout>
@@ -105,7 +135,16 @@
105 <string>Installable from CD-ROM/DVD</string>135 <string>Installable from CD-ROM/DVD</string>
106 </property>136 </property>
107 <layout class="QGridLayout">137 <layout class="QGridLayout">
108 <property name="margin">138 <property name="leftMargin">
139 <number>9</number>
140 </property>
141 <property name="topMargin">
142 <number>9</number>
143 </property>
144 <property name="rightMargin">
145 <number>9</number>
146 </property>
147 <property name="bottomMargin">
109 <number>9</number>148 <number>9</number>
110 </property>149 </property>
111 <property name="spacing">150 <property name="spacing">
@@ -168,7 +207,16 @@
168 <string>Other Software</string>207 <string>Other Software</string>
169 </attribute>208 </attribute>
170 <layout class="QGridLayout">209 <layout class="QGridLayout">
171 <property name="margin">210 <property name="leftMargin">
211 <number>9</number>
212 </property>
213 <property name="topMargin">
214 <number>9</number>
215 </property>
216 <property name="rightMargin">
217 <number>9</number>
218 </property>
219 <property name="bottomMargin">
172 <number>9</number>220 <number>9</number>
173 </property>221 </property>
174 <property name="spacing">222 <property name="spacing">
@@ -233,7 +281,16 @@
233 <string>Updates</string>281 <string>Updates</string>
234 </attribute>282 </attribute>
235 <layout class="QGridLayout">283 <layout class="QGridLayout">
236 <property name="margin">284 <property name="leftMargin">
285 <number>9</number>
286 </property>
287 <property name="topMargin">
288 <number>9</number>
289 </property>
290 <property name="rightMargin">
291 <number>9</number>
292 </property>
293 <property name="bottomMargin">
237 <number>9</number>294 <number>9</number>
238 </property>295 </property>
239 <property name="spacing">296 <property name="spacing">
@@ -245,7 +302,16 @@
245 <string>&lt;b&gt;Ubuntu updates&lt;/b&gt;</string>302 <string>&lt;b&gt;Ubuntu updates&lt;/b&gt;</string>
246 </property>303 </property>
247 <layout class="QGridLayout" name="gridLayout_2">304 <layout class="QGridLayout" name="gridLayout_2">
248 <property name="margin">305 <property name="leftMargin">
306 <number>9</number>
307 </property>
308 <property name="topMargin">
309 <number>9</number>
310 </property>
311 <property name="rightMargin">
312 <number>9</number>
313 </property>
314 <property name="bottomMargin">
249 <number>9</number>315 <number>9</number>
250 </property>316 </property>
251 <property name="spacing">317 <property name="spacing">
@@ -256,7 +322,16 @@
256 <property name="spacing">322 <property name="spacing">
257 <number>6</number>323 <number>6</number>
258 </property>324 </property>
259 <property name="margin">325 <property name="leftMargin">
326 <number>0</number>
327 </property>
328 <property name="topMargin">
329 <number>0</number>
330 </property>
331 <property name="rightMargin">
332 <number>0</number>
333 </property>
334 <property name="bottomMargin">
260 <number>0</number>335 <number>0</number>
261 </property>336 </property>
262 <item>337 <item>
@@ -268,7 +343,16 @@
268 <enum>QFrame::Raised</enum>343 <enum>QFrame::Raised</enum>
269 </property>344 </property>
270 <layout class="QGridLayout">345 <layout class="QGridLayout">
271 <property name="margin">346 <property name="leftMargin">
347 <number>0</number>
348 </property>
349 <property name="topMargin">
350 <number>0</number>
351 </property>
352 <property name="rightMargin">
353 <number>0</number>
354 </property>
355 <property name="bottomMargin">
272 <number>0</number>356 <number>0</number>
273 </property>357 </property>
274 <property name="spacing">358 <property name="spacing">
@@ -288,7 +372,16 @@
288 <string>&lt;b&gt;Automatic updates&lt;/b&gt;</string>372 <string>&lt;b&gt;Automatic updates&lt;/b&gt;</string>
289 </property>373 </property>
290 <layout class="QGridLayout" name="gridLayout">374 <layout class="QGridLayout" name="gridLayout">
291 <property name="margin">375 <property name="leftMargin">
376 <number>9</number>
377 </property>
378 <property name="topMargin">
379 <number>9</number>
380 </property>
381 <property name="rightMargin">
382 <number>9</number>
383 </property>
384 <property name="bottomMargin">
292 <number>9</number>385 <number>9</number>
293 </property>386 </property>
294 <property name="spacing">387 <property name="spacing">
@@ -370,7 +463,16 @@
370 <string>Authentication</string>463 <string>Authentication</string>
371 </attribute>464 </attribute>
372 <layout class="QGridLayout">465 <layout class="QGridLayout">
373 <property name="margin">466 <property name="leftMargin">
467 <number>9</number>
468 </property>
469 <property name="topMargin">
470 <number>9</number>
471 </property>
472 <property name="rightMargin">
473 <number>9</number>
474 </property>
475 <property name="bottomMargin">
374 <number>9</number>476 <number>9</number>
375 </property>477 </property>
376 <property name="spacing">478 <property name="spacing">
@@ -437,12 +539,112 @@
437 </item>539 </item>
438 </layout>540 </layout>
439 </widget>541 </widget>
542 <widget class="QWidget" name="vbox_drivers">
543 <attribute name="title">
544 <string>Additional Drivers</string>
545 </attribute>
546 <layout class="QVBoxLayout" name="verticalLayout_2">
547 <property name="spacing">
548 <number>0</number>
549 </property>
550 <item>
551 <widget class="QScrollArea" name="scrollArea">
552 <property name="widgetResizable">
553 <bool>true</bool>
554 </property>
555 <widget class="QWidget" name="scrollAreaWidgetContents">
556 <property name="geometry">
557 <rect>
558 <x>0</x>
559 <y>0</y>
560 <width>670</width>
561 <height>244</height>
562 </rect>
563 </property>
564 <layout class="QVBoxLayout" name="verticalLayout_3">
565 <property name="spacing">
566 <number>0</number>
567 </property>
568 <property name="leftMargin">
569 <number>0</number>
570 </property>
571 <property name="topMargin">
572 <number>0</number>
573 </property>
574 <property name="rightMargin">
575 <number>0</number>
576 </property>
577 <property name="bottomMargin">
578 <number>0</number>
579 </property>
580 <item>
581 <layout class="QVBoxLayout" name="box_driver_detail"/>
582 </item>
583 </layout>
584 </widget>
585 </widget>
586 </item>
587 <item>
588 <widget class="QGroupBox" name="groupBox_driver_action">
589 <property name="sizePolicy">
590 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
591 <horstretch>0</horstretch>
592 <verstretch>0</verstretch>
593 </sizepolicy>
594 </property>
595 <layout class="QHBoxLayout" name="box_driver_action">
596 <property name="spacing">
597 <number>0</number>
598 </property>
599 <property name="leftMargin">
600 <number>0</number>
601 </property>
602 <property name="topMargin">
603 <number>0</number>
604 </property>
605 <property name="rightMargin">
606 <number>0</number>
607 </property>
608 <property name="bottomMargin">
609 <number>0</number>
610 </property>
611 <item>
612 <widget class="QLabel" name="label_driver_action">
613 <property name="text">
614 <string>No propietary drivers are in use.</string>
615 </property>
616 </widget>
617 </item>
618 </layout>
619 </widget>
620 </item>
621 <item>
622 <widget class="QLabel" name="label_disc">
623 <property name="text">
624 <string>&lt;small&gt;A proprietary driver has private code that Ubuntu developers can't review or improve. Security and other updates are dependent on the driver vendor.&lt;/small&gt;</string>
625 </property>
626 <property name="wordWrap">
627 <bool>true</bool>
628 </property>
629 </widget>
630 </item>
631 </layout>
632 </widget>
440 <widget class="QWidget" name="tab_5">633 <widget class="QWidget" name="tab_5">
441 <attribute name="title">634 <attribute name="title">
442 <string>Statistics</string>635 <string>Statistics</string>
443 </attribute>636 </attribute>
444 <layout class="QGridLayout">637 <layout class="QGridLayout">
445 <property name="margin">638 <property name="leftMargin">
639 <number>9</number>
640 </property>
641 <property name="topMargin">
642 <number>9</number>
643 </property>
644 <property name="rightMargin">
645 <number>9</number>
646 </property>
647 <property name="bottomMargin">
446 <number>9</number>648 <number>9</number>
447 </property>649 </property>
448 <property name="spacing">650 <property name="spacing">
@@ -482,7 +684,7 @@
482 </widget>684 </widget>
483 </widget>685 </widget>
484 </item>686 </item>
485 <item row="1" column="0">687 <item>
486 <widget class="QDialogButtonBox" name="buttonBox">688 <widget class="QDialogButtonBox" name="buttonBox">
487 <property name="orientation">689 <property name="orientation">
488 <enum>Qt::Horizontal</enum>690 <enum>Qt::Horizontal</enum>
489691
=== modified file 'software-properties-qt'
--- software-properties-qt 2018-07-14 10:11:32 +0000
+++ software-properties-qt 2018-12-29 13:41:58 +0000
@@ -82,6 +82,10 @@
82 _("Enable the specified component of the distro's repositories"),82 _("Enable the specified component of the distro's repositories"),
83 "name");83 "name");
84 parser.addOption(componentOption);84 parser.addOption(componentOption);
85 openTabOption = QCommandLineOption("open-tab",
86 _("Open specific tab number on startup"),
87 "open_tab");
88 parser.addOption(openTabOption);
85 ppaOption = QCommandLineOption("enable-ppa",89 ppaOption = QCommandLineOption("enable-ppa",
86 _("Enable PPA with the given name"),90 _("Enable PPA with the given name"),
87 "name");91 "name");
@@ -100,7 +104,7 @@
100104
101 # Check for root permissions105 # Check for root permissions
102 if os.geteuid() != 0:106 if os.geteuid() != 0:
103 text = "Please run this software with administrative rights. To do so, run this program with pkexec."107 text = "Please run this software with administrative rights. To do so, run this program with lxqt-sudo or pkexec."
104 title = "Need administrative powers"108 title = "Need administrative powers"
105 msgbox = QMessageBox.critical(None, title, text)109 msgbox = QMessageBox.critical(None, title, text)
106 sys.exit(1)110 sys.exit(1)
@@ -119,6 +123,7 @@
119 options.debug = parser.isSet(debugOption)123 options.debug = parser.isSet(debugOption)
120 options.debug = parser.isSet(massiveDebugOption)124 options.debug = parser.isSet(massiveDebugOption)
121 options.no_update = parser.isSet(noUpdateOption)125 options.no_update = parser.isSet(noUpdateOption)
126 options.open_tab = parser.value(openTabOption)
122127
123 attachWinID = None128 attachWinID = None
124 if parser.isSet(winidOption):129 if parser.isSet(winidOption):
125130
=== modified file 'softwareproperties/qt/SoftwarePropertiesQt.py'
--- softwareproperties/qt/SoftwarePropertiesQt.py 2018-07-14 10:45:16 +0000
+++ softwareproperties/qt/SoftwarePropertiesQt.py 2018-12-29 13:41:58 +0000
@@ -49,6 +49,28 @@
49from .DialogMirror import DialogMirror49from .DialogMirror import DialogMirror
50from .CdromProgress import CdromProgress50from .CdromProgress import CdromProgress
5151
52from UbuntuDrivers import detect
53import sys
54import apt
55from functools import partial
56from aptdaemon import client
57from aptdaemon.errors import NotAuthorizedError, TransactionFailed
58import aptsources.distro
59import logging
60
61class DetectDriverThread(QThread):
62 #WARNING class to run detect_drivers() in separate thread
63 #so GUI won't freeze.
64 def __init__(self, swprop):
65 QThread.__init__(self)
66 self.swprop = swprop
67
68 def __del__(self):
69 self.wait()
70
71 def run(self):
72 self.swprop.detect_drivers()
73
52class SoftwarePropertiesQtUI(QWidget):74class SoftwarePropertiesQtUI(QWidget):
53 def __init__(self, datadir):75 def __init__(self, datadir):
54 QWidget.__init__(self)76 QWidget.__init__(self)
@@ -101,6 +123,12 @@
101 self.init_isv_sources()123 self.init_isv_sources()
102 self.show_isv_sources()124 self.show_isv_sources()
103 self.show_cdrom_sources()125 self.show_cdrom_sources()
126 # Setup and show the Additonal Drivers tab
127 self.init_drivers()
128
129 # Connect to switch-page before setting initial tab. Otherwise the
130 # first switch goes unnoticed.
131 self.userinterface.tabWidget.currentChanged.connect(self.tab_switched)
104132
105 self.userinterface.checkbutton_source_code.clicked.connect(self.on_checkbutton_source_code_toggled)133 self.userinterface.checkbutton_source_code.clicked.connect(self.on_checkbutton_source_code_toggled)
106 self.userinterface.button_auth_restore.clicked.connect(self.on_restore_clicked)134 self.userinterface.button_auth_restore.clicked.connect(self.on_restore_clicked)
@@ -117,15 +145,36 @@
117 self.userinterface.treeview_sources.itemClicked.connect(self.on_treeview_sources_cursor_changed)145 self.userinterface.treeview_sources.itemClicked.connect(self.on_treeview_sources_cursor_changed)
118 self.userinterface.treeview_cdroms.itemChanged.connect(self.on_cdrom_source_toggled)146 self.userinterface.treeview_cdroms.itemChanged.connect(self.on_cdrom_source_toggled)
119 self.userinterface.treeview2.itemClicked.connect(self.on_treeview_keys_cursor_changed)147 self.userinterface.treeview2.itemClicked.connect(self.on_treeview_keys_cursor_changed)
120 button_close = self.userinterface.buttonBox.button(QDialogButtonBox.Close)148
121 button_close.setIcon(QIcon.fromTheme("dialog-close"))149 self.button_close = self.userinterface.buttonBox.button(QDialogButtonBox.Close)
122 button_revert = self.userinterface.buttonBox.button(QDialogButtonBox.Reset)150 self.button_close.setIcon(QIcon.fromTheme("dialog-close"))
123 button_revert.setIcon(QIcon.fromTheme("edit-undo"))151 self.button_revert = self.userinterface.buttonBox.button(QDialogButtonBox.Reset)
124 button_revert.clicked.connect(self.on_button_revert_clicked)152 self.button_revert.setIcon(QIcon.fromTheme("edit-undo"))
153 self.button_revert.clicked.connect(self.on_button_revert_clicked)
125154
126 self.init_distro()155 self.init_distro()
127 self.show_distro()156 self.show_distro()
128157
158 self.apt_client = client.AptClient()
159
160 if options and options.open_tab:
161 self.userinterface.tabWidget.setCurrentIndex(int(options.open_tab))
162
163 def tab_switched(self):
164 # On the additional drivers page, don't show the backend revert button.
165 if self.userinterface.tabWidget.currentIndex() == 4: #vbox_drivers is 4
166 self.button_revert.setVisible(False)
167 if not self.detect_called:
168 #WARNING detect_drivers() runs in separate thread
169 #in DetectDriverThread class so GUI won't freeze
170 #after finish show_drivers() has to run in main thread because it updates the GUI
171 self.detect_driver_thread = DetectDriverThread(self)
172 self.detect_driver_thread.finished.connect(self.show_drivers)
173 self.detect_driver_thread.start()
174
175 else:
176 self.button_revert.setVisible(True)
177
129 def init_popcon(self):178 def init_popcon(self):
130 """ If popcon is enabled show the statistics tab and an explanation179 """ If popcon is enabled show the statistics tab and an explanation
131 corresponding to the used distro """180 corresponding to the used distro """
@@ -136,8 +185,8 @@
136 self.userinterface.label_popcon_desc.setText(text)185 self.userinterface.label_popcon_desc.setText(text)
137 self.userinterface.checkbutton_popcon.setChecked(is_helpful)186 self.userinterface.checkbutton_popcon.setChecked(is_helpful)
138 else:187 else:
139 self.userinterface.tabWidget.removeTab(4)188 self.userinterface.tabWidget.removeTab(5)
140189
141 def init_server_chooser(self):190 def init_server_chooser(self):
142 """ Set up the widgets that allow to choose an alternate download site """191 """ Set up the widgets that allow to choose an alternate download site """
143 # nothing to do here, set up signal in show_distro()192 # nothing to do here, set up signal in show_distro()
@@ -747,3 +796,427 @@
747796
748 def run(self):797 def run(self):
749 kapp.exec_()798 kapp.exec_()
799
800 def on_driver_changes_progress(self, transaction, progress):
801 self.button_driver_revert.setVisible(False)
802 self.button_driver_apply.setVisible(False)
803 self.button_driver_restart.setVisible(False)
804 self.button_driver_cancel.setVisible(True)
805 self.progress_bar.setVisible(True)
806
807 self.userinterface.label_driver_action.setText("Applying changes...")
808 self.progress_bar.setValue(progress)
809
810 def on_driver_changes_finish(self, transaction, exit_state):
811 self.progress_bar.setVisible(False)
812 self.clear_changes()
813 self.apt_cache = apt.Cache()
814 self.set_driver_action_status()
815 self.update_label_and_icons_from_status()
816 self.button_driver_revert.setVisible(True)
817 self.button_driver_apply.setVisible(True)
818 self.button_driver_cancel.setVisible(False)
819 #self.scrolled_window_drivers.set_sensitive(True)
820
821 def on_driver_changes_error(self, transaction, error_code, error_details):
822 self.on_driver_changes_revert()
823 self.set_driver_action_status()
824 self.update_label_and_icons_from_status()
825 self.button_driver_revert.setVisible(True)
826 self.button_driver_apply.setVisible(True)
827 self.button_driver_cancel.setVisible(False)
828 #self.scrolled_window_drivers.set_sensitive(True)
829
830 def on_driver_changes_cancellable_changed(self, transaction, cancellable):
831 self.button_driver_cancel.setEnabled(cancellable)
832
833 def on_driver_changes_apply(self, button):
834 button = self.userinterface.sender()
835 installs = []
836 removals = []
837
838 for pkg in self.driver_changes:
839 if pkg.is_installed:
840 removals.append(pkg.shortname)
841 # The main NVIDIA package is only a metapackage.
842 # We need to collect its dependencies, so that
843 # we can uninstall the driver properly.
844 if 'nvidia' in pkg.shortname:
845 for dep in get_dependencies(self.apt_cache, pkg.shortname, 'nvidia'):
846 dep_pkg = self.apt_cache[dep]
847 if dep_pkg.is_installed:
848 removals.append(dep_pkg.shortname)
849 else:
850 installs.append(pkg.shortname)
851
852 try:
853 self.transaction = self.apt_client.commit_packages(install=installs, remove=removals, reinstall=[], purge=[], upgrade=[], downgrade=[])
854 self.transaction.connect('progress-changed', self.on_driver_changes_progress)
855 self.transaction.connect('cancellable-changed', self.on_driver_changes_cancellable_changed)
856 self.transaction.connect('finished', self.on_driver_changes_finish)
857 self.transaction.connect('error', self.on_driver_changes_error)
858 self.transaction.set_debconf_frontend("gnome")
859 self.transaction.run()
860 self.button_driver_revert.setEnabled(False)
861 self.button_driver_apply.setEnabled(False)
862 #self.scrolled_window_drivers.set_sensitive(False)
863 except (NotAuthorizedError, TransactionFailed) as e:
864 print("Warning: install transaction not completed successfully: {}".format(e))
865
866 def on_driver_changes_revert(self, button_revert=None):
867 # HACK: set all the "Do not use" first; then go through the list of the
868 # actually selected drivers.
869 for button in self.no_drv:
870 button.setChecked(True)
871
872 for alias in self.orig_selection:
873 button = self.orig_selection[alias]
874 button.setChecked(True)
875
876 self.clear_changes()
877
878 self.button_driver_revert.setEnabled(False)
879 self.button_driver_apply.setEnabled(False)
880
881 def on_driver_changes_cancel(self, button_cancel):
882 self.transaction.cancel()
883 self.clear_changes()
884
885 def on_driver_restart_clicked(self, button_restart):
886 if 'XDG_CURRENT_DESKTOP' in os.environ:
887 desktop = os.environ['XDG_CURRENT_DESKTOP']
888 else:
889 desktop = 'Unknown'
890
891 if (desktop == 'ubuntu:GNOME' and os.path.exists('/usr/bin/gnome-session-quit')):
892 # argument presents a dialog to cancel reboot
893 subprocess.call(['gnome-session-quit', '--reboot'])
894 elif (desktop == 'XFCE' and
895 os.path.exists('/usr/bin/xfce4-session-logout')):
896 subprocess.call(['xfce4-session-logout'])
897 elif (desktop == 'LXDE' and os.path.exists('/usr/bin/lubuntu-logout')):
898 subprocess.call(['lubuntu-logout'])
899 elif (desktop == 'LXQt' and os.path.exists('/usr/bin/lxqt-leave')):
900 subprocess.call(['lxqt-leave'])
901
902 def clear_changes(self):
903 self.orig_selection = {}
904 self.driver_changes = []
905
906 def init_drivers(self):
907 """Additional Drivers tab"""
908 self.button_driver_revert = QPushButton("Revert")
909 self.button_driver_apply = QPushButton("Apply Changes")
910 self.button_driver_cancel = QPushButton("Cancel")
911 self.button_driver_restart = QPushButton("Restart...")
912
913 self.button_driver_revert.clicked.connect(self.on_driver_changes_revert)
914 self.button_driver_apply.clicked.connect(self.on_driver_changes_apply)
915 self.button_driver_cancel.clicked.connect(self.on_driver_changes_cancel)
916 self.button_driver_restart.clicked.connect(self.on_driver_restart_clicked)
917
918 self.button_driver_revert.setEnabled(False)
919 self.button_driver_revert.setVisible(True)
920 self.button_driver_apply.setEnabled(False)
921 self.button_driver_apply.setVisible(True)
922 self.button_driver_cancel.setVisible(False)
923 self.button_driver_restart.setVisible(False)
924
925 #self.userinterface.box_driver_action.addWidget(self.userinterface.label_driver_action)
926 self.userinterface.box_driver_action.addStretch()
927 self.userinterface.box_driver_action.addWidget(self.button_driver_apply)
928 self.userinterface.box_driver_action.addWidget(self.button_driver_revert)
929 self.userinterface.box_driver_action.addWidget(self.button_driver_restart)
930 self.userinterface.box_driver_action.addWidget(self.button_driver_cancel)
931
932 self.label_driver_detail = QLabel("Searching for available drivers...")
933 self.label_driver_detail.setAlignment(Qt.AlignCenter)
934 self.userinterface.box_driver_detail.addWidget(self.label_driver_detail)
935
936 self.progress_bar = QProgressBar()
937
938 self.userinterface.box_driver_action.addWidget(self.progress_bar)
939 self.progress_bar.setVisible(False)
940
941 self.devices = {}
942 self.detect_called = False
943 self.driver_changes = []
944 self.orig_selection = {}
945 # HACK: the case where the selection is actually "Do not use"; is a little
946 # tricky to implement because you can't check for whether a package is
947 # installed or any such thing. So let's keep a list of all the
948 # "Do not use" radios, set those active first, then iterate through
949 # orig_selection when doing a Reset.
950 self.no_drv = []
951 self.nonfree_drivers = 0
952 self.ui_building = False
953
954 def detect_drivers(self):
955 # WARNING: This is run in a separate thread.
956 self.detect_called = True
957 try:
958 self.apt_cache = apt.Cache()
959 self.devices = detect.system_device_drivers(self.apt_cache)
960 except:
961 # Catch all exceptions and feed them to apport.
962 #GLib.idle_add(self.label_driver_detail.set_text, _("An error occurred while searching for drivers."))
963 self.label_driver_detail.setText("An error occurred while searching for drivers.")
964 # For apport to catch this exception. See
965 # http://bugs.python.org/issue1230540
966 sys.excepthook(*sys.exc_info())
967 return
968 def on_driver_selection_changed(self, modalias, pkg_name=None):
969 button = self.userinterface.sender()
970 #print(modalias)
971 #print(pkg_name)
972
973 if self.ui_building:
974 return
975
976 pkg = None
977 try:
978 if pkg_name:
979 pkg = self.apt_cache[pkg_name]
980 # If the package depends on dkms
981 # we need to install the correct linux metapackage
982 # so that users get the latest headers
983 if 'dkms' in pkg.candidate.record['Depends']:
984 linux_meta = detect.get_linux(self.apt_cache)
985 if (linux_meta and linux_meta not in self.driver_changes):
986 # Install the linux metapackage
987 lmp = self.apt_cache[linux_meta]
988 if not lmp.is_installed:
989 self.driver_changes.append(lmp)
990 except KeyError:
991 pass
992
993 if button.isChecked():
994 if pkg in self.driver_changes:
995 self.driver_changes.remove(pkg)
996
997 if (pkg is not None and modalias in self.orig_selection and button is not self.orig_selection[modalias]):
998 self.driver_changes.append(pkg)
999 else:
1000 if pkg in self.driver_changes:
1001 self.driver_changes.remove(pkg)
1002
1003 # for revert; to re-activate the original radio buttons.
1004 if modalias not in self.orig_selection:
1005 self.orig_selection[modalias] = button
1006
1007 if (pkg is not None and pkg not in self.driver_changes and pkg.is_installed):
1008 self.driver_changes.append(pkg)
1009
1010 self.button_driver_revert.setEnabled(bool(self.driver_changes))
1011 self.button_driver_apply.setEnabled(bool(self.driver_changes))
1012
1013 def gather_device_data(self, device):
1014 '''Get various device data used to build the GUI.
1015
1016 return a tuple of (overall_status string, icon, drivers dict).
1017 the drivers dict is using this form:
1018 {"recommended/alternative": {pkg_name: {
1019 'selected': True/False
1020 'description':
1021'description'
1022 'builtin': True/False
1023 }
1024 }}
1025 "manually_installed": {"manual": {'selected': True, 'description':
1026description_string}}
1027 "no_driver": {"no_driver": {'selected': True/False, 'description':
1028description_string}}
1029
1030 Please note that either manually_installed and no_driver are set to
1031None if not applicable
1032 (no_driver isn't present if there are builtins)
1033 '''
1034
1035 possible_overall_status = {
1036 'recommended': (_("This device is using the recommended driver."),
1037"recommended-driver"),
1038 'alternative': (_("This device is using an alternative driver."),
1039"other-driver"),
1040 'manually_installed': (_("This device is using a manually-installed driver."), "other-driver"),
1041 'no_driver': (_("This device is not working."), "disable-device")
1042 }
1043
1044 returned_drivers = {'recommended': {}, 'alternative': {},
1045'manually_installed': {}, 'no_driver': {}}
1046 have_builtin = False
1047 one_selected = False
1048 try:
1049 if device['manual_install']:
1050 returned_drivers['manually_installed'] = {True: {'selected': True,'description': _("Continue using a manually installed driver")}}
1051 except KeyError:
1052 pass
1053
1054 for pkg_driver_name in device['drivers']:
1055 current_driver = device['drivers'][pkg_driver_name]
1056
1057 # get general status
1058 driver_status = 'alternative'
1059 try:
1060 if current_driver['recommended'] and current_driver['from_distro']:
1061 driver_status = 'recommended'
1062 except KeyError:
1063 pass
1064
1065 builtin = False
1066 try:
1067 if current_driver['builtin']:
1068 builtin = True
1069 have_builtin = True
1070 except KeyError:
1071 pass
1072
1073 try:
1074 pkg = self.apt_cache[pkg_driver_name]
1075 installed = pkg.is_installed
1076 if pkg.candidate is not None:
1077 description = _("Using {} from {}").format(pkg.candidate.summary, pkg.shortname)
1078 else:
1079 description = _("Using {}").format(pkg.shortname)
1080 except KeyError:
1081 print("WARNING: a driver ({}) doesn't have any available package associated: {}".format(pkg_driver_name, current_driver))
1082 continue
1083
1084 # gather driver description
1085 if current_driver['free']:
1086 licence = _("open source")
1087 else:
1088 licence = _("proprietary")
1089
1090 if driver_status == 'recommended':
1091 base_string = _("{base_description} ({licence}, tested)")
1092 else:
1093 base_string = _("{base_description} ({licence})")
1094 description = base_string.format(base_description=description,
1095licence=licence)
1096
1097 selected = False
1098 if not builtin and not returned_drivers['manually_installed']:
1099 selected = installed
1100 if installed:
1101 selected = True
1102 one_selected = True
1103
1104 returned_drivers[driver_status].setdefault(pkg_driver_name, {'selected': selected,'description': description,'builtin': builtin})
1105
1106 # adjust making the needed addition
1107 if not have_builtin:
1108 selected = False
1109 if not one_selected:
1110 selected = True
1111 returned_drivers["no_driver"] = {True: {'selected': selected, 'description': _("Do not use the device")}}
1112 else:
1113 # we have a builtin and no selection: builtin is the selected one then
1114 if not one_selected:
1115 for section in ('recommended', 'alternative'):
1116 for pkg_name in returned_drivers[section]:
1117 if returned_drivers[section][pkg_name]['builtin']:
1118 returned_drivers[section][pkg_name]['selected'] = True
1119
1120 # compute overall status
1121 for section in returned_drivers:
1122 for keys in returned_drivers[section]:
1123 if returned_drivers[section][keys]['selected']:
1124 (overall_status, icon) = possible_overall_status[section]
1125
1126 return (overall_status, icon, returned_drivers)
1127
1128 def show_drivers(self):
1129 if not self.devices:
1130 # No drivers found.
1131 self.label_driver_detail.setText("No additional drivers available.")
1132 return
1133 else:
1134 self.label_driver_detail.hide()
1135
1136 self.option_group = {}
1137 self.radio_button = {}
1138 self.ui_building = True
1139 self.dynamic_device_status = {}
1140 for device in sorted(self.devices.keys()):
1141 (overall_status, icon, drivers) = self.gather_device_data(self.devices[device])
1142
1143 driver_status = QLabel()
1144 driver_status.setAlignment(Qt.AlignTop)
1145 driver_status.setAlignment(Qt.AlignHCenter)
1146 pixmap = QIcon.fromTheme(icon).pixmap(QSize(16, 16))
1147 driver_status.setPixmap(pixmap)
1148
1149 device_box = QHBoxLayout()
1150 device_box.addWidget(driver_status)
1151 device_detail = QVBoxLayout()
1152 device_box.addLayout(device_detail,1)#1 for priority over the icon to stretch
1153
1154 widget = QLabel("{}: {}".format(self.devices[device].get('vendor', _('Unknown')), self.devices[device].get('model', _('Unknown'))))
1155 widget.setAlignment(Qt.AlignLeft)
1156 device_detail.addWidget(widget)
1157 widget = QLabel("<small>{}</small>".format(overall_status))
1158 widget.setAlignment(Qt.AlignLeft)
1159 #widget.set_use_markup(True)
1160 device_detail.addWidget(widget)
1161 self.dynamic_device_status[device] = (driver_status, widget)
1162
1163 self.option_group[device] = None
1164 # define the order of introspection
1165 for section in ('recommended', 'alternative', 'manually_installed', 'no_driver'):
1166 for driver in drivers[section]:
1167 self.radio_button = QRadioButton(drivers[section][driver]['description'])
1168 if self.option_group[device]:
1169 self.option_group[device].addButton(self.radio_button)
1170 else:
1171 self.option_group[device] = QButtonGroup()
1172 self.option_group[device].addButton(self.radio_button)
1173
1174 device_detail.addWidget(self.radio_button)
1175 self.radio_button.setChecked(drivers[section][driver]['selected'])
1176
1177 if section == 'no_driver':
1178 self.no_drv.append(self.radio_button)
1179 if section in ('manually_install', 'no_driver') or ('builtin' in drivers[section][driver] and drivers[section][driver]['builtin']):
1180 self.radio_button.toggled.connect(partial( self.on_driver_selection_changed, device))
1181 else:
1182 self.radio_button.toggled.connect(partial( self.on_driver_selection_changed, device, driver))
1183 if drivers['manually_installed'] and section != 'manually_installed':
1184 self.radio_button.setEnabled(False)
1185
1186 self.userinterface.box_driver_detail.addLayout(device_box)
1187
1188 self.userinterface.box_driver_detail.addStretch()
1189 self.ui_building = False
1190 #self.userinterface.box_driver_detail.show_all()
1191 self.set_driver_action_status()
1192
1193 def update_label_and_icons_from_status(self):
1194 '''Update the current label and icon, computing the new device status'''
1195
1196 for device in self.devices:
1197 (overall_status, icon, drivers) = self.gather_device_data(self.devices[device])
1198 (driver_status, widget) = self.dynamic_device_status[device]
1199
1200 pixmap = QIcon.fromTheme(icon).pixmap(QSize(16, 16))
1201 driver_status.setPixmap(pixmap)
1202 widget.setText("<small>{}</small>".format(overall_status))
1203
1204 def set_driver_action_status(self):
1205 # Update the label in case we end up having some kind of proprietary driver in use.
1206 if (os.path.exists('/var/run/reboot-required')):
1207 self.userinterface.label_driver_action.setText("You need to restart the computer to complete the driver changes.")
1208 self.button_driver_restart.setVisible(True)
1209 return
1210
1211 self.nonfree_drivers = 0
1212 for device in self.devices:
1213 for pkg_name in self.devices[device]['drivers']:
1214 pkg = self.apt_cache[pkg_name]
1215 if not self.devices[device]['drivers'][pkg_name]['free'] and pkg.is_installed:
1216 self.nonfree_drivers = self.nonfree_drivers + 1
1217
1218 if self.nonfree_drivers > 0:
1219 text = "%s proprietary driver in use." % ( self.nonfree_drivers)
1220 self.userinterface.label_driver_action.setText(text)
1221 else:
1222 self.userinterface.label_driver_action.setText(_("No proprietary drivers are in use."))

Subscribers

People subscribed via source and target branches

to status/vote changes: