I would like to add various widgets to various cells in a Table Widget, and I would like to trigger commands when those widgets' values are changed. I can get the widgets into the table as desired, but I'm having problems connecting signals so that I know which widget has generated the signal.
Below is a simple example explaining the problem, using just checkboxes:
from PyQt5 import QtWidgets, QtGui, QtCore
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QtWidgets.QTableWidget()
[self.table.insertRow(i) for i in [0,1,2]]
[self.table.insertColumn(i) for i in [0,1]]
# set values for first column:
self.table.setItem(0, 0, QtWidgets.QTableWidgetItem('A') )
self.table.setItem(1, 0, QtWidgets.QTableWidgetItem('B') )
self.table.setItem(2, 0, QtWidgets.QTableWidgetItem('C') )
# add checkboxes to second column:
cb0 = QtWidgets.QCheckBox( parent=self.table )
cb1 = QtWidgets.QCheckBox( parent=self.table )
cb2 = QtWidgets.QCheckBox( parent=self.table )
self.table.setCellWidget(0, 1, cb0)
self.table.setCellWidget(1, 1, cb1)
self.table.setCellWidget(2, 1, cb2)
# connect table signals:
self.table.cellChanged.connect(self.cell_changed)
self.table.itemChanged.connect(self.item_changed)
# connect checkbox signals:
cb0.clicked.connect(self.checkbox_clicked)
cb1.clicked.connect(self.checkbox_clicked)
cb2.clicked.connect(self.checkbox_clicked)
# show:
self.setCentralWidget(self.table)
self.setWindowTitle('TableWidget, CheckBoxes')
self.show()
def cell_changed(self, row, col):
print(row, col)
def checkbox_clicked(self, checked):
print(checked)
def item_changed(self, item):
print(item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
app.exec_()
Based on table.cellChanged.connect I would naively expect a cellChanged signal when the checkboxes are changed. However this signal is not generated. Nor is the itemChanged signal. I can indeed see the clicked signals, but that is not very useful because it is unclear which checkbox has produced the signal.
One way to solve the problem is to create a different checkbox_clicked function for each checkbox, but that hardly seems elegant.
My questions are:
Why is neither a cellChanged nor an itemChanged signal generated when a checkbox is changed?
How should signals be connected in order to know which checkbox has generated the clicked signal?
Why is neither a cellChanged nor an itemChanged signal generated when
a checkbox is changed?
because when you use setCellWidget() a QTableWidgetItem is not created, and if we check the documentation of cellChanged and itemChanged:
void QTableWidget::cellChanged(int row, int column)
This signal is emitted whenever the data of the item in the cell specified by row and column has changed.
void QTableWidget::itemChanged(QTableWidgetItem *item)
This signal is emitted whenever the data of item has changed.
How should signals be connected in order to know which checkbox has generated the clicked signal?
The way to obtain is indirectly, the first thing to know is that when the widget is added through the setCellWidget() method, the viewport() of the QTableWidget is set as a parent.
Also another thing that should be known is that the position of a widget that is accessed through pos() is relative to the parent, that is, in our case relative to viewport().
There is a very useful method called sender() that returns the object that emits the signal, in this case it will return the QCheckBox.
As the position of the widget with respect to the viewport() is known, its QModelIndex is accessed through the indexAt() method, the QModelIndex has the information of the cell.
All of the above is implemented in the following example:
from PyQt5 import QtWidgets, QtGui, QtCore
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QtWidgets.QTableWidget()
self.table.setRowCount(3)
self.table.setColumnCount(2)
for i, letter in enumerate("ABC"):
self.table.setItem(i, 0, QtWidgets.QTableWidgetItem(letter))
for i in range(self.table.rowCount()):
ch = QtWidgets.QCheckBox(parent=self.table)
ch.clicked.connect(self.onStateChanged)
self.table.setCellWidget(i, 1, ch)
self.setCentralWidget(self.table)
self.setWindowTitle('TableWidget, CheckBoxes')
self.show()
def onStateChanged(self):
ch = self.sender()
print(ch.parent())
ix = self.table.indexAt(ch.pos())
print(ix.row(), ix.column(), ch.isChecked())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
sys.exit(app.exec_())
Another way to do it is through lambda methods or partial.functions where we pass directly new parameters.
from PyQt5 import QtWidgets, QtGui, QtCore
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QtWidgets.QTableWidget()
self.table.setRowCount(3)
self.table.setColumnCount(2)
for i, letter in enumerate("ABC"):
self.table.setItem(i, 0, QtWidgets.QTableWidgetItem(letter))
for i in range(self.table.rowCount()):
ch = QtWidgets.QCheckBox(parent=self.table)
ch.clicked.connect(lambda checked, row=1, col=i: self.onStateChanged(checked, row, col))
self.table.setCellWidget(i, 1, ch)
self.setCentralWidget(self.table)
self.setWindowTitle('TableWidget, CheckBoxes')
self.show()
def onStateChanged(self, checked, row, column):
print(checked, row, column)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
sys.exit(app.exec_())
If you want to know more information how to pass extra parameters through connect() you can review this answer.
use the stateChanged signal for checkboxes.
and my take about that code:
in some cases it's helpful to have a reference to checkbox widgets, for some logic actions.
use loops if possible
use explicit imports in PyQt - the class names are unique and it's more readable
for example:
from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QCheckBox, QApplication
from typing import Dict
class Main(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QTableWidget()
self.table.insertColumn(0)
self.table.insertColumn(1)
self._items: Dict[QTableWidgetItem, QCheckBox] = {}
for i, tag in enumerate(['A', 'B', 'C']):
self.table.insertRow(i)
item = QTableWidgetItem(tag)
cb = QCheckBox(parent=self.table)
self._items[item] = cb
# set values for first column:
self.table.setItem(i, 0, item)
# add checkboxes to second column:
self.table.setCellWidget(i, 1, cb)
# connect cb signals:
self._items[item].stateChanged.connect(self.checkbox_clicked)
# connect table signals:
self.table.cellChanged.connect(self.cell_changed)
# show:
self.setCentralWidget(self.table)
self.setWindowTitle('TableWidget, CheckBoxes')
self.show()
def cell_changed(self, row, col):
print(row, col)
def checkbox_clicked(self, checked):
print(checked)
def item_changed(self, item):
print(item)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
main = Main()
app.exec_()
Related
I'm running a simple code to creat 4 checkboxes in a widget
I have a simple function to change the checkbox text when clicked based on wheater it's checked or not
I'm trying to connect the "clicked" signal to slot "on_checkBox_ss" using Lambda method to pass an extar parameter to identify the clicked checkbox
but it's not working well, it pass False/True instead of the checkbox index
here is the code
from PyQt6 import QtWidgets
from PyQt6.QtWidgets import QWidget, QCheckBox, QApplication, QVBoxLayout
class mainwidget():
def __init__(self) -> None:
super().__init__()
self.widget = QWidget()
vlayout = QVBoxLayout(self.widget)
self.CHK_BoxGRP = [checkBoxClass(self.widget,f"ChkBox_{x}") for x in range(0,4)]
[vlayout.addWidget(self.CHK_BoxGRP[i].chkbox) for i in range(0,4)]
# Build the connection
[self.CHK_BoxGRP[y].chkbox.clicked.connect(lambda y:self.on_checkBox_ss(y)) for
y in range (0,4)]
#function to process the clicked checkbox
def on_checkBox_ss(self,boxnum):
if self.CHK_BoxGRP[boxnum].chkbox.isChecked():
self.CHK_BoxGRP[boxnum].chkbox.setText("Clicked")
else:
self.CHK_BoxGRP[boxnum].chkbox.setText("Not Clicked")
""" Below is check box class """
class checkBoxClass:
def __init__(self,PARENT,CHKLABEL) -> None:
#super().__init__()
self.parent = PARENT
self.chkLabel = CHKLABEL
#self.operat_on = OPERAT_ON
self.chkbox = QtWidgets.QCheckBox(self.parent)
self.chkbox.setStyleSheet("border-color: rgb(85, 85, 255);")
self.chkbox.setObjectName(self.chkLabel)
self.chkbox.setChecked(True)
self.chkbox.setText(f"GROUP {self.chkLabel[-1]}")
""" start of main code"""
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
mainwindow = mainwidget()
mainwindow.widget.show()
sys.exit(app.exec())
QPushButton.clicked.connect() method automatically passes the state of the button (bool) as default first argument, so if there is only one argument -which is 'y' in comprehension in this case- is overwritten with the state of the button.
You can see that if you just print(boxnum) in 'on_checkBox_ss', it prints out True or False which is the state of clicked button, and by coincidence list[True] will return #1 index and list[False] will return #0 (as it should be because boolean expressions are just 0 or 1 at the end of the day)
I said that this was a coincidence because your code actually works (just not the way as intended) and doesn't give an error which make it seem like the problem has something to do with signals and loops in the framework.
So the solution is to overwrite the state argument with anything (which will be 'x' -or anything for that matter) and pass 'y' explicitly.
[self.CHK_BoxGRP[y].chkbox.clicked.connect(lambda x="", y=y: self.on_checkBox_ss(y)) for y in range(4)]
So it will be:
from PyQt6 import QtWidgets
from PyQt6.QtWidgets import QWidget, QCheckBox, QApplication, QVBoxLayout
class mainwidget():
def __init__(self) -> None:
super().__init__()
self.widget = QWidget()
vlayout = QVBoxLayout(self.widget)
self.CHK_BoxGRP = [checkBoxClass(self.widget,f"ChkBox_{x}") for x in range(0,4)]
[vlayout.addWidget(self.CHK_BoxGRP[i].chkbox) for i in range(0,4)]
# Build the connection
#[self.CHK_BoxGRP[y].chkbox.clicked.connect(lambda y:self.on_checkBox_ss(y)) for
#y in range (0,4)]
[self.CHK_BoxGRP[y].chkbox.clicked.connect(lambda x="", y=y:self.on_checkBox_ss(y)) for y in range(4)]
#function to process the clicked checkbox
def on_checkBox_ss(self,boxnum):
print(boxnum) # now prints out actual y value instead of True or False
if self.CHK_BoxGRP[boxnum].chkbox.isChecked():
self.CHK_BoxGRP[boxnum].chkbox.setText("Clicked")
else:
self.CHK_BoxGRP[boxnum].chkbox.setText("Not Clicked")
""" Below is check box class """
class checkBoxClass:
def __init__(self,PARENT,CHKLABEL) -> None:
#super().__init__()
self.parent = PARENT
self.chkLabel = CHKLABEL
#self.operat_on = OPERAT_ON
self.chkbox = QtWidgets.QCheckBox(self.parent)
self.chkbox.setStyleSheet("border-color: rgb(85, 85, 255);")
self.chkbox.setObjectName(self.chkLabel)
self.chkbox.setChecked(True)
self.chkbox.setText(f"GROUP {self.chkLabel[-1]}")
""" start of main code"""
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
mainwindow = mainwidget()
mainwindow.widget.show()
sys.exit(app.exec())
I've got a QTreeView populated by a QStandardItemModel with the TreeView set up so it only allows internal Drag and Drop (InternalMove).
I am trying to detect whenever the user makes such an internal move and would like to extract the item being dragged as well as the start and end location.
QStandardItemModel provides the "rowsMoved" signal which is supposed to emit precisely what I am looking for: parent, start, end, destination, row.
The problem: This signal never gets called when moving around items. Why is this?
Other signals like rowsInserted() or rowsRemoved() work just fine, but rowsMoved() does not.
In the following minimal example, the print() in onMove() should be called when the user moves around items, but it doesn't.
import random
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
model = QtGui.QStandardItemModel(self)
view = QtWidgets.QTreeView()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(view)
view.setModel(model)
root_item = QtGui.QStandardItem("Root")
model.appendRow(root_item)
self.populate(root_item, 3)
view.expandAll()
view.setDragEnabled(True)
view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
view.setDefaultDropAction(QtCore.Qt.MoveAction)
view.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectItems)
model.rowsMoved.connect(self.onMove)
def onMove(self, parent, start, end, destination, row):
print("parent",parent,"start",start,"end",end,"destination",destination,"row",row)
def populate(self, root_item, level):
for i in range(random.randint(2, 4)):
it = QtGui.QStandardItem("item {}".format(i))
it.setCheckable(False)
root_item.appendRow(it)
next_level = level - 1
if next_level > 0:
self.populate(it, next_level)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
The documentation states:
Components connected to this signal use it to adapt to changes
in the model's dimensions. It can only be emitted by the QAbstractItemModel
implementation, and cannot be explicitly emitted in subclass code.
Considering that this signal is not defined by the QStandardItemModel class, any instance of this class won't emit such signal.
The only signals you can connect to, as you mentioned, are rowsInserted() are rowsRemoved() because they are redefined in this class. Note that they are marked as internal in the source file.
If you need information about the item being dragged, I suggest that you create a custom class which inherit from QtWidgets.QTreeView and override the dragEnterEvent and dropEvent methods.
Here is an example on how to retrieve the item with its whole hierarchy being dragged and dropped:
import random
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
def getHierarchy(index, hierarchy=None):
hierarchy = hierarchy or []
if not index.isValid():
return hierarchy
hierarchy.insert(0, (index.row(), index.column()))
return getHierarchy(index.parent(), hierarchy)
class MyTreeView(QtWidgets.QTreeView):
def dropEvent(self, e):
super().dropEvent(e)
currentIndex = e.source().currentIndex()
print('dropEvent.source current index:', getHierarchy(currentIndex))
def dragEnterEvent(self, e):
super().dragEnterEvent(e)
currentIndex = e.source().currentIndex()
print('dragEnterEvent.source current index:', getHierarchy(currentIndex))
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
model = QtGui.QStandardItemModel(self)
view = MyTreeView()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(view)
view.setModel(model)
root_item = QtGui.QStandardItem("Root")
model.appendRow(root_item)
self.populate(root_item, 3)
view.expandAll()
view.setDragEnabled(True)
view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
view.setDefaultDropAction(QtCore.Qt.MoveAction)
view.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectItems)
def populate(self, root_item, level):
for i in range(random.randint(2, 4)):
it = QtGui.QStandardItem("item {}".format(i))
it.setCheckable(False)
root_item.appendRow(it)
next_level = level - 1
if next_level > 0:
self.populate(it, next_level)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I suggest to look at this repository which deals with a similar problem as yours: https://github.com/d1vanov/PyQt5-reorderable-list-model
I am trying to pre-select multiple "individual values (or cells some would like to call em)" from the QTableWidget and I don't seem to be able to find the right method. I have tried setRangeSelect,selectRow & selectColumn, and none of the methods works.
Looking for some help. (Please see the test method for what I am trying to do)
import sys
import json
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QPushButton
from PyQt5.Qt import QTableWidgetItem, QAbstractItemView
class Demo(QWidget):
def __init__(self):
super().__init__()
self.rowTracker = []
self.initUI()
self.initQTableWidget()
def initUI(self):
self.resize(600, 600)
# populate some data
self.rows = [['a1','b1', 'c1'], ['a2','b2','c2'], ['a3','b3','c3'], ['a4','b4','c4'], ['a5','b5','c5']]
self.btn = QPushButton(self)
self.btn.move(50, 250)
self.btn.resize(150, 40)
self.btn.setText('Check')
self.btn.clicked.connect(self.test)
def initQTableWidget(self):
self.tableWidget = QTableWidget(self)
self.tableWidget.resize(self.width(), self.height()-400)
self.tableWidget.setRowCount(len(self.rows))
self.tableWidget.setColumnCount(len(self.rows[0]))
# here we will change row selection behavior to multiselection
self.tableWidget.setSelectionMode(QAbstractItemView.MultiSelection)
for row in enumerate(self.rows):
# print(row)
for col in enumerate(row[1]):
item = QTableWidgetItem()
item.setText(col[1])
self.tableWidget.setItem(row[0], col[0], item)
def test(self):
# print(dir(self.tableWidget))
self.tableWidget.select('<2nd row>', '<1st column>')
self.tableWidget.select('<3nd row>', '<2nd column>')
# self.tableWidget.setRangeSelect()
app =QApplication(sys.argv)
widget = Demo()
widget.show()
sys.exit(app.exec_())
There are the following methods:
The setSelected() method of QtableWidgetItem:
self.tableWidget.item(1, 0).setSelected(True)
self.tableWidget.item(2, 1).setSelected(True)
The select() method of QItemSelectionModel:
model = self.tableWidget.model()
selection_model = self.tableWidget.selectionModel()
selection_model.select(model.index(1, 0), QItemSelectionModel.Select)
selection_model.select(model.index(2, 1), QItemSelectionModel.Select)
The second method is general for all views that inherit from QAbstractItemView, and the first method is just a wrapper that makes QTableWidget of the second method.
In my app I have a QTableView with rows that get selected programmatically, e.g. after a query on the data is performed.
How can I prevent the user to change the selected rows on click, while keeping the ability to select rows programmatically?
This is my code:
self.table = QTableView()
pandas_model: QAbstractTableModel = PandasTableModel(self.data_frame, self)
self.table.setModel(pandas_model)
self.table.setSortingEnabled(False)
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # full width table
self.table.setSelectionMode(QAbstractItemView.MultiSelection)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
Should I override its ItemSelectionModel in order to prevent the default behaviour on user click while keeping the programmatic selection mode? How could I achieve this?
If you want to avoid that the user can select an item(s), row(s) or column(s) you should do the following:
Overwrite delegate editorEvent method so that it does not notify the view click.
Deactivate the ability to click on the sections of the headers
from PyQt5 import QtCore, QtGui, QtWidgets
class Delegate(QtWidgets.QStyledItemDelegate):
def editorEvent(self, event, model, option, index):
res = super(Delegate, self).editorEvent(event, model, option, index)
if event.type() in (
QtCore.QEvent.MouseButtonPress,
QtCore.QEvent.MouseButtonRelease,
QtCore.QEvent.MouseButtonDblClick,
QtCore.QEvent.MouseMove,
QtCore.QEvent.KeyPress
):
return True
return res
class TableView(QtWidgets.QTableView):
def __init__(self, parent=None):
super(TableView, self).__init__(parent)
self.setSortingEnabled(False)
self.horizontalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch
)
self.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
delegate = Delegate(self)
self.setItemDelegate(delegate)
self.horizontalHeader().setSectionsClickable(False)
self.verticalHeader().setSectionsClickable(False)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
model = QtGui.QStandardItemModel()
for i in range(15):
for j in range(6):
it = QtGui.QStandardItem("{}-{}".format(i, j))
model.setItem(i, j, it)
table = TableView()
table.setModel(model)
# emulate select by query
import random
for row in random.sample(range(model.rowCount()), 5):
table.selectRow(row)
table.resize(640, 480)
table.show()
sys.exit(app.exec_())
I'm trying to create a table with 160 Rows, and then inserting a QCheckBox every odd number of rows, specifically on column 10. The problem is, i have to create 80 QCheckBox (one for each row, so they can be separately assigned by the user)...Creating one by one the 80 QCheckBox objects for the 9 projects I have to do is simply nonsense!Is there any way of doing that by a loop? I can't think of anything, I searched for the answer and found nothing.
[...]
# importing PySide
from PySide import QtGui, QtCore
[...]
# Creating a Table
class Table(QtGui.QDialog):
def __init__(self, parent=None):
super(Table, self).__init__(parent)
self.table = QtGui.QTableWidget()
self.table.setRowCount(160)
self.table.setColumnCount(10)
# This is the tricky part:
chkBoxItem = QtGui.QTableWidgetItem()
chkBoxItem.setFlags(QtCore.Qt.ItemIsUserCheckable|QtCore.Qt.ItemIsEnabled)
chkBoxItem.setCheckState(QtCore.Qt.Unchecked)
chkBoxItem2 = QtGui.QTableWidgetItem()
chkBoxItem2.setFlags(QtCore.Qt.ItemIsUserCheckable|QtCore.Qt.ItemIsEnabled)
chkBoxItem2.setCheckState(QtCore.Qt.Unchecked)
chkBoxItem3 = QtGui.QTableWidgetItem()
chkBoxItem3.setFlags(QtCore.Qt.ItemIsUserCheckable|QtCore.Qt.ItemIsEnabled)
chkBoxItem3.setCheckState(QtCore.Qt.Unchecked)
[...]
# Then insert all of them in the Table:
self.table.setItem(0, 10, chkBoxItem)
self.table.setItem(2, 10, chkBoxItem2)
self.table.setItem(4, 10, chkBoxItem3)
self.table.setItem(6, 10, chkBoxItem4)
self.table.setItem(8, 10, chkBoxItem5)
self.table.setItem(10, 10, chkBoxItem6)
self.table.setItem(12, 10, chkBoxItem7)
[...]
This basic script creates an UI containing a 160*10 QTable and a QPushButton.
Every odd row, a checkbox is added in the cell of the 10th column.
Clicking on the button displays a list of the state of all checkboxes.
States:
0: Unchecked
2: Checked
There is a state 1 but I don't remember what
it is used for, I'll check the docs.
Note:
This has been made using PyQt
Code:
import math, sys
from PyQt4.QtCore import Qt, QTimer
from PyQt4.QtGui import *
class MainWindow(QMainWindow):
def __init__(self, parent = None):
QMainWindow.__init__(self, parent)
#Create Basic UI
self.mainWidget = QWidget(self)
self.table = QTableWidget()
self.table.setRowCount(160)
self.table.setColumnCount(10)
self.button = QPushButton("Print stuff")
layout = QVBoxLayout(self.mainWidget)
layout.addWidget(self.table)
layout.addWidget(self.button)
self.setCentralWidget(self.mainWidget)
self.button.clicked.connect(self.printStuff)
#################
#Fill the table
self.rowRange = range(0, self.table.rowCount(), 2)
for i in self.rowRange:
chkBoxItem = QTableWidgetItem()
chkBoxItem.setFlags(Qt.ItemIsUserCheckable|Qt.ItemIsEnabled)
chkBoxItem.setCheckState(Qt.Unchecked)
self.table.setItem(i, 9, chkBoxItem)
###############
def printStuff(self): #You can remove this, this is for testing purpose only
print [(i+1, self.table.item(i, 9).checkState()) for i in self.rowRange]
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())