2006.09_QT, PyQT – szybkie tworzenie baz danych_[Programowanie].pdf

(1319 KB) Pobierz
439114564 UNPDF
dla programistów
Python/PyQT
tworzenie baz danych
Marek Sawerwain
Jednym z ważnych uchybień, jakie można było zarzucić bibliotece QT to brak bezpośredniego
wsparcia dla baz danych. Brakowało odpowiednich komponentów które ułatwiają tworzenie typowego
interfejsu właśnie dla tego typu aplikacji. Nowa wersja QT 4.0 oraz poprzednia 3.0 wprowadzają
kilkanaście nowych klas, które oferują dostęp do baz danych i co ważne zawarte w nich API jest
dopracowane, co oznacza iż łatwo można tworzyć aplikacje korzystające z baz danych.
tworzenie programu korzystającego z ba-
zy danych w ramach QT, a będzie to ty-
powa baza teleadresowa, nie jest trud-
nym zadaniem, szczególnie, gdy wykorzystujemy program
Designer oraz porty biblioteki QT do języka Python. Język
ten, choć interpretowany, znakomicie nadaje się do tego
rodzaju zastosowań. Artykuł jest rozszerzeniem informa-
cji jakie o pakiecie PyQT zostały przedstawione w tekście
pt.: QShow -- szybki podglądacz obrazków, który ukazał się
w poprzednim numerze L+. Dlatego osoby, które chciały-
by poznać podstawy posługiwania się biblioteką PyQT
mogą zajrzeć do przywoływanego przed chwilą tekstu.
aplikacja może funkcjonować bez tej funkcji. Jednak w dal-
szej części będziemy omawiać sposób tworzenia relacji
i wtedy okaże się do czego było nam potrzebne wymienio-
ne dodatkowe okno ze spisem miast. Trzecim oknem jest
okno przeszukiwania danych, aby nasza aplikacja miała
bardziej profesjonalny charakter pozwolimy użytkowni-
kowi na zadawanie własnych zapytań.
Skoro dajemy możliwość tworzenia własnych zapytań
to oznacza to, iż będziemy korzystać z języka SQL. Ponie-
waż API biblioteki QT jest niezależne od bazy danych mo-
żemy założyć iż baza danych z jakiej będziemy korzystać
jest zupełnie dowolna. Jednak w naszej aplikacji ustalimy,
że będzie to baza SQLITE która znakomicie nadaje się do
tworzenia niewielkich baz danych jak aplikacja którą bę-
dziemy za chwilę tworzyć. Choć tak naprawdę, aby zmie-
nić serwer danych wystarczą drobne zmiany w metodach
tworzących nową bazę oraz sposób w jaki uzyskujemy do-
stęp do już istniejącej bazy.
Ostatnią wstępną uwagą jest naturalnie utworzenie
bazy danych. Będziemy używać tylko dwóch tabel. Pierw-
sza z nich o nazwie person przechowuje osoby w naszej
teleadresowej bazie, druga citylist zawiera spis miast
Projekt aplikacji
Choć nasza baza danych będzie należeć do niewielkich
aplikacji to warto jak zawsze przygotować plan programu.
Wszystkie najważniejsze operacje zostały przedstawione
na Rysunku 1. W naszym programie występują trzy pod-
stawowe okna. Okno główne aplikacji, gdzie przetwarza-
my dane w podstawowej tabeli. Drugie okno spisu miast
ma dla nas znaczenie dodatkowe i równie dobrze nasza
70
wrzesień 2006
QT, PyQT – szybkie
W tym artykule postaram się pokazać, że
439114564.042.png 439114564.043.png 439114564.044.png
 
dla programistów
Python/PyQT
sy. Po pierwsze wywołujemy konstruktor kla-
sy QMainWindow, który dokona inicjalizacji
wszystkich niezbędnych elementów. W dru-
gim kroku tworzymy interfejs używając kla-
sy Ui_MainWindow i metody setupUi . Wy-
mieniona klasa jest generowana samodziel-
nie przez program pyuic na podstawie for-
matki okna utworzonej w programie Desi-
gner. Następnie tworzymy dwa ważne dla
nas pola. Pierwsze z nich pole db będzie za-
wierać obiekt wskazujący na aktualnie otwo-
rzoną bazę danych natomiast old_name re-
prezentuje starą nazwę pliku z bazą danych,
jaka została otworzona wcześniej niż aktual-
nie używana baza danych. Do ostatnich czyn-
ności jakie wykonujemy w konstruktorze to
połączenie sygnałów ze slotami. Listing 2 po-
kazuje nam przykład podłączenia odpowied-
nich metod dla elementu menu okna główne-
go tworzącego nową bazę danych oraz dla
przycisku, który wykonuje tą samą czynność.
W obydwu przypadkach odwołujemy się do
tej samej metody.
Klasa okna głównego posiada jeszcze
wiele innych metod np.: metoda odpowie-
dzialna za zamknięcie aplikacji jest następu-
jąca:
def on_EndWorkBTN_click(self):
self.close()
Rysunek 1. Schemat najważniejszych obiektów w tworzonej aplikacji
Innym krótkim przykładem jest wywołanie
okna zawierającego spis miast:
i jest nam potrzebna, aby utworzyć odpowie-
dnią relację pomiędzy tabelami. Do tworze-
nia tabel stosujemy, słowo kluczowe create
i odpowiednie polecenia znajdują się na Lis-
ting 1.
Tworzymy obiekt aplikacji, następnie okno
główne i jak widać nawet nie musimy jawnie
specyikować, który obiekt jest naszym oknem
głównym. Jednakże kilka istotnych czynno-
ści jest wykonywanych w konstruktorze kla-
def actionCity_list(self):
citywin=CityListWindow(self)
citywin.show()
Okno główne
Po pierwszych założeniach możemy przejść
do implementacji. Okno główne programu
jest reprezentowane przez klasę MiniDBWind-
ow i zostało zaprojektowane w programie
Designer. Jest to okno, które dziedziczy z kla-
sy QMainWindow . Jest to dla nas bardzo istot-
ne, ponieważ w ten sposób mamy okno z me-
nu, paskiem statusu i etc. Inaczej mówiąc
jest to okno główne aplikacji. Pozostałe okna
stosowane w naszym programie dziedziczą
z klasy QDialog , co naturalnie oznacza, że
są to okna typu dialogowego.
Wyświetlenie okna głównego jest realizo-
wane w typowy dla PyQT sposób:
app = QtGui.QApplication
(sys.argv)
win = MiniDBWindow()
win.show()
sys.exit(app.exec_())
Rysunek 2. Baza danych teleadresowych
www.lpmagazine.org
71
439114564.001.png 439114564.002.png 439114564.003.png 439114564.004.png 439114564.005.png 439114564.006.png 439114564.007.png 439114564.008.png 439114564.009.png 439114564.010.png 439114564.011.png 439114564.012.png 439114564.013.png 439114564.014.png 439114564.015.png 439114564.016.png 439114564.017.png 439114564.018.png 439114564.019.png 439114564.020.png
dla programistów
Python/PyQT
Listing 1. Wyrażenia SQL tworzące dwie podsta-
wowe tabele
create table person
( id integer primary key ,
irstname varchar ( 20 ) ,
lastname varchar ( 25 ) ,
house_number int , lat_number int ,
street varchar ( 30 ) , city integer ,
code varchar ( 10 ) , mobile_telephone
varchar ( 13 ) , telephone varchar ( 13 ) ,
remark varchar ( 255 )) , create table
citylist ( id integer primary key ,
city varchar ( 30 ))
Wykonujemy dwie czynności: tworzymy ok-
no oraz wyświetlamy je na ekranie. W analo-
giczny sposób postępujemy dla okna prze-
szukiwana danych.
Tworzenie nowej bazy danych
Proces tworzenia nowej bazy danych rozpo-
czyna się od metody on_NewDBBTN_click . Po-
nieważ dane osobowe będą przechowywa-
ne w pliku, to wywołujemy standardowe ok-
no dialogowe, w którym wybieramy nazwę
dla nowego pliku:
Rysunek 3. Okno główne w programie Designer
sytuację należy zastosować instrukcję warun-
kową:
posiadać obce klucze. Pozostałe dwie wcze-
śniejsze metody niestety wymagają włożenia
większego wysiłku.
Zacznijmy od opisu metody create_new_
database . Treść wszystkich najważniejszych
fragmentów tej metody jest zawarta na Li-
stingu 3. Pierwsza czynność to usunięcie ot-
wartej bazy danych. Aby to zrobić wystarczy
sprawdzić, czy zmienna old_name jest różna
od None . Jeśli tak jest to, wywołujemy meto-
removeDatabase, który zamknie otwartą
bazę danych. W przypadku, gdy old_name
jest równe None , oznacza to iż nikt jeszcze nie
utworzył żadnej bazy danych, więc trzeba
w pierwszej kolejności załadować odpowied-
ni sterownik, a w naszym przypadku będzie
to sterownik o nazwie QSQLITE . Wynik tej ope-
racji jest umieszczany w zmiennej db . Na-
stępnie już bez żadnych dodatkowych wa-
runków za pomocą setDatabaseName okre-
ślamy nazwę naszej bazy danych. Jednak
trzeba się upewnić, czy baza danych została
poprawnie założona i w tym celu wywołuje-
my metodę open() . Jeśli nie udało się utwo-
rzyć bazy danych to zostanie wyświetlone
odpowiednie okno z komunikatem i za po-
mocą return zakończymy wykonywane tej
metody. W przeciwnym przypadku tworzy-
my dwa zapytania, których zadaniem jest
utworzenie dwóch tabel person oraz city-
list . Pozostaje do wykonania jeszcze jedna
czynność a jest nią wpisanie do pola old_na-
me aktualnie używanej nazwy dla bazy da-
nych. Jest to istotne, aby przy następnym
wywołaniu, pierwszy omawiany warunek
poprawnie wykrył iż wcześniej utworzono
już jakąś bazę danych.
if not ileName.isEmpty():
self.create_new_database(ileName)
self.create_model()
self.ui.DataView.setModel
(self.ui.DataViewModel)
self.ui.DataView.setItemDelegate
(QtSql.QSqlRelationalDelegate
(self.ui.DataView))
ileName = QtGui.QFileDialog.
getSaveFileName(self,
self.tr("Select name for ile
with database"), "newdb.sqdb",
self.tr("All Files (*);;Sqlite
DB Files (*.sqdb)"))
Metoda getSaveFileName przyjmuje cztery
parametry. Pierwszy to obiekt rodzicielski,
w drugim podajemy tytuł okna. Trzeci argu-
ment to proponowana nazwa pliku, w ostat-
nim argumencie określamy wpisy do pola Sa-
ve as type . Jeśli uda się nam poprawnie utwo-
rzyć nowy plik, to jego nazwa znajdzie się
w zmiennej ileName . Jeśli nie, to zmienna i-
leName będzie pusta, dlatego aby wykryć tą
Gdy nazwa, nie jest pusta to należy wyko-
nać trzy czynności: utworzyć bazę danych
w ramach podanego pliku za pomocą cre-
ate_new_database , utworzyć tzw. model da-
nych metodą o nazwie create_model oraz
podać ten model do widgetu DataView, który
jest odpowiedzialny za wyświetlanie danych.
Tylko ta czynność sprowadza się do jednej li-
nii kodu. Bowiem w drugiej linii kodu infor-
mujemy widget DataView, iż tablica będzie
Listing 2. Fragmenty konstruktora klasy MiniDBWindow
def __init__ ( self ):
QtGui . QMainWindow . __init__ ( self )
self . ui = Ui_MainWindow ()
self . ui . setupUi ( self )
self . db = None
self . old_name = None
QtCore . QObject . connect ( self . ui . actionNew_Database , QtCore . SIGNAL
( "activated()" ) , self . on_NewDBBTN_click )
QtCore . QObject . connect ( self . ui . NewDBBTN , QtCore . SIGNAL
( "clicked()" ) , self . on_NewDBBTN_click )
72
wrzesień 2006
439114564.021.png 439114564.022.png 439114564.023.png 439114564.024.png
 
dla programistów
Python/PyQT
Otwieranie bazy danych
W bardzo podobny sposób postępujemy
w przypadku otwierania istniejącej bazy da-
nych. Zajmuje się tym metoda on_OpenDB-
BTN_click . Ponieważ jest ona dość podobna
do omówionej w poprzednim punkcie me-
tody, warto skupić się tylko na czynnościach
jakie wykonujemy. Pierwszą czynnością jest
wyświetlenie okna dialogowego, za pomocą
którego użytkownik wskaże plik do otwar-
cia.
Po wybraniu nazwy analogicznie jak
przed chwilą sprawdzamy, czy użytkownik
otwierał już wcześniej jakąś bazę danych
i dodajemy sterownik QSQLITE lub usuwa-
my połączenie z istniejącą bazą danych. Jed-
nak bez względu na to, co się stanie za po-
mocą setDatabaseName podłączymy wska-
zany plik jako źródło danych w naszej apli-
kacji.
W dalszej kolejności sprawdzamy, czy uda-
ło się nawiązać połączenie i jeśli nie, wyświe-
tlamy odpowiedni komunikat. Gdy wszyst-
ko poszło dobrze, należy utworzyć model.
Zajmuje się tym metoda create_model iden-
tyczna dla otwarcia i utworzenia nowej ba-
zy danych. Fragmenty kodu tej metody znaj-
dują się na Listingu 4. Pierwszą istotną czyn-
nością jest utworzenie obiektu, który repre-
zentuje model. W naszym przypadku będzie
to obiekt o nazwie DataViewModel:
Rysunek 4. Okno przeszukiwania w programie Designer
określa postać naszej relacji. Pierwszy argu-
ment to nazwa tabeli, w drugim argumen-
cie podajemy pole, z którego będzie pobiera-
na wartość, a w ostatnim trzecim argumen-
cie określamy pole które będzie wyświetla-
ne.
ność będą za nas wykonywać wewnętrzne
mechanizmy QT, dlatego tym problemem nie
będziemy się zajmować.
Dodawanie nowego rekordu
Dodawanie nowego rekordu danych jest zrea-
lizowane w naszym programie za pomo-
cą oddzielnego okna. Proces dodawania da-
nych rozpoczyna się w metodzie actionAdd_
new_entry . Przyjmuje ona następującą pos-
tać:
Następnie pozostaje nam tylko określić
za pomocą metody setHeaderData jak bę-
dą przedstawiać się poszczególne kolumny
w tabeli. Ważna jest również ostatnia linia
self.ui.DataViewModel.select() powodu-
je, że tabela zostanie wypełniona danymi we-
dług wcześniej przyjętych kryteriów.
Warto też wspomnieć, iż nie musimy się
martwić o zamykanie bazy danych. Tą czyn-
self.ui.DataViewModel =
QtSql.QSqlRelationalTableModel()
Następnie musimy wskazać z której tabeli
korzystamy a będzie to tabela o nazwie per-
son . Kolejny krok jest bardzo ważny, gdyż
wywołaniem metody setEditStrategy us-
talamy, kiedy mają być wprowadzane zmia-
ny do bazy danych. Argument OnFieldChan-
ge oznacza, że zmiana choćby wartości jed-
nego pola, oznacza natychmiastowe uaktu-
alnienie bazy danych. W naszym przypad-
ku ten model się sprawdzi, choćby dlatego,
iż ostatecznie nasza baza jest małych roz-
miarów.
Następny ważny krok to ustalenie któ-
re pole odnosi się do innej tabeli. W naszym
przypadku będzie to pole dotyczące mias-
ta:
Addwin = AddDataWindow(self)
addwin.SetModel(self.ui.DataViewModel)
addwin.show()
self.ui.DataViewModel.setRelation
(6, QtSql.QSqlRelation
("citylist", "id", "city"))
Szóstka oznacza numer kolumny w tabeli,
przy czym kolumny numerujemy od zera. Dru-
gi argument a jest nim obiekt QSqlRelation
Strona domowa języka PHP
www.lpmagazine.org
73
439114564.025.png 439114564.026.png 439114564.027.png 439114564.028.png 439114564.029.png 439114564.030.png 439114564.031.png 439114564.032.png 439114564.033.png 439114564.034.png 439114564.035.png 439114564.036.png
 
dla programistów
Python/PyQT
Listing 3. Utworzenie nowe bazy danych za pomocą SQLITE
tów QLineEdit jest łatwe korzystamy tylko
z metody text() . Nieco kłopotów sprawia
właśnie odczytanie identyikatora miasta. Dla-
tego w pierwszej kolejności odczytujemy in-
deks aktualnie wybranego miasta:
def create_new_database ( self , name ):
if self . old_name != None :
QtSql . QSqlDatabase . removeDatabase ( self . old_name )
else :
self . db = QtSql . QSqlDatabase . addDatabase ( "QSQLITE" )
self . db . setDatabaseName ( name )
if not self . db . open ():
QtGui . QMessageBox . critical ( 0 , QtGui . qApp . tr ( "Cannot open database" ) ,
QtGui . qApp . tr ( komunikat o k ł opotach ) ,
QtGui . QMessageBox . Cancle , QtGui . QMessageBox . NoButton )
return False
query1 = QtSql . QSqlQuery () ; query1 . exec_ ( "wyrażenia SQL" )
query2 = QtSql . QSqlQuery () ; query2 . exec_ ( "wyrażenia SQL" )
self . old_name = name
idx = self.ui.CityEDT.currentIndex()
Następnie uzyskaną wartość wykorzystu-
jemy do otrzymania wartości id wybrane-
go miasta, korzystamy z następującego wy-
rażenia:
self.ui.CityViewModel.record(idx).val
ue("id").toString()
W ten sposób otrzymamy wartość id w po-
staci ciągu znaków. Jak widać nie sprawia to
większych kłopotów, teraz pozostaje nam tyl-
ko w zmiennej sqlstr umieścić odpowiednio
przygotowane polecenie insert języka SQL.
Następnie nakazujemy wykonanie polecenia
SQL wyrażeniem: query.exec_( sqlstr ) .
Nieco tajemniczo przedstawia się ostatnie wy-
rażenie na Listingu 5. Oznacza ono nic inne-
go, jak odświeżenie widoku w oknie głów-
nym, czyli zawartości tabeli person .
return True
Klasa AddDataWindow powstaje w dość podob-
ny sposób jak okno główne ale trzeba pamię-
tać iż jest to okno dialogowe. W oknie tym ma-
my szereg pól edycyjnych QLineEdit, ale mias-
to posiada jedno szczególne pole a mianowicie
widget QComboBox . Widget ten umie wykorzy-
stać model, aby uzyskać spis dostępnych ele-
mentów. W naszym przypadku będą to natu-
ralnie miasta. Jednak, musimy oprogramować
widget tak, aby użytkownik widział nazwy
miast natomiast wartością tej kontrolki ma być
wartość ID danego miasta.
Tworzenie modelu rozpoczynamy w po-
dobny sposób, jak w poprzednim przypad-
ku, ale korzystamy z nieco innego obiektu:
self.ui.CityEDT.setModelColumn( 1 )
self.ui.CityEDT.setCurrentIndex( -1 )
W naszym oknie dodawania danych dostęp-
ne są trzy przyciski, kasowania formularza,
dodania rekordu oraz zamknięcia okna. Ob-
sługa kasowania okna jest zrealizowana za
pomocą metody on_ClearDataBTN_click .
Taka metoda jest przydatna, jeśli wpisaliśmy
już jeden rekord i chcemy dodać nowy rekord.
Wtedy przyciskiem Clear form wyczyścimy
wszystkie pola w formularzu. Przykład czysz-
czenia dla dwóch przykładowych pól przed-
stawia się następująco:
Czasem trzeba coś usunąć
Jak głosi tytuł tego akapitu może się zdarzyć,
że będzie trzeba skasować wybrane rekordy.
W naszej aplikacji odpowiedzialna jest za to
metoda actionDelete, której treść przedsta-
wia się następująco:
self.ui.CityViewModel =
QtSql.QSqlTableModel()
self.ui.LastNameEDT.setText("")
self.ui.CityEDT.setCurrentIndex(-1)
def actionDelete(self):
idx = self.ui.DataView.
currentIndex().row()
if idx >= 0:
rec_id = self.ui.DataViewModel.
record(idx).value("id").toString()
query = QtSql.QSqlQuery()
query.exec_( "delete from person
Kolejne kroki są podobne do opisanych wcze-
śniej, określamy tabelę, strategię edycji (jed-
nak w tym przypadku nie jest ona istotna),
postać nagłówków oraz metodą select naka-
zujemy zebrać dostępne dane. Po tych czyn-
nościach ustalmy, że widget o nazwie City-
EDT , czyli QComboBox dla kontrolki związanej
z miastem będzie korzystał z nowo utworzo-
nego modelu:
A jak wygląda wpisanie danych do tabeli per-
son ? Nie okazuje się to wbrew pozorom bar-
dzo skomplikowane. Listing 5 przedstawia
odpowiedni fragment kodu. Odczytanie war-
tości wpisanych do poszczególnych widge-
Listing 4. Utworzenie modelu
def create_model ( self ):
self . ui . DataViewModel = QtSql . QSqlRelationalTableModel ()
self . ui . DataViewModel . setTable ( "person" )
self . ui . DataViewModel . setEditStrategy ( QtSql . QSqlTableModel . OnFieldChange )
self . ui . DataViewModel . setRelation ( 6 , QtSql . QSqlRelation
( "citylist" , "id" , "city" ))
self . ui . DataViewModel . setHeaderData ( 0 , QtCore . Qt . Horizontal ,
QtCore . QVariant ( QtCore . QObject . tr ( self . ui . DataViewModel , "ID" )))
self . ui . DataViewModel . setHeaderData ( 6 , QtCore . Qt . Horizontal ,
QtCore . QVariant ( QtCore . QObject . tr ( self . ui . DataViewModel , "City" )))
self . ui . DataViewModel . setHeaderData ( 10 , QtCore . Qt . Horizontal ,
QtCore . QVariant ( QtCore . QObject . tr ( self . ui . DataViewModel , "Remark" )))
self . ui . DataViewModel . select ()
self.
ui.CityEDT.setModel
(self.ui.CityViewModel)
Teraz należy określić, jaka kolumna będzie
wyświetlana. Nas interesują miasta, więc bę-
dzie to kolumna o indeksie jeden. Określamy
też, że początkowo kontrolka CityEDT nie bę-
dzie wskazywać żadnego miasta. Obydwie
czynności realizują dwie linie kodu:
74
wrzesień 2006
439114564.037.png 439114564.038.png 439114564.039.png 439114564.040.png 439114564.041.png
 
Zgłoś jeśli naruszono regulamin