Thiết kế giao diện với PyQt5 (Image to ASCII)


Bài viết này gồm 2 phần, đây là phần đầu với thiết kế giao diện với PyQt5 Designer. Mục tiêu của bài viết này là xây dựng được một phần mềm đơn giản có các chức năng nạp file, xử lý file, xuất file và cách thiết kế giao diện trên PyQt5 Designer sao cho hiện đại. Bài sau mình sẽ viết phần backend để xử lý ảnh đưa về ASCII (Image to ASCII) theo trend đang nổi hiện nay và dùng luôn giao diện này. Để bắt đầu bài học này chúng ta cần tải và cài đặt phần mềm PyQt5 Designer tại đây.


Kết quả sau khi hoàn thành thiết kế giao diện Image to ASCII.


Bắt đầu với việc tạo mới một project: File > New > Chọn Main Window > Create. Một cửa sổ mới sẽ hiện ra, chúng ta sẽ cài đặt lại kích thước cho cửa sổ này trong mục geometry phía bên tay phải của các bạn. Mình sẽ điều chỉnh kích thước màn hình với Width = 1020 và Height = 604. Mình sẽ xoá Status bar và Menu bar trong phần object Inspector đi vì không cần dùng đến. Để đổi màu nền cho cửa sổ này, click chuột phải vào centralwidget chọn Edit Style Sheet và nhập đoạn mã định dạng như sau:

QWidget {
    background-color: #121313;
}

Tạo một Group Box và đặt tên là Group1, trong Group1 này bao gồm Push Button tên Browser, Label Filename, Push Button I2T để chuyển hình ảnh về ASCII và lưu vào *.txt, Push Button I2I để tạo ảnh màu bằng các ký tự ASCII, mỗi nút cho mỗi chức năng. Phía dưới là phần Style Sheet của mỗi thành phần trong giao diện phần mềm Image to ASCII.

QGroupBox#Group1 {
    background-color: #242526;
    border: none;
    border-radius: 10px;
}
QPushButton#Browser:hover:!pressed {
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:0, stop:0.306818 rgba(63, 0, 64, 255), stop:0.886364 rgba(0, 27, 68, 255));
}
QPushButton#Browser {
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:0, stop:0.306818 rgba(143, 0, 146, 255), stop:0.886364 rgba(0, 100, 245, 255));
border: none;
color: white;
border-radius: 25px;
}

Thuộc tính :hover và :!pressed được sử dụng để định dạng button khi rê chuột lại gần hoặc nhấn vào button. Tại đây mình để hiệu ứng sẫm màu, có thể tạo các màu gradient trong cửa sổ Set Style Sheet. Trông kiểu định dạng khá là giống với CSS.

QLabel#Filename {
background-color: #3A3B3C;
border: none;
color: white;
border-radius: 25px;
}
QPushButton#I2T:disabled {
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:0, stop:0.306818 rgba(63, 0, 64, 255), stop:0.886364 rgba(0, 27, 68, 255));
}
QPushButton#I2T:hover:!pressed {
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:0, stop:0.306818 rgba(63, 0, 64, 255), stop:0.886364 rgba(0, 27, 68, 255));
}
QPushButton#I2T {
background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:0, y2:0, stop:0.306818 rgba(143, 0, 146, 255), stop:0.886364 rgba(0, 100, 245, 255));
border: none;
color: white;
border-radius: 20px;
}

Thuộc tính :disable dùng khi muốn định dạng kiểu hiển thị khi vô hiệu hoá một button nào đó. Button I2I tương tự I2T. Mình sử dụng các file *.svg từ Font Awesome để thêm icon cho các button, chỉ cần tải về chọn button cần thêm icon và khai báo tới đường dẫn icon trong cửa sổ Property Editor, mục QAbtractButton.



Mình sẽ ẩn đi thanh title mặc định của window và tạo một thanh title mới cho phần mềm Image to ASCII này với title, nút close và nút minimized. Các bạn có thể để ý thấy khi định dạng một thành phần nào đó sẽ bao gồm tên thành phần và ID của thành phần đó. Tất nhiên nếu không có ID thì định dạng sẽ được áp dụng cho mọi thành phần. Giờ thì tạo Group Box đặt tên là Group2, định dạng các thành phần như sau:

QGroupBox#Group2 {
background-color: #242526;
border: none;
border-radius: 10px;
}
QLabel#Appname {
color: white;
background-color: #242526;
}
QPushButton#Minimun:hover:!pressed {
background-color: rgb(72, 72, 72);
}
QPushButton#Minimun {
background-color: rgb(140, 140, 140);
border: none;
color: white;
border-radius: 10px;
}
QPushButton#Close:hover:!pressed {
background-color: rgb(167, 30, 30);
}
QPushButton#Close {
background-color: rgb(220, 60, 60);
border: none;
color: white;
border-radius: 10px;
}

Hạn chế của việc không dùng title của window là không thể kéo cửa sổ ứng dụng đi đến vị trí khác. Mình nghĩ chắc là có cách nhưng mình tìm chưa ra, hiện tại với việc tạo mới thanh title thế này sẽ khiến ứng dụng cố định khi được mở lên. Mình sẽ tạo thêm một cửa sổ Preview bằng Group Box, trong cửa sổ này sẽ chứa một Label với giá trị text mặc định là "Nothing to show" và mình dùng nó để hiển thị ảnh preview. Một số Label khác dùng để đặt tên cho các cửa sổ nhỏ.



Mình cũng tạo thêm 3 cái button phía bên kia gồm button Author dẫn đến blog, button lưu file ASCII dạng *txt và button lưu hình ảnh sau khi chuyển thành ASCII (Image to ASCII). Mình thêm vào thuộc tính :disable cho các nút chức năng Save để ngăn không cho người dùng thực hiện khi chưa có dữ liệu. Một cửa sổ lớn hơn cũng được tạo dùng để hiển thị hình ảnh sau khi xử lý. Các button và Group Box mới này định dạng tương tự nên mình sẽ không đề cập lại.



Sau khi đã định dạng xong, mình lưu lại với tên Image_to_ASCII.ui, đây chính là file giao diện mà mình sẽ sử dụng. Tuy nhiên, để có thể đóng gói chương trình này thành file *.exe chúng ta cần phải chuyển định dạng *.ui này sang *.py. Điều này có thể thực hiện dễ dàng với thư viện PyQt5. Hãy install thư viện này sau đó truy cập đến địa chỉ chứa file *.ui và thực hiện lệnh: pyuic5 filename.ui > newname.py 



Đây là phần mềm Image to ASCII mà mình đã đóng gói thành file *.exe, các bạn có thể tải về và trải nghiệm tại đây
Mình sẽ public source code trong bài sau, hy vọng các bài viết mang lại kiến thức cho mọi người.