RkBlog

Hardware, programming and astronomy tutorials and reviews.

Tworzenie interfejsów graficznych w QtDesigner dla aplikacji PyQt4

Qt Designer to aplikacja dostarczana wraz z biblioteką Qt służąca do tworzenia interfejsów użytkownika naszych aplikacji. Za pomocą przeciągania widżetów z dostępnej palety możemy umieścić wszystkie potrzebne elementy. Co ważne możemy także zadbać o rozmieszczenie poszczególnych widżetów za pomocą szablonów (Layouts), jak i o nadanie etykiet i domyślnych ustawień poszczególnym widgetom. W tym artykule opiszę tworzenie proste interfejsu prezentującego proces poprawnego tworzenia interfejsu w QtDesignerze.

W przypadku Windowsa QtDesigner będzie dostępny po zainstalowaniu PyQt4. W przypadku Linuksa będzie w pakiecie Qt, lub np. w przypadku Ubuntu, które stosuje rozbite pakiety - jako oddzielny pakiet (szukaj "designer").

Tworzenie interfejsu w QtDesigner

Naszym celem będzie stworzenie prostej aplikacji, która będzie startowała pasek postępu przyrastający co sekundę:
pyqtdes10
Trzy przyciski QPushButton (Start, Stop, Exit) odpowiednio do uruchamiania i zatrzymywania paska postępu, oraz do zamykania aplikacji. Do tego jeden QProgressBar.
  • Otwieramy Qt Designera i tworzymy nowy Widget:
    pyqtdes0
    pyqtdes1
  • Z menu po lewej przeciągamy trzy przyciski ("Push Button") i jeden pasek postępu ("Progress Bar").
    pyqtdes2
  • Klikając na przycisk możemy edytować tekst jaki wyświetla. Gdy zaznaczymy jakiś widget na oknie - w menu po prawej stronie pojawią się jego ustawienia. W przypadku QProgressBar znajdziemy ustawienia określające startową wartość (mamy 24, a wypada zmienić na 0):
    pyqtdes3
    Zmieniamy nazwy na przyciskach, a także startową wiadomość paska postępu.
  • Każdy widżet ma swoją unikalną etykietę, po której w kodzie można go identyfikować i wykorzystywać. Warto nazwać wszystkie używane widgety spójnie i zgodnie z ich funkcją/przeznaczeniem. Etykietę zmieniamy w menu ustawień:
    pyqtdes4
    Przycisk "Start" nazwałem "startButton", "Stop" - "stopButton", "Exit" - "exitButton". Pasek postępu pozostał jako "progressBar".
  • Interfejs mamy już gotowy jeżeli chodzi o zawartość. W menu górnym możesz sprawdzić wygląd zaprojektowanego okna: Formularz - Podgląd. Spróbuj rozszerzyć wyświetlone okno. Zobaczysz efekt tego typu:
    pyqtdes5
    Widgety nie skalują się wraz z oknem. By widżety skalowały się i odpowiednio układały się w oknie należy zastosować Layout ("szablon"). Taki szablon możemy (i zazwyczaj to robimy) ustawić dla danego okna wybierając szablon z górnego menu QtDesignera
    pyqtdes6a
    Mamy odpowiednio poziomy i pionowy szablon (rozmieszcza odpowiednio widżety jeden po drugim i jeden pod drugim), następnie poziomy i pionowy Splitter (użytkownik może później przesuwać granicę podziału). Ostatni szablon do "grid" umożliwiający rozmieścić szablony w poziomie i pionie. Te same szablony można także wykorzystywać do zgrupowania kilku widżetów.
  • Zaznacz obecne trzy przyciski (klikaj na nie z wciśniętym klawiszem CTRL) a następnie kliknij na poziomy szablon (pierwszy):
    pyqtdes6
    Przyciski zostały zgrupowane w szablonie. Teraz nie mając zaznaczonego żadnego widżeta wybierz szablon pionowy (drugi) - by ustawić go dla całego okna. Oto efekt:
    pyqtdes6c
    Przyciski skalują się do równych długości, jak i też szablon zajmuje całą wolną wysokość.
  • Do dopasowywania skalowania się widgetów możemy użyć "wirtualnych" widżetów "Spacers" ("odstępy"):
    pyqtdes6b
    Wstaw poziomy odstep między przyciskiem Stop a Exit:
    pyqtdes7
    A pionowy pod paskiem postępu:
    pyqtdes8
  • Teraz widżety w naszym oknie będą płynnie skalowały się wraz z rozmiarem okna:
    pyqtdes9
    Interfejs jest gotowy i można przystąpić do jego oprogramowania. Zapisz interfejs w pustym katalogu jako np. counter.ui

Oprogramowanie interfejsu

Zakładam że przerobiłeś już pierwsze tutoriale opisujące podstawy programowania w PyQt4. Zaczynamy od wygenerowania klasy Pythona z pliku UI:
pyuic4 counter.ui > counter.py
Następtnie tworzymy plik run.py z takim oto szkieletowym kodem:
# -*- coding: utf-8 -*-
import sys

from PyQt4 import QtCore, QtGui
from counter import Ui_CounterWindow

class MyCounter(QtGui.QWidget):
	def __init__(self, parent=None):
		QtGui.QWidget.__init__(self, parent)
		self.ui = Ui_CounterWindow()
		self.ui.setupUi(self)

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	myapp = MyCounter()
	myapp.show()
	sys.exit(app.exec_())
Gdzie from counter import Ui_CounterWindow to import klasy naszego okna z pliku counter.py. Otwierając ten plik zdobędziemy nazwę klasy (Ui_CounterWindow w tym przypadku). Będzie to nazwa wg. schematu "Ui_etykietaOkna". Uruchom plik run.py - powinno wyświetlić się okno.
  • Zaczynamy od połączenia sygnału kliknięcia w przycisk z własnym slotem - metodą wykonującą określony kod. Oto rozwinięty kod szkieletu:
    # -*- coding: utf-8 -*-
    import sys
    
    from PyQt4 import QtCore, QtGui
    from counter import Ui_CounterWindow
    
    class MyCounter(QtGui.QWidget):
    	def __init__(self, parent=None):
    		QtGui.QWidget.__init__(self, parent)
    		self.ui = Ui_CounterWindow()
    		self.ui.setupUi(self)
    		
    		QtCore.QObject.connect(self.ui.startButton,QtCore.SIGNAL("clicked()"), self.start)
    		QtCore.QObject.connect(self.ui.stopButton,QtCore.SIGNAL("clicked()"), self.stop)
    		QtCore.QObject.connect(self.ui.exitButton,QtCore.SIGNAL("clicked()"), self.close_app)
    	
    	def start(self):
    		"""
    		Start the counter
    		"""
    		print 'start'
    	
    	def stop(self):
    		"""
    		Stop the counter
    		"""
    		print 'stop'
    	
    	def close_app(self):
    		"""
    		Close the app
    		"""
    		app.exit()
    
    if __name__ == "__main__":
    	app = QtGui.QApplication(sys.argv)
    	myapp = MyCounter()
    	myapp.show()
    	sys.exit(app.exec_())
    
    Metoda close_app zostanie wywołana po kliknięciu na przycisk "Exit". Sygnał kliknięcia (clicked()) został połączony z tą metodą za pomocą QtCore.QObject.connect. Przycisk określony jest jako self.ui.exitButton, gdzie exitButton to etykieta widżeta ustawiona przez nas w Qt Designerze. Podobnie połączyłem pozostałe dwa przyciski. Na razie metody wypisują tekst w konsoli gdy przycisk został kliknięty.
  • By stworzyć ładnie postępujący stan paska postępu użyję Stoperów opisanych w podlinkowanym artykule.
  • Oto gotowy kod pliku run.py:
    # -*- coding: utf-8 -*-
    import sys
    
    from PyQt4 import QtCore, QtGui
    from counter import Ui_CounterWindow
    
    class MyCounter(QtGui.QWidget):
    	def __init__(self, parent=None):
    		QtGui.QWidget.__init__(self, parent)
    		self.ui = Ui_CounterWindow()
    		self.ui.setupUi(self)
    		
    		QtCore.QObject.connect(self.ui.startButton,QtCore.SIGNAL("clicked()"), self.start)
    		QtCore.QObject.connect(self.ui.stopButton,QtCore.SIGNAL("clicked()"), self.stop)
    		QtCore.QObject.connect(self.ui.exitButton,QtCore.SIGNAL("clicked()"), self.close_app)
    		
    		# stoper
    		self.progress_timer = QtCore.QTimer()
    		QtCore.QObject.connect(self.progress_timer, QtCore.SIGNAL("timeout()"), self.progress_update)
    	
    	def progress_update(self):
    		"""
    		Update the progress bar
    		"""
    		val = self.ui.progressBar.value() + 10
    		if val <= 100:
    			self.ui.progressBar.setValue(val)
    		else:
    			self.progress_timer.stop()
    	
    	
    	def start(self):
    		"""
    		Start the counter
    		"""
    		print 'start'
    		self.progress_timer.start(1000)
    	
    	def stop(self):
    		"""
    		Stop the counter
    		"""
    		print 'stop'
    		self.progress_timer.stop()
    	
    	def close_app(self):
    		"""
    		Close the app
    		"""
    		app.exit()
    
    if __name__ == "__main__":
    	app = QtGui.QApplication(sys.argv)
    	myapp = MyCounter()
    	myapp.show()
    	sys.exit(app.exec_())
    
  • Metoda start uruchamia stoper, który co 1 sekundę będzie wywoływał metodę progress_update, która to o 10% zwiększy postęp paska postępu :)

Kod źródłowy

RkBlog

PyQt, 19 October 2009, Piotr Maliński

Comment article